diff options
Diffstat (limited to 'ui/src')
| -rw-r--r-- | ui/src/filter_sort.rs | 191 | ||||
| -rw-r--r-- | ui/src/format.rs | 84 | ||||
| -rw-r--r-- | ui/src/home.rs | 4 | ||||
| -rw-r--r-- | ui/src/items.rs | 2 | ||||
| -rw-r--r-- | ui/src/search.rs | 6 |
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 } |