fix: add pedantic lints

This commit is contained in:
ThetaDev 2023-05-13 02:40:26 +02:00
parent 81280200f7
commit cbeb14f3fd
41 changed files with 520 additions and 447 deletions

View file

@ -322,7 +322,7 @@ fn map_vanity_url(url: &str, id: &str) -> Option<String> {
Url::parse(url).ok().map(|mut parsed_url| {
// The vanity URL from YouTube is http for some reason
let _ = parsed_url.set_scheme("https");
_ = parsed_url.set_scheme("https");
parsed_url.to_string()
})
}
@ -392,11 +392,8 @@ fn map_channel(
content: (),
},
response::channel::Header::CarouselHeaderRenderer(carousel) => {
let hdata = carousel
.contents
.into_iter()
.filter_map(|item| {
match item {
let hdata = carousel.contents.into_iter().find_map(|item| {
match item {
response::channel::CarouselHeaderRendererItem::TopicChannelDetailsRenderer {
subscriber_count_text,
subtitle,
@ -404,8 +401,7 @@ fn map_channel(
} => Some((subscriber_count_text.or(subtitle), avatar)),
response::channel::CarouselHeaderRendererItem::None => None,
}
})
.next();
});
Channel {
id: metadata.external_id,
@ -568,7 +564,7 @@ fn _order_ctoken(
pb_80226972.string(3, &pbi.to_base64());
let mut pb = ProtoBuilder::new();
pb.embedded(80226972, pb_80226972);
pb.embedded(80_226_972, pb_80226972);
pb.to_base64()
}

View file

@ -3,7 +3,7 @@ use std::collections::BTreeMap;
use crate::{
error::{Error, ExtractionError},
model::ChannelRss,
report::Report,
report::{Report, RustyPipeInfo},
};
use super::{response, RustyPipeQuery};
@ -19,10 +19,7 @@ impl RustyPipeQuery {
/// The downside of using the RSS feed is that it does not provide video durations.
pub async fn channel_rss<S: AsRef<str>>(&self, channel_id: S) -> Result<ChannelRss, Error> {
let channel_id = channel_id.as_ref();
let url = format!(
"https://www.youtube.com/feeds/videos.xml?channel_id={}",
channel_id,
);
let url = format!("https://www.youtube.com/feeds/videos.xml?channel_id={channel_id}");
let xml = self
.client
.http_request_txt(&self.client.inner.http.get(&url).build()?)
@ -40,15 +37,15 @@ impl RustyPipeQuery {
Err(e) => {
if let Some(reporter) = &self.client.inner.reporter {
let report = Report {
info: Default::default(),
info: RustyPipeInfo::default(),
level: crate::report::Level::ERR,
operation: "channel_rss".to_owned(),
operation: "channel_rss",
error: Some(e.to_string()),
msgs: Vec::new(),
deobf_data: None,
http_request: crate::report::HTTPRequest {
url,
method: "GET".to_owned(),
url: &url,
method: "GET",
req_header: BTreeMap::new(),
req_body: String::new(),
status: 200,

View file

@ -39,7 +39,7 @@ use crate::{
deobfuscate::DeobfData,
error::{Error, ExtractionError},
param::{Country, Language},
report::{FileReporter, Level, Report, Reporter, DEFAULT_REPORT_DIR},
report::{FileReporter, Level, Report, Reporter, RustyPipeInfo, DEFAULT_REPORT_DIR},
serializer::MapResult,
util,
};
@ -73,7 +73,7 @@ pub enum ClientType {
}
impl ClientType {
fn is_web(&self) -> bool {
fn is_web(self) -> bool {
match self {
ClientType::Desktop | ClientType::DesktopMusic | ClientType::TvHtml5Embed => true,
ClientType::Android | ClientType::Ios => false,
@ -118,11 +118,11 @@ struct ClientInfo<'a> {
impl Default for ClientInfo<'_> {
fn default() -> Self {
Self {
client_name: Default::default(),
client_version: Default::default(),
client_name: "",
client_version: Cow::default(),
client_screen: None,
device_model: None,
platform: Default::default(),
platform: "",
original_url: None,
visitor_data: None,
hl: Language::En,
@ -432,6 +432,7 @@ impl RustyPipeBuilder {
/// Return a new `RustyPipeBuilder`.
///
/// This is the same as [`RustyPipe::builder`]
#[must_use]
pub fn new() -> Self {
RustyPipeBuilder {
default_opts: RustyPipeOpts::default(),
@ -445,6 +446,7 @@ impl RustyPipeBuilder {
}
/// Return a new, configured RustyPipe instance.
#[must_use]
pub fn build(self) -> RustyPipe {
let mut client_builder = ClientBuilder::new()
.user_agent(self.user_agent.unwrap_or_else(|| DEFAULT_UA.to_owned()))
@ -509,6 +511,7 @@ impl RustyPipeBuilder {
/// This option has no effect if the storage backend or reporter are manually set or disabled.
///
/// **Default value**: current working directory
#[must_use]
pub fn storage_dir<P: Into<PathBuf>>(mut self, path: P) -> Self {
self.storage_dir = Some(path.into());
self
@ -519,12 +522,14 @@ impl RustyPipeBuilder {
/// program executions.
///
/// **Default value**: [`FileStorage`] in `rustypipe_cache.json`
#[must_use]
pub fn storage(mut self, storage: Box<dyn CacheStorage>) -> Self {
self.storage = DefaultOpt::Some(storage);
self
}
/// Disable cache storage
#[must_use]
pub fn no_storage(mut self) -> Self {
self.storage = DefaultOpt::None;
self
@ -533,12 +538,14 @@ impl RustyPipeBuilder {
/// Add a `Reporter` to collect error details
///
/// **Default value**: [`FileReporter`] creating reports in `./rustypipe_reports`
#[must_use]
pub fn reporter(mut self, reporter: Box<dyn Reporter>) -> Self {
self.reporter = DefaultOpt::Some(reporter);
self
}
/// Disable the creation of report files in case of errors and warnings.
#[must_use]
pub fn no_reporter(mut self) -> Self {
self.reporter = DefaultOpt::None;
self
@ -550,12 +557,14 @@ impl RustyPipeBuilder {
/// response body has finished.
///
/// **Default value**: 10s
#[must_use]
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = DefaultOpt::Some(timeout);
self
}
/// Disable the HTTP request timeout.
#[must_use]
pub fn no_timeout(mut self) -> Self {
self.timeout = DefaultOpt::None;
self
@ -570,6 +579,7 @@ impl RustyPipeBuilder {
/// random jitter to be less predictable).
///
/// **Default value**: 2
#[must_use]
pub fn n_http_retries(mut self, n_retries: u32) -> Self {
self.n_http_retries = n_retries;
self
@ -579,6 +589,7 @@ impl RustyPipeBuilder {
///
/// **Default value**: `Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0`
/// (Firefox ESR on Debian)
#[must_use]
pub fn user_agent<S: Into<String>>(mut self, user_agent: S) -> Self {
self.user_agent = Some(user_agent.into());
self
@ -591,6 +602,7 @@ impl RustyPipeBuilder {
/// **Default value**: `Language::En` (English)
///
/// **Info**: you can set this option for individual queries, too
#[must_use]
pub fn lang(mut self, lang: Language) -> Self {
self.default_opts.lang = lang;
self
@ -603,6 +615,7 @@ impl RustyPipeBuilder {
/// **Default value**: `Country::Us` (USA)
///
/// **Info**: you can set this option for individual queries, too
#[must_use]
pub fn country(mut self, country: Country) -> Self {
self.default_opts.country = validate_country(country);
self
@ -613,6 +626,7 @@ impl RustyPipeBuilder {
/// This should only be used for debugging.
///
/// **Info**: you can set this option for individual queries, too
#[must_use]
pub fn report(mut self) -> Self {
self.default_opts.report = true;
self
@ -624,6 +638,7 @@ impl RustyPipeBuilder {
/// This should only be used for testing.
///
/// **Info**: you can set this option for individual queries, too
#[must_use]
pub fn strict(mut self) -> Self {
self.default_opts.strict = true;
self
@ -643,6 +658,7 @@ impl RustyPipeBuilder {
/// visitor, so you should not use the same vistor data cookie for batch operations.
///
/// **Info**: you can set this option for individual queries, too
#[must_use]
pub fn visitor_data<S: Into<String>>(mut self, visitor_data: S) -> Self {
self.default_opts.visitor_data = Some(visitor_data.into());
self
@ -653,6 +669,7 @@ impl RustyPipeBuilder {
/// see also [`RustyPipeBuilder::visitor_data`]
///
/// **Info**: you can set this option for individual queries, too
#[must_use]
pub fn visitor_data_opt<S: Into<String>>(mut self, visitor_data: Option<S>) -> Self {
self.default_opts.visitor_data = visitor_data.map(S::into);
self
@ -669,6 +686,7 @@ impl RustyPipe {
/// Create a new RustyPipe instance with default settings.
///
/// To create an instance with custom options, use [`RustyPipeBuilder`] instead.
#[must_use]
pub fn new() -> Self {
RustyPipeBuilder::new().build()
}
@ -676,11 +694,13 @@ impl RustyPipe {
/// Create a new [`RustyPipeBuilder`]
///
/// This is the same as [`RustyPipeBuilder::new`]
#[must_use]
pub fn builder() -> RustyPipeBuilder {
RustyPipeBuilder::new()
}
/// Create a new [`RustyPipeQuery`] to run an API request
#[must_use]
pub fn query(&self) -> RustyPipeQuery {
RustyPipeQuery {
client: self.clone(),
@ -779,7 +799,7 @@ impl RustyPipe {
.get(sw_url)
.header(header::ORIGIN, origin)
.header(header::REFERER, origin)
.header(header::COOKIE, self.inner.consent_cookie.to_owned())
.header(header::COOKIE, self.inner.consent_cookie.clone())
.build()
.unwrap(),
)
@ -828,13 +848,13 @@ impl RustyPipe {
let mut desktop_client = self.inner.cache.desktop_client.write().await;
match desktop_client.get() {
Some(cdata) => cdata.version.to_owned(),
Some(cdata) => cdata.version.clone(),
None => {
log::debug!("getting desktop client version");
match self.extract_desktop_client_version().await {
Ok(version) => {
*desktop_client = CacheEntry::from(ClientData {
version: version.to_owned(),
version: version.clone(),
});
drop(desktop_client);
self.store_cache().await;
@ -860,13 +880,13 @@ impl RustyPipe {
let mut music_client = self.inner.cache.music_client.write().await;
match music_client.get() {
Some(cdata) => cdata.version.to_owned(),
Some(cdata) => cdata.version.clone(),
None => {
log::debug!("getting music client version");
match self.extract_music_client_version().await {
Ok(version) => {
*music_client = CacheEntry::from(ClientData {
version: version.to_owned(),
version: version.clone(),
});
drop(music_client);
self.store_cache().await;
@ -944,6 +964,7 @@ impl RustyPipeQuery {
/// Set the language parameter used when accessing the YouTube API
///
/// This will change multilanguage video titles, descriptions and textual dates
#[must_use]
pub fn lang(mut self, lang: Language) -> Self {
self.opts.lang = lang;
self
@ -952,6 +973,7 @@ impl RustyPipeQuery {
/// Set the country parameter used when accessing the YouTube API.
///
/// This will change trends and recommended content.
#[must_use]
pub fn country(mut self, country: Country) -> Self {
self.opts.country = validate_country(country);
self
@ -960,6 +982,7 @@ impl RustyPipeQuery {
/// Generate a report on every operation.
///
/// This should only be used for debugging.
#[must_use]
pub fn report(mut self) -> Self {
self.opts.report = true;
self
@ -969,6 +992,7 @@ impl RustyPipeQuery {
/// are warnings during deserialization (e.g. invalid items).
///
/// This should only be used for testing.
#[must_use]
pub fn strict(mut self) -> Self {
self.opts.strict = true;
self
@ -986,6 +1010,7 @@ impl RustyPipeQuery {
///
/// Note that YouTube has a rate limit on the number of requests from a single
/// visitor, so you should not use the same vistor data cookie for batch operations.
#[must_use]
pub fn visitor_data<S: Into<String>>(mut self, visitor_data: S) -> Self {
self.opts.visitor_data = Some(visitor_data.into());
self
@ -994,6 +1019,7 @@ impl RustyPipeQuery {
/// Set the YouTube visitor data cookie to an optional value
///
/// see also [`RustyPipeQuery::visitor_data`]
#[must_use]
pub fn visitor_data_opt<S: Into<String>>(mut self, visitor_data: Option<S>) -> Self {
self.opts.visitor_data = visitor_data.map(S::into);
self
@ -1011,13 +1037,10 @@ impl RustyPipeQuery {
localized: bool,
visitor_data: Option<&'a str>,
) -> YTContext {
let hl = match localized {
true => self.opts.lang,
false => Language::En,
};
let gl = match localized {
true => self.opts.country,
false => Country::Us,
let (hl, gl) = if localized {
(self.opts.lang, self.opts.country)
} else {
(Language::En, Country::Us)
};
let visitor_data = self.opts.visitor_data.as_deref().or(visitor_data);
@ -1119,7 +1142,7 @@ impl RustyPipeQuery {
))
.header(header::ORIGIN, YOUTUBE_HOME_URL)
.header(header::REFERER, YOUTUBE_HOME_URL)
.header(header::COOKIE, self.client.inner.consent_cookie.to_owned())
.header(header::COOKIE, self.client.inner.consent_cookie.clone())
.header("X-YouTube-Client-Name", "1")
.header(
"X-YouTube-Client-Version",
@ -1134,7 +1157,7 @@ impl RustyPipeQuery {
))
.header(header::ORIGIN, YOUTUBE_MUSIC_HOME_URL)
.header(header::REFERER, YOUTUBE_MUSIC_HOME_URL)
.header(header::COOKIE, self.client.inner.consent_cookie.to_owned())
.header(header::COOKIE, self.client.inner.consent_cookie.clone())
.header("X-YouTube-Client-Name", "67")
.header(
"X-YouTube-Client-Version",
@ -1187,7 +1210,7 @@ impl RustyPipeQuery {
/// Get a YouTube visitor data cookie, which is necessary for certain requests
async fn get_visitor_data(&self) -> Result<String, Error> {
match &self.opts.visitor_data {
Some(vd) => Ok(vd.to_owned()),
Some(vd) => Ok(vd.clone()),
None => self.client.get_visitor_data().await,
}
}
@ -1333,21 +1356,19 @@ impl RustyPipeQuery {
if level > Level::DBG || self.opts.report {
if let Some(reporter) = &self.client.inner.reporter {
let report = Report {
info: Default::default(),
info: RustyPipeInfo::default(),
level,
operation: format!("{operation}({id})"),
operation: &format!("{operation}({id})"),
error,
msgs,
deobf_data: deobf.cloned(),
http_request: crate::report::HTTPRequest {
url: request.url().to_string(),
method: "POST".to_string(),
url: request.url().as_str(),
method: request.method().as_str(),
req_header: request
.headers()
.iter()
.map(|(k, v)| {
(k.to_string(), v.to_str().unwrap_or_default().to_owned())
})
.map(|(k, v)| (k.as_str(), v.to_str().unwrap_or_default().to_owned()))
.collect(),
req_body: serde_json::to_string(body).unwrap_or_default(),
status: req_res.status.into(),

View file

@ -26,9 +26,10 @@ impl RustyPipeQuery {
all_albums: bool,
) -> Result<MusicArtist, Error> {
let artist_id = artist_id.as_ref();
let visitor_data = match all_albums {
true => Some(self.get_visitor_data().await?),
false => None,
let visitor_data = if all_albums {
Some(self.get_visitor_data().await?)
} else {
None
};
let res = self._music_artist(artist_id, visitor_data.as_deref()).await;
@ -196,7 +197,7 @@ fn map_artist_page(
lang,
ArtistId {
id: Some(id.to_owned()),
name: header.title.to_owned(),
name: header.title.clone(),
},
);

View file

@ -60,7 +60,7 @@ impl RustyPipeQuery {
// In rare cases, albums may have track numbers =0 (example: MPREb_RM0QfZ0eSKL)
// They should be replaced with the track number derived from the previous track.
let mut n_prev = 0;
for track in album.tracks.iter_mut() {
for track in &mut album.tracks {
let tn = track.track_nr.unwrap_or_default();
if tn == 0 {
n_prev += 1;
@ -80,7 +80,7 @@ impl RustyPipeQuery {
.enumerate()
.filter_map(|(i, track)| {
if track.is_video {
Some((i, track.name.to_owned()))
Some((i, track.name.clone()))
} else {
None
}
@ -97,7 +97,7 @@ impl RustyPipeQuery {
for (i, title) in to_replace {
let found_track = playlist.tracks.items.iter().find_map(|track| {
if track.name == title && !track.is_video {
Some((track.id.to_owned(), track.duration))
Some((track.id.clone(), track.duration))
} else {
None
}
@ -173,7 +173,7 @@ impl MapResponse<MusicPlaylist> for response::MusicPlaylist {
.split(|p| p == DOT_SEPARATOR)
.collect::<Vec<_>>();
parts
.get(if parts.len() > 2 { 1 } else { 0 })
.get(usize::from(parts.len() > 2))
.and_then(|txt| util::parse_numeric::<u64>(&txt[0]).ok())
})
} else {
@ -293,7 +293,7 @@ impl MapResponse<MusicAlbum> for response::MusicPlaylist {
match section {
response::music_item::ItemSection::MusicShelfRenderer(sh) => shelf = Some(sh),
response::music_item::ItemSection::MusicCarouselShelfRenderer(sh) => {
album_variants = Some(sh.contents)
album_variants = Some(sh.contents);
}
_ => (),
}
@ -355,7 +355,7 @@ impl MapResponse<MusicAlbum> for response::MusicPlaylist {
)
})
.unwrap_or_default();
let artist_id = artist_id.or_else(|| artists.first().and_then(|a| a.id.to_owned()));
let artist_id = artist_id.or_else(|| artists.first().and_then(|a| a.id.clone()));
let mut mapper = MusicListMapper::with_album(
lang,
@ -363,7 +363,7 @@ impl MapResponse<MusicAlbum> for response::MusicPlaylist {
by_va,
AlbumId {
id: id.to_owned(),
name: header.title.to_owned(),
name: header.title.clone(),
},
);
mapper.map_response(shelf.contents);

View file

@ -170,9 +170,10 @@ impl RustyPipeQuery {
) -> Result<MusicSearchFiltered<MusicPlaylistItem>, Error> {
self._music_search_playlists(
query,
match community {
true => Params::CommunityPlaylists,
false => Params::YtmPlaylists,
if community {
Params::CommunityPlaylists
} else {
Params::YtmPlaylists
},
)
.await
@ -266,7 +267,7 @@ impl MapResponse<MusicSearchResult> for response::MusicSearch {
}
response::music_search::ItemSection::ItemSectionRenderer { contents } => {
if let Some(corrected) = contents.into_iter().next() {
corrected_query = Some(corrected.showing_results_for_renderer.corrected_query)
corrected_query = Some(corrected.showing_results_for_renderer.corrected_query);
}
}
response::music_search::ItemSection::None => {}
@ -324,7 +325,7 @@ impl<T: FromYtItem> MapResponse<MusicSearchFiltered<T>> for response::MusicSearc
}
response::music_search::ItemSection::ItemSectionRenderer { contents } => {
if let Some(corrected) = contents.into_iter().next() {
corrected_query = Some(corrected.showing_results_for_renderer.corrected_query)
corrected_query = Some(corrected.showing_results_for_renderer.corrected_query);
}
}
response::music_search::ItemSection::None => {}

View file

@ -177,12 +177,12 @@ impl MapResponse<VideoPlayer> for response::Player {
}
response::player::PlayabilityStatus::LoginRequired { reason, messages } => {
let mut msg = reason;
messages.iter().for_each(|m| {
for m in &messages {
if !msg.is_empty() {
msg.push(' ');
}
msg.push_str(m);
});
}
// reason (age restriction): "Sign in to confirm your age"
// or: "This video may be inappropriate for some users."
@ -341,9 +341,9 @@ impl MapResponse<VideoPlayer> for response::Player {
+ "&sigh="
+ sigh;
let sprite_count = ((total_count as f64)
/ (frames_per_page_x * frames_per_page_y) as f64)
.ceil() as u32;
let sprite_count = (f64::from(total_count)
/ f64::from(frames_per_page_x * frames_per_page_y))
.ceil() as u32;
Some(Frameset {
url_template: url,
@ -413,11 +413,11 @@ fn deobf_nsig(
let nsig: String;
if let Some(n) = url_params.get("n") {
nsig = if n == &last_nsig[0] {
last_nsig[1].to_owned()
last_nsig[1].clone()
} else {
let nsig = deobf.deobfuscate_nsig(n)?;
last_nsig[0] = n.to_string();
last_nsig[1] = nsig.to_owned();
last_nsig[1] = nsig.clone();
nsig
};
@ -490,25 +490,19 @@ fn map_video_stream(
deobf: &Deobfuscator,
last_nsig: &mut [String; 2],
) -> MapResult<Option<VideoStream>> {
let (mtype, codecs) = match parse_mime(&f.mime_type) {
Some(x) => x,
None => {
return MapResult {
c: None,
warnings: vec![format!(
"Invalid mime type `{}` in video format {:?}",
&f.mime_type, &f
)],
}
let Some((mtype, codecs)) = parse_mime(&f.mime_type) else {
return MapResult {
c: None,
warnings: vec![format!(
"Invalid mime type `{}` in video format {:?}",
&f.mime_type, &f
)],
}
};
let format = match get_video_format(mtype) {
Some(f) => f,
None => {
return MapResult {
c: None,
warnings: vec![format!("invalid video format. itag: {}", f.itag)],
}
let Some(format) = get_video_format(mtype) else {
return MapResult {
c: None,
warnings: vec![format!("invalid video format. itag: {}", f.itag)],
}
};
let map_res = map_url(&f.url, &f.signature_cipher, deobf, last_nsig);
@ -532,9 +526,9 @@ fn map_video_stream(
quality: f.quality_label.unwrap(),
hdr: f.color_info.unwrap_or_default().primaries
== player::Primaries::ColorPrimariesBt2020,
mime: f.mime_type.to_owned(),
format,
codec: get_video_codec(codecs),
mime: f.mime_type,
throttled: url.throttled,
}),
warnings: map_res.warnings,
@ -551,25 +545,19 @@ fn map_audio_stream(
deobf: &Deobfuscator,
last_nsig: &mut [String; 2],
) -> MapResult<Option<AudioStream>> {
let (mtype, codecs) = match parse_mime(&f.mime_type) {
Some(x) => x,
None => {
return MapResult {
c: None,
warnings: vec![format!(
"Invalid mime type `{}` in video format {:?}",
&f.mime_type, &f
)],
}
let Some((mtype, codecs)) = parse_mime(&f.mime_type) else {
return MapResult {
c: None,
warnings: vec![format!(
"Invalid mime type `{}` in video format {:?}",
&f.mime_type, &f
)],
}
};
let format = match get_audio_format(mtype) {
Some(f) => f,
None => {
return MapResult {
c: None,
warnings: vec![format!("invalid audio format. itag: {}", f.itag)],
}
let Some(format) = get_audio_format(mtype) else {
return MapResult {
c: None,
warnings: vec![format!("invalid audio format. itag: {}", f.itag)],
}
};
let map_res = map_url(&f.url, &f.signature_cipher, deobf, last_nsig);
@ -586,9 +574,9 @@ fn map_audio_stream(
index_range: f.index_range,
init_range: f.init_range,
duration_ms: f.approx_duration_ms,
mime: f.mime_type.to_owned(),
format,
codec: get_audio_codec(codecs),
mime: f.mime_type,
channels: f.audio_channels,
loudness_db: f.loudness_db,
throttled: url.throttled,
@ -686,7 +674,7 @@ fn map_audio_track(
}
},
_ => {}
})
});
}
AudioTrack {

View file

@ -60,9 +60,8 @@ impl MapResponse<Playlist> for response::Playlist {
lang: crate::param::Language,
_deobf: Option<&crate::deobfuscate::DeobfData>,
) -> Result<MapResult<Playlist>, ExtractionError> {
let (contents, header) = match (self.contents, self.header) {
(Some(contents), Some(header)) => (contents, header),
_ => return Err(response::alerts_to_err(id, self.alerts)),
let (Some(contents), Some(header)) = (self.contents, self.header) else {
return Err(response::alerts_to_err(id, self.alerts));
};
let video_items = contents

View file

@ -87,11 +87,9 @@ impl From<ChannelRss> for crate::model::ChannelRss {
feed.entry
.iter()
.find_map(|entry| {
if !entry.channel_id.is_empty() {
Some(entry.channel_id.to_owned())
} else {
None
}
Some(entry.channel_id.as_str())
.filter(|id| id.is_empty())
.map(str::to_owned)
})
.or_else(|| {
feed.author

View file

@ -349,7 +349,7 @@ impl From<Icon> for crate::model::Verification {
match icon.icon_type {
IconType::Check => Self::Verified,
IconType::OfficialArtistBadge => Self::Artist,
_ => Self::None,
IconType::Like => Self::None,
}
}
}

View file

@ -500,7 +500,7 @@ impl MusicListMapper {
let pt_id = item
.navigation_endpoint
.and_then(|ne| ne.music_page())
.and_then(NavigationEndpoint::music_page)
.or_else(|| {
c1.and_then(|c1| {
c1.renderer.text.0.into_iter().next().and_then(|t| match t {
@ -796,7 +796,7 @@ impl MusicListMapper {
name: item.title,
duration: None,
cover: item.thumbnail_renderer.into(),
artist_id: artists.first().and_then(|a| a.id.to_owned()),
artist_id: artists.first().and_then(|a| a.id.clone()),
artists,
album: None,
view_count: subtitle_p2.and_then(|c| {
@ -872,7 +872,7 @@ impl MusicListMapper {
id,
name: item.title,
cover: item.thumbnail_renderer.into(),
artist_id: artists.first().and_then(|a| a.id.to_owned()),
artist_id: artists.first().and_then(|a| a.id.clone()),
artists,
album_type,
year,
@ -886,8 +886,7 @@ impl MusicListMapper {
let from_ytm = subtitle_p2
.as_ref()
.and_then(|p| p.0.first())
.map(util::is_ytm)
.unwrap_or(true);
.map_or(true, util::is_ytm);
let channel = subtitle_p2.and_then(|p| {
p.0.into_iter().find_map(|c| ChannelId::try_from(c).ok())
});
@ -973,7 +972,7 @@ impl MusicListMapper {
id,
name: card.title,
cover: card.thumbnail.into(),
artist_id: artists.first().and_then(|a| a.id.to_owned()),
artist_id: artists.first().and_then(|a| a.id.clone()),
artists,
album_type,
year: subtitle_p3.and_then(|y| util::parse_numeric(y.first_str()).ok()),
@ -1010,7 +1009,7 @@ impl MusicListMapper {
name: card.title,
duration,
cover: card.thumbnail.into(),
artist_id: artists.first().and_then(|a| a.id.to_owned()),
artist_id: artists.first().and_then(|a| a.id.clone()),
artists,
album,
view_count,
@ -1024,8 +1023,7 @@ impl MusicListMapper {
let from_ytm = subtitle_p2
.as_ref()
.and_then(|p| p.0.first())
.map(util::is_ytm)
.unwrap_or(true);
.map_or(true, util::is_ytm);
let channel = subtitle_p2
.and_then(|p| p.0.into_iter().find_map(|c| ChannelId::try_from(c).ok()));
let track_count =
@ -1128,9 +1126,10 @@ impl MusicListMapper {
///
/// Therefore it is safest to discard such responses and retry the request.
pub fn check_unknown(&self) -> Result<(), ExtractionError> {
match self.has_unknown {
true => Err(ExtractionError::InvalidData("unknown YTM items".into())),
false => Ok(()),
if self.has_unknown {
Err(ExtractionError::InvalidData("unknown YTM items".into()))
} else {
Ok(())
}
}
}
@ -1167,7 +1166,7 @@ fn map_artist_id_fallback(
fallback_artist: Option<&ArtistId>,
) -> Option<String> {
menu.and_then(|m| map_artist_id(m.menu_renderer.contents))
.or_else(|| fallback_artist.and_then(|a| a.id.to_owned()))
.or_else(|| fallback_artist.and_then(|a| a.id.clone()))
}
pub(crate) fn map_artist_id(entries: Vec<MusicItemMenuEntry>) -> Option<String> {

View file

@ -69,6 +69,7 @@ impl<'de> Deserialize<'de> for BrowseEndpoint {
let bep = BEp::deserialize(deserializer)?;
// Remove the VL prefix from the playlist id
#[allow(clippy::map_unwrap_or)]
let browse_id = bep
.browse_endpoint_context_supported_configs
.as_ref()
@ -167,9 +168,8 @@ pub(crate) enum PageType {
impl PageType {
pub(crate) fn to_url_target(self, id: String) -> Option<UrlTarget> {
match self {
PageType::Artist => Some(UrlTarget::Channel { id }),
PageType::Artist | PageType::Channel => Some(UrlTarget::Channel { id }),
PageType::Album => Some(UrlTarget::Album { id }),
PageType::Channel => Some(UrlTarget::Channel { id }),
PageType::Playlist => Some(UrlTarget::Playlist { id }),
PageType::Unknown => None,
}

View file

@ -419,8 +419,8 @@ impl<T> YouTubeListMapper<T> {
Self {
lang,
channel: Some(ChannelTag {
id: channel.id.to_owned(),
name: channel.name.to_owned(),
id: channel.id.clone(),
name: channel.name.clone(),
avatar: Vec::new(),
verification: channel.verification,
subscriber_count: channel.subscriber_count,
@ -572,14 +572,15 @@ impl<T> YouTubeListMapper<T> {
fn map_channel(&mut self, channel: ChannelRenderer) -> ChannelItem {
// channel handle instead of subscriber count (A/B test 3)
let (sc_txt, vc_text) = match channel
let (sc_txt, vc_text) = if channel
.subscriber_count_text
.as_ref()
.map(|txt| txt.starts_with('@'))
.unwrap_or_default()
{
true => (channel.video_count_text, None),
false => (channel.subscriber_count_text, channel.video_count_text),
(channel.video_count_text, None)
} else {
(channel.subscriber_count_text, channel.video_count_text)
};
ChannelItem {
@ -643,7 +644,7 @@ impl YouTubeListMapper<YouTubeItem> {
.map(|url| (l.title, util::sanitize_yt_url(&url.url)))
})
.collect(),
})
});
}
YouTubeListItem::RichItemRenderer { content } => {
self.map_item(*content);
@ -701,7 +702,7 @@ impl YouTubeListMapper<PlaylistItem> {
match item {
YouTubeListItem::PlaylistRenderer(playlist) => {
let mapped = self.map_playlist(playlist);
self.items.push(mapped)
self.items.push(mapped);
}
YouTubeListItem::ContinuationItemRenderer {
continuation_endpoint,

View file

@ -168,12 +168,13 @@ impl RustyPipeQuery {
e,
Error::Extraction(ExtractionError::NotFound { .. })
) {
match util::VIDEO_ID_REGEX.is_match(id) {
true => Ok(UrlTarget::Video {
if util::VIDEO_ID_REGEX.is_match(id) {
Ok(UrlTarget::Video {
id: id.to_owned(),
start_time: get_start_time(),
}),
false => Err(e),
})
} else {
Err(e)
}
} else {
Err(e)

View file

@ -393,7 +393,7 @@ impl MapResponse<Paginator<Comment>> for response::VideoComments {
lang,
);
comments.push(res.c);
warnings.append(&mut res.warnings)
warnings.append(&mut res.warnings);
}
response::video_details::CommentListItem::CommentRenderer(comment) => {
let mut res = map_comment(
@ -403,7 +403,7 @@ impl MapResponse<Paginator<Comment>> for response::VideoComments {
lang,
);
comments.push(res.c);
warnings.append(&mut res.warnings)
warnings.append(&mut res.warnings);
}
response::video_details::CommentListItem::ContinuationItemRenderer {
continuation_endpoint,
@ -433,11 +433,11 @@ fn map_recommendations(
let mut mapper = response::YouTubeListMapper::<VideoItem>::new(lang);
mapper.map_response(r);
if let Some(continuations) = continuations {
continuations.into_iter().for_each(|c| {
mapper.ctoken = Some(c.next_continuation_data.continuation);
})
};
mapper.ctoken = mapper.ctoken.or_else(|| {
continuations
.and_then(|c| c.into_iter().next())
.map(|c| c.next_continuation_data.continuation)
});
MapResult {
c: Paginator::new_ext(