1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
/*
This file is part of jellything (https://codeberg.org/metamuffin/jellything)
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use jellycommon::{
api::{FilterProperty, NodeFilterSort, SortOrder, SortProperty},
helpers::SortAnyway,
user::NodeUserData,
Node, NodeKind, Rating,
};
use rocket::{
http::uri::fmt::{Query, UriDisplay},
FromForm, FromFormField, UriDisplayQuery,
};
use std::sync::Arc;
pub fn filter_and_sort_nodes(
f: &NodeFilterSort,
default_sort: (SortProperty, SortOrder),
nodes: &mut Vec<(Arc<Node>, NodeUserData)>,
) {
let sort_prop = f.sort_by.unwrap_or(default_sort.0);
nodes.retain(|(node, _udata)| {
let mut o = true;
if let Some(prop) = &f.filter_kind {
o = false;
for p in prop {
o |= match p {
// FilterProperty::FederationLocal => node.federated.is_none(),
// FilterProperty::FederationRemote => node.federated.is_some(),
FilterProperty::KindMovie => node.kind == NodeKind::Movie,
FilterProperty::KindVideo => node.kind == NodeKind::Video,
FilterProperty::KindShortFormVideo => node.kind == NodeKind::ShortFormVideo,
FilterProperty::KindMusic => node.kind == NodeKind::Music,
FilterProperty::KindCollection => node.kind == NodeKind::Collection,
FilterProperty::KindChannel => node.kind == NodeKind::Channel,
FilterProperty::KindShow => node.kind == NodeKind::Show,
FilterProperty::KindSeries => node.kind == NodeKind::Series,
FilterProperty::KindSeason => node.kind == NodeKind::Season,
FilterProperty::KindEpisode => node.kind == NodeKind::Episode,
// FilterProperty::Watched => udata.watched == WatchedState::Watched,
// FilterProperty::Unwatched => udata.watched == WatchedState::None,
// FilterProperty::WatchProgress => {
// matches!(udata.watched, WatchedState::Progress(_))
// }
_ => false, // TODO
}
}
}
match sort_prop {
SortProperty::ReleaseDate => o &= node.release_date.is_some(),
SortProperty::Duration => o &= node.media.is_some(),
_ => (),
}
o
});
match sort_prop {
SortProperty::Duration => {
nodes.sort_by_key(|(n, _)| (n.media.as_ref().unwrap().duration * 1000.) as i64)
}
SortProperty::ReleaseDate => {
nodes.sort_by_key(|(n, _)| n.release_date.expect("asserted above"))
}
SortProperty::Title => nodes.sort_by(|(a, _), (b, _)| a.title.cmp(&b.title)),
SortProperty::Index => nodes.sort_by(|(a, _), (b, _)| {
a.index
.unwrap_or(usize::MAX)
.cmp(&b.index.unwrap_or(usize::MAX))
}),
SortProperty::RatingRottenTomatoes => nodes.sort_by_cached_key(|(n, _)| {
SortAnyway(*n.ratings.get(&Rating::RottenTomatoes).unwrap_or(&0.))
}),
SortProperty::RatingMetacritic => nodes.sort_by_cached_key(|(n, _)| {
SortAnyway(*n.ratings.get(&Rating::Metacritic).unwrap_or(&0.))
}),
SortProperty::RatingImdb => nodes
.sort_by_cached_key(|(n, _)| SortAnyway(*n.ratings.get(&Rating::Imdb).unwrap_or(&0.))),
SortProperty::RatingTmdb => nodes
.sort_by_cached_key(|(n, _)| SortAnyway(*n.ratings.get(&Rating::Tmdb).unwrap_or(&0.))),
SortProperty::RatingYoutubeViews => nodes.sort_by_cached_key(|(n, _)| {
SortAnyway(*n.ratings.get(&Rating::YoutubeViews).unwrap_or(&0.))
}),
SortProperty::RatingYoutubeLikes => nodes.sort_by_cached_key(|(n, _)| {
SortAnyway(*n.ratings.get(&Rating::YoutubeLikes).unwrap_or(&0.))
}),
SortProperty::RatingYoutubeFollowers => nodes.sort_by_cached_key(|(n, _)| {
SortAnyway(*n.ratings.get(&Rating::YoutubeFollowers).unwrap_or(&0.))
}),
SortProperty::RatingLikesDivViews => nodes.sort_by_cached_key(|(n, _)| {
SortAnyway(
*n.ratings.get(&Rating::YoutubeLikes).unwrap_or(&0.)
/ (1. + *n.ratings.get(&Rating::YoutubeViews).unwrap_or(&0.)),
)
}),
SortProperty::RatingUser => nodes.sort_by_cached_key(|(_, u)| u.rating),
}
match f.sort_order.unwrap_or(default_sort.1) {
SortOrder::Ascending => (),
SortOrder::Descending => nodes.reverse(),
}
}
|