feat!: add TV client

This commit is contained in:
ThetaDev 2024-07-30 01:55:24 +02:00
parent b6bc05c1f3
commit e608811e5f
No known key found for this signature in database
GPG key ID: E319D3C5148D65B6
18 changed files with 3887 additions and 132 deletions

View file

@ -1,6 +1,6 @@
#![warn(clippy::todo, clippy::dbg_macro)]
use std::{path::PathBuf, str::FromStr};
use std::{path::PathBuf, str::FromStr, time::Duration};
use clap::{Parser, Subcommand, ValueEnum};
use futures::stream::{self, StreamExt};
@ -10,7 +10,7 @@ use rustypipe::{
model::{UrlTarget, VideoId, YouTubeItem},
param::{search_filter, ChannelVideoTab, Country, Language, StreamFilter},
};
use rustypipe_downloader::{DownloadQuery, DownloaderBuilder};
use rustypipe_downloader::{DownloadError, DownloadQuery, DownloaderBuilder};
use serde::Serialize;
use tracing::level_filters::LevelFilter;
use tracing_subscriber::{fmt::MakeWriter, EnvFilter};
@ -236,6 +236,7 @@ enum MusicSearchCategory {
enum PlayerType {
Desktop,
Tv,
TvEmbed,
Android,
Ios,
}
@ -286,7 +287,8 @@ impl From<PlayerType> for ClientType {
fn from(value: PlayerType) -> Self {
match value {
PlayerType::Desktop => Self::Desktop,
PlayerType::Tv => Self::TvHtml5Embed,
PlayerType::TvEmbed => Self::TvHtml5Embed,
PlayerType::Tv => Self::Tv,
PlayerType::Android => Self::Android,
PlayerType::Ios => Self::Ios,
}
@ -324,7 +326,7 @@ async fn download_video(
}
}
let dl = DownloaderBuilder::new()
.client(rp)
.rustypipe(rp)
.stream_filter(filter)
.progress_bar(multi)
.build();
@ -356,7 +358,7 @@ async fn download_videos(
}
}
let dl = DownloaderBuilder::new()
.client(rp)
.rustypipe(rp)
.stream_filter(filter)
.progress_bar(multi.clone())
.path_precheck()
@ -388,10 +390,11 @@ async fn download_videos(
async move {
if let Err(e) = q.download().await {
tracing::error!("[{id}]: {e}");
} else {
main.inc(1);
if !matches!(e, DownloadError::Exists(_)) {
tracing::error!("[{id}]: {e}");
}
}
main.inc(1);
}
})
.await;
@ -433,7 +436,9 @@ async fn main() {
.with_writer(ProgWriter(multi.clone()))
.init();
let mut rp = RustyPipe::builder().visitor_data_opt(cli.vdata);
let mut rp = RustyPipe::builder()
.visitor_data_opt(cli.vdata)
.timeout(Duration::from_secs(15));
if cli.report {
rp = rp.report();
} else {

View file

@ -66,9 +66,10 @@ pub async fn download_testfiles() {
music_genre().await;
}
const CLIENT_TYPES: [ClientType; 5] = [
const CLIENT_TYPES: [ClientType; 6] = [
ClientType::Desktop,
ClientType::DesktopMusic,
ClientType::Tv,
ClientType::TvHtml5Embed,
ClientType::Android,
ClientType::Ios,

View file

@ -34,7 +34,7 @@ use tokio::{
process::Command,
};
use util::DownloadError;
pub use util::DownloadError;
type Result<T> = core::result::Result<T, DownloadError>;
@ -64,6 +64,8 @@ pub struct DownloaderBuilder {
struct DownloaderInner {
/// YT client
rp: RustyPipe,
/// HTTP client
http: Client,
/// Path to the ffmpeg binary
ffmpeg: String,
/// Global progress
@ -217,7 +219,7 @@ impl DownloaderBuilder {
/// Use a custom [`RustyPipe`] client
#[must_use]
pub fn client(mut self, rp: &RustyPipe) -> Self {
pub fn rustypipe(mut self, rp: &RustyPipe) -> Self {
self.rp = Some(rp.clone());
self
}
@ -277,9 +279,20 @@ impl DownloaderBuilder {
/// Create a new, configured [`Downloader`] instance
pub fn build(self) -> Downloader {
self.build_with_client(
Client::builder()
.timeout(Duration::from_secs(20))
.build()
.expect("http client"),
)
}
/// Create a new, configured [`Downloader`] instance using a custom Reqwest [`Client`]
pub fn build_with_client(self, http_client: Client) -> Downloader {
Downloader {
i: Arc::new(DownloaderInner {
rp: self.rp.unwrap_or_default(),
http: http_client,
ffmpeg: self.ffmpeg,
multi: self.multi,
filter: self.filter,
@ -300,7 +313,7 @@ impl Default for Downloader {
impl Downloader {
/// Create a new [`Downloader`] using the given [`RustyPipe`] instance
pub fn new(rp: &RustyPipe) -> Self {
DownloaderBuilder::new().client(rp).build()
DownloaderBuilder::new().rustypipe(rp).build()
}
/// Create a new [`DownloaderBuilder`]
@ -442,7 +455,7 @@ impl DownloadQuery {
let err = match self.download_attempt(&pb, n).await {
Ok(res) => return Ok(res),
Err(DownloadError::Http(e)) => {
if e.status() != Some(StatusCode::FORBIDDEN) {
if !e.is_timeout() && e.status() != Some(StatusCode::FORBIDDEN) {
return Err(DownloadError::Http(e));
}
DownloadError::Http(e)
@ -521,7 +534,24 @@ impl DownloadQuery {
},
};
let pv = DownloadVideo::from_video(&player_data);
let (name, details) = match &player_data.details.name {
Some(n) => (n.to_owned(), None),
None => {
let details = self.dl.i.rp.query().video_details(&self.video.id).await?;
(details.name.to_owned(), Some(details))
}
};
let pv = DownloadVideo {
id: player_data.details.id.to_owned(),
name: Some(name.to_owned()),
channel_id: Some(player_data.details.channel_id.to_owned()),
channel_name: player_data
.details
.channel_name
.clone()
.or(details.map(|d| d.channel.name)),
};
let output_path = self.dest.get_dest_path(&pv).with_extension(extension);
if output_path.exists() {
@ -551,22 +581,12 @@ impl DownloadQuery {
}
if let Some(pb) = pb {
pb.set_message(format!(
"Downloading {}{}",
player_data.name(),
attempt_suffix
))
pb.set_message(format!("Downloading {name}{attempt_suffix}"))
}
download_streams(
&downloads,
self.dl.i.rp.http_client(),
&user_agent,
pb.clone(),
)
.await?;
download_streams(&downloads, &self.dl.i.http, &user_agent, pb.clone()).await?;
if let Some(pb) = &pb {
pb.set_message(format!("Converting {}", player_data.name()));
pb.set_message(format!("Converting {name}"));
pb.set_style(
ProgressStyle::with_template("{msg}\n{spinner:.green} [{elapsed_precise}]")
.unwrap(),
@ -574,13 +594,7 @@ impl DownloadQuery {
pb.enable_steady_tick(Duration::from_millis(500));
}
convert_streams(
&downloads,
&output_path,
&self.dl.i.ffmpeg,
player_data.name(),
)
.await?;
convert_streams(&downloads, &output_path, &self.dl.i.ffmpeg, &name).await?;
if let Some(pb) = pb {
pb.disable_steady_tick();
}
@ -787,6 +801,12 @@ async fn download_chunks_by_header(
.await?
.error_for_status()?;
if res.content_length().unwrap_or_default() == 0 {
return Err(DownloadError::Progressive(
format!("empty chunk {}-{}", range.start, range.end).into(),
));
}
// Content-Range: bytes 0-100/451368980
let cr_header = res
.headers()
@ -859,7 +879,12 @@ async fn download_chunks_by_param(
.await?
.error_for_status()?;
let clen = res.content_length().unwrap();
let clen = res.content_length().unwrap_or_default();
if clen == 0 {
return Err(DownloadError::Progressive(
format!("empty chunk {}-{}", range.start, range.end).into(),
));
}
tracing::debug!("Retrieving chunks...");
let mut stream = res.bytes_stream();

View file

@ -61,6 +61,8 @@ pub enum ClientType {
///
/// can access age-restricted videos, cannot access non-embeddable videos
TvHtml5Embed,
/// Client used by youtube.com/tv
Tv,
/// Client used by the Android app
///
/// no obfuscated stream URLs, includes lower resolution audio streams
@ -74,7 +76,10 @@ pub enum ClientType {
impl ClientType {
fn is_web(self) -> bool {
match self {
ClientType::Desktop | ClientType::DesktopMusic | ClientType::TvHtml5Embed => true,
ClientType::Desktop
| ClientType::DesktopMusic
| ClientType::TvHtml5Embed
| ClientType::Tv => true,
ClientType::Android | ClientType::Ios => false,
}
}
@ -183,6 +188,7 @@ struct QContinuation<'a> {
}
const DEFAULT_UA: &str = "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0";
const TV_UA: &str = "Mozilla/5.0 (SMART-TV; Linux; Tizen 5.0) AppleWebKit/538.1 (KHTML, like Gecko) Version/5.0 NativeTVAds Safari/538.1";
const CONSENT_COOKIE: &str = "SOCS=CAISAiAD";
@ -191,12 +197,14 @@ const YOUTUBEI_V1_GAPIS_URL: &str = "https://youtubei.googleapis.com/youtubei/v1
const YOUTUBE_MUSIC_V1_URL: &str = "https://music.youtube.com/youtubei/v1/";
const YOUTUBE_HOME_URL: &str = "https://www.youtube.com/";
const YOUTUBE_MUSIC_HOME_URL: &str = "https://music.youtube.com/";
const YOUTUBE_TV_URL: &str = "https://www.youtube.com/tv";
const DISABLE_PRETTY_PRINT_PARAMETER: &str = "prettyPrint=false";
// Desktop client
const DESKTOP_CLIENT_VERSION: &str = "2.20230126.00.00";
const TVHTML5_CLIENT_VERSION: &str = "2.0";
const TV_CLIENT_VERSION: &str = "7.20240724.13.00";
const DESKTOP_MUSIC_CLIENT_VERSION: &str = "1.20230123.01.01";
// Mobile client
@ -454,7 +462,7 @@ impl RustyPipeBuilder {
self.build_with_client(ClientBuilder::new())
}
/// Create a new, configured RustyPipe instance using a Reqwest client builder.
/// Create a new, configured RustyPipe instance using a Reqwest [`ClientBuilder`].
pub fn build_with_client(self, mut client_builder: ClientBuilder) -> Result<RustyPipe, Error> {
let user_agent = self
.user_agent
@ -717,31 +725,33 @@ impl RustyPipe {
}
}
/// Get the internal HTTP client
///
/// Can be used for downloading videos or custom YT requests.
#[must_use]
pub fn http_client(&self) -> &Client {
&self.inner.http
}
/// Execute the given http request.
async fn http_request(&self, request: &Request) -> Result<Response, reqwest::Error> {
let mut last_resp = None;
for n in 0..=self.inner.n_http_retries {
let resp = self
.inner
.http
.execute(request.try_clone().unwrap())
.await?;
let resp = self.inner.http.execute(request.try_clone().unwrap()).await;
let status = resp.status();
// Immediately return in case of success or unrecoverable status code
if status.is_success()
|| (!status.is_server_error() && status != StatusCode::TOO_MANY_REQUESTS)
{
return Ok(resp);
}
let err = match resp {
Ok(resp) => {
let status = resp.status();
// Immediately return in case of success or unrecoverable status code
if status.is_success()
|| (!status.is_server_error() && status != StatusCode::TOO_MANY_REQUESTS)
{
return Ok(resp);
}
last_resp = Some(Ok(resp));
status.to_string()
}
Err(e) => {
// Retry in case of a timeout error
if !e.is_timeout() {
return Err(e);
}
last_resp = Some(Err(e));
"timeout".to_string()
}
};
// Retry in case of a recoverable status code (server err, too many requests)
if n != self.inner.n_http_retries {
@ -749,15 +759,13 @@ impl RustyPipe {
tracing::warn!(
"Retry attempt #{}. Error: {}. Waiting {} ms",
n + 1,
status,
err,
ms
);
tokio::time::sleep(Duration::from_millis(ms.into())).await;
}
last_resp = Some(resp);
}
Ok(last_resp.unwrap())
last_resp.unwrap()
}
/// Execute the given http request, returning an error in case of a
@ -1098,6 +1106,7 @@ impl RustyPipeQuery {
ClientType::Desktop | ClientType::DesktopMusic | ClientType::TvHtml5Embed => {
Cow::Borrowed(&self.client.inner.user_agent)
}
ClientType::Tv => TV_UA.into(),
ClientType::Android => format!(
"com.google.android.youtube/{} (Linux; U; Android 12; {}) gzip",
MOBILE_CLIENT_VERSION, self.opts.country
@ -1178,6 +1187,24 @@ impl RustyPipeQuery {
embed_url: YOUTUBE_HOME_URL,
}),
},
ClientType::Tv => YTContext {
client: ClientInfo {
client_name: "TVHTML5",
client_version: Cow::Borrowed(TV_CLIENT_VERSION),
client_screen: Some("WATCH"),
platform: "TV",
device_model: Some("SmartTV"),
visitor_data,
hl,
gl,
..Default::default()
},
request: Some(RequestYT::default()),
user: User::default(),
third_party: Some(ThirdParty {
embed_url: YOUTUBE_TV_URL,
}),
},
ClientType::Android => YTContext {
client: ClientInfo {
client_name: "ANDROID",
@ -1266,6 +1293,17 @@ impl RustyPipeQuery {
.header(header::REFERER, YOUTUBE_HOME_URL)
.header("X-YouTube-Client-Name", "1")
.header("X-YouTube-Client-Version", TVHTML5_CLIENT_VERSION),
ClientType::Tv => self
.client
.inner
.http
.post(format!(
"{YOUTUBEI_V1_URL}{endpoint}?{DISABLE_PRETTY_PRINT_PARAMETER}"
))
.header(header::ORIGIN, YOUTUBE_HOME_URL)
.header(header::REFERER, YOUTUBE_TV_URL)
.header("X-YouTube-Client-Name", "7")
.header("X-YouTube-Client-Version", TV_CLIENT_VERSION),
ClientType::Android => self
.client
.inner

View file

@ -13,8 +13,8 @@ use crate::{
deobfuscate::Deobfuscator,
error::{internal::DeobfError, Error, ExtractionError, UnavailabilityReason},
model::{
traits::QualityOrd, AudioCodec, AudioFormat, AudioStream, AudioTrack, ChannelId, Frameset,
Subtitle, VideoCodec, VideoFormat, VideoPlayer, VideoPlayerDetails, VideoStream,
traits::QualityOrd, AudioCodec, AudioFormat, AudioStream, AudioTrack, Frameset, Subtitle,
VideoCodec, VideoFormat, VideoPlayer, VideoPlayerDetails, VideoStream,
},
util,
};
@ -265,10 +265,8 @@ impl MapResponse<VideoPlayer> for response::Player {
description: video_details.short_description,
duration: video_details.length_seconds,
thumbnail: video_details.thumbnail.into(),
channel: ChannelId {
id: video_details.channel_id,
name: video_details.author,
},
channel_id: video_details.channel_id,
channel_name: video_details.author,
view_count: video_details.view_count,
keywords: video_details.keywords,
is_live,
@ -737,6 +735,7 @@ mod tests {
#[rstest]
#[case::desktop(ClientType::Desktop)]
#[case::desktop_music(ClientType::DesktopMusic)]
#[case::tv(ClientType::Tv)]
#[case::tv_html5_embed(ClientType::TvHtml5Embed)]
#[case::android(ClientType::Android)]
#[case::ios(ClientType::Ios)]

View file

@ -236,7 +236,7 @@ pub(crate) struct CaptionTrack {
#[serde(rename_all = "camelCase")]
pub(crate) struct VideoDetails {
pub video_id: String,
pub title: String,
pub title: Option<String>,
#[serde_as(as = "DisplayFromStr")]
pub length_seconds: u32,
#[serde(default)]
@ -245,9 +245,9 @@ pub(crate) struct VideoDetails {
pub short_description: Option<String>,
#[serde(default)]
pub thumbnail: Thumbnails,
#[serde_as(as = "DisplayFromStr")]
pub view_count: u64,
pub author: String,
#[serde_as(as = "Option<DisplayFromStr>")]
pub view_count: Option<u64>,
pub author: Option<String>,
pub is_live_content: bool,
}

View file

@ -5,7 +5,7 @@ expression: map_res.c
VideoPlayer(
details: VideoPlayerDetails(
id: "pPvd8UxmSbQ",
name: "Inspiring Cinematic Uplifting (Creative Commons)",
name: Some("Inspiring Cinematic Uplifting (Creative Commons)"),
description: Some("► Download Music: http://bit.ly/2QLufeh\nImportant to know! You can download this track for free through Patreon. You will pay only for new tracks! So join others and let\'s make next track together!\n\n► MORE MUSIC: Become my patron and get access to all our music from Patreon library. More Info here: http://bit.ly/2JJDFHb\n\n► Additional edit versions of this track you can download here: http://bit.ly/2WdRinT (5 versions)\n--------------------- \n\n►DESCRIPTION:\nInspiring Cinematic Uplifting Trailer Background - epic music for trailer video project with powerful drums, energetic orchestra and gentle piano melody. This motivational cinematic theme will work as perfect background for beautiful epic moments, landscapes, nature, drone video, motivational products and achievements.\n--------------------- \n\n► LICENSE:\n● If you need a license for your project, you can purchase it here: \nhttps://1.envato.market/ajicu (Audiojungle)\nhttps://bit.ly/3fWZZuI (Pond5)\n--------------------- \n\n► LISTEN ON:\n● Spotify - https://spoti.fi/2sHm3UH\n● Apple Music - https://apple.co/3qBjbUO\n--------------------- \n\n► SUBSCRIBE FOR MORE: \nPatreon: http://bit.ly/2JJDFHb\nYoutube: http://bit.ly/2AYBzfA\nFacebook: http://bit.ly/2T6dTx5\nInstagram: http://bit.ly/2BHJ8rB\nTwitter: http://bit.ly/2MwtOlT\nSoundCloud: http://bit.ly/2IwVVmt\nAudiojungle: https://1.envato.market/ajrsm\nPond5: https://bit.ly/2TLi1rW\n--------------------- \n►Photo by Vittorio Staffolani from Pexels\n--------------------- \n\nFAQ:\n\n► Can I use this music in my videos? \n● Sure! Just download this track and you are ready to use it! We only ask to credit us. \n-------------------- \n\n► What is \"Creative Commons\"? \nCreative Commons is a system that allows you to legally use “some rights reserved” music, movies, images, and other content — all for free. Licensees may copy, distribute, display and perform the work and make derivative works and remixes based on it only if they give the author or licensor the credits.\n-------------------- \n\n► Will I have any copyright issues with this track?\n● No, you should not have any copyright problems with this track!\n-------------------- \n\n► Is it necessary to become your patron?\n● No it\'s not necessary. But we recommend you to become our patron because you will get access to huge library of music. You will download only highest quality files. You will find additional edited versions of every track. You always be tuned with our news. You will find music not only from Roman Senyk but also from another talented authors.\n-------------------- \n\n► Why I received a copyright claim when I used this track?\n● Do not panic! This is very common situation. Content ID fingerprint system can mismatch our music. Just dispute the claim by showing our original track. Or send us the link to your video (romansenykmusic@gmail.com) and attach some screenshot with claim information. Claim will be released until 24 hours!\n\n► How to credit you in my video?\n● Just add to the description of your project information about Author, Name of Song and the link to our original track. Or copy and paste:\n\nMusic Info: Inspiring Cinematic Uplifting by RomanSenykMusic.\nMusic Link: https://youtu.be/pPvd8UxmSbQ\n--------------------- \n\n► If you have any questions, you can write in the comments for this video or by email: romansenykmusic@gmail.com\n--------------------- \n\nStay tuned! The best is yet to come! \nThanks For Listening!\nRoman Senyk"),
duration: 163,
thumbnail: [
@ -30,11 +30,9 @@ VideoPlayer(
height: 480,
),
],
channel: ChannelId(
id: "UCbxxEi-ImPlbLx5F-fHetEg",
name: "RomanSenykMusic - Royalty Free Music",
),
view_count: 426567,
channel_id: "UCbxxEi-ImPlbLx5F-fHetEg",
channel_name: Some("RomanSenykMusic - Royalty Free Music"),
view_count: Some(426567),
keywords: [
"no copyright music",
"background music",

View file

@ -5,7 +5,7 @@ expression: map_res.c
VideoPlayer(
details: VideoPlayerDetails(
id: "pPvd8UxmSbQ",
name: "Inspiring Cinematic Uplifting (Creative Commons)",
name: Some("Inspiring Cinematic Uplifting (Creative Commons)"),
description: Some("► Download Music: http://bit.ly/2QLufeh\nImportant to know! You can download this track for free through Patreon. You will pay only for new tracks! So join others and let\'s make next track together!\n\n► MORE MUSIC: Become my patron and get access to all our music from Patreon library. More Info here: http://bit.ly/2JJDFHb\n\n► Additional edit versions of this track you can download here: http://bit.ly/2WdRinT (5 versions)\n--------------------- \n\n►DESCRIPTION:\nInspiring Cinematic Uplifting Trailer Background - epic music for trailer video project with powerful drums, energetic orchestra and gentle piano melody. This motivational cinematic theme will work as perfect background for beautiful epic moments, landscapes, nature, drone video, motivational products and achievements.\n--------------------- \n\n► LICENSE:\n● If you need a license for your project, you can purchase it here: \nhttps://1.envato.market/ajicu (Audiojungle)\nhttps://bit.ly/3fWZZuI (Pond5)\n--------------------- \n\n► LISTEN ON:\n● Spotify - https://spoti.fi/2sHm3UH\n● Apple Music - https://apple.co/3qBjbUO\n--------------------- \n\n► SUBSCRIBE FOR MORE: \nPatreon: http://bit.ly/2JJDFHb\nYoutube: http://bit.ly/2AYBzfA\nFacebook: http://bit.ly/2T6dTx5\nInstagram: http://bit.ly/2BHJ8rB\nTwitter: http://bit.ly/2MwtOlT\nSoundCloud: http://bit.ly/2IwVVmt\nAudiojungle: https://1.envato.market/ajrsm\nPond5: https://bit.ly/2TLi1rW\n--------------------- \n►Photo by Vittorio Staffolani from Pexels\n--------------------- \n\nFAQ:\n\n► Can I use this music in my videos? \n● Sure! Just download this track and you are ready to use it! We only ask to credit us. \n-------------------- \n\n► What is \"Creative Commons\"? \nCreative Commons is a system that allows you to legally use “some rights reserved” music, movies, images, and other content — all for free. Licensees may copy, distribute, display and perform the work and make derivative works and remixes based on it only if they give the author or licensor the credits.\n-------------------- \n\n► Will I have any copyright issues with this track?\n● No, you should not have any copyright problems with this track!\n-------------------- \n\n► Is it necessary to become your patron?\n● No it\'s not necessary. But we recommend you to become our patron because you will get access to huge library of music. You will download only highest quality files. You will find additional edited versions of every track. You always be tuned with our news. You will find music not only from Roman Senyk but also from another talented authors.\n-------------------- \n\n► Why I received a copyright claim when I used this track?\n● Do not panic! This is very common situation. Content ID fingerprint system can mismatch our music. Just dispute the claim by showing our original track. Or send us the link to your video (romansenykmusic@gmail.com) and attach some screenshot with claim information. Claim will be released until 24 hours!\n\n► How to credit you in my video?\n● Just add to the description of your project information about Author, Name of Song and the link to our original track. Or copy and paste:\n\nMusic Info: Inspiring Cinematic Uplifting by RomanSenykMusic.\nMusic Link: https://youtu.be/pPvd8UxmSbQ\n--------------------- \n\n► If you have any questions, you can write in the comments for this video or by email: romansenykmusic@gmail.com\n--------------------- \n\nStay tuned! The best is yet to come! \nThanks For Listening!\nRoman Senyk"),
duration: 163,
thumbnail: [
@ -35,11 +35,9 @@ VideoPlayer(
height: 1080,
),
],
channel: ChannelId(
id: "UCbxxEi-ImPlbLx5F-fHetEg",
name: "RomanSenykMusic - Royalty Free Music",
),
view_count: 426567,
channel_id: "UCbxxEi-ImPlbLx5F-fHetEg",
channel_name: Some("RomanSenykMusic - Royalty Free Music"),
view_count: Some(426567),
keywords: [
"no copyright music",
"background music",

View file

@ -5,7 +5,7 @@ expression: map_res.c
VideoPlayer(
details: VideoPlayerDetails(
id: "pPvd8UxmSbQ",
name: "Inspiring Cinematic Uplifting",
name: Some("Inspiring Cinematic Uplifting"),
description: None,
duration: 163,
thumbnail: [
@ -25,11 +25,9 @@ VideoPlayer(
height: 480,
),
],
channel: ChannelId(
id: "UCbxxEi-ImPlbLx5F-fHetEg",
name: "Romansenykmusic",
),
view_count: 426583,
channel_id: "UCbxxEi-ImPlbLx5F-fHetEg",
channel_name: Some("Romansenykmusic"),
view_count: Some(426583),
keywords: [],
is_live: false,
is_live_content: false,

View file

@ -5,7 +5,7 @@ expression: map_res.c
VideoPlayer(
details: VideoPlayerDetails(
id: "pPvd8UxmSbQ",
name: "Inspiring Cinematic Uplifting (Creative Commons)",
name: Some("Inspiring Cinematic Uplifting (Creative Commons)"),
description: Some("► Download Music: http://bit.ly/2QLufeh\nImportant to know! You can download this track for free through Patreon. You will pay only for new tracks! So join others and let\'s make next track together!\n\n► MORE MUSIC: Become my patron and get access to all our music from Patreon library. More Info here: http://bit.ly/2JJDFHb\n\n► Additional edit versions of this track you can download here: http://bit.ly/2WdRinT (5 versions)\n--------------------- \n\n►DESCRIPTION:\nInspiring Cinematic Uplifting Trailer Background - epic music for trailer video project with powerful drums, energetic orchestra and gentle piano melody. This motivational cinematic theme will work as perfect background for beautiful epic moments, landscapes, nature, drone video, motivational products and achievements.\n--------------------- \n\n► LICENSE:\n● If you need a license for your project, you can purchase it here: \nhttps://1.envato.market/ajicu (Audiojungle)\nhttps://bit.ly/3fWZZuI (Pond5)\n--------------------- \n\n► LISTEN ON:\n● Spotify - https://spoti.fi/2sHm3UH\n● Apple Music - https://apple.co/3qBjbUO\n--------------------- \n\n► SUBSCRIBE FOR MORE: \nPatreon: http://bit.ly/2JJDFHb\nYoutube: http://bit.ly/2AYBzfA\nFacebook: http://bit.ly/2T6dTx5\nInstagram: http://bit.ly/2BHJ8rB\nTwitter: http://bit.ly/2MwtOlT\nSoundCloud: http://bit.ly/2IwVVmt\nAudiojungle: https://1.envato.market/ajrsm\nPond5: https://bit.ly/2TLi1rW\n--------------------- \n►Photo by Vittorio Staffolani from Pexels\n--------------------- \n\nFAQ:\n\n► Can I use this music in my videos? \n● Sure! Just download this track and you are ready to use it! We only ask to credit us. \n-------------------- \n\n► What is \"Creative Commons\"? \nCreative Commons is a system that allows you to legally use “some rights reserved” music, movies, images, and other content — all for free. Licensees may copy, distribute, display and perform the work and make derivative works and remixes based on it only if they give the author or licensor the credits.\n-------------------- \n\n► Will I have any copyright issues with this track?\n● No, you should not have any copyright problems with this track!\n-------------------- \n\n► Is it necessary to become your patron?\n● No it\'s not necessary. But we recommend you to become our patron because you will get access to huge library of music. You will download only highest quality files. You will find additional edited versions of every track. You always be tuned with our news. You will find music not only from Roman Senyk but also from another talented authors.\n-------------------- \n\n► Why I received a copyright claim when I used this track?\n● Do not panic! This is very common situation. Content ID fingerprint system can mismatch our music. Just dispute the claim by showing our original track. Or send us the link to your video (romansenykmusic@gmail.com) and attach some screenshot with claim information. Claim will be released until 24 hours!\n\n► How to credit you in my video?\n● Just add to the description of your project information about Author, Name of Song and the link to our original track. Or copy and paste:\n\nMusic Info: Inspiring Cinematic Uplifting by RomanSenykMusic.\nMusic Link: https://youtu.be/pPvd8UxmSbQ\n--------------------- \n\n► If you have any questions, you can write in the comments for this video or by email: romansenykmusic@gmail.com\n--------------------- \n\nStay tuned! The best is yet to come! \nThanks For Listening!\nRoman Senyk"),
duration: 163,
thumbnail: [
@ -25,11 +25,9 @@ VideoPlayer(
height: 480,
),
],
channel: ChannelId(
id: "UCbxxEi-ImPlbLx5F-fHetEg",
name: "RomanSenykMusic - Royalty Free Music",
),
view_count: 426567,
channel_id: "UCbxxEi-ImPlbLx5F-fHetEg",
channel_name: Some("RomanSenykMusic - Royalty Free Music"),
view_count: Some(426567),
keywords: [
"no copyright music",
"background music",

View file

@ -0,0 +1,518 @@
---
source: src/client/player.rs
expression: map_res.c
---
VideoPlayer(
details: VideoPlayerDetails(
id: "pPvd8UxmSbQ",
name: None,
description: None,
duration: 163,
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/pPvd8UxmSbQ/hqdefault.jpg",
width: 480,
height: 360,
),
],
channel_id: "UCbxxEi-ImPlbLx5F-fHetEg",
channel_name: None,
view_count: None,
keywords: [],
is_live: false,
is_live_content: false,
),
video_streams: [
VideoStream(
url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?bui=AXc671IT4iUCpJNJWUitTMgIi6njuKSsi3MNed1Szyf0qysTX0v1Nf6AyCvjIGbek5Fn50kuBrGtRJ5q&c=TVHTML5&clen=10262148&dur=163.096&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=18&lmt=1700885551970466&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=BMzwItzIOB1HhmG&ns=YmgbZhlLp0C-9ilsQWGAyUAQ&pl=26&ratebypass=yes&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRAIgUah4qH8RqPzmo75ExCWSiRYlUlsAk0v9gl638LitVNICICxFs5lK3CsmOAja0bsXavXkyykzpdhHZKGXOZQYT1f8&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&svpuc=1&txp=1318224&vprv=1&xpc=EgVo2aDSNQ%3D%3D",
itag: 18,
bitrate: 503574,
average_bitrate: 503367,
size: Some(10262148),
index_range: None,
init_range: None,
duration_ms: Some(163096),
width: 640,
height: 360,
fps: 30,
quality: "360p",
hdr: false,
mime: "video/mp4; codecs=\"avc1.42001E, mp4a.40.2\"",
format: mp4,
codec: avc1,
),
],
video_only_streams: [
VideoStream(
url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=2273274&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=160&keepalive=yes&lmt=1705967288821438&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRAIgb8eXnQ6MSJ3PuvFVBdYIWTnFobH8mTC9zbZpBNxLbBYCICkPLKEm3gNbW5HIFXs7bwF5rSqUKHHnXNK91qMslQog&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D",
itag: 160,
bitrate: 114816,
average_bitrate: 111551,
size: Some(2273274),
index_range: Some(Range(
start: 738,
end: 1165,
)),
init_range: Some(Range(
start: 0,
end: 737,
)),
duration_ms: Some(163029),
width: 256,
height: 144,
fps: 30,
quality: "144p",
hdr: false,
mime: "video/mp4; codecs=\"avc1.4d400c\"",
format: mp4,
codec: avc1,
),
VideoStream(
url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=1151892&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=278&keepalive=yes&lmt=1705966620402771&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIhAP4IybR7cZRpx7IX1ke6UIu_hdFZN3LOuHBDywg_xv5WAiB8_XEx8VhT9OlFxmM-cY0fl6-7GT9uj3clMIPDk2w7cA%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D",
itag: 278,
bitrate: 70630,
average_bitrate: 56524,
size: Some(1151892),
index_range: Some(Range(
start: 218,
end: 767,
)),
init_range: Some(Range(
start: 0,
end: 217,
)),
duration_ms: Some(163029),
width: 256,
height: 144,
fps: 30,
quality: "144p",
hdr: false,
mime: "video/webm; codecs=\"vp9\"",
format: webm,
codec: vp9,
),
VideoStream(
url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=5026513&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=133&keepalive=yes&lmt=1705967298859029&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRAIgPF0ms4OEe15BTjOFVCkvf52UeTUf0b62_pavCfEyGjcCIH-0AoxzyT8iioWFFaX7iYjqzzaUTpo8rgAPQ0uX8DJa&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D",
itag: 133,
bitrate: 257417,
average_bitrate: 246656,
size: Some(5026513),
index_range: Some(Range(
start: 739,
end: 1166,
)),
init_range: Some(Range(
start: 0,
end: 738,
)),
duration_ms: Some(163029),
width: 426,
height: 240,
fps: 30,
quality: "240p",
hdr: false,
mime: "video/mp4; codecs=\"avc1.4d4015\"",
format: mp4,
codec: avc1,
),
VideoStream(
url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=2541351&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=242&keepalive=yes&lmt=1705966614837727&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIgKj1JyMGwYtf16zLJsmbnizz5_v3jaZSa7-j-ls8-qzECIQDKUd50iIc52h7zOX50Hf1SkbV9h-hP4QHs-wkik1fk6Q%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D",
itag: 242,
bitrate: 149589,
average_bitrate: 124706,
size: Some(2541351),
index_range: Some(Range(
start: 219,
end: 768,
)),
init_range: Some(Range(
start: 0,
end: 218,
)),
duration_ms: Some(163029),
width: 426,
height: 240,
fps: 30,
quality: "240p",
hdr: false,
mime: "video/webm; codecs=\"vp9\"",
format: webm,
codec: vp9,
),
VideoStream(
url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=7810925&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=134&keepalive=yes&lmt=1705967286812435&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRgIhAJ92IgZgdk3_WLsfzJV_ZyrSFSbzpsoJh3DkRKDHbNxzAiEA9UbnVlXQ2S3BUimLmWC5TZQfhIkc-PlLnZ81fL0S5yA%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D",
itag: 134,
bitrate: 537902,
average_bitrate: 383290,
size: Some(7810925),
index_range: Some(Range(
start: 740,
end: 1167,
)),
init_range: Some(Range(
start: 0,
end: 739,
)),
duration_ms: Some(163029),
width: 640,
height: 360,
fps: 30,
quality: "360p",
hdr: false,
mime: "video/mp4; codecs=\"avc1.4d401e\"",
format: mp4,
codec: avc1,
),
VideoStream(
url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=4188954&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=243&keepalive=yes&lmt=1705966624121874&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIgSCLGQvdZKNXym0zt7c3Yw_4e0J8-wNxtPagPRRn4dRoCIQCOj0IzalNG4EcowBIyK2LC6NLFDr8Zt6sNVkqPjw6lGg%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D",
itag: 243,
bitrate: 248858,
average_bitrate: 205556,
size: Some(4188954),
index_range: Some(Range(
start: 220,
end: 770,
)),
init_range: Some(Range(
start: 0,
end: 219,
)),
duration_ms: Some(163029),
width: 640,
height: 360,
fps: 30,
quality: "360p",
hdr: false,
mime: "video/webm; codecs=\"vp9\"",
format: webm,
codec: vp9,
),
VideoStream(
url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=14723538&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=135&keepalive=yes&lmt=1705967282545273&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRgIhAM843wAa1e7Gc1S69gfXckm7hdgIKPXp0bUSh3hO6W5zAiEA-DDEPGsZBmF5N8VbPy75dhy3rLpE1F18KtWgmrUm2Pg%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D",
itag: 135,
bitrate: 978945,
average_bitrate: 722499,
size: Some(14723538),
index_range: Some(Range(
start: 740,
end: 1167,
)),
init_range: Some(Range(
start: 0,
end: 739,
)),
duration_ms: Some(163029),
width: 854,
height: 480,
fps: 30,
quality: "480p",
hdr: false,
mime: "video/mp4; codecs=\"avc1.4d401f\"",
format: mp4,
codec: avc1,
),
VideoStream(
url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=7788899&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=244&keepalive=yes&lmt=1705966622098793&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIhAKGyn799bfkVHYE195sPmD60dCMppqJrBM0O-sjgYTzzAiAoBjkNAtL90sXw2YP9UTW9JrMhPSvPiBI_KiCVMJAkFQ%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D",
itag: 244,
bitrate: 467884,
average_bitrate: 382209,
size: Some(7788899),
index_range: Some(Range(
start: 220,
end: 770,
)),
init_range: Some(Range(
start: 0,
end: 219,
)),
duration_ms: Some(163029),
width: 854,
height: 480,
fps: 30,
quality: "480p",
hdr: false,
mime: "video/webm; codecs=\"vp9\"",
format: webm,
codec: vp9,
),
VideoStream(
url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=24616305&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=136&keepalive=yes&lmt=1705967307531372&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRgIhAM57L2Utesn4xVyT0HSwR9Khv_S-efx4uFAbCPkZFoRXAiEAtIu63-jF2_FZkOMmZAqGU3SRU9QgxoajRjBhMFwcOuk%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D",
itag: 136,
bitrate: 1560439,
average_bitrate: 1207947,
size: Some(24616305),
index_range: Some(Range(
start: 739,
end: 1166,
)),
init_range: Some(Range(
start: 0,
end: 738,
)),
duration_ms: Some(163029),
width: 1280,
height: 720,
fps: 30,
quality: "720p",
hdr: false,
mime: "video/mp4; codecs=\"avc1.4d401f\"",
format: mp4,
codec: avc1,
),
VideoStream(
url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=34544823&dur=163.046&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=298&keepalive=yes&lmt=1705967092637061&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRgIhAIIGU41JunuODw9qIlSoYQcwkCYO6k9XOVlDn1Nxqnu7AiEAoiMOgYU8s8lp01fW0L86hHrSrtlvOLSI9XA50iyIGBc%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D",
itag: 298,
bitrate: 2188961,
average_bitrate: 1694973,
size: Some(34544823),
index_range: Some(Range(
start: 739,
end: 1166,
)),
init_range: Some(Range(
start: 0,
end: 738,
)),
duration_ms: Some(163046),
width: 1280,
height: 720,
fps: 60,
quality: "720p60",
hdr: false,
mime: "video/mp4; codecs=\"avc1.4d4020\"",
format: mp4,
codec: avc1,
),
VideoStream(
url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=14723992&dur=163.029&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=247&keepalive=yes&lmt=1705966613897741&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRgIhAL-upITxk7r9FQL5F4WL0A6SjPw673qyyzmXIC48eKfTAiEAlkdkx7IFYtehbhKakbffvIebpPXRtxSgBWLl7WEHCrE%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D",
itag: 247,
bitrate: 929607,
average_bitrate: 722521,
size: Some(14723992),
index_range: Some(Range(
start: 220,
end: 770,
)),
init_range: Some(Range(
start: 0,
end: 219,
)),
duration_ms: Some(163029),
width: 1280,
height: 720,
fps: 30,
quality: "720p",
hdr: false,
mime: "video/webm; codecs=\"vp9\"",
format: webm,
codec: vp9,
),
VideoStream(
url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=30205331&dur=163.046&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=302&keepalive=yes&lmt=1705966545733919&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIhAL428Az_BKxxff4FlH4WleHSy4Igq3mR71NuTMOc9xU3AiBN4lXfH9DklGaQUMnOT8wAhiMuzR73bW3cwr744TSoNA%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D",
itag: 302,
bitrate: 2250391,
average_bitrate: 1482051,
size: Some(30205331),
index_range: Some(Range(
start: 219,
end: 786,
)),
init_range: Some(Range(
start: 0,
end: 218,
)),
duration_ms: Some(163046),
width: 1280,
height: 720,
fps: 60,
quality: "720p60",
hdr: false,
mime: "video/webm; codecs=\"vp9\"",
format: webm,
codec: vp9,
),
VideoStream(
url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=62057888&dur=163.046&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=299&keepalive=yes&lmt=1705967093743693&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIgBEemc0Cvd3KhNooNRblgX64_fjNSP30RmWDfFwDR7qYCIQCXpQ9FO0_X93ZHcyvRZCKX5gbJuusCReaRcJbRLFsM_g%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D",
itag: 299,
bitrate: 3926810,
average_bitrate: 3044926,
size: Some(62057888),
index_range: Some(Range(
start: 740,
end: 1167,
)),
init_range: Some(Range(
start: 0,
end: 739,
)),
duration_ms: Some(163046),
width: 1920,
height: 1080,
fps: 60,
quality: "1080p60",
hdr: false,
mime: "video/mp4; codecs=\"avc1.64002a\"",
format: mp4,
codec: avc1,
),
VideoStream(
url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?aitags=133%2C134%2C135%2C136%2C160%2C242%2C243%2C244%2C247%2C278%2C298%2C299%2C302%2C303&bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=55300085&dur=163.046&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=303&keepalive=yes&lmt=1705966651743358&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=video%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIgTZlmOcsLYJ_a9SnVLehXnaoajtreQO97qawEIDPEi8sCIQDKFdtBWWMuQUb9X8H-x92B3q-y0g8TvAPanR95cfklXQ%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=130F224&vprv=1&xpc=EgVo2aDSNQ%3D%3D",
itag: 303,
bitrate: 3473307,
average_bitrate: 2713348,
size: Some(55300085),
index_range: Some(Range(
start: 219,
end: 792,
)),
init_range: Some(Range(
start: 0,
end: 218,
)),
duration_ms: Some(163046),
width: 1920,
height: 1080,
fps: 60,
quality: "1080p60",
hdr: false,
mime: "video/webm; codecs=\"vp9\"",
format: webm,
codec: vp9,
),
],
audio_streams: [
AudioStream(
url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=934750&dur=163.061&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=249&keepalive=yes&lmt=1714877357172339&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIhAItfaWkRs94vqyae7GR4M1xHoQO2lduvNRFugRSf0h-IAiA9fdLOJMwPI8vAO2C13igyv2qGSpOlKQptS4sN6p5Ffw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D",
itag: 249,
bitrate: 53073,
average_bitrate: 45860,
size: 934750,
index_range: Some(Range(
start: 266,
end: 551,
)),
init_range: Some(Range(
start: 0,
end: 265,
)),
duration_ms: Some(163061),
mime: "audio/webm; codecs=\"opus\"",
format: webm,
codec: opus,
channels: Some(2),
loudness_db: Some(5.21),
track: None,
),
AudioStream(
url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=1245582&dur=163.061&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=250&keepalive=yes&lmt=1714877466693058&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIgdJ1SjWwaloQecEblSIMFp2qFmpG_kKYZP1vX_M55dE0CIQCDSfa_FsaiFRcNL-1LRTgCIRSO7dj5vrpKR1Ya-KbmMw%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D",
itag: 250,
bitrate: 71197,
average_bitrate: 61109,
size: 1245582,
index_range: Some(Range(
start: 266,
end: 551,
)),
init_range: Some(Range(
start: 0,
end: 265,
)),
duration_ms: Some(163061),
mime: "audio/webm; codecs=\"opus\"",
format: webm,
codec: opus,
channels: Some(2),
loudness_db: Some(5.21),
track: None,
),
AudioStream(
url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=2640283&dur=163.096&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=140&keepalive=yes&lmt=1705966477945761&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fmp4&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRAIgSxdbLrbojMVJcyRzsI2TrzOf78LN28bWcsHpbs4QXDwCIHidfXoriWMHfuiktUCdzLuUmksU7r5vITdh6u0puNmx&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D",
itag: 140,
bitrate: 130268,
average_bitrate: 129508,
size: 2640283,
index_range: Some(Range(
start: 632,
end: 867,
)),
init_range: Some(Range(
start: 0,
end: 631,
)),
duration_ms: Some(163096),
mime: "audio/mp4; codecs=\"mp4a.40.2\"",
format: m4a,
codec: mp4a,
channels: Some(2),
loudness_db: Some(5.2200003),
track: None,
),
AudioStream(
url: "https://rr5---sn-h0jeenek.googlevideo.com/videoplayback?bui=AXc671IvQBUNCtxNiAkj0M-Bvcb-N5cUu1XFk68f4Cnj0sFLEy1sixyW5lThzLYJXioG8kVQ2xT9KNLS&c=TVHTML5&clen=2480393&dur=163.061&ei=viioZtTdKteHi9oPl42KsAg&expire=1722318110&fvip=4&gir=yes&id=o-AC7iotZ_nCvg7C6fK7ofX174GXVOdwW68lsyXLLmCs0h&initcwndbps=1957500&ip=93.235.183.158&itag=251&keepalive=yes&lmt=1714877359450110&lmw=1&lsig=AGtxev0wRgIhANyFV4Ji7jlkXvfkb_czMQDZCiu6AbJ3Kzyv_s9V9WyvAiEA0o8XuM9kyh98hG1yg7h44L3I5OAUXuTpQdjxUaZ1V4A%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&mh=mQ&mime=audio%2Fwebm&mm=31%2C29&mn=sn-h0jeenek%2Csn-h0jelnez&ms=au%2Crdu&mt=1722295996&mv=m&mvi=5&n=SWvqB0UTkUvifuM&ns=ZR8RwjQ3VJGDvQifdaM1IRMQ&pl=26&requiressl=yes&rqh=1&sefc=1&sig=AJfQdSswRQIgO0jG-x2l6AF7tjryIX_oM3np78WgNDiseezppLfbQrgCIQCVLdpDhclKc8vQgWGzKXcqsAxgNl5S3MlLT8u1Jeok2A%3D%3D&source=youtube&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&svpuc=1&txp=1308224&vprv=1&xpc=EgVo2aDSNQ%3D%3D",
itag: 251,
bitrate: 140833,
average_bitrate: 121691,
size: 2480393,
index_range: Some(Range(
start: 266,
end: 551,
)),
init_range: Some(Range(
start: 0,
end: 265,
)),
duration_ms: Some(163061),
mime: "audio/webm; codecs=\"opus\"",
format: webm,
codec: opus,
channels: Some(2),
loudness_db: Some(5.21),
track: None,
),
],
subtitles: [
Subtitle(
url: "https://www.youtube.com/api/timedtext?v=pPvd8UxmSbQ&ei=viioZtTdKteHi9oPl42KsAg&caps=asr&opi=112496729&exp=xbt&xoaf=5&hl=en&ip=0.0.0.0&ipbits=0&expire=1722321710&sparams=ip,ipbits,expire,v,ei,caps,opi,exp,xoaf&signature=7B002D0C2B79781E0E46F374D5BB53C6059A5252.E7B05ECC8D799DB96F3C21B727A0161E0032CDFA&key=yt8&lang=en",
lang: "en",
lang_name: "English",
auto_generated: false,
),
],
expires_in_seconds: 21540,
hls_manifest_url: None,
dash_manifest_url: None,
preview_frames: [
Frameset(
url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L0/default.jpg?sqp=-oaymwGbA0g48quKqQOSA4gBAZUBAAAEQpgBMqABPKgBBLABELABDbABDLABELABFbABH7ABJrABLbABDrABDrABD7ABErABF7ABK7ABLLABKbABD7ABDrABELABFbABH7ABKrABMrABKbABD7ABEbABFLABGLABJrABPbABOLABLbABEbABFLABHrABKrABMbABS7ABR7ABNrABFbABHLABKbABLrABObABR7ABTbABP7ABJbABLrABN7ABPbABR7ABUrABUbABRbABM7ABQLABQrABQ7ABTLABRLABRrABQ7gBEbgBEbgBFbgBI7gBRLgBQ7gBQ7gBQ7gBEbgBE7gBFrgBL7gBQ7gBQ7gBQ7gBQ7gBFbgBFrgBKbgBQ7gBQ7gBQ7gBQ7gBQ7gBI7gBL7gBQ7gBQ7gBQ7gBQ7gBQ7gBQ7gBRLgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLCsCT8Lprh2S0ptmCRsWH7VtDl3YQ",
frame_width: 48,
frame_height: 27,
page_count: 1,
total_count: 100,
duration_per_frame: 0,
frames_per_page_x: 10,
frames_per_page_y: 10,
),
Frameset(
url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L1/M$M.jpg?sqp=-oaymwGbA0g48quKqQOSA4gBAZUBAAAEQpgBMqABPKgBBLABELABDbABDLABELABFbABH7ABJrABLbABDrABDrABD7ABErABF7ABK7ABLLABKbABD7ABDrABELABFbABH7ABKrABMrABKbABD7ABEbABFLABGLABJrABPbABOLABLbABEbABFLABHrABKrABMbABS7ABR7ABNrABFbABHLABKbABLrABObABR7ABTbABP7ABJbABLrABN7ABPbABR7ABUrABUbABRbABM7ABQLABQrABQ7ABTLABRLABRrABQ7gBEbgBEbgBFbgBI7gBRLgBQ7gBQ7gBQ7gBEbgBE7gBFrgBL7gBQ7gBQ7gBQ7gBQ7gBFbgBFrgBKbgBQ7gBQ7gBQ7gBQ7gBQ7gBI7gBL7gBQ7gBQ7gBQ7gBQ7gBQ7gBQ7gBRLgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLBXrdgfuYV1WLnTGXqZtSAUm8oZCA",
frame_width: 80,
frame_height: 45,
page_count: 1,
total_count: 83,
duration_per_frame: 2000,
frames_per_page_x: 10,
frames_per_page_y: 10,
),
Frameset(
url_template: "https://i.ytimg.com/sb/pPvd8UxmSbQ/storyboard3_L2/M$M.jpg?sqp=-oaymwGbA0g48quKqQOSA4gBAZUBAAAEQpgBMqABPKgBBLABELABDbABDLABELABFbABH7ABJrABLbABDrABDrABD7ABErABF7ABK7ABLLABKbABD7ABDrABELABFbABH7ABKrABMrABKbABD7ABEbABFLABGLABJrABPbABOLABLbABEbABFLABHrABKrABMbABS7ABR7ABNrABFbABHLABKbABLrABObABR7ABTbABP7ABJbABLrABN7ABPbABR7ABUrABUbABRbABM7ABQLABQrABQ7ABTLABRLABRrABQ7gBEbgBEbgBFbgBI7gBRLgBQ7gBQ7gBQ7gBEbgBE7gBFrgBL7gBQ7gBQ7gBQ7gBQ7gBFbgBFrgBKbgBQ7gBQ7gBQ7gBQ7gBQ7gBI7gBL7gBQ7gBQ7gBQ7gBQ7gBQ7gBQ7gBRLgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQ7gBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQ7gBQ7gBQ7gBQrgBQrgBQrgBQrgBQqLzl_8DBgjf8LPxBQ==&sigh=rs$AOn4CLCRazj84zMuwJLaCCc_PiUakX_YdQ",
frame_width: 160,
frame_height: 90,
page_count: 4,
total_count: 83,
duration_per_frame: 2000,
frames_per_page_x: 5,
frames_per_page_y: 5,
),
],
client_type: tv,
visitor_data: Some("CgtrbXRsWU4wUEtXbyi-0aC1BjIKCgJERRIEEgAgZg%3D%3D"),
)

View file

@ -5,7 +5,7 @@ expression: map_res.c
VideoPlayer(
details: VideoPlayerDetails(
id: "pPvd8UxmSbQ",
name: "Inspiring Cinematic Uplifting (Creative Commons)",
name: Some("Inspiring Cinematic Uplifting (Creative Commons)"),
description: Some("► Download Music: http://bit.ly/2QLufeh\nImportant to know! You can download this track for free through Patreon. You will pay only for new tracks! So join others and let\'s make next track together!\n\n► MORE MUSIC: Become my patron and get access to all our music from Patreon library. More Info here: http://bit.ly/2JJDFHb\n\n► Additional edit versions of this track you can download here: http://bit.ly/2WdRinT (5 versions)\n--------------------- \n\n►DESCRIPTION:\nInspiring Cinematic Uplifting Trailer Background - epic music for trailer video project with powerful drums, energetic orchestra and gentle piano melody. This motivational cinematic theme will work as perfect background for beautiful epic moments, landscapes, nature, drone video, motivational products and achievements.\n--------------------- \n\n► LICENSE:\n● If you need a license for your project, you can purchase it here: \nhttps://1.envato.market/ajicu (Audiojungle)\nhttps://bit.ly/3fWZZuI (Pond5)\n--------------------- \n\n► LISTEN ON:\n● Spotify - https://spoti.fi/2sHm3UH\n● Apple Music - https://apple.co/3qBjbUO\n--------------------- \n\n► SUBSCRIBE FOR MORE: \nPatreon: http://bit.ly/2JJDFHb\nYoutube: http://bit.ly/2AYBzfA\nFacebook: http://bit.ly/2T6dTx5\nInstagram: http://bit.ly/2BHJ8rB\nTwitter: http://bit.ly/2MwtOlT\nSoundCloud: http://bit.ly/2IwVVmt\nAudiojungle: https://1.envato.market/ajrsm\nPond5: https://bit.ly/2TLi1rW\n--------------------- \n►Photo by Vittorio Staffolani from Pexels\n--------------------- \n\nFAQ:\n\n► Can I use this music in my videos? \n● Sure! Just download this track and you are ready to use it! We only ask to credit us. \n-------------------- \n\n► What is \"Creative Commons\"? \nCreative Commons is a system that allows you to legally use “some rights reserved” music, movies, images, and other content — all for free. Licensees may copy, distribute, display and perform the work and make derivative works and remixes based on it only if they give the author or licensor the credits.\n-------------------- \n\n► Will I have any copyright issues with this track?\n● No, you should not have any copyright problems with this track!\n-------------------- \n\n► Is it necessary to become your patron?\n● No it\'s not necessary. But we recommend you to become our patron because you will get access to huge library of music. You will download only highest quality files. You will find additional edited versions of every track. You always be tuned with our news. You will find music not only from Roman Senyk but also from another talented authors.\n-------------------- \n\n► Why I received a copyright claim when I used this track?\n● Do not panic! This is very common situation. Content ID fingerprint system can mismatch our music. Just dispute the claim by showing our original track. Or send us the link to your video (romansenykmusic@gmail.com) and attach some screenshot with claim information. Claim will be released until 24 hours!\n\n► How to credit you in my video?\n● Just add to the description of your project information about Author, Name of Song and the link to our original track. Or copy and paste:\n\nMusic Info: Inspiring Cinematic Uplifting by RomanSenykMusic.\nMusic Link: https://youtu.be/pPvd8UxmSbQ\n--------------------- \n\n► If you have any questions, you can write in the comments for this video or by email: romansenykmusic@gmail.com\n--------------------- \n\nStay tuned! The best is yet to come! \nThanks For Listening!\nRoman Senyk"),
duration: 163,
thumbnail: [
@ -35,11 +35,9 @@ VideoPlayer(
height: 1080,
),
],
channel: ChannelId(
id: "UCbxxEi-ImPlbLx5F-fHetEg",
name: "RomanSenykMusic - Royalty Free Music",
),
view_count: 426567,
channel_id: "UCbxxEi-ImPlbLx5F-fHetEg",
channel_name: Some("RomanSenykMusic - Royalty Free Music"),
view_count: Some(426567),
keywords: [
"no copyright music",
"background music",

View file

@ -156,7 +156,7 @@ pub struct VideoPlayerDetails {
/// Unique YouTube video ID
pub id: String,
/// Video title
pub name: String,
pub name: Option<String>,
/// Video description in plaintext format
pub description: Option<String>,
/// Video duration in seconds
@ -165,10 +165,12 @@ pub struct VideoPlayerDetails {
pub duration: u32,
/// Video thumbnail
pub thumbnail: Vec<Thumbnail>,
/// Channel of the video
pub channel: ChannelId,
/// Channel ID of the video
pub channel_id: String,
/// Channel name of the video
pub channel_name: Option<String>,
/// Number of views / current viewers in case of a livestream.
pub view_count: u64,
pub view_count: Option<u64>,
/// List of words that describe the topic of the video
pub keywords: Vec<String>,
/// True if the video is an active livestream

View file

@ -233,6 +233,7 @@ macro_rules! yt_entity_owner_music {
};
}
/*
impl YtEntity for VideoPlayer {
fn id(&self) -> &str {
&self.details.id
@ -243,14 +244,33 @@ impl YtEntity for VideoPlayer {
}
fn channel_id(&self) -> Option<&str> {
Some(&self.details.channel.id)
Some(&self.details.channel_id)
}
fn channel_name(&self) -> Option<&str> {
Some(&self.details.channel.name)
self.details.channel_name.as_deref()
}
}
impl YtEntity for VideoPlayerDetails {
fn id(&self) -> &str {
&self.channel_id
}
fn name(&self) -> &str {
&self.name
}
fn channel_id(&self) -> Option<&str> {
Some(&self.channel_id)
}
fn channel_name(&self) -> Option<&str> {
self.channel_name.as_deref()
}
}
*/
impl<T> YtEntity for Channel<T> {
fn id(&self) -> &str {
&self.id
@ -269,7 +289,6 @@ impl<T> YtEntity for Channel<T> {
}
}
yt_entity_owner! {VideoPlayerDetails}
yt_entity_owner_opt! {Playlist}
yt_entity! {ChannelId}
yt_entity_owner! {VideoDetails}

File diff suppressed because one or more lines are too long

View file

@ -31,7 +31,8 @@
"height": 1080
}
],
"channel": { "id": "UCYq-iAOSZBvoUxvfzwKIZWA", "name": "Jacob + Katie Schwarz" },
"channel_id": "UCYq-iAOSZBvoUxvfzwKIZWA",
"channel_name": "Jacob + Katie Schwarz",
"view_count": 216221243,
"keywords": [
"4K",

View file

@ -31,10 +31,8 @@
"height": 1080
}
],
"channel": {
"id": "UCX6OQ3DkcsbYNE6H8uQQuVA",
"name": "MrBeast"
},
"channel_id": "UCX6OQ3DkcsbYNE6H8uQQuVA",
"channel_name": "MrBeast",
"view_count": 136908834,
"keywords": [],
"is_live": false,

View file

@ -26,6 +26,7 @@ use rustypipe::validate;
#[rstest]
#[case::desktop(ClientType::Desktop)]
#[case::tv(ClientType::Tv)]
#[case::tv_html5_embed(ClientType::TvHtml5Embed)]
#[case::android(ClientType::Android)]
#[case::ios(ClientType::Ios)]
@ -40,13 +41,26 @@ async fn get_player_from_client(#[case] client_type: ClientType, rp: RustyPipe)
// dbg!(&player_data);
assert_eq!(player_data.details.id, "n4tK7LYFxI0");
assert_eq!(
player_data.details.name,
"Spektrem - Shine | Progressive House | NCS - Copyright Free Music"
);
if client_type == ClientType::DesktopMusic {
assert!(player_data.details.description.is_none());
} else {
assert_eq!(player_data.details.duration, 259);
assert!(!player_data.details.thumbnail.is_empty());
assert_eq!(player_data.details.channel_id, "UC_aEa8K-EOJ3D6gOs7HcyNg");
assert!(!player_data.details.is_live_content);
// The TV client dows not output most video metadata
if client_type != ClientType::Tv {
assert_eq!(
player_data.details.name.expect("name"),
"Spektrem - Shine | Progressive House | NCS - Copyright Free Music"
);
assert_eq!(
player_data.details.channel_name.expect("channel name"),
"NoCopyrightSounds"
);
assert_gte(
player_data.details.view_count.expect("view count"),
146_818_808,
"view count",
);
assert!(player_data
.details
.description
@ -54,15 +68,10 @@ async fn get_player_from_client(#[case] client_type: ClientType, rp: RustyPipe)
.contains(
"NCS (NoCopyrightSounds): Empowering Creators through Copyright / Royalty Free Music"
));
assert_eq!(player_data.details.keywords[0], "spektrem");
}
assert_eq!(player_data.details.duration, 259);
assert!(!player_data.details.thumbnail.is_empty());
assert_eq!(player_data.details.channel.id, "UC_aEa8K-EOJ3D6gOs7HcyNg");
assert_eq!(player_data.details.channel.name, "NoCopyrightSounds");
assert_gte(player_data.details.view_count, 146_818_808, "view count");
assert_eq!(player_data.details.keywords[0], "spektrem");
assert!(!player_data.details.is_live_content);
// Ios uses different A/V formats
if client_type == ClientType::Ios {
let video = player_data
.video_only_streams
@ -237,13 +246,13 @@ async fn get_player(
let details = player_data.details;
assert_eq!(details.id, id);
assert_eq!(details.name, name);
assert_eq!(details.name.expect("name"), name);
let desc = details.description.expect("description");
assert!(desc.contains(description), "description: {desc}");
assert_eq!(details.duration, duration);
assert_eq!(details.channel.id, channel_id);
assert_eq!(details.channel.name, channel_name);
assert_gte(details.view_count, views, "views");
assert_eq!(details.channel_id, channel_id);
assert_eq!(details.channel_name.expect("channel name"), channel_name);
assert_gte(details.view_count.expect("view count"), views, "views");
assert_eq!(details.is_live, is_live);
assert_eq!(details.is_live_content, is_live_content);