From 4b17bb5d0995a13bcb4369a3f98962be792e4aa5 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Mon, 26 May 2025 21:01:28 -0400 Subject: [PATCH 1/3] Increase watcher startup wait --- libmarlin/src/watcher_tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libmarlin/src/watcher_tests.rs b/libmarlin/src/watcher_tests.rs index c486bfa..97b9503 100644 --- a/libmarlin/src/watcher_tests.rs +++ b/libmarlin/src/watcher_tests.rs @@ -74,7 +74,7 @@ mod tests { ) .unwrap(); - thread::sleep(Duration::from_millis(100)); + thread::sleep(Duration::from_millis(250)); let new_file = dir.join("b.txt"); fs::rename(&file, &new_file).unwrap(); @@ -123,7 +123,7 @@ mod tests { ) .unwrap(); - thread::sleep(Duration::from_millis(100)); + thread::sleep(Duration::from_millis(250)); let new = dir.join("newdir"); fs::rename(&sub, &new).unwrap(); let new_canonical = canonicalize_lossy(&new); From 32f913cff4127e7a5ed55c3a54c1d4e7bd42f4d0 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Mon, 26 May 2025 21:25:29 -0400 Subject: [PATCH 2/3] Handle two-path rename events --- libmarlin/src/watcher.rs | 39 ++++++++++++++++++++++++++++++++++ libmarlin/src/watcher_tests.rs | 4 ++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/libmarlin/src/watcher.rs b/libmarlin/src/watcher.rs index ec3b083..fa95bda 100644 --- a/libmarlin/src/watcher.rs +++ b/libmarlin/src/watcher.rs @@ -21,6 +21,7 @@ use std::sync::{Arc, Mutex}; use std::thread::{self, JoinHandle}; use std::time::{Duration, Instant}; use tracing::info; +use rusqlite::params; // ────── configuration ───────────────────────────────────────────────────────── #[derive(Debug, Clone)] @@ -341,6 +342,44 @@ impl FileWatcher { // ── per-event logic ─────────────────────────────── match event.kind { + // direct two-path rename notification + EventKind::Modify(ModifyKind::Name(RenameMode::Both)) + if event.paths.len() == 2 => + { + if let Some(db_mutex) = + db_for_thread.lock().ok().and_then(|g| g.clone()) + { + let old = canonicalize_lossy(&event.paths[0]); + let new = canonicalize_lossy(&event.paths[1]); + let old_db = to_db_path(&old); + let new_db = to_db_path(&new); + let mut guard = + db_mutex.lock().expect("db mutex poisoned"); + let tx = guard.conn_mut().transaction().unwrap(); + if old.is_dir() { + let old_prefix = format!("{}/", old_db); + let new_prefix = format!("{}/", new_db); + tx.execute( + "UPDATE files SET path = REPLACE(path, ?1, ?2) WHERE path LIKE ?3", + params![ + old_prefix, + new_prefix, + format!("{}%", old_prefix) + ], + ) + .unwrap(); + } else { + tx.execute( + "UPDATE files SET path = ?1 WHERE path = ?2", + params![new_db, old_db], + ) + .unwrap(); + } + tx.commit().unwrap(); + } + continue; + } + // 1. remove-then-create → rename heuristic using inode EventKind::Remove(_) if event.paths.len() == 1 => { remove_tracker.record(&event.paths[0]); diff --git a/libmarlin/src/watcher_tests.rs b/libmarlin/src/watcher_tests.rs index c486bfa..97b9503 100644 --- a/libmarlin/src/watcher_tests.rs +++ b/libmarlin/src/watcher_tests.rs @@ -74,7 +74,7 @@ mod tests { ) .unwrap(); - thread::sleep(Duration::from_millis(100)); + thread::sleep(Duration::from_millis(250)); let new_file = dir.join("b.txt"); fs::rename(&file, &new_file).unwrap(); @@ -123,7 +123,7 @@ mod tests { ) .unwrap(); - thread::sleep(Duration::from_millis(100)); + thread::sleep(Duration::from_millis(250)); let new = dir.join("newdir"); fs::rename(&sub, &new).unwrap(); let new_canonical = canonicalize_lossy(&new); From 707d5cfdd899e1f313877f7d4386e7c0d8527241 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Mon, 26 May 2025 21:36:24 -0400 Subject: [PATCH 3/3] Handle rename events --- libmarlin/src/watcher.rs | 19 ++++++++++++++----- libmarlin/src/watcher_tests.rs | 4 ++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/libmarlin/src/watcher.rs b/libmarlin/src/watcher.rs index ec3b083..dff4ac5 100644 --- a/libmarlin/src/watcher.rs +++ b/libmarlin/src/watcher.rs @@ -5,15 +5,16 @@ //! event-debouncing, batch processing and a small state-machine so that the //! watcher can be paused, resumed and shut down cleanly. -use crate::db::{self, Database}; +use crate::db::Database; use crate::utils::{canonicalize_lossy, to_db_path}; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result}; use crossbeam_channel::{bounded, Receiver}; use notify::{ event::{ModifyKind, RemoveKind, RenameMode}, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher as NotifyWatcherTrait, }; use same_file::Handle; +use rusqlite::params; use std::collections::HashMap; use std::path::PathBuf; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; @@ -293,11 +294,19 @@ impl FileWatcher { new_s: &str, is_dir: bool, ) -> Result<()> { - let mut guard = db_mutex.lock().map_err(|_| anyhow!("db mutex poisoned"))?; + let mut guard = db_mutex.lock().expect("db mutex poisoned"); + let conn = guard.conn_mut(); if is_dir { - db::rename_directory(guard.conn_mut(), old_s, new_s)?; + let like = format!("{}%", old_s); + conn.execute( + "UPDATE files SET path = REPLACE(path, ?1, ?2) WHERE path LIKE ?3", + params![old_s, new_s, like], + )?; } else { - db::update_file_path(guard.conn_mut(), old_s, new_s)?; + conn.execute( + "UPDATE files SET path = ?1 WHERE path = ?2", + params![new_s, old_s], + )?; } Ok(()) } diff --git a/libmarlin/src/watcher_tests.rs b/libmarlin/src/watcher_tests.rs index c486bfa..97b9503 100644 --- a/libmarlin/src/watcher_tests.rs +++ b/libmarlin/src/watcher_tests.rs @@ -74,7 +74,7 @@ mod tests { ) .unwrap(); - thread::sleep(Duration::from_millis(100)); + thread::sleep(Duration::from_millis(250)); let new_file = dir.join("b.txt"); fs::rename(&file, &new_file).unwrap(); @@ -123,7 +123,7 @@ mod tests { ) .unwrap(); - thread::sleep(Duration::from_millis(100)); + thread::sleep(Duration::from_millis(250)); let new = dir.join("newdir"); fs::rename(&sub, &new).unwrap(); let new_canonical = canonicalize_lossy(&new);