Compare commits

...

1 commit

Author SHA1 Message Date
a2f9e87154 feat: fetch channel feed from TV client
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-04-15 01:05:09 +02:00
28 changed files with 44632 additions and 123 deletions

View file

@ -0,0 +1,93 @@
use std::{collections::BTreeMap, fs::File, io::BufReader, path::Path};
use futures::{stream, StreamExt};
use path_macro::path;
use rustypipe::{
client::RustyPipe,
param::{locale::LANGUAGES, Language},
};
use crate::util;
type CollectedDates = BTreeMap<Language, String>;
const FILENAME: &str = "datetime_samples.json";
// A channel with an upcoming video or livestream
const CHANNEL_ID: &str = "UCWxlUwW9BgGISaakjGM37aw";
const VIDEO_ID: &str = "p9FfS9l2NVA";
const YEAR: u64 = 2023;
const YEAR_SHORT: u64 = 23;
const MONTH: u64 = 4;
const DAY: u64 = 14;
const HOUR: u64 = 15;
const HOUR_12: u64 = 3;
const MINUTE: u64 = 0;
/// Collect upcoming video dates from the TV client in every supported language
/// and write them to `testfiles/dict/datetime_samples.json`
pub async fn collect_datetimes(project_root: &Path, concurrency: usize) {
let json_path = path!(project_root / "testfiles" / "dict" / FILENAME);
let rp = RustyPipe::new();
let collected_dates: CollectedDates = stream::iter(LANGUAGES)
.map(|lang| {
let rp = rp.clone();
println!("collecting {lang}");
async move {
let channel = rp.query().lang(lang).channel_tv(CHANNEL_ID).await.unwrap();
let video = channel
.videos
.into_iter()
.find(|v| v.id == VIDEO_ID)
.unwrap();
(
lang,
video
.publish_date_txt
.unwrap_or_else(|| panic!("no publish_date_txt in {}", lang)),
)
}
})
.buffer_unordered(concurrency)
.collect()
.await;
let file = File::create(json_path).unwrap();
serde_json::to_writer_pretty(file, &collected_dates).unwrap();
}
/// Attempt to parse the numbers collected by `collect-datetimes`
/// and write the results to `dictionary.json`.
pub fn write_samples_to_dict(project_root: &Path) {
let json_path = path!(project_root / "testfiles" / "dict" / FILENAME);
let json_file = File::open(json_path).unwrap();
let collected_dates: CollectedDates =
serde_json::from_reader(BufReader::new(json_file)).unwrap();
let mut dict = util::read_dict(project_root);
let langs = dict.keys().map(|k| k.to_owned()).collect::<Vec<_>>();
for lang in langs {
let datestr = &collected_dates[&lang];
let numbers = util::parse_numeric_vec::<u64>(datestr);
let order = numbers
.iter()
.map(|n| match *n {
YEAR => 'Y',
YEAR_SHORT => 'y',
MONTH => 'M',
DAY => 'D',
HOUR => 'H',
HOUR_12 => 'h',
MINUTE => 'm',
_ => panic!("unknown number {n} in {datestr} ({lang})"),
})
.collect::<String>();
assert_eq!(order.len(), 5);
dict.get_mut(&lang).unwrap().datetime_order = order;
}
util::write_dict(project_root, &dict);
}

View file

@ -33,6 +33,7 @@ pub async fn download_testfiles(project_root: &Path) {
channel_info(&testfiles).await; channel_info(&testfiles).await;
channel_videos_cont(&testfiles).await; channel_videos_cont(&testfiles).await;
channel_playlists_cont(&testfiles).await; channel_playlists_cont(&testfiles).await;
channel_tv(&testfiles).await;
search(&testfiles).await; search(&testfiles).await;
search_cont(&testfiles).await; search_cont(&testfiles).await;
search_playlists(&testfiles).await; search_playlists(&testfiles).await;
@ -412,6 +413,26 @@ async fn channel_playlists_cont(testfiles: &Path) {
playlists.content.next(rp.query()).await.unwrap().unwrap(); playlists.content.next(rp.query()).await.unwrap().unwrap();
} }
async fn channel_tv(testfiles: &Path) {
for (name, id) in [
("base", "UCXuqSBlHAE6Xw-yeJA0Tunw"),
("music", "UC_vmjW5e1xEHhYjY2a0kK1A"),
("live", "UCSJ4gkVC6NrvII8umztf0Ow"),
("live_upcoming", "UCWxlUwW9BgGISaakjGM37aw"),
("onevideo", "UCAkeE1thnToEXZTao-CZkHw"),
] {
let mut json_path = testfiles.to_path_buf();
json_path.push("channel_tv");
json_path.push(format!("{name}.json"));
if json_path.exists() {
continue;
}
let rp = rp_testfile(&json_path);
rp.query().channel_tv(id).await.unwrap();
}
}
async fn search(testfiles: &Path) { async fn search(testfiles: &Path) {
let mut json_path = testfiles.to_path_buf(); let mut json_path = testfiles.to_path_buf();
json_path.push("search"); json_path.push("search");

View file

@ -30,6 +30,19 @@ fn parse_tu(tu: &str) -> (u8, Option<TimeUnit>) {
} }
} }
fn parse_date_cmp(c: char) -> &'static str {
match c {
'Y' => "Y",
'y' => "YShort",
'M' => "M",
'D' => "D",
'H' => "Hr",
'h' => "Hr12",
'm' => "Mi",
_ => panic!("invalid date cmp: {c}"),
}
}
pub fn generate_dictionary(project_root: &Path) { pub fn generate_dictionary(project_root: &Path) {
let dict = util::read_dict(project_root); let dict = util::read_dict(project_root);
@ -54,14 +67,20 @@ pub(crate) struct Entry {
/// Identifiers: `Y`(ear), `M`(month), `W`(eek), `D`(ay), /// Identifiers: `Y`(ear), `M`(month), `W`(eek), `D`(ay),
/// `h`(our), `m`(inute), `s`(econd) /// `h`(our), `m`(inute), `s`(econd)
pub timeago_tokens: phf::Map<&'static str, TaToken>, pub timeago_tokens: phf::Map<&'static str, TaToken>,
/// Order in which to parse numeric date components. Formatted as /// Order in which to parse numeric date components.
/// a string of date identifiers (Y, M, D).
/// ///
/// Examples: /// Examples:
/// ///
/// - 03.01.2020 => `"DMY"` /// - 03.01.2020 => `"DMY"`
/// - Jan 3, 2020 => `"DY"` /// - Jan 3, 2020 => `"DY"`
pub date_order: &'static [DateCmp], pub date_order: &'static [DateCmp],
/// Order in which to parse datetimes.
///
/// Examples:
///
/// - 2023-04-14 15:00 => `[Y,M,D,Hr,Mi]`
/// - 4/14/23, 3:00 PM => `[M,D,YShort,Hr12,Mi]`
pub datetime_order: &'static [DateCmp],
/// Tokens for parsing month names. /// Tokens for parsing month names.
/// ///
/// Format: Parsed token -> Month number (starting from 1) /// Format: Parsed token -> Month number (starting from 1)
@ -131,10 +150,17 @@ pub(crate) fn entry(lang: Language) -> Entry {
// Date order // Date order
let mut date_order = "&[".to_owned(); let mut date_order = "&[".to_owned();
entry.date_order.chars().for_each(|c| { entry.date_order.chars().for_each(|c| {
let _ = write!(date_order, "DateCmp::{c}, "); let _ = write!(date_order, "DateCmp::{}, ", parse_date_cmp(c));
}); });
date_order = date_order.trim_end_matches([' ', ',']).to_owned() + "]"; date_order = date_order.trim_end_matches([' ', ',']).to_owned() + "]";
// Datetime order
let mut datetime_order = "&[".to_owned();
entry.datetime_order.chars().for_each(|c| {
let _ = write!(datetime_order, "DateCmp::{}, ", parse_date_cmp(c));
});
datetime_order = datetime_order.trim_end_matches([' ', ',']).to_owned() + "]";
// Number tokens // Number tokens
let mut number_tokens = phf_codegen::Map::<&str>::new(); let mut number_tokens = phf_codegen::Map::<&str>::new();
entry.number_tokens.iter().for_each(|(txt, mag)| { entry.number_tokens.iter().for_each(|(txt, mag)| {
@ -153,8 +179,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
let code_number_tokens = &number_tokens.build().to_string().replace('\n', "\n "); let code_number_tokens = &number_tokens.build().to_string().replace('\n', "\n ");
let code_album_types = &album_types.build().to_string().replace('\n', "\n "); let code_album_types = &album_types.build().to_string().replace('\n', "\n ");
let _ = write!(code_timeago_tokens, "{} => Entry {{\n by_char: {:?},\n timeago_tokens: {},\n date_order: {},\n months: {},\n timeago_nd_tokens: {},\n comma_decimal: {:?},\n number_tokens: {},\n album_types: {},\n }},\n ", let _ = write!(code_timeago_tokens, "{} => Entry {{\n by_char: {:?},\n timeago_tokens: {},\n date_order: {},\n datetime_order: {},\n months: {},\n timeago_nd_tokens: {},\n comma_decimal: {:?},\n number_tokens: {},\n album_types: {},\n }},\n ",
selector, entry.by_char, code_ta_tokens, date_order, code_months, code_ta_nd_tokens, entry.comma_decimal, code_number_tokens, code_album_types); selector, entry.by_char, code_ta_tokens, date_order, datetime_order, code_months, code_ta_nd_tokens, entry.comma_decimal, code_number_tokens, code_album_types);
}); });
code_timeago_tokens = code_timeago_tokens.trim_end().to_owned() + "\n }\n}\n"; code_timeago_tokens = code_timeago_tokens.trim_end().to_owned() + "\n }\n}\n";

View file

@ -1,5 +1,6 @@
mod abtest; mod abtest;
mod collect_album_types; mod collect_album_types;
mod collect_datetimes;
mod collect_large_numbers; mod collect_large_numbers;
mod collect_playlist_dates; mod collect_playlist_dates;
mod download_testfiles; mod download_testfiles;
@ -26,9 +27,11 @@ enum Commands {
CollectPlaylistDates, CollectPlaylistDates,
CollectLargeNumbers, CollectLargeNumbers,
CollectAlbumTypes, CollectAlbumTypes,
CollectDatetimes,
ParsePlaylistDates, ParsePlaylistDates,
ParseLargeNumbers, ParseLargeNumbers,
ParseAlbumTypes, ParseAlbumTypes,
ParseDatetimes,
GenLocales, GenLocales,
GenDict, GenDict,
DownloadTestfiles, DownloadTestfiles,
@ -55,6 +58,9 @@ async fn main() {
Commands::CollectAlbumTypes => { Commands::CollectAlbumTypes => {
collect_album_types::collect_album_types(&cli.project_root, cli.concurrency).await; collect_album_types::collect_album_types(&cli.project_root, cli.concurrency).await;
} }
Commands::CollectDatetimes => {
collect_datetimes::collect_datetimes(&cli.project_root, cli.concurrency).await;
}
Commands::ParsePlaylistDates => { Commands::ParsePlaylistDates => {
collect_playlist_dates::write_samples_to_dict(&cli.project_root) collect_playlist_dates::write_samples_to_dict(&cli.project_root)
} }
@ -62,6 +68,7 @@ async fn main() {
collect_large_numbers::write_samples_to_dict(&cli.project_root) collect_large_numbers::write_samples_to_dict(&cli.project_root)
} }
Commands::ParseAlbumTypes => collect_album_types::write_samples_to_dict(&cli.project_root), Commands::ParseAlbumTypes => collect_album_types::write_samples_to_dict(&cli.project_root),
Commands::ParseDatetimes => collect_datetimes::write_samples_to_dict(&cli.project_root),
Commands::GenLocales => { Commands::GenLocales => {
gen_locales::generate_locales(&cli.project_root).await; gen_locales::generate_locales(&cli.project_root).await;
} }

View file

@ -38,6 +38,14 @@ pub struct DictEntry {
/// - 03.01.2020 => `"DMY"` /// - 03.01.2020 => `"DMY"`
/// - Jan 3, 2020 => `"DY"` /// - Jan 3, 2020 => `"DY"`
pub date_order: String, pub date_order: String,
/// Order in which to parse datetimes. Formatted as a string of
/// date/time identifiers (Y, y, M, D, H, h, m).
///
/// Examples:
///
/// - 2023-04-14 15:00 => `"YMDHm"`
/// - 4/14/23, 3:00 PM => `"MDyhm"`
pub datetime_order: String,
/// Tokens for parsing month names. /// Tokens for parsing month names.
/// ///
/// Format: Parsed token -> Month number (starting from 1) /// Format: Parsed token -> Month number (starting from 1)

231
src/client/channel_tv.rs Normal file
View file

@ -0,0 +1,231 @@
use super::{
response,
response::video_item::{IsLive, IsShort, IsUpcoming},
ClientType, MapResponse, QBrowse, RustyPipeQuery,
};
use crate::{
error::{Error, ExtractionError},
model::{ChannelTag, ChannelTv, Verification, VideoItem},
param::Language,
serializer::MapResult,
timeago,
util::{self, TryRemove},
};
impl RustyPipeQuery {
/// Get the latest videos of a YouTube channel using the SmartTV client
pub async fn channel_tv<S: AsRef<str>>(&self, channel_id: S) -> Result<ChannelTv, Error> {
let channel_id = channel_id.as_ref();
let context = self.get_context(ClientType::TvHtml5, true, None).await;
let request_body = QBrowse {
browse_id: channel_id,
context,
};
self.execute_request::<response::ChannelTv, _, _>(
ClientType::TvHtml5,
"channel_tv",
channel_id.as_ref(),
"browse",
&request_body,
)
.await
}
}
impl MapResponse<ChannelTv> for response::ChannelTv {
fn map_response(
self,
id: &str,
lang: Language,
_deobf: Option<&crate::deobfuscate::DeobfData>,
) -> Result<MapResult<ChannelTv>, ExtractionError> {
// dbg!(&self);
let cr = self
.contents
.tv_browse_renderer
.content
.tv_surface_content_renderer;
let header = cr.header.tv_surface_header_renderer;
let content = cr.content.section_list_renderer.contents;
let subscribe_btn = header.buttons.into_iter().next();
let subscriber_count = subscribe_btn
.as_ref()
.and_then(|b| b.subscribe_button_renderer.subscriber_count_text.as_deref())
.and_then(|txt| util::parse_large_numstr(txt, lang));
let channel_id = subscribe_btn
.map(|b| b.subscribe_button_renderer.channel_id)
.unwrap_or_else(|| id.to_owned());
let uploads = content.into_iter().find(|shelf| {
shelf
.shelf_renderer
.endpoint
.browse_endpoint
.as_ref()
.map(|ep| ep.params == "EgZ2aWRlb3MYAyAAcADyBgsKCToCCAGiAQIIAQ%3D%3D")
.unwrap_or_default()
});
let mut warnings = Vec::new();
let videos = uploads
.map(|uploads| {
let mut items = uploads
.shelf_renderer
.content
.horizontal_list_renderer
.items;
warnings.append(&mut items.warnings);
items
.c
.into_iter()
.filter_map(|v| {
let v = v.tile_renderer;
match v.content_type {
response::channel_tv::ContentType::Video => {
let h = v.header.tile_header_renderer;
let mut m = v.metadata.tile_metadata_renderer;
let length = h.thumbnail_overlays.first().and_then(|overlay| {
util::parse_video_length(
&overlay.thumbnail_overlay_time_status_renderer.text,
)
});
let is_upcoming = h.thumbnail_overlays.is_upcoming();
// Normal video:
// Line1: "Channel name", Line2: "View count" "•" "Upload date"
// Current stream:
// Line1: "Channel name", Line2: "10k watching"
// Upcoming stream:
// Line1: "Channel name", Line2: "Scheduled for 4/15/23, 12:00 AM"
let (view_count, publish_date_txt) = m
.lines
.try_swap_remove(1)
.map(|mut line| {
let date_i = if is_upcoming { 0 } else { 2 };
let view_count =
line.line_renderer.items.get(0).and_then(|vc| {
util::parse_large_numstr(
&vc.line_item_renderer.text,
lang,
)
});
let publish_date_txt = line
.line_renderer
.items
.try_swap_remove(date_i)
.map(|dt| dt.line_item_renderer.text);
(view_count, publish_date_txt)
})
.unwrap_or_default();
let publish_date = publish_date_txt.as_deref().and_then(|txt| {
if is_upcoming {
timeago::parse_datetime_or_warn(lang, txt, &mut warnings)
} else {
timeago::parse_textual_date_or_warn(
lang,
txt,
&mut warnings,
)
}
});
Some(VideoItem {
id: v.content_id,
name: m.title,
length,
thumbnail: h.thumbnail.into(),
channel: Some(ChannelTag {
id: channel_id.to_owned(),
name: header.title.to_owned(),
avatar: Vec::new(),
verification: Verification::None,
subscriber_count,
}),
publish_date,
publish_date_txt,
view_count,
is_live: h.thumbnail_overlays.is_live(),
is_short: h.thumbnail_overlays.is_short(),
is_upcoming,
short_description: None,
})
}
_ => None,
}
})
.collect()
})
.unwrap_or_default();
Ok(MapResult {
c: ChannelTv {
id: channel_id,
name: header.title,
subscriber_count,
avatar: header.thumbnail.into(),
tv_banner: header.banner.into(),
videos,
visitor_data: self.response_context.visitor_data,
},
warnings,
})
}
}
#[cfg(test)]
mod tests {
use std::{fs::File, io::BufReader};
use path_macro::path;
use rstest::rstest;
use crate::{
client::{response, MapResponse},
model::ChannelTv,
param::Language,
serializer::MapResult,
util::tests::TESTFILES,
};
#[rstest]
#[case::base("base", "UCXuqSBlHAE6Xw-yeJA0Tunw")]
#[case::music("music", "UC_vmjW5e1xEHhYjY2a0kK1A")]
#[case::live("live", "UCSJ4gkVC6NrvII8umztf0Ow")]
#[case::live_upcoming("live_upcoming", "UC9CoZyztR-8Xok8Pptzpq1Q")]
#[case::onevideo("onevideo", "UCAkeE1thnToEXZTao-CZkHw")]
fn map_channel_videos(#[case] name: &str, #[case] id: &str) {
let json_path = path!(*TESTFILES / "channel_tv" / format!("{name}.json"));
let json_file = File::open(json_path).unwrap();
let channel: response::ChannelTv =
serde_json::from_reader(BufReader::new(json_file)).unwrap();
let map_res: MapResult<ChannelTv> = channel.map_response(id, Language::En, None).unwrap();
assert!(
map_res.warnings.is_empty(),
"deserialization/mapping warnings: {:?}",
map_res.warnings
);
if name == "live_upcoming" {
insta::assert_ron_snapshot!(format!("map_channel_{name}"), map_res.c, {
".videos[1:].publish_date" => "[date]",
});
} else {
insta::assert_ron_snapshot!(format!("map_channel_{name}"), map_res.c, {
".videos[].publish_date" => "[date]",
});
}
}
}

View file

@ -3,6 +3,7 @@
pub(crate) mod response; pub(crate) mod response;
mod channel; mod channel;
mod channel_tv;
mod music_artist; mod music_artist;
mod music_charts; mod music_charts;
mod music_details; mod music_details;
@ -57,15 +58,17 @@ pub enum ClientType {
/// ///
/// can access YTM-specific data, cannot access non-music content /// can access YTM-specific data, cannot access non-music content
DesktopMusic, DesktopMusic,
/// used by Smart TVs /// Client used by Smart TVs (youtube.com/tv)
TvHtml5,
/// Client used by the embedded player for Smart TVs
/// ///
/// can access age-restricted videos, cannot access non-embeddable videos /// can access age-restricted videos, cannot access non-embeddable videos
TvHtml5Embed, TvHtml5Embed,
/// used by the Android app /// Client used by the Android app
/// ///
/// no obfuscated stream URLs, includes lower resolution audio streams /// no obfuscated stream URLs, includes lower resolution audio streams
Android, Android,
/// used by the iOS app /// Client used by the iOS app
/// ///
/// no obfuscated stream URLs /// no obfuscated stream URLs
Ios, Ios,
@ -74,7 +77,10 @@ pub enum ClientType {
impl ClientType { impl ClientType {
fn is_web(&self) -> bool { fn is_web(&self) -> bool {
match self { match self {
ClientType::Desktop | ClientType::DesktopMusic | ClientType::TvHtml5Embed => true, ClientType::Desktop
| ClientType::DesktopMusic
| ClientType::TvHtml5
| ClientType::TvHtml5Embed => true,
ClientType::Android | ClientType::Ios => false, ClientType::Android | ClientType::Ios => false,
} }
} }
@ -110,6 +116,26 @@ struct ClientInfo<'a> {
visitor_data: Option<&'a str>, visitor_data: Option<&'a str>,
hl: Language, hl: Language,
gl: Country, gl: Country,
time_zone: &'a str,
utc_offset_minutes: i16,
}
impl Default for ClientInfo<'_> {
fn default() -> Self {
Self {
client_name: Default::default(),
client_version: Default::default(),
client_screen: None,
device_model: None,
platform: Default::default(),
original_url: None,
visitor_data: None,
hl: Language::En,
gl: Country::Us,
time_zone: "UTC",
utc_offset_minutes: 0,
}
}
} }
#[derive(Clone, Debug, Serialize)] #[derive(Clone, Debug, Serialize)]
@ -174,12 +200,19 @@ const YOUTUBE_MUSIC_HOME_URL: &str = "https://music.youtube.com/";
const DISABLE_PRETTY_PRINT_PARAMETER: &str = "&prettyPrint=false"; const DISABLE_PRETTY_PRINT_PARAMETER: &str = "&prettyPrint=false";
// Desktop client
const DESKTOP_CLIENT_VERSION: &str = "2.20230126.00.00"; const DESKTOP_CLIENT_VERSION: &str = "2.20230126.00.00";
const DESKTOP_API_KEY: &str = "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8"; const DESKTOP_API_KEY: &str = "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8";
const TVHTML5_CLIENT_VERSION: &str = "2.0";
const DESKTOP_MUSIC_API_KEY: &str = "AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30"; const DESKTOP_MUSIC_API_KEY: &str = "AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30";
const DESKTOP_MUSIC_CLIENT_VERSION: &str = "1.20230123.01.01"; const DESKTOP_MUSIC_CLIENT_VERSION: &str = "1.20230123.01.01";
// TV client
const TVHTML5_UA: &str = "Mozilla/5.0 (SMART-TV; LINUX; Tizen 5.5) AppleWebKit/537.36 (KHTML, like Gecko) 69.0.3497.106.1/5.5 TV Safari/537.36";
const TVHTML5_API_KEY: &str = "AIzaSyDCU8hByM-4DrUqRUYnGn-3llEO78bcxq8";
const TVHTML5_CLIENT_VERSION: &str = "7.20230412.04.00";
const TVHTML5_EMBED_CLIENT_VERSION: &str = "2.0";
// Mobile client
const MOBILE_CLIENT_VERSION: &str = "18.03.33"; const MOBILE_CLIENT_VERSION: &str = "18.03.33";
const ANDROID_API_KEY: &str = "AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w"; const ANDROID_API_KEY: &str = "AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w";
const IOS_API_KEY: &str = "AIzaSyB-63vPrdThhKuerbB2N_l7Kwwcxj6yUAc"; const IOS_API_KEY: &str = "AIzaSyB-63vPrdThhKuerbB2N_l7Kwwcxj6yUAc";
@ -252,13 +285,16 @@ impl Default for RustyPipeOpts {
struct CacheHolder { struct CacheHolder {
desktop_client: RwLock<CacheEntry<ClientData>>, desktop_client: RwLock<CacheEntry<ClientData>>,
music_client: RwLock<CacheEntry<ClientData>>, music_client: RwLock<CacheEntry<ClientData>>,
tv_client: RwLock<CacheEntry<ClientData>>,
deobf: RwLock<CacheEntry<DeobfData>>, deobf: RwLock<CacheEntry<DeobfData>>,
} }
#[derive(Default, Debug, Clone, Serialize, Deserialize)] #[derive(Default, Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
struct CacheData { struct CacheData {
desktop_client: CacheEntry<ClientData>, desktop_client: CacheEntry<ClientData>,
music_client: CacheEntry<ClientData>, music_client: CacheEntry<ClientData>,
tv_client: CacheEntry<ClientData>,
deobf: CacheEntry<DeobfData>, deobf: CacheEntry<DeobfData>,
} }
@ -364,6 +400,7 @@ impl RustyPipeBuilder {
cache: CacheHolder { cache: CacheHolder {
desktop_client: RwLock::new(cdata.desktop_client), desktop_client: RwLock::new(cdata.desktop_client),
music_client: RwLock::new(cdata.music_client), music_client: RwLock::new(cdata.music_client),
tv_client: RwLock::new(cdata.tv_client),
deobf: RwLock::new(cdata.deobf), deobf: RwLock::new(cdata.deobf),
}, },
default_opts: self.default_opts, default_opts: self.default_opts,
@ -558,61 +595,47 @@ impl RustyPipe {
/// Extract the current version of the YouTube desktop client from the website. /// Extract the current version of the YouTube desktop client from the website.
async fn extract_desktop_client_version(&self) -> Result<String, Error> { async fn extract_desktop_client_version(&self) -> Result<String, Error> {
let from_swjs = async { self.extract_client_version(
let swjs = self Some("https://www.youtube.com/sw.js"),
.http_request_txt( "https://www.youtube.com/results?search_query=",
self.inner "https://www.youtube.com",
.http None,
.get("https://www.youtube.com/sw.js") )
.header(header::ORIGIN, "https://www.youtube.com") .await
.header(header::REFERER, "https://www.youtube.com")
.header(header::COOKIE, self.inner.consent_cookie.to_owned())
.build()
.unwrap(),
)
.await?;
util::get_cg_from_regexes(CLIENT_VERSION_REGEXES.iter(), &swjs, 1).ok_or(
Error::Extraction(ExtractionError::InvalidData(Cow::Borrowed(
"Could not find desktop client version in sw.js",
))),
)
};
let from_html = async {
let html = self
.http_request_txt(
self.inner
.http
.get("https://www.youtube.com/results?search_query=")
.build()
.unwrap(),
)
.await?;
util::get_cg_from_regexes(CLIENT_VERSION_REGEXES.iter(), &html, 1).ok_or(
Error::Extraction(ExtractionError::InvalidData(Cow::Borrowed(
"Could not find desktop client version on html page",
))),
)
};
match from_swjs.await {
Ok(client_version) => Ok(client_version),
Err(_) => from_html.await,
}
} }
/// Extract the current version of the YouTube Music desktop client from the website. /// Extract the current version of the YouTube Music desktop client from the website.
async fn extract_music_client_version(&self) -> Result<String, Error> { async fn extract_music_client_version(&self) -> Result<String, Error> {
let from_swjs = async { self.extract_client_version(
Some("https://music.youtube.com/sw.js"),
"https://music.youtube.com",
"https://music.youtube.com",
None,
)
.await
}
/// Extract the current version of the YouTube Music TV client from the website.
async fn extract_tv_client_version(&self) -> Result<String, Error> {
self.extract_client_version(None, "https://www.youtube.com/tv", "", Some(TVHTML5_UA))
.await
}
async fn extract_client_version(
&self,
sw_url: Option<&str>,
html_url: &str,
origin: &str,
ua: Option<&str>,
) -> Result<String, Error> {
let from_swjs = sw_url.map(|sw_url| async move {
let swjs = self let swjs = self
.http_request_txt( .http_request_txt(
self.inner self.inner
.http .http
.get("https://music.youtube.com/sw.js") .get(sw_url)
.header(header::ORIGIN, "https://music.youtube.com") .header(header::ORIGIN, origin)
.header(header::REFERER, "https://music.youtube.com") .header(header::REFERER, origin)
.header(header::COOKIE, self.inner.consent_cookie.to_owned()) .header(header::COOKIE, self.inner.consent_cookie.to_owned())
.build() .build()
.unwrap(), .unwrap(),
@ -621,32 +644,33 @@ impl RustyPipe {
util::get_cg_from_regexes(CLIENT_VERSION_REGEXES.iter(), &swjs, 1).ok_or( util::get_cg_from_regexes(CLIENT_VERSION_REGEXES.iter(), &swjs, 1).ok_or(
Error::Extraction(ExtractionError::InvalidData(Cow::Borrowed( Error::Extraction(ExtractionError::InvalidData(Cow::Borrowed(
"Could not find music client version in sw.js", "Could not find client version in sw.js",
))), ))),
) )
}; });
let from_html = async { let from_html = async {
let html = self let mut builder = self.inner.http.get(html_url);
.http_request_txt( if let Some(ua) = ua {
self.inner builder = builder.header(header::USER_AGENT, ua);
.http }
.get("https://music.youtube.com")
.build() let html = self.http_request_txt(builder.build().unwrap()).await?;
.unwrap(),
)
.await?;
util::get_cg_from_regexes(CLIENT_VERSION_REGEXES.iter(), &html, 1).ok_or( util::get_cg_from_regexes(CLIENT_VERSION_REGEXES.iter(), &html, 1).ok_or(
Error::Extraction(ExtractionError::InvalidData(Cow::Borrowed( Error::Extraction(ExtractionError::InvalidData(Cow::Borrowed(
"Could not find music client version on html page", "Could not find client version on html page",
))), ))),
) )
}; };
match from_swjs.await { if let Some(from_swjs) = from_swjs {
Ok(client_version) => Ok(client_version), match from_swjs.await {
Err(_) => from_html.await, Ok(client_version) => Ok(client_version),
Err(_) => from_html.await,
}
} else {
from_html.await
} }
} }
@ -674,7 +698,7 @@ impl RustyPipe {
version version
} }
Err(e) => { Err(e) => {
log::warn!("{}, falling back to hardcoded version", e); log::warn!("{}, falling back to hardcoded desktop client version", e);
DESKTOP_CLIENT_VERSION.to_owned() DESKTOP_CLIENT_VERSION.to_owned()
} }
} }
@ -706,7 +730,7 @@ impl RustyPipe {
version version
} }
Err(e) => { Err(e) => {
log::warn!("{}, falling back to hardcoded version", e); log::warn!("{}, falling back to hardcoded music client version", e);
DESKTOP_MUSIC_CLIENT_VERSION.to_owned() DESKTOP_MUSIC_CLIENT_VERSION.to_owned()
} }
} }
@ -714,6 +738,37 @@ impl RustyPipe {
} }
} }
/// Get the current version of the YouTube TV client from the following sources
///
/// 1. from cache
/// 2. from the YouTube TV website
/// 3. fall back to the hardcoded version
async fn get_tv_client_version(&self) -> String {
// Write lock here to prevent concurrent tasks from fetching the same data
let mut tv_client = self.inner.cache.tv_client.write().await;
match tv_client.get() {
Some(cdata) => cdata.version.to_owned(),
None => {
log::debug!("getting tv client version");
match self.extract_tv_client_version().await {
Ok(version) => {
*tv_client = CacheEntry::from(ClientData {
version: version.to_owned(),
});
drop(tv_client);
self.store_cache().await;
version
}
Err(e) => {
log::warn!("{}, falling back to hardcoded tv client version", e);
TVHTML5_CLIENT_VERSION.to_owned()
}
}
}
}
}
/// Instantiate a new deobfuscator from either cached or extracted YouTube JavaScript code. /// Instantiate a new deobfuscator from either cached or extracted YouTube JavaScript code.
async fn get_deobf_data(&self) -> Result<DeobfData, Error> { async fn get_deobf_data(&self) -> Result<DeobfData, Error> {
// Write lock here to prevent concurrent tasks from fetching the same data // Write lock here to prevent concurrent tasks from fetching the same data
@ -732,12 +787,13 @@ impl RustyPipe {
} }
} }
/// Write the given cache data to the storage backend. /// Write the current cache data to the storage backend.
async fn store_cache(&self) { async fn store_cache(&self) {
if let Some(storage) = &self.inner.storage { if let Some(storage) = &self.inner.storage {
let cdata = CacheData { let cdata = CacheData {
desktop_client: self.inner.cache.desktop_client.read().await.clone(), desktop_client: self.inner.cache.desktop_client.read().await.clone(),
music_client: self.inner.cache.music_client.read().await.clone(), music_client: self.inner.cache.music_client.read().await.clone(),
tv_client: self.inner.cache.tv_client.read().await.clone(),
deobf: self.inner.cache.deobf.read().await.clone(), deobf: self.inner.cache.deobf.read().await.clone(),
}; };
@ -838,13 +894,12 @@ impl RustyPipeQuery {
client: ClientInfo { client: ClientInfo {
client_name: "WEB", client_name: "WEB",
client_version: Cow::Owned(self.client.get_desktop_client_version().await), client_version: Cow::Owned(self.client.get_desktop_client_version().await),
client_screen: None,
device_model: None,
platform: "DESKTOP", platform: "DESKTOP",
original_url: Some("https://www.youtube.com/"), original_url: Some("https://www.youtube.com/"),
visitor_data, visitor_data,
hl, hl,
gl, gl,
..Default::default()
}, },
request: Some(RequestYT::default()), request: Some(RequestYT::default()),
user: User::default(), user: User::default(),
@ -854,13 +909,29 @@ impl RustyPipeQuery {
client: ClientInfo { client: ClientInfo {
client_name: "WEB_REMIX", client_name: "WEB_REMIX",
client_version: Cow::Owned(self.client.get_music_client_version().await), client_version: Cow::Owned(self.client.get_music_client_version().await),
client_screen: None,
device_model: None,
platform: "DESKTOP", platform: "DESKTOP",
original_url: Some("https://music.youtube.com/"), original_url: Some("https://music.youtube.com/"),
visitor_data, visitor_data,
hl, hl,
gl, gl,
..Default::default()
},
request: Some(RequestYT::default()),
user: User::default(),
third_party: None,
},
ClientType::TvHtml5 => YTContext {
client: ClientInfo {
client_name: "TVHTML5",
client_version: Cow::Owned(self.client.get_tv_client_version().await),
client_screen: None,
device_model: Some("SmartTV"),
platform: "TV",
original_url: Some("https://www.youtube.com/tv"),
visitor_data,
hl,
gl,
..Default::default()
}, },
request: Some(RequestYT::default()), request: Some(RequestYT::default()),
user: User::default(), user: User::default(),
@ -869,14 +940,13 @@ impl RustyPipeQuery {
ClientType::TvHtml5Embed => YTContext { ClientType::TvHtml5Embed => YTContext {
client: ClientInfo { client: ClientInfo {
client_name: "TVHTML5_SIMPLY_EMBEDDED_PLAYER", client_name: "TVHTML5_SIMPLY_EMBEDDED_PLAYER",
client_version: Cow::Borrowed(TVHTML5_CLIENT_VERSION), client_version: Cow::Borrowed(TVHTML5_EMBED_CLIENT_VERSION),
client_screen: Some("EMBED"), client_screen: Some("EMBED"),
device_model: None,
platform: "TV", platform: "TV",
original_url: None,
visitor_data, visitor_data,
hl, hl,
gl, gl,
..Default::default()
}, },
request: Some(RequestYT::default()), request: Some(RequestYT::default()),
user: User::default(), user: User::default(),
@ -888,13 +958,11 @@ impl RustyPipeQuery {
client: ClientInfo { client: ClientInfo {
client_name: "ANDROID", client_name: "ANDROID",
client_version: Cow::Borrowed(MOBILE_CLIENT_VERSION), client_version: Cow::Borrowed(MOBILE_CLIENT_VERSION),
client_screen: None,
device_model: None,
platform: "MOBILE", platform: "MOBILE",
original_url: None,
visitor_data, visitor_data,
hl, hl,
gl, gl,
..Default::default()
}, },
request: None, request: None,
user: User::default(), user: User::default(),
@ -904,13 +972,12 @@ impl RustyPipeQuery {
client: ClientInfo { client: ClientInfo {
client_name: "IOS", client_name: "IOS",
client_version: Cow::Borrowed(MOBILE_CLIENT_VERSION), client_version: Cow::Borrowed(MOBILE_CLIENT_VERSION),
client_screen: None,
device_model: Some(IOS_DEVICE_MODEL), device_model: Some(IOS_DEVICE_MODEL),
platform: "MOBILE", platform: "MOBILE",
original_url: None,
visitor_data, visitor_data,
hl, hl,
gl, gl,
..Default::default()
}, },
request: None, request: None,
user: User::default(), user: User::default(),
@ -958,6 +1025,17 @@ impl RustyPipeQuery {
"X-YouTube-Client-Version", "X-YouTube-Client-Version",
self.client.get_music_client_version().await, self.client.get_music_client_version().await,
), ),
ClientType::TvHtml5 => self
.client
.inner
.http
.post(format!(
"{YOUTUBEI_V1_URL}{endpoint}?key={TVHTML5_API_KEY}{DISABLE_PRETTY_PRINT_PARAMETER}"
))
.header(header::ORIGIN, "https://www.youtube.com")
.header(header::REFERER, "https://www.youtube.com/tv")
.header(header::USER_AGENT, TVHTML5_UA)
.header("X-YouTube-Client-Version", self.client.get_tv_client_version().await),
ClientType::TvHtml5Embed => self ClientType::TvHtml5Embed => self
.client .client
.inner .inner
@ -968,7 +1046,7 @@ impl RustyPipeQuery {
.header(header::ORIGIN, "https://www.youtube.com") .header(header::ORIGIN, "https://www.youtube.com")
.header(header::REFERER, "https://www.youtube.com") .header(header::REFERER, "https://www.youtube.com")
.header("X-YouTube-Client-Name", "1") .header("X-YouTube-Client-Name", "1")
.header("X-YouTube-Client-Version", TVHTML5_CLIENT_VERSION), .header("X-YouTube-Client-Version", TVHTML5_EMBED_CLIENT_VERSION),
ClientType::Android => self ClientType::Android => self
.client .client
.inner .inner
@ -1222,6 +1300,33 @@ fn validate_country(country: Country) -> Country {
mod tests { mod tests {
use super::*; use super::*;
fn get_major_version(version: &str) -> u32 {
let parts = version.split('.').collect::<Vec<_>>();
assert_eq!(parts.len(), 4);
parts[0].parse().unwrap()
}
#[test]
fn t_extract_desktop_client_version() {
let rp = RustyPipe::new();
let version = tokio_test::block_on(rp.extract_desktop_client_version()).unwrap();
assert!(get_major_version(&version) >= 2);
}
#[test]
fn t_extract_music_client_version() {
let rp = RustyPipe::new();
let version = tokio_test::block_on(rp.extract_music_client_version()).unwrap();
assert!(get_major_version(&version) >= 1);
}
#[test]
fn t_extract_tv_client_version() {
let rp = RustyPipe::new();
let version = tokio_test::block_on(rp.extract_tv_client_version()).unwrap();
assert!(get_major_version(&version) >= 7);
}
#[test] #[test]
fn t_get_ytm_visitor_data() { fn t_get_ytm_visitor_data() {
let rp = RustyPipe::new(); let rp = RustyPipe::new();

View file

@ -0,0 +1,202 @@
use serde::Deserialize;
use serde_with::{serde_as, VecSkipError};
use super::{
url_endpoint::NavigationEndpoint, video_item::TimeOverlay, ContentRenderer, ResponseContext,
Thumbnails,
};
use crate::serializer::{text::Text, MapResult, VecLogError};
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ChannelTv {
pub contents: Contents,
pub response_context: ResponseContext,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Contents {
pub tv_browse_renderer: ContentRenderer<TvSurface>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct TvSurface {
pub tv_surface_content_renderer: SurfaceContentRenderer,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct SurfaceContentRenderer {
#[serde(default)]
pub content: SurfaceContent,
pub header: SurfaceHeader,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct SurfaceHeader {
pub tv_surface_header_renderer: SurfaceHeaderRenderer,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct SurfaceHeaderRenderer {
// TODO: really?
// #[serde(default)]
#[serde_as(as = "Text")]
pub title: String,
/// Channel avatar
#[serde(default)]
pub thumbnail: Thumbnails,
#[serde(default)]
pub banner: Thumbnails,
#[serde_as(as = "VecSkipError<_>")]
pub buttons: Vec<SubscribeButton>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct SubscribeButton {
pub subscribe_button_renderer: SubscribeButtonRenderer,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct SubscribeButtonRenderer {
pub channel_id: String,
#[serde_as(as = "Option<Text>")]
pub subscriber_count_text: Option<String>,
}
#[derive(Default, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct SurfaceContent {
pub section_list_renderer: SectionList,
}
#[serde_as]
#[derive(Default, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct SectionList {
#[serde_as(as = "VecSkipError<_>")]
pub contents: Vec<Shelf>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Shelf {
pub shelf_renderer: ShelfRenderer,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ShelfRenderer {
pub content: ShelfContent,
pub endpoint: NavigationEndpoint,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ShelfContent {
pub horizontal_list_renderer: HorizontalListRenderer,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct HorizontalListRenderer {
#[serde_as(as = "VecLogError<_>")]
pub items: MapResult<Vec<Tile>>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Tile {
pub tile_renderer: TileRenderer,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct TileRenderer {
pub content_id: String,
pub content_type: ContentType,
pub header: TileHeader,
pub metadata: Metadata,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct TileHeader {
pub tile_header_renderer: TileHeaderRenderer,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct TileHeaderRenderer {
pub thumbnail: Thumbnails,
/// Contains Live tag
#[serde(default)]
#[serde_as(as = "VecSkipError<_>")]
pub thumbnail_overlays: Vec<TimeOverlay>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Metadata {
pub tile_metadata_renderer: MetadataRenderer,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct MetadataRenderer {
#[serde_as(as = "Text")]
pub title: String,
#[serde(default)]
#[serde_as(as = "VecSkipError<_>")]
pub lines: Vec<Line>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Line {
pub line_renderer: LineRenderer,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct LineRenderer {
#[serde_as(as = "VecSkipError<_>")]
pub items: Vec<LineItem>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct LineItem {
pub line_item_renderer: LineItemRenderer,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct LineItemRenderer {
#[serde_as(as = "Text")]
pub text: String,
}
#[derive(Debug, Deserialize)]
pub(crate) enum ContentType {
#[serde(rename = "TILE_CONTENT_TYPE_VIDEO")]
Video,
#[serde(rename = "TILE_CONTENT_TYPE_CHANNEL")]
Channel,
#[serde(rename = "TILE_CONTENT_TYPE_PLAYLIST")]
Playlist,
}

View file

@ -1,4 +1,5 @@
pub(crate) mod channel; pub(crate) mod channel;
pub(crate) mod channel_tv;
pub(crate) mod music_artist; pub(crate) mod music_artist;
pub(crate) mod music_charts; pub(crate) mod music_charts;
pub(crate) mod music_details; pub(crate) mod music_details;
@ -16,6 +17,7 @@ pub(crate) mod video_details;
pub(crate) mod video_item; pub(crate) mod video_item;
pub(crate) use channel::Channel; pub(crate) use channel::Channel;
pub(crate) use channel_tv::ChannelTv;
pub(crate) use music_artist::MusicArtist; pub(crate) use music_artist::MusicArtist;
pub(crate) use music_artist::MusicArtistAlbums; pub(crate) use music_artist::MusicArtistAlbums;
pub(crate) use music_charts::MusicCharts; pub(crate) use music_charts::MusicCharts;

View file

@ -273,6 +273,7 @@ pub(crate) enum TimeOverlayStyle {
Default, Default,
Live, Live,
Shorts, Shorts,
Upcoming,
} }
#[serde_as] #[serde_as]
@ -356,14 +357,18 @@ pub(crate) struct PrimaryLink {
pub navigation_endpoint: NavigationEndpoint, pub navigation_endpoint: NavigationEndpoint,
} }
trait IsLive { pub(crate) trait IsLive {
fn is_live(&self) -> bool; fn is_live(&self) -> bool;
} }
trait IsShort { pub(crate) trait IsShort {
fn is_short(&self) -> bool; fn is_short(&self) -> bool;
} }
pub(crate) trait IsUpcoming {
fn is_upcoming(&self) -> bool;
}
impl IsLive for Vec<VideoBadge> { impl IsLive for Vec<VideoBadge> {
fn is_live(&self) -> bool { fn is_live(&self) -> bool {
self.iter().any(|badge| { self.iter().any(|badge| {
@ -388,6 +393,14 @@ impl IsShort for Vec<TimeOverlay> {
} }
} }
impl IsUpcoming for Vec<TimeOverlay> {
fn is_upcoming(&self) -> bool {
self.iter().any(|overlay| {
overlay.thumbnail_overlay_time_status_renderer.style == TimeOverlayStyle::Upcoming
})
}
}
/// Result of mapping a list of different YouTube enities /// Result of mapping a list of different YouTube enities
/// (videos, channels, playlists) /// (videos, channels, playlists)
#[derive(Debug)] #[derive(Debug)]

View file

@ -0,0 +1,658 @@
---
source: src/client/channel_tv.rs
expression: map_res.c
---
ChannelTv(
id: "UCXuqSBlHAE6Xw-yeJA0Tunw",
name: "Linus Tech Tips",
subscriber_count: Some(15400000),
avatar: [
Thumbnail(
url: "https://yt3.googleusercontent.com/Vy6KL7EM_apxPSxF0pPy5w_c87YDTOlBQo3MADDF0Wl51kwxmt9wmRotnt2xQXwlrcyO0Xe56w=s72-c-k-c0x00ffffff-no-rj",
width: 72,
height: 72,
),
],
tv_banner: [
Thumbnail(
url: "https://yt3.googleusercontent.com/ZM642NyuML42iPDUaopQKNyHmTbECQ87IRQ5EOpAmyLV8LnGT2h-D2DGV8zbO32P7ovliRtBNjY=w320-fcrop64=1,00000000ffffffff-k-c0xffffffff-no-nd-rj",
width: 320,
height: 180,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/ZM642NyuML42iPDUaopQKNyHmTbECQ87IRQ5EOpAmyLV8LnGT2h-D2DGV8zbO32P7ovliRtBNjY=w854-fcrop64=1,00000000ffffffff-k-c0xffffffff-no-nd-rj",
width: 854,
height: 480,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/ZM642NyuML42iPDUaopQKNyHmTbECQ87IRQ5EOpAmyLV8LnGT2h-D2DGV8zbO32P7ovliRtBNjY=w1280-fcrop64=1,00000000ffffffff-k-c0xffffffff-no-nd-rj",
width: 1280,
height: 720,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/ZM642NyuML42iPDUaopQKNyHmTbECQ87IRQ5EOpAmyLV8LnGT2h-D2DGV8zbO32P7ovliRtBNjY=w1920-fcrop64=1,00000000ffffffff-k-c0xffffffff-no-nd-rj",
width: 1920,
height: 1080,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/ZM642NyuML42iPDUaopQKNyHmTbECQ87IRQ5EOpAmyLV8LnGT2h-D2DGV8zbO32P7ovliRtBNjY=w2120-fcrop64=1,00000000ffffffff-k-c0xffffffff-no-nd-rj",
width: 2120,
height: 1192,
),
],
videos: [
VideoItem(
id: "1vpepaQ-VQQ",
name: "STOP Buying ANDROID TV Boxes!",
length: Some(620),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/1vpepaQ-VQQ/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/1vpepaQ-VQQ/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/1vpepaQ-VQQ/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLBUaVN2jfJqIDmESCGLN7278aOFLw",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/1vpepaQ-VQQ/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/1vpepaQ-VQQ/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/1vpepaQ-VQQ/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCXuqSBlHAE6Xw-yeJA0Tunw",
name: "Linus Tech Tips",
avatar: [],
verification: None,
subscriber_count: Some(15400000),
)),
publish_date: "[date]",
publish_date_txt: Some("21 hours ago"),
view_count: Some(1200000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "nkh9VGCY8as",
name: "Im Dreading this Review RTX 4070",
length: Some(701),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/nkh9VGCY8as/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/nkh9VGCY8as/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/nkh9VGCY8as/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLAXgvQB3T_twGIsf3AQdwGQl5G7Fg",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/nkh9VGCY8as/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/nkh9VGCY8as/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/nkh9VGCY8as/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCXuqSBlHAE6Xw-yeJA0Tunw",
name: "Linus Tech Tips",
avatar: [],
verification: None,
subscriber_count: Some(15400000),
)),
publish_date: "[date]",
publish_date_txt: Some("2 days ago"),
view_count: Some(1800000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "9hTYUHm3pKw",
name: "Giving out free tech tips AGAIN",
length: Some(36),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/9hTYUHm3pKw/default.jpg?sqp=-oaymwEkCHgQWvKriqkDGvABAfgBtgiAAoAPigIMCAAQARhjIGMoYzAP&rs=AOn4CLCS7gCg5w_iExAOqftGiAQ8Wq_a2Q",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/9hTYUHm3pKw/mqdefault.jpg?sqp=-oaymwEmCMACELQB8quKqQMa8AEB-AG2CIACgA-KAgwIABABGGMgYyhjMA8=&rs=AOn4CLB6z2XIAF9_wJ5KzE3yMjnreACxgQ",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/9hTYUHm3pKw/hqdefault.jpg?sqp=-oaymwExCLwDEPoBSFryq4qpAyMIARUAAIhCGAHwAQH4AbYIgAKAD4oCDAgAEAEYYyBjKGMwDw==&rs=AOn4CLBe_TI0cWb_PRKBrCr6TjpmETqZHA",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/9hTYUHm3pKw/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AG2CIACgA-KAgwIABABGGMgYyhjMA8=&rs=AOn4CLDMuyrew8kihs0-SzA6HvYXCdOuqg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/9hTYUHm3pKw/sddefault.jpg?sqp=-oaymwEmCIAFEOAD8quKqQMa8AEB-AG2CIACgA-KAgwIABABGGMgYyhjMA8=&rs=AOn4CLBDgipAqpEfEBoi8lCVGt5DSlj0bA",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/9hTYUHm3pKw/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AG2CIACgA-KAgwIABABGGMgYyhjMA8=&rs=AOn4CLDGQ6aAeJcPKhhboiUiY33OJNdLCw",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCXuqSBlHAE6Xw-yeJA0Tunw",
name: "Linus Tech Tips",
avatar: [],
verification: None,
subscriber_count: Some(15400000),
)),
publish_date: "[date]",
publish_date_txt: Some("2 days ago"),
view_count: Some(363000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "g7oNuP83VXs",
name: "The Amazon Basics CPU Cooler",
length: Some(500),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/g7oNuP83VXs/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/g7oNuP83VXs/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/g7oNuP83VXs/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLCko9k-lp8mijshkO--ADN2EVA7zQ",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/g7oNuP83VXs/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/g7oNuP83VXs/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/g7oNuP83VXs/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCXuqSBlHAE6Xw-yeJA0Tunw",
name: "Linus Tech Tips",
avatar: [],
verification: None,
subscriber_count: Some(15400000),
)),
publish_date: "[date]",
publish_date_txt: Some("2 days ago"),
view_count: Some(1000000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "LVkqK4bl1Gw",
name: "If you can fix this PC, its yours!",
length: Some(1325),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/LVkqK4bl1Gw/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/LVkqK4bl1Gw/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/LVkqK4bl1Gw/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLBJRqXG5q_aXx2LgJoM2gnKJPcAug",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/LVkqK4bl1Gw/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/LVkqK4bl1Gw/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/LVkqK4bl1Gw/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCXuqSBlHAE6Xw-yeJA0Tunw",
name: "Linus Tech Tips",
avatar: [],
verification: None,
subscriber_count: Some(15400000),
)),
publish_date: "[date]",
publish_date_txt: Some("3 days ago"),
view_count: Some(1100000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "nLIp4wd0oXs",
name: "This Makes Hacking TOO Easy - Flipper Zero",
length: Some(732),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/nLIp4wd0oXs/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/nLIp4wd0oXs/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/nLIp4wd0oXs/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLBe8ewilB3sskZPwqG-XopT5T5tPw",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/nLIp4wd0oXs/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/nLIp4wd0oXs/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/nLIp4wd0oXs/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCXuqSBlHAE6Xw-yeJA0Tunw",
name: "Linus Tech Tips",
avatar: [],
verification: None,
subscriber_count: Some(15400000),
)),
publish_date: "[date]",
publish_date_txt: Some("4 days ago"),
view_count: Some(3100000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "1ugJ1BJx0HE",
name: "The SCAM of Wireless ESD Straps - Feat. ElectroBOOM",
length: Some(1763),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/1ugJ1BJx0HE/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/1ugJ1BJx0HE/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/1ugJ1BJx0HE/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLA_7h5PVIjqmCDOFwN7kVFxuJP7sw",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/1ugJ1BJx0HE/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/1ugJ1BJx0HE/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/1ugJ1BJx0HE/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCXuqSBlHAE6Xw-yeJA0Tunw",
name: "Linus Tech Tips",
avatar: [],
verification: None,
subscriber_count: Some(15400000),
)),
publish_date: "[date]",
publish_date_txt: Some("5 days ago"),
view_count: Some(2500000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "s-llzV_3LaE",
name: "A Monster Lawsuit Is Coming For Me - WAN Show April 7, 2023",
length: Some(18993),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/s-llzV_3LaE/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/s-llzV_3LaE/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/s-llzV_3LaE/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLB1s67Dq72qpCeHvyYbjzNiCr3INw",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/s-llzV_3LaE/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/s-llzV_3LaE/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/s-llzV_3LaE/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCXuqSBlHAE6Xw-yeJA0Tunw",
name: "Linus Tech Tips",
avatar: [],
verification: None,
subscriber_count: Some(15400000),
)),
publish_date: "[date]",
publish_date_txt: Some("Streamed 6 days ago"),
view_count: Some(988000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "EzdV27b1V1Q",
name: "Does this ACTUALLY hurt your PC?",
length: Some(35),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/EzdV27b1V1Q/default.jpg?sqp=-oaymwEkCHgQWvKriqkDGvABAfgBtgiAAoAPigIMCAAQARhlIFMoSjAP&rs=AOn4CLDZXi514ordaLSYrB-qaYZdsvJAEg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/EzdV27b1V1Q/mqdefault.jpg?sqp=-oaymwEmCMACELQB8quKqQMa8AEB-AG2CIACgA-KAgwIABABGGUgUyhKMA8=&rs=AOn4CLA9miEzhK8HK_1WETFjVqgn0NtMNQ",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/EzdV27b1V1Q/hqdefault.jpg?sqp=-oaymwExCLwDEPoBSFryq4qpAyMIARUAAIhCGAHwAQH4AbYIgAKAD4oCDAgAEAEYZSBTKEowDw==&rs=AOn4CLAp25U83rZDwrUpogDGHYJjGaRI8g",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/EzdV27b1V1Q/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AG2CIACgA-KAgwIABABGGUgUyhKMA8=&rs=AOn4CLC4XNTvkQt54_eREftUvCwStfJg1Q",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/EzdV27b1V1Q/sddefault.jpg?sqp=-oaymwEmCIAFEOAD8quKqQMa8AEB-AG2CIACgA-KAgwIABABGGUgUyhKMA8=&rs=AOn4CLD-vCRuPU9MqGMAQJ1K7Vxge-5_Zw",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/EzdV27b1V1Q/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AG2CIACgA-KAgwIABABGGUgUyhKMA8=&rs=AOn4CLBRCNjDmhZlVjBAmpoVt3daA_BPog",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCXuqSBlHAE6Xw-yeJA0Tunw",
name: "Linus Tech Tips",
avatar: [],
verification: None,
subscriber_count: Some(15400000),
)),
publish_date: "[date]",
publish_date_txt: Some("6 days ago"),
view_count: Some(1300000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "k7wnNt65lcE",
name: "Can a Gaming PC Survive the North Pole?",
length: Some(1113),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/k7wnNt65lcE/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/k7wnNt65lcE/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/k7wnNt65lcE/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLBRrjrpo5NAGat65o1U2ec2G61HLw",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/k7wnNt65lcE/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/k7wnNt65lcE/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/k7wnNt65lcE/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCXuqSBlHAE6Xw-yeJA0Tunw",
name: "Linus Tech Tips",
avatar: [],
verification: None,
subscriber_count: Some(15400000),
)),
publish_date: "[date]",
publish_date_txt: Some("7 days ago"),
view_count: Some(1800000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "nj4gn7od0jY",
name: "It\'s the Best Gaming CPU on the Planet.. AND I\'M MAD. - Ryzen 7 7800X3D Review",
length: Some(823),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/nj4gn7od0jY/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/nj4gn7od0jY/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/nj4gn7od0jY/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLDJXMMuyasfoq_dbMTcJW8px_vW4g",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/nj4gn7od0jY/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/nj4gn7od0jY/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/nj4gn7od0jY/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCXuqSBlHAE6Xw-yeJA0Tunw",
name: "Linus Tech Tips",
avatar: [],
verification: None,
subscriber_count: Some(15400000),
)),
publish_date: "[date]",
publish_date_txt: Some("9 days ago"),
view_count: Some(1800000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "k_3NVXml470",
name: "No… THIS is the CLEANEST Setup",
length: Some(643),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/k_3NVXml470/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/k_3NVXml470/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/k_3NVXml470/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLBu3-leVqNCGsYN7_o7g5Ji8T3V7A",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/k_3NVXml470/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/k_3NVXml470/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/k_3NVXml470/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCXuqSBlHAE6Xw-yeJA0Tunw",
name: "Linus Tech Tips",
avatar: [],
verification: None,
subscriber_count: Some(15400000),
)),
publish_date: "[date]",
publish_date_txt: Some("9 days ago"),
view_count: Some(2900000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
],
visitor_data: Some("Cgs1UVZkeFlUN1ZXWSiWvuWhBg%3D%3D"),
)

View file

@ -0,0 +1,658 @@
---
source: src/client/channel_tv.rs
expression: map_res.c
---
ChannelTv(
id: "UCSJ4gkVC6NrvII8umztf0Ow",
name: "Lofi Girl",
subscriber_count: Some(12300000),
avatar: [
Thumbnail(
url: "https://yt3.googleusercontent.com/gY8H7K-3Eg3olVftRBiqqFe-N5d9Rx90jAsrfQuxDa4m32Wm-kWK6AQJhwchvYLf-H4EjGhCSw=s72-c-k-c0x00ffffff-no-rj",
width: 72,
height: 72,
),
],
tv_banner: [
Thumbnail(
url: "https://yt3.googleusercontent.com/X9zEVI1HRWuZ7gHhG9fv_4IjSLwJMdAadTuXfa2vMAHSAIun4s_e6TgNa1WQOr-f88SWbU1uuQ=w320-fcrop64=1,00000000ffffffff-k-c0xffffffff-no-nd-rj",
width: 320,
height: 180,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/X9zEVI1HRWuZ7gHhG9fv_4IjSLwJMdAadTuXfa2vMAHSAIun4s_e6TgNa1WQOr-f88SWbU1uuQ=w854-fcrop64=1,00000000ffffffff-k-c0xffffffff-no-nd-rj",
width: 854,
height: 480,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/X9zEVI1HRWuZ7gHhG9fv_4IjSLwJMdAadTuXfa2vMAHSAIun4s_e6TgNa1WQOr-f88SWbU1uuQ=w1280-fcrop64=1,00000000ffffffff-k-c0xffffffff-no-nd-rj",
width: 1280,
height: 720,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/X9zEVI1HRWuZ7gHhG9fv_4IjSLwJMdAadTuXfa2vMAHSAIun4s_e6TgNa1WQOr-f88SWbU1uuQ=w1920-fcrop64=1,00000000ffffffff-k-c0xffffffff-no-nd-rj",
width: 1920,
height: 1080,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/X9zEVI1HRWuZ7gHhG9fv_4IjSLwJMdAadTuXfa2vMAHSAIun4s_e6TgNa1WQOr-f88SWbU1uuQ=w2120-fcrop64=1,00000000ffffffff-k-c0xffffffff-no-nd-rj",
width: 2120,
height: 1192,
),
],
videos: [
VideoItem(
id: "gVKsKpoPAYg",
name: "Synthwave Launch 🌌 - Lofi Girl POV",
length: Some(48),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/gVKsKpoPAYg/default.jpg?sqp=-oaymwEkCHgQWvKriqkDGvABAfgBzgWAAoAKigIMCAAQARg8IEQocjAP&rs=AOn4CLAllPiZpD9AFINMDEjU5dZOzfwVUw",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/gVKsKpoPAYg/mqdefault.jpg?sqp=-oaymwEmCMACELQB8quKqQMa8AEB-AHOBYACgAqKAgwIABABGDwgRChyMA8=&rs=AOn4CLCWybBzmcom_-zoyu0Wg5wKLYO-uQ",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/gVKsKpoPAYg/hqdefault.jpg?sqp=-oaymwExCLwDEPoBSFryq4qpAyMIARUAAIhCGAHwAQH4Ac4FgAKACooCDAgAEAEYPCBEKHIwDw==&rs=AOn4CLBqWFhamy1_BSPTBz7TBig2dzmqNg",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/gVKsKpoPAYg/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AHOBYACgAqKAgwIABABGDwgRChyMA8=&rs=AOn4CLDfQM-GAowYQlSB4XRG2oKf1SlJXQ",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/gVKsKpoPAYg/sddefault.jpg?sqp=-oaymwEmCIAFEOAD8quKqQMa8AEB-AHOBYACgAqKAgwIABABGDwgRChyMA8=&rs=AOn4CLByedJwLMk68FhOb9QWoWNGbbSSbQ",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/gVKsKpoPAYg/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AHOBYACgAqKAgwIABABGDwgRChyMA8=&rs=AOn4CLARMx-s-uKPhUenIn33pO2_qfyFdQ",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCSJ4gkVC6NrvII8umztf0Ow",
name: "Lofi Girl",
avatar: [],
verification: None,
subscriber_count: Some(12300000),
)),
publish_date: "[date]",
publish_date_txt: Some("20 hours ago"),
view_count: Some(55000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "ZBh_mQl-2SQ",
name: "Synthwave Launch 🌌 - Lofi Boy POV",
length: Some(41),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/ZBh_mQl-2SQ/default.jpg?sqp=-oaymwEkCHgQWvKriqkDGvABAfgB_gmAAtAFigIMCAAQARguIDYofzAP&rs=AOn4CLDd4sv05hoiBIKRAT4PbLpmv8uBWQ",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ZBh_mQl-2SQ/mqdefault.jpg?sqp=-oaymwEmCMACELQB8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGC4gNih_MA8=&rs=AOn4CLApfKBRT7E9k8AiNwYPZFeykDilWQ",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ZBh_mQl-2SQ/hqdefault.jpg?sqp=-oaymwExCLwDEPoBSFryq4qpAyMIARUAAIhCGAHwAQH4Af4JgALQBYoCDAgAEAEYLiA2KH8wDw==&rs=AOn4CLAISEXvWDkxAQzzVQS4YDrxWTO6DA",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ZBh_mQl-2SQ/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGC4gNih_MA8=&rs=AOn4CLAvnQlEavMwF9-RSpxIm8q_LeJqYA",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ZBh_mQl-2SQ/sddefault.jpg?sqp=-oaymwEmCIAFEOAD8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGC4gNih_MA8=&rs=AOn4CLDcXdJlM7-ynHW_MEuvsggZJLyk8w",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ZBh_mQl-2SQ/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGC4gNih_MA8=&rs=AOn4CLDQsvu8EmLHSeE7O3_vdHqByo9qCQ",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCSJ4gkVC6NrvII8umztf0Ow",
name: "Lofi Girl",
avatar: [],
verification: None,
subscriber_count: Some(12300000),
)),
publish_date: "[date]",
publish_date_txt: Some("1 day ago"),
view_count: Some(420000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "b9IctXpyPCE",
name: "Synthwave Launch 🌌 - Lofi Girl POV",
length: Some(48),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/b9IctXpyPCE/default.jpg?sqp=-oaymwEkCHgQWvKriqkDGvABAfgB_gmAAtAFigIMCAAQARg-IDoofzAP&rs=AOn4CLCbXUK0zKikwTNK1vhXkHt3aJxCAA",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/b9IctXpyPCE/mqdefault.jpg?sqp=-oaymwEmCMACELQB8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGD4gOih_MA8=&rs=AOn4CLBk3cCVT11R8E7vhZpcRYkUL1q4Fw",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/b9IctXpyPCE/hqdefault.jpg?sqp=-oaymwExCLwDEPoBSFryq4qpAyMIARUAAIhCGAHwAQH4Af4JgALQBYoCDAgAEAEYPiA6KH8wDw==&rs=AOn4CLC5SevU_YD0sw_PB9ES21KwdD2-1Q",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/b9IctXpyPCE/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGD4gOih_MA8=&rs=AOn4CLDInx0-m4dL-8OgN_yWwF6ew-i2TQ",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/b9IctXpyPCE/sddefault.jpg?sqp=-oaymwEmCIAFEOAD8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGD4gOih_MA8=&rs=AOn4CLDDjhE31W7dN0NlkPFylmTNvmFyyQ",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/b9IctXpyPCE/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGD4gOih_MA8=&rs=AOn4CLDde8MHooriWO6obGxSudvX3rcltA",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCSJ4gkVC6NrvII8umztf0Ow",
name: "Lofi Girl",
avatar: [],
verification: None,
subscriber_count: Some(12300000),
)),
publish_date: "[date]",
publish_date_txt: Some("1 day ago"),
view_count: Some(581000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "5cEPXBss8QM",
name: "What is happening with this blue window? 🤨",
length: Some(36),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/5cEPXBss8QM/default.jpg?sqp=-oaymwEkCHgQWvKriqkDGvABAfgB3ASAArgIigIMCAAQARhlIEAoSTAP&rs=AOn4CLAmZoxnBXD10_QWlTbkQiylMFQSRQ",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/5cEPXBss8QM/mqdefault.jpg?sqp=-oaymwEmCMACELQB8quKqQMa8AEB-AHcBIACuAiKAgwIABABGGUgQChJMA8=&rs=AOn4CLD6RBYAb-NAX3QXsiXkKS-wZIonqQ",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/5cEPXBss8QM/hqdefault.jpg?sqp=-oaymwExCLwDEPoBSFryq4qpAyMIARUAAIhCGAHwAQH4AdwEgAK4CIoCDAgAEAEYZSBAKEkwDw==&rs=AOn4CLCI_0yo-jEjtLeazZk3joad3Pmv3Q",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/5cEPXBss8QM/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AHcBIACuAiKAgwIABABGGUgQChJMA8=&rs=AOn4CLAzagmNZtxPAzlHiIxyVPIIG6B69Q",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/5cEPXBss8QM/sddefault.jpg?sqp=-oaymwEmCIAFEOAD8quKqQMa8AEB-AHcBIACuAiKAgwIABABGGUgQChJMA8=&rs=AOn4CLAMhY8RjaBMWNhz0U_D6uIeUyl59g",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/5cEPXBss8QM/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AHcBIACuAiKAgwIABABGGUgQChJMA8=&rs=AOn4CLD0NIOdYXyjktyYcIiI1ob2xTd3Xg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCSJ4gkVC6NrvII8umztf0Ow",
name: "Lofi Girl",
avatar: [],
verification: None,
subscriber_count: Some(12300000),
)),
publish_date: "[date]",
publish_date_txt: Some("6 days ago"),
view_count: Some(100000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "lvfI-9DMILA",
name: "What is happening with this blue window? 🤨",
length: Some(36),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/lvfI-9DMILA/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/lvfI-9DMILA/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/lvfI-9DMILA/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLAE8Ufe-rHgTGAgVxR2DoVMm44wGA",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/lvfI-9DMILA/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/lvfI-9DMILA/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/lvfI-9DMILA/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCSJ4gkVC6NrvII8umztf0Ow",
name: "Lofi Girl",
avatar: [],
verification: None,
subscriber_count: Some(12300000),
)),
publish_date: "[date]",
publish_date_txt: Some("6 days ago"),
view_count: Some(748000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "MVPTGNGiI-4",
name: "synthwave radio 🌌 - beats to chill/game to",
length: None,
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/MVPTGNGiI-4/default_live.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/MVPTGNGiI-4/mqdefault_live.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/MVPTGNGiI-4/hqdefault_live.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLDBtHLdfMneZyfYrENSn-Wt7C-vuQ",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/MVPTGNGiI-4/hqdefault_live.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/MVPTGNGiI-4/sddefault_live.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/MVPTGNGiI-4/maxresdefault_live.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCSJ4gkVC6NrvII8umztf0Ow",
name: "Lofi Girl",
avatar: [],
verification: None,
subscriber_count: Some(12300000),
)),
publish_date: "[date]",
publish_date_txt: None,
view_count: Some(19000),
is_live: true,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "3CIVNxvQRzA",
name: "Lofi Girl finally stops studying 😱",
length: Some(8),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/3CIVNxvQRzA/default.jpg?sqp=-oaymwEkCHgQWvKriqkDGvABAfgBtgiAAv4OigIMCAAQARh_ID4oJjAP&rs=AOn4CLAE5Sc7TQs7igSxBU07H7NIIre4NA",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/3CIVNxvQRzA/mqdefault.jpg?sqp=-oaymwEmCMACELQB8quKqQMa8AEB-AG2CIAC_g6KAgwIABABGH8gPigmMA8=&rs=AOn4CLCMUGGdbhINd1WacSqRVinfbO0cyg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/3CIVNxvQRzA/hqdefault.jpg?sqp=-oaymwExCLwDEPoBSFryq4qpAyMIARUAAIhCGAHwAQH4AbYIgAL-DooCDAgAEAEYfyA-KCYwDw==&rs=AOn4CLAuS_30v--v1KGCzumWhEULTvLuJA",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/3CIVNxvQRzA/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AG2CIAC_g6KAgwIABABGH8gPigmMA8=&rs=AOn4CLBwf3ag6DantZd0_DXVLKyWe1zrIA",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/3CIVNxvQRzA/sddefault.jpg?sqp=-oaymwEmCIAFEOAD8quKqQMa8AEB-AG2CIAC_g6KAgwIABABGH8gPigmMA8=&rs=AOn4CLAnwEsjw0bVV-rAM161dUE74fKrIg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/3CIVNxvQRzA/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AG2CIAC_g6KAgwIABABGH8gPigmMA8=&rs=AOn4CLC_RuPVKzI0RWNCicd81hSuWPhFbQ",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCSJ4gkVC6NrvII8umztf0Ow",
name: "Lofi Girl",
avatar: [],
verification: None,
subscriber_count: Some(12300000),
)),
publish_date: "[date]",
publish_date_txt: Some("12 days ago"),
view_count: Some(168000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "uHxgR7GzRNY",
name: "Lofi Girl finally stops studying 😱",
length: Some(148),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/uHxgR7GzRNY/default.jpg?sqp=-oaymwEkCHgQWvKriqkDGvABAfgB_gmAAtAFigIMCAAQARhyIEMoNTAP&rs=AOn4CLAR7wezefrApQ0aSxhGMXc-imbzeg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/uHxgR7GzRNY/mqdefault.jpg?sqp=-oaymwEmCMACELQB8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGHIgQyg1MA8=&rs=AOn4CLAopDnPneEkCVNJWHCjY8eGYzL5pQ",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/uHxgR7GzRNY/hqdefault.jpg?sqp=-oaymwExCLwDEPoBSFryq4qpAyMIARUAAIhCGAHwAQH4Af4JgALQBYoCDAgAEAEYciBDKDUwDw==&rs=AOn4CLDj0vLIiP1V1hPEK1cPMfSDzjrzSA",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/uHxgR7GzRNY/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGHIgQyg1MA8=&rs=AOn4CLDBw6HFU4g3fPo02jrq2-rEB-lZBA",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/uHxgR7GzRNY/sddefault.jpg?sqp=-oaymwEmCIAFEOAD8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGHIgQyg1MA8=&rs=AOn4CLCqV5Jt-U3D09meQjnFjHTq5LS8XA",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/uHxgR7GzRNY/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGHIgQyg1MA8=&rs=AOn4CLBe8ct3EsMimsEFZdvo0SI0XYrCAw",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCSJ4gkVC6NrvII8umztf0Ow",
name: "Lofi Girl",
avatar: [],
verification: None,
subscriber_count: Some(12300000),
)),
publish_date: "[date]",
publish_date_txt: Some("12 days ago"),
view_count: Some(787000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "VrtXuFQLel8",
name: "Lofi Cat is love, Lofi Cat is life 🫶 ✨",
length: Some(7),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/VrtXuFQLel8/default.jpg?sqp=-oaymwEkCHgQWvKriqkDGvABAfgBtgiAAoAPigIMCAAQARh_ICwoMDAP&rs=AOn4CLC6Y0PF2HDFqbBLcEEQrcy1YG3WuQ",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/VrtXuFQLel8/mqdefault.jpg?sqp=-oaymwEmCMACELQB8quKqQMa8AEB-AG2CIACgA-KAgwIABABGH8gLCgwMA8=&rs=AOn4CLCf-XfwA3ePIcRFh_BlOkOcw_JfXQ",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/VrtXuFQLel8/hqdefault.jpg?sqp=-oaymwExCLwDEPoBSFryq4qpAyMIARUAAIhCGAHwAQH4AbYIgAKAD4oCDAgAEAEYfyAsKDAwDw==&rs=AOn4CLCeDpZ85ZEmzk1JfSS5nT3hf1bk9g",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/VrtXuFQLel8/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AG2CIACgA-KAgwIABABGH8gLCgwMA8=&rs=AOn4CLA5m5PgHk5fpacw_-NW9HsB-8VyDw",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/VrtXuFQLel8/sddefault.jpg?sqp=-oaymwEmCIAFEOAD8quKqQMa8AEB-AG2CIACgA-KAgwIABABGH8gLCgwMA8=&rs=AOn4CLBtNdSVka23uytjitK2AuKNiEmVtA",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/VrtXuFQLel8/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AG2CIACgA-KAgwIABABGH8gLCgwMA8=&rs=AOn4CLCUYKUjARNS-JW6u2pt8rHtmC_huA",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCSJ4gkVC6NrvII8umztf0Ow",
name: "Lofi Girl",
avatar: [],
verification: None,
subscriber_count: Some(12300000),
)),
publish_date: "[date]",
publish_date_txt: Some("2 weeks ago"),
view_count: Some(143000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "cCF0SNS26gc",
name: "When youve been studying for 5+ years… 🙄",
length: Some(8),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/cCF0SNS26gc/default.jpg?sqp=-oaymwEkCHgQWvKriqkDGvABAfgBtgiAAoAPigIMCAAQARh_IEUoMTAP&rs=AOn4CLBL3KeYaYjrE4qX_1yeXMN7tWUmFw",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/cCF0SNS26gc/mqdefault.jpg?sqp=-oaymwEmCMACELQB8quKqQMa8AEB-AG2CIACgA-KAgwIABABGH8gRSgxMA8=&rs=AOn4CLAYzqMqqDG3KrtwjhrbkxbbNaT5Bw",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/cCF0SNS26gc/hqdefault.jpg?sqp=-oaymwExCLwDEPoBSFryq4qpAyMIARUAAIhCGAHwAQH4AbYIgAKAD4oCDAgAEAEYfyBFKDEwDw==&rs=AOn4CLCKpJoGK7LcfqKjecX0Vw-F9OEVEg",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/cCF0SNS26gc/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AG2CIACgA-KAgwIABABGH8gRSgxMA8=&rs=AOn4CLCE9mgcI2iL21MOcrFIBatKrUxjLg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/cCF0SNS26gc/sddefault.jpg?sqp=-oaymwEmCIAFEOAD8quKqQMa8AEB-AG2CIACgA-KAgwIABABGH8gRSgxMA8=&rs=AOn4CLACrsY9egZ6YOOKAZtoTdKx3cRuiQ",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/cCF0SNS26gc/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AG2CIACgA-KAgwIABABGH8gRSgxMA8=&rs=AOn4CLBr5xKlWjVwVEXlN7DwLMVDx8IN9A",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCSJ4gkVC6NrvII8umztf0Ow",
name: "Lofi Girl",
avatar: [],
verification: None,
subscriber_count: Some(12300000),
)),
publish_date: "[date]",
publish_date_txt: Some("3 weeks ago"),
view_count: Some(285000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "ywsXCTQ9vCI",
name: "I guess youve wondered where Ive been 🌱💃",
length: Some(9),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/ywsXCTQ9vCI/default.jpg?sqp=-oaymwEkCHgQWvKriqkDGvABAfgBtgiAAoAPigIMCAAQARh_IEMoLTAP&rs=AOn4CLDuC4a7nRUlXlBFCv3pIbTxXRZVPw",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ywsXCTQ9vCI/mqdefault.jpg?sqp=-oaymwEmCMACELQB8quKqQMa8AEB-AG2CIACgA-KAgwIABABGH8gQygtMA8=&rs=AOn4CLATDhHYdSI2zS2pykxNh_MPJCbQzw",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ywsXCTQ9vCI/hqdefault.jpg?sqp=-oaymwExCLwDEPoBSFryq4qpAyMIARUAAIhCGAHwAQH4AbYIgAKAD4oCDAgAEAEYfyBDKC0wDw==&rs=AOn4CLAUZw-rudW1_yzIocuXi_wcilFnVQ",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ywsXCTQ9vCI/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AG2CIACgA-KAgwIABABGH8gQygtMA8=&rs=AOn4CLD3hdkjRKJ-SFkzUnk82Y7I0ZCTtQ",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ywsXCTQ9vCI/sddefault.jpg?sqp=-oaymwEmCIAFEOAD8quKqQMa8AEB-AG2CIACgA-KAgwIABABGH8gQygtMA8=&rs=AOn4CLDpODHlMHMb7MgOSSQOpM_hJUtGfg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ywsXCTQ9vCI/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AG2CIACgA-KAgwIABABGH8gQygtMA8=&rs=AOn4CLAK6jAEK0rfufMyY_rWRasU25dWHw",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCSJ4gkVC6NrvII8umztf0Ow",
name: "Lofi Girl",
avatar: [],
verification: None,
subscriber_count: Some(12300000),
)),
publish_date: "[date]",
publish_date_txt: Some("1 month ago"),
view_count: Some(513000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "Uzo6x3uwWRA",
name: "Get your own Lofi avatar merch now! 👕 (Link in comments 🔗)",
length: Some(25),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/Uzo6x3uwWRA/default.jpg?sqp=-oaymwEkCHgQWvKriqkDGvABAfgBtgiAAoAPigIMCAAQARhyIFcoQTAP&rs=AOn4CLAntbjIEzzY0MHNuV59m-iWn1VS_w",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Uzo6x3uwWRA/mqdefault.jpg?sqp=-oaymwEmCMACELQB8quKqQMa8AEB-AG2CIACgA-KAgwIABABGHIgVyhBMA8=&rs=AOn4CLCdoRffILQ3_rpwfQn5tn4SzWQDzw",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Uzo6x3uwWRA/hqdefault.jpg?sqp=-oaymwExCLwDEPoBSFryq4qpAyMIARUAAIhCGAHwAQH4AbYIgAKAD4oCDAgAEAEYciBXKEEwDw==&rs=AOn4CLBq_MQJHQdSx97dgT9OoAJByqVJcw",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Uzo6x3uwWRA/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AG2CIACgA-KAgwIABABGHIgVyhBMA8=&rs=AOn4CLDxViAcvucN_oL0i1GNoHAp7o5MHQ",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Uzo6x3uwWRA/sddefault.jpg?sqp=-oaymwEmCIAFEOAD8quKqQMa8AEB-AG2CIACgA-KAgwIABABGHIgVyhBMA8=&rs=AOn4CLAUSgHmTIlbT5BK8h43dw5WthNvwA",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Uzo6x3uwWRA/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AG2CIACgA-KAgwIABABGHIgVyhBMA8=&rs=AOn4CLBZN107K9EmsTktng19I_x78LrDwA",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCSJ4gkVC6NrvII8umztf0Ow",
name: "Lofi Girl",
avatar: [],
verification: None,
subscriber_count: Some(12300000),
)),
publish_date: "[date]",
publish_date_txt: Some("1 month ago"),
view_count: Some(98000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
],
visitor_data: Some("CgttSVVrMVpBMEVfZyjM2eWhBg%3D%3D"),
)

View file

@ -0,0 +1,658 @@
---
source: src/client/channel_tv.rs
expression: map_res.c
---
ChannelTv(
id: "UCWxlUwW9BgGISaakjGM37aw",
name: "Valkyrae",
subscriber_count: Some(3860000),
avatar: [
Thumbnail(
url: "https://yt3.googleusercontent.com/0Qu_iyV5XEUmy7MBd46TqRzsMuTED6M5zvunG8W6GjOrRn3pgs-BSTLREWopbkmzQIC66R_FPDs=s72-c-k-c0x00ffffff-no-rj",
width: 72,
height: 72,
),
],
tv_banner: [
Thumbnail(
url: "https://yt3.googleusercontent.com/PXxQnCyAMLb1SKCBLAMt7Y9EkYe_oCPlVDeNnNcecWC6CdWSvC-czHiNLS_te1DgO4G9qLcQKQ=w320-fcrop64=1,00000000ffffffff-k-c0xffffffff-no-nd-rj",
width: 320,
height: 180,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/PXxQnCyAMLb1SKCBLAMt7Y9EkYe_oCPlVDeNnNcecWC6CdWSvC-czHiNLS_te1DgO4G9qLcQKQ=w854-fcrop64=1,00000000ffffffff-k-c0xffffffff-no-nd-rj",
width: 854,
height: 480,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/PXxQnCyAMLb1SKCBLAMt7Y9EkYe_oCPlVDeNnNcecWC6CdWSvC-czHiNLS_te1DgO4G9qLcQKQ=w1280-fcrop64=1,00000000ffffffff-k-c0xffffffff-no-nd-rj",
width: 1280,
height: 720,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/PXxQnCyAMLb1SKCBLAMt7Y9EkYe_oCPlVDeNnNcecWC6CdWSvC-czHiNLS_te1DgO4G9qLcQKQ=w1920-fcrop64=1,00000000ffffffff-k-c0xffffffff-no-nd-rj",
width: 1920,
height: 1080,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/PXxQnCyAMLb1SKCBLAMt7Y9EkYe_oCPlVDeNnNcecWC6CdWSvC-czHiNLS_te1DgO4G9qLcQKQ=w2120-fcrop64=1,00000000ffffffff-k-c0xffffffff-no-nd-rj",
width: 2120,
height: 1192,
),
],
videos: [
VideoItem(
id: "p9FfS9l2NVA",
name: "🔴LIVE! PANCAKE ART STREAM WITH POKI, JANET, ABE, WENDY, MIYOUNG!",
length: None,
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/p9FfS9l2NVA/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/p9FfS9l2NVA/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/p9FfS9l2NVA/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLDJMx8f-8HX5HxX4UoU6seW_D48pw",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/p9FfS9l2NVA/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/p9FfS9l2NVA/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/p9FfS9l2NVA/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCWxlUwW9BgGISaakjGM37aw",
name: "Valkyrae",
avatar: [],
verification: None,
subscriber_count: Some(3860000),
)),
publish_date: Some("2023-04-15T00:00:00Z"),
publish_date_txt: Some("Scheduled for 4/15/23, 12:00\u{202f}AM"),
view_count: Some(415231200),
is_live: false,
is_short: false,
is_upcoming: true,
short_description: None,
),
VideoItem(
id: "O6JydZZ4oeg",
name: "🔴LIVE! LATE NIGHT VALORANT CUSTOMS WITH FRIENDS",
length: Some(21637),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/O6JydZZ4oeg/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/O6JydZZ4oeg/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/O6JydZZ4oeg/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLCIubr_9LF228yHYZ8qNbsansQCsg",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/O6JydZZ4oeg/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/O6JydZZ4oeg/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/O6JydZZ4oeg/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCWxlUwW9BgGISaakjGM37aw",
name: "Valkyrae",
avatar: [],
verification: None,
subscriber_count: Some(3860000),
)),
publish_date: "[date]",
publish_date_txt: Some("Streamed 19 hours ago"),
view_count: Some(154000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "Icncr_uD9uI",
name: "Valkyrae finds the Philippines playing GeoGuessr",
length: Some(40),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/Icncr_uD9uI/default.jpg?sqp=-oaymwEkCHgQWvKriqkDGvABAfgBtgiAAoAPigIMCAAQARhLIEUoZTAP&rs=AOn4CLCKbMp0kLZ5PcherysRHhSojpNxXw",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Icncr_uD9uI/mqdefault.jpg?sqp=-oaymwEmCMACELQB8quKqQMa8AEB-AG2CIACgA-KAgwIABABGEsgRShlMA8=&rs=AOn4CLBgiSwjm7nbD3Z76apbfXEAOm4ZRA",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Icncr_uD9uI/hqdefault.jpg?sqp=-oaymwExCLwDEPoBSFryq4qpAyMIARUAAIhCGAHwAQH4AbYIgAKAD4oCDAgAEAEYSyBFKGUwDw==&rs=AOn4CLB_16Ojqn-iGBwWJBOPqgCKSutAOg",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Icncr_uD9uI/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AG2CIACgA-KAgwIABABGEsgRShlMA8=&rs=AOn4CLA112dBnQCeaPHeV1bogHBbUwxIvw",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Icncr_uD9uI/sddefault.jpg?sqp=-oaymwEmCIAFEOAD8quKqQMa8AEB-AG2CIACgA-KAgwIABABGEsgRShlMA8=&rs=AOn4CLD1TkrCpNRnj6BZV7qSIIQCApDA3g",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Icncr_uD9uI/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AG2CIACgA-KAgwIABABGEsgRShlMA8=&rs=AOn4CLCcBVkBlipt-vIs5fvg82SzSzOhCQ",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCWxlUwW9BgGISaakjGM37aw",
name: "Valkyrae",
avatar: [],
verification: None,
subscriber_count: Some(3860000),
)),
publish_date: "[date]",
publish_date_txt: Some("1 day ago"),
view_count: Some(197000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "0CmGjltsNf0",
name: "🔴LIVE! extra extra extra secret minecraft pixelmon stream huehuehuehu",
length: Some(15429),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/0CmGjltsNf0/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/0CmGjltsNf0/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/0CmGjltsNf0/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLDIWpYtLAFAM0wnRVCBnMV8kjm2ig",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/0CmGjltsNf0/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/0CmGjltsNf0/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/0CmGjltsNf0/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCWxlUwW9BgGISaakjGM37aw",
name: "Valkyrae",
avatar: [],
verification: None,
subscriber_count: Some(3860000),
)),
publish_date: "[date]",
publish_date_txt: Some("Streamed 1 day ago"),
view_count: Some(126000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "ZFfRrYf11Ns",
name: "what is up guys, it\'s sykkuno here",
length: Some(47),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/ZFfRrYf11Ns/default.jpg?sqp=-oaymwEkCHgQWvKriqkDGvABAfgBtgiAAoAPigIMCAAQARhkIGQoZDAP&rs=AOn4CLByFuwEpIgs87JsrJi5E-u3ozAhDg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ZFfRrYf11Ns/mqdefault.jpg?sqp=-oaymwEmCMACELQB8quKqQMa8AEB-AG2CIACgA-KAgwIABABGGQgZChkMA8=&rs=AOn4CLDp_GipatHrrwxQthGWT1euKxKogQ",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ZFfRrYf11Ns/hqdefault.jpg?sqp=-oaymwExCLwDEPoBSFryq4qpAyMIARUAAIhCGAHwAQH4AbYIgAKAD4oCDAgAEAEYZCBkKGQwDw==&rs=AOn4CLA08FrVUpZ-gbw6c_Nl0HFgd2E0Nw",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ZFfRrYf11Ns/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AG2CIACgA-KAgwIABABGGQgZChkMA8=&rs=AOn4CLBeOgCqq-lQiYfpWcKhG9kVqpAhmg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ZFfRrYf11Ns/sddefault.jpg?sqp=-oaymwEmCIAFEOAD8quKqQMa8AEB-AG2CIACgA-KAgwIABABGGQgZChkMA8=&rs=AOn4CLD8lwtn-8bBC2hfTrejUxv-4vrfGA",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ZFfRrYf11Ns/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AG2CIACgA-KAgwIABABGGQgZChkMA8=&rs=AOn4CLAc_p7D9LB2G6OTSIZ2HQQyARi4Ow",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCWxlUwW9BgGISaakjGM37aw",
name: "Valkyrae",
avatar: [],
verification: None,
subscriber_count: Some(3860000),
)),
publish_date: "[date]",
publish_date_txt: Some("1 day ago"),
view_count: Some(130000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "ZF0dBucsr_Y",
name: "🔴LIVE! SHINY HUNTING EVENT IN MINECRAFT PIXELMON!! Prepping for Tournament this upcoming Tuesday!",
length: Some(21033),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/ZF0dBucsr_Y/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ZF0dBucsr_Y/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ZF0dBucsr_Y/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLC_gc0i4B-YNXgo8Vy-odV8EDYl3w",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ZF0dBucsr_Y/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ZF0dBucsr_Y/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/ZF0dBucsr_Y/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCWxlUwW9BgGISaakjGM37aw",
name: "Valkyrae",
avatar: [],
verification: None,
subscriber_count: Some(3860000),
)),
publish_date: "[date]",
publish_date_txt: Some("Streamed 2 days ago"),
view_count: Some(168000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "0JdncHeIH4Y",
name: "Drinking And Mario Party Is A Bad Idea",
length: Some(607),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/0JdncHeIH4Y/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/0JdncHeIH4Y/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/0JdncHeIH4Y/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLAkO1rWOWBg-bqGpi2YcOaMHuHhKA",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/0JdncHeIH4Y/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/0JdncHeIH4Y/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/0JdncHeIH4Y/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCWxlUwW9BgGISaakjGM37aw",
name: "Valkyrae",
avatar: [],
verification: None,
subscriber_count: Some(3860000),
)),
publish_date: "[date]",
publish_date_txt: Some("3 days ago"),
view_count: Some(169000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "aT3awRbkrmE",
name: "🔴LIVE! OTV podcast rescheduled! MINECRAFT PIXELMON grinding for tournament next tuesday",
length: Some(16263),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/aT3awRbkrmE/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/aT3awRbkrmE/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/aT3awRbkrmE/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLBW7btLvwFJehpcCQG7Pf7NY5GWhg",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/aT3awRbkrmE/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/aT3awRbkrmE/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/aT3awRbkrmE/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCWxlUwW9BgGISaakjGM37aw",
name: "Valkyrae",
avatar: [],
verification: None,
subscriber_count: Some(3860000),
)),
publish_date: "[date]",
publish_date_txt: Some("Streamed 3 days ago"),
view_count: Some(150000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "7MuYYZaeMGY",
name: "Pokimane inspired Valkyrae to do this",
length: Some(48),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/7MuYYZaeMGY/default.jpg?sqp=-oaymwEkCHgQWvKriqkDGvABAfgBtgiAAoAPigIMCAAQARhkIGQoZDAP&rs=AOn4CLAMe4zJbFCXEYEkmlz_IlCt0wVlbA",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/7MuYYZaeMGY/mqdefault.jpg?sqp=-oaymwEmCMACELQB8quKqQMa8AEB-AG2CIACgA-KAgwIABABGGQgZChkMA8=&rs=AOn4CLDge_3nSYUigeoTZFbj11re2Zm-aw",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/7MuYYZaeMGY/hqdefault.jpg?sqp=-oaymwExCLwDEPoBSFryq4qpAyMIARUAAIhCGAHwAQH4AbYIgAKAD4oCDAgAEAEYZCBkKGQwDw==&rs=AOn4CLAKrzrr6nKbHJ8cLLbC2Q9O0yDl6Q",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/7MuYYZaeMGY/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AG2CIACgA-KAgwIABABGGQgZChkMA8=&rs=AOn4CLB0qpFfF0dCr6RVgm0WTuHn1xp7Yw",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/7MuYYZaeMGY/sddefault.jpg?sqp=-oaymwEmCIAFEOAD8quKqQMa8AEB-AG2CIACgA-KAgwIABABGGQgZChkMA8=&rs=AOn4CLBD4Ocl-3BmD3_gNcl7vomIM9N31A",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/7MuYYZaeMGY/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AG2CIACgA-KAgwIABABGGQgZChkMA8=&rs=AOn4CLD6CApz_43WzD77sI5HmHfN67AQfw",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCWxlUwW9BgGISaakjGM37aw",
name: "Valkyrae",
avatar: [],
verification: None,
subscriber_count: Some(3860000),
)),
publish_date: "[date]",
publish_date_txt: Some("3 days ago"),
view_count: Some(231000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "8YHqas_yqxc",
name: "🔴LIVE! super secret MINECRAFT PIXELMON grinding stream for tournament!",
length: Some(18741),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/8YHqas_yqxc/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/8YHqas_yqxc/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/8YHqas_yqxc/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLDBF3HreXwgG040aAb1za3-NSzKMA",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/8YHqas_yqxc/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/8YHqas_yqxc/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/8YHqas_yqxc/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCWxlUwW9BgGISaakjGM37aw",
name: "Valkyrae",
avatar: [],
verification: None,
subscriber_count: Some(3860000),
)),
publish_date: "[date]",
publish_date_txt: Some("Streamed 4 days ago"),
view_count: Some(200000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "l-n15_3LPec",
name: "Valkyrae Approved OTV&Friends New Intro",
length: Some(42),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/l-n15_3LPec/default.jpg?sqp=-oaymwEkCHgQWvKriqkDGvABAfgBtgiAAoAPigIMCAAQARhRIEkocjAP&rs=AOn4CLAZh6zhNqic1SRPgbqlmT1AVY0DHQ",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/l-n15_3LPec/mqdefault.jpg?sqp=-oaymwEmCMACELQB8quKqQMa8AEB-AG2CIACgA-KAgwIABABGFEgSShyMA8=&rs=AOn4CLA64LwLd6rHBugVBvo_nUAR8lxjDg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/l-n15_3LPec/hqdefault.jpg?sqp=-oaymwExCLwDEPoBSFryq4qpAyMIARUAAIhCGAHwAQH4AbYIgAKAD4oCDAgAEAEYUSBJKHIwDw==&rs=AOn4CLBwBNvszWXxocEnOpmhMRC0vDKD-A",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/l-n15_3LPec/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AG2CIACgA-KAgwIABABGFEgSShyMA8=&rs=AOn4CLBkhkehZX9Ga6S2PE7lSMgMhnUc3Q",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/l-n15_3LPec/sddefault.jpg?sqp=-oaymwEmCIAFEOAD8quKqQMa8AEB-AG2CIACgA-KAgwIABABGFEgSShyMA8=&rs=AOn4CLDa_BOAEKPCy3gq_GPT3TuCLbd69A",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/l-n15_3LPec/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AG2CIACgA-KAgwIABABGFEgSShyMA8=&rs=AOn4CLD5HuEQa9IhweU3gqEMlisLKW59EA",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCWxlUwW9BgGISaakjGM37aw",
name: "Valkyrae",
avatar: [],
verification: None,
subscriber_count: Some(3860000),
)),
publish_date: "[date]",
publish_date_txt: Some("4 days ago"),
view_count: Some(255000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "lTwneHLpAYo",
name: "🔴LIVE! MINECRAFT PIXELMON THEN DRUNK WITH SYKKUNO, FUSLIE AND KKATAMINA",
length: Some(28571),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/lTwneHLpAYo/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/lTwneHLpAYo/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/lTwneHLpAYo/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLAEMkeC_HFuyXsJr_RPq8n4t8lc4Q",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/lTwneHLpAYo/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/lTwneHLpAYo/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/lTwneHLpAYo/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCWxlUwW9BgGISaakjGM37aw",
name: "Valkyrae",
avatar: [],
verification: None,
subscriber_count: Some(3860000),
)),
publish_date: "[date]",
publish_date_txt: Some("Streamed 5 days ago"),
view_count: Some(323000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
],
visitor_data: Some("Cgthc1JHM2E1QkNxZyjwmeehBg%3D%3D"),
)

View file

@ -0,0 +1,658 @@
---
source: src/client/channel_tv.rs
expression: map_res.c
---
ChannelTv(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh - Topic",
subscriber_count: Some(2770),
avatar: [
Thumbnail(
url: "https://yt3.googleusercontent.com/sv-mI1QXWqBbrQPm4j919deVSt6L2r6HmBHTVS9R5Fu5qALvGABzfV4Vt5bjtXrZzdT7kxbh=s72-c-k-c0x00ffffff-no-rj",
width: 72,
height: 72,
),
],
tv_banner: [
Thumbnail(
url: "https://yt3.googleusercontent.com/16E_pmaqBBiJdP2m4NBpcMxAc3QznBGsl-wXq3Ja1zkJ-Nudda3GBCmzLBBBI6ORf2_DKFZD=w320-fcrop64=1,00000000ffffffff-k-c0xffffffff-no-nd-rj",
width: 320,
height: 180,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/16E_pmaqBBiJdP2m4NBpcMxAc3QznBGsl-wXq3Ja1zkJ-Nudda3GBCmzLBBBI6ORf2_DKFZD=w854-fcrop64=1,00000000ffffffff-k-c0xffffffff-no-nd-rj",
width: 854,
height: 480,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/16E_pmaqBBiJdP2m4NBpcMxAc3QznBGsl-wXq3Ja1zkJ-Nudda3GBCmzLBBBI6ORf2_DKFZD=w1280-fcrop64=1,00000000ffffffff-k-c0xffffffff-no-nd-rj",
width: 1280,
height: 720,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/16E_pmaqBBiJdP2m4NBpcMxAc3QznBGsl-wXq3Ja1zkJ-Nudda3GBCmzLBBBI6ORf2_DKFZD=w1920-fcrop64=1,00000000ffffffff-k-c0xffffffff-no-nd-rj",
width: 1920,
height: 1080,
),
Thumbnail(
url: "https://yt3.googleusercontent.com/16E_pmaqBBiJdP2m4NBpcMxAc3QznBGsl-wXq3Ja1zkJ-Nudda3GBCmzLBBBI6ORf2_DKFZD=w2120-fcrop64=1,00000000ffffffff-k-c0xffffffff-no-nd-rj",
width: 2120,
height: 1192,
),
],
videos: [
VideoItem(
id: "Qt5O4m1Ec2Q",
name: "EGAL WIE WEIT",
length: Some(165),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/Qt5O4m1Ec2Q/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Qt5O4m1Ec2Q/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Qt5O4m1Ec2Q/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLDtI7c4eE1u8uqxg3Irtmf-MGGmFw",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Qt5O4m1Ec2Q/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Qt5O4m1Ec2Q/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Qt5O4m1Ec2Q/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh - Topic",
avatar: [],
verification: None,
subscriber_count: Some(2770),
)),
publish_date: "[date]",
publish_date_txt: None,
view_count: Some(1400),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "Rm6zRFZ4wwE",
name: "SICHER BEI DIR",
length: Some(156),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/Rm6zRFZ4wwE/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Rm6zRFZ4wwE/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Rm6zRFZ4wwE/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLC5UbJUxU8HBUGchCgv0SetZFTnjQ",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Rm6zRFZ4wwE/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Rm6zRFZ4wwE/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/Rm6zRFZ4wwE/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh - Topic",
avatar: [],
verification: None,
subscriber_count: Some(2770),
)),
publish_date: "[date]",
publish_date_txt: None,
view_count: Some(1200),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "iY-sFmlzPK0",
name: "DURCH RAUM UND ZEIT",
length: Some(162),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/iY-sFmlzPK0/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/iY-sFmlzPK0/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/iY-sFmlzPK0/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLCh7tYoG_6xw4VgXyDFuCTNETDYwg",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/iY-sFmlzPK0/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/iY-sFmlzPK0/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/iY-sFmlzPK0/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh - Topic",
avatar: [],
verification: None,
subscriber_count: Some(2770),
)),
publish_date: "[date]",
publish_date_txt: None,
view_count: Some(869),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "JzrIx7s7rgQ",
name: "FALLEN",
length: Some(182),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/JzrIx7s7rgQ/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/JzrIx7s7rgQ/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/JzrIx7s7rgQ/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLBmSyAQ9MDfkaYD9S78Rx750S9L4g",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/JzrIx7s7rgQ/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/JzrIx7s7rgQ/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/JzrIx7s7rgQ/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh - Topic",
avatar: [],
verification: None,
subscriber_count: Some(2770),
)),
publish_date: "[date]",
publish_date_txt: None,
view_count: Some(1000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "93u_DBVvmog",
name: "AUFHÖREN",
length: Some(153),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/93u_DBVvmog/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/93u_DBVvmog/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/93u_DBVvmog/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLD9Uh9HmGJHNbsrFFNLBcGTWOD6qw",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/93u_DBVvmog/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/93u_DBVvmog/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/93u_DBVvmog/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh - Topic",
avatar: [],
verification: None,
subscriber_count: Some(2770),
)),
publish_date: "[date]",
publish_date_txt: None,
view_count: Some(541),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "IQ6TL22BNNc",
name: "MUSIK MUSIK",
length: Some(170),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/IQ6TL22BNNc/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/IQ6TL22BNNc/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/IQ6TL22BNNc/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLB85WMOGLhtIJFBYjubqDiqb5IfZQ",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/IQ6TL22BNNc/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/IQ6TL22BNNc/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/IQ6TL22BNNc/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh - Topic",
avatar: [],
verification: None,
subscriber_count: Some(2770),
)),
publish_date: "[date]",
publish_date_txt: None,
view_count: Some(454),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "kxt7rRfep34",
name: "VORHANG FÄLLT",
length: Some(166),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/kxt7rRfep34/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/kxt7rRfep34/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/kxt7rRfep34/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLA8ayzzU7JuY89xlWx3wqnJkfnOjQ",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/kxt7rRfep34/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/kxt7rRfep34/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/kxt7rRfep34/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh - Topic",
avatar: [],
verification: None,
subscriber_count: Some(2770),
)),
publish_date: "[date]",
publish_date_txt: None,
view_count: Some(1800),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "YrNOaUFg7y0",
name: "VERLIEREN",
length: Some(167),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/YrNOaUFg7y0/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/YrNOaUFg7y0/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/YrNOaUFg7y0/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLB_Ph13YVWFj9JGmbHBnBOzcbOA6g",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/YrNOaUFg7y0/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/YrNOaUFg7y0/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/YrNOaUFg7y0/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh - Topic",
avatar: [],
verification: None,
subscriber_count: Some(2770),
)),
publish_date: "[date]",
publish_date_txt: None,
view_count: Some(325),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "CIxzZw0hp0Y",
name: "ALLES HAT SEINE ZEIT",
length: Some(172),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/CIxzZw0hp0Y/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/CIxzZw0hp0Y/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/CIxzZw0hp0Y/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLCuyBOgqnDfDxPifi1zPCRDI_0oYg",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/CIxzZw0hp0Y/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/CIxzZw0hp0Y/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/CIxzZw0hp0Y/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh - Topic",
avatar: [],
verification: None,
subscriber_count: Some(2770),
)),
publish_date: "[date]",
publish_date_txt: None,
view_count: Some(2600),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "nA_1PDF77Ns",
name: "DAS BESTE AM TAG",
length: Some(168),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/nA_1PDF77Ns/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/nA_1PDF77Ns/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/nA_1PDF77Ns/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLAEz6-L8pw8dzG3bwPnz-6VMmH1Ag",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/nA_1PDF77Ns/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/nA_1PDF77Ns/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/nA_1PDF77Ns/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh - Topic",
avatar: [],
verification: None,
subscriber_count: Some(2770),
)),
publish_date: "[date]",
publish_date_txt: None,
view_count: Some(1900),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "qW2ic3FeBjM",
name: "ICH LASS DICH ZIEHEN",
length: Some(287),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/qW2ic3FeBjM/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/qW2ic3FeBjM/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/qW2ic3FeBjM/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLBUN1-VVbDamdYBr4vm0GsTne998g",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/qW2ic3FeBjM/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/qW2ic3FeBjM/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/qW2ic3FeBjM/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh - Topic",
avatar: [],
verification: None,
subscriber_count: Some(2770),
)),
publish_date: "[date]",
publish_date_txt: None,
view_count: Some(1300),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "jZ2ticfS1D4",
name: "DU KANNST MICH SEHEN",
length: Some(200),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/jZ2ticfS1D4/default.jpg",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/jZ2ticfS1D4/mqdefault.jpg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/jZ2ticfS1D4/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLC_sXVGbdwZeNWsr90XZjKFQzZNKw",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/jZ2ticfS1D4/hqdefault.jpg",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/jZ2ticfS1D4/sddefault.jpg",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/jZ2ticfS1D4/maxresdefault.jpg",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh - Topic",
avatar: [],
verification: None,
subscriber_count: Some(2770),
)),
publish_date: "[date]",
publish_date_txt: None,
view_count: Some(1900),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
],
visitor_data: Some("CgtMal9oOXFScTd4cyiXvuWhBg%3D%3D"),
)

View file

@ -0,0 +1,71 @@
---
source: src/client/channel_tv.rs
expression: map_res.c
---
ChannelTv(
id: "UCAkeE1thnToEXZTao-CZkHw",
name: "FiveV",
subscriber_count: Some(939),
avatar: [
Thumbnail(
url: "https://yt3.googleusercontent.com/ytc/AGIKgqPN812F8UPxmHD4vUBaoqrjAqzUIWKs7XENz3FO6g=s72-c-k-c0x00ffffff-no-rj",
width: 72,
height: 72,
),
],
tv_banner: [],
videos: [
VideoItem(
id: "mUOxprz6g1A",
name: "👑GoodLifeRP👑 Vorstellungs Video",
length: Some(121),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/mUOxprz6g1A/default.jpg?sqp=-oaymwEkCHgQWvKriqkDGvABAfgB1AaAAuADigIMCAAQARhlIFIoTzAP&rs=AOn4CLDzWpAQemNy4coi5mGX4Zidtz8D6g",
width: 120,
height: 90,
),
Thumbnail(
url: "https://i.ytimg.com/vi/mUOxprz6g1A/mqdefault.jpg?sqp=-oaymwEmCMACELQB8quKqQMa8AEB-AHUBoAC4AOKAgwIABABGGUgUihPMA8=&rs=AOn4CLApsX2G48JM12sRKBtz4S4C99LNIg",
width: 320,
height: 180,
),
Thumbnail(
url: "https://i.ytimg.com/vi/mUOxprz6g1A/hqdefault.jpg?sqp=-oaymwExCLwDEPoBSFryq4qpAyMIARUAAIhCGAHwAQH4AdQGgALgA4oCDAgAEAEYZSBSKE8wDw==&rs=AOn4CLDi1avXHbcTse7rvzBelOhmZG7_-g",
width: 444,
height: 250,
),
Thumbnail(
url: "https://i.ytimg.com/vi/mUOxprz6g1A/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AHUBoAC4AOKAgwIABABGGUgUihPMA8=&rs=AOn4CLAeN1JLavadZyIRnBVbsY3q71WmNQ",
width: 480,
height: 360,
),
Thumbnail(
url: "https://i.ytimg.com/vi/mUOxprz6g1A/sddefault.jpg?sqp=-oaymwEmCIAFEOAD8quKqQMa8AEB-AHUBoAC4AOKAgwIABABGGUgUihPMA8=&rs=AOn4CLDy0YOfnfHhou6_EWPlMMt-k0rMeQ",
width: 640,
height: 480,
),
Thumbnail(
url: "https://i.ytimg.com/vi/mUOxprz6g1A/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AHUBoAC4AOKAgwIABABGGUgUihPMA8=&rs=AOn4CLBmuGzO6Ti0hVEC1gp0XUA_l6-HNA",
width: 1920,
height: 1080,
),
],
channel: Some(ChannelTag(
id: "UCAkeE1thnToEXZTao-CZkHw",
name: "FiveV",
avatar: [],
verification: None,
subscriber_count: Some(939),
)),
publish_date: "[date]",
publish_date_txt: Some("2 years ago"),
view_count: Some(125),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
],
visitor_data: Some("CgtfTVpMVElZSjBVOCj90uWhBg%3D%3D"),
)

View file

@ -764,6 +764,29 @@ pub struct ChannelRss {
pub create_date: OffsetDateTime, pub create_date: OffsetDateTime,
} }
/// YouTube channel TV feed
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct ChannelTv {
/// Unique YouTube Channel-ID (e.g. `UC-lHJZR3Gqxm24_Vd_AJ5Yw`)
pub id: String,
/// Channel name
pub name: String,
/// Channel subscriber count
///
/// [`None`] if the subscriber count was hidden by the owner
/// or could not be parsed.
pub subscriber_count: Option<u64>,
/// Channel avatar / profile picture
pub avatar: Vec<Thumbnail>,
/// Banner image shown above the channel (16:9 fullscreen format for TV)
pub tv_banner: Vec<Thumbnail>,
/// List of the latest channel videos
pub videos: Vec<VideoItem>,
/// YouTube visitor data cookie
pub visitor_data: Option<String>,
}
/// YouTube video fetched from a channel's RSS feed /// YouTube video fetched from a channel's RSS feed
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive] #[non_exhaustive]

View file

@ -848,13 +848,13 @@ impl Display for Country {
impl FromStr for Language { impl FromStr for Language {
type Err = serde_json::Error; type Err = serde_json::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
serde_json::from_str(&format!("\"{s}\"")) serde_json::from_str(&format!("\"{}\"", s))
} }
} }
impl FromStr for Country { impl FromStr for Country {
type Err = serde_json::Error; type Err = serde_json::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
serde_json::from_str(&format!("\"{s}\"")) serde_json::from_str(&format!("\"{}\"", s))
} }
} }

View file

@ -18,7 +18,7 @@
use std::ops::Mul; use std::ops::Mul;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use time::{Date, Duration, OffsetDateTime}; use time::{Date, Duration, OffsetDateTime, PrimitiveDateTime, Time};
use crate::{ use crate::{
param::Language, param::Language,
@ -76,9 +76,20 @@ pub(crate) struct TaToken {
} }
pub(crate) enum DateCmp { pub(crate) enum DateCmp {
// Year
Y, Y,
// Month
M, M,
// Day
D, D,
// Hour in 24hr format
Hr,
// Hour in 12hr format
Hr12,
// Minute
Mi,
// Year in 2-digit format
YShort,
} }
impl Mul<u8> for TimeAgo { impl Mul<u8> for TimeAgo {
@ -226,7 +237,7 @@ pub(crate) fn parse_timeago_dt_or_warn(
res res
} }
/// Parse a textual date (e.g. "29 minutes ago" or "Jul 2, 2014") into a ParsedDate object. /// Parse a textual date (e.g. "29 minutes ago" or "Jul 2, 2014") into a [`ParsedDate`] object.
/// ///
/// Returns None if the date could not be parsed. /// Returns None if the date could not be parsed.
pub fn parse_textual_date(lang: Language, textual_date: &str) -> Option<ParsedDate> { pub fn parse_textual_date(lang: Language, textual_date: &str) -> Option<ParsedDate> {
@ -254,6 +265,7 @@ pub fn parse_textual_date(lang: Language, textual_date: &str) -> Option<ParsedDa
DateCmp::Y => y = Some(*n), DateCmp::Y => y = Some(*n),
DateCmp::M => m = Some(*n), DateCmp::M => m = Some(*n),
DateCmp::D => d = Some(*n), DateCmp::D => d = Some(*n),
_ => {}
}); });
if m.is_none() { if m.is_none() {
@ -274,7 +286,68 @@ pub fn parse_textual_date(lang: Language, textual_date: &str) -> Option<ParsedDa
} }
} }
/// Parse a textual date (e.g. "29 minutes ago" or "Jul 2, 2014") into a Chrono DateTime object. /// Parse a textual datetime (e.g. "4/14/23, 3:00 PM") into a [`OffsetDateTime`]
/// (assuming UTC timezone)
pub fn parse_datetime(lang: Language, datetime_txt: &str) -> Option<OffsetDateTime> {
let entry = dictionary::entry(lang);
let (nums, last_i) = util::parse_numeric_vec_i::<u16>(datetime_txt);
if nums.len() == entry.datetime_order.len() {
let mut y: Option<u16> = None;
let mut m: Option<u16> = None;
let mut d: Option<u16> = None;
let mut hr: Option<u16> = None;
let mut mi: Option<u16> = None;
nums.iter()
.enumerate()
.for_each(|(i, n)| match entry.datetime_order[i] {
DateCmp::Y => y = Some(*n),
DateCmp::YShort => y = year_from_short(*n),
DateCmp::M => m = Some(*n),
DateCmp::D => d = Some(*n),
DateCmp::Hr => hr = Some(*n),
DateCmp::Hr12 => {
let pm = datetime_txt
.chars()
.skip(last_i + 1)
.any(|c| c == 'p' || c == 'P');
hr = Some(if pm {
if *n == 12 {
*n
} else {
*n + 12
}
} else if *n == 12 {
0
} else {
*n
});
}
DateCmp::Mi => mi = Some(*n),
});
match (y, m, d, hr, mi) {
(Some(y), Some(m), Some(d), Some(hr), Some(mi)) => Some(
PrimitiveDateTime::new(
Date::from_calendar_date(
y.into(),
util::month_from_n(m as u8)?,
d.try_into().ok()?,
)
.ok()?,
Time::from_hms(hr.try_into().ok()?, mi.try_into().ok()?, 0).ok()?,
)
.assume_utc(),
),
_ => None,
}
} else {
None
}
}
/// Parse a textual date (e.g. "29 minutes ago" or "Jul 2, 2014") into a [`OffsetDateTime`] object.
/// ///
/// Returns None if the date could not be parsed. /// Returns None if the date could not be parsed.
pub fn parse_textual_date_to_dt(lang: Language, textual_date: &str) -> Option<OffsetDateTime> { pub fn parse_textual_date_to_dt(lang: Language, textual_date: &str) -> Option<OffsetDateTime> {
@ -293,6 +366,33 @@ pub(crate) fn parse_textual_date_or_warn(
res res
} }
pub(crate) fn parse_datetime_or_warn(
lang: Language,
datetime_txt: &str,
warnings: &mut Vec<String>,
) -> Option<OffsetDateTime> {
let res = parse_datetime(lang, datetime_txt);
if res.is_none() {
warnings.push(format!("could not parse textual date `{datetime_txt}`"));
}
res
}
/// Convert year from a short 2 digit representation to the actual year
fn year_from_short(short_year: u16) -> Option<u16> {
if short_year > 99 {
return None;
}
let mut year: u16 = time::OffsetDateTime::now_utc().year().try_into().ok()?;
year = year - (year % 100);
if short_year > 49 && year >= 100 {
year -= 100;
}
Some(year + short_year)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::{collections::BTreeMap, fs::File, io::BufReader}; use std::{collections::BTreeMap, fs::File, io::BufReader};
@ -654,4 +754,23 @@ mod tests {
let now = OffsetDateTime::now_utc(); let now = OffsetDateTime::now_utc();
assert_eq!(date.year(), now.year() - 1); assert_eq!(date.year(), now.year() - 1);
} }
#[test]
fn t_parse_datetime_samples() {
let json_path = path!(*TESTFILES / "dict" / "datetime_samples.json");
let json_file = File::open(json_path).unwrap();
let datetime_samples: BTreeMap<Language, String> =
serde_json::from_reader(BufReader::new(json_file)).unwrap();
datetime_samples.iter().for_each(|(lang, txt)| {
let datetime =
parse_datetime(*lang, txt).unwrap_or_else(|| panic!("parse error, lang: {lang}"));
assert_eq!(datetime.year(), 2023);
assert_eq!(datetime.month(), time::Month::April);
assert_eq!(datetime.day(), 14);
assert_eq!(datetime.hour(), 15);
assert_eq!(datetime.minute(), 0);
assert_eq!(datetime.second(), 0);
});
}
} }

View file

@ -19,14 +19,20 @@ pub(crate) struct Entry {
/// Identifiers: `Y`(ear), `M`(month), `W`(eek), `D`(ay), /// Identifiers: `Y`(ear), `M`(month), `W`(eek), `D`(ay),
/// `h`(our), `m`(inute), `s`(econd) /// `h`(our), `m`(inute), `s`(econd)
pub timeago_tokens: phf::Map<&'static str, TaToken>, pub timeago_tokens: phf::Map<&'static str, TaToken>,
/// Order in which to parse numeric date components. Formatted as /// Order in which to parse numeric date components.
/// a string of date identifiers (Y, M, D).
/// ///
/// Examples: /// Examples:
/// ///
/// - 03.01.2020 => `"DMY"` /// - 03.01.2020 => `"DMY"`
/// - Jan 3, 2020 => `"DY"` /// - Jan 3, 2020 => `"DY"`
pub date_order: &'static [DateCmp], pub date_order: &'static [DateCmp],
/// Order in which to parse datetimes.
///
/// Examples:
///
/// - 2023-04-14 15:00 => `[Y,M,D,Hr,Mi]`
/// - 4/14/23, 3:00 PM => `[M,D,YShort,Hr12,Mi]`
pub datetime_order: &'static [DateCmp],
/// Tokens for parsing month names. /// Tokens for parsing month names.
/// ///
/// Format: Parsed token -> Month number (starting from 1) /// Format: Parsed token -> Month number (starting from 1)
@ -76,6 +82,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::Y, DateCmp::M, DateCmp::D, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -160,6 +167,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 15467950696543387533, key: 15467950696543387533,
disps: &[ disps: &[
@ -257,6 +265,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -320,6 +329,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -384,6 +394,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 8694567506910003252, key: 8694567506910003252,
disps: &[ disps: &[
@ -480,6 +491,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 15467950696543387533, key: 15467950696543387533,
disps: &[ disps: &[
@ -565,6 +577,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -627,6 +640,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 10121458955350035957, key: 10121458955350035957,
disps: &[ disps: &[
@ -719,6 +733,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 10121458955350035957, key: 10121458955350035957,
disps: &[ disps: &[
@ -804,6 +819,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 15467950696543387533, key: 15467950696543387533,
disps: &[ disps: &[
@ -890,6 +906,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -959,6 +976,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 7485420634051515786, key: 7485420634051515786,
disps: &[ disps: &[
@ -1043,6 +1061,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -1112,6 +1131,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 15467950696543387533, key: 15467950696543387533,
disps: &[ disps: &[
@ -1170,7 +1190,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
}, },
Language::En | Language::EnGb | Language::EnIn => Entry { Language::En => Entry {
by_char: false, by_char: false,
timeago_tokens: ::phf::Map { timeago_tokens: ::phf::Map {
key: 7485420634051515786, key: 7485420634051515786,
@ -1197,6 +1217,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::M, DateCmp::D, DateCmp::YShort, DateCmp::Hr12, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -1232,15 +1253,185 @@ pub(crate) fn entry(lang: Language) -> Entry {
}, },
comma_decimal: false, comma_decimal: false,
number_tokens: ::phf::Map { number_tokens: ::phf::Map {
key: 14108922650502679131, key: 12913932095322966823,
disps: &[
(0, 0),
],
entries: &[
("M", 6),
("B", 9),
],
},
album_types: ::phf::Map {
key: 8694567506910003252,
disps: &[
(3, 0),
],
entries: &[
("show", AlbumType::Show),
("ep", AlbumType::Ep),
("single", AlbumType::Single),
("album", AlbumType::Album),
("audiobook", AlbumType::Audiobook),
],
},
},
Language::EnGb => Entry {
by_char: false,
timeago_tokens: ::phf::Map {
key: 7485420634051515786,
disps: &[
(0, 7),
(8, 12),
(5, 0),
],
entries: &[
("months", TaToken { n: 1, unit: Some(TimeUnit::Month) }),
("month", TaToken { n: 1, unit: Some(TimeUnit::Month) }),
("hours", TaToken { n: 1, unit: Some(TimeUnit::Hour) }),
("days", TaToken { n: 1, unit: Some(TimeUnit::Day) }),
("years", TaToken { n: 1, unit: Some(TimeUnit::Year) }),
("week", TaToken { n: 1, unit: Some(TimeUnit::Week) }),
("second", TaToken { n: 1, unit: Some(TimeUnit::Second) }),
("day", TaToken { n: 1, unit: Some(TimeUnit::Day) }),
("minute", TaToken { n: 1, unit: Some(TimeUnit::Minute) }),
("seconds", TaToken { n: 1, unit: Some(TimeUnit::Second) }),
("weeks", TaToken { n: 1, unit: Some(TimeUnit::Week) }),
("hour", TaToken { n: 1, unit: Some(TimeUnit::Hour) }),
("minutes", TaToken { n: 1, unit: Some(TimeUnit::Minute) }),
("year", TaToken { n: 1, unit: Some(TimeUnit::Year) }),
],
},
date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map {
key: 12913932095322966823,
disps: &[
(8, 0),
(5, 8),
(4, 0),
],
entries: &[
("nov", 11),
("sept", 9),
("apr", 4),
("dec", 12),
("mar", 3),
("jun", 6),
("sep", 9),
("may", 5),
("jul", 7),
("jan", 1),
("oct", 10),
("feb", 2),
("aug", 8),
],
},
timeago_nd_tokens: ::phf::Map {
key: 12913932095322966823,
disps: &[
(0, 0),
],
entries: &[
("yesterday", TaToken { n: 1, unit: Some(TimeUnit::Day) }),
("today", TaToken { n: 0, unit: Some(TimeUnit::Day) }),
],
},
comma_decimal: false,
number_tokens: ::phf::Map {
key: 12913932095322966823,
disps: &[
(0, 0),
],
entries: &[
("M", 6),
("B", 9),
],
},
album_types: ::phf::Map {
key: 8694567506910003252,
disps: &[
(3, 0),
],
entries: &[
("show", AlbumType::Show),
("ep", AlbumType::Ep),
("single", AlbumType::Single),
("album", AlbumType::Album),
("audiobook", AlbumType::Audiobook),
],
},
},
Language::EnIn => Entry {
by_char: false,
timeago_tokens: ::phf::Map {
key: 7485420634051515786,
disps: &[
(0, 7),
(8, 12),
(5, 0),
],
entries: &[
("months", TaToken { n: 1, unit: Some(TimeUnit::Month) }),
("month", TaToken { n: 1, unit: Some(TimeUnit::Month) }),
("hours", TaToken { n: 1, unit: Some(TimeUnit::Hour) }),
("days", TaToken { n: 1, unit: Some(TimeUnit::Day) }),
("years", TaToken { n: 1, unit: Some(TimeUnit::Year) }),
("week", TaToken { n: 1, unit: Some(TimeUnit::Week) }),
("second", TaToken { n: 1, unit: Some(TimeUnit::Second) }),
("day", TaToken { n: 1, unit: Some(TimeUnit::Day) }),
("minute", TaToken { n: 1, unit: Some(TimeUnit::Minute) }),
("seconds", TaToken { n: 1, unit: Some(TimeUnit::Second) }),
("weeks", TaToken { n: 1, unit: Some(TimeUnit::Week) }),
("hour", TaToken { n: 1, unit: Some(TimeUnit::Hour) }),
("minutes", TaToken { n: 1, unit: Some(TimeUnit::Minute) }),
("year", TaToken { n: 1, unit: Some(TimeUnit::Year) }),
],
},
date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map {
key: 12913932095322966823,
disps: &[
(8, 0),
(5, 8),
(4, 0),
],
entries: &[
("nov", 11),
("sept", 9),
("apr", 4),
("dec", 12),
("mar", 3),
("jun", 6),
("sep", 9),
("may", 5),
("jul", 7),
("jan", 1),
("oct", 10),
("feb", 2),
("aug", 8),
],
},
timeago_nd_tokens: ::phf::Map {
key: 12913932095322966823,
disps: &[
(0, 0),
],
entries: &[
("yesterday", TaToken { n: 1, unit: Some(TimeUnit::Day) }),
("today", TaToken { n: 0, unit: Some(TimeUnit::Day) }),
],
},
comma_decimal: false,
number_tokens: ::phf::Map {
key: 12913932095322966823,
disps: &[ disps: &[
(1, 0), (1, 0),
], ],
entries: &[ entries: &[
("lakh", 5),
("crore", 7), ("crore", 7),
("M", 6), ("lakh", 5),
("B", 9),
], ],
}, },
album_types: ::phf::Map { album_types: ::phf::Map {
@ -1284,6 +1475,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 15467950696543387533, key: 15467950696543387533,
disps: &[ disps: &[
@ -1341,7 +1533,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
}, },
Language::EsUs | Language::Es419 => Entry { Language::Es419 => Entry {
by_char: false, by_char: false,
timeago_tokens: ::phf::Map { timeago_tokens: ::phf::Map {
key: 10121458955350035957, key: 10121458955350035957,
@ -1368,6 +1560,92 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map {
key: 15467950696543387533,
disps: &[
(1, 0),
(1, 6),
(0, 4),
],
entries: &[
("jul", 7),
("oct", 10),
("ene", 1),
("may", 5),
("sept", 9),
("feb", 2),
("nov", 11),
("dic", 12),
("abr", 4),
("jun", 6),
("ago", 8),
("mar", 3),
],
},
timeago_nd_tokens: ::phf::Map {
key: 12913932095322966823,
disps: &[
(0, 0),
],
entries: &[
("ayer", TaToken { n: 1, unit: Some(TimeUnit::Day) }),
("hoy", TaToken { n: 0, unit: Some(TimeUnit::Day) }),
],
},
comma_decimal: false,
number_tokens: ::phf::Map {
key: 12913932095322966823,
disps: &[
(1, 0),
],
entries: &[
("mil", 3),
("M", 6),
],
},
album_types: ::phf::Map {
key: 15467950696543387533,
disps: &[
(0, 0),
],
entries: &[
("ep", AlbumType::Ep),
("audiolibro", AlbumType::Audiobook),
("sencillo", AlbumType::Single),
("programa", AlbumType::Show),
("álbum", AlbumType::Album),
],
},
},
Language::EsUs => Entry {
by_char: false,
timeago_tokens: ::phf::Map {
key: 10121458955350035957,
disps: &[
(8, 9),
(2, 0),
(7, 5),
],
entries: &[
("segundos", TaToken { n: 1, unit: Some(TimeUnit::Second) }),
("meses", TaToken { n: 1, unit: Some(TimeUnit::Month) }),
("minutos", TaToken { n: 1, unit: Some(TimeUnit::Minute) }),
("semanas", TaToken { n: 1, unit: Some(TimeUnit::Week) }),
("segundo", TaToken { n: 1, unit: Some(TimeUnit::Second) }),
("días", TaToken { n: 1, unit: Some(TimeUnit::Day) }),
("mes", TaToken { n: 1, unit: Some(TimeUnit::Month) }),
("hora", TaToken { n: 1, unit: Some(TimeUnit::Hour) }),
("años", TaToken { n: 1, unit: Some(TimeUnit::Year) }),
("día", TaToken { n: 1, unit: Some(TimeUnit::Day) }),
("semana", TaToken { n: 1, unit: Some(TimeUnit::Week) }),
("horas", TaToken { n: 1, unit: Some(TimeUnit::Hour) }),
("año", TaToken { n: 1, unit: Some(TimeUnit::Year) }),
("minuto", TaToken { n: 1, unit: Some(TimeUnit::Minute) }),
],
},
date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr12, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 15467950696543387533, key: 15467950696543387533,
disps: &[ disps: &[
@ -1455,6 +1733,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 14108922650502679131, key: 14108922650502679131,
disps: &[ disps: &[
@ -1535,6 +1814,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::Y, DateCmp::D], date_order: &[DateCmp::Y, DateCmp::D],
datetime_order: &[DateCmp::YShort, DateCmp::M, DateCmp::D, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 10121458955350035957, key: 10121458955350035957,
disps: &[ disps: &[
@ -1610,6 +1890,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::Y, DateCmp::M, DateCmp::D, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -1695,6 +1976,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -1757,6 +2039,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::M, DateCmp::D, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -1814,7 +2097,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
}, },
Language::Fr | Language::FrCa => Entry { Language::Fr => Entry {
by_char: false, by_char: false,
timeago_tokens: ::phf::Map { timeago_tokens: ::phf::Map {
key: 10121458955350035957, key: 10121458955350035957,
@ -1840,6 +2123,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -1877,12 +2161,99 @@ pub(crate) fn entry(lang: Language) -> Entry {
number_tokens: ::phf::Map { number_tokens: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
(0, 0),
],
entries: &[
("M", 6),
("Md", 9),
],
},
album_types: ::phf::Map {
key: 12913932095322966823,
disps: &[
(0, 0),
(5, 0),
],
entries: &[
("microalbum", AlbumType::Ep),
("album", AlbumType::Album),
("livre audio", AlbumType::Audiobook),
("émission", AlbumType::Show),
("simple", AlbumType::Single),
("ep", AlbumType::Ep),
("single", AlbumType::Single),
],
},
},
Language::FrCa => Entry {
by_char: false,
timeago_tokens: ::phf::Map {
key: 10121458955350035957,
disps: &[
(12, 3),
(2, 11),
(2, 0), (2, 0),
], ],
entries: &[ entries: &[
("G", 9), ("minute", TaToken { n: 1, unit: Some(TimeUnit::Minute) }),
("semaine", TaToken { n: 1, unit: Some(TimeUnit::Week) }),
("jour", TaToken { n: 1, unit: Some(TimeUnit::Day) }),
("mois", TaToken { n: 1, unit: Some(TimeUnit::Month) }),
("semaines", TaToken { n: 1, unit: Some(TimeUnit::Week) }),
("jours", TaToken { n: 1, unit: Some(TimeUnit::Day) }),
("seconde", TaToken { n: 1, unit: Some(TimeUnit::Second) }),
("heures", TaToken { n: 1, unit: Some(TimeUnit::Hour) }),
("secondes", TaToken { n: 1, unit: Some(TimeUnit::Second) }),
("minutes", TaToken { n: 1, unit: Some(TimeUnit::Minute) }),
("an", TaToken { n: 1, unit: Some(TimeUnit::Year) }),
("ans", TaToken { n: 1, unit: Some(TimeUnit::Year) }),
("heure", TaToken { n: 1, unit: Some(TimeUnit::Hour) }),
],
},
date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::Y, DateCmp::M, DateCmp::D, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map {
key: 12913932095322966823,
disps: &[
(9, 0),
(7, 6),
(1, 5),
],
entries: &[
("août", 8),
("avr.", 4),
("juil.", 7),
("févr.", 2),
("oct.", 10),
("déc.", 12),
("janv.", 1),
("mars", 3),
("juin", 6),
("juill.", 7),
("nov.", 11),
("sept.", 9),
("mai", 5),
],
},
timeago_nd_tokens: ::phf::Map {
key: 12913932095322966823,
disps: &[
(0, 0),
],
entries: &[
("hier", TaToken { n: 1, unit: Some(TimeUnit::Day) }),
("aujourd'hui", TaToken { n: 0, unit: Some(TimeUnit::Day) }),
],
},
comma_decimal: true,
number_tokens: ::phf::Map {
key: 12913932095322966823,
disps: &[
(0, 0),
],
entries: &[
("M", 6), ("M", 6),
("Md", 9), ("G", 9),
], ],
}, },
album_types: ::phf::Map { album_types: ::phf::Map {
@ -1929,6 +2300,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -2004,6 +2376,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 15467950696543387533, key: 15467950696543387533,
disps: &[ disps: &[
@ -2082,6 +2455,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -2174,6 +2548,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 15467950696543387533, key: 15467950696543387533,
disps: &[ disps: &[
@ -2259,6 +2634,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::Y, DateCmp::D], date_order: &[DateCmp::Y, DateCmp::D],
datetime_order: &[DateCmp::Y, DateCmp::M, DateCmp::D, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 7485420634051515786, key: 7485420634051515786,
disps: &[ disps: &[
@ -2336,6 +2712,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 15467950696543387533, key: 15467950696543387533,
disps: &[ disps: &[
@ -2413,6 +2790,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 106375038446233661, key: 106375038446233661,
disps: &[ disps: &[
@ -2498,6 +2876,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 8694567506910003252, key: 8694567506910003252,
disps: &[ disps: &[
@ -2583,6 +2962,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 7485420634051515786, key: 7485420634051515786,
disps: &[ disps: &[
@ -2674,6 +3054,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 15467950696543387533, key: 15467950696543387533,
disps: &[ disps: &[
@ -2751,6 +3132,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::Y, DateCmp::M, DateCmp::D], date_order: &[DateCmp::Y, DateCmp::M, DateCmp::D],
datetime_order: &[DateCmp::Y, DateCmp::M, DateCmp::D, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -2812,6 +3194,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 15467950696543387533, key: 15467950696543387533,
disps: &[ disps: &[
@ -2889,6 +3272,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::Y, DateCmp::D], date_order: &[DateCmp::Y, DateCmp::D],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 10121458955350035957, key: 10121458955350035957,
disps: &[ disps: &[
@ -2967,6 +3351,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 7485420634051515786, key: 7485420634051515786,
disps: &[ disps: &[
@ -3052,6 +3437,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -3128,6 +3514,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::Y, DateCmp::M, DateCmp::D], date_order: &[DateCmp::Y, DateCmp::M, DateCmp::D],
datetime_order: &[DateCmp::YShort, DateCmp::M, DateCmp::D, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -3189,6 +3576,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::Y, DateCmp::D], date_order: &[DateCmp::Y, DateCmp::D],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 2980949210194914378, key: 2980949210194914378,
disps: &[ disps: &[
@ -3266,6 +3654,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 471159234146692604, key: 471159234146692604,
disps: &[ disps: &[
@ -3361,6 +3750,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::Y, DateCmp::M, DateCmp::D], date_order: &[DateCmp::Y, DateCmp::M, DateCmp::D],
datetime_order: &[DateCmp::Y, DateCmp::M, DateCmp::D, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -3431,6 +3821,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::Y, DateCmp::D], date_order: &[DateCmp::Y, DateCmp::D],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 10121458955350035957, key: 10121458955350035957,
disps: &[ disps: &[
@ -3516,6 +3907,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -3579,6 +3971,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::Y, DateCmp::D], date_order: &[DateCmp::Y, DateCmp::D],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -3655,6 +4048,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::Y, DateCmp::M, DateCmp::D], date_order: &[DateCmp::Y, DateCmp::M, DateCmp::D],
datetime_order: &[DateCmp::Y, DateCmp::M, DateCmp::D, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -3725,6 +4119,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 15467950696543387533, key: 15467950696543387533,
disps: &[ disps: &[
@ -3803,6 +4198,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -3880,6 +4276,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::Y, DateCmp::D], date_order: &[DateCmp::Y, DateCmp::D],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 7485420634051515786, key: 7485420634051515786,
disps: &[ disps: &[
@ -3961,6 +4358,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::Y, DateCmp::D], date_order: &[DateCmp::Y, DateCmp::D],
datetime_order: &[DateCmp::YShort, DateCmp::M, DateCmp::D, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 15467950696543387533, key: 15467950696543387533,
disps: &[ disps: &[
@ -4045,6 +4443,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -4129,6 +4528,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 15467950696543387533, key: 15467950696543387533,
disps: &[ disps: &[
@ -4205,6 +4605,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::M, DateCmp::D, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 7485420634051515786, key: 7485420634051515786,
disps: &[ disps: &[
@ -4285,6 +4686,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 8694567506910003252, key: 8694567506910003252,
disps: &[ disps: &[
@ -4377,6 +4779,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 15467950696543387533, key: 15467950696543387533,
disps: &[ disps: &[
@ -4462,6 +4865,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -4547,6 +4951,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -4617,6 +5022,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 7485420634051515786, key: 7485420634051515786,
disps: &[ disps: &[
@ -4708,6 +5114,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -4785,6 +5192,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::Y, DateCmp::D], date_order: &[DateCmp::Y, DateCmp::D],
datetime_order: &[DateCmp::Y, DateCmp::M, DateCmp::D, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -4871,6 +5279,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -4951,6 +5360,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 15467950696543387533, key: 15467950696543387533,
disps: &[ disps: &[
@ -5031,6 +5441,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 7485420634051515786, key: 7485420634051515786,
disps: &[ disps: &[
@ -5119,6 +5530,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -5193,6 +5605,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -5262,6 +5675,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::Y, DateCmp::M, DateCmp::D, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -5340,6 +5754,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 15467950696543387533, key: 15467950696543387533,
disps: &[ disps: &[
@ -5424,6 +5839,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 15467950696543387533, key: 15467950696543387533,
disps: &[ disps: &[
@ -5508,6 +5924,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -5585,6 +6002,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -5666,6 +6084,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 10121458955350035957, key: 10121458955350035957,
disps: &[ disps: &[
@ -5758,6 +6177,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 15467950696543387533, key: 15467950696543387533,
disps: &[ disps: &[
@ -5842,6 +6262,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 15467950696543387533, key: 15467950696543387533,
disps: &[ disps: &[
@ -5920,6 +6341,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -5998,6 +6420,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::M, DateCmp::Y],
datetime_order: &[DateCmp::Hr, DateCmp::Mi, DateCmp::D, DateCmp::M, DateCmp::Y],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -6060,6 +6483,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::Y, DateCmp::M, DateCmp::D], date_order: &[DateCmp::Y, DateCmp::M, DateCmp::D],
datetime_order: &[DateCmp::Y, DateCmp::M, DateCmp::D, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -6122,6 +6546,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::Y, DateCmp::M, DateCmp::D], date_order: &[DateCmp::Y, DateCmp::M, DateCmp::D],
datetime_order: &[DateCmp::D, DateCmp::M, DateCmp::Y, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -6183,6 +6608,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::Y, DateCmp::M, DateCmp::D], date_order: &[DateCmp::Y, DateCmp::M, DateCmp::D],
datetime_order: &[DateCmp::Y, DateCmp::M, DateCmp::D, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 12913932095322966823, key: 12913932095322966823,
disps: &[ disps: &[
@ -6252,6 +6678,7 @@ pub(crate) fn entry(lang: Language) -> Entry {
], ],
}, },
date_order: &[DateCmp::D, DateCmp::Y], date_order: &[DateCmp::D, DateCmp::Y],
datetime_order: &[DateCmp::M, DateCmp::D, DateCmp::YShort, DateCmp::Hr, DateCmp::Mi],
months: ::phf::Map { months: ::phf::Map {
key: 7485420634051515786, key: 7485420634051515786,
disps: &[ disps: &[

View file

@ -116,16 +116,27 @@ where
buf.parse() buf.parse()
} }
/// Parse all numbers occurring in a string and reurn them as a vec /// Parse all numbers occurring in a string and return them as a vec
pub fn parse_numeric_vec<F>(string: &str) -> Vec<F> pub fn parse_numeric_vec<F>(string: &str) -> Vec<F>
where
F: FromStr,
{
parse_numeric_vec_i(string).0
}
/// Parse all numbers occurring in a string and return them as a vec
/// together with the index of the last digit
pub fn parse_numeric_vec_i<F>(string: &str) -> (Vec<F>, usize)
where where
F: FromStr, F: FromStr,
{ {
let mut numbers = vec![]; let mut numbers = vec![];
let mut last_i = 0;
let mut buf = String::new(); let mut buf = String::new();
for c in string.chars() { for (i, c) in string.chars().enumerate() {
if c.is_ascii_digit() { if c.is_ascii_digit() {
last_i = i;
buf.push(c); buf.push(c);
} else if !buf.is_empty() { } else if !buf.is_empty() {
buf.parse::<F>().map_or((), |n| numbers.push(n)); buf.parse::<F>().map_or((), |n| numbers.push(n));
@ -136,7 +147,7 @@ where
buf.parse::<F>().map_or((), |n| numbers.push(n)); buf.parse::<F>().map_or((), |n| numbers.push(n));
} }
numbers (numbers, last_i)
} }
/// Parse textual video length (e.g. `0:49`, `2:02` or `1:48:18`) /// Parse textual video length (e.g. `0:49`, `2:02` or `1:48:18`)

19821
testfiles/channel_tv/base.json Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,556 @@
{
"contents": {
"tvBrowseRenderer": {
"content": {
"tvSurfaceContentRenderer": {
"content": {
"sectionListRenderer": {
"contents": [
{
"shelfRenderer": {
"content": {
"horizontalListRenderer": {
"continuations": [
{
"nextContinuationData": {
"clickTrackingParams": "CAkQybcCIhMI7fKJz9Cp_gIVdvQRCB0mpAoR",
"continuation": "4qmFsgKWARIYVUNBa2VFMXRoblRvRVhaVGFvLUNaa0h3GnprZ0VjQ0dZYUdGVkRRV3RsUlRGMGFHNVViMFZZV2xSaGJ5MURXbXRJZDVnQkFlb0RORU5uWkVaYU1EVkZWMnBDUmtsb2QwbGFhRzlaVmxWT1FtRXlWa1pOV0ZKdllteFNkbEpXYUdGV1IwWjJURlZPWVdFd2FETSUzRA%3D%3D"
}
}
],
"items": [
{
"tileRenderer": {
"contentId": "mUOxprz6g1A",
"contentType": "TILE_CONTENT_TYPE_VIDEO",
"header": {
"tileHeaderRenderer": {
"thumbnail": {
"thumbnails": [
{
"height": 90,
"url": "https://i.ytimg.com/vi/mUOxprz6g1A/default.jpg?sqp=-oaymwEkCHgQWvKriqkDGvABAfgB1AaAAuADigIMCAAQARhlIFIoTzAP&rs=AOn4CLDzWpAQemNy4coi5mGX4Zidtz8D6g",
"width": 120
},
{
"height": 180,
"url": "https://i.ytimg.com/vi/mUOxprz6g1A/mqdefault.jpg?sqp=-oaymwEmCMACELQB8quKqQMa8AEB-AHUBoAC4AOKAgwIABABGGUgUihPMA8=&rs=AOn4CLApsX2G48JM12sRKBtz4S4C99LNIg",
"width": 320
},
{
"height": 250,
"url": "https://i.ytimg.com/vi/mUOxprz6g1A/hqdefault.jpg?sqp=-oaymwExCLwDEPoBSFryq4qpAyMIARUAAIhCGAHwAQH4AdQGgALgA4oCDAgAEAEYZSBSKE8wDw==&rs=AOn4CLDi1avXHbcTse7rvzBelOhmZG7_-g",
"width": 444
},
{
"height": 360,
"url": "https://i.ytimg.com/vi/mUOxprz6g1A/hqdefault.jpg?sqp=-oaymwEmCOADEOgC8quKqQMa8AEB-AHUBoAC4AOKAgwIABABGGUgUihPMA8=&rs=AOn4CLAeN1JLavadZyIRnBVbsY3q71WmNQ",
"width": 480
},
{
"height": 480,
"url": "https://i.ytimg.com/vi/mUOxprz6g1A/sddefault.jpg?sqp=-oaymwEmCIAFEOAD8quKqQMa8AEB-AHUBoAC4AOKAgwIABABGGUgUihPMA8=&rs=AOn4CLDy0YOfnfHhou6_EWPlMMt-k0rMeQ",
"width": 640
},
{
"height": 1080,
"url": "https://i.ytimg.com/vi/mUOxprz6g1A/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AHUBoAC4AOKAgwIABABGGUgUihPMA8=&rs=AOn4CLBmuGzO6Ti0hVEC1gp0XUA_l6-HNA",
"width": 1920
}
]
},
"thumbnailOverlays": [
{
"thumbnailOverlayTimeStatusRenderer": {
"style": "DEFAULT",
"text": {
"accessibility": {
"accessibilityData": {
"label": "2 minutes, 1 second"
}
},
"simpleText": "2:01"
}
}
}
]
}
},
"metadata": {
"tileMetadataRenderer": {
"lines": [
{
"lineRenderer": {
"items": [
{
"lineItemRenderer": {
"text": {
"runs": [
{
"text": "FiveV"
}
]
}
}
}
]
}
},
{
"lineRenderer": {
"items": [
{
"lineItemRenderer": {
"text": {
"accessibility": {
"accessibilityData": {
"label": "125 views"
}
},
"simpleText": "125 views"
}
}
},
{
"lineItemRenderer": {
"text": {
"simpleText": "•"
}
}
},
{
"lineItemRenderer": {
"text": {
"simpleText": "2 years ago"
}
}
}
]
}
}
],
"title": {
"simpleText": "👑GoodLifeRP👑 Vorstellungs Video"
}
}
},
"onSelectCommand": {
"clickTrackingParams": "CAoQlDUYACITCO3yic_Qqf4CFXb0EQgdJqQKETIKZy1oaWdoLWNydloYVUNBa2VFMXRoblRvRVhaVGFvLUNaa0h3mgEFEPI4GGaqARhVVUFrZUUxdGhuVG9FWFpUYW8tQ1prSHc=",
"watchEndpoint": {
"loggingContext": {
"vssLoggingContext": {
"serializedContextData": "GhhVVUFrZUUxdGhuVG9FWFpUYW8tQ1prSHc%3D"
}
},
"params": "wAEB",
"playlistId": "UUAkeE1thnToEXZTao-CZkHw",
"videoId": "mUOxprz6g1A",
"watchEndpointSupportedOnesieConfig": {
"html5PlaybackOnesieConfig": {
"commonConfig": {
"url": "https://rr2---sn-h0jeln7l.googlevideo.com/initplayback?source=youtube&oeis=1&c=TVHTML5&oad=3200&ovd=3200&oaad=3200&oavd=3200&ocs=700&oewis=1&oputc=1&ofpcc=1&msp=1&odepv=1&id=9943b1a6bcfa8350&ip=2003%3Ade%3Aaf16%3A1900%3Af521%3A1529%3A4c2d%3Ad13&initcwndbps=1578750&mt=1681483502"
}
}
}
}
},
"style": "TILE_STYLE_YTLR_DEFAULT",
"trackingParams": "CAoQlDUYACITCO3yic_Qqf4CFXb0EQgdJqQKEUDQhurn67TsoZkBqgEYVVVBa2VFMXRoblRvRVhaVGFvLUNaa0h3"
}
}
],
"trackingParams": "CAgQxjkiEwjt8onP0Kn-AhV29BEIHSakChE=",
"visibleItemCount": 4
}
},
"endpoint": {
"browseEndpoint": {
"browseId": "UCAkeE1thnToEXZTao-CZkHw",
"canonicalBaseUrl": "/@lftvanshii",
"params": "EgZ2aWRlb3MYAyAAcADyBgsKCToCCAGiAQIIAQ%3D%3D"
},
"clickTrackingParams": "CAcQ3BwYACITCO3yic_Qqf4CFXb0EQgdJqQKEQ=="
},
"headerRenderer": {
"shelfHeaderRenderer": {
"title": {
"runs": [
{
"navigationEndpoint": {
"browseEndpoint": {
"browseId": "UCAkeE1thnToEXZTao-CZkHw",
"canonicalBaseUrl": "/@lftvanshii",
"params": "EgZ2aWRlb3MYAyAAcADyBgsKCToCCAGiAQIIAQ%3D%3D"
},
"clickTrackingParams": "CAcQ3BwYACITCO3yic_Qqf4CFXb0EQgdJqQKEQ=="
},
"text": "Uploads"
}
]
}
}
},
"trackingParams": "CAcQ3BwYACITCO3yic_Qqf4CFXb0EQgdJqQKEQ=="
}
},
{
"shelfRenderer": {
"content": {
"horizontalListRenderer": {
"items": [
{
"tileRenderer": {
"contentId": "PLKXzgqjDJuvBrmhX5t4TRcqkQImpywAuO",
"contentType": "TILE_CONTENT_TYPE_PLAYLIST",
"header": {
"tileHeaderRenderer": {
"thumbnail": {
"thumbnails": [
{
"height": 90,
"url": "https://i.ytimg.com/vi/sHL8wXUbfhg/default.jpg",
"width": 120
},
{
"height": 180,
"url": "https://i.ytimg.com/vi/sHL8wXUbfhg/mqdefault.jpg",
"width": 320
},
{
"height": 250,
"url": "https://i.ytimg.com/vi/sHL8wXUbfhg/hqdefault.jpg?sqp=-oaymwEXCLwDEPoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLAvwxqx0LQpi-NKVtVSH9Ty4CoLSw",
"width": 444
},
{
"height": 360,
"url": "https://i.ytimg.com/vi/sHL8wXUbfhg/hqdefault.jpg",
"width": 480
}
]
},
"thumbnailOverlays": [
{
"thumbnailOverlayTimeStatusRenderer": {
"icon": {
"iconType": "PLAYLISTS"
},
"style": "DEFAULT",
"text": {
"runs": [
{
"bold": true,
"text": "7"
},
{
"text": " videos"
}
]
}
}
}
]
}
},
"metadata": {
"tileMetadataRenderer": {
"lines": [
{
"lineRenderer": {
"items": [
{
"lineItemRenderer": {
"text": {
"runs": [
{
"navigationEndpoint": {
"browseEndpoint": {
"browseId": "UCAkeE1thnToEXZTao-CZkHw"
},
"clickTrackingParams": "CAYQljUYACITCO3yic_Qqf4CFXb0EQgdJqQKETIGZy1oaWdo"
},
"text": "FiveV"
}
]
}
}
}
],
"wrap": true
}
}
],
"title": {
"simpleText": "test"
}
}
},
"onFocusCommand": {
"showHintCommand": {
"shouldShowHint": true
}
},
"onSelectCommand": {
"browseEndpoint": {
"browseId": "VLPLKXzgqjDJuvBrmhX5t4TRcqkQImpywAuO"
},
"clickTrackingParams": "CAYQljUYACITCO3yic_Qqf4CFXb0EQgdJqQKETIGZy1oaWdo"
},
"style": "TILE_STYLE_YTLR_DEFAULT",
"trackingParams": "CAYQljUYACITCO3yic_Qqf4CFXb0EQgdJqQKEQ=="
}
}
],
"trackingParams": "CAUQxjkiEwjt8onP0Kn-AhV29BEIHSakChE=",
"visibleItemCount": 4
}
},
"endpoint": {
"browseEndpoint": {
"browseId": "UCAkeE1thnToEXZTao-CZkHw",
"canonicalBaseUrl": "/@lftvanshii",
"params": "EglwbGF5bGlzdHMYAyABcADyBgkKB0IAogECCAE%3D"
},
"clickTrackingParams": "CAQQ3BwYASITCO3yic_Qqf4CFXb0EQgdJqQKEQ=="
},
"headerRenderer": {
"shelfHeaderRenderer": {
"title": {
"runs": [
{
"navigationEndpoint": {
"browseEndpoint": {
"browseId": "UCAkeE1thnToEXZTao-CZkHw",
"canonicalBaseUrl": "/@lftvanshii",
"params": "EglwbGF5bGlzdHMYAyABcADyBgkKB0IAogECCAE%3D"
},
"clickTrackingParams": "CAQQ3BwYASITCO3yic_Qqf4CFXb0EQgdJqQKEQ=="
},
"text": "Created playlists"
}
]
}
}
},
"trackingParams": "CAQQ3BwYASITCO3yic_Qqf4CFXb0EQgdJqQKEQ=="
}
}
],
"trackingParams": "CAMQui8iEwjt8onP0Kn-AhV29BEIHSakChE="
}
},
"header": {
"tvSurfaceHeaderRenderer": {
"buttons": [
{
"subscribeButtonRenderer": {
"buttonText": {
"runs": [
{
"text": "Subscribe"
}
]
},
"channelId": "UCAkeE1thnToEXZTao-CZkHw",
"enabled": true,
"longSubscriberCountText": {
"accessibility": {
"accessibilityData": {
"label": "939 subscribers"
}
},
"simpleText": "939 subscribers"
},
"serviceEndpoints": [
{
"authDeterminedCommand": {
"authenticatedCommand": {
"clickTrackingParams": "CAIQmysYACITCO3yic_Qqf4CFXb0EQgdJqQKEQ==",
"subscribeEndpoint": {
"channelIds": [
"UCAkeE1thnToEXZTao-CZkHw"
],
"params": "EgIIAhgA"
}
},
"unauthenticatedCommand": {
"authRequiredCommand": {
"identityActionContext": {
"eventTrigger": "ACCOUNT_EVENT_TRIGGER_SUBSCRIBE",
"nextEndpoint": {
"clickTrackingParams": "CAIQmysYACITCO3yic_Qqf4CFXb0EQgdJqQKEQ==",
"commandExecutorCommand": {
"commands": [
{
"clickTrackingParams": "CAIQmysYACITCO3yic_Qqf4CFXb0EQgdJqQKEQ==",
"signalAction": {
"signal": "CLOSE_POPUP"
}
},
{
"clickTrackingParams": "CAIQmysYACITCO3yic_Qqf4CFXb0EQgdJqQKEQ==",
"subscribeEndpoint": {
"channelIds": [
"UCAkeE1thnToEXZTao-CZkHw"
],
"params": "EgIIAhgA"
}
}
]
}
}
}
},
"clickTrackingParams": "CAIQmysYACITCO3yic_Qqf4CFXb0EQgdJqQKEQ=="
}
},
"clickTrackingParams": "CAIQmysYACITCO3yic_Qqf4CFXb0EQgdJqQKEQ=="
},
{
"clickTrackingParams": "CAIQmysYACITCO3yic_Qqf4CFXb0EQgdJqQKEQ==",
"unsubscribeEndpoint": {
"channelIds": [
"UCAkeE1thnToEXZTao-CZkHw"
],
"params": "CgIIAhgA"
}
}
],
"shortSubscriberCountText": {
"simpleText": "939"
},
"showPreferences": false,
"subscribed": false,
"subscribedButtonText": {
"runs": [
{
"text": "Subscribed"
}
]
},
"subscribedEntityKey": "EhhVQ0FrZUUxdGhuVG9FWFpUYW8tQ1prSHcgMygB",
"subscriberCountText": {
"simpleText": "939"
},
"subscriberCountWithSubscribeText": {
"simpleText": "940"
},
"trackingParams": "CAIQmysYACITCO3yic_Qqf4CFXb0EQgdJqQKEQ==",
"type": "FREE",
"unsubscribeButtonText": {
"runs": [
{
"text": "Unsubscribe"
}
]
},
"unsubscribedButtonText": {
"runs": [
{
"text": "Subscribe"
}
]
}
}
}
],
"style": "TV_SURFACE_CONTENT_HEADER_STYLE_BANNER",
"thumbnail": {
"thumbnails": [
{
"height": 72,
"url": "https://yt3.googleusercontent.com/ytc/AGIKgqPN812F8UPxmHD4vUBaoqrjAqzUIWKs7XENz3FO6g=s72-c-k-c0x00ffffff-no-rj",
"width": 72
}
]
},
"title": {
"simpleText": "FiveV"
}
}
},
"trackingParams": "CAEQ0IwDIhMI7fKJz9Cp_gIVdvQRCB0mpAoR"
}
}
}
},
"frameworkUpdates": {
"entityBatchUpdate": {
"mutations": [
{
"entityKey": "EhhVQ0FrZUUxdGhuVG9FWFpUYW8tQ1prSHcgMygB",
"payload": {
"subscriptionStateEntity": {
"key": "EhhVQ0FrZUUxdGhuVG9FWFpUYW8tQ1prSHcgMygB",
"subscribed": false
}
},
"type": "ENTITY_MUTATION_TYPE_REPLACE"
}
],
"timestamp": {
"nanos": 347759864,
"seconds": "1681484157"
}
}
},
"responseContext": {
"serviceTrackingParams": [
{
"params": [
{
"key": "route",
"value": "channel.featured"
},
{
"key": "is_casual",
"value": "false"
},
{
"key": "is_owner",
"value": "false"
},
{
"key": "is_monetization_enabled",
"value": "false"
},
{
"key": "num_shelves",
"value": "0"
},
{
"key": "is_alc_surface",
"value": "false"
},
{
"key": "browse_id",
"value": "UCAkeE1thnToEXZTao-CZkHw"
},
{
"key": "browse_id_prefix",
"value": ""
},
{
"key": "logged_in",
"value": "0"
},
{
"key": "e",
"value": "9405988,23804281,23858057,23918597,23943651,23946420,23966208,23983296,23998056,24004644,24007246,24034168,24036948,24077241,24080738,24120819,24135310,24140247,24166867,24169501,24173287,24181174,24186126,24187043,24187377,24201855,24209350,24211178,24219382,24219713,24228638,24236210,24241378,24248955,24255165,24255543,24255545,24255547,24255549,24262346,24268142,24269412,24269413,24288047,24288664,24289856,24290971,24390675,24391539,24404640,24405914,24407191,24407198,24409417,24415864,24415866,24416290,24425061,24429093,24439361,24439483,24440132,24440406,24441788,24445832,24447671,24450367,24451319,24453129,24455284,24455285,24455286,24456681,24458839,24465011,24465095,24466371,24466462,24466833,24468691,24468724,24469651,24471752,24474986,24476489,24477227,24482081,24483503,24484441,24484443,24485239,24485410,24485421,24486580,24488188,24490510,24492688,24492804,24494081,24494981,24494986,24495060,24495839,24495960,24495965,24495988,24497807,24499289,24499304,24499566,24499577,24499792,24510563,24515366,24516017,24516156,24518452,24519079,24519102,24519143,24530042,24530194,24530296,24530948,24531268,24534337,24534339,24536757,24537200,24537812,24539626,24539641,24539646,24539666,24539775,24550520,39323074,39323341,39323343,39323456,39323458,39323495,39323497,39323565,39323567,39323575,39323577,39323582,39323584,39323668,39323670,39323672,39323674,39323679,39323681"
}
],
"service": "GFEEDBACK"
},
{
"params": [
{
"key": "sugexp",
"value": "yttlrso_e,ytposo.bo.me=1,ytpsoso.bo.me=1,ytposo.bo.tso.e=1,ytpsoso.bo.ft=1,ytposo.dzp=1,tre.en=0,cfro=1"
}
],
"service": "SUGGEST"
}
],
"visitorData": "CgtfTVpMVElZSjBVOCj90uWhBg%3D%3D"
},
"trackingParams": "CAAQhGciEwjt8onP0Kn-AhV29BEIHSakChE="
}

View file

@ -0,0 +1,85 @@
{
"af": "Geskeduleer vir 2023-04-14 15:00",
"am": "ለ14/04/2023 15:00 የተዘጋጀ",
"ar": "سيتم بثّه مباشرةً في 14/4/2023، 15:00",
"as": "14-4-2023, 15.00ৰ বাবে সময়সূচী নিৰ্ধাৰণ কৰা হৈছে",
"az": "14.04.23, 15:00 üçün planlaşdırılıb",
"be": "Запланавана на 14.04.23, 15:00",
"bg": "Насрочено за 14.04.23г., 15:00 ч.",
"bn": "14/4/23, 15:00 তারিখে লাইভ হবে",
"bs": "Zakazano za 14. 4. 2023. 15:00",
"ca": "S'ha programat per al dia 14/4/23 15:00",
"cs": "Naplánováno na 14.04.23 15:00",
"da": "Planlagt til 14.04.2023 15.00",
"de": "Geplant für: 14.04.23, 15:00",
"el": "Προγραμματίστηκε για τις 14/4/23, 15:00",
"en": "Scheduled for 4/14/23, 3:00PM",
"en-GB": "Scheduled for 14/04/2023, 15:00",
"en-IN": "Scheduled for 14/04/23, 15:00",
"es": "Programado para el 14/4/23, 15:00",
"es-419": "Programado para el 14/4/23, 15:00",
"es-US": "Programado para el 14/4/2023, 3:00p. m.",
"et": "Ajastatud avaldamiseks 14.04.23, 15:00",
"eu": "Igorpenaren data: 23/4/14, 15:00",
"fa": "زمان‌بندی‌شده برای 2023/4/14, 15:00",
"fi": "Esitetään 14.4.2023 15.00",
"fil": "Naka-iskedyul para sa 4/14/23, 15:00",
"fr": "Planifié pour le 14/04/2023 15:00",
"fr-CA": "Planifié le 2023-04-14 15 h 00",
"gl": "Programado para o 14/04/23, 15:00",
"gu": "14/4/23 15:00 માટે શેડ્યૂલ કર્યો",
"hi": "14/4/23, 15:00 के लिए शेड्यूल किया गया है",
"hr": "Zakazano za 14. 04. 2023. 15:00",
"hu": "Ekkorra ütemezve: 2023. 04. 14. 15:00",
"hy": "Ցուցադրվելու է՝ 14.04.23, 15:00",
"id": "Tayang 14/04/23, 15.00",
"is": "Á dagskrá 14.4.2023, 15:00",
"it": "Programmato per il giorno 14/04/23, 15:00",
"iw": "מתוזמן לתאריך 14.4.2023, 15:00",
"ja": "2023/04/14 15:00 に公開予定",
"ka": "დაგეგმილია 14.04.23, 15:00-ზე",
"kk": "14.04.23, 15:00 күні болады",
"km": "បាន​កំណត់​ពេល​សម្រាប់ 14/4/23, 15:00",
"kn": "14/4/23, 15:00 ಕ್ಕೆ ನಿಗದಿಪಡಿಸಲಾಗಿದೆ",
"ko": "예정일: 23. 4. 14. 15:00",
"ky": "Качан башталат: 14/4/23 15:00",
"lo": "ກຳນົດເວລາໄວ້ສຳລັບ 14/4/2023, 15:00 ແລ້ວ",
"lt": "Suplanuota rodyti 2023-04-14 15:00",
"lv": "Plānotā tiešraide: 14.04.23 15:00",
"mk": "Закажано за 14.4.23, во 15:00",
"ml": "14/4/23 15:00-ന് ഷെഡ്യൂൾ ചെയ്‌തു",
"mn": "2023.04.14 15:00-д товлосон",
"mr": "14/4/23, 15:00 साठी शेड्यूल केलेले",
"ms": "Dijadualkan pada 14/04/23, 15:00",
"my": "14/4/23 15:00 အတွက် စီစဉ်ထားသည်",
"ne": "23/4/14, 15:00 मा लाइभ हुने भनी समयतालिका तय गरिएको छ।",
"nl": "Gepland voor 14-04-2023 15:00",
"no": "Planlagt publisert 14.04.2023, 15:00",
"or": "4/14/23, 15:00 ପାଇଁ ସମୟ ଧାର୍ଯ୍ୟ କରାଯାଇଛି",
"pa": "14/4/23, 15:00 ਲਈ ਨਿਯਤ ਕੀਤਾ ਗਿਆ",
"pl": "Zaplanowany na 14.04.2023, 15:00",
"pt": "Programado para 14/04/2023, 15:00",
"pt-PT": "Agendado para 14/04/23, 15:00",
"ro": "Programat pentru 14.04.2023, 15:00",
"ru": "Планируемая дата публикации: 14.04.2023, 15:00",
"si": "2023-04-14, 15.00 සඳහා සැලසුම් කෙරිණි",
"sk": "Naplánované na 14. 4. 2023 15:00",
"sl": "Načrtovano za 14. 04. 23, 15:00",
"sq": "Planifikuar për 14.4.23, 15:00",
"sr": "Заказано за 14.4.23. 15:00",
"sr-Latn": "Zakazano za 14.4.23. 15:00",
"sv": "Har schemalagts till 2023-04-14 15:00",
"sw": "Imeratibiwa kuanza 14/04/2023, 15:00",
"ta": "வெளியிடத் திட்டமிடப்பட்டுள்ளது: 14/4/23, 15:00",
"te": "14-04-23 15:00 తేదీకి షెడ్యూల్ చేయబడింది",
"th": "กำหนดเผยแพร่ 14/4/23 15:00",
"tr": "14.04.2023 15:00 tarihi için planlandı",
"uk": "Заплановано на 14.04.23, 15:00",
"ur": "14/4/23، 15:00 کیلئے شیڈول کردہ",
"uz": "Rejalashtirilgan: 14/04/23, 15:00",
"vi": "Đã lên lịch xuất bản vào 15:00 14/04/2023",
"zh-CN": "预定发布时间2023/4/14 15:00",
"zh-HK": "預定發佈時間14/4/2023 15:00",
"zh-TW": "預定發布時間2023/4/14 下午15:00",
"zu": "Kuhlelelwe umhlaka-4/14/23 15:00"
}

View file

@ -18,6 +18,7 @@
"weke": "W" "weke": "W"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "YMDHm",
"months": { "months": {
"apr.": 4, "apr.": 4,
"aug.": 8, "aug.": 8,
@ -69,6 +70,7 @@
"ደቂቃዎች": "m" "ደቂቃዎች": "m"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMYHm",
"months": { "months": {
"ማርች": 3, "ማርች": 3,
"ሜይ": 5, "ሜይ": 5,
@ -131,6 +133,7 @@
"يومًا": "D" "يومًا": "D"
}, },
"date_order": "DMY", "date_order": "DMY",
"datetime_order": "DMYHm",
"months": {}, "months": {},
"timeago_nd_tokens": { "timeago_nd_tokens": {
"اليوم": "0D", "اليوم": "0D",
@ -164,6 +167,7 @@
"সপ্তাহ": "W" "সপ্তাহ": "W"
}, },
"date_order": "DMY", "date_order": "DMY",
"datetime_order": "DMYHm",
"months": {}, "months": {},
"timeago_nd_tokens": { "timeago_nd_tokens": {
"আজি": "0D", "আজি": "0D",
@ -198,6 +202,7 @@
"saniyə": "s" "saniyə": "s"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"apr": 4, "apr": 4,
"avq": 8, "avq": 8,
@ -259,6 +264,7 @@
"хвіліны": "m" "хвіліны": "m"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"вер": 9, "вер": 9,
"жні": 8, "жні": 8,
@ -311,6 +317,7 @@
"часа": "h" "часа": "h"
}, },
"date_order": "DMY", "date_order": "DMY",
"datetime_order": "DMyHm",
"months": {}, "months": {},
"timeago_nd_tokens": { "timeago_nd_tokens": {
"вчера": "1D", "вчера": "1D",
@ -343,6 +350,7 @@
"সেকেন্ড": "s" "সেকেন্ড": "s"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"অক্টোবর,": 10, "অক্টোবর,": 10,
"আগস্ট,": 8, "আগস্ট,": 8,
@ -401,6 +409,7 @@
"sekundu": "s" "sekundu": "s"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMYHm",
"months": { "months": {
"apr": 4, "apr": 4,
"aug": 8, "aug": 8,
@ -453,6 +462,7 @@
"setmanes": "W" "setmanes": "W"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"des.": 12, "des.": 12,
"dabr.": 4, "dabr.": 4,
@ -506,6 +516,7 @@
"týdny": "W" "týdny": "W"
}, },
"date_order": "DMY", "date_order": "DMY",
"datetime_order": "DMyHm",
"months": {}, "months": {},
"timeago_nd_tokens": { "timeago_nd_tokens": {
"dnes": "0D", "dnes": "0D",
@ -544,6 +555,7 @@
"år": "Y" "år": "Y"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMYHm",
"months": { "months": {
"apr.": 4, "apr.": 4,
"aug.": 8, "aug.": 8,
@ -595,6 +607,7 @@
"wochen": "W" "wochen": "W"
}, },
"date_order": "DMY", "date_order": "DMY",
"datetime_order": "DMyHm",
"months": {}, "months": {},
"timeago_nd_tokens": { "timeago_nd_tokens": {
"gestern": "1D", "gestern": "1D",
@ -633,6 +646,7 @@
"ώρες": "h" "ώρες": "h"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"απρ": 4, "απρ": 4,
"αυγ": 8, "αυγ": 8,
@ -666,10 +680,7 @@
} }
}, },
"en": { "en": {
"equivalent": [ "equivalent": [],
"en-GB",
"en-IN"
],
"by_char": false, "by_char": false,
"timeago_tokens": { "timeago_tokens": {
"day": "D", "day": "D",
@ -688,6 +699,7 @@
"years": "Y" "years": "Y"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "MDyhm",
"months": { "months": {
"apr": 4, "apr": 4,
"aug": 8, "aug": 8,
@ -710,7 +722,111 @@
"comma_decimal": false, "comma_decimal": false,
"number_tokens": { "number_tokens": {
"B": 9, "B": 9,
"M": 6, "M": 6
},
"album_types": {
"album": "Album",
"audiobook": "Audiobook",
"ep": "Ep",
"show": "Show",
"single": "Single"
}
},
"en-GB": {
"equivalent": [],
"by_char": false,
"timeago_tokens": {
"day": "D",
"days": "D",
"hour": "h",
"hours": "h",
"minute": "m",
"minutes": "m",
"month": "M",
"months": "M",
"second": "s",
"seconds": "s",
"week": "W",
"weeks": "W",
"year": "Y",
"years": "Y"
},
"date_order": "DY",
"datetime_order": "DMYHm",
"months": {
"apr": 4,
"aug": 8,
"dec": 12,
"feb": 2,
"jan": 1,
"jul": 7,
"jun": 6,
"mar": 3,
"may": 5,
"nov": 11,
"oct": 10,
"sep": 9,
"sept": 9
},
"timeago_nd_tokens": {
"today": "0D",
"yesterday": "1D"
},
"comma_decimal": false,
"number_tokens": {
"B": 9,
"M": 6
},
"album_types": {
"album": "Album",
"audiobook": "Audiobook",
"ep": "Ep",
"show": "Show",
"single": "Single"
}
},
"en-IN": {
"equivalent": [],
"by_char": false,
"timeago_tokens": {
"day": "D",
"days": "D",
"hour": "h",
"hours": "h",
"minute": "m",
"minutes": "m",
"month": "M",
"months": "M",
"second": "s",
"seconds": "s",
"week": "W",
"weeks": "W",
"year": "Y",
"years": "Y"
},
"date_order": "DY",
"datetime_order": "DMyHm",
"months": {
"apr": 4,
"aug": 8,
"dec": 12,
"feb": 2,
"jan": 1,
"jul": 7,
"jun": 6,
"mar": 3,
"may": 5,
"nov": 11,
"oct": 10,
"sep": 9,
"sept": 9
},
"timeago_nd_tokens": {
"today": "0D",
"yesterday": "1D"
},
"comma_decimal": false,
"number_tokens": {
"crore": 7, "crore": 7,
"lakh": 5 "lakh": 5
}, },
@ -742,6 +858,7 @@
"semanas": "W" "semanas": "W"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"abr": 4, "abr": 4,
"ago": 8, "ago": 8,
@ -773,10 +890,8 @@
"álbum": "Album" "álbum": "Album"
} }
}, },
"es-US": { "es-419": {
"equivalent": [ "equivalent": [],
"es-419"
],
"by_char": false, "by_char": false,
"timeago_tokens": { "timeago_tokens": {
"año": "Y", "año": "Y",
@ -795,6 +910,59 @@
"semanas": "W" "semanas": "W"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": {
"abr": 4,
"ago": 8,
"dic": 12,
"ene": 1,
"feb": 2,
"jul": 7,
"jun": 6,
"mar": 3,
"may": 5,
"nov": 11,
"oct": 10,
"sept": 9
},
"timeago_nd_tokens": {
"ayer": "1D",
"hoy": "0D"
},
"comma_decimal": false,
"number_tokens": {
"M": 6,
"mil": 3
},
"album_types": {
"audiolibro": "Audiobook",
"ep": "Ep",
"programa": "Show",
"sencillo": "Single",
"álbum": "Album"
}
},
"es-US": {
"equivalent": [],
"by_char": false,
"timeago_tokens": {
"año": "Y",
"años": "Y",
"día": "D",
"días": "D",
"hora": "h",
"horas": "h",
"mes": "M",
"meses": "M",
"minuto": "m",
"minutos": "m",
"segundo": "s",
"segundos": "s",
"semana": "W",
"semanas": "W"
},
"date_order": "DY",
"datetime_order": "DMYhm",
"months": { "months": {
"abr": 4, "abr": 4,
"ago": 8, "ago": 8,
@ -848,6 +1016,7 @@
"tunni": "h" "tunni": "h"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"apr": 4, "apr": 4,
"aug": 8, "aug": 8,
@ -896,6 +1065,7 @@
"urtebete": "Y" "urtebete": "Y"
}, },
"date_order": "YD", "date_order": "YD",
"datetime_order": "yMDHm",
"months": { "months": {
"abe.": 12, "abe.": 12,
"abu.": 8, "abu.": 8,
@ -939,6 +1109,7 @@
"هفته": "W" "هفته": "W"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "YMDHm",
"months": { "months": {
"آوریل": 4, "آوریل": 4,
"اوت": 8, "اوت": 8,
@ -991,6 +1162,7 @@
"vuotta": "Y" "vuotta": "Y"
}, },
"date_order": "DMY", "date_order": "DMY",
"datetime_order": "DMYHm",
"months": {}, "months": {},
"timeago_nd_tokens": { "timeago_nd_tokens": {
"eilen": "1D", "eilen": "1D",
@ -1023,6 +1195,7 @@
"taon": "Y" "taon": "Y"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "MDyHm",
"months": { "months": {
"abr": 4, "abr": 4,
"ago": 8, "ago": 8,
@ -1055,9 +1228,7 @@
} }
}, },
"fr": { "fr": {
"equivalent": [ "equivalent": [],
"fr-CA"
],
"by_char": false, "by_char": false,
"timeago_tokens": { "timeago_tokens": {
"an": "Y", "an": "Y",
@ -1075,6 +1246,61 @@
"semaines": "W" "semaines": "W"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMYHm",
"months": {
"août": 8,
"avr.": 4,
"déc.": 12,
"févr.": 2,
"janv.": 1,
"juil.": 7,
"juill.": 7,
"juin": 6,
"mai": 5,
"mars": 3,
"nov.": 11,
"oct.": 10,
"sept.": 9
},
"timeago_nd_tokens": {
"aujourd'hui": "0D",
"hier": "1D"
},
"comma_decimal": true,
"number_tokens": {
"M": 6,
"Md": 9
},
"album_types": {
"album": "Album",
"ep": "Ep",
"livre audio": "Audiobook",
"microalbum": "Ep",
"simple": "Single",
"single": "Single",
"émission": "Show"
}
},
"fr-CA": {
"equivalent": [],
"by_char": false,
"timeago_tokens": {
"an": "Y",
"ans": "Y",
"heure": "h",
"heures": "h",
"jour": "D",
"jours": "D",
"minute": "m",
"minutes": "m",
"mois": "M",
"seconde": "s",
"secondes": "s",
"semaine": "W",
"semaines": "W"
},
"date_order": "DY",
"datetime_order": "YMDHm",
"months": { "months": {
"août": 8, "août": 8,
"avr.": 4, "avr.": 4,
@ -1097,8 +1323,7 @@
"comma_decimal": true, "comma_decimal": true,
"number_tokens": { "number_tokens": {
"G": 9, "G": 9,
"M": 6, "M": 6
"Md": 9
}, },
"album_types": { "album_types": {
"album": "Album", "album": "Album",
@ -1130,6 +1355,7 @@
"semanas": "W" "semanas": "W"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"abr.": 4, "abr.": 4,
"ago.": 8, "ago.": 8,
@ -1173,6 +1399,7 @@
"સેકંડ": "s" "સેકંડ": "s"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"એપ્રિલ,": 4, "એપ્રિલ,": 4,
"ઑક્ટો,": 10, "ઑક્ટો,": 10,
@ -1219,6 +1446,7 @@
"सेकंड": "s" "सेकंड": "s"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"अक्तू॰": 10, "अक्तू॰": 10,
"अग॰": 8, "अग॰": 8,
@ -1277,6 +1505,7 @@
"tjedna": "W" "tjedna": "W"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMYHm",
"months": { "months": {
"kol": 8, "kol": 8,
"lip": 6, "lip": 6,
@ -1329,6 +1558,7 @@
"órával": "h" "órával": "h"
}, },
"date_order": "YD", "date_order": "YD",
"datetime_order": "YMDHm",
"months": { "months": {
"aug.": 8, "aug.": 8,
"dec.": 12, "dec.": 12,
@ -1374,6 +1604,7 @@
"օր": "D" "օր": "D"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"ապր,": 4, "ապր,": 4,
"դեկ,": 12, "դեկ,": 12,
@ -1419,6 +1650,7 @@
"tahun": "Y" "tahun": "Y"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"agu": 8, "agu": 8,
"apr": 4, "apr": 4,
@ -1471,6 +1703,7 @@
"árum": "Y" "árum": "Y"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMYHm",
"months": { "months": {
"apr.": 4, "apr.": 4,
"des.": 12, "des.": 12,
@ -1523,6 +1756,7 @@
"settimane": "W" "settimane": "W"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"ago": 8, "ago": 8,
"apr": 4, "apr": 4,
@ -1580,6 +1814,7 @@
"שתי": "2" "שתי": "2"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMYHm",
"months": { "months": {
"באוג׳": 8, "באוג׳": 8,
"באוק׳": 10, "באוק׳": 10,
@ -1625,6 +1860,7 @@
"週": "W" "週": "W"
}, },
"date_order": "YMD", "date_order": "YMD",
"datetime_order": "YMDHm",
"months": {}, "months": {},
"timeago_nd_tokens": { "timeago_nd_tokens": {
"日": "1D", "日": "1D",
@ -1656,6 +1892,7 @@
"წუთის": "m" "წუთის": "m"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"აგვ.": 8, "აგვ.": 8,
"აპრ.": 4, "აპრ.": 4,
@ -1701,6 +1938,7 @@
"секунд": "s" "секунд": "s"
}, },
"date_order": "YD", "date_order": "YD",
"datetime_order": "DMyHm",
"months": { "months": {
"ақп.": 2, "ақп.": 2,
"жел.": 12, "жел.": 12,
@ -1747,6 +1985,7 @@
"សប្ដាហ៍មុន": "W" "សប្ដាហ៍មុន": "W"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"កក្កដា": 7, "កក្កដា": 7,
"កញ្ញា": 9, "កញ្ញា": 9,
@ -1799,6 +2038,7 @@
"ಸೆಕೆಂಡ್": "s" "ಸೆಕೆಂಡ್": "s"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"ಅಕ್ಟೋ": 10, "ಅಕ್ಟೋ": 10,
"ಆಗ": 8, "ಆಗ": 8,
@ -1843,6 +2083,7 @@
"초": "s" "초": "s"
}, },
"date_order": "YMD", "date_order": "YMD",
"datetime_order": "yMDHm",
"months": {}, "months": {},
"timeago_nd_tokens": { "timeago_nd_tokens": {
"오늘": "0D" "오늘": "0D"
@ -1874,6 +2115,7 @@
"секунд": "s" "секунд": "s"
}, },
"date_order": "YD", "date_order": "YD",
"datetime_order": "DMyHm",
"months": { "months": {
"авг.": 8, "авг.": 8,
"апр.": 4, "апр.": 4,
@ -1919,6 +2161,7 @@
"ເດືອນກ່ອນ": "M" "ເດືອນກ່ອນ": "M"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMYHm",
"months": { "months": {
"ກ.ຍ.": 9, "ກ.ຍ.": 9,
"ກ.ພ.": 2, "ກ.ພ.": 2,
@ -1979,6 +2222,7 @@
"valandų": "h" "valandų": "h"
}, },
"date_order": "YMD", "date_order": "YMD",
"datetime_order": "YMDHm",
"months": {}, "months": {},
"timeago_nd_tokens": { "timeago_nd_tokens": {
"vakar": "1D", "vakar": "1D",
@ -2018,6 +2262,7 @@
"stundām": "h" "stundām": "h"
}, },
"date_order": "YD", "date_order": "YD",
"datetime_order": "DMyHm",
"months": { "months": {
"apr.": 4, "apr.": 4,
"aug.": 8, "aug.": 8,
@ -2070,6 +2315,7 @@
"часа": "h" "часа": "h"
}, },
"date_order": "DMY", "date_order": "DMY",
"datetime_order": "DMyHm",
"months": {}, "months": {},
"timeago_nd_tokens": { "timeago_nd_tokens": {
"вчера": "1D", "вчера": "1D",
@ -2103,6 +2349,7 @@
"സെക്കൻഡ്": "s" "സെക്കൻഡ്": "s"
}, },
"date_order": "YD", "date_order": "YD",
"datetime_order": "DMyHm",
"months": { "months": {
"ഏപ്രി": 4, "ഏപ്രി": 4,
"ഒക്ടോ": 10, "ഒക്ടോ": 10,
@ -2147,6 +2394,7 @@
"өдрийн": "D" "өдрийн": "D"
}, },
"date_order": "YMD", "date_order": "YMD",
"datetime_order": "YMDHm",
"months": {}, "months": {},
"timeago_nd_tokens": { "timeago_nd_tokens": {
"өнөөдөр": "0D", "өнөөдөр": "0D",
@ -2186,6 +2434,7 @@
"सेकंदापूर्वी": "s" "सेकंदापूर्वी": "s"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"एप्रि,": 4, "एप्रि,": 4,
"ऑक्टो,": 10, "ऑक्टो,": 10,
@ -2232,6 +2481,7 @@
"tahun": "Y" "tahun": "Y"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"apr": 4, "apr": 4,
"dis": 12, "dis": 12,
@ -2277,6 +2527,7 @@
"လ": "M" "လ": "M"
}, },
"date_order": "YD", "date_order": "YD",
"datetime_order": "DMyHm",
"months": { "months": {
"စက်": 9, "စက်": 9,
"ဇန်": 1, "ဇန်": 1,
@ -2325,6 +2576,7 @@
"हप्ता": "W" "हप्ता": "W"
}, },
"date_order": "YD", "date_order": "YD",
"datetime_order": "yMDHm",
"months": { "months": {
"अक्टोबर": 10, "अक्टोबर": 10,
"अगस्ट": 8, "अगस्ट": 8,
@ -2376,6 +2628,7 @@
"weken": "W" "weken": "W"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMYHm",
"months": { "months": {
"apr.": 4, "apr.": 4,
"aug.": 8, "aug.": 8,
@ -2427,6 +2680,7 @@
"år": "Y" "år": "Y"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMYHm",
"months": { "months": {
"apr.": 4, "apr.": 4,
"aug.": 8, "aug.": 8,
@ -2471,6 +2725,7 @@
"ସେକେଣ୍ଡ": "s" "ସେକେଣ୍ଡ": "s"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "MDyHm",
"months": { "months": {
"ଅକ୍ଟୋବର": 10, "ଅକ୍ଟୋବର": 10,
"ଅଗଷ୍ଟ": 8, "ଅଗଷ୍ଟ": 8,
@ -2519,6 +2774,7 @@
"ਹਫ਼ਤੇ": "W" "ਹਫ਼ਤੇ": "W"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"ਅਕਤੂ": 10, "ਅਕਤੂ": 10,
"ਅਗ": 8, "ਅਗ": 8,
@ -2577,6 +2833,7 @@
"tygodnie": "W" "tygodnie": "W"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMYHm",
"months": { "months": {
"cze": 6, "cze": 6,
"gru": 12, "gru": 12,
@ -2629,6 +2886,7 @@
"semanas": "W" "semanas": "W"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMYHm",
"months": { "months": {
"abr.": 4, "abr.": 4,
"ago.": 8, "ago.": 8,
@ -2681,6 +2939,7 @@
"semanas": "W" "semanas": "W"
}, },
"date_order": "DMY", "date_order": "DMY",
"datetime_order": "DMyHm",
"months": {}, "months": {},
"timeago_nd_tokens": { "timeago_nd_tokens": {
"hoje": "0D", "hoje": "0D",
@ -2720,6 +2979,7 @@
"zile": "D" "zile": "D"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMYHm",
"months": { "months": {
"apr.": 4, "apr.": 4,
"aug.": 8, "aug.": 8,
@ -2777,6 +3037,7 @@
"часов": "h" "часов": "h"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMYHm",
"months": { "months": {
"авг.": 8, "авг.": 8,
"апр.": 4, "апр.": 4,
@ -2822,6 +3083,7 @@
"සති": "W" "සති": "W"
}, },
"date_order": "YD", "date_order": "YD",
"datetime_order": "YMDHm",
"months": { "months": {
"අගෝ": 8, "අගෝ": 8,
"අප්‍රේල්": 4, "අප්‍රේල්": 4,
@ -2875,6 +3137,7 @@
"týždňom": "W" "týždňom": "W"
}, },
"date_order": "DMY", "date_order": "DMY",
"datetime_order": "DMYHm",
"months": {}, "months": {},
"timeago_nd_tokens": { "timeago_nd_tokens": {
"dnes": "0D", "dnes": "0D",
@ -2922,6 +3185,7 @@
"uro": "h" "uro": "h"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"apr.": 4, "apr.": 4,
"avg.": 8, "avg.": 8,
@ -2970,6 +3234,7 @@
"vjet": "Y" "vjet": "Y"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"dhj": 12, "dhj": 12,
"gush": 8, "gush": 8,
@ -3024,6 +3289,7 @@
"секунди": "s" "секунди": "s"
}, },
"date_order": "DMY", "date_order": "DMY",
"datetime_order": "DMyHm",
"months": {}, "months": {},
"timeago_nd_tokens": { "timeago_nd_tokens": {
"данас": "0D", "данас": "0D",
@ -3066,6 +3332,7 @@
"sekundi": "s" "sekundi": "s"
}, },
"date_order": "DMY", "date_order": "DMY",
"datetime_order": "DMyHm",
"months": {}, "months": {},
"timeago_nd_tokens": { "timeago_nd_tokens": {
"danas": "0D", "danas": "0D",
@ -3104,6 +3371,7 @@
"år": "Y" "år": "Y"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "YMDHm",
"months": { "months": {
"apr.": 4, "apr.": 4,
"aug.": 8, "aug.": 8,
@ -3150,6 +3418,7 @@
"wiki": "W" "wiki": "W"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMYHm",
"months": { "months": {
"ago": 8, "ago": 8,
"apr": 4, "apr": 4,
@ -3201,6 +3470,7 @@
"விநாடிக்கு": "s" "விநாடிக்கு": "s"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"அக்.,": 10, "அக்.,": 10,
"ஆக.,": 8, "ஆக.,": 8,
@ -3252,6 +3522,7 @@
"సెకన్ల": "s" "సెకన్ల": "s"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"అక్టో,": 10, "అక్టో,": 10,
"ఆగ,": 8, "ఆగ,": 8,
@ -3297,6 +3568,7 @@
"เดือนที่ผ่านมา": "M" "เดือนที่ผ่านมา": "M"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"ก.ค.": 7, "ก.ค.": 7,
"ก.พ.": 2, "ก.พ.": 2,
@ -3345,6 +3617,7 @@
"yıl": "Y" "yıl": "Y"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMYHm",
"months": { "months": {
"ara": 12, "ara": 12,
"ağu": 8, "ağu": 8,
@ -3403,6 +3676,7 @@
"хвилину": "m" "хвилину": "m"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"бер.": 3, "бер.": 3,
"вер.": 9, "вер.": 9,
@ -3454,6 +3728,7 @@
"ہفتے": "W" "ہفتے": "W"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"اپریل،": 4, "اپریل،": 4,
"اکتوبر،": 10, "اکتوبر،": 10,
@ -3500,6 +3775,7 @@
"yil": "Y" "yil": "Y"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "DMyHm",
"months": { "months": {
"apr,": 4, "apr,": 4,
"avg,": 8, "avg,": 8,
@ -3546,6 +3822,7 @@
"tuần": "W" "tuần": "W"
}, },
"date_order": "DMY", "date_order": "DMY",
"datetime_order": "HmDMY",
"months": {}, "months": {},
"timeago_nd_tokens": { "timeago_nd_tokens": {
"nay": "0D", "nay": "0D",
@ -3578,6 +3855,7 @@
"秒": "s" "秒": "s"
}, },
"date_order": "YMD", "date_order": "YMD",
"datetime_order": "YMDHm",
"months": {}, "months": {},
"timeago_nd_tokens": { "timeago_nd_tokens": {
"今": "0D", "今": "0D",
@ -3610,6 +3888,7 @@
"秒": "s" "秒": "s"
}, },
"date_order": "YMD", "date_order": "YMD",
"datetime_order": "DMYHm",
"months": {}, "months": {},
"timeago_nd_tokens": { "timeago_nd_tokens": {
"今": "0D", "今": "0D",
@ -3641,6 +3920,7 @@
"週": "W" "週": "W"
}, },
"date_order": "YMD", "date_order": "YMD",
"datetime_order": "YMDHm",
"months": {}, "months": {},
"timeago_nd_tokens": { "timeago_nd_tokens": {
"今": "0D", "今": "0D",
@ -3679,6 +3959,7 @@
"unyaka": "Y" "unyaka": "Y"
}, },
"date_order": "DY", "date_order": "DY",
"datetime_order": "MDyHm",
"months": { "months": {
"aga": 8, "aga": 8,
"dis": 12, "dis": 12,

View file

@ -1023,6 +1023,30 @@ mod channel_rss {
} }
} }
//# CHANNEL TV
#[rstest]
#[case::default("UCHnyfMqiRRG1u-2MsSQLbXA", "Veritasium", 13_000_000, false)]
#[case::empty("UCApUH1k001sYlUTIIVIqGCA", "FreeUser", 600, true)]
#[case::music("UC_vmjW5e1xEHhYjY2a0kK1A", "Oonagh - Topic", 2000, true)]
fn get_channel_tv(
#[case] id: &str,
#[case] name: &str,
#[case] subscribers: u64,
#[case] empty: bool,
rp: RustyPipe,
) {
let channel = tokio_test::block_on(rp.query().channel_tv(id)).unwrap();
assert_eq!(channel.id, id);
assert_eq!(channel.name, name);
assert!(channel.subscriber_count.unwrap() > subscribers);
assert!(!channel.tv_banner.is_empty(), "got no tv banners");
if !empty {
assert!(!channel.videos.is_empty(), "got no videos");
}
}
//#SEARCH //#SEARCH
#[rstest] #[rstest]