diff --git a/codegen/src/abtest.rs b/codegen/src/abtest.rs index 811a90e..6408e58 100644 --- a/codegen/src/abtest.rs +++ b/codegen/src/abtest.rs @@ -15,6 +15,7 @@ pub enum ABTest { AttributedTextDescription = 1, ThreeTabChannelLayout = 2, ChannelHandlesInSearchResults = 3, + TrendsVideoTab = 4, } const TESTS_TO_RUN: [ABTest; 2] = [ @@ -38,6 +39,15 @@ struct QVideo<'a> { racy_check_ok: bool, } +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct QBrowse<'a> { + context: YTContext<'a>, + browse_id: &'a str, + #[serde(skip_serializing_if = "Option::is_none")] + params: Option<&'a str>, +} + pub async fn run_test( ab: ABTest, n: usize, @@ -72,6 +82,7 @@ pub async fn run_test( ABTest::ChannelHandlesInSearchResults => { channel_handles_in_search_results(&rp, &visitor_data).await } + ABTest::TrendsVideoTab => trends_video_tab(&rp, &visitor_data).await, } .unwrap(); pb.inc(1); @@ -170,3 +181,21 @@ pub async fn channel_handles_in_search_results(rp: &RustyPipe, visitor_data: &st _ => false, })) } + +pub async fn trends_video_tab(rp: &RustyPipe, visitor_data: &str) -> Result { + let query = rp.query().visitor_data(visitor_data); + let context = query.get_context(ClientType::Desktop, true, None).await; + let res = query + .raw( + ClientType::Desktop, + "browse", + &QBrowse { + context, + browse_id: "FEtrending", + params: None, + }, + ) + .await?; + + Ok(res.contains("\"4gIOGgxtb3N0X3BvcHVsYXI%3D\"")) +} diff --git a/codegen/src/main.rs b/codegen/src/main.rs index 003b386..82243c6 100644 --- a/codegen/src/main.rs +++ b/codegen/src/main.rs @@ -81,8 +81,14 @@ async fn main() { n, occurrences as f32 / n as f32 * 100.0 ); - eprintln!("visitor_data (present): {vd_present:?}"); - eprintln!("visitor_data (absent): {vd_absent:?}"); + eprintln!( + "visitor_data (present): {}", + vd_present.as_deref().unwrap_or("n/a") + ); + eprintln!( + "visitor_data (absent): {}", + vd_absent.as_deref().unwrap_or("n/a") + ); } None => { let res = abtest::run_all_tests(n, cli.concurrency).await; diff --git a/notes/AB_Tests.md b/notes/AB_Tests.md index 3af5c71..87bd86a 100644 --- a/notes/AB_Tests.md +++ b/notes/AB_Tests.md @@ -266,3 +266,23 @@ Note that channels without handles still use the old data model, even on the sam } } ``` + +## [4] Video tab on the Trending page + +- **Encountered on:** 1.04.2023 +- **Impact:** 🟢 Low +- **Endpoint:** browse (trending videos) + +YouTube moved the list of trending videos from the main *trending* page to a +separate tab (Videos). + +The video tab is fetched with the params `4gIOGgxtb3N0X3BvcHVsYXI%3D`. The data model +of the video tab did not change. + +**OLD** + +![A/B test 4 old screenshot](./_img/ab_4_old.png) + +**NEW** + +![A/B test 4 old screenshot](./_img/ab_4_new.png) diff --git a/notes/_img/ab_4_new.png b/notes/_img/ab_4_new.png new file mode 100644 index 0000000..9a106ad Binary files /dev/null and b/notes/_img/ab_4_new.png differ diff --git a/notes/_img/ab_4_old.png b/notes/_img/ab_4_old.png new file mode 100644 index 0000000..78dc837 Binary files /dev/null and b/notes/_img/ab_4_old.png differ diff --git a/src/client/trends.rs b/src/client/trends.rs index 5d43316..37922d1 100644 --- a/src/client/trends.rs +++ b/src/client/trends.rs @@ -8,7 +8,7 @@ use crate::{ util::TryRemove, }; -use super::{response, ClientType, MapResponse, QBrowse, RustyPipeQuery}; +use super::{response, ClientType, MapResponse, QBrowse, QBrowseParams, RustyPipeQuery}; impl RustyPipeQuery { /// Get the videos from the YouTube startpage @@ -32,9 +32,10 @@ impl RustyPipeQuery { /// Get the videos from the YouTube trending page pub async fn trending(&self) -> Result, Error> { let context = self.get_context(ClientType::Desktop, true, None).await; - let request_body = QBrowse { + let request_body = QBrowseParams { context, browse_id: "FEtrending", + params: "4gIOGgxtb3N0X3BvcHVsYXI%3D", }; self.execute_request::( diff --git a/tests/youtube.rs b/tests/youtube.rs index cfd5da6..ecbd431 100644 --- a/tests/youtube.rs +++ b/tests/youtube.rs @@ -91,12 +91,12 @@ fn get_player_from_client(#[case] client_type: ClientType, rp: RustyPipe) { .video_only_streams .into_iter() .find(|s| s.itag == 398) - .unwrap(); + .expect("video stream not found"); let audio = player_data .audio_streams .into_iter() .find(|s| s.itag == 251) - .unwrap(); + .expect("audio stream not found"); assert_approx(video.bitrate as f64, 1340829.0); assert_approx(video.average_bitrate as f64, 1233444.0); @@ -1172,7 +1172,7 @@ fn startpage(rp: RustyPipe) { fn trending(rp: RustyPipe) { let result = tokio_test::block_on(rp.query().trending()).unwrap(); - assert_gte(result.len(), 50, "items"); + assert_gte(result.len(), 40, "items"); } //#MUSIC