diff options
author | metamuffin <metamuffin@disroot.org> | 2025-02-07 16:10:29 +0100 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2025-02-07 16:10:29 +0100 |
commit | 314bca18a43e22368a87fcad53d91190fe101b60 (patch) | |
tree | 3cf89e2a6904c5a6abae8fe92a5a3aa96501a63c | |
parent | 346095d20e3d817d150cbea49e87a49fbcaa2304 (diff) | |
download | jellything-314bca18a43e22368a87fcad53d91190fe101b60.tar jellything-314bca18a43e22368a87fcad53d91190fe101b60.tar.bz2 jellything-314bca18a43e22368a87fcad53d91190fe101b60.tar.zst |
nodes modified since endpoint
-rw-r--r-- | base/src/database.rs | 21 | ||||
-rw-r--r-- | common/src/impl.rs | 15 | ||||
-rw-r--r-- | common/src/lib.rs | 4 | ||||
-rw-r--r-- | server/src/routes/api.rs | 23 | ||||
-rw-r--r-- | server/src/routes/mod.rs | 6 |
5 files changed, 62 insertions, 7 deletions
diff --git a/base/src/database.rs b/base/src/database.rs index c0699f0..54ead92 100644 --- a/base/src/database.rs +++ b/base/src/database.rs @@ -4,7 +4,7 @@ Copyright (C) 2025 metamuffin <metamuffin.org> */ use anyhow::{anyhow, bail, Context, Result}; -use bincode::{Decode, Encode}; +use bincode::{config::standard, Decode, Encode}; use jellycommon::{ user::{NodeUserData, User}, Node, NodeID, @@ -13,6 +13,7 @@ use log::info; use redb::{Durability, ReadableTable, StorageError, TableDefinition}; use std::{ fs::create_dir_all, + hash::{DefaultHasher, Hasher}, path::Path, sync::{Arc, RwLock}, time::SystemTime, @@ -164,7 +165,17 @@ impl Database { let mut t_node_children = txn.open_table(T_NODE_CHILDREN)?; let mut t_node_external_id = txn.open_table(T_NODE_EXTERNAL_ID)?; let mut node = t_node.get(id.0)?.map(|v| v.value().0).unwrap_or_default(); + + let mut dh_before = HashWriter(DefaultHasher::new()); + bincode::encode_into_writer(&node, &mut dh_before, standard()).unwrap(); update(&mut node)?; + let mut dh_after = HashWriter(DefaultHasher::new()); + bincode::encode_into_writer(&node, &mut dh_after, standard()).unwrap(); + + if dh_before.0.finish() == dh_after.0.finish() { + return Ok(()); + } + for parent in &node.parents { t_node_children.insert((parent.0, id.0), ())?; } @@ -463,6 +474,14 @@ impl NodeTextSearchIndex { } } +pub struct HashWriter(DefaultHasher); +impl bincode::enc::write::Writer for HashWriter { + fn write(&mut self, bytes: &[u8]) -> std::result::Result<(), bincode::error::EncodeError> { + self.0.write(bytes); + Ok(()) + } +} + #[derive(Debug)] #[cfg(not(feature = "db_json"))] pub struct Ser<T>(pub T); diff --git a/common/src/impl.rs b/common/src/impl.rs index db702d3..1aeac22 100644 --- a/common/src/impl.rs +++ b/common/src/impl.rs @@ -8,6 +8,7 @@ use crate::{ TraktKind, }; use hex::FromHexError; +use serde::{Deserialize, Serialize}; use std::{fmt::Display, str::FromStr}; impl SourceTrackKind { @@ -242,3 +243,17 @@ impl From<String> for NodeIDOrSlug { Self::Slug(value) } } +impl Serialize for NodeID { + fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + serializer.serialize_str(&hex::encode(self.0)) + } +} +impl<'de> Deserialize<'de> for NodeID { + fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { + let mut k = [0; 32]; + hex::decode_to_slice(String::deserialize(deserializer)?, &mut k).map_err(|_| { + <D::Error as serde::de::Error>::custom(format_args!("nodeid hex invalid")) + })?; + Ok(NodeID(k)) + } +} diff --git a/common/src/lib.rs b/common/src/lib.rs index 43adfba..3f9cfc3 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -22,9 +22,7 @@ use std::{ path::PathBuf, }; -#[derive( - Clone, Copy, Debug, Serialize, Deserialize, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, -)] +#[derive(Clone, Copy, Debug, Encode, Decode, PartialEq, Eq, PartialOrd, Ord)] pub struct NodeID(pub [u8; 32]); pub enum NodeIDOrSlug { diff --git a/server/src/routes/api.rs b/server/src/routes/api.rs index f761a8f..4e2211f 100644 --- a/server/src/routes/api.rs +++ b/server/src/routes/api.rs @@ -4,12 +4,15 @@ Copyright (C) 2025 metamuffin <metamuffin.org> */ use super::ui::{ - account::{login_logic, session::AdminSession}, + account::{ + login_logic, + session::{AdminSession, Session}, + }, error::MyResult, }; use crate::database::Database; use jellybase::assetfed::AssetInner; -use jellycommon::user::CreateSessionParams; +use jellycommon::{user::CreateSessionParams, NodeID, Visibility}; use rocket::{ get, http::MediaType, @@ -53,6 +56,22 @@ pub fn r_api_asset_token_raw(_admin: AdminSession, token: &str) -> MyResult<Json Ok(Json(AssetInner::deser(token)?)) } +#[get("/api/nodes_modified?<since>")] +pub fn r_api_nodes_modified_since( + _session: Session, + database: &State<Database>, + since: u64, +) -> MyResult<Json<Vec<NodeID>>> { + let mut nodes = database.get_nodes_modified_since(since)?; + nodes.retain(|id| { + database.get_node(*id).map_or(false, |n| { + n.as_ref() + .map_or(false, |n| n.visibility >= Visibility::Reduced) + }) + }); + Ok(Json(nodes)) +} + pub struct AcceptJson(bool); impl Deref for AcceptJson { type Target = bool; diff --git a/server/src/routes/mod.rs b/server/src/routes/mod.rs index e48b7cf..6d5dd5c 100644 --- a/server/src/routes/mod.rs +++ b/server/src/routes/mod.rs @@ -5,7 +5,10 @@ */ use self::playersync::{r_playersync, PlayersyncChannels}; use crate::{database::Database, routes::ui::error::MyResult}; -use api::{r_api_account_login, r_api_asset_token_raw, r_api_root, r_api_version}; +use api::{ + r_api_account_login, r_api_asset_token_raw, r_api_nodes_modified_since, r_api_root, + r_api_version, +}; use base64::Engine; use compat::{ jellyfin::{ @@ -173,6 +176,7 @@ pub fn build_rocket(database: Database, federation: Federation) -> Rocket<Build> // API r_api_account_login, r_api_asset_token_raw, + r_api_nodes_modified_since, r_api_root, r_api_version, // Compat |