aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2026-03-18 17:44:58 +0100
committermetamuffin <metamuffin@disroot.org>2026-03-18 17:44:58 +0100
commit53b0268eaa850d0a7250c94373d76906a7b28250 (patch)
tree16cb09376d8446b1bb7edb60ee0f162b93ab1a92
parent3c541b7487a93d627f87d4fc1a5385cc57d5ee45 (diff)
downloadjellything-53b0268eaa850d0a7250c94373d76906a7b28250.tar
jellything-53b0268eaa850d0a7250c94373d76906a7b28250.tar.bz2
jellything-53b0268eaa850d0a7250c94373d76906a7b28250.tar.zst
filter by kind without filter parser
-rw-r--r--common/src/api.rs5
-rw-r--r--database/src/kv/mod.rs6
-rw-r--r--database/src/lib.rs2
-rw-r--r--server/src/routes/node.rs77
-rw-r--r--ui/src/components/filter.rs23
-rw-r--r--ui/src/components/mod.rs4
-rw-r--r--ui/src/components/node_list.rs47
-rw-r--r--ui/src/components/node_page.rs18
8 files changed, 123 insertions, 59 deletions
diff --git a/common/src/api.rs b/common/src/api.rs
index 061ff09..709e407 100644
--- a/common/src/api.rs
+++ b/common/src/api.rs
@@ -30,3 +30,8 @@ pub struct StatsBin {
pub max_size: u64,
pub max_size_node: String,
}
+
+#[derive(Debug, Clone)]
+pub struct NodeFilter {
+ pub kind: Option<Tag>,
+}
diff --git a/database/src/kv/mod.rs b/database/src/kv/mod.rs
index 0ce4683..816dc9f 100644
--- a/database/src/kv/mod.rs
+++ b/database/src/kv/mod.rs
@@ -14,7 +14,7 @@ pub mod prefix_iterator;
pub mod tests;
use crate::{
- Database, Query, RowNum, Transaction,
+ Database, Filter, Query, RowNum, Transaction,
kv::{
helpers::{read_counter, write_counter},
index::{iter_index, read_count_index, test_index_identical, update_index},
@@ -135,9 +135,9 @@ impl Transaction for &mut dyn jellykv::Transaction {
fn query_single(&mut self, query: Query) -> Result<Option<RowNum>> {
Ok(self.query(query)?.next().transpose()?.map(|(e, _)| e))
}
- fn count(&mut self, query: Query) -> Result<u64> {
+ fn count(&mut self, filter: Filter) -> Result<u64> {
let mut total = 0;
- for (binning, mut prefix) in query.filter.get_bins() {
+ for (binning, mut prefix) in filter.get_bins() {
let ik = IndexKey(binning, SortKey::Count);
let is = get_or_create_index(*self, &ik)?;
prefix.splice(0..0, is.to_be_bytes());
diff --git a/database/src/lib.rs b/database/src/lib.rs
index 9bd4c06..da58003 100644
--- a/database/src/lib.rs
+++ b/database/src/lib.rs
@@ -33,7 +33,7 @@ pub trait Transaction {
query: Query,
) -> Result<Box<dyn Iterator<Item = Result<(RowNum, Vec<u8>)>> + 'a>>;
fn query_single(&mut self, query: Query) -> Result<Option<RowNum>>;
- fn count(&mut self, query: Query) -> Result<u64>;
+ fn count(&mut self, filter: Filter) -> Result<u64>;
}
#[derive(Debug, Default, Clone, PartialEq)]
diff --git a/server/src/routes/node.rs b/server/src/routes/node.rs
index 506d771..240486e 100644
--- a/server/src/routes/node.rs
+++ b/server/src/routes/node.rs
@@ -12,7 +12,10 @@ use jellycommon::{
*,
};
use jellydb::{Filter, MultiBehaviour, Query, Sort, SortOrder, Transaction, ValueSort};
-use jellyui::components::node_page::NodePage;
+use jellyui::components::{
+ node_list::{NodeFilterInfo, NodeListData},
+ node_page::NodePage,
+};
use rocket::{get, response::content::RawHtml};
use std::{borrow::Cow, collections::BTreeMap};
@@ -20,8 +23,10 @@ use std::{borrow::Cow, collections::BTreeMap};
pub fn r_node(ri: RequestInfo<'_>, slug: &str) -> MyResult<RawHtml<String>> {
ri.require_user()?;
+ let filter = NodeFilter { kind: None };
+
let mut nku = None;
- let mut children = Vec::new();
+ let mut children = None;
let mut credits = Vec::new();
let mut credited = Vec::new();
ri.state.database.transaction(&mut |txn| {
@@ -31,13 +36,13 @@ pub fn r_node(ri: RequestInfo<'_>, slug: &str) -> MyResult<RawHtml<String>> {
})? {
let n = txn.get(row)?.unwrap();
nku = Some(create_nku(txn, row)?);
- children = c_children(txn, row, &n)?;
+ children = Some(c_children(txn, filter.clone(), row, &n)?);
credits = c_credits(txn, &n)?;
credited = c_credited(txn, row)?;
}
Ok(())
})?;
- let Some(nku) = nku else {
+ let (Some(nku), Some(children)) = (nku, children) else {
Err(anyhow!("no such node"))?
};
@@ -50,7 +55,25 @@ pub fn r_node(ri: RequestInfo<'_>, slug: &str) -> MyResult<RawHtml<String>> {
}))
}
-fn c_children(txn: &mut dyn Transaction, row: u64, node: &Object) -> Result<Vec<Nku<'static>>> {
+const KINDS_TAB_ORDER: &[Tag] = &[
+ KIND_COLLECTION,
+ KIND_MOVIE,
+ KIND_SHOW,
+ KIND_SERIES,
+ KIND_SEASON,
+ KIND_EPISODE,
+ KIND_CHANNEL,
+ KIND_VIDEO,
+ KIND_SHORTFORMVIDEO,
+ KIND_MUSIC,
+];
+
+fn c_children(
+ txn: &mut dyn Transaction,
+ mut filter: NodeFilter,
+ row: u64,
+ node: &Object,
+) -> Result<NodeListData<'static>> {
let kind = node.get(NO_KIND).unwrap_or(KIND_COLLECTION);
let (order, path) = match kind {
KIND_CHANNEL => (SortOrder::Descending, Path(vec![NO_RELEASEDATE.0])),
@@ -58,6 +81,28 @@ fn c_children(txn: &mut dyn Transaction, row: u64, node: &Object) -> Result<Vec<
_ => (SortOrder::Ascending, Path(vec![NO_TITLE.0])),
};
+ let mut filter_kinds = Vec::new();
+ for &kind in KINDS_TAB_ORDER {
+ let count = txn.count(Filter::All(vec![
+ Filter::Match(Path(vec![NO_VISIBILITY.0]), VISI_VISIBLE.into()),
+ Filter::Match(Path(vec![NO_PARENT.0]), row.into()),
+ Filter::Match(Path(vec![NO_KIND.0]), kind.into()),
+ ]))?;
+ if count > 0 {
+ filter_kinds.push((kind, count));
+ }
+ }
+ if filter.kind.is_none() {
+ filter.kind = filter_kinds.first().map(|(t, _)| *t);
+ }
+
+ let mut filters = vec![
+ Filter::Match(Path(vec![NO_VISIBILITY.0]), VISI_VISIBLE.into()),
+ Filter::Match(Path(vec![NO_PARENT.0]), row.into()),
+ ];
+ if let Some(kind) = filter.kind {
+ filters.push(Filter::Match(Path(vec![NO_KIND.0]), kind.into()))
+ }
let children_rows = txn
.query(Query {
sort: Sort::Value(ValueSort {
@@ -66,19 +111,22 @@ fn c_children(txn: &mut dyn Transaction, row: u64, node: &Object) -> Result<Vec<
order,
path,
}),
- filter: Filter::All(vec![
- Filter::Match(Path(vec![NO_VISIBILITY.0]), VISI_VISIBLE.into()),
- Filter::Match(Path(vec![NO_PARENT.0]), row.into()),
- ]),
+ filter: Filter::All(filters),
..Default::default()
})?
.collect::<Result<Vec<_>>>()?;
- let mut list = Vec::new();
+ let mut items = Vec::new();
for (row, _) in children_rows {
- list.push(create_nku(txn, row)?);
+ items.push(create_nku(txn, row)?);
}
- Ok(list)
+ Ok(NodeListData {
+ filter_info: NodeFilterInfo {
+ cur: filter,
+ kinds: filter_kinds,
+ },
+ items,
+ })
}
fn c_credits(txn: &mut dyn Transaction, node: &Object) -> Result<Vec<(Tag, Vec<Nku<'static>>)>> {
@@ -136,10 +184,7 @@ fn c_credited(txn: &mut dyn Transaction, row: u64) -> Result<Vec<Nku<'static>>>
}
pub fn create_nku(txn: &mut dyn Transaction, row: u64) -> Result<Nku<'static>> {
- let child_count = txn.count(Query {
- filter: Filter::Match(Path(vec![NO_PARENT.0]), row.into()),
- ..Default::default()
- })?;
+ let child_count = txn.count(Filter::Match(Path(vec![NO_PARENT.0]), row.into()))?;
Ok(Nku {
node: Cow::Owned(txn.get(row)?.unwrap()),
role: None,
diff --git a/ui/src/components/filter.rs b/ui/src/components/filter.rs
deleted file mode 100644
index cd2b63c..0000000
--- a/ui/src/components/filter.rs
+++ /dev/null
@@ -1,23 +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) 2026 metamuffin <metamuffin.org>
-*/
-
-use crate::RenderInfo;
-use jellycommon::jellyobject::Tag;
-use jellyui_locale::tr;
-
-pub struct FilterInfo {
- kinds: Vec<(Tag, u64)>,
-}
-
-markup::define! {
- Filter<'a>(ri: &'a RenderInfo<'a>, info: &'a FilterInfo) { .filter {
- ul.kinds {
- @for (k, count) in &info.kinds {
- li { a[href=format!("?fk={k}")] { @tr(ri.lang, &format!("tag.kind.{k}")) ": " @count } }
- }
- }
- }}
-}
diff --git a/ui/src/components/mod.rs b/ui/src/components/mod.rs
index 5c90a26..28d09f2 100644
--- a/ui/src/components/mod.rs
+++ b/ui/src/components/mod.rs
@@ -11,9 +11,9 @@ pub mod items;
pub mod login;
pub mod message;
pub mod node_card;
+pub mod node_list;
pub mod node_page;
pub mod props;
+pub mod search;
pub mod stats;
pub mod user;
-pub mod search;
-pub mod filter;
diff --git a/ui/src/components/node_list.rs b/ui/src/components/node_list.rs
new file mode 100644
index 0000000..906deae
--- /dev/null
+++ b/ui/src/components/node_list.rs
@@ -0,0 +1,47 @@
+/*
+ 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) 2026 metamuffin <metamuffin.org>
+*/
+
+use crate::{
+ RenderInfo,
+ components::node_card::{NodeCard, NodeCardWide},
+};
+use jellycommon::{Nku, NodeFilter, jellyobject::Tag, *};
+use jellyui_locale::tr;
+
+pub struct NodeFilterInfo {
+ pub cur: NodeFilter,
+ pub kinds: Vec<(Tag, u64)>,
+}
+
+pub struct NodeListData<'a> {
+ pub filter_info: NodeFilterInfo,
+ pub items: Vec<Nku<'a>>,
+}
+
+markup::define! {
+ NodeList<'a>(ri: &'a RenderInfo<'a>, kind: Tag, data: &'a NodeListData<'a>) {
+ @if !data.items.is_empty() {
+ @Filter { ri, info: &data.filter_info }
+ @if matches!(*kind, KIND_SHOW | KIND_SEASON) {
+ ul.nl.list { @for nku in &data.items {
+ li { @NodeCardWide { ri, nku } }
+ }}
+ } else {
+ ul.nl.grid { @for nku in &data.items {
+ li { @NodeCard { ri, nku } }
+ }}
+ }
+ }
+ }
+
+ Filter<'a>(ri: &'a RenderInfo<'a>, info: &'a NodeFilterInfo) { .filter {
+ ul.kinds {
+ @for (k, count) in &info.kinds {
+ li { a[href=format!("?fk={k}")] { @tr(ri.lang, &format!("tag.kind.{k}")) ": " @count } }
+ }
+ }
+ }}
+}
diff --git a/ui/src/components/node_page.rs b/ui/src/components/node_page.rs
index 29fb6b9..bcee525 100644
--- a/ui/src/components/node_page.rs
+++ b/ui/src/components/node_page.rs
@@ -7,7 +7,8 @@
use crate::{
RenderInfo,
components::{
- node_card::{NodeCard, NodeCardWide},
+ node_card::NodeCard,
+ node_list::{NodeList, NodeListData},
props::Props,
},
format::format_duration,
@@ -40,7 +41,7 @@ markup::define! {
NodePage<'a>(
ri: &'a RenderInfo<'a>,
nku: Nku<'a>,
- children: &'a [Nku<'a>],
+ children: &'a NodeListData<'a>,
credits: &'a [(Tag, Vec<Nku<'a>>)],
credited: &'a [Nku<'a>]
) {
@@ -183,18 +184,7 @@ markup::define! {
li { @NodeCard { ri, nku } }
}}
}
- @if !children.is_empty() {
- // @Filter { ri, info }
- @if matches!(node.get(NO_KIND).unwrap_or(KIND_COLLECTION), KIND_SHOW | KIND_SEASON) {
- ul.nl.list { @for nku in *children {
- li { @NodeCardWide { ri, nku } }
- }}
- } else {
- ul.nl.grid { @for nku in *children {
- li { @NodeCard { ri, nku } }
- }}
- }
- }
+ @NodeList { ri, data: children, kind: node.get(NO_KIND).unwrap_or(KIND_COLLECTION) }
}
Player<'a>(ri: &'a RenderInfo<'a>, nku: Nku<'a>) {