diff --git a/CHANGELOG.md b/CHANGELOG.md index 91b35a459..ab3f91924 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog # 3.3.0 (2025-11-xx) +- !BREAKING CHANGE! Diue to some structure changes the old data format is invalid. You need to clean your data folder and update the playlists - !BREAKING CHANGE! config.yml threads attribute is now renamed to process_parallel and is a boolean (true or false). - !BREAKING CHANGE! config.yml adds a reverse proxy config field rewrite_secret to keep resource URLs valid after restart. - !BREAKING CHANGE! removed `forced_retry_interval_secs`. diff --git a/backend/src/library/metadata.rs b/backend/src/library/metadata.rs index 9c4d19b7b..49b313066 100644 --- a/backend/src/library/metadata.rs +++ b/backend/src/library/metadata.rs @@ -120,21 +120,31 @@ pub struct SeriesMetadata { pub seasons: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub episodes: Option>, + #[serde(default)] pub source: MetadataSource, + #[serde(default)] pub number_of_episodes: u32, + #[serde(default)] pub number_of_seasons: u32, // Last updated timestamp (Unix epoch) + #[serde(default)] pub last_updated: i64, + #[serde(default)] pub videos: Option> } // Episode metadata for TV series #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EpisodeMetadata { + #[serde(default)] pub id: u32, + #[serde(default)] pub tmdb_id: u32, + #[serde(default)] pub title: String, + #[serde(default)] pub season: u32, + #[serde(default)] pub episode: u32, // Aired date (ISO 8601 format) #[serde(skip_serializing_if = "Option::is_none")] diff --git a/backend/src/processing/parser/xtream.rs b/backend/src/processing/parser/xtream.rs index f8d7fa822..ede77df91 100644 --- a/backend/src/processing/parser/xtream.rs +++ b/backend/src/processing/parser/xtream.rs @@ -26,6 +26,7 @@ async fn map_to_xtream_category(categories: DynReader) -> Result Result, TuliproxError> { spawn_blocking(move || { let reader = tokio_util::io::SyncIoBridge::new(streams); + let parsed: Result, serde_json::Error> = match xtream_cluster { XtreamCluster::Live => serde_json::from_reader::<_, Vec>(reader).map(|list| list.into_iter().map(StreamProperties::Live).collect()), XtreamCluster::Video => serde_json::from_reader::<_, Vec>(reader).map(|list| list.into_iter().map(Box::new).map(StreamProperties::Video).collect()), diff --git a/backend/src/utils/network/request.rs b/backend/src/utils/network/request.rs index ef48b29fd..1dcdce9cb 100644 --- a/backend/src/utils/network/request.rs +++ b/backend/src/utils/network/request.rs @@ -415,12 +415,9 @@ pub async fn get_remote_content_as_stream( } async fn get_remote_content(client: &reqwest::Client, input: &InputSource, headers: Option<&HeaderMap>, url: &Url, disabled_headers: Option<&ReverseProxyDisabledHeaderConfig>) -> Result<(String, String), Error> { - let start_time = Instant::now(); - let (mut stream, response_url) = get_remote_content_as_stream(client, input, headers, url, disabled_headers).await.map_err(|e| str_to_io_error(&format!("Failed to read content: {e}")))?; let mut content = String::new(); stream.read_to_string(&mut content).await.map_err(|e| str_to_io_error(&format!("Failed to read content: {e}")))?; - debug_if_enabled!("Request took: {} {}", format_elapsed_time(start_time.elapsed().as_secs()), sanitize_sensitive_info(url.as_str())); Ok((content, response_url)) } @@ -529,7 +526,7 @@ pub async fn download_text_content_as_stream( } async fn download_json_content(client: &reqwest::Client, disabled_headers: Option<&ReverseProxyDisabledHeaderConfig>, input: &InputSource, persist_filepath: Option) -> Result { - debug_if_enabled!("downloading json content from {}", sanitize_sensitive_info(&input.url)); + debug_if_enabled!("Downloading json content from {}", sanitize_sensitive_info(&input.url)); match download_text_content(client, disabled_headers, input, None, persist_filepath).await { Ok((content, _response_url)) => { match serde_json::from_str::(&content) { @@ -549,7 +546,7 @@ pub async fn get_input_json_content(client: &reqwest::Client, disabled_headers: } async fn download_json_content_as_stream(client: &reqwest::Client, disabled_headers: Option<&ReverseProxyDisabledHeaderConfig>, input: &InputSource, persist_filepath: Option) -> Result { - debug_if_enabled!("downloading json content from {}", sanitize_sensitive_info(&input.url)); + debug_if_enabled!("Downloading json content from {}", sanitize_sensitive_info(&input.url)); match download_text_content_as_stream(client, disabled_headers, input, None, persist_filepath).await { Ok((reader, _response_url)) => Ok(reader), Err(err) => Err(err) diff --git a/shared/src/model/stream_properties.rs b/shared/src/model/stream_properties.rs index a4b9eb65b..aa5cd8b77 100644 --- a/shared/src/model/stream_properties.rs +++ b/shared/src/model/stream_properties.rs @@ -183,7 +183,7 @@ pub struct SeriesStreamProperties { pub youtube_trailer: String, #[serde(default, deserialize_with = "deserialize_number_from_string")] pub tmdb: Option, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub details: Option, } @@ -423,7 +423,6 @@ impl VideoStreamProperties { } } - impl SeriesStreamProperties { pub fn from_info(info: &XtreamSeriesInfo, pli: &PlaylistItem) -> SeriesStreamProperties { SeriesStreamProperties {