From 27d84edf9ca391eeaba5735503e5e7bd285ec377 Mon Sep 17 00:00:00 2001 From: Goldy <153996346+g0ldyy@users.noreply.github.com> Date: Tue, 19 Aug 2025 17:09:20 +0200 Subject: [PATCH] Add is_trakt_group field to PlaylistGroup and update sorting logic - Introduced `is_trakt_group` field in `PlaylistGroup` struct to differentiate between Trakt and non-Trakt groups. - Updated relevant parsing functions in `m3u.rs`, `xtream.rs`, and `trakt.rs` to set this field appropriately. - Enhanced sorting logic in `sort.rs` to prioritize Trakt groups during playlist sorting. - Ensured backward compatibility by defaulting `is_trakt_group` to false where applicable. --- backend/src/processing/parser/m3u.rs | 2 +- backend/src/processing/parser/xtream.rs | 1 + backend/src/processing/processor/playlist.rs | 2 ++ backend/src/processing/processor/sort.rs | 23 +++++++++++++++++++ backend/src/processing/processor/trakt.rs | 1 + .../src/processing/processor/xtream_series.rs | 1 + shared/src/model/playlist.rs | 3 +++ 7 files changed, 32 insertions(+), 1 deletion(-) diff --git a/backend/src/processing/parser/m3u.rs b/backend/src/processing/parser/m3u.rs index d66790ba1..1610d8708 100644 --- a/backend/src/processing/parser/m3u.rs +++ b/backend/src/processing/parser/m3u.rs @@ -266,7 +266,7 @@ where if let Some((cluster, group_title)) = channel.map(|pli| (pli.header.xtream_cluster, &pli.header.group)) { grp_id += 1; - Some(PlaylistGroup { id: grp_id, xtream_cluster: cluster, title: group_title.to_string(), channels }) + Some(PlaylistGroup { id: grp_id, xtream_cluster: cluster, title: group_title.to_string(), channels, is_trakt_group: false }) } else { None } diff --git a/backend/src/processing/parser/xtream.rs b/backend/src/processing/parser/xtream.rs index bbd7d64c9..a40c17dd9 100644 --- a/backend/src/processing/parser/xtream.rs +++ b/backend/src/processing/parser/xtream.rs @@ -180,6 +180,7 @@ pub fn parse_xtream(input: &ConfigInput, xtream_cluster, title: category.category_name.to_string(), channels: category.channels.clone(), + is_trakt_group: false, } }).collect())) } diff --git a/backend/src/processing/processor/playlist.rs b/backend/src/processing/processor/playlist.rs index 86c7b40ed..63338a40c 100644 --- a/backend/src/processing/processor/playlist.rs +++ b/backend/src/processing/processor/playlist.rs @@ -55,6 +55,7 @@ fn filter_playlist(playlist: &mut [PlaylistGroup], target: &ConfigTarget) -> Opt title: pg.title.clone(), channels, xtream_cluster: pg.xtream_cluster, + is_trakt_group: pg.is_trakt_group, }); } } @@ -175,6 +176,7 @@ fn map_playlist(playlist: &mut [PlaylistGroup], target: &ConfigTarget) -> Option title: title.to_string(), channels: vec![channel.clone()], xtream_cluster: *cluster, + is_trakt_group: false, }); } } diff --git a/backend/src/processing/processor/sort.rs b/backend/src/processing/processor/sort.rs index f6a9a26e1..418c55778 100644 --- a/backend/src/processing/processor/sort.rs +++ b/backend/src/processing/processor/sort.rs @@ -94,7 +94,20 @@ fn playlist_comparator( } } +fn trakt_priority_comparator(a: &PlaylistGroup, b: &PlaylistGroup) -> Option { + match (a.is_trakt_group, b.is_trakt_group) { + (true, false) => Some(Ordering::Less), // Trakt categories come first + (false, true) => Some(Ordering::Greater), // Non-Trakt categories come after + _ => None, // Both are Trakt or both are not Trakt, use other sorting criteria + } +} + fn playlistgroup_comparator(a: &PlaylistGroup, b: &PlaylistGroup, group_sort: &ConfigSortGroup, match_as_ascii: bool) -> Ordering { + // Prioritize Trakt categories first + if let Some(ordering) = trakt_priority_comparator(a, b) { + return ordering; + } + let value_a = if match_as_ascii { deunicode(&a.title) } else { a.title.to_string() }; let value_b = if match_as_ascii { deunicode(&b.title) } else { b.title.to_string() }; @@ -120,6 +133,11 @@ pub(in crate::processing::processor) fn sort_playlist(target: &ConfigTarget, new let match_as_ascii = sort.match_as_ascii; if let Some(group_sort) = &sort.groups { new_playlist.sort_by(|a, b| playlistgroup_comparator(a, b, group_sort, match_as_ascii)); + } else { + // Even without specific group sorting, prioritize Trakt categories then alphabetical + new_playlist.sort_by(|a, b| { + trakt_priority_comparator(a, b).unwrap_or_else(|| a.title.cmp(&b.title)) + }); } if let Some(channel_sorts) = &sort.channels { for channel_sort in channel_sorts { @@ -132,6 +150,11 @@ pub(in crate::processing::processor) fn sort_playlist(target: &ConfigTarget, new } } } + } else { + // No sort configuration at all - still prioritize Trakt categories then alphabetical + new_playlist.sort_by(|a, b| { + trakt_priority_comparator(a, b).unwrap_or_else(|| a.title.cmp(&b.title)) + }); } } diff --git a/backend/src/processing/processor/trakt.rs b/backend/src/processing/processor/trakt.rs index 16f694835..712a16bf1 100644 --- a/backend/src/processing/processor/trakt.rs +++ b/backend/src/processing/processor/trakt.rs @@ -206,6 +206,7 @@ fn create_category_from_matches<'a>( title: String::from(group_title), channels: matched_items, xtream_cluster: cluster, + is_trakt_group: true, }) } diff --git a/backend/src/processing/processor/xtream_series.rs b/backend/src/processing/processor/xtream_series.rs index b08772a62..13ba215af 100644 --- a/backend/src/processing/processor/xtream_series.rs +++ b/backend/src/processing/processor/xtream_series.rs @@ -197,6 +197,7 @@ async fn process_series_info( title: plg.title.clone(), channels: group_series, xtream_cluster: XtreamCluster::Series, + is_trakt_group: false, }); } } diff --git a/shared/src/model/playlist.rs b/shared/src/model/playlist.rs index cbca4f3ec..4b41962d0 100644 --- a/shared/src/model/playlist.rs +++ b/shared/src/model/playlist.rs @@ -743,6 +743,9 @@ pub struct PlaylistGroup { pub channels: Vec, #[serde(skip)] pub xtream_cluster: XtreamCluster, + #[serde(skip)] + #[serde(default)] + pub is_trakt_group: bool, } impl PlaylistGroup {