test: add integration tests for YTM albums/search

This commit is contained in:
ThetaDev 2022-11-01 15:37:06 +01:00
parent c7ae2f8531
commit b38bc711c7
16 changed files with 3743 additions and 286 deletions

View file

@ -63,3 +63,10 @@ Dec PL1J-6JOckZtHo91uApeb10Qlf2XhkfM-9 24.12.2021
10e3: 5K UC0QEucPrn0-Ddi3JBTcs5Kw
10e2: 388 UCllyEQfcoiPN68zHv6mGHDQ
10e1: 37 UCGiJh0NZ52wRhYKYnuZI08Q
# Album types
Album: MPREb_nlBWQROfvjo
Single: MPREb_bHfHGoy7vuv
EP: MPREb_u1I69lSAe5v
Show: MPREb_cwzk8EUwypZ
Audiobook: MPREb_gaoNzsQHedo

View file

@ -128,7 +128,7 @@ impl MapResponse<Paginator<MusicItem>> for response::MusicContinuation {
_deobf: Option<&crate::deobfuscate::Deobfuscator>,
) -> Result<MapResult<Paginator<MusicItem>>, ExtractionError> {
let mut mapper = MusicListMapper::new(lang);
let mut shelf = self.continuation_contents.music_playlist_shelf_continuation;
let mut shelf = self.continuation_contents.music_shelf_continuation;
mapper.map_response(shelf.contents);
let map_res = mapper.items();

View file

@ -183,7 +183,8 @@ pub(crate) struct MusicContinuation {
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ContinuationContents {
pub music_playlist_shelf_continuation: MusicShelf,
#[serde(alias = "musicPlaylistShelfContinuation")]
pub music_shelf_continuation: MusicShelf,
}
/*

View file

@ -36,13 +36,47 @@ pub(crate) struct WatchEndpoint {
pub start_time_seconds: u32,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
#[derive(Debug)]
pub(crate) struct BrowseEndpoint {
pub browse_id: String,
pub browse_endpoint_context_supported_configs: Option<BrowseEndpointConfig>,
}
impl<'de> Deserialize<'de> for BrowseEndpoint {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct BEp {
pub browse_id: String,
pub browse_endpoint_context_supported_configs: Option<BrowseEndpointConfig>,
}
let bep = BEp::deserialize(deserializer)?;
// Remove the VL prefix from the playlist id
let browse_id = bep
.browse_endpoint_context_supported_configs
.as_ref()
.and_then(
|cfg| match cfg.browse_endpoint_context_music_config.page_type {
PageType::Playlist => bep.browse_id.strip_prefix("VL"),
_ => None,
},
)
.map(str::to_owned)
.unwrap_or(bep.browse_id);
Ok(Self {
browse_id,
browse_endpoint_context_supported_configs: bep
.browse_endpoint_context_supported_configs,
})
}
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct UrlEndpoint {
@ -75,9 +109,12 @@ pub(crate) struct WebCommandMetadata {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
pub(crate) enum PageType {
#[serde(rename = "MUSIC_PAGE_TYPE_ARTIST")]
#[serde(
rename = "MUSIC_PAGE_TYPE_ARTIST",
alias = "MUSIC_PAGE_TYPE_AUDIOBOOK_ARTIST"
)]
Artist,
#[serde(rename = "MUSIC_PAGE_TYPE_ALBUM")]
#[serde(rename = "MUSIC_PAGE_TYPE_ALBUM", alias = "MUSIC_PAGE_TYPE_AUDIOBOOK")]
Album,
#[serde(
rename = "WEB_PAGE_TYPE_CHANNEL",

View file

@ -356,7 +356,7 @@ MusicSearchResult(
],
playlists: [
MusicPlaylistItem(
id: "VLPLk76iSbFqNJsu_Gozn9SkEXxQ7t-bpXid",
id: "PLk76iSbFqNJsu_Gozn9SkEXxQ7t-bpXid",
name: "IRMA MIRTILLA Black Mamba",
thumbnail: [
Thumbnail(
@ -383,7 +383,7 @@ MusicSearchResult(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPLIL9Q2jz6euDEJZKHd4QaG4iic944_vKY",
id: "PLIL9Q2jz6euDEJZKHd4QaG4iic944_vKY",
name: "Black Mamba",
thumbnail: [
Thumbnail(
@ -410,7 +410,7 @@ MusicSearchResult(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPLinm7-cvTdN7RqadpfNrncUGqkdyKNpn6",
id: "PLinm7-cvTdN7RqadpfNrncUGqkdyKNpn6",
name: "Black Mamba",
thumbnail: [
Thumbnail(

View file

@ -7,7 +7,7 @@ MusicSearchFiltered(
count: None,
items: [
MusicPlaylistItem(
id: "VLPLDIoUOhQQPlVr3qepMVRsDe4T8vNQsvno",
id: "PLDIoUOhQQPlVr3qepMVRsDe4T8vNQsvno",
name: "Türkçe Pop Şarkılar 2022 - Yeni Hit Şarkılar 2022",
thumbnail: [
Thumbnail(
@ -34,7 +34,7 @@ MusicSearchFiltered(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPLI_7Mg2Z_-4Lf7IYeiTEOV8HBn-nMqz5N",
id: "PLI_7Mg2Z_-4Lf7IYeiTEOV8HBn-nMqz5N",
name: "Pop 2022 ♫ Mix Pop En Ingles (English Pop Songs 2022)",
thumbnail: [
Thumbnail(
@ -61,7 +61,7 @@ MusicSearchFiltered(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPLTC7VQ12-9raqhLCx1S1E_ic35t94dj28",
id: "PLTC7VQ12-9raqhLCx1S1E_ic35t94dj28",
name: "Pop & Pop Rock Hits 🔥 2012 - 2022",
thumbnail: [
Thumbnail(
@ -88,7 +88,7 @@ MusicSearchFiltered(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPLPssMy7y6ygl-Kzi9Mc7TVFFPWwyAXIwW",
id: "PLPssMy7y6ygl-Kzi9Mc7TVFFPWwyAXIwW",
name: "Bester deutscher Pop/Best of German Pop music",
thumbnail: [
Thumbnail(
@ -115,7 +115,7 @@ MusicSearchFiltered(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPLMC9KNkIncKtPzgY-5rmhvj7fax8fdxoj",
id: "PLMC9KNkIncKtPzgY-5rmhvj7fax8fdxoj",
name: "Pop Music Playlist - Timeless Pop Songs (Updated Weekly 2022)",
thumbnail: [
Thumbnail(
@ -142,7 +142,7 @@ MusicSearchFiltered(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPLX6L4t7t6ZanfCJ1wBxRdGZ_mk9ygmKqo",
id: "PLX6L4t7t6ZanfCJ1wBxRdGZ_mk9ygmKqo",
name: "Deutsch Pop Hits NEU 2022",
thumbnail: [
Thumbnail(
@ -169,7 +169,7 @@ MusicSearchFiltered(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPL1jMjCxDqxLvomtUMbqa_4pAjMmzpfgUy",
id: "PL1jMjCxDqxLvomtUMbqa_4pAjMmzpfgUy",
name: "Top 100 Músicas Internacionais Pop 2022",
thumbnail: [
Thumbnail(
@ -196,7 +196,7 @@ MusicSearchFiltered(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPLgRdph0qPLy53IhYrQLPpATDDA2TpFey5",
id: "PLgRdph0qPLy53IhYrQLPpATDDA2TpFey5",
name: "Teen-Pop 90-2000",
thumbnail: [
Thumbnail(
@ -223,7 +223,7 @@ MusicSearchFiltered(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPLV9Y77TQ4I9dXpIF-r54ZTLM5T7C4k5jf",
id: "PLV9Y77TQ4I9dXpIF-r54ZTLM5T7C4k5jf",
name: "Most Popular Soft Pop Songs of 2022 Playlist - Best Soft Pop Music Hits of the Year 2022",
thumbnail: [
Thumbnail(
@ -250,7 +250,7 @@ MusicSearchFiltered(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPLlYKDqBVDxX2TXAf8ruuF3FVZgzF3wCTx",
id: "PLlYKDqBVDxX2TXAf8ruuF3FVZgzF3wCTx",
name: "Pop 2022 ♫ Best Pop Songs Playlist 2022",
thumbnail: [
Thumbnail(
@ -277,7 +277,7 @@ MusicSearchFiltered(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPLMC9KNkIncKvYin_USF1qoJQnIyMAfRxl",
id: "PLMC9KNkIncKvYin_USF1qoJQnIyMAfRxl",
name: "Best Pop Songs of All Time: Playlist of Good Songs (Throwback Hits & Pop Music 2022)",
thumbnail: [
Thumbnail(
@ -304,7 +304,7 @@ MusicSearchFiltered(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPLKUA473MWUv2jmkqIxzQR3YL4kuPArj4G",
id: "PLKUA473MWUv2jmkqIxzQR3YL4kuPArj4G",
name: "Best Pop Music for Office Good Songs for Work: Pop Hits Collection (Playlist Updated in 2022)",
thumbnail: [
Thumbnail(
@ -331,7 +331,7 @@ MusicSearchFiltered(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPLkqz3S84Tw-RfPS9HHi3MRmrinOBKxIr8",
id: "PLkqz3S84Tw-RfPS9HHi3MRmrinOBKxIr8",
name: "Top POP Hits 2022 Biggest Pop Music Videos - Vevo",
thumbnail: [
Thumbnail(
@ -358,7 +358,7 @@ MusicSearchFiltered(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPLEBX04z4REEaHbC7ghEqFwwL4ptIw8qaZ",
id: "PLEBX04z4REEaHbC7ghEqFwwL4ptIw8qaZ",
name: "Playlist Pop Rock 2022 ♫ Musique Pop Rock 2022 Nouveauté",
thumbnail: [
Thumbnail(
@ -385,7 +385,7 @@ MusicSearchFiltered(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPLMC9KNkIncKtGvr2kFRuXBVmBev6cAJ2u",
id: "PLMC9KNkIncKtGvr2kFRuXBVmBev6cAJ2u",
name: "Best Pop Music Videos - Top Pop Hits Playlist",
thumbnail: [
Thumbnail(
@ -412,7 +412,7 @@ MusicSearchFiltered(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPL9NY5axt700FjL6HlEhqlMFT1gjMGVBgy",
id: "PL9NY5axt700FjL6HlEhqlMFT1gjMGVBgy",
name: "Pop Music Songs 2022",
thumbnail: [
Thumbnail(
@ -439,7 +439,7 @@ MusicSearchFiltered(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPLTariUrSxayvzHkCnEiX5-NSeFgLTqzHE",
id: "PLTariUrSxayvzHkCnEiX5-NSeFgLTqzHE",
name: "Pop Music Playlist 2022",
thumbnail: [
Thumbnail(
@ -466,7 +466,7 @@ MusicSearchFiltered(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPLB8HqqmpyIBcPirb5lDm-ol01trE6dOuM",
id: "PLB8HqqmpyIBcPirb5lDm-ol01trE6dOuM",
name: "Mix Internacional 2022 ♫ As Mais Tocadas 2022 Internacionais (Playlist Musicas Pop As Melhores)",
thumbnail: [
Thumbnail(
@ -493,7 +493,7 @@ MusicSearchFiltered(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPLkqz3S84Tw-Qlth8bNBpXGO7L4J1PkCW7",
id: "PLkqz3S84Tw-Qlth8bNBpXGO7L4J1PkCW7",
name: "POP INTERNATIONAL HOTLIST - Pop Hits 2022- Brandneue Pop Videos | Vevo Playlist",
thumbnail: [
Thumbnail(
@ -520,7 +520,7 @@ MusicSearchFiltered(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPLkqz3S84Tw-SvUSUY5Ov8LTP3KWuWxFab",
id: "PLkqz3S84Tw-SvUSUY5Ov8LTP3KWuWxFab",
name: "Cheesy Sing-Along Pop Hits!",
thumbnail: [
Thumbnail(

View file

@ -7,7 +7,7 @@ MusicSearchFiltered(
count: None,
items: [
MusicPlaylistItem(
id: "VLRDCLAK5uy_nmS3YoxSwVVQk9lEQJ0UX4ZCjXsW_psU8",
id: "RDCLAK5uy_nmS3YoxSwVVQk9lEQJ0UX4ZCjXsW_psU8",
name: "Pop\'s Biggest Hits",
thumbnail: [
Thumbnail(
@ -36,7 +36,7 @@ MusicSearchFiltered(
from_ytm: true,
),
MusicPlaylistItem(
id: "VLRDCLAK5uy_nfs_t4FUu00E5ED6lveEBBX1VMYe1mFjk",
id: "RDCLAK5uy_nfs_t4FUu00E5ED6lveEBBX1VMYe1mFjk",
name: "Dance-Pop Bangers",
thumbnail: [
Thumbnail(
@ -65,7 +65,7 @@ MusicSearchFiltered(
from_ytm: true,
),
MusicPlaylistItem(
id: "VLRDCLAK5uy_nH_fdBVCcbNaVwi_tmZajZRq-ekddiuFY",
id: "RDCLAK5uy_nH_fdBVCcbNaVwi_tmZajZRq-ekddiuFY",
name: "Pop Meets Country",
thumbnail: [
Thumbnail(
@ -94,7 +94,7 @@ MusicSearchFiltered(
from_ytm: true,
),
MusicPlaylistItem(
id: "VLRDCLAK5uy_lb6CVU6S4uVugLVNTU9WhqfaomWAgnho4",
id: "RDCLAK5uy_lb6CVU6S4uVugLVNTU9WhqfaomWAgnho4",
name: "Shout-Out Pop Hits",
thumbnail: [
Thumbnail(
@ -123,7 +123,7 @@ MusicSearchFiltered(
from_ytm: true,
),
MusicPlaylistItem(
id: "VLRDCLAK5uy_mfdqvCAl8wodlx2P2_Ai2gNkiRDAufkkI",
id: "RDCLAK5uy_mfdqvCAl8wodlx2P2_Ai2gNkiRDAufkkI",
name: "Happy Pop Hits",
thumbnail: [
Thumbnail(
@ -152,7 +152,7 @@ MusicSearchFiltered(
from_ytm: true,
),
MusicPlaylistItem(
id: "VLRDCLAK5uy_nHSqCJjDrW9HBhCNdF6tWPdnOMngOv0wA",
id: "RDCLAK5uy_nHSqCJjDrW9HBhCNdF6tWPdnOMngOv0wA",
name: "Pop Gold",
thumbnail: [
Thumbnail(
@ -181,7 +181,7 @@ MusicSearchFiltered(
from_ytm: true,
),
MusicPlaylistItem(
id: "VLRDCLAK5uy_kLB769E3eFSzgy4fbpu6-1YPLh90b0JAY",
id: "RDCLAK5uy_kLB769E3eFSzgy4fbpu6-1YPLh90b0JAY",
name: "Pop Hotlist",
thumbnail: [
Thumbnail(
@ -210,7 +210,7 @@ MusicSearchFiltered(
from_ytm: true,
),
MusicPlaylistItem(
id: "VLRDCLAK5uy_m_0U5VQNyyzwwH1lRi7cPAAGXqNQnAOqY",
id: "RDCLAK5uy_m_0U5VQNyyzwwH1lRi7cPAAGXqNQnAOqY",
name: "Laid-Back Sofa Pop",
thumbnail: [
Thumbnail(
@ -239,7 +239,7 @@ MusicSearchFiltered(
from_ytm: true,
),
MusicPlaylistItem(
id: "VLRDCLAK5uy_mHW5bcduhjB-PkTePAe6EoRMj1xNT8gzY",
id: "RDCLAK5uy_mHW5bcduhjB-PkTePAe6EoRMj1xNT8gzY",
name: "K-Pop Girl Crush",
thumbnail: [
Thumbnail(
@ -268,7 +268,7 @@ MusicSearchFiltered(
from_ytm: true,
),
MusicPlaylistItem(
id: "VLRDCLAK5uy_nWFLXjO1mIJnP4wAmxjfDS40Zmu6nF9DU",
id: "RDCLAK5uy_nWFLXjO1mIJnP4wAmxjfDS40Zmu6nF9DU",
name: "Se Habla Pop",
thumbnail: [
Thumbnail(
@ -297,7 +297,7 @@ MusicSearchFiltered(
from_ytm: true,
),
MusicPlaylistItem(
id: "VLRDCLAK5uy_lHu_HYAQ0A-OfCwhuky240HOdM1UduuyA",
id: "RDCLAK5uy_lHu_HYAQ0A-OfCwhuky240HOdM1UduuyA",
name: "Presenting Pop Smoke",
thumbnail: [
Thumbnail(
@ -326,7 +326,7 @@ MusicSearchFiltered(
from_ytm: true,
),
MusicPlaylistItem(
id: "VLRDCLAK5uy_lw21MuKaqFsDBIPZSbRmZcoDrHmV_c6uY",
id: "RDCLAK5uy_lw21MuKaqFsDBIPZSbRmZcoDrHmV_c6uY",
name: "Happy latin pop",
thumbnail: [
Thumbnail(
@ -355,7 +355,7 @@ MusicSearchFiltered(
from_ytm: true,
),
MusicPlaylistItem(
id: "VLRDCLAK5uy_lY6JFrs7W9yIhFjUN_yxQ_ubkjcrqQaVs",
id: "RDCLAK5uy_lY6JFrs7W9yIhFjUN_yxQ_ubkjcrqQaVs",
name: "Bedroom Pop",
thumbnail: [
Thumbnail(
@ -384,7 +384,7 @@ MusicSearchFiltered(
from_ytm: true,
),
MusicPlaylistItem(
id: "VLRDCLAK5uy_nbK9qSkqYZvtMXH1fLCMmC1yn8HEm0W90",
id: "RDCLAK5uy_nbK9qSkqYZvtMXH1fLCMmC1yn8HEm0W90",
name: "J-Hits!",
thumbnail: [
Thumbnail(
@ -413,7 +413,7 @@ MusicSearchFiltered(
from_ytm: true,
),
MusicPlaylistItem(
id: "VLRDCLAK5uy_kIcfhFcnJ9fdkKHw96Gvr6Q0_bAyWiZbM",
id: "RDCLAK5uy_kIcfhFcnJ9fdkKHw96Gvr6Q0_bAyWiZbM",
name: "Reiwa Era J-Pop",
thumbnail: [
Thumbnail(
@ -442,7 +442,7 @@ MusicSearchFiltered(
from_ytm: true,
),
MusicPlaylistItem(
id: "VLRDCLAK5uy_nDL8KeBrUagwyISwNmyEiSfYgz1gVCesg",
id: "RDCLAK5uy_nDL8KeBrUagwyISwNmyEiSfYgz1gVCesg",
name: "Mellow Pop Classics",
thumbnail: [
Thumbnail(
@ -471,7 +471,7 @@ MusicSearchFiltered(
from_ytm: true,
),
MusicPlaylistItem(
id: "VLRDCLAK5uy_l7K78k4EkjcFojhd1617rmUjY-aet6-t0",
id: "RDCLAK5uy_l7K78k4EkjcFojhd1617rmUjY-aet6-t0",
name: "K-Pop Party Hits",
thumbnail: [
Thumbnail(
@ -500,7 +500,7 @@ MusicSearchFiltered(
from_ytm: true,
),
MusicPlaylistItem(
id: "VLRDCLAK5uy_nCVF_zUZizzRcojIUuYmaXxMoPgg2WMDo",
id: "RDCLAK5uy_nCVF_zUZizzRcojIUuYmaXxMoPgg2WMDo",
name: "Klangfarbe: German Pop Hits",
thumbnail: [
Thumbnail(
@ -529,7 +529,7 @@ MusicSearchFiltered(
from_ytm: true,
),
MusicPlaylistItem(
id: "VLRDCLAK5uy_nGKsW16ocmP8l8wL1oMIyR53cu4N5fyKI",
id: "RDCLAK5uy_nGKsW16ocmP8l8wL1oMIyR53cu4N5fyKI",
name: "GIF Pop BR",
thumbnail: [
Thumbnail(
@ -558,7 +558,7 @@ MusicSearchFiltered(
from_ytm: true,
),
MusicPlaylistItem(
id: "VLRDCLAK5uy_mVJ3RRi_YBfUJnZnQxLAedQQcXHujbUcg",
id: "RDCLAK5uy_mVJ3RRi_YBfUJnZnQxLAedQQcXHujbUcg",
name: "Pump-Up Pop",
thumbnail: [
Thumbnail(

View file

@ -364,7 +364,7 @@ MusicSearchResult(
],
playlists: [
MusicPlaylistItem(
id: "VLPL46jG0sBz4EabjYqv0tVEd1RvrZRjfJ8m",
id: "PL46jG0sBz4EabjYqv0tVEd1RvrZRjfJ8m",
name: "Lieblingsmensch",
thumbnail: [
Thumbnail(
@ -391,7 +391,7 @@ MusicSearchResult(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPL-2Z-8cOvEixQ2EKumbXXW8XivJG6U7-D",
id: "PL-2Z-8cOvEixQ2EKumbXXW8XivJG6U7-D",
name: "Namika - Lieblingsmensch",
thumbnail: [
Thumbnail(
@ -418,7 +418,7 @@ MusicSearchResult(
from_ytm: false,
),
MusicPlaylistItem(
id: "VLPLDnJKuRrOR5sx_jZ8-dsweq1OdayL_kpz",
id: "PLDnJKuRrOR5sx_jZ8-dsweq1OdayL_kpz",
name: "Lieblingsmensch",
thumbnail: [
Thumbnail(

View file

@ -5,7 +5,7 @@ mod ordering;
mod paginator;
pub mod richtext;
pub(crate) use convert::FromYtItem;
pub use convert::FromYtItem;
pub use paginator::Paginator;
use serde_with::serde_as;
@ -955,6 +955,7 @@ pub struct MusicPlaylistItem {
/// YouTube Music album type
#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub enum AlbumType {
/// Regular album (default)
#[default]
@ -1031,9 +1032,13 @@ pub struct MusicAlbum {
/// YouTube Music search result
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct MusicSearchResult {
/// Found tracks
pub tracks: Vec<TrackItem>,
/// Found albums
pub albums: Vec<AlbumItem>,
/// Found artists
pub artists: Vec<ArtistItem>,
/// Found playlists
pub playlists: Vec<MusicPlaylistItem>,
/// Corrected search query
///
@ -1041,6 +1046,8 @@ pub struct MusicSearchResult {
/// for the corrected search term and displays it on top of the
/// search results page.
pub corrected_query: Option<String>,
/// Order of the item sections of the search page, starting with
/// the most relevant.
pub order: Vec<MusicEntityType>,
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,108 @@
---
source: tests/youtube.rs
expression: album
---
MusicAlbum(
id: "MPREb_u1I69lSAe5v",
playlist_id: Some("OLAK5uy_lGP_zv0vJDUlecQDzugUJmjcF7pvyVNyY"),
name: "Waldbrand",
cover: "[cover]",
artists: [
ChannelId(
id: "UCpJyCbFbdTrx0M90HCNBHFQ",
name: "Madeline Juno",
),
],
artists_txt: "Madeline Juno",
album_type: Ep,
year: Some(2016),
by_va: false,
tracks: [
TrackItem(
id: "aGd3VKSOTxY",
title: "Ich wache auf",
duration: 221,
cover: [],
artists: [
ChannelId(
id: "UCpJyCbFbdTrx0M90HCNBHFQ",
name: "Madeline Juno",
),
],
artists_txt: Some("Madeline Juno"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(1),
),
TrackItem(
id: "lhPOMUjV4rE",
title: "Waldbrand",
duration: 208,
cover: [],
artists: [
ChannelId(
id: "UCpJyCbFbdTrx0M90HCNBHFQ",
name: "Madeline Juno",
),
],
artists_txt: Some("Madeline Juno"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(2),
),
TrackItem(
id: "Bu26uFtpt58",
title: "Verlernt",
duration: 223,
cover: [],
artists: [
ChannelId(
id: "UCpJyCbFbdTrx0M90HCNBHFQ",
name: "Madeline Juno",
),
],
artists_txt: Some("Madeline Juno"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(3),
),
TrackItem(
id: "RgwNqqiVqdY",
title: "In Farbe",
duration: 221,
cover: [],
artists: [
ChannelId(
id: "UCpJyCbFbdTrx0M90HCNBHFQ",
name: "Madeline Juno",
),
],
artists_txt: Some("Madeline Juno"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(4),
),
TrackItem(
id: "2TuOh30XbCI",
title: "Stadt im Hinterland",
duration: 197,
cover: [],
artists: [
ChannelId(
id: "UCpJyCbFbdTrx0M90HCNBHFQ",
name: "Madeline Juno",
),
],
artists_txt: Some("Madeline Juno"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(5),
),
],
variants: [],
)

View file

@ -0,0 +1,355 @@
---
source: tests/youtube.rs
expression: album
---
MusicAlbum(
id: "MPREb_nlBWQROfvjo",
playlist_id: Some("OLAK5uy_myZkBX2d2TzcrlQhIwLy3hCj2MkAMaPR4"),
name: "Märchen enden gut",
cover: "[cover]",
artists: [
ChannelId(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh",
),
],
artists_txt: "Oonagh",
album_type: Album,
year: Some(2016),
by_va: false,
tracks: [
TrackItem(
id: "g0iRiJ_ck48",
title: "Aulë und Yavanna",
duration: 216,
cover: [],
artists: [
ChannelId(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh",
),
],
artists_txt: Some("Oonagh"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(1),
),
TrackItem(
id: "rREEBXp0y9s",
title: "Numenor",
duration: 224,
cover: [],
artists: [
ChannelId(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh",
),
],
artists_txt: Some("Oonagh"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(2),
),
TrackItem(
id: "zvU5Y8Q19hU",
title: "Das Mädchen und die Liebe (feat. Santiano)",
duration: 176,
cover: [],
artists: [
ChannelId(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh",
),
],
artists_txt: Some("Oonagh"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(3),
),
TrackItem(
id: "ARKLrzzTQA0",
title: "Niënna",
duration: 215,
cover: [],
artists: [
ChannelId(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh",
),
],
artists_txt: Some("Oonagh"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(4),
),
TrackItem(
id: "tstLgN8A_Ng",
title: "Der fahle Mond",
duration: 268,
cover: [],
artists: [
ChannelId(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh",
),
],
artists_txt: Some("Oonagh"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(5),
),
TrackItem(
id: "k2DjgQOY3Ts",
title: "Weise den Weg",
duration: 202,
cover: [],
artists: [
ChannelId(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh",
),
],
artists_txt: Some("Oonagh"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(6),
),
TrackItem(
id: "azHwhecxEsI",
title: "Zeit der Sommernächte",
duration: 185,
cover: [],
artists: [
ChannelId(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh",
),
],
artists_txt: Some("Oonagh"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(7),
),
TrackItem(
id: "_FcsdYIQ2co",
title: "Märchen enden gut",
duration: 226,
cover: [],
artists: [
ChannelId(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh",
),
],
artists_txt: Some("Oonagh"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(8),
),
TrackItem(
id: "27bOWEbshyE",
title: "Das Mädchen und der Tod",
duration: 207,
cover: [],
artists: [
ChannelId(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh",
),
],
artists_txt: Some("Oonagh"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(9),
),
TrackItem(
id: "riD_3oZwt8w",
title: "Wir sehn uns wieder",
duration: 211,
cover: [],
artists: [
ChannelId(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh",
),
],
artists_txt: Some("Oonagh"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(10),
),
TrackItem(
id: "8GNvjF3no9s",
title: "Tanz mit mir",
duration: 179,
cover: [],
artists: [
ChannelId(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh",
),
],
artists_txt: Some("Oonagh"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(11),
),
TrackItem(
id: "YHMFzf1uN2U",
title: "Nachtigall",
duration: 218,
cover: [],
artists: [
ChannelId(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh",
),
],
artists_txt: Some("Oonagh"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(12),
),
TrackItem(
id: "jvV-z5F3oAo",
title: "Gayatri Mantra",
duration: 277,
cover: [],
artists: [
ChannelId(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh",
),
],
artists_txt: Some("Oonagh"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(13),
),
TrackItem(
id: "u8_9cxlrh8k",
title: "Sing mir deine Lieder",
duration: 204,
cover: [],
artists: [
ChannelId(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh",
),
],
artists_txt: Some("Oonagh"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(14),
),
TrackItem(
id: "gSvKcvM1Wk0",
title: "Laurië lantar",
duration: 202,
cover: [],
artists: [
ChannelId(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh",
),
],
artists_txt: Some("Oonagh"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(15),
),
TrackItem(
id: "wQHgKRJ0pDQ",
title: "Wächter vor dem Tor",
duration: 222,
cover: [],
artists: [
ChannelId(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh",
),
],
artists_txt: Some("Oonagh"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(16),
),
TrackItem(
id: "Ckz5i6-hzf0",
title: "Stroh zu Gold",
duration: 177,
cover: [],
artists: [
ChannelId(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh",
),
],
artists_txt: Some("Oonagh"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(17),
),
TrackItem(
id: "y5zuUgyFqrc",
title: "Sonnenwendnacht",
duration: 220,
cover: [],
artists: [
ChannelId(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh",
),
],
artists_txt: Some("Oonagh"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(18),
),
],
variants: [
AlbumItem(
id: "MPREb_jk6Msw8izou",
name: "Märchen enden gut (Nyáre Ranta (Märchenedition))",
cover: [
Thumbnail(
url: "https://lh3.googleusercontent.com/BKgnW_-hapCHk599AtRfTYZGdXVIo0C4bJp1Bh7qUpGK7fNAXGW8Bhv2x-ukeFM8cuxKbGqqGaTo8fZASA=w226-h226-l90-rj",
width: 226,
height: 226,
),
Thumbnail(
url: "https://lh3.googleusercontent.com/BKgnW_-hapCHk599AtRfTYZGdXVIo0C4bJp1Bh7qUpGK7fNAXGW8Bhv2x-ukeFM8cuxKbGqqGaTo8fZASA=w544-h544-l90-rj",
width: 544,
height: 544,
),
],
artists: [
ChannelId(
id: "UC_vmjW5e1xEHhYjY2a0kK1A",
name: "Oonagh",
),
],
artists_txt: "Oonagh",
album_type: Album,
year: None,
),
],
)

View file

@ -0,0 +1,261 @@
---
source: tests/youtube.rs
expression: album
---
MusicAlbum(
id: "MPREb_cwzk8EUwypZ",
playlist_id: Some("OLAK5uy_kODvYZ5CEpYdtd4VPsmg0eRTlpazG0dvA"),
name: "Folge 2: Eiszeit (Das Original-Hörspiel zur TV-Serie)",
cover: "[cover]",
artists: [
ChannelId(
id: "UCNoyEM0e2A7WlsBmP2w3avg",
name: "Kingdom Force",
),
],
artists_txt: "Kingdom Force",
album_type: Album,
year: Some(2022),
by_va: false,
tracks: [
TrackItem(
id: "lSbKz5LWvKE",
title: "Achtung, Waldbrand - Teil 1",
duration: 229,
cover: [],
artists: [
ChannelId(
id: "UCNoyEM0e2A7WlsBmP2w3avg",
name: "Kingdom Force",
),
],
artists_txt: Some("Kingdom Force"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(1),
),
TrackItem(
id: "fdO6gu4qjRw",
title: "Achtung, Waldbrand - Teil 2",
duration: 235,
cover: [],
artists: [
ChannelId(
id: "UCNoyEM0e2A7WlsBmP2w3avg",
name: "Kingdom Force",
),
],
artists_txt: Some("Kingdom Force"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(2),
),
TrackItem(
id: "muCxstXirvY",
title: "Achtung, Waldbrand - Teil 3",
duration: 197,
cover: [],
artists: [
ChannelId(
id: "UCNoyEM0e2A7WlsBmP2w3avg",
name: "Kingdom Force",
),
],
artists_txt: Some("Kingdom Force"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(3),
),
TrackItem(
id: "aG1N0vo__Ng",
title: "Eiszeit - Teil 1",
duration: 186,
cover: [],
artists: [
ChannelId(
id: "UCNoyEM0e2A7WlsBmP2w3avg",
name: "Kingdom Force",
),
],
artists_txt: Some("Kingdom Force"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(4),
),
TrackItem(
id: "roHhLNYS9yo",
title: "Eiszeit - Teil 2",
duration: 188,
cover: [],
artists: [
ChannelId(
id: "UCNoyEM0e2A7WlsBmP2w3avg",
name: "Kingdom Force",
),
],
artists_txt: Some("Kingdom Force"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(5),
),
TrackItem(
id: "nJ49NuLvcAw",
title: "Eiszeit - Teil 3",
duration: 205,
cover: [],
artists: [
ChannelId(
id: "UCNoyEM0e2A7WlsBmP2w3avg",
name: "Kingdom Force",
),
],
artists_txt: Some("Kingdom Force"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(6),
),
TrackItem(
id: "Me119D570h0",
title: "Eiszeit - Teil 4",
duration: 219,
cover: [],
artists: [
ChannelId(
id: "UCNoyEM0e2A7WlsBmP2w3avg",
name: "Kingdom Force",
),
],
artists_txt: Some("Kingdom Force"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(7),
),
TrackItem(
id: "YXnRLK-qKG8",
title: "Im Sinkloch - Teil 1",
duration: 240,
cover: [],
artists: [
ChannelId(
id: "UCNoyEM0e2A7WlsBmP2w3avg",
name: "Kingdom Force",
),
],
artists_txt: Some("Kingdom Force"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(8),
),
TrackItem(
id: "A61wz1jz9X0",
title: "Im Sinkloch - Teil 2",
duration: 239,
cover: [],
artists: [
ChannelId(
id: "UCNoyEM0e2A7WlsBmP2w3avg",
name: "Kingdom Force",
),
],
artists_txt: Some("Kingdom Force"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(9),
),
TrackItem(
id: "u_S08EJOTUg",
title: "Im Sinkloch - Teil 3",
duration: 197,
cover: [],
artists: [
ChannelId(
id: "UCNoyEM0e2A7WlsBmP2w3avg",
name: "Kingdom Force",
),
],
artists_txt: Some("Kingdom Force"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(10),
),
TrackItem(
id: "0qwYJihV1EU",
title: "Vorsicht, heiß - Teil 1",
duration: 201,
cover: [],
artists: [
ChannelId(
id: "UCNoyEM0e2A7WlsBmP2w3avg",
name: "Kingdom Force",
),
],
artists_txt: Some("Kingdom Force"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(11),
),
TrackItem(
id: "zjhoyTnEzuQ",
title: "Vorsicht, heiß - Teil 2",
duration: 187,
cover: [],
artists: [
ChannelId(
id: "UCNoyEM0e2A7WlsBmP2w3avg",
name: "Kingdom Force",
),
],
artists_txt: Some("Kingdom Force"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(12),
),
TrackItem(
id: "oDjDd0UBzAY",
title: "Vorsicht, heiß - Teil 3",
duration: 183,
cover: [],
artists: [
ChannelId(
id: "UCNoyEM0e2A7WlsBmP2w3avg",
name: "Kingdom Force",
),
],
artists_txt: Some("Kingdom Force"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(13),
),
TrackItem(
id: "_3-WVmqgi-Q",
title: "Vorsicht, heiß - Teil 4",
duration: 193,
cover: [],
artists: [
ChannelId(
id: "UCNoyEM0e2A7WlsBmP2w3avg",
name: "Kingdom Force",
),
],
artists_txt: Some("Kingdom Force"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(14),
),
],
variants: [],
)

View file

@ -0,0 +1,48 @@
---
source: tests/youtube.rs
expression: album
---
MusicAlbum(
id: "MPREb_bHfHGoy7vuv",
playlist_id: Some("OLAK5uy_kdSWBZ-9AZDkYkuy0QCc3p0KO9DEHVNH0"),
name: "Der Himmel reißt auf",
cover: "[cover]",
artists: [
ChannelId(
id: "UCXGYZ-OhdOpPBamHX3K9YRg",
name: "Joel Brandenstein",
),
ChannelId(
id: "UCFTcSVPYRWlDoHisR-ZKwgw",
name: "Vanessa Mai",
),
],
artists_txt: "Joel Brandenstein & Vanessa Mai",
album_type: Single,
year: Some(2020),
by_va: false,
tracks: [
TrackItem(
id: "XX0epju-YvY",
title: "Der Himmel reißt auf",
duration: 183,
cover: [],
artists: [
ChannelId(
id: "UCXGYZ-OhdOpPBamHX3K9YRg",
name: "Joel Brandenstein",
),
ChannelId(
id: "UCFTcSVPYRWlDoHisR-ZKwgw",
name: "Vanessa Mai",
),
],
artists_txt: Some("Joel Brandenstein & Vanessa Mai"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(1),
),
],
variants: [],
)

View file

@ -0,0 +1,95 @@
---
source: tests/youtube.rs
expression: album
---
MusicAlbum(
id: "MPREb_8QkDeEIawvX",
playlist_id: Some("OLAK5uy_mEX9ljZeeEWgTM1xLL1isyiGaWXoPyoOk"),
name: "Queendom2 FINAL",
cover: "[cover]",
artists: [],
artists_txt: "Various Artists",
album_type: Single,
year: Some(2022),
by_va: true,
tracks: [
TrackItem(
id: "8IqLxg0GqXc",
title: "Waka Boom (My Way) (feat. Lee Young Ji)",
duration: 274,
cover: [],
artists: [],
artists_txt: Some("HYOLYN"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(1),
),
TrackItem(
id: "9WYpLYAEub0",
title: "AURA",
duration: 216,
cover: [],
artists: [],
artists_txt: Some("WJSN"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(2),
),
TrackItem(
id: "R48tE237bW4",
title: "THE GIRLS (Cant turn me down)",
duration: 239,
cover: [],
artists: [
ChannelId(
id: "UCAKvDuIX3m1AUdPpDSqV_3w",
name: "Kep1er",
),
],
artists_txt: Some("Kep1er"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(3),
),
TrackItem(
id: "-UzsoR6z-vg",
title: "Red Sun!",
duration: 254,
cover: [],
artists: [],
artists_txt: Some("VIVIZ"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(4),
),
TrackItem(
id: "kbNVyn8Ex28",
title: "POSE",
duration: 187,
cover: [],
artists: [],
artists_txt: Some("LOONA"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(5),
),
TrackItem(
id: "NJrQZUzWP5Y",
title: "Whistle",
duration: 224,
cover: [],
artists: [],
artists_txt: Some("Brave Girls"),
album: None,
view_count: None,
is_video: true,
track_nr: Some(6),
),
],
variants: [],
)

View file

@ -1,14 +1,16 @@
use std::collections::HashSet;
use std::fmt::Display;
use rstest::rstest;
use time::macros::date;
use time::OffsetDateTime;
use rustypipe::client::{ClientType, RustyPipe};
use rustypipe::client::{ClientType, RustyPipe, RustyPipeQuery};
use rustypipe::error::{Error, ExtractionError};
use rustypipe::model::richtext::ToPlaintext;
use rustypipe::model::{
AudioCodec, AudioFormat, Channel, UrlTarget, Verification, VideoCodec, VideoFormat, YouTubeItem,
AlbumType, AudioCodec, AudioFormat, Channel, FromYtItem, MusicEntityType, Paginator, UrlTarget,
Verification, VideoCodec, VideoFormat, YouTubeItem,
};
use rustypipe::param::search_filter::{self, SearchFilter};
@ -45,7 +47,7 @@ async fn get_player_from_client(#[case] client_type: ClientType) {
assert!(!player_data.details.thumbnail.is_empty());
assert_eq!(player_data.details.channel.id, "UC_aEa8K-EOJ3D6gOs7HcyNg");
assert_eq!(player_data.details.channel.name, "NoCopyrightSounds");
assert!(player_data.details.view_count > 146_818_808);
assert_gte(player_data.details.view_count, 146_818_808, "view count");
assert_eq!(player_data.details.keywords[0], "spektrem");
assert_eq!(player_data.details.is_live_content, false);
@ -76,7 +78,7 @@ async fn get_player_from_client(#[case] client_type: ClientType) {
assert_approx(audio.bitrate as f64, 130685.0);
assert_approx(audio.average_bitrate as f64, 129496.0);
assert_eq!(audio.size, 4193863);
assert_approx(audio.size as f64, 4193863.0);
assert_eq!(audio.mime, "audio/mp4; codecs=\"mp4a.40.2\"");
assert_eq!(audio.format, AudioFormat::M4a);
assert_eq!(audio.codec, AudioCodec::Mp4a);
@ -207,12 +209,7 @@ async fn get_player(
assert_eq!(details.length, length);
assert_eq!(details.channel.id, channel_id);
assert_eq!(details.channel.name, channel_name);
assert!(
details.view_count > views,
"expected > {} views, got {}",
views,
details.view_count
);
assert_gte(details.view_count, views, "views");
assert_eq!(details.is_live, is_live);
assert_eq!(details.is_live_content, is_live_content);
@ -255,7 +252,7 @@ async fn get_player(
_ => {}
};
assert!(player_data.expires_in_seconds > 10000);
assert_gte(player_data.expires_in_seconds, 10_000, "expiry time");
}
#[rstest]
@ -425,22 +422,13 @@ async fn get_video_details() {
assert_eq!(details.channel.name, "SMTOWN");
assert!(!details.channel.avatar.is_empty(), "no channel avatars");
assert_eq!(details.channel.verification, Verification::Verified);
assert!(
details.channel.subscriber_count.unwrap() > 30000000,
"expected >30M subs, got {}",
details.channel.subscriber_count.unwrap()
);
assert!(
details.view_count > 232000000,
"expected > 232M views, got {}",
details.view_count
);
assert!(
details.like_count.unwrap() > 4000000,
"expected > 4M likes, got {}",
details.like_count.unwrap()
assert_gte(
details.channel.subscriber_count.unwrap(),
30_000_000,
"subscribers",
);
assert_gte(details.view_count, 232_000_000, "views");
assert_gte(details.like_count.unwrap(), 4_000_000, "likes");
let date = details.publish_date.unwrap();
assert_eq!(date.date(), date!(2020 - 11 - 17));
@ -448,14 +436,9 @@ async fn get_video_details() {
assert!(!details.is_live);
assert!(!details.is_ccommons);
assert!(!details.recommended.items.is_empty());
assert!(!details.recommended.is_exhausted());
assert_next(details.recommended, &rp.query(), 10, 2).await;
assert!(
details.top_comments.count.unwrap() > 700000,
"expected > 700K comments, got {}",
details.top_comments.count.unwrap()
);
assert_gte(details.top_comments.count.unwrap(), 700_000, "comments");
assert!(!details.top_comments.is_exhausted());
assert!(!details.latest_comments.is_exhausted());
}
@ -476,22 +459,13 @@ async fn get_video_details_music() {
assert_eq!(details.channel.name, "Sentamusic");
assert!(!details.channel.avatar.is_empty(), "no channel avatars");
assert_eq!(details.channel.verification, Verification::Artist);
assert!(
details.channel.subscriber_count.unwrap() > 33000,
"expected >33K subs, got {}",
details.channel.subscriber_count.unwrap()
);
assert!(
details.view_count > 20309,
"expected > 20309 views, got {}",
details.view_count
);
assert!(
details.like_count.unwrap() > 145,
"expected > 145 likes, got {}",
details.like_count.unwrap()
assert_gte(
details.channel.subscriber_count.unwrap(),
33_000,
"subscribers",
);
assert_gte(details.view_count, 20_309, "views");
assert_gte(details.like_count.unwrap(), 145, "likes");
let date = details.publish_date.unwrap();
assert_eq!(date.date(), date!(2020 - 8 - 6));
@ -499,8 +473,7 @@ async fn get_video_details_music() {
assert!(!details.is_live);
assert!(!details.is_ccommons);
assert!(!details.recommended.items.is_empty());
assert!(!details.recommended.is_exhausted());
assert_next(details.recommended, &rp.query(), 10, 2).await;
// Update(01.11.2022): comments are sometimes enabled
/*
@ -534,22 +507,13 @@ async fn get_video_details_ccommons() {
assert_eq!(details.channel.name, "media.ccc.de");
assert!(!details.channel.avatar.is_empty(), "no channel avatars");
assert_eq!(details.channel.verification, Verification::None);
assert!(
details.channel.subscriber_count.unwrap() > 170000,
"expected >170K subs, got {}",
details.channel.subscriber_count.unwrap()
);
assert!(
details.view_count > 2517358,
"expected > 2517358 views, got {}",
details.view_count
);
assert!(
details.like_count.unwrap() > 52330,
"expected > 52330 likes, got {}",
details.like_count.unwrap()
assert_gte(
details.channel.subscriber_count.unwrap(),
170_000,
"subscribers",
);
assert_gte(details.view_count, 2_517_358, "views");
assert_gte(details.like_count.unwrap(), 52_330, "likes");
let date = details.publish_date.unwrap();
assert_eq!(date.date(), date!(2019 - 12 - 29));
@ -557,14 +521,9 @@ async fn get_video_details_ccommons() {
assert!(!details.is_live);
assert!(details.is_ccommons);
assert!(!details.recommended.items.is_empty());
assert!(!details.recommended.is_exhausted());
assert_next(details.recommended, &rp.query(), 10, 2).await;
assert!(
details.top_comments.count.unwrap() > 2199,
"expected > 2199 comments, got {}",
details.top_comments.count.unwrap()
);
assert_gte(details.top_comments.count.unwrap(), 2199, "comments");
assert!(!details.top_comments.is_exhausted());
assert!(!details.latest_comments.is_exhausted());
}
@ -589,22 +548,13 @@ async fn get_video_details_chapters() {
assert_eq!(details.channel.name, "Linus Tech Tips");
assert!(!details.channel.avatar.is_empty(), "no channel avatars");
assert_eq!(details.channel.verification, Verification::Verified);
assert!(
details.channel.subscriber_count.unwrap() > 14700000,
"expected >14.7M subs, got {}",
details.channel.subscriber_count.unwrap()
);
assert!(
details.view_count > 1157262,
"expected > 1157262 views, got {}",
details.view_count
);
assert!(
details.like_count.unwrap() > 54670,
"expected > 54670 likes, got {}",
details.like_count.unwrap()
assert_gte(
details.channel.subscriber_count.unwrap(),
14_700_000,
"subscribers",
);
assert_gte(details.view_count, 1_157_262, "views");
assert_gte(details.like_count.unwrap(), 54_670, "likes");
let date = details.publish_date.unwrap();
assert_eq!(date.date(), date!(2022 - 9 - 15));
@ -695,14 +645,9 @@ async fn get_video_details_chapters() {
"###);
}
assert!(!details.recommended.items.is_empty());
assert!(!details.recommended.is_exhausted());
assert_next(details.recommended, &rp.query(), 10, 2).await;
assert!(
details.top_comments.count.unwrap() > 3199,
"expected > 3199 comments, got {}",
details.top_comments.count.unwrap()
);
assert_gte(details.top_comments.count.unwrap(), 3200, "comments");
assert!(!details.top_comments.is_exhausted());
assert!(!details.latest_comments.is_exhausted());
}
@ -730,22 +675,13 @@ async fn get_video_details_live() {
assert_eq!(details.channel.name, "Space Videos");
assert!(!details.channel.avatar.is_empty(), "no channel avatars");
assert_eq!(details.channel.verification, Verification::Verified);
assert!(
details.channel.subscriber_count.unwrap() > 5500000,
"expected >5.5M subs, got {}",
details.channel.subscriber_count.unwrap()
);
assert!(
details.view_count > 10,
"expected > 10 views, got {}",
details.view_count
);
assert!(
details.like_count.unwrap() > 872290,
"expected > 872290 likes, got {}",
details.like_count.unwrap()
assert_gte(
details.channel.subscriber_count.unwrap(),
5_500_000,
"subscribers",
);
assert_gte(details.view_count, 10, "views");
assert_gte(details.like_count.unwrap(), 872_290, "likes");
let date = details.publish_date.unwrap();
assert_eq!(date.date(), date!(2021 - 9 - 23));
@ -753,8 +689,7 @@ async fn get_video_details_live() {
assert!(details.is_live);
assert!(!details.is_ccommons);
assert!(!details.recommended.items.is_empty());
assert!(!details.recommended.is_exhausted());
assert_next(details.recommended, &rp.query(), 10, 2).await;
// No comments because livestream
assert_eq!(details.top_comments.count, Some(0));
@ -781,17 +716,12 @@ async fn get_video_details_agegate() {
assert_eq!(details.channel.name, "PrinceOfFALLEN");
assert!(!details.channel.avatar.is_empty(), "no channel avatars");
assert_eq!(details.channel.verification, Verification::None);
assert!(
details.channel.subscriber_count.unwrap() > 1400,
"expected >1400 subs, got {}",
details.channel.subscriber_count.unwrap()
);
assert!(
details.view_count > 200,
"expected > 200 views, got {}",
details.view_count
assert_gte(
details.channel.subscriber_count.unwrap(),
1400,
"subscribers",
);
assert_gte(details.view_count, 200, "views");
assert!(details.like_count.is_none(), "like count not hidden");
let date = details.publish_date.unwrap();
@ -820,26 +750,6 @@ async fn get_video_details_not_found() {
)
}
#[tokio::test]
async fn get_video_recommendations() {
let rp = RustyPipe::builder().strict().build();
let details = rp.query().video_details("ZeerrnuLi5E").await.unwrap();
let next_recommendations = details
.recommended
.next(&rp.query())
.await
.unwrap()
.unwrap();
// dbg!(&next_recommendations);
assert!(
next_recommendations.items.len() > 10,
"expected > 10 next recommendations, got {}",
next_recommendations.items.len()
);
assert!(!next_recommendations.is_exhausted());
}
#[tokio::test]
async fn get_video_comments() {
let rp = RustyPipe::builder().strict().build();
@ -851,21 +761,17 @@ async fn get_video_comments() {
.await
.unwrap()
.unwrap();
assert!(
top_comments.items.len() > 10,
"expected > 10 next comments, got {}",
top_comments.items.len()
);
assert_gte(top_comments.items.len(), 10, "comments");
assert!(!top_comments.is_exhausted());
let n_comments = top_comments.count.unwrap();
assert_gte(n_comments, 700_000, "comments");
// Comment count should be exact after fetching first page
assert!(
n_comments > 700000,
"expected > 700k comments, got {}",
n_comments % 1000 != 0,
"estimated comment count: {}",
n_comments
);
// Comment count should be exact after fetching first page
assert!(n_comments % 1000 != 0);
let latest_comments = details
.latest_comments
@ -873,11 +779,7 @@ async fn get_video_comments() {
.await
.unwrap()
.unwrap();
assert!(
latest_comments.items.len() > 10,
"expected > 10 next comments, got {}",
latest_comments.items.len()
);
assert_gte(latest_comments.items.len(), 10, "next comments");
assert!(!latest_comments.is_exhausted());
}
@ -906,11 +808,7 @@ async fn channel_videos() {
assert!(age_days < 60, "latest video older than 60 days");
let next = channel.content.next(&rp.query()).await.unwrap().unwrap();
assert!(
!next.is_exhausted() && !next.items.is_empty(),
"no more videos"
);
assert_next(channel.content, &rp.query(), 15, 2).await;
}
#[tokio::test]
@ -928,11 +826,7 @@ async fn channel_shorts() {
// dbg!(&channel);
assert_eq!(channel.id, "UCh8gHdtzO2tXd593_bjErWg");
assert_eq!(channel.name, "Doobydobap");
assert!(
channel.subscriber_count.unwrap() > 2800000,
"expected >2.8M subscribers, got {}",
channel.subscriber_count.unwrap()
);
assert_gte(channel.subscriber_count.unwrap(), 2_800_000, "subscribers");
assert!(!channel.avatar.is_empty(), "got no thumbnails");
assert_eq!(channel.verification, Verification::Verified);
assert!(channel
@ -951,11 +845,7 @@ async fn channel_shorts() {
"got no shorts"
);
let next = channel.content.next(&rp.query()).await.unwrap().unwrap();
assert!(
!next.is_exhausted() && !next.items.is_empty(),
"no more shorts"
);
assert_next(channel.content, &rp.query(), 15, 1).await;
}
#[tokio::test]
@ -978,8 +868,7 @@ async fn channel_livestreams() {
"got no streams"
);
let next = channel.content.next(&rp.query()).await.unwrap().unwrap();
assert!(!next.items.is_empty(), "no more streams");
assert_next(channel.content, &rp.query(), 5, 1).await;
}
#[tokio::test]
@ -998,11 +887,7 @@ async fn channel_playlists() {
"got no playlists"
);
let next = channel.content.next(&rp.query()).await.unwrap().unwrap();
assert!(
!next.is_exhausted() && !next.items.is_empty(),
"no more playlists"
);
assert_next(channel.content, &rp.query(), 15, 1).await;
}
#[tokio::test]
@ -1020,10 +905,10 @@ async fn channel_info() {
let created = channel.content.create_date.unwrap();
assert_eq!(created, date!(2009 - 4 - 4));
assert!(
channel.content.view_count.unwrap() > 186854340,
"exp >186M views, got {}",
channel.content.view_count.unwrap()
assert_gte(
channel.content.view_count.unwrap(),
186854340,
"channel views",
);
insta::assert_ron_snapshot!(channel.content.links, @r###"
@ -1048,11 +933,7 @@ async fn channel_info() {
fn assert_channel_eevblog<T>(channel: &Channel<T>) {
assert_eq!(channel.id, "UC2DjFE7Xf11URZqWBigcVOQ");
assert_eq!(channel.name, "EEVblog");
assert!(
channel.subscriber_count.unwrap() > 880000,
"exp >880K subscribers, got {}",
channel.subscriber_count.unwrap()
);
assert_gte(channel.subscriber_count.unwrap(), 880_000, "subscribers");
assert!(!channel.avatar.is_empty(), "got no thumbnails");
assert_eq!(channel.verification, Verification::Verified);
assert_eq!(channel.description, "NO SCRIPT, NO FEAR, ALL OPINION\nAn off-the-cuff Video Blog about Electronics Engineering, for engineers, hobbyists, enthusiasts, hackers and Makers\nHosted by Dave Jones from Sydney Australia\n\nDONATIONS:\nBitcoin: 3KqyH1U3qrMPnkLufM2oHDU7YB4zVZeFyZ\nEthereum: 0x99ccc4d2654ba40744a1f678d9868ecb15e91206\nPayPal: david@alternatezone.com\n\nPatreon: https://www.patreon.com/eevblog\n\nEEVblog2: http://www.youtube.com/EEVblog2\nEEVdiscover: https://www.youtube.com/channel/UCkGvUEt8iQLmq3aJIMjT2qQ\n\nEMAIL:\nAdvertising/Commercial: eevblog+business@gmail.com\nFan mail: eevblog+fan@gmail.com\nHate Mail: eevblog+hate@gmail.com\n\nI DON'T DO PAID VIDEO SPONSORSHIPS, DON'T ASK!\n\nPLEASE:\nDo NOT ask for personal advice on something, post it in the EEVblog forum.\nI read ALL email, but please don't be offended if I don't have time to reply, I get a LOT of email.\n\nMailbag\nPO Box 7949\nBaulkham Hills NSW 2153\nAUSTRALIA");
@ -1191,22 +1072,9 @@ async fn search() {
"expected > 7000 total results, got {}",
result.items.count.unwrap()
);
assert!(
result.items.items.len() > 10,
"expected > 10 search results, got {}",
result.items.items.len()
);
assert!(!result.items.is_exhausted());
assert_eq!(result.corrected_query.unwrap(), "doobydobap");
let next = result.items.next(&rp.query()).await.unwrap().unwrap();
assert!(
next.items.len() > 10,
"expected > 10 continuation results, got {}",
next.items.len()
);
assert!(!next.is_exhausted());
assert_next(result.items, &rp.query(), 10, 2).await;
}
#[rstest]
@ -1216,18 +1084,14 @@ async fn search() {
#[tokio::test]
async fn search_filter_entity(#[case] entity: search_filter::Entity) {
let rp = RustyPipe::builder().strict().build();
let result = rp
let mut result = rp
.query()
.search_filter("music", &SearchFilter::new().entity(entity))
.await
.unwrap();
assert!(
result.items.items.len() > 10,
"expected > 10 search results, got {}",
result.items.items.len()
);
assert!(!result.items.is_exhausted());
result.items.extend(&rp.query()).await.unwrap();
assert_gte(result.items.items.len(), 20, "items");
result.items.items.iter().for_each(|item| match item {
YouTubeItem::Video(_) => {
@ -1343,30 +1207,10 @@ async fn resolve_channel_not_found() {
#[tokio::test]
async fn startpage() {
let rp = RustyPipe::builder().strict().build();
let result = rp.query().startpage().await.unwrap();
assert!(
result.items.len() >= 20,
"expected >= 20 items, got {}",
result.items.len()
);
assert!(!result.is_exhausted());
}
#[tokio::test]
async fn startpage_cont() {
let rp = RustyPipe::builder().strict().build();
let startpage = rp.query().startpage().await.unwrap();
let next = startpage.next(&rp.query()).await.unwrap().unwrap();
assert!(
next.items.len() >= 20,
"expected >= 20 items, got {}",
next.items.len()
);
assert!(!next.is_exhausted());
assert_next(startpage, &rp.query(), 20, 2).await;
}
#[tokio::test]
@ -1374,11 +1218,7 @@ async fn trending() {
let rp = RustyPipe::builder().strict().build();
let result = rp.query().trending().await.unwrap();
assert!(
result.len() >= 50,
"expected >= 50 items, got {}",
result.len()
);
assert_gte(result.len(), 50, "items");
}
//#MUSIC
@ -1474,6 +1314,263 @@ async fn music_playlist_not_found() {
);
}
#[rstest]
#[case::one_artist("one_artist", "MPREb_nlBWQROfvjo")]
#[case::various_artists("various_artists", "MPREb_8QkDeEIawvX")]
#[case::single("single", "MPREb_bHfHGoy7vuv")]
#[case::ep("ep", "MPREb_u1I69lSAe5v")]
#[case::audiobook("audiobook", "MPREb_gaoNzsQHedo")]
#[case::show("show", "MPREb_cwzk8EUwypZ")]
#[tokio::test]
async fn music_album(#[case] name: &str, #[case] id: &str) {
let rp = RustyPipe::builder().strict().build();
let album = rp.query().music_album(id).await.unwrap();
assert!(!album.cover.is_empty(), "got no cover");
insta::assert_ron_snapshot!(format!("music_album_{}", name), album,
{".cover" => "[cover]"}
);
}
#[tokio::test]
async fn music_album_not_found() {
let rp = RustyPipe::builder().strict().build();
let err = rp
.query()
.music_album("MPREb_nlBWQROfvjoz")
.await
.unwrap_err();
assert!(
matches!(
err,
Error::Extraction(ExtractionError::ContentUnavailable(_))
),
"got: {}",
err
);
}
#[rstest]
#[case::default(false)]
#[case::typo(true)]
#[tokio::test]
async fn music_search(#[case] typo: bool) {
let rp = RustyPipe::builder().strict().build();
let res = rp
.query()
.music_search(match typo {
false => "black mamba",
true => "blck mamba",
})
.await
.unwrap();
assert!(!res.tracks.is_empty(), "no tracks");
assert!(!res.albums.is_empty(), "no albums");
assert!(!res.artists.is_empty(), "no artists");
assert!(!res.playlists.is_empty(), "no playlists");
assert_eq!(res.order[0], MusicEntityType::Track);
if typo {
assert_eq!(res.corrected_query.unwrap(), "black mamba");
} else {
assert_eq!(res.corrected_query, None);
}
let track = &res.tracks[0];
dbg!(&track);
assert_eq!(track.id, "ZeerrnuLi5E");
assert_eq!(track.title, "Black Mamba");
assert_eq!(track.duration, 230);
assert!(!track.cover.is_empty(), "got no cover");
let track_artist = &track.artists[0];
assert_eq!(track_artist.id, "UCEdZAdnnKqbaHOlv8nM6OtA");
assert_eq!(track_artist.name, "aespa");
assert_eq!(track.artists_txt.as_ref().unwrap(), "aespa");
assert_eq!(track.album, None);
assert_gte(track.view_count.unwrap(), 230_000_000, "views");
assert!(track.is_video, "got no video");
assert_eq!(track.track_nr, None);
}
#[rstest]
#[case::tracks(false)]
#[case::videos(true)]
#[tokio::test]
async fn music_search_tracks(#[case] videos: bool) {
let rp = RustyPipe::builder().strict().build();
let res = rp
.query()
.music_search_tracks("black mamba", videos)
.await
.unwrap();
let track = &res.items.items[0];
assert_eq!(track.title, "Black Mamba");
assert!(!track.cover.is_empty(), "got no cover");
assert_eq!(track.is_video, videos);
assert_eq!(track.track_nr, None);
let track_artist = &track.artists[0];
assert_eq!(track_artist.id, "UCEdZAdnnKqbaHOlv8nM6OtA");
assert_eq!(track_artist.name, "aespa");
assert_eq!(track.artists_txt.as_ref().unwrap(), "aespa");
if videos {
assert_eq!(track.id, "ZeerrnuLi5E");
assert_eq!(track.duration, 230);
assert_eq!(track.album, None);
assert_gte(track.view_count.unwrap(), 230_000_000, "views");
} else {
assert_eq!(track.id, "BL-aIpCLWnU");
assert_eq!(track.duration, 175);
let album = track.album.as_ref().unwrap();
assert_eq!(album.id, "MPREb_OpHWHwyNOuY");
assert_eq!(album.name, "Black Mamba");
}
assert_next(res.items, &rp.query(), 15, 2).await;
}
#[rstest]
#[case::single(
"black mamba",
"Black Mamba",
"MPREb_OpHWHwyNOuY",
"aespa",
"UCEdZAdnnKqbaHOlv8nM6OtA",
2020,
AlbumType::Single,
2
)]
#[case::ep(
"waldbrand",
"Waldbrand",
"MPREb_u1I69lSAe5v",
"Madeline Juno",
"UCpJyCbFbdTrx0M90HCNBHFQ",
2016,
AlbumType::Ep,
1
)]
#[case::album(
"märchen enden gut",
"Märchen enden gut",
"MPREb_nlBWQROfvjo",
"Oonagh",
"UC_vmjW5e1xEHhYjY2a0kK1A",
2016,
AlbumType::Album,
1
)]
#[tokio::test]
async fn music_search_albums(
#[case] query: &str,
#[case] name: &str,
#[case] id: &str,
#[case] artist: &str,
#[case] artist_id: &str,
#[case] year: u16,
#[case] album_type: AlbumType,
#[case] n_pages: usize,
) {
let rp = RustyPipe::builder().strict().build();
let res = rp.query().music_search_albums(query).await.unwrap();
let album = &res.items.items[0];
assert_eq!(album.name, name);
assert_eq!(album.id, id);
assert_eq!(album.artists_txt, artist);
let album_artist = &album.artists[0];
assert_eq!(album_artist.id, artist_id);
assert_eq!(album_artist.name, artist);
assert!(!album.cover.is_empty(), "got no cover");
assert_eq!(album.year.as_ref().unwrap(), &year);
assert_eq!(album.album_type, album_type);
assert_eq!(res.corrected_query, None);
assert_next(res.items, &rp.query(), 15, n_pages).await;
}
#[tokio::test]
async fn music_search_artists() {
let rp = RustyPipe::builder().strict().build();
let res = rp.query().music_search_artists("namika").await.unwrap();
let artist = &res.items.items[0];
assert_eq!(artist.id, "UCIh4j8fXWf2U0ro0qnGU8Mg");
assert_eq!(artist.name, "Namika");
assert!(!artist.avatar.is_empty(), "got no avatar");
assert!(
artist.subscriber_count.unwrap() > 735_000,
"expected >735K subscribers, got {}",
artist.subscriber_count.unwrap()
);
assert_eq!(res.corrected_query, None);
}
#[tokio::test]
async fn music_search_artists_cont() {
let rp = RustyPipe::builder().strict().build();
let res = rp.query().music_search_artists("band").await.unwrap();
assert_eq!(res.corrected_query, None);
assert_next(res.items, &rp.query(), 15, 2).await;
}
#[tokio::test]
async fn music_search_playlists() {
let rp = RustyPipe::builder().strict().build();
let res = rp
.query()
.music_search_playlists("easy pop", false)
.await
.unwrap();
assert_eq!(res.corrected_query, None);
let playlist = &res.items.items[0];
assert_eq!(playlist.id, "RDCLAK5uy_kFQXdnqMaQCVx2wpUM4ZfbsGCDibZtkJk");
assert_eq!(playlist.name, "Easy Pop");
assert!(!playlist.thumbnail.is_empty(), "got no thumbnail");
assert_gte(playlist.track_count.unwrap(), 80, "tracks");
assert_eq!(playlist.channel, None);
assert!(playlist.from_ytm);
}
#[tokio::test]
async fn music_search_playlists_community() {
let rp = RustyPipe::builder().strict().build();
let res = rp
.query()
.music_search_playlists("Best Pop Music Videos - Top Pop Hits Playlist", true)
.await
.unwrap();
assert_eq!(res.corrected_query, None);
let playlist = &res.items.items[0];
assert_eq!(playlist.id, "PLMC9KNkIncKtGvr2kFRuXBVmBev6cAJ2u");
assert_eq!(
playlist.name,
"Best Pop Music Videos - Top Pop Hits Playlist"
);
assert!(!playlist.thumbnail.is_empty(), "got no thumbnail");
assert_gte(playlist.track_count.unwrap(), 250, "tracks");
let channel = playlist.channel.as_ref().unwrap();
assert_eq!(channel.id, "UCs72iRpTEuwV3y6pdWYLgiw");
assert_eq!(channel.name, "Redlist - Just Hits");
assert!(!playlist.from_ytm);
}
//#TESTUTIL
/// Assert equality within 10% margin
@ -1488,3 +1585,26 @@ fn assert_approx(left: f64, right: f64) {
);
}
}
fn assert_gte<T: PartialOrd + Display>(a: T, b: T, msg: &str) {
assert!(a >= b, "expected {} {}, got {}", b, msg, a);
}
async fn assert_next<T: FromYtItem>(
paginator: Paginator<T>,
query: &RustyPipeQuery,
min_items: usize,
n_pages: usize,
) {
let mut p = paginator;
assert_gte(p.items.len(), min_items, "items on page 0");
for i in 0..n_pages {
p = p.next(query).await.unwrap().expect("paginator exhausted");
assert_gte(
p.items.len(),
min_items,
&format!("items on page {}", i + 1),
);
}
}