--- title: "Batch Processing & Quota Management" author: "tuber maintainers" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Batch Processing & Quota Management} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", eval = FALSE ) ``` ## Understanding YouTube API quotas YouTube now tracks ordinary Data API units, search calls, and video-upload calls in separate default buckets. Google Cloud Console remains the source of truth; the package counters only estimate requests made by the current R session. ### Quota Costs by Operation Different operations have different quota costs: | Operation | Default accounting | Example function | |-----------|-----------|------------------| | Ordinary list request | 1 Data API unit | `get_video_details()` | | Search request | 1 Search Query call | `yt_search()` | | Typical write or delete | 50 Data API units | `create_playlist()`, `delete_video()` | | Caption list, download, insert, update | 50, 200, 400, 450 Data API units | Caption functions | | Video upload | 1 Video Upload call | `upload_video()` | Google's default allocations are 10,000 Data API units, 100 Search Query calls, and 100 Video Upload calls per day. These defaults can change or be increased for a project. ### Tracking Quota Usage tuber provides built-in quota tracking: ```{r quota-tracking, eval=FALSE} library(tuber) # Check current quota usage quota_status <- yt_get_quota_usage() print(quota_status) # View details quota_status[, c("bucket", "quota_used", "quota_limit", "quota_remaining")] quota_status$reset_time[1] # Midnight Pacific Time ``` ### Setting Custom Quota Limits If you've requested a quota increase from Google, update your limit: ```{r set-quota, eval=FALSE} # Set a custom quota limit yt_set_quota_limit(50000, bucket = "data") # Configure a project-specific search-call allocation yt_set_quota_limit(200, bucket = "search") # Reset quota tracking (e.g., after midnight) yt_reset_quota() ``` ## Authentication Strategies tuber supports two authentication methods, each with different use cases. ### API Key Authentication Best for **read-only public data**: - Searching videos - Getting public video statistics - Fetching channel information - Reading public comments ```{r api-key, eval=FALSE} # Set up API key (get one from Google Cloud Console) yt_set_key("YOUR_API_KEY") # Use API key for read operations video_stats <- get_video_details( video_ids = "dQw4w9WgXcQ", auth = "key" ) channel_stats <- get_channel_details( channel_ids = "UCuAXFkgsw1L7xaCfnd5JJOw", auth = "key" ) ``` ### OAuth2 Authentication Required for: - Accessing private/unlisted content - Writing data (creating playlists, uploading videos) - Deleting resources - Accessing user's own channel data ```{r oauth, eval=FALSE} # Set up OAuth2 (opens browser for authentication) yt_oauth("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET") # Write operations require OAuth create_playlist( title = "My New Playlist", description = "A collection of favorites", status = "private" ) # Access your own channel's unlisted videos list_my_videos() ``` ### Which Authentication to Use? | Task | API Key | OAuth2 | |------|---------|--------| | Search public videos | Yes | Yes | | Get public video stats | Yes | Yes | | Get public channel info | Yes | Yes | | Read public comments | Yes | Yes | | Access unlisted videos (own) | No | Yes | | Access private videos (own) | No | Yes | | Create/update playlists | No | Yes | | Upload videos | No | Yes | | Delete content | No | Yes | | Manage comments | No | Yes | ## Batch Processing When working with many videos or channels, batch processing is essential for efficiency. ### Processing Multiple Videos ```{r batch-videos, eval=FALSE} # Get details for multiple videos at once video_ids <- c("dQw4w9WgXcQ", "M7FIvfx5J10", "kJQP7kiw5Fk") # Single API call for multiple videos (more efficient) videos <- get_video_details( video_ids = video_ids, part = c("snippet", "statistics"), auth = "key" ) # Analyze results head(videos) ``` ### Using Bulk Analysis Functions tuber provides high-level functions for common analysis tasks: ```{r bulk-analysis, eval=FALSE} # Comprehensive video analysis analysis <- bulk_video_analysis( video_ids = video_ids, include_comments = FALSE, auth = "key" ) # Access results analysis$video_data # Detailed video information analysis$benchmarks # Performance percentiles analysis$summary # Overall statistics # Channel analysis channel_analysis <- analyze_channel( channel_id = "UCuAXFkgsw1L7xaCfnd5JJOw", max_videos = 50, auth = "key" ) # Compare multiple channels comparison <- compare_channels( channel_ids = c("UC1", "UC2", "UC3"), metrics = c("subscriber_count", "video_count", "view_count"), auth = "key" ) ``` ### Pagination Handling tuber automatically handles pagination for large result sets: ```{r pagination, eval=FALSE} # Request more items than API allows per page (50) # tuber automatically makes multiple API calls playlist_items <- list_playlist_items( playlist_id = "PLrAXtmErZgOeiKm4sgNOknGvNjby9efdf", max_results = 200, # Will make 4 API calls auth = "key" ) # Get many comments with automatic pagination comments <- list_comment_threads( video_id = "dQw4w9WgXcQ", max_results = 500, # Will paginate automatically auth = "key" ) ``` ## Extracting Data from Results YouTube API responses contain nested JSON. Here's how to work with them. ### Using simplify = TRUE (Default) Most functions flatten nested data into data frames: ```{r extract-simple, eval=FALSE} # Get simplified output videos <- get_video_details( video_ids = "dQw4w9WgXcQ", simplify = TRUE, auth = "key" ) # Access fields directly videos$title videos$viewCount videos$likeCount videos$channelTitle ``` ### Using simplify = FALSE for Complex Data When you need the full nested structure: ```{r extract-raw, eval=FALSE} # Get raw API response videos_raw <- get_video_details( video_ids = "dQw4w9WgXcQ", simplify = FALSE, auth = "key" ) # Navigate nested structure video <- videos_raw$items[[1]] video$snippet$title video$snippet$thumbnails$high$url video$statistics$viewCount video$contentDetails$duration ``` ### Common Field Access Patterns ```{r field-patterns, eval=FALSE} # Video details videos$snippet.title # Title videos$snippet.description # Description videos$statistics.viewCount # View count videos$statistics.likeCount # Like count videos$contentDetails.duration # Duration (ISO 8601) # Channel details channels$snippet.title # Channel name channels$statistics.subscriberCount channels$statistics.videoCount channels$brandingSettings.channel.description # Comment extraction comments$text_display comments$author_name comments$like_count ``` ## Error Handling & Retries ### Using with_retry for Transient Errors ```{r retry, eval=FALSE} # Automatic retry with exponential backoff result <- with_retry( get_video_details(video_ids = "dQw4w9WgXcQ", auth = "key"), max_retries = 3, base_delay = 1 ) ``` ### Handling Quota Exhaustion ```{r quota-handling, eval=FALSE} # Check before making data API requests quota <- yt_get_quota_usage() data_quota <- quota[quota$bucket == "data", ] if (data_quota$quota_remaining < 100) { warning("Low quota! Consider waiting until reset at: ", data_quota$reset_time) } # Wrap expensive operations tryCatch({ results <- yt_search(term = "R programming", max_results = 50) }, error = function(e) { if (grepl("quota", e$message, ignore.case = TRUE)) { message("Quota exceeded. Try again after: ", yt_get_quota_usage()$reset_time) } }) ``` ### Rate Limiting Best Practices ```{r rate-limiting, eval=FALSE} # Add delays between requests video_ids <- c("id1", "id2", "id3", "id4", "id5") results <- lapply(video_ids, function(vid) { Sys.sleep(0.5) # 500ms delay between requests get_video_details(video_ids = vid, auth = "key") }) ``` ## Caching for Performance tuber includes built-in caching for frequently accessed data: ```{r caching, eval=FALSE} # Configure cache tuber_cache_config( enabled = TRUE, max_size = 100, default_ttl = 3600 ) # Eligible GET requests are cached transparently cats <- list_video_categories(region_code = "US", auth = "key") langs <- list_languages(auth = "key") regions <- list_regions(auth = "key") # Check cache status tuber_cache_info() # Clear cache when needed tuber_cache_clear() ``` ## Practical Examples ### Example 1: Analyze a Channel's Performance ```{r example-channel, eval=FALSE} # Full channel analysis analysis <- analyze_channel( channel_id = "UCuAXFkgsw1L7xaCfnd5JJOw", max_videos = 100, auth = "key" ) # Summary statistics cat("Channel:", analysis$channel_info$title, "\n") cat("Subscribers:", analysis$channel_info$subscriberCount, "\n") cat("Average views:", analysis$performance_metrics$avg_views_per_video, "\n") cat("Engagement rate:", analysis$performance_metrics$engagement_rate, "\n") ``` ### Example 2: Trending Analysis ```{r example-trending, eval=FALSE} # Analyze trending topics trends <- analyze_trends( search_terms = c("machine learning", "AI", "data science"), time_period = "month", max_results = 25, region_code = "US", auth = "key" ) # View trend summary print(trends$trend_summary) # Most trending term best_trend <- trends$trend_summary[1, ] cat("Top trending:", best_trend$search_term, "\n") cat("Total views:", best_trend$total_views, "\n") ``` ### Example 3: Efficient Video Processing ```{r example-batch, eval=FALSE} # Get all videos from a playlist playlist_videos <- list_playlist_items( playlist_id = "PLrAXtmErZgOeiKm4sgNOknGvNjby9efdf", max_results = 100, auth = "key" ) # Extract video IDs video_ids <- playlist_videos$video_id # Get detailed stats for all videos in one call video_stats <- get_video_details( video_ids = video_ids, part = c("statistics", "contentDetails"), auth = "key" ) # Analyze performance total_views <- sum(as.numeric(video_stats$viewCount), na.rm = TRUE) avg_duration <- mean(as.numeric(video_stats$duration), na.rm = TRUE) ``` ## Troubleshooting ### Common Issues **"quotaExceeded" error:** - Check quota with `yt_get_quota_usage()` - Wait until `reset_time` or request quota increase from Google **"forbidden" error:** - Ensure YouTube Data API is enabled in Google Cloud Console - Check API key/OAuth credentials are correct - Verify the resource isn't private **"videoNotFound" or empty results:** - Video may be private, deleted, or region-restricted - Double-check the video/channel ID format **Rate limiting (429 errors):** - Add delays with `Sys.sleep()` between requests - Use `with_retry()` for automatic backoff ### Getting Help ```{r help, eval=FALSE} # Check function documentation ?get_video_details ?yt_search ?with_retry # View package vignettes browseVignettes("tuber") ```