aboutsummaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
Diffstat (limited to 'ui')
-rw-r--r--ui/src/filter_sort.rs191
-rw-r--r--ui/src/format.rs84
-rw-r--r--ui/src/home.rs4
-rw-r--r--ui/src/items.rs2
-rw-r--r--ui/src/search.rs6
5 files changed, 134 insertions, 153 deletions
diff --git a/ui/src/filter_sort.rs b/ui/src/filter_sort.rs
index 80395e5..55cb113 100644
--- a/ui/src/filter_sort.rs
+++ b/ui/src/filter_sort.rs
@@ -3,128 +3,109 @@
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
-use markup::RenderAttributeValue;
-const SORT_CATS: &[(&str, &[(SortProperty, &str)])] = {
- &[
- (
- "filter_sort.sort.general",
- &[(Title, "node.title"), (ReleaseDate, "node.release_date")],
- ),
- ("filter_sort.sort.media", &[(Duration, "media.duration")]),
- (
- "filter_sort.sort.rating",
- &[
- (RatingImdb, "rating.imdb"),
- (RatingTmdb, "rating.tmdb"),
- (RatingMetacritic, "rating.metacritic"),
- (RatingRottenTomatoes, "rating.rotten_tomatoes"),
- (RatingYoutubeFollowers, "rating.youtube_followers"),
- (RatingYoutubeLikes, "rating.youtube_likes"),
- (RatingYoutubeViews, "rating.youtube_views"),
- (RatingUser, "filter_sort.sort.rating.user"),
- (
- RatingLikesDivViews,
- "filter_sort.sort.rating.likes_div_views",
- ),
- ],
- ),
- ]
-};
-const FILTER_CATS: &[(&str, &[(FilterProperty, &str)])] = {
- &[
- (
- "filter_sort.filter.kind",
- &[
- (KindMovie, "kind.movie"),
- (KindVideo, "kind.video"),
- (KindShortFormVideo, "kind.short_form_video"),
- (KindMusic, "kind.music"),
- (KindCollection, "kind.collection"),
- (KindChannel, "kind.channel"),
- (KindShow, "kind.show"),
- (KindSeries, "kind.series"),
- (KindSeason, "kind.season"),
- (KindEpisode, "kind.episode"),
- ],
- ),
- (
- "filter_sort.filter.federation",
- &[
- (FederationLocal, "federation.local"),
- (FederationRemote, "federation.remote"),
- ],
- ),
- (
- "filter_sort.filter.watched",
- &[
- (Watched, "watched.watched"),
- (Unwatched, "watched.none"),
- (WatchProgress, "watched.progress"),
- ],
- ),
- ]
-};
+use crate::scaffold::RenderInfo;
+
+// const SORT_CATS: &[(&str, &[(SortProperty, &str)])] = {
+// &[
+// (
+// "filter_sort.sort.general",
+// &[(Title, "node.title"), (ReleaseDate, "node.release_date")],
+// ),
+// ("filter_sort.sort.media", &[(Duration, "media.duration")]),
+// (
+// "filter_sort.sort.rating",
+// &[
+// (RatingImdb, "rating.imdb"),
+// (RatingTmdb, "rating.tmdb"),
+// (RatingMetacritic, "rating.metacritic"),
+// (RatingRottenTomatoes, "rating.rotten_tomatoes"),
+// (RatingYoutubeFollowers, "rating.youtube_followers"),
+// (RatingYoutubeLikes, "rating.youtube_likes"),
+// (RatingYoutubeViews, "rating.youtube_views"),
+// (RatingUser, "filter_sort.sort.rating.user"),
+// (
+// RatingLikesDivViews,
+// "filter_sort.sort.rating.likes_div_views",
+// ),
+// ],
+// ),
+// ]
+// };
+// const FILTER_CATS: &[(&str, &[(FilterProperty, &str)])] = {
+// &[
+// (
+// "filter_sort.filter.kind",
+// &[
+// (KindMovie, "kind.movie"),
+// (KindVideo, "kind.video"),
+// (KindShortFormVideo, "kind.short_form_video"),
+// (KindMusic, "kind.music"),
+// (KindCollection, "kind.collection"),
+// (KindChannel, "kind.channel"),
+// (KindShow, "kind.show"),
+// (KindSeries, "kind.series"),
+// (KindSeason, "kind.season"),
+// (KindEpisode, "kind.episode"),
+// ],
+// ),
+// (
+// "filter_sort.filter.federation",
+// &[
+// (FederationLocal, "federation.local"),
+// (FederationRemote, "federation.remote"),
+// ],
+// ),
+// (
+// "filter_sort.filter.watched",
+// &[
+// (Watched, "watched.watched"),
+// (Unwatched, "watched.none"),
+// (WatchProgress, "watched.progress"),
+// ],
+// ),
+// ]
+// };
markup::define! {
- NodeFilterSortForm<'a>(f: &'a NodeFilterSort, lang: &'a Language) {
+ NodeFilterSortForm<'a>(ri: &'a RenderInfo<'a>, f: &'a NodeFilterSort) {
details.filtersort[open=f.filter_kind.is_some() || f.sort_by.is_some()] {
summary { "Filter and Sort" }
form[method="GET", action=""] {
fieldset.filter {
legend { "Filter" }
- .categories {
- @for (cname, cat) in FILTER_CATS {
- .category {
- h3 { @trs(lang, cname) }
- @for (value, label) in *cat {
- label { input[type="checkbox", name="filter_kind", value=A(*value), checked=f.filter_kind.as_ref().map(|k|k.contains(value)).unwrap_or(true)]; @trs(lang, label) } br;
- }
- }
- }
- }
+ // .categories {
+ // @for (cname, cat) in FILTER_CATS {
+ // .category {
+ // h3 { @trs(lang, cname) }
+ // @for (value, label) in *cat {
+ // label { input[type="checkbox", name="filter_kind", value=A(*value), checked=f.filter_kind.as_ref().map(|k|k.contains(value)).unwrap_or(true)]; @trs(lang, label) } br;
+ // }
+ // }
+ // }
+ // }
}
fieldset.sortby {
legend { "Sort" }
- .categories {
- @for (cname, cat) in SORT_CATS {
- .category {
- h3 { @trs(lang, cname) }
- @for (value, label) in *cat {
- label { input[type="radio", name="sort_by", value=A(*value), checked=Some(value)==f.sort_by.as_ref()]; @trs(lang, label) } br;
- }
- }
- }
- }
+ // .categories {
+ // @for (cname, cat) in SORT_CATS {
+ // .category {
+ // h3 { @trs(lang, cname) }
+ // @for (value, label) in *cat {
+ // label { input[type="radio", name="sort_by", value=A(*value), checked=Some(value)==f.sort_by.as_ref()]; @trs(lang, label) } br;
+ // }
+ // }
+ // }
+ // }
}
fieldset.sortorder {
legend { "Sort Order" }
- @for (value, label) in [(Ascending, "filter_sort.order.asc"), (Descending, "filter_sort.order.desc")] {
- label { input[type="radio", name="sort_order", value=A(value), checked=Some(value)==f.sort_order]; @trs(lang, label) } br;
- }
+ // @for (value, label) in [(Ascending, "filter_sort.order.asc"), (Descending, "filter_sort.order.desc")] {
+ // label { input[type="radio", name="sort_order", value=A(value), checked=Some(value)==f.sort_order]; @trs(lang, label) } br;
+ // }
}
input[type="submit", value="Apply"]; a[href="?"] { "Clear" }
}
}
}
}
-
-struct A<T>(pub T);
-impl markup::Render for A<SortProperty> {
- fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
- writer.write_str(self.0.to_str())
- }
-}
-impl markup::Render for A<SortOrder> {
- fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
- writer.write_str(self.0.to_str())
- }
-}
-impl markup::Render for A<FilterProperty> {
- fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
- writer.write_str(self.0.to_str())
- }
-}
-impl RenderAttributeValue for A<SortOrder> {}
-impl RenderAttributeValue for A<FilterProperty> {}
-impl RenderAttributeValue for A<SortProperty> {}
diff --git a/ui/src/format.rs b/ui/src/format.rs
index 11040de..811211a 100644
--- a/ui/src/format.rs
+++ b/ui/src/format.rs
@@ -4,7 +4,10 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
-use jellycommon::{LANG_ENG, Language};
+use jellycommon::{
+ jellyobject::{Object, Tag},
+ *,
+};
use crate::locale::tr;
use std::fmt::Write;
@@ -71,50 +74,47 @@ fn test_duration_long() {
pub fn format_size(size: u64) -> String {
humansize::format_size(size, humansize::DECIMAL)
}
-pub fn format_kind(k: NodeKind, lang: Language) -> TrString<'static> {
- trs(
- &lang,
- match k {
- NodeKind::Unknown => "kind.unknown",
- NodeKind::Movie => "kind.movie",
- NodeKind::Video => "kind.video",
- NodeKind::Music => "kind.music",
- NodeKind::ShortFormVideo => "kind.short_form_video",
- NodeKind::Collection => "kind.collection",
- NodeKind::Channel => "kind.channel",
- NodeKind::Show => "kind.show",
- NodeKind::Series => "kind.series",
- NodeKind::Season => "kind.season",
- NodeKind::Episode => "kind.episode",
+pub fn format_kind(lang: Language, kind: Tag) -> &'static str {
+ tr(
+ lang,
+ match kind {
+ KIND_MOVIE => "kind.movie",
+ KIND_VIDEO => "kind.video",
+ KIND_MUSIC => "kind.music",
+ KIND_SHORTFORMVIDEO => "kind.short_form_video",
+ KIND_COLLECTION => "kind.collection",
+ KIND_CHANNEL => "kind.channel",
+ KIND_SHOW => "kind.show",
+ KIND_SERIES => "kind.series",
+ KIND_SEASON => "kind.season",
+ KIND_EPISODE => "kind.episode",
+ _ => "kind.unknown",
},
)
}
-pub trait MediaInfoExt {
- fn resolution_name(&self) -> &'static str;
-}
-impl MediaInfoExt for &MediaInfo {
- fn resolution_name(&self) -> &'static str {
- let mut maxdim = 0;
- for t in &self.tracks {
- if let SourceTrackKind::Video { width, height, .. } = &t.kind {
- maxdim = maxdim.max(*width.max(height))
- }
+pub fn node_resolution_name(node: &Object) -> &'static str {
+ let mut maxdim = 0;
+ for t in node.iter(NO_TRACK) {
+ if let Some(width) = t.get(TR_PIXEL_WIDTH) {
+ maxdim = maxdim.max(width)
}
-
- match maxdim {
- 30720.. => "32K",
- 15360.. => "16K",
- 7680.. => "8K UHD",
- 5120.. => "5K UHD",
- 3840.. => "4K UHD",
- 2560.. => "QHD 1440p",
- 1920.. => "FHD 1080p",
- 1280.. => "HD 720p",
- 854.. => "SD 480p",
- _ => "Unkown",
+ if let Some(height) = t.get(TR_PIXEL_HEIGHT) {
+ maxdim = maxdim.max(height)
}
}
+ match maxdim {
+ 30720.. => "32K",
+ 15360.. => "16K",
+ 7680.. => "8K UHD",
+ 5120.. => "5K UHD",
+ 3840.. => "4K UHD",
+ 2560.. => "QHD 1440p",
+ 1920.. => "FHD 1080p",
+ 1280.. => "HD 720p",
+ 854.. => "SD 480p",
+ _ => "Unkown",
+ }
}
pub fn format_count(n: impl Into<usize>) -> String {
@@ -129,13 +129,13 @@ pub fn format_count(n: impl Into<usize>) -> String {
}
}
-pub fn format_chapter(c: &Chapter) -> (String, String) {
+pub fn format_chapter(c: &Object) -> (String, String) {
(
format!(
"{}-{}",
- c.time_start.map(format_duration).unwrap_or_default(),
- c.time_end.map(format_duration).unwrap_or_default(),
+ c.get(CH_START).map(format_duration).unwrap_or_default(),
+ c.get(CH_END).map(format_duration).unwrap_or_default(),
),
- c.labels.first().map(|l| l.1.clone()).unwrap_or_default(),
+ c.get(CH_NAME).unwrap_or_default().to_string(),
)
}
diff --git a/ui/src/home.rs b/ui/src/home.rs
index 5de9e17..a3088c8 100644
--- a/ui/src/home.rs
+++ b/ui/src/home.rs
@@ -9,8 +9,8 @@ use markup::DynRender;
markup::define! {
HomePage<'a>(ri: RenderInfo<'a>, r: ApiHomeResponse) {
h2 { @tr(ri.lang, "home.bin.root").replace("{title}", &CONF.brand) }
- ul.children.hlist {@for (node, udata) in &r.toplevel {
- li { @NodeCard { node, udata, lang } }
+ ul.children.hlist {@for nodeu in &r.toplevel {
+ li { @NodeCard { ri, nodeu } }
}}
@for (name, nodes) in &r.categories {
// @if !nodes.is_empty() {
diff --git a/ui/src/items.rs b/ui/src/items.rs
index 447ec01..529a5d6 100644
--- a/ui/src/items.rs
+++ b/ui/src/items.rs
@@ -29,7 +29,7 @@ markup::define! {
impl Page for ItemsPage<'_> {
fn title(&self) -> String {
- tr(*self.lang, "home").to_string()
+ tr(self.ri.lang, "home").to_string()
}
fn to_render(&self) -> DynRender<'_> {
markup::new!(@self)
diff --git a/ui/src/search.rs b/ui/src/search.rs
index 35ae82d..0eb34b9 100644
--- a/ui/src/search.rs
+++ b/ui/src/search.rs
@@ -9,7 +9,7 @@ use markup::DynRender;
impl Page for SearchPage<'_> {
fn title(&self) -> String {
- tr(*self.lang, "search.title").to_string()
+ tr(self.ri.lang, "search.title").to_string()
}
fn class(&self) -> Option<&'static str> {
Some("search")
@@ -29,8 +29,8 @@ markup::define! {
@if let Some(r) = &r {
h2 { @tr(ri.lang, "search.results.title") }
p.stats { @tr(ri.lang, "search.results.stats").replace("{count}", &r.count.to_string()).replace("{dur}", &format!("{:?}", r.duration)) }
- ul.children {@for (node, udata) in r.results.iter() {
- li { @NodeCard { node, udata, lang } }
+ ul.children {@for nodeu in r.results.iter() {
+ li { @NodeCard { ri, nodeu } }
}}
// TODO pagination
}