From 25869aa98f9093d857894c92f726f7056bbae39c Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Mon, 2 Oct 2023 08:32:36 -0500 Subject: [PATCH 01/14] Additional functions for playlists. --- R/add_video_to_playlist.R | 38 +++++++++++++++++ R/change_playlist_title.R | 49 ++++++++++++++++++++++ R/create_playlist.R | 33 +++++++++++++++ R/delete_playlist_items.R | 2 +- R/get_playlist_item_ids.R | 74 ++++++++++++++++++++++++++++++++++ R/get_playlist_item_videoids.R | 74 ++++++++++++++++++++++++++++++++++ 6 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 R/add_video_to_playlist.R create mode 100644 R/change_playlist_title.R create mode 100644 R/create_playlist.R create mode 100644 R/get_playlist_item_ids.R create mode 100644 R/get_playlist_item_videoids.R diff --git a/R/add_video_to_playlist.R b/R/add_video_to_playlist.R new file mode 100644 index 0000000..2d504f6 --- /dev/null +++ b/R/add_video_to_playlist.R @@ -0,0 +1,38 @@ +#' Add Video to Playlist +#' +#' @param playlist_id string; Required. The ID of the playlist. +#' @param video_id string; Required. The ID of the video to add. +#' @param ... Additional arguments passed to \code{\link{tuber_POST_json}}. +#' +#' @return Details of the added video in the playlist. +#' +#' @export +#' @references \url{https://developers.google.com/youtube/v3/docs/playlistItems/insert} +#' +#' @examples +#' \dontrun{ +#' +#' # Set API token via yt_oauth() first +#' +#' add_video_to_playlist(playlist_id = "YourPlaylistID", video_id = "2_gLD1jarfU") +#' } + +add_video_to_playlist <- function(playlist_id, video_id, ...) { + + # Prepare the request body + body <- list( + snippet = list( + playlistId = playlist_id, + resourceId = list( + kind = "youtube#video", + videoId = video_id + ) + ) + ) + + # Make the POST request using tuber_POST_json + raw_res <- tuber_POST_json(path = "playlistItems", query = list(part = "snippet"), body = body, ...) + + # Return the response + return(raw_res) +} diff --git a/R/change_playlist_title.R b/R/change_playlist_title.R new file mode 100644 index 0000000..33b2811 --- /dev/null +++ b/R/change_playlist_title.R @@ -0,0 +1,49 @@ +#' Change the title of a YouTube playlist. +#' +#' This function updates the title of an existing YouTube playlist using the YouTube Data API. +#' +#' @param playlist_id A character string specifying the ID of the playlist you want to update. +#' @param new_title A character string specifying the new title for the playlist. +#' +#' @return A list containing the server response after the update attempt. +#' @export +#' +#' @examples +#' \dontrun{ +#' change_playlist_title(playlist_id = "YourPlaylistID", new_title = "New Playlist Title") +#' } +#' + +change_playlist_title <- function(playlist_id, new_title) { + # Check for a valid token + yt_check_token() + + # Define the body for the PUT request + body <- list( + id = playlist_id, + snippet = list(title = new_title) + ) + + body_json <- jsonlite::toJSON(body, auto_unbox = TRUE) + + # Use the existing tuber infrastructure to send the PUT request + req <- httr::PUT( + url = "https://www.googleapis.com/youtube/v3/playlists", + query = list(key = getOption("google_key"), part = "snippet"), + config = httr::config(token = getOption("google_token")), + body = body_json, + httr::add_headers( + `Accept` = "application/json", + `Content-Type` = "application/json" + ) + ) + + # Check for errors + tuber_check(req) + + # Extract and return the content + return(httr::content(req)) +} + +# Example usage: +# change_playlist_title(playlist_id = "PLOfOyOpiu5saim7DsJmz61uCf8igQiTpF", new_title = "Lil' Casey's Top 40 Countdown 2023-09-29") diff --git a/R/create_playlist.R b/R/create_playlist.R new file mode 100644 index 0000000..027dde2 --- /dev/null +++ b/R/create_playlist.R @@ -0,0 +1,33 @@ +#' Create New Playlist +#' +#' @param title string; Required. The title of the playlist. +#' @param description string; Optional. The description of the playlist. +#' @param status string; Optional. Default: 'public'. Can be one of: 'private', 'public', or 'unlisted'. +#' @param ... Additional arguments passed to \code{\link{tuber_POST}}. +#' +#' @return The created playlist's details. +#' +#' @export +#' @references \url{https://developers.google.com/youtube/v3/docs/playlists/insert} +#' +#' @examples +#' \dontrun{ +#' +#' # Set API token via yt_oauth() first +#' +#' create_playlist(title = "My New Playlist", description = "This is a test playlist.") +#' } + +create_playlist <- function(title, status, ...) { + # Prepare the request body + body <- list( + snippet = list(title = title), + status = list(privacyStatus = status) + ) + + # Make the POST request using tuber_POST_json + raw_res <- tuber_POST_json(path = "playlists", query = list(part = "snippet,status"), body = body, ...) + + # Return the response + return(raw_res) +} diff --git a/R/delete_playlist_items.R b/R/delete_playlist_items.R index 04cd43d..c6aaebb 100644 --- a/R/delete_playlist_items.R +++ b/R/delete_playlist_items.R @@ -13,7 +13,7 @@ #' #' # Set API token via yt_oauth() first #' -#' delete_playlist_items(id = "y3ElXcEME3lSISz6izkWVT5GvxjPu8pA") +#' delete_playlist_items(id = "YourPlaylistItemID") #' } delete_playlist_items <- function(id = NULL, ...) { diff --git a/R/get_playlist_item_ids.R b/R/get_playlist_item_ids.R new file mode 100644 index 0000000..ef6219b --- /dev/null +++ b/R/get_playlist_item_ids.R @@ -0,0 +1,74 @@ +#' Get Playlist Item IDs +#' +#' @param filter string; Required. +#' named vector of length 1 +#' potential names of the entry in the vector: +#' \code{item_id}: comma-separated list of one or more unique playlist item IDs. +#' \code{playlist_id}: YouTube playlist ID. +#' +#' @param part Required. Comma separated string including one or more of the +#' following: \code{contentDetails, id, snippet, status}. Default: +#' \code{contentDetails}. +#' @param max_results Maximum number of items that should be returned. +#' Integer. Optional. Default is 50. +#' If over 50, all the results are returned. +#' @param simplify returns a data.frame rather than a list. +#' @param page_token specific page in the result set that should be +#' returned, optional +#' @param video_id Optional. request should return only the playlist +#' items that contain the specified video. +#' @param \dots Additional arguments passed to \code{\link{tuber_GET}}. +#' +#' @return playlist items +#' @export +#' @references \url{https://developers.google.com/youtube/v3/docs/playlists/list} +#' +#' @examples +#' \dontrun{ +#' +#' # Set API token via yt_oauth() first +#' +#' get_playlist_items(filter = +#' c(playlist_id = "PLrEnWoR732-CN09YykVof2lxdI3MLOZda")) +#' get_playlist_items(filter = +#' c(playlist_id = "PL0fOlXVeVW9QMO3GoESky4yDgQfK2SsXN"), +#' max_results = 51) +#' } + +get_playlist_item_ids <- function(filter = NULL, part = "contentDetails", + max_results = 50, video_id = NULL, + page_token = NULL, simplify = TRUE, ...) { + + if (max_results < 0 || max_results > 50) { + stop("max_results must be a value between 0 and 50.") + } + + valid_filters <- c("item_id", "playlist_id") + if (!(names(filter) %in% valid_filters)) { + stop("filter can only take one of the following values: item_id, playlist_id.") + } + + if (length(filter) != 1) { + stop("filter must be a vector of length 1.") + } + + translate_filter <- c(item_id = "id", playlist_id = "playlistId") + filter_name <- translate_filter[names(filter)] + names(filter) <- filter_name + + querylist <- list(part = part, + maxResults = max(min(max_results, 50), 1), + pageToken = page_token, videoId = video_id) + querylist <- c(querylist, filter) + + res <- tuber_GET("playlistItems", querylist, ...) + res_items <- res$items + + item_ids <- rep("", length(res_items)) + for(i in 1:length(res_items)){ + item_ids[i] <- res_items[[i]]$id + } + return(item_ids) +} + + diff --git a/R/get_playlist_item_videoids.R b/R/get_playlist_item_videoids.R new file mode 100644 index 0000000..01ec76b --- /dev/null +++ b/R/get_playlist_item_videoids.R @@ -0,0 +1,74 @@ +#' Get Playlist Item Video IDs +#' +#' @param filter string; Required. +#' named vector of length 1 +#' potential names of the entry in the vector: +#' \code{item_id}: comma-separated list of one or more unique playlist item IDs. +#' \code{playlist_id}: YouTube playlist ID. +#' +#' @param part Required. Comma separated string including one or more of the +#' following: \code{contentDetails, id, snippet, status}. Default: +#' \code{contentDetails}. +#' @param max_results Maximum number of items that should be returned. +#' Integer. Optional. Default is 50. +#' If over 50, all the results are returned. +#' @param simplify returns a data.frame rather than a list. +#' @param page_token specific page in the result set that should be +#' returned, optional +#' @param video_id Optional. request should return only the playlist +#' items that contain the specified video. +#' @param \dots Additional arguments passed to \code{\link{tuber_GET}}. +#' +#' @return playlist items +#' @export +#' @references \url{https://developers.google.com/youtube/v3/docs/playlists/list} +#' +#' @examples +#' \dontrun{ +#' +#' # Set API token via yt_oauth() first +#' +#' get_playlist_items(filter = +#' c(playlist_id = "YourPlaylistID")) +#' get_playlist_items(filter = +#' c(playlist_id = "YourPlaylistID"), +#' max_results = 51) +#' } + +get_playlist_item_videoids <- function(filter = NULL, part = "contentDetails", + max_results = 50, video_id = NULL, + page_token = NULL, simplify = TRUE, ...) { + + if (max_results < 0 || max_results > 50) { + stop("max_results must be a value between 0 and 50.") + } + + valid_filters <- c("item_id", "playlist_id") + if (!(names(filter) %in% valid_filters)) { + stop("filter can only take one of the following values: item_id, playlist_id.") + } + + if (length(filter) != 1) { + stop("filter must be a vector of length 1.") + } + + translate_filter <- c(item_id = "id", playlist_id = "playlistId") + filter_name <- translate_filter[names(filter)] + names(filter) <- filter_name + + querylist <- list(part = part, + maxResults = max(min(max_results, 50), 1), + pageToken = page_token, videoId = video_id) + querylist <- c(querylist, filter) + + res <- tuber_GET("playlistItems", querylist, ...) + res_items <- res$items + + item_ids <- rep("", length(res_items)) + for(i in 1:length(res_items)){ + item_ids[i] <- res_items[[i]]$contentDetails$videoId + } + return(item_ids) +} + + From 07e7e3f08c9ed3c2bb72032f78d3e3cbaae35975 Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Tue, 3 Oct 2023 10:18:30 -0500 Subject: [PATCH 02/14] squashed some warnings and notes from devtools::check() --- DESCRIPTION | 3 +- NAMESPACE | 5 +++ R/create_playlist.R | 5 +-- R/get_video_details.R | 2 + R/tuber.R | 25 +++++++++++++ R/upload_video.R | 2 +- R/yt_search.R | 25 +++++++++---- man/add_video_to_playlist.Rd | 32 ++++++++++++++++ man/change_playlist_title.Rd | 25 +++++++++++++ man/create_playlist.Rd | 34 +++++++++++++++++ man/delete_playlist_items.Rd | 2 +- man/get_playlist_item_ids.Rd | 62 +++++++++++++++++++++++++++++++ man/get_playlist_item_videoids.Rd | 62 +++++++++++++++++++++++++++++++ man/tuber.Rd | 1 + man/tuber_POST_json.Rd | 23 ++++++++++++ man/yt_search.Rd | 13 +++++++ 16 files changed, 307 insertions(+), 14 deletions(-) create mode 100644 man/add_video_to_playlist.Rd create mode 100644 man/change_playlist_title.Rd create mode 100644 man/create_playlist.Rd create mode 100644 man/get_playlist_item_ids.Rd create mode 100644 man/get_playlist_item_videoids.Rd create mode 100644 man/tuber_POST_json.Rd diff --git a/DESCRIPTION b/DESCRIPTION index bd1c96b..b252a39 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -18,12 +18,13 @@ BugReports: https://github.com/soodoku/tuber/issues Depends: R (>= 3.4.0) Imports: + askpass, dplyr, - httpuv, httr, httr2, jsonlite, magrittr, + mime, plyr, purrr, tibble, diff --git a/NAMESPACE b/NAMESPACE index 8109b46..da2500d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,6 +1,9 @@ # Generated by roxygen2: do not edit by hand export("%>%") +export(add_video_to_playlist) +export(change_playlist_title) +export(create_playlist) export(delete_captions) export(delete_channel_sections) export(delete_comments) @@ -13,6 +16,8 @@ export(get_captions) export(get_channel_stats) export(get_comment_threads) export(get_comments) +export(get_playlist_item_ids) +export(get_playlist_item_videoids) export(get_playlist_items) export(get_playlists) export(get_related_videos) diff --git a/R/create_playlist.R b/R/create_playlist.R index 027dde2..07cdf8a 100644 --- a/R/create_playlist.R +++ b/R/create_playlist.R @@ -17,11 +17,10 @@ #' #' create_playlist(title = "My New Playlist", description = "This is a test playlist.") #' } - -create_playlist <- function(title, status, ...) { +create_playlist <- function(title, description, status, ...) { # Prepare the request body body <- list( - snippet = list(title = title), + snippet = list(title = title, description = description), status = list(privacyStatus = status) ) diff --git a/R/get_video_details.R b/R/get_video_details.R index c6dd926..cc7c0bb 100644 --- a/R/get_video_details.R +++ b/R/get_video_details.R @@ -7,6 +7,8 @@ conditional_unnest_wider <- function(data_input, var) { } } +# Added to squash notes on devtools check. +utils::globalVariables(c("kind", "etag", "items", "snippet")) json_to_df <- function(res) { intermediate <- res %>% diff --git a/R/tuber.R b/R/tuber.R index b46dd82..4164c69 100644 --- a/R/tuber.R +++ b/R/tuber.R @@ -214,6 +214,31 @@ tuber_POST <- function(path, query, body = "", ...) { res } +#' +#' POST encoded in json +#' +#' @param path path to specific API request URL +#' @param query query list +#' @param body passing image through body +#' @param \dots Additional arguments passed to \code{\link[httr]{GET}}. +#' +#' @return list + +tuber_POST_json <- function(path, query, body = "", ...) { + + yt_check_token() + + req <- httr::POST("https://www.googleapis.com", path = paste0("youtube/v3/", path), + body = body, query = query, + config(token = getOption("google_token")), + encode = "json", ...) + + tuber_check(req) + res <- content(req) + + res +} + #' #' DELETE #' diff --git a/R/upload_video.R b/R/upload_video.R index f2bc276..8769d54 100644 --- a/R/upload_video.R +++ b/R/upload_video.R @@ -103,7 +103,7 @@ upload_video <- function( resumable_upload_url <- "https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=parts" resumable_upload_req <- httr::POST(resumable_upload_url, config(token = getOption("google_token")), - add_headers(headers), + httr::add_headers(headers), ... ) diff --git a/R/yt_search.R b/R/yt_search.R index 45d0c45..e202ac1 100644 --- a/R/yt_search.R +++ b/R/yt_search.R @@ -33,6 +33,13 @@ #' For instance, "1970-01-01T00:00:00Z" #' @param published_before Character. Optional. RFC 339 Format. #' For instance, "1970-01-01T00:00:00Z" +#' @param relevance_language Character. Optional. The relevance_language +#' argument instructs the API to return search results that are most relevant to +#' the specified language. The parameter value is typically an ISO 639-1 +#' two-letter language code. However, you should use the values zh-Hans for +#' simplified Chinese and zh-Hant for traditional Chinese. Please note that +#' results in other languages will still be returned if they are highly relevant +#' to the search query term. #' @param type Character. Optional. Takes one of three values: #' \code{'video', 'channel', 'playlist'}. Default is \code{'video'}. #' @param video_caption Character. Optional. Takes one of three values: @@ -44,6 +51,8 @@ #' @param video_syndicated Character. Optional. Takes one of two values: #' \code{'any'} (return all videos; Default), \code{'true'} #' (return only syndicated videos) +#' @param region_code Character. Required. Has to be a ISO 3166-1 alpha-2 code +#' (see \url{https://www.iso.org/obp/ui/#search}). #' @param video_definition Character. Optional. #' Takes one of three values: \code{'any'} (return all videos; Default), #' \code{'high', 'standard'} @@ -136,12 +145,12 @@ yt_search <- function(term = NULL, max_results = 50, channel_id = NULL, stop("Location radius must be specified with location") } - querylist <- list(part = "snippet", - q = term, + querylist <- list(part = "snippet", + q = term, maxResults = max_results, - channelId = channel_id, + channelId = channel_id, type = type, - channelType = channel_type, + channelType = channel_type, eventType = event_type, location = location, locationRadius = location_radius, @@ -149,9 +158,9 @@ yt_search <- function(term = NULL, max_results = 50, channel_id = NULL, publishedBefore = published_before, videoDefinition = video_definition, videoCaption = video_caption, - videoType = video_type, + videoType = video_type, videoSyndicated = video_syndicated, - videoLicense = video_license, + videoLicense = video_license, regionCode = region_code, relevanceLanguage = relevance_language, pageToken = page_token) @@ -180,9 +189,9 @@ yt_search <- function(term = NULL, max_results = 50, channel_id = NULL, while (is.character(page_token)) { - a_res <- yt_search(part = "snippet", + a_res <- yt_search(part = "snippet", term = term, - max_results = max_results, + max_results = max_results, channel_id = channel_id, type = type, relevance_language = relevance_language, diff --git a/man/add_video_to_playlist.Rd b/man/add_video_to_playlist.Rd new file mode 100644 index 0000000..9149e32 --- /dev/null +++ b/man/add_video_to_playlist.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/add_video_to_playlist.R +\name{add_video_to_playlist} +\alias{add_video_to_playlist} +\title{Add Video to Playlist} +\usage{ +add_video_to_playlist(playlist_id, video_id, ...) +} +\arguments{ +\item{playlist_id}{string; Required. The ID of the playlist.} + +\item{video_id}{string; Required. The ID of the video to add.} + +\item{...}{Additional arguments passed to \code{\link{tuber_POST_json}}.} +} +\value{ +Details of the added video in the playlist. +} +\description{ +Add Video to Playlist +} +\examples{ +\dontrun{ + +# Set API token via yt_oauth() first + +add_video_to_playlist(playlist_id = "YourPlaylistID", video_id = "2_gLD1jarfU") +} +} +\references{ +\url{https://developers.google.com/youtube/v3/docs/playlistItems/insert} +} diff --git a/man/change_playlist_title.Rd b/man/change_playlist_title.Rd new file mode 100644 index 0000000..afdd48d --- /dev/null +++ b/man/change_playlist_title.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/change_playlist_title.R +\name{change_playlist_title} +\alias{change_playlist_title} +\title{Change the title of a YouTube playlist.} +\usage{ +change_playlist_title(playlist_id, new_title) +} +\arguments{ +\item{playlist_id}{A character string specifying the ID of the playlist you want to update.} + +\item{new_title}{A character string specifying the new title for the playlist.} +} +\value{ +A list containing the server response after the update attempt. +} +\description{ +This function updates the title of an existing YouTube playlist using the YouTube Data API. +} +\examples{ +\dontrun{ +change_playlist_title(playlist_id = "YourPlaylistID", new_title = "New Playlist Title") +} + +} diff --git a/man/create_playlist.Rd b/man/create_playlist.Rd new file mode 100644 index 0000000..6166068 --- /dev/null +++ b/man/create_playlist.Rd @@ -0,0 +1,34 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_playlist.R +\name{create_playlist} +\alias{create_playlist} +\title{Create New Playlist} +\usage{ +create_playlist(title, description, status, ...) +} +\arguments{ +\item{title}{string; Required. The title of the playlist.} + +\item{description}{string; Optional. The description of the playlist.} + +\item{status}{string; Optional. Default: 'public'. Can be one of: 'private', 'public', or 'unlisted'.} + +\item{...}{Additional arguments passed to \code{\link{tuber_POST}}.} +} +\value{ +The created playlist's details. +} +\description{ +Create New Playlist +} +\examples{ +\dontrun{ + +# Set API token via yt_oauth() first + +create_playlist(title = "My New Playlist", description = "This is a test playlist.") +} +} +\references{ +\url{https://developers.google.com/youtube/v3/docs/playlists/insert} +} diff --git a/man/delete_playlist_items.Rd b/man/delete_playlist_items.Rd index 065f9f0..1eae165 100644 --- a/man/delete_playlist_items.Rd +++ b/man/delete_playlist_items.Rd @@ -19,7 +19,7 @@ Delete a Playlist Item # Set API token via yt_oauth() first -delete_playlist_items(id = "y3ElXcEME3lSISz6izkWVT5GvxjPu8pA") +delete_playlist_items(id = "YourPlaylistItemID") } } \references{ diff --git a/man/get_playlist_item_ids.Rd b/man/get_playlist_item_ids.Rd new file mode 100644 index 0000000..97130fe --- /dev/null +++ b/man/get_playlist_item_ids.Rd @@ -0,0 +1,62 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/get_playlist_item_ids.R +\name{get_playlist_item_ids} +\alias{get_playlist_item_ids} +\title{Get Playlist Item IDs} +\usage{ +get_playlist_item_ids( + filter = NULL, + part = "contentDetails", + max_results = 50, + video_id = NULL, + page_token = NULL, + simplify = TRUE, + ... +) +} +\arguments{ +\item{filter}{string; Required. +named vector of length 1 +potential names of the entry in the vector: +\code{item_id}: comma-separated list of one or more unique playlist item IDs. +\code{playlist_id}: YouTube playlist ID.} + +\item{part}{Required. Comma separated string including one or more of the +following: \code{contentDetails, id, snippet, status}. Default: +\code{contentDetails}.} + +\item{max_results}{Maximum number of items that should be returned. +Integer. Optional. Default is 50. +If over 50, all the results are returned.} + +\item{video_id}{Optional. request should return only the playlist +items that contain the specified video.} + +\item{page_token}{specific page in the result set that should be +returned, optional} + +\item{simplify}{returns a data.frame rather than a list.} + +\item{\dots}{Additional arguments passed to \code{\link{tuber_GET}}.} +} +\value{ +playlist items +} +\description{ +Get Playlist Item IDs +} +\examples{ +\dontrun{ + +# Set API token via yt_oauth() first + +get_playlist_items(filter = + c(playlist_id = "PLrEnWoR732-CN09YykVof2lxdI3MLOZda")) +get_playlist_items(filter = + c(playlist_id = "PL0fOlXVeVW9QMO3GoESky4yDgQfK2SsXN"), + max_results = 51) +} +} +\references{ +\url{https://developers.google.com/youtube/v3/docs/playlists/list} +} diff --git a/man/get_playlist_item_videoids.Rd b/man/get_playlist_item_videoids.Rd new file mode 100644 index 0000000..cbaa038 --- /dev/null +++ b/man/get_playlist_item_videoids.Rd @@ -0,0 +1,62 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/get_playlist_item_videoids.R +\name{get_playlist_item_videoids} +\alias{get_playlist_item_videoids} +\title{Get Playlist Item Video IDs} +\usage{ +get_playlist_item_videoids( + filter = NULL, + part = "contentDetails", + max_results = 50, + video_id = NULL, + page_token = NULL, + simplify = TRUE, + ... +) +} +\arguments{ +\item{filter}{string; Required. +named vector of length 1 +potential names of the entry in the vector: +\code{item_id}: comma-separated list of one or more unique playlist item IDs. +\code{playlist_id}: YouTube playlist ID.} + +\item{part}{Required. Comma separated string including one or more of the +following: \code{contentDetails, id, snippet, status}. Default: +\code{contentDetails}.} + +\item{max_results}{Maximum number of items that should be returned. +Integer. Optional. Default is 50. +If over 50, all the results are returned.} + +\item{video_id}{Optional. request should return only the playlist +items that contain the specified video.} + +\item{page_token}{specific page in the result set that should be +returned, optional} + +\item{simplify}{returns a data.frame rather than a list.} + +\item{\dots}{Additional arguments passed to \code{\link{tuber_GET}}.} +} +\value{ +playlist items +} +\description{ +Get Playlist Item Video IDs +} +\examples{ +\dontrun{ + +# Set API token via yt_oauth() first + +get_playlist_items(filter = + c(playlist_id = "YourPlaylistID")) +get_playlist_items(filter = + c(playlist_id = "YourPlaylistID"), + max_results = 51) +} +} +\references{ +\url{https://developers.google.com/youtube/v3/docs/playlists/list} +} diff --git a/man/tuber.Rd b/man/tuber.Rd index 42f41be..83c5f38 100644 --- a/man/tuber.Rd +++ b/man/tuber.Rd @@ -3,6 +3,7 @@ \docType{package} \name{tuber} \alias{tuber} +\alias{tuber-package} \title{\pkg{tuber} provides access to the YouTube API V3.} \description{ \pkg{tuber} provides access to the YouTube API V3 via diff --git a/man/tuber_POST_json.Rd b/man/tuber_POST_json.Rd new file mode 100644 index 0000000..25dc41d --- /dev/null +++ b/man/tuber_POST_json.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/tuber.R +\name{tuber_POST_json} +\alias{tuber_POST_json} +\title{POST encoded in json} +\usage{ +tuber_POST_json(path, query, body = "", ...) +} +\arguments{ +\item{path}{path to specific API request URL} + +\item{query}{query list} + +\item{body}{passing image through body} + +\item{\dots}{Additional arguments passed to \code{\link[httr]{GET}}.} +} +\value{ +list +} +\description{ +POST encoded in json +} diff --git a/man/yt_search.Rd b/man/yt_search.Rd index 3c0bb76..24d772f 100644 --- a/man/yt_search.Rd +++ b/man/yt_search.Rd @@ -19,6 +19,8 @@ yt_search( video_caption = "any", video_license = "any", video_syndicated = "any", + region_code = NULL, + relevance_language = "en", video_type = "any", simplify = TRUE, get_all = TRUE, @@ -86,6 +88,17 @@ license), \code{'youtube'} (return videos with standard YouTube license).} \code{'any'} (return all videos; Default), \code{'true'} (return only syndicated videos)} +\item{region_code}{Character. Required. Has to be a ISO 3166-1 alpha-2 code +(see \url{https://www.iso.org/obp/ui/#search}).} + +\item{relevance_language}{Character. Optional. The relevance_language +argument instructs the API to return search results that are most relevant to +the specified language. The parameter value is typically an ISO 639-1 +two-letter language code. However, you should use the values zh-Hans for +simplified Chinese and zh-Hant for traditional Chinese. Please note that +results in other languages will still be returned if they are highly relevant +to the search query term.} + \item{video_type}{Character. Optional. Takes one of three values: \code{'any'} (return all videos; Default), \code{'episode'} (return episode of shows), 'movie' (return movies)} From 5aaa0b4f38fc526af08db40422d241427e05d3c4 Mon Sep 17 00:00:00 2001 From: Troy Hernandez Date: Thu, 9 Nov 2023 17:11:01 -0600 Subject: [PATCH 03/14] added position to add_video_to_playlist fixed upload_video. --- R/add_video_to_playlist.R | 3 ++- R/get_all_channel_video_stats.R | 2 +- R/tuber.R | 2 +- R/upload_video.R | 18 +++++++++--------- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/R/add_video_to_playlist.R b/R/add_video_to_playlist.R index 2d504f6..33f195d 100644 --- a/R/add_video_to_playlist.R +++ b/R/add_video_to_playlist.R @@ -17,12 +17,13 @@ #' add_video_to_playlist(playlist_id = "YourPlaylistID", video_id = "2_gLD1jarfU") #' } -add_video_to_playlist <- function(playlist_id, video_id, ...) { +add_video_to_playlist <- function(playlist_id, video_id, position, ...) { # Prepare the request body body <- list( snippet = list( playlistId = playlist_id, + position = position, resourceId = list( kind = "youtube#video", videoId = video_id diff --git a/R/get_all_channel_video_stats.R b/R/get_all_channel_video_stats.R index f0e5abb..10763a6 100644 --- a/R/get_all_channel_video_stats.R +++ b/R/get_all_channel_video_stats.R @@ -32,7 +32,7 @@ get_all_channel_video_stats <- function(channel_id = NULL, mine = FALSE, ...) { channel_resources <- list_channel_resources(filter = list(channel_id = channel_id), part = "contentDetails") playlist_id <- channel_resources$items$contentDetails$relatedPlaylists$uploads - playlist_items <- get_playlist_items(filter = list(playlist_id = playlist_id), max_results = 100) + playlist_items <- get_playlist_items(filter = list(playlist_id = playlist_id), max_results = 50) vid_ids <- playlist_items$contentDetails$videoId res <- lapply(vid_ids, get_stats) diff --git a/R/tuber.R b/R/tuber.R index 4164c69..780b3ae 100644 --- a/R/tuber.R +++ b/R/tuber.R @@ -233,7 +233,7 @@ tuber_POST_json <- function(path, query, body = "", ...) { config(token = getOption("google_token")), encode = "json", ...) - tuber_check(req) + tuber::tuber_check(req) res <- content(req) res diff --git a/R/upload_video.R b/R/upload_video.R index 8769d54..d6348bd 100644 --- a/R/upload_video.R +++ b/R/upload_video.R @@ -1,6 +1,6 @@ #' Upload Video to Youtube #' -#' @param file Filename of the video locally +#' @param file_path Filename of the video locally #' @param snippet Additional fields for the video, including `description` #' and `title`. See #' \url{https://developers.google.com/youtube/v3/docs/videos#resource} for @@ -38,7 +38,7 @@ #' status = list(privacyStatus = "private") upload_video <- function( - file, + file_path, snippet = NULL, status = list(privacyStatus = "public"), query = NULL, @@ -86,18 +86,18 @@ upload_video <- function( writeLines(body, metadata) body <- list( - metadata = upload_file(metadata, type = "application/json; charset=UTF-8"), - y = httr::upload_file(file) + metadata = httr::upload_file(metadata, type = "application/json; charset=UTF-8"), + y = httr::upload_file(file_path) ) yt_check_token() headers <- c( - "Authorization" = paste("Bearer", getOption("google_token")), - "Content-Length" = file.size(file), + "Authorization" = paste("Bearer", getOption("google_token")$credentials$access_token), + "Content-Length" = file.size(file_path), "Content-Type" = "application/json; charset=utf-8", - "X-Upload-Content-Length" = file.size(file), - "X-Upload-Content-Type" = mime::guess_type(file) + "X-Upload-Content-Length" = file.size(file_path), + "X-Upload-Content-Type" = mime::guess_type(file_path) ) resumable_upload_url <- "https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=parts" @@ -114,7 +114,7 @@ upload_video <- function( upload_url <- httr::headers(resumable_upload_req)$`x-guploader-uploadid` upload_req <- httr::PUT(upload_url, - body = httr::upload_file(file), + body = httr::upload_file(file_path), config(token = getOption("google_token")), ... ) From 4e995fd92e3be570fc914f839865bb085185ac17 Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Fri, 10 Nov 2023 08:28:23 -0600 Subject: [PATCH 04/14] Reverting to old file name for consistency. --- R/upload_video.R | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/R/upload_video.R b/R/upload_video.R index d6348bd..bc15ea9 100644 --- a/R/upload_video.R +++ b/R/upload_video.R @@ -1,6 +1,6 @@ #' Upload Video to Youtube #' -#' @param file_path Filename of the video locally +#' @param file Filename of the video locally #' @param snippet Additional fields for the video, including `description` #' and `title`. See #' \url{https://developers.google.com/youtube/v3/docs/videos#resource} for @@ -38,7 +38,7 @@ #' status = list(privacyStatus = "private") upload_video <- function( - file_path, + file, snippet = NULL, status = list(privacyStatus = "public"), query = NULL, @@ -87,17 +87,17 @@ upload_video <- function( body <- list( metadata = httr::upload_file(metadata, type = "application/json; charset=UTF-8"), - y = httr::upload_file(file_path) + y = httr::upload_file(file) ) yt_check_token() headers <- c( "Authorization" = paste("Bearer", getOption("google_token")$credentials$access_token), - "Content-Length" = file.size(file_path), + "Content-Length" = file.size(file), "Content-Type" = "application/json; charset=utf-8", - "X-Upload-Content-Length" = file.size(file_path), - "X-Upload-Content-Type" = mime::guess_type(file_path) + "X-Upload-Content-Length" = file.size(file), + "X-Upload-Content-Type" = mime::guess_type(file) ) resumable_upload_url <- "https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=parts" @@ -114,7 +114,7 @@ upload_video <- function( upload_url <- httr::headers(resumable_upload_req)$`x-guploader-uploadid` upload_req <- httr::PUT(upload_url, - body = httr::upload_file(file_path), + body = httr::upload_file(file), config(token = getOption("google_token")), ... ) From af7215d41dc5aec0ec2384e15a3da888b6c63a3d Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Tue, 14 Nov 2023 08:48:16 -0600 Subject: [PATCH 05/14] yt_oauth fixed to handle second logins. readRDS on line 41 returns a list, not an environment variable. httr::oauth2.0_token functionality made to mirror spotifyr. --- NAMESPACE | 1 + R/add_video_to_playlist.R | 30 +++++++++++++++++++++--------- R/upload_video.R | 2 +- R/yt_oauth.R | 30 +++++++++++++++--------------- 4 files changed, 38 insertions(+), 25 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index da2500d..9d508a9 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -38,6 +38,7 @@ export(list_regions) export(list_videocats) export(list_videos) export(read_sbv) +export(tuber_check) export(upload_caption) export(upload_video) export(yt_authorized) diff --git a/R/add_video_to_playlist.R b/R/add_video_to_playlist.R index 33f195d..06c64c7 100644 --- a/R/add_video_to_playlist.R +++ b/R/add_video_to_playlist.R @@ -17,19 +17,31 @@ #' add_video_to_playlist(playlist_id = "YourPlaylistID", video_id = "2_gLD1jarfU") #' } -add_video_to_playlist <- function(playlist_id, video_id, position, ...) { +add_video_to_playlist <- function(playlist_id, video_id, position = NULL, ...) { # Prepare the request body - body <- list( - snippet = list( - playlistId = playlist_id, - position = position, - resourceId = list( - kind = "youtube#video", - videoId = video_id + if(is.null(position)){ + body <- list( + snippet = list( + playlistId = playlist_id, + resourceId = list( + kind = "youtube#video", + videoId = video_id + ) ) ) - ) + } else { + body <- list( + snippet = list( + playlistId = playlist_id, + position = position, + resourceId = list( + kind = "youtube#video", + videoId = video_id + ) + ) + ) + } # Make the POST request using tuber_POST_json raw_res <- tuber_POST_json(path = "playlistItems", query = list(part = "snippet"), body = body, ...) diff --git a/R/upload_video.R b/R/upload_video.R index bc15ea9..3ebd006 100644 --- a/R/upload_video.R +++ b/R/upload_video.R @@ -93,7 +93,7 @@ upload_video <- function( yt_check_token() headers <- c( - "Authorization" = paste("Bearer", getOption("google_token")$credentials$access_token), + "Authorization" = paste("Bearer", getOption("google_token")), #$credentials$access_token), "Content-Length" = file.size(file), "Content-Type" = "application/json; charset=utf-8", "X-Upload-Content-Length" = file.size(file), diff --git a/R/yt_oauth.R b/R/yt_oauth.R index 8cee11e..e22f955 100644 --- a/R/yt_oauth.R +++ b/R/yt_oauth.R @@ -36,21 +36,21 @@ #' } yt_oauth <- function(app_id = NULL, app_secret = NULL, scope = "ssl", token = ".httr-oauth", ...) { - if (file.exists(token)) { - google_token <- tryCatch( - suppressWarnings(readRDS(token)), - error = function(e) { - warning(sprintf("Unable to read token from: %s", token)) - NULL - } - ) - } - - if (!file.exists(token) || (file.exists(token) && is.null(google_token))) { - stopifnot(!is.null(app_id), !is.null(app_secret)) + # if (file.exists(token)) { + # google_token <- tryCatch( + # suppressWarnings(readRDS(token)), + # error = function(e) { + # warning(sprintf("Unable to read token from: %s", token)) + # NULL + # } + # ) + # } + + # if (!file.exists(token) || (file.exists(token) && is.null(google_token))) { + # stopifnot(!is.null(app_id), !is.null(app_secret)) myapp <- oauth_app("google", key = app_id, secret = app_secret) scope <- match.arg(scope, c( - "ssl", "basic", "own_account_readonly", + "ssl", "basic", "own_account_readonly", "upload_and_manage_own_videos", "partner_audit", "partner" )) scope_url <- switch(scope, @@ -62,7 +62,7 @@ yt_oauth <- function(app_id = NULL, app_secret = NULL, scope = "ssl", token = ". partner = "https://www.googleapis.com/auth/youtubepartner" ) google_token <- oauth2.0_token(oauth_endpoints("google"), myapp, scope = scope_url, ...) - } - + # } + options(google_token = google_token) } From d87d3bb0abcd20c0638829c28a1d8f37efd9146a Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Wed, 13 Dec 2023 11:43:32 -0600 Subject: [PATCH 06/14] update_video_metadata function added --- NAMESPACE | 3 +- R/tuber.R | 2 +- R/update_video_metadata.R | 65 ++++++++++++++++++++++++++++++++++++ man/add_video_to_playlist.Rd | 2 +- man/update_video_metadata.Rd | 44 ++++++++++++++++++++++++ 5 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 R/update_video_metadata.R create mode 100644 man/update_video_metadata.Rd diff --git a/NAMESPACE b/NAMESPACE index 9d508a9..3fef1d0 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -38,9 +38,10 @@ export(list_regions) export(list_videocats) export(list_videos) export(read_sbv) -export(tuber_check) +export(update_video_metadata) export(upload_caption) export(upload_video) +export(upload_video2) export(yt_authorized) export(yt_get_key) export(yt_oauth) diff --git a/R/tuber.R b/R/tuber.R index 780b3ae..4164c69 100644 --- a/R/tuber.R +++ b/R/tuber.R @@ -233,7 +233,7 @@ tuber_POST_json <- function(path, query, body = "", ...) { config(token = getOption("google_token")), encode = "json", ...) - tuber::tuber_check(req) + tuber_check(req) res <- content(req) res diff --git a/R/update_video_metadata.R b/R/update_video_metadata.R new file mode 100644 index 0000000..360bacc --- /dev/null +++ b/R/update_video_metadata.R @@ -0,0 +1,65 @@ +#' Update a YouTube Video's Metadata +#' +#' This function updates the metadata of an existing YouTube video using the YouTube Data API. +#' +#' @param video_id A character string specifying the ID of the video you want to update. +#' @param title A character string specifying the new title for the video. +#' @param category_id A character string specifying the new category ID for the video. +#' @param description A character string specifying the new description for the video. +#' @param privacy_status A character string specifying the new privacy status for the video ('public', 'private', or 'unlisted'). +#' @param made_for_kids A boolean specifying whether the video is self-declared as made for kids. +#' +#' @return A list containing the server response after the update attempt. +#' @export +#' +#' @examples +#' \dontrun{ +#' update_video_metadata(video_id = "YourVideoID", +#' title = "New Video Title", +#' category_id = "24", +#' description = "New Description", +#' privacy_status = "public", +#' made_for_kids = FALSE) +#' } + +update_video_metadata <- function(video_id, title, category_id, description, privacy_status, made_for_kids) { + # Check for a valid token + yt_check_token() + + # Define the body for the PUT request + body <- list( + id = video_id, + snippet = list( + title = title, + categoryId = category_id, + description = description + ), + status = list( + privacyStatus = privacy_status, + selfDeclaredMadeForKids = made_for_kids + ) + ) + + body_json <- jsonlite::toJSON(body, auto_unbox = TRUE) + + # Use the existing tuber infrastructure to send the PUT request + req <- httr::PUT( + url = "https://www.googleapis.com/youtube/v3/videos", + query = list(key = getOption("google_key"), part = "snippet,status"), + config = httr::config(token = getOption("google_token")), + body = body_json, + httr::add_headers( + `Accept` = "application/json", + `Content-Type` = "application/json" + ) + ) + + # Check for errors + tuber_check(req) + + # Extract and return the content + return(httr::content(req)) +} + +# Example usage: +# update_video_metadata(video_id = "YourVideoID", title = "New Video Title", category_id = "24", description = "New Description", privacy_status = "public", made_for_kids = FALSE) diff --git a/man/add_video_to_playlist.Rd b/man/add_video_to_playlist.Rd index 9149e32..0f1d56e 100644 --- a/man/add_video_to_playlist.Rd +++ b/man/add_video_to_playlist.Rd @@ -4,7 +4,7 @@ \alias{add_video_to_playlist} \title{Add Video to Playlist} \usage{ -add_video_to_playlist(playlist_id, video_id, ...) +add_video_to_playlist(playlist_id, video_id, position = NULL, ...) } \arguments{ \item{playlist_id}{string; Required. The ID of the playlist.} diff --git a/man/update_video_metadata.Rd b/man/update_video_metadata.Rd new file mode 100644 index 0000000..081a14c --- /dev/null +++ b/man/update_video_metadata.Rd @@ -0,0 +1,44 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/update_video_metadata.R +\name{update_video_metadata} +\alias{update_video_metadata} +\title{Update a YouTube Video's Metadata} +\usage{ +update_video_metadata( + video_id, + title, + category_id, + description, + privacy_status, + made_for_kids +) +} +\arguments{ +\item{video_id}{A character string specifying the ID of the video you want to update.} + +\item{title}{A character string specifying the new title for the video.} + +\item{category_id}{A character string specifying the new category ID for the video.} + +\item{description}{A character string specifying the new description for the video.} + +\item{privacy_status}{A character string specifying the new privacy status for the video ('public', 'private', or 'unlisted').} + +\item{made_for_kids}{A boolean specifying whether the video is self-declared as made for kids.} +} +\value{ +A list containing the server response after the update attempt. +} +\description{ +This function updates the metadata of an existing YouTube video using the YouTube Data API. +} +\examples{ +\dontrun{ +update_video_metadata(video_id = "YourVideoID", + title = "New Video Title", + category_id = "24", + description = "New Description", + privacy_status = "public", + made_for_kids = FALSE) +} +} From e3e929b27311d597c16bed0dbc0452ee7c869820 Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Tue, 19 Mar 2024 13:08:23 -0500 Subject: [PATCH 07/14] az --- DESCRIPTION | 2 +- NAMESPACE | 1 - man/tuber.Rd | 18 ++++++++++++++++++ vignettes/tuber-ex.Rmd | 2 +- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index b252a39..021bfb7 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -42,4 +42,4 @@ VignetteBuilder: knitr Encoding: UTF-8 LazyData: true -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.1 diff --git a/NAMESPACE b/NAMESPACE index 3fef1d0..4a9a38b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -41,7 +41,6 @@ export(read_sbv) export(update_video_metadata) export(upload_caption) export(upload_video) -export(upload_video2) export(yt_authorized) export(yt_get_key) export(yt_oauth) diff --git a/man/tuber.Rd b/man/tuber.Rd index 83c5f38..5081163 100644 --- a/man/tuber.Rd +++ b/man/tuber.Rd @@ -9,3 +9,21 @@ \pkg{tuber} provides access to the YouTube API V3 via RESTful calls. } +\seealso{ +Useful links: +\itemize{ + \item \url{https://github.com/soodoku/tuber} + \item Report bugs at \url{https://github.com/soodoku/tuber/issues} +} + +} +\author{ +\strong{Maintainer}: Gaurav Sood \email{gsood07@gmail.com} + +Other contributors: +\itemize{ + \item Kate Lyons \email{k.lyons7@gmail.com} [contributor] + \item John Muschelli \email{muschellij2@gmail.com} [contributor] +} + +} diff --git a/vignettes/tuber-ex.Rmd b/vignettes/tuber-ex.Rmd index 3e1f84a..ed15f20 100644 --- a/vignettes/tuber-ex.Rmd +++ b/vignettes/tuber-ex.Rmd @@ -250,7 +250,7 @@ res = list_channel_resources(filter = c(username = "GoogleDevelopers"), part="id # Parse out channel_id if(!is.null(res$items[[1]]$id)){ - channel_id = res$items[[1]]$id + channel_id <- res$items[[1]]$id } else { stop("User not found") } From ab793c8422c774964750a3f5b61032ee1cb6f389 Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Mon, 2 Oct 2023 08:32:36 -0500 Subject: [PATCH 08/14] Additional functions for playlists. --- R/add_video_to_playlist.R | 38 +++++++++++++++++ R/change_playlist_title.R | 49 ++++++++++++++++++++++ R/create_playlist.R | 33 +++++++++++++++ R/delete_playlist_items.R | 2 +- R/get_playlist_item_ids.R | 74 ++++++++++++++++++++++++++++++++++ R/get_playlist_item_videoids.R | 74 ++++++++++++++++++++++++++++++++++ 6 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 R/add_video_to_playlist.R create mode 100644 R/change_playlist_title.R create mode 100644 R/create_playlist.R create mode 100644 R/get_playlist_item_ids.R create mode 100644 R/get_playlist_item_videoids.R diff --git a/R/add_video_to_playlist.R b/R/add_video_to_playlist.R new file mode 100644 index 0000000..2d504f6 --- /dev/null +++ b/R/add_video_to_playlist.R @@ -0,0 +1,38 @@ +#' Add Video to Playlist +#' +#' @param playlist_id string; Required. The ID of the playlist. +#' @param video_id string; Required. The ID of the video to add. +#' @param ... Additional arguments passed to \code{\link{tuber_POST_json}}. +#' +#' @return Details of the added video in the playlist. +#' +#' @export +#' @references \url{https://developers.google.com/youtube/v3/docs/playlistItems/insert} +#' +#' @examples +#' \dontrun{ +#' +#' # Set API token via yt_oauth() first +#' +#' add_video_to_playlist(playlist_id = "YourPlaylistID", video_id = "2_gLD1jarfU") +#' } + +add_video_to_playlist <- function(playlist_id, video_id, ...) { + + # Prepare the request body + body <- list( + snippet = list( + playlistId = playlist_id, + resourceId = list( + kind = "youtube#video", + videoId = video_id + ) + ) + ) + + # Make the POST request using tuber_POST_json + raw_res <- tuber_POST_json(path = "playlistItems", query = list(part = "snippet"), body = body, ...) + + # Return the response + return(raw_res) +} diff --git a/R/change_playlist_title.R b/R/change_playlist_title.R new file mode 100644 index 0000000..33b2811 --- /dev/null +++ b/R/change_playlist_title.R @@ -0,0 +1,49 @@ +#' Change the title of a YouTube playlist. +#' +#' This function updates the title of an existing YouTube playlist using the YouTube Data API. +#' +#' @param playlist_id A character string specifying the ID of the playlist you want to update. +#' @param new_title A character string specifying the new title for the playlist. +#' +#' @return A list containing the server response after the update attempt. +#' @export +#' +#' @examples +#' \dontrun{ +#' change_playlist_title(playlist_id = "YourPlaylistID", new_title = "New Playlist Title") +#' } +#' + +change_playlist_title <- function(playlist_id, new_title) { + # Check for a valid token + yt_check_token() + + # Define the body for the PUT request + body <- list( + id = playlist_id, + snippet = list(title = new_title) + ) + + body_json <- jsonlite::toJSON(body, auto_unbox = TRUE) + + # Use the existing tuber infrastructure to send the PUT request + req <- httr::PUT( + url = "https://www.googleapis.com/youtube/v3/playlists", + query = list(key = getOption("google_key"), part = "snippet"), + config = httr::config(token = getOption("google_token")), + body = body_json, + httr::add_headers( + `Accept` = "application/json", + `Content-Type` = "application/json" + ) + ) + + # Check for errors + tuber_check(req) + + # Extract and return the content + return(httr::content(req)) +} + +# Example usage: +# change_playlist_title(playlist_id = "PLOfOyOpiu5saim7DsJmz61uCf8igQiTpF", new_title = "Lil' Casey's Top 40 Countdown 2023-09-29") diff --git a/R/create_playlist.R b/R/create_playlist.R new file mode 100644 index 0000000..027dde2 --- /dev/null +++ b/R/create_playlist.R @@ -0,0 +1,33 @@ +#' Create New Playlist +#' +#' @param title string; Required. The title of the playlist. +#' @param description string; Optional. The description of the playlist. +#' @param status string; Optional. Default: 'public'. Can be one of: 'private', 'public', or 'unlisted'. +#' @param ... Additional arguments passed to \code{\link{tuber_POST}}. +#' +#' @return The created playlist's details. +#' +#' @export +#' @references \url{https://developers.google.com/youtube/v3/docs/playlists/insert} +#' +#' @examples +#' \dontrun{ +#' +#' # Set API token via yt_oauth() first +#' +#' create_playlist(title = "My New Playlist", description = "This is a test playlist.") +#' } + +create_playlist <- function(title, status, ...) { + # Prepare the request body + body <- list( + snippet = list(title = title), + status = list(privacyStatus = status) + ) + + # Make the POST request using tuber_POST_json + raw_res <- tuber_POST_json(path = "playlists", query = list(part = "snippet,status"), body = body, ...) + + # Return the response + return(raw_res) +} diff --git a/R/delete_playlist_items.R b/R/delete_playlist_items.R index 04cd43d..c6aaebb 100644 --- a/R/delete_playlist_items.R +++ b/R/delete_playlist_items.R @@ -13,7 +13,7 @@ #' #' # Set API token via yt_oauth() first #' -#' delete_playlist_items(id = "y3ElXcEME3lSISz6izkWVT5GvxjPu8pA") +#' delete_playlist_items(id = "YourPlaylistItemID") #' } delete_playlist_items <- function(id = NULL, ...) { diff --git a/R/get_playlist_item_ids.R b/R/get_playlist_item_ids.R new file mode 100644 index 0000000..ef6219b --- /dev/null +++ b/R/get_playlist_item_ids.R @@ -0,0 +1,74 @@ +#' Get Playlist Item IDs +#' +#' @param filter string; Required. +#' named vector of length 1 +#' potential names of the entry in the vector: +#' \code{item_id}: comma-separated list of one or more unique playlist item IDs. +#' \code{playlist_id}: YouTube playlist ID. +#' +#' @param part Required. Comma separated string including one or more of the +#' following: \code{contentDetails, id, snippet, status}. Default: +#' \code{contentDetails}. +#' @param max_results Maximum number of items that should be returned. +#' Integer. Optional. Default is 50. +#' If over 50, all the results are returned. +#' @param simplify returns a data.frame rather than a list. +#' @param page_token specific page in the result set that should be +#' returned, optional +#' @param video_id Optional. request should return only the playlist +#' items that contain the specified video. +#' @param \dots Additional arguments passed to \code{\link{tuber_GET}}. +#' +#' @return playlist items +#' @export +#' @references \url{https://developers.google.com/youtube/v3/docs/playlists/list} +#' +#' @examples +#' \dontrun{ +#' +#' # Set API token via yt_oauth() first +#' +#' get_playlist_items(filter = +#' c(playlist_id = "PLrEnWoR732-CN09YykVof2lxdI3MLOZda")) +#' get_playlist_items(filter = +#' c(playlist_id = "PL0fOlXVeVW9QMO3GoESky4yDgQfK2SsXN"), +#' max_results = 51) +#' } + +get_playlist_item_ids <- function(filter = NULL, part = "contentDetails", + max_results = 50, video_id = NULL, + page_token = NULL, simplify = TRUE, ...) { + + if (max_results < 0 || max_results > 50) { + stop("max_results must be a value between 0 and 50.") + } + + valid_filters <- c("item_id", "playlist_id") + if (!(names(filter) %in% valid_filters)) { + stop("filter can only take one of the following values: item_id, playlist_id.") + } + + if (length(filter) != 1) { + stop("filter must be a vector of length 1.") + } + + translate_filter <- c(item_id = "id", playlist_id = "playlistId") + filter_name <- translate_filter[names(filter)] + names(filter) <- filter_name + + querylist <- list(part = part, + maxResults = max(min(max_results, 50), 1), + pageToken = page_token, videoId = video_id) + querylist <- c(querylist, filter) + + res <- tuber_GET("playlistItems", querylist, ...) + res_items <- res$items + + item_ids <- rep("", length(res_items)) + for(i in 1:length(res_items)){ + item_ids[i] <- res_items[[i]]$id + } + return(item_ids) +} + + diff --git a/R/get_playlist_item_videoids.R b/R/get_playlist_item_videoids.R new file mode 100644 index 0000000..01ec76b --- /dev/null +++ b/R/get_playlist_item_videoids.R @@ -0,0 +1,74 @@ +#' Get Playlist Item Video IDs +#' +#' @param filter string; Required. +#' named vector of length 1 +#' potential names of the entry in the vector: +#' \code{item_id}: comma-separated list of one or more unique playlist item IDs. +#' \code{playlist_id}: YouTube playlist ID. +#' +#' @param part Required. Comma separated string including one or more of the +#' following: \code{contentDetails, id, snippet, status}. Default: +#' \code{contentDetails}. +#' @param max_results Maximum number of items that should be returned. +#' Integer. Optional. Default is 50. +#' If over 50, all the results are returned. +#' @param simplify returns a data.frame rather than a list. +#' @param page_token specific page in the result set that should be +#' returned, optional +#' @param video_id Optional. request should return only the playlist +#' items that contain the specified video. +#' @param \dots Additional arguments passed to \code{\link{tuber_GET}}. +#' +#' @return playlist items +#' @export +#' @references \url{https://developers.google.com/youtube/v3/docs/playlists/list} +#' +#' @examples +#' \dontrun{ +#' +#' # Set API token via yt_oauth() first +#' +#' get_playlist_items(filter = +#' c(playlist_id = "YourPlaylistID")) +#' get_playlist_items(filter = +#' c(playlist_id = "YourPlaylistID"), +#' max_results = 51) +#' } + +get_playlist_item_videoids <- function(filter = NULL, part = "contentDetails", + max_results = 50, video_id = NULL, + page_token = NULL, simplify = TRUE, ...) { + + if (max_results < 0 || max_results > 50) { + stop("max_results must be a value between 0 and 50.") + } + + valid_filters <- c("item_id", "playlist_id") + if (!(names(filter) %in% valid_filters)) { + stop("filter can only take one of the following values: item_id, playlist_id.") + } + + if (length(filter) != 1) { + stop("filter must be a vector of length 1.") + } + + translate_filter <- c(item_id = "id", playlist_id = "playlistId") + filter_name <- translate_filter[names(filter)] + names(filter) <- filter_name + + querylist <- list(part = part, + maxResults = max(min(max_results, 50), 1), + pageToken = page_token, videoId = video_id) + querylist <- c(querylist, filter) + + res <- tuber_GET("playlistItems", querylist, ...) + res_items <- res$items + + item_ids <- rep("", length(res_items)) + for(i in 1:length(res_items)){ + item_ids[i] <- res_items[[i]]$contentDetails$videoId + } + return(item_ids) +} + + From 1ffc24ed516a7e0015481e8b4a8729812ad973c7 Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Tue, 3 Oct 2023 10:18:30 -0500 Subject: [PATCH 09/14] squashed some warnings and notes from devtools::check() --- NAMESPACE | 5 +++ R/create_playlist.R | 5 +-- R/get_video_details.R | 2 + R/tuber.R | 25 +++++++++++++ R/upload_video.R | 2 +- R/yt_search.R | 10 ++++- man/add_video_to_playlist.Rd | 32 ++++++++++++++++ man/change_playlist_title.Rd | 25 +++++++++++++ man/create_playlist.Rd | 34 +++++++++++++++++ man/delete_playlist_items.Rd | 2 +- man/get_playlist_item_ids.Rd | 62 +++++++++++++++++++++++++++++++ man/get_playlist_item_videoids.Rd | 62 +++++++++++++++++++++++++++++++ man/tuber_POST_json.Rd | 23 ++++++++++++ man/yt_search.Rd | 14 +++++-- 14 files changed, 294 insertions(+), 9 deletions(-) create mode 100644 man/add_video_to_playlist.Rd create mode 100644 man/change_playlist_title.Rd create mode 100644 man/create_playlist.Rd create mode 100644 man/get_playlist_item_ids.Rd create mode 100644 man/get_playlist_item_videoids.Rd create mode 100644 man/tuber_POST_json.Rd diff --git a/NAMESPACE b/NAMESPACE index 8109b46..da2500d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,6 +1,9 @@ # Generated by roxygen2: do not edit by hand export("%>%") +export(add_video_to_playlist) +export(change_playlist_title) +export(create_playlist) export(delete_captions) export(delete_channel_sections) export(delete_comments) @@ -13,6 +16,8 @@ export(get_captions) export(get_channel_stats) export(get_comment_threads) export(get_comments) +export(get_playlist_item_ids) +export(get_playlist_item_videoids) export(get_playlist_items) export(get_playlists) export(get_related_videos) diff --git a/R/create_playlist.R b/R/create_playlist.R index 027dde2..07cdf8a 100644 --- a/R/create_playlist.R +++ b/R/create_playlist.R @@ -17,11 +17,10 @@ #' #' create_playlist(title = "My New Playlist", description = "This is a test playlist.") #' } - -create_playlist <- function(title, status, ...) { +create_playlist <- function(title, description, status, ...) { # Prepare the request body body <- list( - snippet = list(title = title), + snippet = list(title = title, description = description), status = list(privacyStatus = status) ) diff --git a/R/get_video_details.R b/R/get_video_details.R index c6dd926..cc7c0bb 100644 --- a/R/get_video_details.R +++ b/R/get_video_details.R @@ -7,6 +7,8 @@ conditional_unnest_wider <- function(data_input, var) { } } +# Added to squash notes on devtools check. +utils::globalVariables(c("kind", "etag", "items", "snippet")) json_to_df <- function(res) { intermediate <- res %>% diff --git a/R/tuber.R b/R/tuber.R index b46dd82..4164c69 100644 --- a/R/tuber.R +++ b/R/tuber.R @@ -214,6 +214,31 @@ tuber_POST <- function(path, query, body = "", ...) { res } +#' +#' POST encoded in json +#' +#' @param path path to specific API request URL +#' @param query query list +#' @param body passing image through body +#' @param \dots Additional arguments passed to \code{\link[httr]{GET}}. +#' +#' @return list + +tuber_POST_json <- function(path, query, body = "", ...) { + + yt_check_token() + + req <- httr::POST("https://www.googleapis.com", path = paste0("youtube/v3/", path), + body = body, query = query, + config(token = getOption("google_token")), + encode = "json", ...) + + tuber_check(req) + res <- content(req) + + res +} + #' #' DELETE #' diff --git a/R/upload_video.R b/R/upload_video.R index f2bc276..8769d54 100644 --- a/R/upload_video.R +++ b/R/upload_video.R @@ -103,7 +103,7 @@ upload_video <- function( resumable_upload_url <- "https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=parts" resumable_upload_req <- httr::POST(resumable_upload_url, config(token = getOption("google_token")), - add_headers(headers), + httr::add_headers(headers), ... ) diff --git a/R/yt_search.R b/R/yt_search.R index 88a9290..9745044 100644 --- a/R/yt_search.R +++ b/R/yt_search.R @@ -33,6 +33,13 @@ #' For instance, "1970-01-01T00:00:00Z" #' @param published_before Character. Optional. RFC 339 Format. #' For instance, "1970-01-01T00:00:00Z" +#' @param relevance_language Character. Optional. The relevance_language +#' argument instructs the API to return search results that are most relevant to +#' the specified language. The parameter value is typically an ISO 639-1 +#' two-letter language code. However, you should use the values zh-Hans for +#' simplified Chinese and zh-Hant for traditional Chinese. Please note that +#' results in other languages will still be returned if they are highly relevant +#' to the search query term. #' @param type Character. Optional. Takes one of three values: #' \code{'video', 'channel', 'playlist'}. Default is \code{'video'}. #' @param video_caption Character. Optional. Takes one of three values: @@ -44,6 +51,8 @@ #' @param video_syndicated Character. Optional. Takes one of two values: #' \code{'any'} (return all videos; Default), \code{'true'} #' (return only syndicated videos) +#' @param region_code Character. Required. Has to be a ISO 3166-1 alpha-2 code +#' (see \url{https://www.iso.org/obp/ui/#search}). #' @param video_definition Character. Optional. #' Takes one of three values: \code{'any'} (return all videos; Default), #' \code{'high', 'standard'} @@ -51,7 +60,6 @@ #' Takes one of three values: \code{'any'} (return all videos; Default), #' \code{'creativeCommon'} (return videos with Creative Commons #' license), \code{'youtube'} (return videos with standard YouTube license). -#' @param region_code Character. i18nRegion. Default is NULL. #' @param relevance_language Character. Default is "en". #' @param simplify Boolean. Return a data.frame if \code{TRUE}. #' Default is \code{TRUE}. diff --git a/man/add_video_to_playlist.Rd b/man/add_video_to_playlist.Rd new file mode 100644 index 0000000..9149e32 --- /dev/null +++ b/man/add_video_to_playlist.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/add_video_to_playlist.R +\name{add_video_to_playlist} +\alias{add_video_to_playlist} +\title{Add Video to Playlist} +\usage{ +add_video_to_playlist(playlist_id, video_id, ...) +} +\arguments{ +\item{playlist_id}{string; Required. The ID of the playlist.} + +\item{video_id}{string; Required. The ID of the video to add.} + +\item{...}{Additional arguments passed to \code{\link{tuber_POST_json}}.} +} +\value{ +Details of the added video in the playlist. +} +\description{ +Add Video to Playlist +} +\examples{ +\dontrun{ + +# Set API token via yt_oauth() first + +add_video_to_playlist(playlist_id = "YourPlaylistID", video_id = "2_gLD1jarfU") +} +} +\references{ +\url{https://developers.google.com/youtube/v3/docs/playlistItems/insert} +} diff --git a/man/change_playlist_title.Rd b/man/change_playlist_title.Rd new file mode 100644 index 0000000..afdd48d --- /dev/null +++ b/man/change_playlist_title.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/change_playlist_title.R +\name{change_playlist_title} +\alias{change_playlist_title} +\title{Change the title of a YouTube playlist.} +\usage{ +change_playlist_title(playlist_id, new_title) +} +\arguments{ +\item{playlist_id}{A character string specifying the ID of the playlist you want to update.} + +\item{new_title}{A character string specifying the new title for the playlist.} +} +\value{ +A list containing the server response after the update attempt. +} +\description{ +This function updates the title of an existing YouTube playlist using the YouTube Data API. +} +\examples{ +\dontrun{ +change_playlist_title(playlist_id = "YourPlaylistID", new_title = "New Playlist Title") +} + +} diff --git a/man/create_playlist.Rd b/man/create_playlist.Rd new file mode 100644 index 0000000..6166068 --- /dev/null +++ b/man/create_playlist.Rd @@ -0,0 +1,34 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_playlist.R +\name{create_playlist} +\alias{create_playlist} +\title{Create New Playlist} +\usage{ +create_playlist(title, description, status, ...) +} +\arguments{ +\item{title}{string; Required. The title of the playlist.} + +\item{description}{string; Optional. The description of the playlist.} + +\item{status}{string; Optional. Default: 'public'. Can be one of: 'private', 'public', or 'unlisted'.} + +\item{...}{Additional arguments passed to \code{\link{tuber_POST}}.} +} +\value{ +The created playlist's details. +} +\description{ +Create New Playlist +} +\examples{ +\dontrun{ + +# Set API token via yt_oauth() first + +create_playlist(title = "My New Playlist", description = "This is a test playlist.") +} +} +\references{ +\url{https://developers.google.com/youtube/v3/docs/playlists/insert} +} diff --git a/man/delete_playlist_items.Rd b/man/delete_playlist_items.Rd index 065f9f0..1eae165 100644 --- a/man/delete_playlist_items.Rd +++ b/man/delete_playlist_items.Rd @@ -19,7 +19,7 @@ Delete a Playlist Item # Set API token via yt_oauth() first -delete_playlist_items(id = "y3ElXcEME3lSISz6izkWVT5GvxjPu8pA") +delete_playlist_items(id = "YourPlaylistItemID") } } \references{ diff --git a/man/get_playlist_item_ids.Rd b/man/get_playlist_item_ids.Rd new file mode 100644 index 0000000..97130fe --- /dev/null +++ b/man/get_playlist_item_ids.Rd @@ -0,0 +1,62 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/get_playlist_item_ids.R +\name{get_playlist_item_ids} +\alias{get_playlist_item_ids} +\title{Get Playlist Item IDs} +\usage{ +get_playlist_item_ids( + filter = NULL, + part = "contentDetails", + max_results = 50, + video_id = NULL, + page_token = NULL, + simplify = TRUE, + ... +) +} +\arguments{ +\item{filter}{string; Required. +named vector of length 1 +potential names of the entry in the vector: +\code{item_id}: comma-separated list of one or more unique playlist item IDs. +\code{playlist_id}: YouTube playlist ID.} + +\item{part}{Required. Comma separated string including one or more of the +following: \code{contentDetails, id, snippet, status}. Default: +\code{contentDetails}.} + +\item{max_results}{Maximum number of items that should be returned. +Integer. Optional. Default is 50. +If over 50, all the results are returned.} + +\item{video_id}{Optional. request should return only the playlist +items that contain the specified video.} + +\item{page_token}{specific page in the result set that should be +returned, optional} + +\item{simplify}{returns a data.frame rather than a list.} + +\item{\dots}{Additional arguments passed to \code{\link{tuber_GET}}.} +} +\value{ +playlist items +} +\description{ +Get Playlist Item IDs +} +\examples{ +\dontrun{ + +# Set API token via yt_oauth() first + +get_playlist_items(filter = + c(playlist_id = "PLrEnWoR732-CN09YykVof2lxdI3MLOZda")) +get_playlist_items(filter = + c(playlist_id = "PL0fOlXVeVW9QMO3GoESky4yDgQfK2SsXN"), + max_results = 51) +} +} +\references{ +\url{https://developers.google.com/youtube/v3/docs/playlists/list} +} diff --git a/man/get_playlist_item_videoids.Rd b/man/get_playlist_item_videoids.Rd new file mode 100644 index 0000000..cbaa038 --- /dev/null +++ b/man/get_playlist_item_videoids.Rd @@ -0,0 +1,62 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/get_playlist_item_videoids.R +\name{get_playlist_item_videoids} +\alias{get_playlist_item_videoids} +\title{Get Playlist Item Video IDs} +\usage{ +get_playlist_item_videoids( + filter = NULL, + part = "contentDetails", + max_results = 50, + video_id = NULL, + page_token = NULL, + simplify = TRUE, + ... +) +} +\arguments{ +\item{filter}{string; Required. +named vector of length 1 +potential names of the entry in the vector: +\code{item_id}: comma-separated list of one or more unique playlist item IDs. +\code{playlist_id}: YouTube playlist ID.} + +\item{part}{Required. Comma separated string including one or more of the +following: \code{contentDetails, id, snippet, status}. Default: +\code{contentDetails}.} + +\item{max_results}{Maximum number of items that should be returned. +Integer. Optional. Default is 50. +If over 50, all the results are returned.} + +\item{video_id}{Optional. request should return only the playlist +items that contain the specified video.} + +\item{page_token}{specific page in the result set that should be +returned, optional} + +\item{simplify}{returns a data.frame rather than a list.} + +\item{\dots}{Additional arguments passed to \code{\link{tuber_GET}}.} +} +\value{ +playlist items +} +\description{ +Get Playlist Item Video IDs +} +\examples{ +\dontrun{ + +# Set API token via yt_oauth() first + +get_playlist_items(filter = + c(playlist_id = "YourPlaylistID")) +get_playlist_items(filter = + c(playlist_id = "YourPlaylistID"), + max_results = 51) +} +} +\references{ +\url{https://developers.google.com/youtube/v3/docs/playlists/list} +} diff --git a/man/tuber_POST_json.Rd b/man/tuber_POST_json.Rd new file mode 100644 index 0000000..25dc41d --- /dev/null +++ b/man/tuber_POST_json.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/tuber.R +\name{tuber_POST_json} +\alias{tuber_POST_json} +\title{POST encoded in json} +\usage{ +tuber_POST_json(path, query, body = "", ...) +} +\arguments{ +\item{path}{path to specific API request URL} + +\item{query}{query list} + +\item{body}{passing image through body} + +\item{\dots}{Additional arguments passed to \code{\link[httr]{GET}}.} +} +\value{ +list +} +\description{ +POST encoded in json +} diff --git a/man/yt_search.Rd b/man/yt_search.Rd index 2a14640..423410f 100644 --- a/man/yt_search.Rd +++ b/man/yt_search.Rd @@ -88,9 +88,17 @@ license), \code{'youtube'} (return videos with standard YouTube license).} \code{'any'} (return all videos; Default), \code{'true'} (return only syndicated videos)} -\item{region_code}{Character. i18nRegion. Default is NULL.} - -\item{relevance_language}{Character. Default is "en".} +\item{region_code}{Character. Required. Has to be a ISO 3166-1 alpha-2 code +(see \url{https://www.iso.org/obp/ui/#search}).} + +\item{relevance_language}{Character. Optional. The relevance_language +argument instructs the API to return search results that are most relevant to +the specified language. The parameter value is typically an ISO 639-1 +two-letter language code. However, you should use the values zh-Hans for +simplified Chinese and zh-Hant for traditional Chinese. Please note that +results in other languages will still be returned if they are highly relevant +to the search query term.} +>>>>>>> 07e7e3f (squashed some warnings and notes from devtools::check()) \item{video_type}{Character. Optional. Takes one of three values: \code{'any'} (return all videos; Default), \code{'episode'} From f9d95effbe5f209a5c5015ade4c509bbc71cb5c0 Mon Sep 17 00:00:00 2001 From: Troy Hernandez Date: Thu, 9 Nov 2023 17:11:01 -0600 Subject: [PATCH 10/14] added position to add_video_to_playlist fixed upload_video. --- R/add_video_to_playlist.R | 3 ++- R/get_all_channel_video_stats.R | 2 +- R/tuber.R | 2 +- R/upload_video.R | 18 +++++++++--------- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/R/add_video_to_playlist.R b/R/add_video_to_playlist.R index 2d504f6..33f195d 100644 --- a/R/add_video_to_playlist.R +++ b/R/add_video_to_playlist.R @@ -17,12 +17,13 @@ #' add_video_to_playlist(playlist_id = "YourPlaylistID", video_id = "2_gLD1jarfU") #' } -add_video_to_playlist <- function(playlist_id, video_id, ...) { +add_video_to_playlist <- function(playlist_id, video_id, position, ...) { # Prepare the request body body <- list( snippet = list( playlistId = playlist_id, + position = position, resourceId = list( kind = "youtube#video", videoId = video_id diff --git a/R/get_all_channel_video_stats.R b/R/get_all_channel_video_stats.R index f0e5abb..10763a6 100644 --- a/R/get_all_channel_video_stats.R +++ b/R/get_all_channel_video_stats.R @@ -32,7 +32,7 @@ get_all_channel_video_stats <- function(channel_id = NULL, mine = FALSE, ...) { channel_resources <- list_channel_resources(filter = list(channel_id = channel_id), part = "contentDetails") playlist_id <- channel_resources$items$contentDetails$relatedPlaylists$uploads - playlist_items <- get_playlist_items(filter = list(playlist_id = playlist_id), max_results = 100) + playlist_items <- get_playlist_items(filter = list(playlist_id = playlist_id), max_results = 50) vid_ids <- playlist_items$contentDetails$videoId res <- lapply(vid_ids, get_stats) diff --git a/R/tuber.R b/R/tuber.R index 4164c69..780b3ae 100644 --- a/R/tuber.R +++ b/R/tuber.R @@ -233,7 +233,7 @@ tuber_POST_json <- function(path, query, body = "", ...) { config(token = getOption("google_token")), encode = "json", ...) - tuber_check(req) + tuber::tuber_check(req) res <- content(req) res diff --git a/R/upload_video.R b/R/upload_video.R index 8769d54..d6348bd 100644 --- a/R/upload_video.R +++ b/R/upload_video.R @@ -1,6 +1,6 @@ #' Upload Video to Youtube #' -#' @param file Filename of the video locally +#' @param file_path Filename of the video locally #' @param snippet Additional fields for the video, including `description` #' and `title`. See #' \url{https://developers.google.com/youtube/v3/docs/videos#resource} for @@ -38,7 +38,7 @@ #' status = list(privacyStatus = "private") upload_video <- function( - file, + file_path, snippet = NULL, status = list(privacyStatus = "public"), query = NULL, @@ -86,18 +86,18 @@ upload_video <- function( writeLines(body, metadata) body <- list( - metadata = upload_file(metadata, type = "application/json; charset=UTF-8"), - y = httr::upload_file(file) + metadata = httr::upload_file(metadata, type = "application/json; charset=UTF-8"), + y = httr::upload_file(file_path) ) yt_check_token() headers <- c( - "Authorization" = paste("Bearer", getOption("google_token")), - "Content-Length" = file.size(file), + "Authorization" = paste("Bearer", getOption("google_token")$credentials$access_token), + "Content-Length" = file.size(file_path), "Content-Type" = "application/json; charset=utf-8", - "X-Upload-Content-Length" = file.size(file), - "X-Upload-Content-Type" = mime::guess_type(file) + "X-Upload-Content-Length" = file.size(file_path), + "X-Upload-Content-Type" = mime::guess_type(file_path) ) resumable_upload_url <- "https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=parts" @@ -114,7 +114,7 @@ upload_video <- function( upload_url <- httr::headers(resumable_upload_req)$`x-guploader-uploadid` upload_req <- httr::PUT(upload_url, - body = httr::upload_file(file), + body = httr::upload_file(file_path), config(token = getOption("google_token")), ... ) From 2540df33904f67781de506e3e41cea7cf696ecaa Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Fri, 10 Nov 2023 08:28:23 -0600 Subject: [PATCH 11/14] Reverting to old file name for consistency. --- R/upload_video.R | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/R/upload_video.R b/R/upload_video.R index d6348bd..bc15ea9 100644 --- a/R/upload_video.R +++ b/R/upload_video.R @@ -1,6 +1,6 @@ #' Upload Video to Youtube #' -#' @param file_path Filename of the video locally +#' @param file Filename of the video locally #' @param snippet Additional fields for the video, including `description` #' and `title`. See #' \url{https://developers.google.com/youtube/v3/docs/videos#resource} for @@ -38,7 +38,7 @@ #' status = list(privacyStatus = "private") upload_video <- function( - file_path, + file, snippet = NULL, status = list(privacyStatus = "public"), query = NULL, @@ -87,17 +87,17 @@ upload_video <- function( body <- list( metadata = httr::upload_file(metadata, type = "application/json; charset=UTF-8"), - y = httr::upload_file(file_path) + y = httr::upload_file(file) ) yt_check_token() headers <- c( "Authorization" = paste("Bearer", getOption("google_token")$credentials$access_token), - "Content-Length" = file.size(file_path), + "Content-Length" = file.size(file), "Content-Type" = "application/json; charset=utf-8", - "X-Upload-Content-Length" = file.size(file_path), - "X-Upload-Content-Type" = mime::guess_type(file_path) + "X-Upload-Content-Length" = file.size(file), + "X-Upload-Content-Type" = mime::guess_type(file) ) resumable_upload_url <- "https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=parts" @@ -114,7 +114,7 @@ upload_video <- function( upload_url <- httr::headers(resumable_upload_req)$`x-guploader-uploadid` upload_req <- httr::PUT(upload_url, - body = httr::upload_file(file_path), + body = httr::upload_file(file), config(token = getOption("google_token")), ... ) From 16783c0933c2f964e925c91d814b549916ec3ac5 Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Tue, 14 Nov 2023 08:48:16 -0600 Subject: [PATCH 12/14] yt_oauth fixed to handle second logins. readRDS on line 41 returns a list, not an environment variable. httr::oauth2.0_token functionality made to mirror spotifyr. --- NAMESPACE | 1 + R/add_video_to_playlist.R | 30 +++++++++++++++++++++--------- R/upload_video.R | 2 +- R/yt_oauth.R | 30 +++++++++++++++--------------- 4 files changed, 38 insertions(+), 25 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index da2500d..9d508a9 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -38,6 +38,7 @@ export(list_regions) export(list_videocats) export(list_videos) export(read_sbv) +export(tuber_check) export(upload_caption) export(upload_video) export(yt_authorized) diff --git a/R/add_video_to_playlist.R b/R/add_video_to_playlist.R index 33f195d..06c64c7 100644 --- a/R/add_video_to_playlist.R +++ b/R/add_video_to_playlist.R @@ -17,19 +17,31 @@ #' add_video_to_playlist(playlist_id = "YourPlaylistID", video_id = "2_gLD1jarfU") #' } -add_video_to_playlist <- function(playlist_id, video_id, position, ...) { +add_video_to_playlist <- function(playlist_id, video_id, position = NULL, ...) { # Prepare the request body - body <- list( - snippet = list( - playlistId = playlist_id, - position = position, - resourceId = list( - kind = "youtube#video", - videoId = video_id + if(is.null(position)){ + body <- list( + snippet = list( + playlistId = playlist_id, + resourceId = list( + kind = "youtube#video", + videoId = video_id + ) ) ) - ) + } else { + body <- list( + snippet = list( + playlistId = playlist_id, + position = position, + resourceId = list( + kind = "youtube#video", + videoId = video_id + ) + ) + ) + } # Make the POST request using tuber_POST_json raw_res <- tuber_POST_json(path = "playlistItems", query = list(part = "snippet"), body = body, ...) diff --git a/R/upload_video.R b/R/upload_video.R index bc15ea9..3ebd006 100644 --- a/R/upload_video.R +++ b/R/upload_video.R @@ -93,7 +93,7 @@ upload_video <- function( yt_check_token() headers <- c( - "Authorization" = paste("Bearer", getOption("google_token")$credentials$access_token), + "Authorization" = paste("Bearer", getOption("google_token")), #$credentials$access_token), "Content-Length" = file.size(file), "Content-Type" = "application/json; charset=utf-8", "X-Upload-Content-Length" = file.size(file), diff --git a/R/yt_oauth.R b/R/yt_oauth.R index 8cee11e..e22f955 100644 --- a/R/yt_oauth.R +++ b/R/yt_oauth.R @@ -36,21 +36,21 @@ #' } yt_oauth <- function(app_id = NULL, app_secret = NULL, scope = "ssl", token = ".httr-oauth", ...) { - if (file.exists(token)) { - google_token <- tryCatch( - suppressWarnings(readRDS(token)), - error = function(e) { - warning(sprintf("Unable to read token from: %s", token)) - NULL - } - ) - } - - if (!file.exists(token) || (file.exists(token) && is.null(google_token))) { - stopifnot(!is.null(app_id), !is.null(app_secret)) + # if (file.exists(token)) { + # google_token <- tryCatch( + # suppressWarnings(readRDS(token)), + # error = function(e) { + # warning(sprintf("Unable to read token from: %s", token)) + # NULL + # } + # ) + # } + + # if (!file.exists(token) || (file.exists(token) && is.null(google_token))) { + # stopifnot(!is.null(app_id), !is.null(app_secret)) myapp <- oauth_app("google", key = app_id, secret = app_secret) scope <- match.arg(scope, c( - "ssl", "basic", "own_account_readonly", + "ssl", "basic", "own_account_readonly", "upload_and_manage_own_videos", "partner_audit", "partner" )) scope_url <- switch(scope, @@ -62,7 +62,7 @@ yt_oauth <- function(app_id = NULL, app_secret = NULL, scope = "ssl", token = ". partner = "https://www.googleapis.com/auth/youtubepartner" ) google_token <- oauth2.0_token(oauth_endpoints("google"), myapp, scope = scope_url, ...) - } - + # } + options(google_token = google_token) } From 6bb76c0ff0f2532d3d817c72522602d700645ad3 Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Wed, 13 Dec 2023 11:43:32 -0600 Subject: [PATCH 13/14] update_video_metadata function added --- NAMESPACE | 3 +- R/tuber.R | 2 +- R/update_video_metadata.R | 65 ++++++++++++++++++++++++++++++++++++ man/add_video_to_playlist.Rd | 2 +- man/update_video_metadata.Rd | 44 ++++++++++++++++++++++++ 5 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 R/update_video_metadata.R create mode 100644 man/update_video_metadata.Rd diff --git a/NAMESPACE b/NAMESPACE index 9d508a9..3fef1d0 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -38,9 +38,10 @@ export(list_regions) export(list_videocats) export(list_videos) export(read_sbv) -export(tuber_check) +export(update_video_metadata) export(upload_caption) export(upload_video) +export(upload_video2) export(yt_authorized) export(yt_get_key) export(yt_oauth) diff --git a/R/tuber.R b/R/tuber.R index 780b3ae..4164c69 100644 --- a/R/tuber.R +++ b/R/tuber.R @@ -233,7 +233,7 @@ tuber_POST_json <- function(path, query, body = "", ...) { config(token = getOption("google_token")), encode = "json", ...) - tuber::tuber_check(req) + tuber_check(req) res <- content(req) res diff --git a/R/update_video_metadata.R b/R/update_video_metadata.R new file mode 100644 index 0000000..360bacc --- /dev/null +++ b/R/update_video_metadata.R @@ -0,0 +1,65 @@ +#' Update a YouTube Video's Metadata +#' +#' This function updates the metadata of an existing YouTube video using the YouTube Data API. +#' +#' @param video_id A character string specifying the ID of the video you want to update. +#' @param title A character string specifying the new title for the video. +#' @param category_id A character string specifying the new category ID for the video. +#' @param description A character string specifying the new description for the video. +#' @param privacy_status A character string specifying the new privacy status for the video ('public', 'private', or 'unlisted'). +#' @param made_for_kids A boolean specifying whether the video is self-declared as made for kids. +#' +#' @return A list containing the server response after the update attempt. +#' @export +#' +#' @examples +#' \dontrun{ +#' update_video_metadata(video_id = "YourVideoID", +#' title = "New Video Title", +#' category_id = "24", +#' description = "New Description", +#' privacy_status = "public", +#' made_for_kids = FALSE) +#' } + +update_video_metadata <- function(video_id, title, category_id, description, privacy_status, made_for_kids) { + # Check for a valid token + yt_check_token() + + # Define the body for the PUT request + body <- list( + id = video_id, + snippet = list( + title = title, + categoryId = category_id, + description = description + ), + status = list( + privacyStatus = privacy_status, + selfDeclaredMadeForKids = made_for_kids + ) + ) + + body_json <- jsonlite::toJSON(body, auto_unbox = TRUE) + + # Use the existing tuber infrastructure to send the PUT request + req <- httr::PUT( + url = "https://www.googleapis.com/youtube/v3/videos", + query = list(key = getOption("google_key"), part = "snippet,status"), + config = httr::config(token = getOption("google_token")), + body = body_json, + httr::add_headers( + `Accept` = "application/json", + `Content-Type` = "application/json" + ) + ) + + # Check for errors + tuber_check(req) + + # Extract and return the content + return(httr::content(req)) +} + +# Example usage: +# update_video_metadata(video_id = "YourVideoID", title = "New Video Title", category_id = "24", description = "New Description", privacy_status = "public", made_for_kids = FALSE) diff --git a/man/add_video_to_playlist.Rd b/man/add_video_to_playlist.Rd index 9149e32..0f1d56e 100644 --- a/man/add_video_to_playlist.Rd +++ b/man/add_video_to_playlist.Rd @@ -4,7 +4,7 @@ \alias{add_video_to_playlist} \title{Add Video to Playlist} \usage{ -add_video_to_playlist(playlist_id, video_id, ...) +add_video_to_playlist(playlist_id, video_id, position = NULL, ...) } \arguments{ \item{playlist_id}{string; Required. The ID of the playlist.} diff --git a/man/update_video_metadata.Rd b/man/update_video_metadata.Rd new file mode 100644 index 0000000..081a14c --- /dev/null +++ b/man/update_video_metadata.Rd @@ -0,0 +1,44 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/update_video_metadata.R +\name{update_video_metadata} +\alias{update_video_metadata} +\title{Update a YouTube Video's Metadata} +\usage{ +update_video_metadata( + video_id, + title, + category_id, + description, + privacy_status, + made_for_kids +) +} +\arguments{ +\item{video_id}{A character string specifying the ID of the video you want to update.} + +\item{title}{A character string specifying the new title for the video.} + +\item{category_id}{A character string specifying the new category ID for the video.} + +\item{description}{A character string specifying the new description for the video.} + +\item{privacy_status}{A character string specifying the new privacy status for the video ('public', 'private', or 'unlisted').} + +\item{made_for_kids}{A boolean specifying whether the video is self-declared as made for kids.} +} +\value{ +A list containing the server response after the update attempt. +} +\description{ +This function updates the metadata of an existing YouTube video using the YouTube Data API. +} +\examples{ +\dontrun{ +update_video_metadata(video_id = "YourVideoID", + title = "New Video Title", + category_id = "24", + description = "New Description", + privacy_status = "public", + made_for_kids = FALSE) +} +} From bd888b2414022ac87754d41dfe18c6771f81dff9 Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Tue, 19 Mar 2024 13:08:23 -0500 Subject: [PATCH 14/14] az --- NAMESPACE | 1 - vignettes/tuber-ex.Rmd | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 3fef1d0..4a9a38b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -41,7 +41,6 @@ export(read_sbv) export(update_video_metadata) export(upload_caption) export(upload_video) -export(upload_video2) export(yt_authorized) export(yt_get_key) export(yt_oauth) diff --git a/vignettes/tuber-ex.Rmd b/vignettes/tuber-ex.Rmd index 3e1f84a..ed15f20 100644 --- a/vignettes/tuber-ex.Rmd +++ b/vignettes/tuber-ex.Rmd @@ -250,7 +250,7 @@ res = list_channel_resources(filter = c(username = "GoogleDevelopers"), part="id # Parse out channel_id if(!is.null(res$items[[1]]$id)){ - channel_id = res$items[[1]]$id + channel_id <- res$items[[1]]$id } else { stop("User not found") }