Compare commits

..

2 commits

4 changed files with 59 additions and 72 deletions

View file

@ -7,7 +7,7 @@ use url::Url;
use crate::{
error::{Error, ExtractionError},
model::{Channel, ChannelInfo, Paginator, PlaylistItem, VideoItem},
param::{ChannelOrder, Language},
param::Language,
serializer::MapResult,
timeago,
util::{self, TryRemove},
@ -26,11 +26,7 @@ struct QChannel<'a> {
#[derive(Debug, Serialize)]
enum Params {
#[serde(rename = "EgZ2aWRlb3PyBgQKAjoA")]
VideosLatest,
#[serde(rename = "EgZ2aWRlb3MYAiAAMAE%3D")]
VideosOldest,
#[serde(rename = "EgZ2aWRlb3MYASAAMAE%3D")]
VideosPopular,
Videos,
#[serde(rename = "EglwbGF5bGlzdHMgAQ%3D%3D")]
Playlists,
#[serde(rename = "EgVhYm91dPIGBAoCEgA%3D")]
@ -41,25 +37,12 @@ impl RustyPipeQuery {
pub async fn channel_videos(
&self,
channel_id: &str,
) -> Result<Channel<Paginator<VideoItem>>, Error> {
self.channel_videos_ordered(channel_id, ChannelOrder::default())
.await
}
pub async fn channel_videos_ordered(
&self,
channel_id: &str,
order: ChannelOrder,
) -> Result<Channel<Paginator<VideoItem>>, Error> {
let context = self.get_context(ClientType::Desktop, true, None).await;
let request_body = QChannel {
context,
browse_id: channel_id,
params: match order {
ChannelOrder::Latest => Params::VideosLatest,
ChannelOrder::Oldest => Params::VideosOldest,
ChannelOrder::Popular => Params::VideosPopular,
},
params: Params::Videos,
};
self.execute_request::<response::Channel, _, _>(

View file

@ -183,7 +183,6 @@ struct RustyPipeRef {
reporter: Option<Box<dyn Reporter>>,
n_http_retries: u32,
consent_cookie: String,
visitor_data: Option<String>,
cache: CacheHolder,
default_opts: RustyPipeOpts,
}
@ -194,6 +193,7 @@ struct RustyPipeOpts {
country: Country,
report: bool,
strict: bool,
visitor_data: Option<String>,
}
pub struct RustyPipeBuilder {
@ -201,7 +201,6 @@ pub struct RustyPipeBuilder {
reporter: Option<Box<dyn Reporter>>,
n_http_retries: u32,
user_agent: String,
visitor_data: Option<String>,
default_opts: RustyPipeOpts,
}
@ -218,6 +217,7 @@ impl Default for RustyPipeOpts {
country: Country::Us,
report: false,
strict: false,
visitor_data: None,
}
}
}
@ -294,7 +294,6 @@ impl RustyPipeBuilder {
reporter: Some(Box::new(FileReporter::default())),
n_http_retries: 2,
user_agent: DEFAULT_UA.to_owned(),
visitor_data: None,
}
}
@ -335,7 +334,6 @@ impl RustyPipeBuilder {
CONSENT_COOKIE_YES,
rand::thread_rng().gen_range(100..1000)
),
visitor_data: self.visitor_data,
cache: CacheHolder {
desktop_client: RwLock::new(cdata.desktop_client),
music_client: RwLock::new(cdata.music_client),
@ -439,8 +437,15 @@ impl RustyPipeBuilder {
self
}
/// Set the default YouTube visitor data cookie
pub fn visitor_data(mut self, visitor_data: &str) -> Self {
self.visitor_data = Some(visitor_data.to_owned());
self.default_opts.visitor_data = Some(visitor_data.to_owned());
self
}
/// Set the default YouTube visitor data cookie to an optional value
pub fn visitor_data_opt(mut self, visitor_data: Option<String>) -> Self {
self.default_opts.visitor_data = visitor_data;
self
}
}
@ -748,6 +753,18 @@ impl RustyPipeQuery {
self
}
/// Set the YouTube visitor data cookie
pub fn visitor_data(mut self, visitor_data: &str) -> Self {
self.opts.visitor_data = Some(visitor_data.to_owned());
self
}
/// Set the YouTube visitor data cookie to an optional value
pub fn visitor_data_opt(mut self, visitor_data: Option<String>) -> Self {
self.opts.visitor_data = visitor_data;
self
}
/// Create a new context object, which is included in every request to
/// the YouTube API and contains language, country and device parameters.
///
@ -768,7 +785,7 @@ impl RustyPipeQuery {
true => self.opts.country,
false => Country::Us,
};
let visitor_data = self.client.inner.visitor_data.as_deref().or(visitor_data);
let visitor_data = self.opts.visitor_data.as_deref().or(visitor_data);
match ctype {
ClientType::Desktop => YTContext {

View file

@ -7,19 +7,6 @@ pub use locale::{Country, Language};
use serde::{Deserialize, Serialize};
pub use stream_filter::StreamFilter;
/// Channel video sort order
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ChannelOrder {
/// Output the latest videos first
#[default]
Latest,
/// Output the oldest videos first
Oldest,
/// Output the most viewed videos first
Popular,
}
/// YouTube API endpoint to fetch continuations from
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]

View file

@ -10,10 +10,7 @@ use rustypipe::model::richtext::ToPlaintext;
use rustypipe::model::{
AudioCodec, AudioFormat, Channel, UrlTarget, Verification, VideoCodec, VideoFormat, YouTubeItem,
};
use rustypipe::param::{
search_filter::{self, SearchFilter},
ChannelOrder,
};
use rustypipe::param::search_filter::{self, SearchFilter};
//#PLAYER
@ -260,20 +257,41 @@ async fn get_player(
}
#[rstest]
#[case::not_found("86abcdefghi", "extraction error: Video cant be played because of deletion/censorship. Reason (from YT): This video is unavailable")]
#[case::deleted("64DYi_8ESh0", "extraction error: Video cant be played because of deletion/censorship. Reason (from YT): This video is unavailable")]
#[case::censored("6SJNVb0GnPI", "extraction error: Video cant be played because of deletion/censorship. Reason (from YT): This video has been removed for violating YouTube's policy on hate speech. Learn more about combating hate speech in your country.")]
#[case::not_found(
"86abcdefghi",
"extraction error: Video cant be played because of deletion/censorship. Reason (from YT): "
)]
#[case::deleted(
"64DYi_8ESh0",
"extraction error: Video cant be played because of deletion/censorship. Reason (from YT): "
)]
#[case::censored(
"6SJNVb0GnPI",
"extraction error: Video cant be played because of deletion/censorship. Reason (from YT): "
)]
// This video is geoblocked outside of Japan, so expect this test case to fail when using a Japanese IP address.
#[case::geoblock("sJL6WA-aGkQ", "extraction error: Video cant be played because of DRM/Geoblock. Reason (from YT): The uploader has not made this video available in your country")]
#[case::drm("1bfOsni7EgI", "extraction error: Video cant be played because of DRM/Geoblock. Reason (from YT): This video can only be played on newer versions of Android or other supported devices.")]
#[case::private("s7_qI6_mIXc", "extraction error: Video cant be played because of private video. Reason (from YT): This video is private")]
#[case::t1("CUO8secmc0g", "extraction error: Video cant be played because of DRM/Geoblock. Reason (from YT): Playback on other websites has been disabled by the video owner")]
#[case::geoblock(
"sJL6WA-aGkQ",
"extraction error: Video cant be played because of DRM/Geoblock. Reason (from YT): "
)]
#[case::drm(
"1bfOsni7EgI",
"extraction error: Video cant be played because of DRM/Geoblock. Reason (from YT): "
)]
#[case::private(
"s7_qI6_mIXc",
"extraction error: Video cant be played because of private video. Reason (from YT): "
)]
#[case::t1(
"CUO8secmc0g",
"extraction error: Video cant be played because of DRM/Geoblock. Reason (from YT): "
)]
#[tokio::test]
async fn get_player_error(#[case] id: &str, #[case] msg: &str) {
let rp = RustyPipe::builder().strict().build();
let err = rp.query().player(id).await.unwrap_err();
assert_eq!(err.to_string(), msg);
assert!(err.to_string().starts_with(msg), "got error msg: {}", err);
}
//#PLAYLIST
@ -860,16 +878,12 @@ async fn get_video_comments() {
//#CHANNEL
#[rstest]
#[case::latest(ChannelOrder::Latest)]
#[case::oldest(ChannelOrder::Oldest)]
#[case::popular(ChannelOrder::Popular)]
#[tokio::test]
async fn channel_videos(#[case] order: ChannelOrder) {
async fn channel_videos() {
let rp = RustyPipe::builder().strict().build();
let channel = rp
.query()
.channel_videos_ordered("UC2DjFE7Xf11URZqWBigcVOQ", order)
.channel_videos("UC2DjFE7Xf11URZqWBigcVOQ")
.await
.unwrap();
@ -885,21 +899,7 @@ async fn channel_videos(#[case] order: ChannelOrder) {
let first_video_date = first_video.publish_date.unwrap();
let age_days = (OffsetDateTime::now_utc() - first_video_date).whole_days();
match order {
ChannelOrder::Latest => {
assert!(age_days < 60, "latest video older than 60 days")
}
ChannelOrder::Oldest => {
assert!(age_days > 4700, "oldest video newer than 4700 days")
}
ChannelOrder::Popular => {
assert!(
first_video.view_count.unwrap() > 2300000,
"most popular video < 2.3M views"
)
}
_ => unimplemented!(),
}
assert!(age_days < 60, "latest video older than 60 days");
let next = channel.content.next(&rp.query()).await.unwrap().unwrap();
assert!(