- **Breaking CHANGE** Sort refactor, now with filter

- Fixed xtream api season info, filled missing seasons attribute
- xtream api category_id = 0 filter ignored
- Fixed logo resource query
This commit is contained in:
euzu
2026-01-10 20:21:43 +01:00
parent 24e09f0c7d
commit 1f9ca609b4
11 changed files with 127 additions and 94 deletions
+5 -3
View File
@@ -220,6 +220,7 @@ pub fn metadata_cache_entry_to_xtream_movie_info(
Some(StreamProperties::Video(Box::new(properties)))
}
#[allow(clippy::too_many_lines)]
pub fn metadata_cache_entry_to_xtream_series_info(
entry: &MetadataCacheEntry,
) -> Option<StreamProperties> {
@@ -257,7 +258,7 @@ pub fn metadata_cache_entry_to_xtream_series_info(
let episode_release_date = episode.aired.as_ref().map(ToString::to_string).unwrap_or_default();
let tmdb_id = (episode.tmdb_id > 0).then_some(episode.tmdb_id);
let _ =season_data.entry(episode.season).or_insert_with(|| {
let season_entry =season_data.entry(episode.season).or_insert_with(|| {
SeriesStreamDetailSeasonProperties {
name: concat_string!(&series.title, " ", &episode.season.to_string()),
season_number: episode.season,
@@ -269,7 +270,8 @@ pub fn metadata_cache_entry_to_xtream_series_info(
cover_big: None,
duration: None,
}
}).episode_count.saturating_add(1);
});
season_entry.episode_count = season_entry.episode_count.saturating_add(1);
SeriesStreamDetailEpisodeProperties {
id: tmdb_id.unwrap_or_default(),
@@ -283,7 +285,7 @@ pub fn metadata_cache_entry_to_xtream_series_info(
tmdb: tmdb_id,
release_date: episode_release_date.clone(),
plot: episode.plot.clone(),
crew: series.actors.as_ref().map(|a| a.iter().map(|a| a.name.clone()).collect::<Vec<_>>().join(", ")),
crew: Some(actor_names.clone()),
duration_secs: episode.runtime.map_or(0, |r| r * 60),
duration: episode.runtime
.map(|r| format!("{:02}:{:02}:00", r / 60, r % 60))
+1 -1
View File
@@ -116,7 +116,7 @@ fn exec_rename(pli: &mut PlaylistItem, rename: Option<&Vec<ConfigRename>>, inter
let result = pli;
for r in renames {
let value = get_field_value(result, r.field);
let cap = r.pattern.replace_all(&*value, &r.new_name);
let cap = r.pattern.replace_all(&value, &r.new_name);
if log_enabled!(log::Level::Debug) && value != cap {
trace_if_enabled!("Renamed {}={value} to {cap}", &r.field);
}
+55 -60
View File
@@ -1,13 +1,14 @@
use crate::model::{ConfigSortRule, ConfigTarget};
use std::cmp::Ordering;
use shared::foundation::filter::{ValueProvider};
use shared::foundation::filter::ValueProvider;
use shared::model::{PlaylistGroup, SortOrder, SortTarget};
use std::cmp::Ordering;
fn direction(order: SortOrder, ordering: Ordering) -> Ordering {
match order {
SortOrder::Asc => if ordering == Ordering::Less { Ordering::Less } else { Ordering::Greater },
SortOrder::Desc => if ordering == Ordering::Less { Ordering::Greater } else { Ordering::Less },
SortOrder::None => Ordering::Equal,
match (order, ordering) {
(SortOrder::None, _)
| (_, Ordering::Equal) => Ordering::Equal,
(SortOrder::Asc, o) => o,
(SortOrder::Desc, o) => o.reverse(),
}
}
@@ -134,14 +135,16 @@ fn sort_groups(
return;
}
groups.sort_by(|a_grp, b_grp| {
for rule in &group_rules {
if rule.order == SortOrder::None {
continue;
}
let (Some(a_chan), Some(b_chan)) = (a_grp.channels.first(), b_grp.channels.first()) else {
continue;
for rule in &group_rules {
if rule.order == SortOrder::None {
continue;
}
groups.sort_by(|a_grp, b_grp| {
let (a_chan, b_chan) = match (a_grp.channels.first(), b_grp.channels.first()) {
(None, None) => return Ordering::Equal,
(Some(_), None) => return direction(rule.order, Ordering::Less),
(None, Some(_)) => return direction(rule.order, Ordering::Greater),
(Some(a), Some(b)) => (a, b),
};
let provider_a = ValueProvider { pli: a_chan, match_as_ascii };
@@ -149,9 +152,9 @@ fn sort_groups(
match (rule.filter.filter(&provider_a), rule.filter.filter(&provider_b)) {
(false, false) => return Ordering::Equal,
(true, false) => return direction(rule.order, Ordering::Less),
(true, false) => return direction(rule.order, Ordering::Less),
(false, true) => return direction(rule.order, Ordering::Greater),
(true, true) => { /* fallthrough */}
(true, true) => { /* fallthrough */ }
}
let va = provider_a.get(rule.field.as_str());
@@ -173,10 +176,9 @@ fn sort_groups(
}
}
}
}
Ordering::Equal
});
Ordering::Equal
});
}
}
fn sort_channels_in_groups(
@@ -193,59 +195,53 @@ fn sort_channels_in_groups(
return;
}
for group in groups {
group.channels.sort_by(|a, b| {
for rule in &channel_rules {
if rule.order == SortOrder::None {
continue;
}
for rule in channel_rules {
for group in &mut *groups {
if rule.order == SortOrder::None {
group.channels.sort_by(|a, b|
a.header.source_ordinal.cmp(&b.header.source_ordinal));
} else {
group.channels.sort_by(|a, b| {
let provider_a = ValueProvider { pli: a, match_as_ascii };
let provider_b = ValueProvider { pli: b, match_as_ascii };
let provider_a = ValueProvider { pli: a, match_as_ascii };
let provider_b = ValueProvider { pli: b, match_as_ascii };
match (rule.filter.filter(&provider_a), rule.filter.filter(&provider_b)) {
(false, false) => return Ordering::Equal,
(true, false) => return direction(rule.order, Ordering::Less),
(false, true) => return direction(rule.order, Ordering::Greater),
(true, true) => { /* fallthrough */ }
}
match (rule.filter.filter(&provider_a), rule.filter.filter(&provider_b)) {
(false, false) => continue,
(true, false) => return direction(rule.order, Ordering::Less),
(false, true) => return direction(rule.order, Ordering::Greater),
(true, true) => {}
}
let va = provider_a.get(rule.field.as_str());
let vb = provider_b.get(rule.field.as_str());
match (va, vb) {
(None, None) => return Ordering::Equal,
(Some(_), None) => return direction(rule.order, Ordering::Less),
(None, Some(_)) => return direction(rule.order, Ordering::Greater),
(Some(va), Some(vb)) => {
let ord = playlist_comparator(
rule.sequence.as_ref(),
rule.order,
&va,
&vb,
);
if ord != Ordering::Equal {
return ord;
let va = provider_a.get(rule.field.as_str());
let vb = provider_b.get(rule.field.as_str());
match (va, vb) {
(None, None) => Ordering::Equal,
(Some(_), None) => direction(rule.order, Ordering::Less),
(None, Some(_)) => direction(rule.order, Ordering::Greater),
(Some(va), Some(vb)) => {
playlist_comparator(
rule.sequence.as_ref(),
rule.order,
&va,
&vb,
)
}
}
}
});
}
a.header.source_ordinal.cmp(&b.header.source_ordinal)
});
}
}
}
#[cfg(test)]
mod tests {
use std::cmp::Ordering;
use crate::model::ConfigSortRule;
use crate::processing::processor::sort::playlist_comparator;
use regex::Regex;
use shared::foundation::filter::Filter;
use shared::model::{ItemField, PlaylistItem, PlaylistItemHeader, SortOrder, SortTarget};
use crate::model::ConfigSortRule;
use crate::processing::processor::sort::playlist_comparator;
use std::cmp::Ordering;
#[test]
fn test_sort() {
@@ -422,5 +418,4 @@ mod tests {
assert_eq!(expected, sorted);
}
}