fix: A/B test 21: music album recommendations

This commit is contained in:
ThetaDev 2025-02-26 15:20:25 +01:00
parent 544782f8de
commit 6737512f5f
No known key found for this signature in database
GPG key ID: E319D3C5148D65B6
13 changed files with 8680 additions and 113 deletions

View file

@ -40,13 +40,11 @@ pub enum ABTest {
MusicPlaylistFacepile = 18,
MusicAlbumGroupsReordered = 19,
MusicContinuationItemRenderer = 20,
AlbumRecommends = 21,
}
/// List of active A/B tests that are run when none is manually specified
const TESTS_TO_RUN: &[ABTest] = &[
ABTest::MusicAlbumGroupsReordered,
ABTest::MusicContinuationItemRenderer,
];
const TESTS_TO_RUN: &[ABTest] = &[ABTest::MusicAlbumGroupsReordered, ABTest::AlbumRecommends];
#[derive(Debug, Serialize, Deserialize)]
pub struct ABTestRes {
@ -121,6 +119,7 @@ pub async fn run_test(
ABTest::MusicContinuationItemRenderer => {
music_continuation_item_renderer(&query).await
}
ABTest::AlbumRecommends => album_recommends(&query).await,
}
.unwrap();
pb.inc(1);
@ -443,3 +442,18 @@ pub async fn music_continuation_item_renderer(rp: &RustyPipeQuery) -> Result<boo
.await?;
Ok(res.contains("\"continuationItemRenderer\""))
}
pub async fn album_recommends(rp: &RustyPipeQuery) -> Result<bool> {
let id = "MPREb_u1I69lSAe5v";
let res = rp
.raw(
ClientType::DesktopMusic,
"browse",
&QBrowse {
browse_id: id,
params: None,
},
)
.await?;
Ok(res.contains("\"musicCarouselShelfRenderer\""))
}

View file

@ -0,0 +1,130 @@
use std::{collections::BTreeMap, fs::File, io::BufReader};
use path_macro::path;
use rustypipe::{
client::{ClientType, RustyPipe},
param::{Language, LANGUAGES},
};
use serde::Deserialize;
use serde_with::rust::deserialize_ignore_any;
use crate::{
model::{QBrowse, SectionList, TextRuns},
util::{self, DICT_DIR},
};
pub async fn collect_album_versions_titles() {
let json_path = path!(*DICT_DIR / "other_versions_titles.json");
let mut res = BTreeMap::new();
let rp = RustyPipe::new();
for lang in LANGUAGES {
let query = QBrowse {
browse_id: "MPREb_nlBWQROfvjo",
params: None,
};
let raw_resp = rp
.query()
.lang(lang)
.raw(ClientType::DesktopMusic, "browse", &query)
.await
.unwrap();
let data = serde_json::from_str::<AlbumData>(&raw_resp).unwrap();
let title = data
.contents
.two_column_browse_results_renderer
.secondary_contents
.section_list_renderer
.contents
.into_iter()
.find_map(|x| match x {
ItemSection::MusicCarouselShelfRenderer(music_carousel_shelf) => {
Some(music_carousel_shelf)
}
ItemSection::None => None,
})
.expect("other versions")
.header
.expect("header")
.music_carousel_shelf_basic_header_renderer
.title
.runs
.into_iter()
.next()
.unwrap()
.text;
println!("{lang}: {title}");
res.insert(lang, title);
}
let file = File::create(json_path).unwrap();
serde_json::to_writer_pretty(file, &res).unwrap();
}
pub fn write_samples_to_dict() {
let json_path = path!(*DICT_DIR / "other_versions_titles.json");
let json_file = File::open(json_path).unwrap();
let collected: BTreeMap<Language, String> =
serde_json::from_reader(BufReader::new(json_file)).unwrap();
let mut dict = util::read_dict();
let langs = dict.keys().copied().collect::<Vec<_>>();
for lang in langs {
let dict_entry = dict.entry(lang).or_default();
let e = collected.get(&lang).unwrap();
assert_eq!(e, e.trim());
dict_entry.album_versions_title = e.to_owned();
for lang in &dict_entry.equivalent {
let ee = collected.get(lang).unwrap();
if ee != e {
panic!("equivalent lang conflict, lang: {lang}");
}
}
}
util::write_dict(dict);
}
#[derive(Debug, Deserialize)]
struct AlbumData {
contents: AlbumDataContents,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct AlbumDataContents {
two_column_browse_results_renderer: X1,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct X1 {
secondary_contents: SectionList<ItemSection>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
enum ItemSection {
MusicCarouselShelfRenderer(MusicCarouselShelf),
#[serde(other, deserialize_with = "deserialize_ignore_any")]
None,
}
#[derive(Debug, Deserialize)]
struct MusicCarouselShelf {
header: Option<MusicCarouselShelfHeader>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct MusicCarouselShelfHeader {
music_carousel_shelf_basic_header_renderer: MusicCarouselShelfHeaderRenderer,
}
#[derive(Debug, Deserialize)]
struct MusicCarouselShelfHeaderRenderer {
title: TextRuns,
}

View file

@ -90,6 +90,8 @@ pub(crate) struct Entry {
pub chan_prefix: &'static str,
/// Channel name suffix on playlist pages
pub chan_suffix: &'static str,
/// "Other versions" title on album pages
pub album_versions_title: &'static str,
}
"#;
@ -178,8 +180,8 @@ pub(crate) fn entry(lang: Language) -> Entry {
.to_string()
.replace('\n', "\n ");
write!(code_timeago_tokens, "{} => Entry {{\n timeago_tokens: {},\n month_before_day: {:?},\n months: {},\n timeago_nd_tokens: {},\n comma_decimal: {:?},\n number_tokens: {},\n number_nd_tokens: {},\n album_types: {},\n chan_prefix: {:?},\n chan_suffix: {:?},\n }},\n ",
selector, code_ta_tokens, entry.month_before_day, code_months, code_ta_nd_tokens, entry.comma_decimal, code_number_tokens, code_number_nd_tokens, code_album_types, entry.chan_prefix, entry.chan_suffix).unwrap();
write!(code_timeago_tokens, "{} => Entry {{\n timeago_tokens: {},\n month_before_day: {:?},\n months: {},\n timeago_nd_tokens: {},\n comma_decimal: {:?},\n number_tokens: {},\n number_nd_tokens: {},\n album_types: {},\n chan_prefix: {:?},\n chan_suffix: {:?},\n album_versions_title: {:?},\n }},\n ",
selector, code_ta_tokens, entry.month_before_day, code_months, code_ta_nd_tokens, entry.comma_decimal, code_number_tokens, code_number_nd_tokens, code_album_types, entry.chan_prefix, entry.chan_suffix, entry.album_versions_title).unwrap();
}
code_timeago_tokens = code_timeago_tokens.trim_end().to_owned() + "\n }\n}\n";

View file

@ -2,6 +2,7 @@
mod abtest;
mod collect_album_types;
mod collect_album_versions_titles;
mod collect_chan_prefixes;
mod collect_history_dates;
mod collect_large_numbers;
@ -34,12 +35,14 @@ enum Commands {
CollectHistoryDates,
CollectMusicHistoryDates,
CollectChanPrefixes,
CollectAlbumVersionsTitles,
ParsePlaylistDates,
ParseHistoryDates,
ParseLargeNumbers,
ParseAlbumTypes,
ParseVideoDurations,
ParseChanPrefixes,
ParseAlbumVersionsTitles,
GenLocales,
GenDict,
DownloadTestfiles,
@ -58,28 +61,25 @@ async fn main() {
match cli.command {
Commands::CollectPlaylistDates => {
collect_playlist_dates::collect_dates(cli.concurrency).await;
collect_playlist_dates::collect_dates(cli.concurrency).await
}
Commands::CollectLargeNumbers => {
collect_large_numbers::collect_large_numbers(cli.concurrency).await;
collect_large_numbers::collect_large_numbers(cli.concurrency).await
}
Commands::CollectAlbumTypes => {
collect_album_types::collect_album_types(cli.concurrency).await;
collect_album_types::collect_album_types(cli.concurrency).await
}
Commands::CollectVideoDurations => {
collect_video_durations::collect_video_durations(cli.concurrency).await;
collect_video_durations::collect_video_durations(cli.concurrency).await
}
Commands::CollectVideoDates => {
collect_video_dates::collect_video_dates(cli.concurrency).await;
collect_video_dates::collect_video_dates(cli.concurrency).await
}
Commands::CollectHistoryDates => {
collect_history_dates::collect_dates().await;
}
Commands::CollectMusicHistoryDates => {
collect_history_dates::collect_dates_music().await;
}
Commands::CollectChanPrefixes => {
collect_chan_prefixes::collect_chan_prefixes().await;
Commands::CollectHistoryDates => collect_history_dates::collect_dates().await,
Commands::CollectMusicHistoryDates => collect_history_dates::collect_dates_music().await,
Commands::CollectChanPrefixes => collect_chan_prefixes::collect_chan_prefixes().await,
Commands::CollectAlbumVersionsTitles => {
collect_album_versions_titles::collect_album_versions_titles().await
}
Commands::ParsePlaylistDates => collect_playlist_dates::write_samples_to_dict(),
Commands::ParseHistoryDates => collect_history_dates::write_samples_to_dict(),
@ -87,9 +87,10 @@ async fn main() {
Commands::ParseAlbumTypes => collect_album_types::write_samples_to_dict(),
Commands::ParseVideoDurations => collect_video_durations::parse_video_durations(),
Commands::ParseChanPrefixes => collect_chan_prefixes::write_samples_to_dict(),
Commands::GenLocales => {
gen_locales::generate_locales().await;
Commands::ParseAlbumVersionsTitles => {
collect_album_versions_titles::write_samples_to_dict()
}
Commands::GenLocales => gen_locales::generate_locales().await,
Commands::GenDict => gen_dictionary::generate_dictionary(),
Commands::DownloadTestfiles => download_testfiles::download_testfiles().await,
Commands::AbTest { id, n } => {

View file

@ -61,6 +61,8 @@ pub struct DictEntry {
pub chan_prefix: String,
/// Channel name suffix on playlist pages
pub chan_suffix: String,
/// "Other versions" title on album pages
pub album_versions_title: String,
}
/// Parsed TimeAgo string, contains amount and time unit.