diff options
Diffstat (limited to 'server/src/ui')
-rw-r--r-- | server/src/ui/account/settings.rs | 52 | ||||
-rw-r--r-- | server/src/ui/browser.rs | 25 | ||||
-rw-r--r-- | server/src/ui/home.rs | 10 | ||||
-rw-r--r-- | server/src/ui/mod.rs | 1 | ||||
-rw-r--r-- | server/src/ui/node.rs | 46 | ||||
-rw-r--r-- | server/src/ui/search.rs | 16 | ||||
-rw-r--r-- | server/src/ui/sort.rs | 103 | ||||
-rw-r--r-- | server/src/ui/stats.rs | 68 |
8 files changed, 19 insertions, 302 deletions
diff --git a/server/src/ui/account/settings.rs b/server/src/ui/account/settings.rs index 4047e4f..3b53bc4 100644 --- a/server/src/ui/account/settings.rs +++ b/server/src/ui/account/settings.rs @@ -51,57 +51,7 @@ fn settings_page( LayoutPage { title: "Settings".to_string(), class: Some("settings"), - content: markup::new! { - h1 { "Settings" } - @if let Some(flash) = &flash { - @match flash { - Ok(mesg) => { section.message { p.success { @mesg } } } - Err(err) => { section.message { p.error { @format!("{err}") } } } - } - } - h2 { @trs(&lang, "account") } - a.switch_account[href=uri!(r_account_login())] { "Switch Account" } - form[method="POST", action=uri!(r_account_settings_post())] { - label[for="username"] { @trs(&lang, "account.username") } - input[type="text", id="username", disabled, value=&session.user.name]; - input[type="submit", disabled, value=&*tr(lang, "settings.immutable")]; - } - form[method="POST", action=uri!(r_account_settings_post())] { - label[for="display_name"] { @trs(&lang, "account.display_name") } - input[type="text", id="display_name", name="display_name", value=&session.user.display_name]; - input[type="submit", value=&*tr(lang, "settings.update")]; - } - form[method="POST", action=uri!(r_account_settings_post())] { - label[for="password"] { @trs(&lang, "account.password") } - input[type="password", id="password", name="password"]; - input[type="submit", value=&*tr(lang, "settings.update")]; - } - h2 { @trs(&lang, "settings.appearance") } - form[method="POST", action=uri!(r_account_settings_post())] { - fieldset { - legend { @trs(&lang, "settings.appearance.theme") } - @for (t, tlabel) in Theme::LIST { - label { input[type="radio", name="theme", value=A(*t), checked=session.user.theme==*t]; @tlabel } br; - } - } - input[type="submit", value=&*tr(lang, "settings.apply")]; - } - form[method="POST", action=uri!(r_account_settings_post())] { - fieldset { - legend { @trs(&lang, "settings.player_preference") } - @for (t, tlabel) in PlayerKind::LIST { - label { input[type="radio", name="player_preference", value=A(*t), checked=session.user.player_preference==*t]; @tlabel } br; - } - } - input[type="submit", value=&*tr(lang, "settings.apply")]; - } - form[method="POST", action=uri!(r_account_settings_post())] { - label[for="native_secret"] { "Native Secret" } - input[type="password", id="native_secret", name="native_secret"]; - input[type="submit", value=&*tr(lang, "settings.update")]; - p { "The secret can be found in " code{"$XDG_CONFIG_HOME/jellynative_secret"} " or by clicking " a.button[href="jellynative://show-secret-v1"] { "Show Secret" } "." } - } - }, + content: } } diff --git a/server/src/ui/browser.rs b/server/src/ui/browser.rs index f7eac93..b780934 100644 --- a/server/src/ui/browser.rs +++ b/server/src/ui/browser.rs @@ -30,29 +30,10 @@ pub fn r_all_items_filter( lang: AcceptLanguage, ) -> Result<Either<DynLayoutPage<'_>, Json<ApiItemsResponse>>, MyError> { let AcceptLanguage(lang) = lang; - let mut items = db.list_nodes_with_udata(sess.user.name.as_str())?; - - items.retain(|(n, _)| matches!(n.visibility, Visibility::Visible)); - - filter_and_sort_nodes( - &filter, - (SortProperty::Title, SortOrder::Ascending), - &mut items, - ); - - let page_size = 100; - let page = page.unwrap_or(0); - let offset = page * page_size; - let from = offset.min(items.len()); - let to = (offset + page_size).min(items.len()); - let max_page = items.len().div_ceil(page_size); - + + let data = all_items()?; Ok(if *aj { - Either::Right(Json(ApiItemsResponse { - count: items.len(), - pages: max_page, - items: items[from..to].to_vec(), - })) + Either::Right(Json(data)) } else { Either::Left(LayoutPage { title: "All Items".to_owned(), diff --git a/server/src/ui/home.rs b/server/src/ui/home.rs index 96b1dc2..2a79965 100644 --- a/server/src/ui/home.rs +++ b/server/src/ui/home.rs @@ -18,14 +18,12 @@ pub fn r_home( lang: AcceptLanguage, ) -> MyResult<Either<DynLayoutPage, Json<ApiHomeResponse>>> { let AcceptLanguage(lang) = lang; - + + let resp = jellylogic::home::home(&db, sess)?; Ok(if *aj { - Either::Right(Json(ApiHomeResponse { - toplevel, - categories, - })) + Either::Right(Json(resp)) } else { - Either::Left() + Either::Left(jellyui::home::home_page(resp)) }) } diff --git a/server/src/ui/mod.rs b/server/src/ui/mod.rs index 89c0e9a..6728b81 100644 --- a/server/src/ui/mod.rs +++ b/server/src/ui/mod.rs @@ -38,7 +38,6 @@ pub mod home; pub mod node; pub mod player; pub mod search; -pub mod sort; pub mod stats; pub mod style; diff --git a/server/src/ui/node.rs b/server/src/ui/node.rs index 1efcc10..5d0f1ff 100644 --- a/server/src/ui/node.rs +++ b/server/src/ui/node.rs @@ -92,49 +92,3 @@ pub async fn r_library_node_filter<'a>( }) }) } - -pub fn get_similar_media( - node: &Node, - db: &Database, - session: &Session, -) -> Result<Vec<(Arc<Node>, NodeUserData)>> { - let this_id = NodeID::from_slug(&node.slug); - let mut ranking = BTreeMap::<NodeID, usize>::new(); - for tag in &node.tags { - let nodes = db.get_tag_nodes(tag)?; - let weight = 1_000_000 / nodes.len(); - for n in nodes { - if n != this_id { - *ranking.entry(n).or_default() += weight; - } - } - } - let mut ranking = ranking.into_iter().collect::<Vec<_>>(); - ranking.sort_by_key(|(_, k)| Reverse(*k)); - ranking - .into_iter() - .take(32) - .map(|(pid, _)| db.get_node_with_userdata(pid, session)) - .collect::<anyhow::Result<Vec<_>>>() -} - -pub trait DatabaseNodeUserDataExt { - fn get_node_with_userdata( - &self, - id: NodeID, - session: &Session, - ) -> Result<(Arc<Node>, NodeUserData)>; -} -impl DatabaseNodeUserDataExt for Database { - fn get_node_with_userdata( - &self, - id: NodeID, - session: &Session, - ) -> Result<(Arc<Node>, NodeUserData)> { - Ok(( - self.get_node(id)?.ok_or(anyhow!("node does not exist"))?, - self.get_node_udata(id, &session.user.name)? - .unwrap_or_default(), - )) - } -} diff --git a/server/src/ui/search.rs b/server/src/ui/search.rs index bfe51a8..51fdcb8 100644 --- a/server/src/ui/search.rs +++ b/server/src/ui/search.rs @@ -25,21 +25,7 @@ pub async fn r_search<'a>( lang: AcceptLanguage, ) -> MyResult<Either<DynLayoutPage<'a>, Json<ApiSearchResponse>>> { let AcceptLanguage(lang) = lang; - let results = if let Some(query) = query { - let timing = Instant::now(); - let (count, ids) = db.search(query, 32, page.unwrap_or_default() * 32)?; - let mut nodes = ids - .into_iter() - .map(|id| db.get_node_with_userdata(id, &session)) - .collect::<Result<Vec<_>, anyhow::Error>>()?; - nodes.retain(|(n, _)| n.visibility >= Visibility::Reduced); - let search_dur = timing.elapsed(); - Some((count, nodes, search_dur)) - } else { - None - }; - let query = query.unwrap_or_default().to_string(); - + Ok(if *aj { let Some((count, results, _)) = results else { Err(anyhow!("no query"))? diff --git a/server/src/ui/sort.rs b/server/src/ui/sort.rs deleted file mode 100644 index 441bac6..0000000 --- a/server/src/ui/sort.rs +++ /dev/null @@ -1,103 +0,0 @@ -/* - 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(), - } -} diff --git a/server/src/ui/stats.rs b/server/src/ui/stats.rs index 345586a..a91e670 100644 --- a/server/src/ui/stats.rs +++ b/server/src/ui/stats.rs @@ -8,21 +8,14 @@ use super::{ layout::{DynLayoutPage, LayoutPage}, }; use crate::{ - api::AcceptJson, - database::Database, - locale::AcceptLanguage, - logic::session::Session, - ui::{ - layout::trs, - node::{ - format_duration, format_duration_long, format_kind, format_size, - rocket_uri_macro_r_library_node, - }, - }, - uri, + api::AcceptJson, database::Database, locale::AcceptLanguage, logic::session::Session, uri, }; use jellybase::locale::tr; -use jellycommon::{Node, NodeID, NodeKind, Visibility}; +use jellycommon::{ + api::{ApiStatsResponse, StatsBin}, + Node, NodeID, NodeKind, Visibility, +}; +use jellylogic::stats::stats; use markup::raw; use rocket::{get, serde::json::Json, Either, State}; use serde::Serialize; @@ -35,54 +28,13 @@ pub fn r_stats( db: &State<Database>, aj: AcceptJson, lang: AcceptLanguage, -) -> Result<Either<DynLayoutPage<'_>, Json<Value>>, MyError> { +) -> Result<Either<DynLayoutPage<'_>, Json<ApiStatsResponse>>, MyError> { let AcceptLanguage(lang) = lang; - let mut items = db.list_nodes_with_udata(sess.user.name.as_str())?; - items.retain(|(n, _)| n.visibility >= Visibility::Reduced); - - #[derive(Default, Serialize)] - struct Bin { - runtime: f64, - size: u64, - count: usize, - max_runtime: (f64, String), - max_size: (u64, String), - } - impl Bin { - fn update(&mut self, node: &Node) { - self.count += 1; - self.size += node.storage_size; - if node.storage_size > self.max_size.0 { - self.max_size = (node.storage_size, node.slug.clone()) - } - if let Some(m) = &node.media { - self.runtime += m.duration; - if m.duration > self.max_runtime.0 { - self.max_runtime = (m.duration, node.slug.clone()) - } - } - } - fn average_runtime(&self) -> f64 { - self.runtime / self.count as f64 - } - fn average_size(&self) -> f64 { - self.size as f64 / self.count as f64 - } - } - - let mut all = Bin::default(); - let mut kinds = BTreeMap::<NodeKind, Bin>::new(); - for (i, _) in items { - all.update(&i); - kinds.entry(i.kind).or_default().update(&i); - } + let data = stats(db)?; Ok(if *aj { - Either::Right(Json(json!({ - "all": all, - "kinds": kinds, - }))) + Either::Right(Json(data)) } else { - Either::Left() + Either::Left(1) }) } |