aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-02-07 16:10:29 +0100
committermetamuffin <metamuffin@disroot.org>2025-02-07 16:10:29 +0100
commit314bca18a43e22368a87fcad53d91190fe101b60 (patch)
tree3cf89e2a6904c5a6abae8fe92a5a3aa96501a63c
parent346095d20e3d817d150cbea49e87a49fbcaa2304 (diff)
downloadjellything-314bca18a43e22368a87fcad53d91190fe101b60.tar
jellything-314bca18a43e22368a87fcad53d91190fe101b60.tar.bz2
jellything-314bca18a43e22368a87fcad53d91190fe101b60.tar.zst
nodes modified since endpoint
-rw-r--r--base/src/database.rs21
-rw-r--r--common/src/impl.rs15
-rw-r--r--common/src/lib.rs4
-rw-r--r--server/src/routes/api.rs23
-rw-r--r--server/src/routes/mod.rs6
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