diff --git a/CHANGELOG.md b/CHANGELOG.md index cfc1414f0..a5f903361 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,11 @@ # 3.1.7 (2025-09-xx) - Added Dark / Bright theme switch - Resource proxy retries failed requests up to three times and honors the `Retry-After` header (or waits 100 ms as a fallback) to reduce transient HTTP 400, 408, 425, 429 and all 5xx statuses errors observed by clients. -- Added `accept_unsecure_ssl_certificate` to `config.yml` +- Added `accept_unsecure_ssl_certificate` to `config.yml`. Necessary if pictures are served over https without valid ssl cert. +- Using tmdbid for VOD from get_vod_streams if available, which makes using resolve_vod for STRM generation not necessary. +- File length problem with strm generation fixed. +- Series name empty paren problem fixed. +- Default sort removed # 3.1.6 (2025-09-01) - EPG Config View added diff --git a/backend/src/processing/processor/playlist.rs b/backend/src/processing/processor/playlist.rs index e733d8dc2..962733bd8 100644 --- a/backend/src/processing/processor/playlist.rs +++ b/backend/src/processing/processor/playlist.rs @@ -507,43 +507,49 @@ async fn process_playlist_for_target(app_config: &AppConfig, info!("Playlist is empty: {}", &target.name); Ok(()) } else { - // Process Trakt categories - trakt_playlist(&client, target, errors, &mut new_playlist).await; - step.tick("trakt categories"); + if trakt_playlist(&client, target, errors, &mut new_playlist).await { + step.tick("trakt categories"); + } let mut flat_new_playlist = flatten_groups(new_playlist); step.tick("playlist merge"); - sort_playlist(target, &mut flat_new_playlist); - step.tick("playlist sort"); + if sort_playlist(target, &mut flat_new_playlist) { + step.tick("playlist sort"); + } assign_channel_no_playlist(&mut flat_new_playlist); step.tick("assigning channel numbers"); map_playlist_counter(target, &mut flat_new_playlist); step.tick("assigning channel counter"); let config = app_config.config.load(); - process_watch(&config, &client, target, &flat_new_playlist); - step.tick("group watches"); + if process_watch(&config, &client, target, &flat_new_playlist) { + step.tick("group watches"); + } let result = persist_playlist(app_config, &mut flat_new_playlist, flatten_tvguide(&new_epg).as_ref(), target).await; step.stop("Persisting playlists"); result } } -async fn trakt_playlist(client: &Arc, target: &ConfigTarget, errors: &mut Vec, playlist: &mut Vec) { +async fn trakt_playlist(client: &Arc, target: &ConfigTarget, errors: &mut Vec, playlist: &mut Vec) -> bool { match process_trakt_categories_for_target(Arc::clone(client), playlist, target).await { - Ok(trakt_categories) => { + Ok(Some(trakt_categories)) => { if !trakt_categories.is_empty() { info!("Adding {} Trakt categories to playlist", trakt_categories.len()); playlist.extend(trakt_categories); } } + Ok(None) => { + return false; + } Err(trakt_errors) => { warn!("Trakt processing failed with {} errors", trakt_errors.len()); errors.extend(trakt_errors); } } + true } fn process_epg(processed_fetched_playlists: &mut Vec) -> (Vec, Vec) { @@ -559,7 +565,7 @@ fn process_epg(processed_fetched_playlists: &mut Vec) -> (Vec, target: &ConfigTarget, new_playlist: &Vec) { +fn process_watch(cfg: &Config, client: &Arc, target: &ConfigTarget, new_playlist: &Vec) -> bool { if let Some(watches) = &target.watch { if default_as_default().eq_ignore_ascii_case(&target.name) { error!("cant watch a target with no unique name"); @@ -570,6 +576,9 @@ fn process_watch(cfg: &Config, client: &Arc, target: &ConfigTar } } } + true + } else { + false } } diff --git a/backend/src/processing/processor/sort.rs b/backend/src/processing/processor/sort.rs index 723a52d2a..38e34ab0b 100644 --- a/backend/src/processing/processor/sort.rs +++ b/backend/src/processing/processor/sort.rs @@ -115,7 +115,7 @@ fn playlistitem_comparator( playlist_comparator(channel_sort.sequence.as_ref(), channel_sort.order, &value_a, &value_b) } -pub(in crate::processing::processor) fn sort_playlist(target: &ConfigTarget, new_playlist: &mut [PlaylistGroup]) { +pub(in crate::processing::processor) fn sort_playlist(target: &ConfigTarget, new_playlist: &mut [PlaylistGroup]) -> bool { if let Some(sort) = &target.sort { let match_as_ascii = sort.match_as_ascii; if let Some(group_sort) = &sort.groups { @@ -132,6 +132,9 @@ pub(in crate::processing::processor) fn sort_playlist(target: &ConfigTarget, new } } } + true + } else { + false } } diff --git a/backend/src/processing/processor/trakt.rs b/backend/src/processing/processor/trakt.rs index 708d788bb..b8a743a70 100644 --- a/backend/src/processing/processor/trakt.rs +++ b/backend/src/processing/processor/trakt.rs @@ -253,10 +253,10 @@ impl TraktCategoriesProcessor { playlist: &[PlaylistGroup], target: &ConfigTarget, trakt_config: &TraktConfig, - ) -> Result, Vec> { + ) -> Result>, Vec> { if trakt_config.lists.is_empty() { debug!("No Trakt lists configured for target {}", target.name); - return Ok(vec![]); + return Ok(None); } info!("Processing {} Trakt lists for target {}", trakt_config.lists.len(), target.name); @@ -288,17 +288,17 @@ impl TraktCategoriesProcessor { info!("Trakt processing complete: created {} categories with {total_matches} total matches", new_categories.len()); - Ok(new_categories) + Ok(Some(new_categories)) } } pub async fn process_trakt_categories_for_target( http_client: Arc, playlist: &[PlaylistGroup], target: &ConfigTarget, -) -> Result, Vec> { +) -> Result>, Vec> { let Some(trakt_config) = target.get_xtream_output().and_then(|output| output.trakt.as_ref()) else { debug!("No Trakt configuration found for target {}", target.name); - return Ok(vec![]); + return Ok(None); }; let processor = TraktCategoriesProcessor::new(http_client, trakt_config); diff --git a/backend/src/utils/network/xtream.rs b/backend/src/utils/network/xtream.rs index b0be89c7c..dd709e0fd 100644 --- a/backend/src/utils/network/xtream.rs +++ b/backend/src/utils/network/xtream.rs @@ -7,7 +7,7 @@ use shared::error::{str_to_io_error, TuliproxError}; use crate::utils::{request}; use chrono::{DateTime}; use log::{info, warn}; -use std::cmp::Ordering; +// use std::cmp::Ordering; use std::io::Error; use std::str::FromStr; use std::sync::Arc; @@ -240,7 +240,8 @@ pub async fn get_xtream_playlist(cfg: &Config, client: Arc, inp } } } - playlist_groups.sort_by(|a, b| a.title.partial_cmp(&b.title).unwrap_or(Ordering::Greater)); + // why we need a sort if there is no sort defined ? + //playlist_groups.sort_by(|a, b| a.title.partial_cmp(&b.title).unwrap_or(Ordering::Greater)); for (grp_id, plg) in (1_u32..).zip(playlist_groups.iter_mut()) { plg.id = grp_id;