fix: correct timezone offset for parsed dates, add timezone_local option
This commit is contained in:
parent
3a2370b97c
commit
a5a7be5b4e
15 changed files with 202 additions and 98 deletions
|
|
@ -220,7 +220,6 @@ impl MapResponse<Channel<Paginator<VideoItem>>> for response::Channel {
|
|||
|
||||
let mut mapper = response::YouTubeListMapper::<VideoItem>::with_channel(
|
||||
ctx.lang,
|
||||
ctx.utc_offset,
|
||||
&channel_data.c,
|
||||
channel_data.warnings,
|
||||
);
|
||||
|
|
@ -266,7 +265,6 @@ impl MapResponse<Channel<Paginator<PlaylistItem>>> for response::Channel {
|
|||
|
||||
let mut mapper = response::YouTubeListMapper::<PlaylistItem>::with_channel(
|
||||
ctx.lang,
|
||||
ctx.utc_offset,
|
||||
&channel_data.c,
|
||||
channel_data.warnings,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -177,10 +177,11 @@ impl MapResponse<Paginator<HistoryItem<VideoItem>>> for response::History {
|
|||
for item in items.c {
|
||||
match item {
|
||||
response::YouTubeListItem::ItemSectionRenderer { header, contents } => {
|
||||
let mut mapper = YouTubeListMapper::<VideoItem>::new(ctx.lang, ctx.utc_offset);
|
||||
let mut mapper = YouTubeListMapper::<VideoItem>::new(ctx.lang);
|
||||
mapper.map_response(contents);
|
||||
mapper.conv_history_items(
|
||||
header.map(|h| h.item_section_header_renderer.title),
|
||||
ctx.utc_offset,
|
||||
&mut map_res,
|
||||
);
|
||||
}
|
||||
|
|
@ -228,7 +229,7 @@ impl MapResponse<Paginator<VideoItem>> for response::History {
|
|||
.section_list_renderer
|
||||
.contents;
|
||||
|
||||
let mut mapper = response::YouTubeListMapper::<VideoItem>::new(ctx.lang, ctx.utc_offset);
|
||||
let mut mapper = response::YouTubeListMapper::<VideoItem>::new(ctx.lang);
|
||||
mapper.map_response(items);
|
||||
|
||||
Ok(MapResult {
|
||||
|
|
|
|||
|
|
@ -897,8 +897,6 @@ impl RustyPipeBuilder {
|
|||
/// Set the timezone and its associated UTC offset in minutes used
|
||||
/// when accessing the YouTube API.
|
||||
///
|
||||
/// This will also change the UTC offset of the returned dates.
|
||||
///
|
||||
/// **Default value**: `0` (UTC)
|
||||
///
|
||||
/// **Info**: you can set this option for individual queries, too
|
||||
|
|
@ -909,6 +907,16 @@ impl RustyPipeBuilder {
|
|||
self
|
||||
}
|
||||
|
||||
/// Access the YouTube API using the local system timezone
|
||||
///
|
||||
/// If the local timezone could not be determined, an error is logged and RustyPipe falls
|
||||
/// back to UTC.
|
||||
#[must_use]
|
||||
pub fn timezone_local(self) -> Self {
|
||||
let (timezone, utc_offset_minutes) = local_tz_offset();
|
||||
self.timezone(timezone, utc_offset_minutes)
|
||||
}
|
||||
|
||||
/// Generate a report on every operation.
|
||||
///
|
||||
/// This should only be used for debugging.
|
||||
|
|
@ -1689,8 +1697,6 @@ impl RustyPipeQuery {
|
|||
|
||||
/// Set the timezone and its associated UTC offset in minutes used
|
||||
/// when accessing the YouTube API.
|
||||
///
|
||||
/// This will also change the UTC offset of the returned dates.
|
||||
#[must_use]
|
||||
pub fn timezone<S: Into<String>>(mut self, timezone: S, utc_offset_minutes: i16) -> Self {
|
||||
self.opts.timezone = Some(timezone.into());
|
||||
|
|
@ -1698,6 +1704,13 @@ impl RustyPipeQuery {
|
|||
self
|
||||
}
|
||||
|
||||
/// Access the YouTube API using the local system timezone
|
||||
#[must_use]
|
||||
pub fn timezone_local(self) -> Self {
|
||||
let (timezone, utc_offset_minutes) = local_tz_offset();
|
||||
self.timezone(timezone, utc_offset_minutes)
|
||||
}
|
||||
|
||||
/// Generate a report on every operation.
|
||||
///
|
||||
/// This should only be used for debugging.
|
||||
|
|
@ -2611,6 +2624,19 @@ fn validate_country(country: Country) -> Country {
|
|||
}
|
||||
}
|
||||
|
||||
fn local_tz_offset() -> (String, i16) {
|
||||
match (
|
||||
util::local_timezone_name(),
|
||||
UtcOffset::current_local_offset().map_err(|_| Error::Other("indeterminate offset".into())),
|
||||
) {
|
||||
(Ok(timezone), Ok(offset)) => (timezone, offset.whole_minutes()),
|
||||
(Err(e), _) | (_, Err(e)) => {
|
||||
tracing::error!("{e}");
|
||||
("UTC".to_owned(), 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ impl MapResponse<Paginator<HistoryItem<TrackItem>>> for response::MusicHistory {
|
|||
};
|
||||
let mut mapper = MusicListMapper::new(ctx.lang);
|
||||
mapper.map_response(shelf.contents);
|
||||
mapper.conv_history_items(shelf.title, &mut map_res);
|
||||
mapper.conv_history_items(shelf.title, ctx.utc_offset, &mut map_res);
|
||||
}
|
||||
|
||||
let ctoken = contents
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ impl MapResponse<Paginator<YouTubeItem>> for response::Continuation {
|
|||
let estimated_results = self.estimated_results;
|
||||
let items = continuation_items(self);
|
||||
|
||||
let mut mapper = response::YouTubeListMapper::<YouTubeItem>::new(ctx.lang, ctx.utc_offset);
|
||||
let mut mapper = response::YouTubeListMapper::<YouTubeItem>::new(ctx.lang);
|
||||
mapper.map_response(items);
|
||||
|
||||
Ok(MapResult {
|
||||
|
|
@ -237,11 +237,11 @@ impl MapResponse<Paginator<HistoryItem<VideoItem>>> for response::Continuation {
|
|||
for item in items.c {
|
||||
match item {
|
||||
response::YouTubeListItem::ItemSectionRenderer { header, contents } => {
|
||||
let mut mapper =
|
||||
response::YouTubeListMapper::<VideoItem>::new(ctx.lang, ctx.utc_offset);
|
||||
let mut mapper = response::YouTubeListMapper::<VideoItem>::new(ctx.lang);
|
||||
mapper.map_response(contents);
|
||||
mapper.conv_history_items(
|
||||
header.map(|h| h.item_section_header_renderer.title),
|
||||
ctx.utc_offset,
|
||||
&mut map_res,
|
||||
);
|
||||
}
|
||||
|
|
@ -281,7 +281,7 @@ impl MapResponse<Paginator<HistoryItem<TrackItem>>> for response::MusicContinuat
|
|||
let mut map_shelf = |shelf: response::music_item::MusicShelf| {
|
||||
let mut mapper = MusicListMapper::new(ctx.lang);
|
||||
mapper.map_response(shelf.contents);
|
||||
mapper.conv_history_items(shelf.title, &mut map_res);
|
||||
mapper.conv_history_items(shelf.title, ctx.utc_offset, &mut map_res);
|
||||
continuations.extend(shelf.continuations);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ impl MapResponse<Playlist> for response::Playlist {
|
|||
.playlist_video_list_renderer
|
||||
.contents;
|
||||
|
||||
let mut mapper = response::YouTubeListMapper::<VideoItem>::new(ctx.lang, ctx.utc_offset);
|
||||
let mut mapper = response::YouTubeListMapper::<VideoItem>::new(ctx.lang);
|
||||
mapper.map_response(video_items);
|
||||
|
||||
let (description, thumbnails, last_update_txt) = match self.sidebar {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use serde::Deserialize;
|
||||
use serde_with::{rust::deserialize_ignore_any, serde_as, DefaultOnError, VecSkipError};
|
||||
use time::UtcOffset;
|
||||
|
||||
use crate::{
|
||||
model::{
|
||||
|
|
@ -1272,6 +1273,7 @@ impl MusicListMapper {
|
|||
pub fn conv_history_items(
|
||||
self,
|
||||
date_txt: Option<String>,
|
||||
utc_offset: UtcOffset,
|
||||
res: &mut MapResult<Vec<HistoryItem<TrackItem>>>,
|
||||
) {
|
||||
res.warnings.extend(self.warnings);
|
||||
|
|
@ -1282,7 +1284,12 @@ impl MusicListMapper {
|
|||
.map(|item| HistoryItem {
|
||||
item,
|
||||
playback_date: date_txt.as_deref().and_then(|s| {
|
||||
timeago::parse_textual_date_to_d(self.lang, s, &mut res.warnings)
|
||||
timeago::parse_textual_date_to_d(
|
||||
self.lang,
|
||||
utc_offset,
|
||||
s,
|
||||
&mut res.warnings,
|
||||
)
|
||||
}),
|
||||
playback_date_txt: date_txt.clone(),
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -461,7 +461,6 @@ impl IsShort for Vec<TimeOverlay> {
|
|||
#[derive(Debug)]
|
||||
pub(crate) struct YouTubeListMapper<T> {
|
||||
lang: Language,
|
||||
utc_offset: UtcOffset,
|
||||
channel: Option<ChannelTag>,
|
||||
|
||||
pub items: Vec<T>,
|
||||
|
|
@ -471,10 +470,9 @@ pub(crate) struct YouTubeListMapper<T> {
|
|||
}
|
||||
|
||||
impl<T> YouTubeListMapper<T> {
|
||||
pub fn new(lang: Language, utc_offset: UtcOffset) -> Self {
|
||||
pub fn new(lang: Language) -> Self {
|
||||
Self {
|
||||
lang,
|
||||
utc_offset,
|
||||
channel: None,
|
||||
items: Vec::new(),
|
||||
warnings: Vec::new(),
|
||||
|
|
@ -483,15 +481,9 @@ impl<T> YouTubeListMapper<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn with_channel<C>(
|
||||
lang: Language,
|
||||
utc_offset: UtcOffset,
|
||||
channel: &Channel<C>,
|
||||
warnings: Vec<String>,
|
||||
) -> Self {
|
||||
pub fn with_channel<C>(lang: Language, channel: &Channel<C>, warnings: Vec<String>) -> Self {
|
||||
Self {
|
||||
lang,
|
||||
utc_offset,
|
||||
channel: Some(ChannelTag {
|
||||
id: channel.id.clone(),
|
||||
name: channel.name.clone(),
|
||||
|
|
@ -794,12 +786,7 @@ impl<T> YouTubeListMapper<T> {
|
|||
thumbnail: tn.image.into(),
|
||||
channel,
|
||||
publish_date: publish_date_txt.as_deref().and_then(|t| {
|
||||
timeago::parse_textual_date_or_warn(
|
||||
self.lang,
|
||||
self.utc_offset,
|
||||
t,
|
||||
&mut self.warnings,
|
||||
)
|
||||
timeago::parse_timeago_dt_or_warn(self.lang, t, &mut self.warnings)
|
||||
}),
|
||||
publish_date_txt,
|
||||
view_count,
|
||||
|
|
@ -920,17 +907,16 @@ impl YouTubeListMapper<VideoItem> {
|
|||
pub(crate) fn conv_history_items(
|
||||
self,
|
||||
date_txt: Option<String>,
|
||||
utc_offset: UtcOffset,
|
||||
res: &mut MapResult<Vec<HistoryItem<VideoItem>>>,
|
||||
) {
|
||||
res.warnings.extend(self.warnings);
|
||||
res.c.extend(self.items.into_iter().map(|item| {
|
||||
HistoryItem {
|
||||
item,
|
||||
playback_date: date_txt.as_deref().and_then(|s| {
|
||||
timeago::parse_textual_date_to_d(self.lang, s, &mut res.warnings)
|
||||
}),
|
||||
playback_date_txt: date_txt.clone(),
|
||||
}
|
||||
res.c.extend(self.items.into_iter().map(|item| HistoryItem {
|
||||
item,
|
||||
playback_date: date_txt.as_deref().and_then(|s| {
|
||||
timeago::parse_textual_date_to_d(self.lang, utc_offset, s, &mut res.warnings)
|
||||
}),
|
||||
playback_date_txt: date_txt.clone(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ impl<T: FromYtItem> MapResponse<SearchResult<T>> for response::Search {
|
|||
.section_list_renderer
|
||||
.contents;
|
||||
|
||||
let mut mapper = response::YouTubeListMapper::<YouTubeItem>::new(ctx.lang, ctx.utc_offset);
|
||||
let mut mapper = response::YouTubeListMapper::<YouTubeItem>::new(ctx.lang);
|
||||
mapper.map_response(items);
|
||||
|
||||
Ok(MapResult {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ impl MapResponse<Vec<VideoItem>> for response::Trending {
|
|||
.section_list_renderer
|
||||
.contents;
|
||||
|
||||
let mut mapper = response::YouTubeListMapper::<VideoItem>::new(ctx.lang, ctx.utc_offset);
|
||||
let mut mapper = response::YouTubeListMapper::<VideoItem>::new(ctx.lang);
|
||||
mapper.map_response(items);
|
||||
|
||||
Ok(MapResult {
|
||||
|
|
|
|||
|
|
@ -475,7 +475,7 @@ fn map_recommendations(
|
|||
visitor_data: Option<String>,
|
||||
ctx: &MapRespCtx<'_>,
|
||||
) -> MapResult<Paginator<VideoItem>> {
|
||||
let mut mapper = response::YouTubeListMapper::<VideoItem>::new(ctx.lang, ctx.utc_offset);
|
||||
let mut mapper = response::YouTubeListMapper::<VideoItem>::new(ctx.lang);
|
||||
mapper.map_response(r);
|
||||
|
||||
mapper.ctoken = mapper.ctoken.or_else(|| {
|
||||
|
|
|
|||
Reference in a new issue