fix: A/B test 16 (pageHeaderRenderer on playlist pages)

This commit is contained in:
ThetaDev 2024-10-12 05:47:47 +02:00
parent f3f2e1d3ca
commit e65f14556f
No known key found for this signature in database
GPG key ID: E319D3C5148D65B6
17 changed files with 6435 additions and 182 deletions

View file

@ -470,10 +470,10 @@ fn map_channel(
)
};
let subscriber_count = sub_part.and_then(|t| {
util::parse_large_numstr_or_warn::<u64>(&t.text, ctx.lang, &mut warnings)
util::parse_large_numstr_or_warn::<u64>(t.as_str(), ctx.lang, &mut warnings)
});
let video_count =
vc_part.and_then(|t| util::parse_numeric_or_warn(&t.text, &mut warnings));
vc_part.and_then(|t| util::parse_numeric_or_warn(t.as_str(), &mut warnings));
Channel {
id: metadata.external_id,
@ -482,7 +482,7 @@ fn map_channel(
md_rows
.first()
.and_then(|md| md.metadata_parts.get(1))
.map(|txt| txt.text.to_owned())
.map(|txt| txt.as_str().to_owned())
.filter(|txt| util::CHANNEL_HANDLE_REGEX.is_match(txt))
}),
subscriber_count,
@ -710,7 +710,7 @@ mod tests {
#[case::livestreams("livestreams", "UC2DjFE7Xf11URZqWBigcVOQ")]
#[case::pageheader("shorts_20240129_pageheader", "UCh8gHdtzO2tXd593_bjErWg")]
#[case::pageheader2("videos_20240324_pageheader2", "UC2DjFE7Xf11URZqWBigcVOQ")]
#[case::shorts2("shorts_20240910_lockup", "UCh8gHdtzO2tXd593_bjErWg")]
#[case::lockup("shorts_20240910_lockup", "UCh8gHdtzO2tXd593_bjErWg")]
fn map_channel_videos(#[case] name: &str, #[case] id: &str) {
let json_path = path!(*TESTFILES / "channel" / format!("channel_{name}.json"));
let json_file = File::open(json_path).unwrap();

View file

@ -10,7 +10,7 @@ use crate::{
ChannelId, Playlist, VideoItem,
},
serializer::text::{TextComponent, TextComponents},
util::{self, timeago, TryRemove},
util::{self, dictionary, timeago, TryRemove},
};
use super::{response, ClientType, MapRespCtx, MapResponse, MapResult, QBrowse, RustyPipeQuery};
@ -98,46 +98,105 @@ impl MapResponse<Playlist> for response::Playlist {
.playlist_sidebar_primary_info_renderer
.description
.filter(|d| !d.0.is_empty()),
primary
.playlist_sidebar_primary_info_renderer
.thumbnail_renderer
.playlist_video_thumbnail_renderer
.thumbnail,
Some(
primary
.playlist_sidebar_primary_info_renderer
.thumbnail_renderer
.playlist_video_thumbnail_renderer
.thumbnail,
),
primary
.playlist_sidebar_primary_info_renderer
.stats
.try_swap_remove(2),
)
}
None => {
let header_banner = header
.playlist_header_renderer
.playlist_header_banner
.ok_or(ExtractionError::InvalidData(Cow::Borrowed(
"no thumbnail found",
)))?;
let mut byline = header.playlist_header_renderer.byline;
let last_update_txt = byline
.try_swap_remove(1)
.map(|b| b.playlist_byline_renderer.text);
(
None,
header_banner.hero_playlist_thumbnail_renderer.thumbnail,
last_update_txt,
)
}
None => (None, None, None),
};
let (name, playlist_id, channel, n_videos_txt, description2, thumbnails2, last_update_txt2) =
match header {
response::playlist::Header::PlaylistHeaderRenderer(header_renderer) => {
let mut byline = header_renderer.byline;
let last_update_txt = byline
.try_swap_remove(1)
.map(|b| b.playlist_byline_renderer.text);
(
header_renderer.title,
header_renderer.playlist_id,
header_renderer
.owner_text
.and_then(|link| ChannelId::try_from(link).ok()),
header_renderer.num_videos_text,
header_renderer
.description_text
.map(|text| TextComponents(vec![TextComponent::new(text)])),
header_renderer
.playlist_header_banner
.map(|b| b.hero_playlist_thumbnail_renderer.thumbnail),
last_update_txt,
)
}
response::playlist::Header::PageHeaderRenderer(content_renderer) => {
let h = content_renderer.content.page_header_view_model;
let rows = h.metadata.content_metadata_view_model.metadata_rows;
let n_videos_txt = rows
.get(1)
.and_then(|r| r.metadata_parts.get(1))
.map(|p| p.as_str().to_owned())
.ok_or(ExtractionError::InvalidData("no video count".into()))?;
let mut channel = rows
.into_iter()
.next()
.and_then(|r| r.metadata_parts.into_iter().next())
.and_then(|p| match p {
response::MetadataPart::Text(_) => None,
response::MetadataPart::AvatarStack {
avatar_stack_view_model,
} => ChannelId::try_from(avatar_stack_view_model.text).ok(),
});
// remove "by" prefix
if let Some(c) = channel.as_mut() {
let entry = dictionary::entry(ctx.lang);
let n = c.name.strip_prefix(entry.chan_prefix).unwrap_or(&c.name);
let n = n.strip_suffix(entry.chan_suffix).unwrap_or(n);
c.name = n.trim().to_owned();
}
let playlist_id = h
.actions
.flexible_actions_view_model
.actions_rows
.into_iter()
.next()
.and_then(|r| r.actions.into_iter().next())
.and_then(|a| {
a.button_view_model
.on_tap
.innertube_command
.into_playlist_id()
})
.ok_or(ExtractionError::InvalidData("no playlist id".into()))?;
(
h.title.dynamic_text_view_model.text,
playlist_id,
channel,
n_videos_txt,
h.description.description_preview_view_model.description,
h.hero_image.content_preview_image_view_model.image.into(),
None,
)
}
};
let n_videos = if mapper.ctoken.is_some() {
util::parse_numeric(&header.playlist_header_renderer.num_videos_text)
.map_err(|_| ExtractionError::InvalidData(Cow::Borrowed("no video count")))?
util::parse_numeric(&n_videos_txt)
.map_err(|_| ExtractionError::InvalidData("no video count".into()))?
} else {
mapper.items.len() as u64
};
let playlist_id = header.playlist_header_renderer.playlist_id;
if playlist_id != ctx.id {
return Err(ExtractionError::WrongResult(format!(
"got wrong playlist id {}, expected {}",
@ -145,24 +204,19 @@ impl MapResponse<Playlist> for response::Playlist {
)));
}
let name = header.playlist_header_renderer.title;
let description = description
.or_else(|| {
header
.playlist_header_renderer
.description_text
.map(|text| TextComponents(vec![TextComponent::new(text)]))
})
.map(RichText::from);
let channel = header
.playlist_header_renderer
.owner_text
.and_then(|link| ChannelId::try_from(link).ok());
let last_update = last_update_txt.as_ref().and_then(|txt| {
timeago::parse_textual_date_or_warn(ctx.lang, txt, &mut mapper.warnings)
.map(OffsetDateTime::date)
});
let description = description.or(description2).map(RichText::from);
let thumbnails = thumbnails
.or(thumbnails2)
.ok_or(ExtractionError::InvalidData(Cow::Borrowed(
"no thumbnail found",
)))?;
let last_update = last_update_txt
.as_deref()
.or(last_update_txt2.as_deref())
.and_then(|txt| {
timeago::parse_textual_date_or_warn(ctx.lang, txt, &mut mapper.warnings)
.map(OffsetDateTime::date)
});
Ok(MapResult {
c: Playlist {
@ -207,6 +261,7 @@ mod tests {
#[case::long("long", "PL5dDx681T4bR7ZF1IuWzOv1omlRbE7PiJ")]
#[case::nomusic("nomusic", "PL1J-6JOckZtE_P9Xx8D3b2O6w0idhuKBe")]
#[case::live("live", "UULVvqRdlKsE5Q8mf8YXbdIJLw")]
#[case::pageheader("20241011_pageheader", "PLT2w2oBf1TZKyvY_M6JsASs73m-wjLzH5")]
fn map_playlist_data(#[case] name: &str, #[case] id: &str) {
let json_path = path!(*TESTFILES / "playlist" / format!("playlist_{name}.json"));
let json_file = File::open(json_path).unwrap();

View file

@ -3,7 +3,8 @@ use serde_with::{rust::deserialize_ignore_any, serde_as, DefaultOnError, VecSkip
use super::{
video_item::YouTubeListRenderer, Alert, ChannelBadge, ContentRenderer, ContentsRenderer,
ContinuationActionWrap, ImageView, ResponseContext, Thumbnails, TwoColumnBrowseResults,
ContinuationActionWrap, ImageView, PageHeaderRendererContent, PhMetadataView, ResponseContext,
Thumbnails, TwoColumnBrowseResults,
};
use crate::serializer::text::{AttributedText, Text, TextComponent};
@ -76,7 +77,7 @@ pub(crate) enum Header {
C4TabbedHeaderRenderer(HeaderRenderer),
/// Used for special channels like YouTube Music
CarouselHeaderRenderer(ContentsRenderer<CarouselHeaderRendererItem>),
PageHeaderRenderer(ContentRenderer<PageHeaderRenderer>),
PageHeaderRenderer(ContentRenderer<PageHeaderRendererContent<PageHeaderRendererInner>>),
}
#[serde_as]
@ -114,12 +115,6 @@ pub(crate) enum CarouselHeaderRendererItem {
None,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PageHeaderRenderer {
pub page_header_view_model: PageHeaderRendererInner,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
@ -225,38 +220,12 @@ pub(crate) struct PhAvatarView3 {
pub avatar_view_model: ImageView,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PhMetadataView {
pub content_metadata_view_model: PhMetadataView2,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PhMetadataView2 {
pub metadata_rows: Vec<PhMetadataRow>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PhMetadataRow {
pub metadata_parts: Vec<TextWrap>,
}
#[derive(Default, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PhBannerView {
pub image_banner_view_model: ImageView,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct TextWrap {
#[serde_as(deserialize_as = "Text")]
pub text: String,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Metadata {

View file

@ -57,7 +57,8 @@ use serde::{
use serde_with::{serde_as, DisplayFromStr, VecSkipError};
use crate::error::ExtractionError;
use crate::serializer::{text::Text, MapResult, VecSkipErrorWrap};
use crate::serializer::text::{AttributedText, Text, TextComponent};
use crate::serializer::{MapResult, VecSkipErrorWrap};
use self::video_item::YouTubeListRenderer;
@ -464,3 +465,61 @@ where
deserializer.deserialize_seq(SeqVisitor(PhantomData::<T>))
}
}
// PAGE HEADER
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PageHeaderRendererContent<T> {
pub page_header_view_model: T,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PhMetadataView {
pub content_metadata_view_model: PhMetadataView2,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PhMetadataView2 {
pub metadata_rows: Vec<PhMetadataRow>,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PhMetadataRow {
#[serde_as(as = "VecSkipError<_>")]
pub metadata_parts: Vec<MetadataPart>,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) enum MetadataPart {
Text(#[serde_as(deserialize_as = "AttributedText")] String),
#[serde(rename_all = "camelCase")]
AvatarStack {
avatar_stack_view_model: AvatarStackViewModel,
},
}
impl MetadataPart {
pub fn as_str(&self) -> &str {
match self {
MetadataPart::Text(s) => s,
MetadataPart::AvatarStack {
avatar_stack_view_model,
} => avatar_stack_view_model.text.as_str(),
}
}
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct AvatarStackViewModel {
#[serde_as(deserialize_as = "AttributedText")]
pub text: TextComponent,
}

View file

@ -1,11 +1,12 @@
use serde::Deserialize;
use serde_with::{serde_as, DefaultOnError};
use serde_with::{serde_as, DefaultOnError, VecSkipError};
use crate::serializer::text::{Text, TextComponent, TextComponents};
use crate::serializer::text::{AttributedText, Text, TextComponent, TextComponents};
use super::{
video_item::YouTubeListRenderer, Alert, ContentsRenderer, ResponseContext, SectionList, Tab,
ThumbnailsWrap, TwoColumnBrowseResults,
url_endpoint::NavigationEndpoint, video_item::YouTubeListRenderer, Alert, ContentRenderer,
ContentsRenderer, ImageView, PageHeaderRendererContent, PhMetadataView, ResponseContext,
SectionList, Tab, ThumbnailsWrap, TwoColumnBrowseResults,
};
#[serde_as]
@ -35,8 +36,9 @@ pub(crate) struct PlaylistVideoListRenderer {
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Header {
pub playlist_header_renderer: HeaderRenderer,
pub(crate) enum Header {
PlaylistHeaderRenderer(HeaderRenderer),
PageHeaderRenderer(ContentRenderer<PageHeaderRendererContent<PageHeaderRendererInner>>),
}
#[serde_as]
@ -111,3 +113,85 @@ pub(crate) struct PlaylistThumbnailRenderer {
#[serde(alias = "playlistCustomThumbnailRenderer")]
pub playlist_video_thumbnail_renderer: ThumbnailsWrap,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PageHeaderRendererInner {
pub title: PhTitleView,
pub metadata: PhMetadataView,
pub actions: PhActions,
pub description: PhDescription,
pub hero_image: PhHeroImage,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PhDescription {
pub description_preview_view_model: PhDescription2,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PhDescription2 {
#[serde_as(as = "Option<AttributedText>")]
pub description: Option<TextComponents>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PhHeroImage {
pub content_preview_image_view_model: ImageView,
}
#[derive(Default, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PhTitleView {
pub dynamic_text_view_model: PhTitleInner,
}
#[serde_as]
#[derive(Default, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PhTitleInner {
#[serde_as(as = "AttributedText")]
pub text: String,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PhActions {
pub flexible_actions_view_model: PhActions2,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PhActions2 {
pub actions_rows: Vec<ActionsRow>,
}
#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ActionsRow {
#[serde_as(as = "VecSkipError<_>")]
pub actions: Vec<ButtonAction>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ButtonAction {
pub button_view_model: ButtonViewModel,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ButtonViewModel {
pub on_tap: ActionOnTap,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ActionOnTap {
pub innertube_command: NavigationEndpoint,
}

View file

@ -157,7 +157,7 @@ pub(crate) struct WatchEndpointConfig {
#[derive(Default, Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
pub(crate) enum MusicVideoType {
#[default]
#[serde(rename = "MUSIC_VIDEO_TYPE_OMV")]
#[serde(rename = "MUSIC_VIDEO_TYPE_OMV", alias = "MUSIC_VIDEO_TYPE_UGC")]
Video,
#[serde(rename = "MUSIC_VIDEO_TYPE_ATV")]
Track,
@ -333,4 +333,26 @@ impl NavigationEndpoint {
None
}
}
pub(crate) fn into_playlist_id(self) -> Option<String> {
match self {
NavigationEndpoint::Watch { watch_endpoint } => watch_endpoint.playlist_id,
NavigationEndpoint::Browse {
browse_endpoint,
command_metadata,
} => Some(browse_endpoint.browse_id).filter(|_| {
browse_endpoint
.browse_endpoint_context_supported_configs
.map(|c| c.browse_endpoint_context_music_config.page_type == PageType::Playlist)
.unwrap_or_default()
|| command_metadata
.map(|c| c.web_command_metadata.web_page_type == PageType::Playlist)
.unwrap_or_default()
}),
NavigationEndpoint::Url { .. } => None,
NavigationEndpoint::WatchPlaylist {
watch_playlist_endpoint,
} => Some(watch_playlist_endpoint.playlist_id),
}
}
}

View file

@ -0,0 +1,456 @@
---
source: src/client/playlist.rs
expression: map_res.c
---
Playlist(
id: "PLT2w2oBf1TZKyvY_M6JsASs73m-wjLzH5",
name: "LilyPichu",
videos: Paginator(
count: Some(10),
items: [
VideoItem(
id: "DXuNJ267Vss",
name: "dreamy night ♫",
duration: Some(246),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/DXuNJ267Vss/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLBQu39qIU8WKbC5KeXQ_a_Kmeq-Mw",
width: 168,
height: 94,
),
Thumbnail(
url: "https://i.ytimg.com/vi/DXuNJ267Vss/hqdefault.jpg?sqp=-oaymwEbCMQBEG5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLAFnmg4-mRI64nmz4R4GUGo720Jzw",
width: 196,
height: 110,
),
Thumbnail(
url: "https://i.ytimg.com/vi/DXuNJ267Vss/hqdefault.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLClxuSTfYdqfosJClOxA2osI934sw",
width: 246,
height: 138,
),
Thumbnail(
url: "https://i.ytimg.com/vi/DXuNJ267Vss/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCVR7qAVJNM3flwQ_ZYfPS3iujF1w",
width: 336,
height: 188,
),
],
channel: Some(ChannelTag(
id: "UCde_xnXu2lPBmRgAp_nq29A",
name: "comfi beats",
avatar: [],
verification: None,
subscriber_count: None,
)),
publish_date: "[date]",
publish_date_txt: Some("4 years ago"),
view_count: Some(15000000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "eutMcjJCVqc",
name: "these days it\'s hard to find the words ♫",
duration: Some(168),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/eutMcjJCVqc/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLDc3Ycpb5YaNFeHu8Nf5smL25Z07A",
width: 168,
height: 94,
),
Thumbnail(
url: "https://i.ytimg.com/vi/eutMcjJCVqc/hqdefault.jpg?sqp=-oaymwEbCMQBEG5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLAScc3jmGa81fZ-rdds1pLhsyeHcA",
width: 196,
height: 110,
),
Thumbnail(
url: "https://i.ytimg.com/vi/eutMcjJCVqc/hqdefault.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCukgu31Ut9lp2E4t5BWlzit6JruA",
width: 246,
height: 138,
),
Thumbnail(
url: "https://i.ytimg.com/vi/eutMcjJCVqc/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBjeA0RixBcCP2w53Ke2H43HJ9j9w",
width: 336,
height: 188,
),
],
channel: Some(ChannelTag(
id: "UCde_xnXu2lPBmRgAp_nq29A",
name: "comfi beats",
avatar: [],
verification: None,
subscriber_count: None,
)),
publish_date: "[date]",
publish_date_txt: Some("4 years ago"),
view_count: Some(1200000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "B5eM3Q3wj0M",
name: "a vision ♫",
duration: Some(169),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/B5eM3Q3wj0M/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLBK3FpJmFmAlA7ALGjJDXrWC-jtfw",
width: 168,
height: 94,
),
Thumbnail(
url: "https://i.ytimg.com/vi/B5eM3Q3wj0M/hqdefault.jpg?sqp=-oaymwEbCMQBEG5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLCwzmil8fgPbrAd-ef4T3vzIzmlKg",
width: 196,
height: 110,
),
Thumbnail(
url: "https://i.ytimg.com/vi/B5eM3Q3wj0M/hqdefault.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBDU2ur1UisffVmAXAG8lXbT49x5Q",
width: 246,
height: 138,
),
Thumbnail(
url: "https://i.ytimg.com/vi/B5eM3Q3wj0M/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBy46YLhKfVL6Wj71RWd1Ru9C3Z0w",
width: 336,
height: 188,
),
],
channel: Some(ChannelTag(
id: "UCde_xnXu2lPBmRgAp_nq29A",
name: "comfi beats",
avatar: [],
verification: None,
subscriber_count: None,
)),
publish_date: "[date]",
publish_date_txt: Some("4 years ago"),
view_count: Some(843000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "MJedvm2TE8o",
name: "a stormy night ♫",
duration: Some(202),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/MJedvm2TE8o/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLBtulKIBStuPyBYbBqlE5B4-2xBaQ",
width: 168,
height: 94,
),
Thumbnail(
url: "https://i.ytimg.com/vi/MJedvm2TE8o/hqdefault.jpg?sqp=-oaymwEbCMQBEG5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLAEgWtwIZHLv5EKCZVIVibIkZLHlg",
width: 196,
height: 110,
),
Thumbnail(
url: "https://i.ytimg.com/vi/MJedvm2TE8o/hqdefault.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBSlJy1g4t2np6JK4hBst8b6PA2ew",
width: 246,
height: 138,
),
Thumbnail(
url: "https://i.ytimg.com/vi/MJedvm2TE8o/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDkDGK867GRxs9b42P7hJkK6B2pRQ",
width: 336,
height: 188,
),
],
channel: Some(ChannelTag(
id: "UCde_xnXu2lPBmRgAp_nq29A",
name: "comfi beats",
avatar: [],
verification: None,
subscriber_count: None,
)),
publish_date: "[date]",
publish_date_txt: Some("4 years ago"),
view_count: Some(1300000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "HHKmS7c5ai4",
name: "unknown waters ♫",
duration: Some(116),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/HHKmS7c5ai4/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLArqrLnSpTLA-1qj-yF6AmfrHOsTA",
width: 168,
height: 94,
),
Thumbnail(
url: "https://i.ytimg.com/vi/HHKmS7c5ai4/hqdefault.jpg?sqp=-oaymwEbCMQBEG5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLCOh-S60OM2Hhswihr3Glb1cM1AAg",
width: 196,
height: 110,
),
Thumbnail(
url: "https://i.ytimg.com/vi/HHKmS7c5ai4/hqdefault.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBEyk0qxSToWD_g9L3bTrtrsJjhsw",
width: 246,
height: 138,
),
Thumbnail(
url: "https://i.ytimg.com/vi/HHKmS7c5ai4/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDG12RCxjnm4JHG9T8Ow_thj_ecxA",
width: 336,
height: 188,
),
],
channel: Some(ChannelTag(
id: "UCde_xnXu2lPBmRgAp_nq29A",
name: "comfi beats",
avatar: [],
verification: None,
subscriber_count: None,
)),
publish_date: "[date]",
publish_date_txt: Some("4 years ago"),
view_count: Some(707000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "zF32eh6PWPk",
name: "wilting memories ♫",
duration: Some(108),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/zF32eh6PWPk/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLDr2_1EoZ50kzSgDKwJQaY6Pv3WJA",
width: 168,
height: 94,
),
Thumbnail(
url: "https://i.ytimg.com/vi/zF32eh6PWPk/hqdefault.jpg?sqp=-oaymwEbCMQBEG5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLC44h27mvgtl5QBA_ed1o4kXPieBA",
width: 196,
height: 110,
),
Thumbnail(
url: "https://i.ytimg.com/vi/zF32eh6PWPk/hqdefault.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDm1iJNfB4KbVMoqPMKrWNHIBaBgg",
width: 246,
height: 138,
),
Thumbnail(
url: "https://i.ytimg.com/vi/zF32eh6PWPk/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDCJEQasRwYLo-iK___zZ767PrCWQ",
width: 336,
height: 188,
),
],
channel: Some(ChannelTag(
id: "UCde_xnXu2lPBmRgAp_nq29A",
name: "comfi beats",
avatar: [],
verification: None,
subscriber_count: None,
)),
publish_date: "[date]",
publish_date_txt: Some("4 years ago"),
view_count: Some(797000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "hbz-8K-pxpY",
name: "sunshine & butterflies ♫",
duration: Some(185),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/hbz-8K-pxpY/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLDMheaVKp1qI6AOcwrLl2K_U7EnAA",
width: 168,
height: 94,
),
Thumbnail(
url: "https://i.ytimg.com/vi/hbz-8K-pxpY/hqdefault.jpg?sqp=-oaymwEbCMQBEG5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLDSWaobRJuSVZFDf9aj1kHVx6WuXw",
width: 196,
height: 110,
),
Thumbnail(
url: "https://i.ytimg.com/vi/hbz-8K-pxpY/hqdefault.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDi33fhhNwD8Rtf56eIrPZV0Wh8ZQ",
width: 246,
height: 138,
),
Thumbnail(
url: "https://i.ytimg.com/vi/hbz-8K-pxpY/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBt77gqVq8oLIUs7njZkvP2EvmTAw",
width: 336,
height: 188,
),
],
channel: Some(ChannelTag(
id: "UCde_xnXu2lPBmRgAp_nq29A",
name: "comfi beats",
avatar: [],
verification: None,
subscriber_count: None,
)),
publish_date: "[date]",
publish_date_txt: Some("4 years ago"),
view_count: Some(2500000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "A2zepLiuEJU",
name: "foreverland ♫",
duration: Some(146),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/A2zepLiuEJU/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLAUpy0PRzet_xrPsGPj2Mw_ik5o0A",
width: 168,
height: 94,
),
Thumbnail(
url: "https://i.ytimg.com/vi/A2zepLiuEJU/hqdefault.jpg?sqp=-oaymwEbCMQBEG5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLCgYyZQkAGdE25F-zZ6AAHY5GuNjQ",
width: 196,
height: 110,
),
Thumbnail(
url: "https://i.ytimg.com/vi/A2zepLiuEJU/hqdefault.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBWFlbG5J4x8J3pxGC2k_P2O7lKmA",
width: 246,
height: 138,
),
Thumbnail(
url: "https://i.ytimg.com/vi/A2zepLiuEJU/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLD6N47yuLYe5m50AwYPX9Cos4RSVA",
width: 336,
height: 188,
),
],
channel: Some(ChannelTag(
id: "UCde_xnXu2lPBmRgAp_nq29A",
name: "comfi beats",
avatar: [],
verification: None,
subscriber_count: None,
)),
publish_date: "[date]",
publish_date_txt: Some("4 years ago"),
view_count: Some(983000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "5yaY7aG1Lpo",
name: "dreamy nightmares ♫",
duration: Some(197),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/5yaY7aG1Lpo/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLB-5OLhj51LzsZswqPsGVPOKfkhFA",
width: 168,
height: 94,
),
Thumbnail(
url: "https://i.ytimg.com/vi/5yaY7aG1Lpo/hqdefault.jpg?sqp=-oaymwEbCMQBEG5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLDISujKyqhzBYW3SdgC5QTxv7i1KQ",
width: 196,
height: 110,
),
Thumbnail(
url: "https://i.ytimg.com/vi/5yaY7aG1Lpo/hqdefault.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDBkkukABZ_d3rsEzewfiHimbM2PA",
width: 246,
height: 138,
),
Thumbnail(
url: "https://i.ytimg.com/vi/5yaY7aG1Lpo/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCnqJbFB4UvqIKUQq5xvVH6RQBm7A",
width: 336,
height: 188,
),
],
channel: Some(ChannelTag(
id: "UCde_xnXu2lPBmRgAp_nq29A",
name: "comfi beats",
avatar: [],
verification: None,
subscriber_count: None,
)),
publish_date: "[date]",
publish_date_txt: Some("4 years ago"),
view_count: Some(1100000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
VideoItem(
id: "WwXJrMhbi-s",
name: "comfy vibes ♫",
duration: Some(194),
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/WwXJrMhbi-s/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLByFfew80T9RbyU8-EiLyb6HRU4Ww",
width: 168,
height: 94,
),
Thumbnail(
url: "https://i.ytimg.com/vi/WwXJrMhbi-s/hqdefault.jpg?sqp=-oaymwEbCMQBEG5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLCcjh19GL-pWHpkpRD1ioCcyoZcrA",
width: 196,
height: 110,
),
Thumbnail(
url: "https://i.ytimg.com/vi/WwXJrMhbi-s/hqdefault.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCa7u5cp-4vH6mDG8HUOKyNTETgtQ",
width: 246,
height: 138,
),
Thumbnail(
url: "https://i.ytimg.com/vi/WwXJrMhbi-s/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLB-vP4BDBiu9PfU-F6DQ75iUsL4xQ",
width: 336,
height: 188,
),
],
channel: Some(ChannelTag(
id: "UCde_xnXu2lPBmRgAp_nq29A",
name: "comfi beats",
avatar: [],
verification: None,
subscriber_count: None,
)),
publish_date: "[date]",
publish_date_txt: Some("4 years ago"),
view_count: Some(1800000),
is_live: false,
is_short: false,
is_upcoming: false,
short_description: None,
),
],
ctoken: None,
endpoint: browse,
),
video_count: 10,
thumbnail: [
Thumbnail(
url: "https://i.ytimg.com/vi/DXuNJ267Vss/hqdefault.jpg?sqp=-oaymwEWCKgBEF5IWvKriqkDCQgBFQAAiEIYAQ==&rs=AOn4CLAHp6V96b70x4SWm9Pe6WEHnQhP6A",
width: 168,
height: 94,
),
Thumbnail(
url: "https://i.ytimg.com/vi/DXuNJ267Vss/hqdefault.jpg?sqp=-oaymwEWCMQBEG5IWvKriqkDCQgBFQAAiEIYAQ==&rs=AOn4CLDPCehYWYW8HhToloH9MJWD_wKq1w",
width: 196,
height: 110,
),
Thumbnail(
url: "https://i.ytimg.com/vi/DXuNJ267Vss/hqdefault.jpg?sqp=-oaymwEXCPYBEIoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLA7aSRV3Ymv8oEFYT7TUpwSZLPbCA",
width: 246,
height: 138,
),
Thumbnail(
url: "https://i.ytimg.com/vi/DXuNJ267Vss/hqdefault.jpg?sqp=-oaymwEXCNACELwBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLAYMzfwTTZbTbHcDUK9kIa450u_7g",
width: 336,
height: 188,
),
],
description: None,
channel: Some(ChannelId(
id: "UCai7BcI5lrXC2vdc3ySku8A",
name: "Kevin Ramirez",
)),
last_update: "[date]",
last_update_txt: Some("Last updated on Oct 13, 2020"),
visitor_data: Some("CgtQNE9Wb3N1MU1HSSic8Ka4BjIKCgJERRIEEgAgMA%3D%3D"),
)

View file

@ -50,6 +50,10 @@ pub(crate) struct Entry {
///
/// Format: Parsed text -> Album type
pub album_types: phf::Map<&'static str, AlbumType>,
/// Channel name prefix on playlist pages (e.g. `by`)
pub chan_prefix: &'static str,
/// Channel name suffix on playlist pages
pub chan_suffix: &'static str,
}
#[rustfmt::skip]
@ -151,6 +155,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("enkelsnit", AlbumType::Single),
],
},
chan_prefix: "deur",
chan_suffix: "",
},
Language::Am => Entry {
timeago_tokens: ::phf::Map {
@ -252,6 +258,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("አልበም", AlbumType::Album),
],
},
chan_prefix: "",
chan_suffix: "",
},
Language::Ar => Entry {
timeago_tokens: ::phf::Map {
@ -355,6 +363,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("الكتب المسموعة", AlbumType::Audiobook),
],
},
chan_prefix: "بواسطة",
chan_suffix: "",
},
Language::As => Entry {
timeago_tokens: ::phf::Map {
@ -438,6 +448,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("ep", AlbumType::Ep),
],
},
chan_prefix: "",
chan_suffix: "ৰ দ\u{9cd}\u{9be}\u{9be}",
},
Language::Az => Entry {
timeago_tokens: ::phf::Map {
@ -524,6 +536,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("ep", AlbumType::Ep),
],
},
chan_prefix: "by",
chan_suffix: "",
},
Language::Be => Entry {
timeago_tokens: ::phf::Map {
@ -640,6 +654,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("міні-альбом", AlbumType::Ep),
],
},
chan_prefix: "ад",
chan_suffix: "",
},
Language::Bg => Entry {
timeago_tokens: ::phf::Map {
@ -725,6 +741,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("предаване", AlbumType::Show),
],
},
chan_prefix: "от",
chan_suffix: "",
},
Language::Bn => Entry {
timeago_tokens: ::phf::Map {
@ -816,6 +834,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("\u{9cd}\u{9be}লব\u{9be}", AlbumType::Album),
],
},
chan_prefix: ",",
chan_suffix: "\u{9cd}\u{9be}\u{9be}",
},
Language::Bs => Entry {
timeago_tokens: ::phf::Map {
@ -926,6 +946,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("ep", AlbumType::Ep),
],
},
chan_prefix: "od",
chan_suffix: "",
},
Language::Ca => Entry {
timeago_tokens: ::phf::Map {
@ -1023,6 +1045,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("audiollibre", AlbumType::Audiobook),
],
},
chan_prefix: "de:",
chan_suffix: "",
},
Language::Cs => Entry {
timeago_tokens: ::phf::Map {
@ -1122,6 +1146,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("album", AlbumType::Album),
],
},
chan_prefix: "autor:",
chan_suffix: "",
},
Language::Da => Entry {
timeago_tokens: ::phf::Map {
@ -1218,6 +1244,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("ep", AlbumType::Ep),
],
},
chan_prefix: "af",
chan_suffix: "",
},
Language::De => Entry {
timeago_tokens: ::phf::Map {
@ -1299,6 +1327,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("hörspiel", AlbumType::Show),
],
},
chan_prefix: "von",
chan_suffix: "",
},
Language::El => Entry {
timeago_tokens: ::phf::Map {
@ -1399,6 +1429,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("άλμπουμ", AlbumType::Album),
],
},
chan_prefix: "από το χρήστη",
chan_suffix: "",
},
Language::En | Language::EnGb | Language::EnIn => Entry {
timeago_tokens: ::phf::Map {
@ -1511,6 +1543,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("audiobook", AlbumType::Audiobook),
],
},
chan_prefix: "by",
chan_suffix: "",
},
Language::Es => Entry {
timeago_tokens: ::phf::Map {
@ -1608,6 +1642,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("ep", AlbumType::Ep),
],
},
chan_prefix: "de",
chan_suffix: "",
},
Language::EsUs | Language::Es419 => Entry {
timeago_tokens: ::phf::Map {
@ -1706,6 +1742,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("álbum", AlbumType::Album),
],
},
chan_prefix: "de",
chan_suffix: "",
},
Language::Et => Entry {
timeago_tokens: ::phf::Map {
@ -1806,6 +1844,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("album", AlbumType::Album),
],
},
chan_prefix: "kanalilt",
chan_suffix: "",
},
Language::Eu => Entry {
timeago_tokens: ::phf::Map {
@ -1892,6 +1932,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("albuma", AlbumType::Album),
],
},
chan_prefix: "egilea:",
chan_suffix: "",
},
Language::Fa => Entry {
timeago_tokens: ::phf::Map {
@ -1978,6 +2020,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("کتاب صوتی", AlbumType::Audiobook),
],
},
chan_prefix: "توسط",
chan_suffix: "",
},
Language::Fi => Entry {
timeago_tokens: ::phf::Map {
@ -2066,6 +2110,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("ep", AlbumType::Ep),
],
},
chan_prefix: "tekijä:",
chan_suffix: "",
},
Language::Fil => Entry {
timeago_tokens: ::phf::Map {
@ -2152,6 +2198,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("ep", AlbumType::Ep),
],
},
chan_prefix: "ni/ng",
chan_suffix: "",
},
Language::Fr | Language::FrCa => Entry {
timeago_tokens: ::phf::Map {
@ -2256,6 +2304,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("single", AlbumType::Single),
],
},
chan_prefix: "de",
chan_suffix: "",
},
Language::Gl => Entry {
timeago_tokens: ::phf::Map {
@ -2352,6 +2402,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("audiolibro", AlbumType::Audiobook),
],
},
chan_prefix: "de",
chan_suffix: "",
},
Language::Gu => Entry {
timeago_tokens: ::phf::Map {
@ -2437,6 +2489,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("સિ\u{a82}ગલ", AlbumType::Single),
],
},
chan_prefix: "",
chan_suffix: "\u{acd}વારા",
},
Language::Hi => Entry {
timeago_tokens: ::phf::Map {
@ -2526,6 +2580,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("एल\u{94d}\u{200d}बम", AlbumType::Album),
],
},
chan_prefix: "",
chan_suffix: "\u{947}\u{93c}रिए",
},
Language::Hr => Entry {
timeago_tokens: ::phf::Map {
@ -2635,6 +2691,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("singl", AlbumType::Single),
],
},
chan_prefix: "omogućio kanal",
chan_suffix: "",
},
Language::Hu => Entry {
timeago_tokens: ::phf::Map {
@ -2734,6 +2792,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("műsor", AlbumType::Show),
],
},
chan_prefix: "",
chan_suffix: "csatornától",
},
Language::Hy => Entry {
timeago_tokens: ::phf::Map {
@ -2826,6 +2886,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("ալբոմ", AlbumType::Album),
],
},
chan_prefix: "հեղինակ՝",
chan_suffix: "",
},
Language::Id => Entry {
timeago_tokens: ::phf::Map {
@ -2918,6 +2980,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("acara", AlbumType::Show),
],
},
chan_prefix: "oleh",
chan_suffix: "",
},
Language::Is => Entry {
timeago_tokens: ::phf::Map {
@ -3023,6 +3087,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("ep", AlbumType::Ep),
],
},
chan_prefix: "eftir",
chan_suffix: "",
},
Language::It => Entry {
timeago_tokens: ::phf::Map {
@ -3125,6 +3191,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("programma", AlbumType::Show),
],
},
chan_prefix: "di",
chan_suffix: "",
},
Language::Iw => Entry {
timeago_tokens: ::phf::Map {
@ -3233,6 +3301,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("סינגל", AlbumType::Single),
],
},
chan_prefix: "מאת",
chan_suffix: "",
},
Language::Ja => Entry {
timeago_tokens: ::phf::Map {
@ -3300,6 +3370,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("表示", AlbumType::Show),
],
},
chan_prefix: "作成者:",
chan_suffix: "",
},
Language::Ka => Entry {
timeago_tokens: ::phf::Map {
@ -3392,6 +3464,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("სინგლი", AlbumType::Single),
],
},
chan_prefix: "",
chan_suffix: "-ის მიერ",
},
Language::Kk => Entry {
timeago_tokens: ::phf::Map {
@ -3485,6 +3559,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("альбом", AlbumType::Album),
],
},
chan_prefix: "қосқан",
chan_suffix: "",
},
Language::Km => Entry {
timeago_tokens: ::phf::Map {
@ -3570,6 +3646,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("ចម\u{17d2}រៀងទោល", AlbumType::Single),
],
},
chan_prefix: "ដោយ",
chan_suffix: "",
},
Language::Kn => Entry {
timeago_tokens: ::phf::Map {
@ -3671,6 +3749,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("ಆಲ\u{ccd}ಬಮ\u{ccd}", AlbumType::Album),
],
},
chan_prefix: "",
chan_suffix: "ಚಾನಲ\u{ccd}\u{200c}\u{cbf}ಂದ",
},
Language::Ko => Entry {
timeago_tokens: ::phf::Map {
@ -3741,6 +3821,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("싱글", AlbumType::Single),
],
},
chan_prefix: "게시자:",
chan_suffix: "",
},
Language::Ky => Entry {
timeago_tokens: ::phf::Map {
@ -3829,6 +3911,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("чакан альбом", AlbumType::Ep),
],
},
chan_prefix: "",
chan_suffix: "каналы аркылуу",
},
Language::Lo => Entry {
timeago_tokens: ::phf::Map {
@ -3923,6 +4007,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("ສະແດງ", AlbumType::Show),
],
},
chan_prefix: "ໂດຍ",
chan_suffix: "",
},
Language::Lt => Entry {
timeago_tokens: ::phf::Map {
@ -4026,6 +4112,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("garsinė knyga", AlbumType::Audiobook),
],
},
chan_prefix: "pridėjo",
chan_suffix: "",
},
Language::Lv => Entry {
timeago_tokens: ::phf::Map {
@ -4132,6 +4220,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("pārraide", AlbumType::Show),
],
},
chan_prefix: "autors:",
chan_suffix: "",
},
Language::Mk => Entry {
timeago_tokens: ::phf::Map {
@ -4215,6 +4305,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("серија", AlbumType::Show),
],
},
chan_prefix: "од",
chan_suffix: "",
},
Language::Ml => Entry {
timeago_tokens: ::phf::Map {
@ -4302,6 +4394,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("ആല\u{d4d}\u{200d}\u{200c}ബം", AlbumType::Album),
],
},
chan_prefix: "",
chan_suffix: "\u{d41}ഖേന",
},
Language::Mn => Entry {
timeago_tokens: ::phf::Map {
@ -4379,6 +4473,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("цомог", AlbumType::Album),
],
},
chan_prefix: "сувгийн нэр:",
chan_suffix: "",
},
Language::Mr => Entry {
timeago_tokens: ::phf::Map {
@ -4481,6 +4577,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("शो", AlbumType::Show),
],
},
chan_prefix: "",
chan_suffix: "\u{94d}वार\u{947}",
},
Language::Ms => Entry {
timeago_tokens: ::phf::Map {
@ -4570,6 +4668,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("ep", AlbumType::Ep),
],
},
chan_prefix: "oleh",
chan_suffix: "",
},
Language::My => Entry {
timeago_tokens: ::phf::Map {
@ -4663,6 +4763,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("အော\u{103a}\u{102e}\u{102d}\u{102f}စာအ\u{102f}\u{103a}", AlbumType::Audiobook),
],
},
chan_prefix: "",
chan_suffix: "\u{103e}",
},
Language::Ne => Entry {
timeago_tokens: ::phf::Map {
@ -4750,6 +4852,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("अडियोब\u{941}", AlbumType::Audiobook),
],
},
chan_prefix: "",
chan_suffix: "\u{94d}वारा",
},
Language::Nl => Entry {
timeago_tokens: ::phf::Map {
@ -4843,6 +4947,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("ep", AlbumType::Ep),
],
},
chan_prefix: "door",
chan_suffix: "",
},
Language::No => Entry {
timeago_tokens: ::phf::Map {
@ -4946,6 +5052,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("ep", AlbumType::Ep),
],
},
chan_prefix: "av",
chan_suffix: "",
},
Language::Or => Entry {
timeago_tokens: ::phf::Map {
@ -5046,6 +5154,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("ep", AlbumType::Ep),
],
},
chan_prefix: "",
chan_suffix: "\u{b4d}\u{b3e}\u{b3e}",
},
Language::Pa => Entry {
timeago_tokens: ::phf::Map {
@ -5135,6 +5245,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("\u{a3c}\u{a4b}", AlbumType::Show),
],
},
chan_prefix: "",
chan_suffix: "\u{a71}\u{a4b}\u{a02}",
},
Language::Pl => Entry {
timeago_tokens: ::phf::Map {
@ -5250,6 +5362,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("audiobook", AlbumType::Audiobook),
],
},
chan_prefix: "autor:",
chan_suffix: "",
},
Language::Pt => Entry {
timeago_tokens: ::phf::Map {
@ -5349,6 +5463,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("audiolivro", AlbumType::Audiobook),
],
},
chan_prefix: "por",
chan_suffix: "",
},
Language::PtPt => Entry {
timeago_tokens: ::phf::Map {
@ -5430,6 +5546,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("audiolivro", AlbumType::Audiobook),
],
},
chan_prefix: "de",
chan_suffix: "",
},
Language::Ro => Entry {
timeago_tokens: ::phf::Map {
@ -5529,6 +5647,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("album", AlbumType::Album),
],
},
chan_prefix: "de",
chan_suffix: "",
},
Language::Ru => Entry {
timeago_tokens: ::phf::Map {
@ -5642,6 +5762,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("аудиошоу", AlbumType::Show),
],
},
chan_prefix: "",
chan_suffix: "",
},
Language::Si => Entry {
timeago_tokens: ::phf::Map {
@ -5729,6 +5851,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("ඇල\u{dca}බමය", AlbumType::Album),
],
},
chan_prefix: "",
chan_suffix: "\u{dd2}\u{dd2}\u{dca}",
},
Language::Sk => Entry {
timeago_tokens: ::phf::Map {
@ -5828,6 +5952,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("audiokniha", AlbumType::Audiobook),
],
},
chan_prefix: "Autori:",
chan_suffix: "",
},
Language::Sl => Entry {
timeago_tokens: ::phf::Map {
@ -5948,6 +6074,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("ep", AlbumType::Ep),
],
},
chan_prefix: "kanal",
chan_suffix: "",
},
Language::Sq => Entry {
timeago_tokens: ::phf::Map {
@ -6039,6 +6167,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("single", AlbumType::Single),
],
},
chan_prefix: "nga",
chan_suffix: "",
},
Language::Sr => Entry {
timeago_tokens: ::phf::Map {
@ -6127,6 +6257,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("серија", AlbumType::Show),
],
},
chan_prefix: "са канала",
chan_suffix: "",
},
Language::SrLatn => Entry {
timeago_tokens: ::phf::Map {
@ -6215,6 +6347,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("serija", AlbumType::Show),
],
},
chan_prefix: "sa kanala",
chan_suffix: "",
},
Language::Sv => Entry {
timeago_tokens: ::phf::Map {
@ -6313,6 +6447,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("singel", AlbumType::Single),
],
},
chan_prefix: "från",
chan_suffix: "",
},
Language::Sw => Entry {
timeago_tokens: ::phf::Map {
@ -6400,6 +6536,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("kipindi", AlbumType::Show),
],
},
chan_prefix: "kutoka",
chan_suffix: "",
},
Language::Ta => Entry {
timeago_tokens: ::phf::Map {
@ -6503,6 +6641,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("ஆடியோ புத\u{bcd}தகம\u{bcd}", AlbumType::Audiobook),
],
},
chan_prefix: "வழங\u{bcd}கியவர\u{bcd}:",
chan_suffix: "",
},
Language::Te => Entry {
timeago_tokens: ::phf::Map {
@ -6603,6 +6743,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("\u{c3f}ంగ\u{c3f}\u{c4d}", AlbumType::Single),
],
},
chan_prefix: "",
chan_suffix: "\u{c3e}\u{c46}\u{c4d}\u{c4d}\u{c3e}\u{c3e}",
},
Language::Th => Entry {
timeago_tokens: ::phf::Map {
@ -6704,6 +6846,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("\u{e31}ลบ\u{e31}\u{e49}", AlbumType::Album),
],
},
chan_prefix: "โดย",
chan_suffix: "",
},
Language::Tr => Entry {
timeago_tokens: ::phf::Map {
@ -6794,6 +6938,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("albüm", AlbumType::Album),
],
},
chan_prefix: "",
chan_suffix: "tarafından",
},
Language::Uk => Entry {
timeago_tokens: ::phf::Map {
@ -6911,6 +7057,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("аудіодрама", AlbumType::Show),
],
},
chan_prefix: "власник:",
chan_suffix: "",
},
Language::Ur => Entry {
timeago_tokens: ::phf::Map {
@ -7006,6 +7154,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("شو", AlbumType::Show),
],
},
chan_prefix: "منجانب",
chan_suffix: "",
},
Language::Uz => Entry {
timeago_tokens: ::phf::Map {
@ -7091,6 +7241,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("audiokitob", AlbumType::Audiobook),
],
},
chan_prefix: "muallif:",
chan_suffix: "",
},
Language::Vi => Entry {
timeago_tokens: ::phf::Map {
@ -7159,6 +7311,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("đĩa nhạc", AlbumType::Album),
],
},
chan_prefix: "của",
chan_suffix: "",
},
Language::ZhCn => Entry {
timeago_tokens: ::phf::Map {
@ -7228,6 +7382,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("单曲", AlbumType::Single),
],
},
chan_prefix: "创建者:",
chan_suffix: "",
},
Language::ZhHk => Entry {
timeago_tokens: ::phf::Map {
@ -7296,6 +7452,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("單曲", AlbumType::Single),
],
},
chan_prefix: "來自",
chan_suffix: "",
},
Language::ZhTw => Entry {
timeago_tokens: ::phf::Map {
@ -7363,6 +7521,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("單曲", AlbumType::Single),
],
},
chan_prefix: "",
chan_suffix: "建立",
},
Language::Zu => Entry {
timeago_tokens: ::phf::Map {
@ -7467,6 +7627,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
("i-ep", AlbumType::Ep),
],
},
chan_prefix: "ka-",
chan_suffix: "",
},
}
}