### Run Unit and Integration Tests Source: https://github.com/thetadev/rustypipe/blob/main/DEVELOPMENT.md Use this command to execute all unit and integration tests for the project. Ensure 'just' is installed. ```bash just test ``` -------------------------------- ### YouTube Video URL Example Source: https://github.com/thetadev/rustypipe/blob/main/notes/deobf.md This is an example of a YouTube video playback URL. It includes various parameters such as expire, id, ip, itag, and signature, which are common for streaming video content. The 'n' parameter is likely related to the Nsig function. ```url https://rr3---sn-h0jeened.googlevideo.com/videoplayback?expire=1658232063&ei=n0jWYuCAFIz3gAeAx4nIAw&ip=93.235.185.61&id=o-AHnSNPNCkequX39D-ysNUiDKYmbe-a8EplrOAV2LQylr&itag=18&source=youtube&requiressl=yes&mh=a7&mm=31%2C29&mn=sn-h0jeened%2Csn-h0jelnez&ms=au%2Crdu&mv=m&mvi=3&pl=26&initcwndbps=1416250&spc=lT-KhsYr92Phls7wH9GQiLWRR-MGnTE&vprv=1&mime=video%2Fmp4&ns=AMUzTf9OiCSKRVVVRqr1VqMH&gir=yes&clen=17923723&ratebypass=yes&dur=208.027&lmt=1641514704547595&mt=1658209972&fvip=4&fexp=24001373%2C24007246&beids=23886220&c=WEB&txp=4538322&n=nrkec0fwgTWolw&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhAOxJLTqKCFUwInEHtxtsH13V0i_fDws_vgCuilecqHa6AiEAhHMFv4WqPrFNZvxsBx3ee5GdVw_7_hMu0yebsClRfw8%3D&sig=AOq0QJ8wRAIgaryQHmplJ9xJSKFywyaSMHuuwZYsoMTfvRviG51qIGECIA5061zWeyfMPX9hEl_U6f9J0tr7GTJMKyPf5XNrJb5f&cpn=uGaaNCVq9tAJ9K8j ``` -------------------------------- ### Get Music Search Suggestions in Rust Source: https://context7.com/thetadev/rustypipe/llms.txt Fetches search suggestions for music queries. It iterates through suggested terms and items, printing each one. ```rust use rustypipe::client::RustyPipe; // Music search suggestions async fn music_suggestions() -> Result<(), Box> { let rp = RustyPipe::new(); let suggestions = rp.query().music_search_suggestion("daft p").await?; for term in &suggestions.terms { println!("Suggestion: {:?}", term); } for item in &suggestions.items { println!("Item: {:?}", item); } Ok(()) } ``` -------------------------------- ### Get YouTube Search Suggestions Source: https://context7.com/thetadev/rustypipe/llms.txt Fetches search suggestions for a given query string 'rust progr'. Requires the `rustypipe` crate. ```rust use rustypipe::client::RustyPipe; async fn get_suggestions() -> Result<(), Box> { let rp = RustyPipe::new(); let suggestions = rp.query().search_suggestion("rust progr").await?; for suggestion in suggestions { println!("Suggestion: {}", suggestion); } Ok(()) } ``` -------------------------------- ### Download Music Album Tracks Source: https://context7.com/thetadev/rustypipe/llms.txt This example downloads all tracks from a music album, enabling audio tagging and cropping cover art. It iterates through album tracks and downloads each one, with basic error handling for individual track downloads. ```rust use rustypipe::client::RustyPipe; use rustypipe_downloader::Downloader; use rustypipe::param::StreamFilter; async fn download_album() -> Result<(), Box> { let rp = RustyPipe::new(); // Configure downloader for audio with tagging support let dl = Downloader::builder() .rustypipe(&rp) .stream_filter(StreamFilter::new().no_video()) .audio_tag() // Enable ID3 tagging (requires 'audiotag' feature) .crop_cover() // Crop thumbnails to square .path_precheck() // Skip existing files .build(); // Get album let album = rp.query().music_album("MPREb_nlBWQROfvjo").await?; // Download each track for track in &album.tracks { match dl.track(track) .to_template("{album}/{track} {title}") .download() .await { Ok(result) => println!("Downloaded: {:?}", result.dest), Err(e) => println!("Error downloading {}: {}", track.name, e), } } Ok(()) } ``` -------------------------------- ### Get Detailed Channel Information Source: https://context7.com/thetadev/rustypipe/llms.txt Fetches detailed information about a YouTube channel, including description, view count, video count, creation date, and country. Requires the `rustypipe` crate. ```rust use rustypipe::client::RustyPipe; async fn get_channel_info() -> Result<(), Box> { let rp = RustyPipe::new(); let info = rp.query().channel_info("UC_x5XG1OV2P6uZZ5FSM9Ttw").await?; println!("Description: {:?}", info.description); println!("Total views: {:?}", info.view_count); println!("Videos: {:?}", info.video_count); println!("Joined: {:?}", info.create_date); println!("Country: {:?}", info.country); for (name, url) in &info.links { println!("Link: {} -> {}", name, url); } Ok(()) } ``` -------------------------------- ### Example Deobfuscated Signature Source: https://github.com/thetadev/rustypipe/blob/main/notes/deobf.md An example of a deobfuscated signature obtained after applying the deobfuscation logic. This is the final output. ```text obfuscatedSig = GOqGOqGOq0QJ8wRAIgaryQHfplJ9xJSKFywyaSMHuuwZYsoMTAvRvfm51qIGECIA5061zWeyfMPX9hEl_U6f9J0tr7GTJMKyPf5XNrJb5fb5i result = AOq0QJ8wRAIgaryQHmplJ9xJSKFywyaSMHuuwZYsoMTfvRviG51qIGECIA5061zWeyfMPX9hEl_U6f9J0tr7GTJMKyPf5XNrJb5f ``` -------------------------------- ### Convert RustyPipe Reports to YAML Source: https://github.com/thetadev/rustypipe/blob/main/DEVELOPMENT.md Convert RustyPipe reports into a more readable YAML format using this 'just' task. This process requires 'yq' to be installed. ```bash just report2yaml ``` -------------------------------- ### Get Channel Videos and Playlists Source: https://context7.com/thetadev/rustypipe/llms.txt Retrieves a channel's videos (default, popular, shorts, live) and playlists using the channel ID. Requires `rustypipe` crate and specific `ChannelOrder` and `ChannelVideoTab` enums. ```rust use rustypipe::client::RustyPipe; use rustypipe::param::{ChannelOrder, ChannelVideoTab}; async fn get_channel_content() -> Result<(), Box> { let rp = RustyPipe::new(); let channel_id = "UC_x5XG1OV2P6uZZ5FSM9Ttw"; // Google Developers // Get channel videos with metadata let channel = rp.query().channel_videos(channel_id).await?; println!("Channel: {} (@{})", channel.name, channel.handle.unwrap_or_default()); println!("Subscribers: {:?}", channel.subscriber_count); println!("Has Shorts: {}", channel.has_shorts); println!("Has Live: {}", channel.has_live); for video in channel.content.items.iter().take(5) { println!(" {} - {} views", video.name, video.view_count.unwrap_or(0)); } // Get videos sorted by popularity let popular = rp.query() .channel_videos_order(channel_id, ChannelOrder::Popular) .await?; println!("\nMost popular:"); for video in popular.items.iter().take(3) { println!(" {} - {} views", video.name, video.view_count.unwrap_or(0)); } // Get Shorts let shorts = rp.query() .channel_videos_tab(channel_id, ChannelVideoTab::Shorts) .await?; // Get livestreams let lives = rp.query() .channel_videos_tab(channel_id, ChannelVideoTab::Live) .await?; // Get playlists let playlists = rp.query().channel_playlists(channel_id).await?; println!("\nPlaylists:"); for playlist in playlists.content.items.iter().take(5) { println!(" {} ({} videos)", playlist.name, playlist.video_count.unwrap_or(0)); } Ok(()) } ``` -------------------------------- ### Get Channel Videos - RustyPipe Source: https://github.com/thetadev/rustypipe/blob/main/README.md Fetches and prints details of videos from a given YouTube channel ID. Requires the `tokio` runtime. Ensure the channel ID is valid. ```rust use rustypipe::client::RustyPipe; #[tokio::main] async fn main() { // Create a client let rp = RustyPipe::new(); // Get the channel let channel = rp .query() .channel_videos("UCl2mFZoRqjw_ELax4Yisf6w") .await .unwrap(); println!("Name: {}", channel.name); println!("Description: {}", channel.description); println!("Subscribers: {}", channel.subscriber_count.unwrap()); channel .content .items .iter() .for_each(|v| println!("[{}] {} ({}s)", v.id, v.name, v.length.unwrap())); } ``` -------------------------------- ### Filter Video Streams by Criteria Source: https://context7.com/thetadev/rustypipe/llms.txt Filter available video and audio streams based on specific criteria such as maximum resolution and audio codec. This example demonstrates selecting the best streams that match the defined filter. ```rust use rustypipe::client::RustyPipe; use rustypipe::param::StreamFilter; async fn get_filtered_streams() -> Result<(), Box> { let rp = RustyPipe::new(); let player = rp.query().player("dQw4w9WgXcQ").await?; // Create a filter for specific stream requirements let filter = StreamFilter::new() .video_max_res(1080) // Max 1080p .audio_codec(rustypipe::model::AudioCodec::Opus); // Select best video and audio streams matching filter let (video, audio) = player.select_video_audio_stream(&filter); if let Some(v) = video { println!("Selected video: {}p {}", v.height, v.codec); } if let Some(a) = audio { println!("Selected audio: {} kbps {}", a.bitrate / 1000, a.codec); } Ok(()) } ``` -------------------------------- ### Get YouTube Playlist Details Source: https://context7.com/thetadev/rustypipe/llms.txt Retrieves details for a specific YouTube playlist, including its name, video count, channel, and a list of videos within the playlist. Requires the `rustypipe` crate. ```rust use rustypipe::client::RustyPipe; async fn get_playlist() -> Result<(), Box> { let rp = RustyPipe::new(); let playlist = rp.query().playlist("PLIivdWyY5sqJxnwJhe3etaK57n6guoGAy").await?; println!("Playlist: {}", playlist.name); println!("Videos: {:?}", playlist.video_count); println!("Channel: {:?}", playlist.channel); for video in playlist.videos.items.iter().take(10) { println!(" {} - {}", video.name, video.id); } Ok(()) } ``` -------------------------------- ### Get Album Details in Rust Source: https://context7.com/thetadev/rustypipe/llms.txt Fetches detailed information about a YouTube Music album using its ID. It prints the album's name, artists, year, type, track count, and details for each track and its variants. ```rust use rustypipe::client::RustyPipe; async fn get_album() -> Result<(), Box> { let rp = RustyPipe::new(); // Get a YouTube Music album let album = rp.query().music_album("MPREb_nlBWQROfvjo").await?; println!("Album: {}", album.name); println!("Artists: {:?}", album.artists); println!("Year: {:?}", album.year); println!("Type: {:?}", album.album_type); println!("Tracks: {}", album.track_count); for track in &album.tracks { println!(" {}. {} ({:?}s)", track.track_nr.unwrap_or(0), track.name, track.duration ); } // Album variants (deluxe editions, etc.) for variant in &album.variants { println!("Variant: {:?}", variant); } Ok(()) } ``` -------------------------------- ### Download Audio and Video Streams Source: https://github.com/thetadev/rustypipe/blob/main/downloader/README.md Instantiate and configure the downloader with optional features like audio tagging and cover cropping. Then, build download queries specifying the video ID, stream filter (e.g., no video, max resolution), and destination file, and initiate the download. ```rust use rustypipe::param::StreamFilter; use rustypipe_downloader::DownloaderBuilder; let dl = DownloaderBuilder::new() .audio_tag() .crop_cover() .build(); let filter_audio = StreamFilter::new().no_video(); dl.id("eRsGyueVLvQ").stream_filter(filter_audio).to_file("audio.opus").download().await; let filter_video = StreamFilter::new().video_max_res(720); dl.id("eRsGyueVLvQ").stream_filter(filter_video).to_file("video.mp4").download().await; ``` -------------------------------- ### Instantiate RustyPipe Client Source: https://github.com/thetadev/rustypipe/blob/main/README.md Demonstrates creating a RustyPipe client with default options or custom storage directory. Requires the tokio runtime. ```rust let rp = RustyPipe::new(); let rp = RustyPipe::builder().storage_dir("/app/data").build().unwrap(); let channel = rp.query().lang(Language::De).channel_videos("UCl2mFZoRqjw_ELax4Yisf6w").await.unwrap(); ``` -------------------------------- ### Get OAuth Device Code Source: https://github.com/thetadev/rustypipe/blob/main/README.md Use this function to obtain a device code for OAuth authentication. You will need to enter this code on google.com/device. ```python rp.user_auth_get_code() ``` -------------------------------- ### Download with Custom Settings Source: https://context7.com/thetadev/rustypipe/llms.txt Configure the downloader with custom settings like FFmpeg path, video resolution, audio language, output format, and retry attempts. It also demonstrates using a template for the output path. ```rust use rustypipe::client::RustyPipe; use rustypipe_downloader::{Downloader, DownloaderBuilder, DownloadVideoFormat}; use rustypipe::param::StreamFilter; async fn download_configured() -> Result<(), Box> { let rp = RustyPipe::new(); let dl = Downloader::builder() .rustypipe(&rp) .ffmpeg("/usr/bin/ffmpeg") // Custom ffmpeg path .stream_filter( StreamFilter::new() .video_max_res(1080) // Max 1080p .audio_language("en") // English audio ) .video_format(DownloadVideoFormat::Mkv) // Output as MKV .n_retries(5) // Retry on failure .path_precheck() // Skip already downloaded .build(); let result = dl.id("dQw4w9WgXcQ") .to_template("{channel}/{title} [{id}]") // Template path .download() .await?; println!("Downloaded to: {:?}", result.dest); Ok(()) } ``` -------------------------------- ### Paginate Channel Videos in Rust Source: https://context7.com/thetadev/rustypipe/llms.txt Demonstrates how to handle paginated results, specifically for channel videos. It shows fetching the first page, navigating to the next page, extending the results to a specific limit, and iterating through all fetched videos. ```rust use rustypipe::client::RustyPipe; async fn paginate_results() -> Result<(), Box> { let rp = RustyPipe::new(); // Get channel videos let channel = rp.query().channel_videos("UC_x5XG1OV2P6uZZ5FSM9Ttw").await?; let mut paginator = channel.content; println!("First page: {} videos", paginator.items.len()); // Fetch next page if paginator.has_next() { paginator.next(&rp).await?; println!("After next: {} videos", paginator.items.len()); } // Extend to a specific number of items paginator.extend_limit(&rp, 100).await?; println!("After extend: {} videos", paginator.items.len()); // Iterate through all items for video in &paginator.items { println!("{}", video.name); } Ok(()) } ``` -------------------------------- ### POST /youtubei/v1/next - Initial Request Source: https://github.com/thetadev/rustypipe/blob/main/notes/next/next_request.txt This endpoint is used to fetch the initial set of recommendations or video data. It requires a context and playback details. ```APIDOC ## POST /youtubei/v1/next ### Description Fetches initial recommendations or video data. ### Method POST ### Endpoint www.youtube.com/youtubei/v1/next ### Parameters #### Request Body - **context** (object) - Required - Contextual information for the request. - **playbackContext** (object) - Required - Playback-related context, including visibility and duration. - **vis** (number) - Required - Visibility state. - **lactMilliseconds** (string) - Required - Last active milliseconds. - **autonavState** (string) - Required - Autonavigation state. - **captionsRequested** (boolean) - Required - Whether captions are requested. - **videoId** (string) - Required - The ID of the video. - **contentCheckOk** (boolean) - Required - Indicates if content check is okay. - **racyCheckOk** (boolean) - Required - Indicates if racy content check is okay. ### Request Example ```json { "context": "...", "playbackContext": {"vis": 0, "lactMilliseconds": "-1"}, "autonavState": "STATE_OFF", "captionsRequested": false, "videoId": "iQfSvIgIs_M", "contentCheckOk": false, "racyCheckOk": false } ``` ### Response #### Success Response (200) - **continuation** (string) - Token for subsequent requests (e.g., for comments or more recommendations). - **responseContext** (object) - Contextual information about the response. - **contents** (array) - Array of recommended content items. #### Response Example ```json { "continuation": "Eg0SC2lRZlN2SWdJc19NGAYymQcK7wZnZXRfcmFua2VkX3N0cmVhbXMtLUNzZ0VDSUFFRlJlMzBUZ2F2UVFLdUFRSTJGOFFnQVFZQnlLdEJBT1VXNDNhOEpSYzdjNU5xQ0l4RHRwVjZaMmIxWXpoOE9MMVBfbWRoa3JEUHlXaV9wX3FVemx5MGF2bFVVc3VsQlRYd0k4WTF2SjV0YVQ2clJCR0hxeVZORWJyUXpJVTFybXNHMmtlaVpSYVdnWjU1U3ljUW51MUxmT0E5MVBjNHlISzhraUJBd3VYd3QtZTRqUlhxWXgzcnpIOGd1TkdnaExkV3NIdjdySkwtblQwd1o5LXUxQU80WFpVRHBKX213cmo5SHp2N2FHakZOeGxtN1dTWlhtZ0g4eUY4eVMxMmRKcF94endsNzhwUHAyeC1sUTQwdUlIdzdDUUxHYnBhV3BHUDZKNTI2SzM4ZkoyYWYtWG45ZWpubzc1T2Nhd3lzaHRDYlg5TmNTd3prbnhQc28yZTFrdVRmT2lURS1PanpWNWVYdy1WYmZDbWZ0UkNtOWhFeTNfeVk2Ml90QmZEVHV1SXlZejRfQnRLeklGa25ZY0E0NkYzdkZJMS0tQi10VHB1OWx3cnVjaldXcVQwVUtoeUQ4Y0ttSjhfdVZaX3A4eG5pcWU1QmJHY1A2NFA5dnQ0YUdNU1JiNTJIcDVPSnpaZXNyNmY0Z0ZPYWE3dFBicnZkVDBsTFVwYmtSMEhhVE44a0tGX3Z1MWtTbi1MRXlpb1RaN19tX2ZBNjNqZ180bXZQNVB2MDkyd2Nrc2tpWGpvTndLMFk5RExudzRsT3k0alVYZkJ0NHduYVY4X2VVUUxmenljVDc4NmY0MnBQeWg1ZmpWN2lFM1M3R25BNzM4RUxUUzI1b2Y3U2psRnFhSDlZSlNwSmJvWjktZUFsSXhMVmFTVS1PXzJ4RGVVRG0wT1AzeUhDM3JBVmRfLWlsc0tLbkxmYnlWNThYa0djZWpKYWxlRXA3RjA0TU1WcnZOM0pKeVMtaTRwQ29Qb29YU0tFako5S2VQejJud1N4aG1TdmNoVEluWHRRejFYN0RkcGpZQUVIZ1NCUWlHSUJnQUVnVUlpU0FZQUJJSENJUWdFRE1ZQVJJRkNJY2dHQUFTQndpWElCQlJHQUVTQndpRklCQlVHQUVTQlFpSUlCZ0FHQUEi } ``` ``` -------------------------------- ### Download with Progress Bars Source: https://context7.com/thetadev/rustypipe/llms.txt Integrate with the `indicatif` crate to display progress bars for downloads. This involves passing a `MultiProgress` instance to the downloader builder. ```rust use rustypipe::client::RustyPipe; use rustypipe_downloader::Downloader; use indicatif::MultiProgress; async fn download_with_progress() -> Result<(), Box> { let rp = RustyPipe::new(); let multi = MultiProgress::new(); let dl = Downloader::builder() .rustypipe(&rp) .multi_progress(multi) // Enable progress bars .build(); // Downloads will show progress automatically let result = dl.id("dQw4w9WgXcQ") .to_dir("downloads") .download() .await?; println!("Downloaded to: {:?}", result.dest); Ok(()) } ``` -------------------------------- ### Run YouTube Integration Tests for All Languages Source: https://github.com/thetadev/rustypipe/blob/main/DEVELOPMENT.md Execute YouTube integration tests across all supported languages. This can be a time-consuming process and is not typically run in CI. ```bash just testintl ``` -------------------------------- ### Download Missing Test Files Source: https://github.com/thetadev/rustypipe/blob/main/DEVELOPMENT.md Use this 'just' task to download any necessary test files required for unit tests. This ensures all dependencies for testing are met. ```bash just testfiles ``` -------------------------------- ### Create RustyPipe Client Source: https://context7.com/thetadev/rustypipe/llms.txt Instantiate a RustyPipe client using default settings or configure it with a builder pattern. The builder allows setting storage directories for caching and specifying response language and country codes. ```rust use rustypipe::client::RustyPipe; use rustypipe::param::Language; // Simple default client let rp = RustyPipe::new(); // Configured client with caching and language settings let rp = RustyPipe::builder() .storage_dir("cache") // Enable filesystem caching .language(Language::De) // Set response language .country(rustypipe::param::Country::De) .build(); // Create a query object to make API calls let query = rp.query(); ``` -------------------------------- ### Get Music Playlist in Rust Source: https://context7.com/thetadev/rustypipe/llms.txt Retrieves details for a specific YouTube Music playlist using its ID. It prints the playlist's name, track count, whether it's from YTM, and details of the first 10 tracks. ```rust use rustypipe::client::RustyPipe; async fn get_music_playlist() -> Result<(), Box> { let rp = RustyPipe::new(); // Get a YouTube Music playlist let playlist = rp.query() .music_playlist("RDCLAK5uy_kFQXdnqMaQCVx2wpUM4ZfbsGCDibZtkJk") .await?; println!("Playlist: {}", playlist.name); println!("Tracks: {:?}", playlist.track_count); println!("From YTM: {}", playlist.from_ytm); for track in playlist.tracks.items.iter().take(10) { println!(" {} - {:?}", track.name, track.artists.first().map(|a| &a.name)); } Ok(()) } ``` -------------------------------- ### POST /youtubei/v1/next - Requesting Comments or More Recommendations Source: https://github.com/thetadev/rustypipe/blob/main/notes/next/next_request.txt This endpoint can be used to fetch comments or additional recommendations by providing a continuation token obtained from an initial request. ```APIDOC ## POST /youtubei/v1/next - Continuation Request ### Description Fetches comments or more recommendations using a continuation token. ### Method POST ### Endpoint www.youtube.com/youtubei/v1/next ### Parameters #### Request Body - **context** (object) - Required - Contextual information for the request. - **continuation** (string) - Required - Continuation token from a previous response. ### Request Example ```json { "context": "...", "continuation": "Eg0SC2lRZlN2SWdJc19NGAYymQcK7wZnZXRfcmFua2VkX3N0cmVhbXMtLUNzZ0VDSUFFRlJlMzBUZ2F2UVFLdUFRSTJGOFFnQVFZQnlLdEJBT1VXNDNhOEpSYzdjNU5xQ0l4RHRwVjZaMmIxWXpoOE9MMVBfbWRoa3JEUHlXaV9wX3FVemx5MGF2bFVVc3VsQlRYd0k4WTF2SjV0YVQ2clJCR0hxeVZORWJyUXpJVTFybXNHMmtlaVpSYVdnWjU1U3ljUW51MUxmT0E5MVBjNHlISzhraUJBd3VYd3QtZTRqUlhxWXgzcnpIOGd1TkdnaExkV3NIdjdySkwtblQwd1o5LXUxQU80WFpVRHBKX213cmo5SHp2N2FHakZOeGxtN1dTWlhtZ0g4eUY4eVMxMmRKcF94endsNzhwUHAyeC1sUTQwdUlIdzdDUUxHYnBhV3BHUDZKNTI2SzM4ZkoyYWYtWG45ZWpubzc1T2Nhd3lzaHRDYlg5TmNTd3prbnhQc28yZTFrdVRmT2lURS1PanpWNWVYdy1WYmZDbWZ0UkNtOWhFeTNfeVk2Ml90QmZEVHV1SXlZejRfQnRLeklGa25ZY0E0NkYzdkZJMS0tQi10VHB1OWx3cnVjaldXcVQwVUtoeUQ4Y0ttSjhfdVZaX3A4eG5pcWU1QmJHY1A2NFA5dnQ0YUdNU1JiNTJIcDVPSnpaZXNyNmY0Z0ZPYWE3dFBicnZkVDBsTFVwYmtSMEhhVE44a0tGX3Z1MWtTbi1MRXlpb1RaN19tX2ZBNjNqZ180bXZQNVB2MDkyd2Nrc2tpWGpvTndLMFk5RExudzRsT3k0alVYZkJ0NHduYVY4X2VVUUxmenljVDc4NmY0MnBQeWg1ZmpWN2lFM1M3R25BNzM4RUxUUzI1b2Y3U2psRnFhSDlZSlNwSmJvWjktZUFsSXhMVmFTVS1PXzJ4RGVVRG0wT1AzeUhDM3JBVmRfLWlsc0tLbkxmYnlWNThYa0djZWpKYWxlRXA3RjA0TU1WcnZOM0pKeVMtaTRwQ29Qb29YU0tFako5S2VQejJud1N4aG1TdmNoVEluWHRRejFYN0RkcGpZQUVIZ1NCUWlHSUJnQUVnVUlpU0FZQUJJSENJUWdFRE1ZQVJJRkNJY2dHQUFTQndpWElCQlJHQUVTQndpRklCQlVHQUVTQlFpSUlCZ0FHQUEi } ``` ### Response #### Success Response (200) - **continuation** (string) - Token for subsequent requests. - **responseContext** (object) - Contextual information about the response. - **onResponseReceivedActions** (array) - Actions to be performed based on the response. #### Response Example ```json { "continuation": "...", "responseContext": { ... }, "onResponseReceivedActions": [ ... ] } ``` ``` -------------------------------- ### Run YouTube Integration Tests Source: https://github.com/thetadev/rustypipe/blob/main/DEVELOPMENT.md Run integration tests specifically for YouTube functionality. This task is part of the 'just' task suite. ```bash just testyt ``` -------------------------------- ### Search YouTube Videos, Channels, and Playlists Source: https://context7.com/thetadev/rustypipe/llms.txt Performs a generic search for 'rust programming' and iterates through the first 10 results, printing details for videos, channels, or playlists found. Requires the `rustypipe` crate. ```rust use rustypipe::client::RustyPipe; use rustypipe::model::{VideoItem, ChannelItem, PlaylistItem, SearchItem}; use rustypipe::param::SearchFilter; async fn search_youtube() -> Result<(), Box> { let rp = RustyPipe::new(); // Generic search returning all result types let results = rp.query().search::("rust programming").await?; for item in results.items.items.iter().take(10) { match item { SearchItem::Video(v) => println!("Video: {} [{}]", v.name, v.id), SearchItem::Channel(c) => println!("Channel: {} [{}]", c.name, c.id), SearchItem::Playlist(p) => println!("Playlist: {} [{}]", p.name, p.id), _ => {} } } Ok(()) } ``` -------------------------------- ### Watch a YouTube Video with RustyPipe Source: https://github.com/thetadev/rustypipe/blob/main/README.md Fetches video and audio streams for a given YouTube video ID and plays it using the mpv player. Requires RustyPipe and tokio. ```rust use std::process::Command; use rustypipe::{client::RustyPipe, param::StreamFilter}; #[tokio::main] async fn main() { // Create a client let rp = RustyPipe::new(); // Fetch the player let player = rp.query().player("pPvd8UxmSbQ").await.unwrap(); // Select the best streams let (video, audio) = player.select_video_audio_stream(&StreamFilter::default()); // Open mpv player let mut args = vec![video.expect("no video stream").url.to_owned()]; if let Some(audio) = audio { args.push(format!("--audio-file={}", audio.url)); } Command::new("mpv").args(args).output().unwrap(); } ``` -------------------------------- ### Download Video by ID Source: https://context7.com/thetadev/rustypipe/llms.txt Use this to download a video to a specified directory using its ID. Ensure the 'downloads' directory exists or is creatable. ```rust use rustypipe::client::RustyPipe; use rustypipe_downloader::{Downloader, DownloadVideoFormat}; use rustypipe::param::StreamFilter; async fn download_video() -> Result<(), Box> { let rp = RustyPipe::new(); let dl = Downloader::new(&rp); // Download a video by ID let result = dl.id("dQw4w9WgXcQ") .to_dir("downloads") .download() .await?; println!("Downloaded to: {:?}", result.dest); Ok(()) } ``` -------------------------------- ### Initial YouTube API Request Source: https://github.com/thetadev/rustypipe/blob/main/notes/next/next_request.txt This snippet shows the structure of an initial POST request to the YouTube API's 'next' endpoint to retrieve video data. It includes essential context and playback information. ```json { "context": ..., "playbackContext": {"vis": 0, "lactMilliseconds": "-1"}, "autonavState": "STATE_OFF", "captionsRequested": false, "videoId": "iQfSvIgIs_M", "contentCheckOk": false, "racyCheckOk": false } ``` -------------------------------- ### Fetch and Display Playlist Details with RustyPipe Source: https://github.com/thetadev/rustypipe/blob/main/README.md Retrieves playlist information, including name, author, and video items, then prints them to the console. Supports fetching up to 1000 items. ```rust use rustypipe::client::RustyPipe #[tokio::main] async fn main() { // Create a client let rp = RustyPipe::new(); // Get the playlist let playlist = rp .query() .playlist("PL2_OBreMn7FrsiSW0VDZjdq0xqUKkZYHT") .await .unwrap(); // Get all items (maximum: 1000) playlist.videos.extend_limit(rp.query(), 1000).await.unwrap(); println!("Name: {}", playlist.name); println!("Author: {}", playlist.channel.unwrap().name); println!("Last update: {}", playlist.last_update.unwrap()); playlist .videos .items .iter() .for_each(|v| println!("[{}] {} ({}s)", v.id, v.name, v.length)); } ``` -------------------------------- ### Requesting Comments or More Recommendations Source: https://github.com/thetadev/rustypipe/blob/main/notes/next/next_request.txt This snippet demonstrates how to request comments or further recommendations by using a continuation token obtained from a previous API response. This is useful for paginating through comments or recommendations. ```json { "context": ..., "continuation": "Eg0SC2lRZlN2SWdJc19NGAYymQcK7wZnZXRfcmFua2VkX3N0cmVhbXMtLUNzZ0VDSUFFRlJlMzBUZ2F2UVFLdUFRSTJGOFFnQVFZQnlLdEJBT1VXNDNhOEpSYzdjNU5xQ0l4RHRwVjZaMmIxWXpoOE9MMVBfbWRoa3JEUHlXaV9wX3FVemx5MGF2bFVVc3VsQlRYd0k4WTF2SjV0YVQ2clJCR0hxeVZORWJyUXpJVTFybXNHMmtlaVpSYVdnWjU1U3ljUW51MUxmT0E5MVBjNHlISzhraUJBd3VYd3QtZTRqUlhxWXgzcnpIOGd1TkdnaExkV3NIdjdySkwtblQwd1o5LXUxQU80WFpVRHBKX213cmo5SHp2N2FHakZOeGxtN1dTWlhtZ0g4eUY4eVMxMmRKcF94endsNzhwUHAyeC1sUTQwdUlIdzdDUUxHYnBhV3BHUDZKNTI2SzM4ZkoyYWYtWG45ZWpubzc1T2Nhd3lzaHRDYlg5TmNTd3prbnhQc28yZTFrdVRmT2lURS1PanpWNWVYdy1WYmZDbWZ0UkNtOWhFeTNfeVk2Ml90QmZEVHV1SXlZejRfQnRLeklGa25ZY0E0NkYzdkZJMS0tQi10VHB1OWx3cnVjaldXcVQwVUtoeUQ4Y0ttSjhfdVZaX3A4eG5pcWU1QmJHY1A2NFA5dnQ0YUdNU1JiNTJIcDVPSnpaZXNyNmY0Z0ZPYWE3dFBicnZkVDBsTFVwYmtSMEhhVE44a0tGX3Z1MWtTbi1MRXlpb1RaN19tX2ZBNjNqZ180bXZQNVB2MDkyd2Nrc2tpWGpvTndLMFk5RExudzRsT3k0alVYZkJ0NHduYVY4X2VVUUxmenljVDc4NmY0MnBQeWg1ZmpWN2lFM1M3R25BNzM4RUxUUzI1b2Y3U2psRnFhSDlZSlNwSmJvWjktZUFsSXhMVmFTVS1PXzJ4RGVVRG0wT1AzeUhDM3JBVmRfLWlsc0tLbkxmYnlWNThYa0djZWpKYWxlRXA3RjA0TU1WcnZOM0pKeVMtaTRwQ29Qb29YU0tFako5S2VQejJud1N4aG1TdmNoVEluWHRRejFYN0RkcGpZQUVIZ1NCUWlHSUJnQUVnVUlpU0FZQUJJSENJUWdFRE1ZQVJJRkNJY2dHQUFTQndpWElCQlJHQUVTQndpRklCQlVHQUVTQlFpSUlCZ0FHQUEiESILaVFmU3ZJZ0lzX00wAHgBKHhCEGNvbW1lbnRzLXNlY3Rpb24 ``` -------------------------------- ### Add RustyPipe Downloader to Cargo.toml Source: https://context7.com/thetadev/rustypipe/llms.txt If you need the downloader module, add `rustypipe-downloader` to your Cargo.toml. This is separate from the core RustyPipe library. ```toml [dependencies] rustypipe-downloader = "0.11" ``` -------------------------------- ### Compare Old and New Video Description Data Models Source: https://github.com/thetadev/rustypipe/blob/main/notes/AB_Tests.md Illustrates the structural changes in YouTube's video description data model for handling internal links, transitioning from a 'runs' array to an 'attributedDescription' with 'commandRuns'. Note that positions are based on UTF-16 characters. ```json { "videoSecondaryInfoRenderer": { "description": { "runs": [ { "text": "🎧Listen and download aespa's debut single \"Black Mamba\": " }, { "navigationEndpoint": { "commandMetadata": { "webCommandMetadata": { "rootVe": 83769, "url": "https://www.youtube.com/redirect?", "webPageType": "WEB_PAGE_TYPE_UNKNOWN" } }, "urlEndpoint": { "nofollow": true, "target": "TARGET_NEW_WINDOW", "url": "https://www.youtube.com/redirect?" } }, "text": "https://smarturl.it/aespa_BlackMamba" } ] } } } ``` ```json { "videoSecondaryInfoRenderer": { "attributedDescription": { "content": "🎧Listen and download aespa's debut single \"Black Mamba\": https://smarturl.it/aespa_BlackMamba\n🐍The Debut Stage...", "commandRuns": [ { "startIndex": 58, "length": 36, "onTap": { "innertubeCommand": { "commandMetadata": { "webCommandMetadata": { "url": "https://www.youtube.com/redirect?", "webPageType": "WEB_PAGE_TYPE_UNKNOWN", "rootVe": 83769 } }, "urlEndpoint": { "url": "https://www.youtube.com/redirect?", "target": "TARGET_NEW_WINDOW", "nofollow": true } } } } ] } } } ``` -------------------------------- ### Run Unit Tests Only Source: https://github.com/thetadev/rustypipe/blob/main/DEVELOPMENT.md Execute only the unit tests using the 'just' task runner. This is faster than running all tests. ```bash just unittest ``` -------------------------------- ### Search Within a Channel Source: https://context7.com/thetadev/rustypipe/llms.txt Searches for a specific query ('kubernetes') within a given YouTube channel ID. Requires the `rustypipe` crate. ```rust use rustypipe::client::RustyPipe; async fn search_channel() -> Result<(), Box> { let rp = RustyPipe::new(); let results = rp.query() .channel_search("UC_x5XG1OV2P6uZZ5FSM9Ttw", "kubernetes") .await?; for video in results.content.items.iter().take(5) { println!("{}", video.name); } Ok(()) } ``` -------------------------------- ### Search YouTube with Filters Source: https://context7.com/thetadev/rustypipe/llms.txt Searches for 'breaking news' specifically for videos, uploaded within the last hour, and sorted by view count. Requires the `rustypipe` crate and `SearchFilter` parameters. ```rust use rustypipe::client::RustyPipe; use rustypipe::model::VideoItem; use rustypipe::param::SearchFilter; async fn search_filtered() -> Result<(), Box> { let rp = RustyPipe::new(); // Search only for videos let filter = SearchFilter::new() .videos() .within_hour() // Uploaded within last hour .sort_by_view_count(); // Sort by popularity let results = rp.query() .search_filter::("breaking news", &filter) .await?; for video in &results.items.items { println!("{} - {} views", video.name, video.view_count.unwrap_or(0)); } Ok(()) } ``` -------------------------------- ### Run YouTube Integration Tests for a Specific Language Source: https://github.com/thetadev/rustypipe/blob/main/DEVELOPMENT.md Run YouTube integration tests for a single, specified language by setting the YT_LANG environment variable before executing the 'just' task. ```bash YT_LANG=de just testyt ```