Always refetch /orphans (and fix the web client's stale cache roots)

The clients cache library listings except for server-mutable folder roots.
/orphans is recomputed from the store on every visit, so a cached listing
froze the orphan set until a client restart. Add /orphans to the TUI's
mutable-roots list so entering the provider always re-walks.

The web client's list was also stale from the store refactor — it still
named the removed /captures, /queues, /bookmarks providers and omitted
/crabidy, so /crabidy (and now /orphans) listings went stale there too.
Reset it to the real mutable roots: /crabidy, /fs, /orphans.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Test User 2026-07-23 00:13:50 +02:00
parent cd33b790b5
commit 3a03114cb9
2 changed files with 25 additions and 7 deletions

View File

@ -87,12 +87,13 @@ pub struct RpcClient {
/// ///
/// The server-side folder providers mutate behind the client's back — /// The server-side folder providers mutate behind the client's back —
/// saves and captures appear under `/crabidy` (`w`/`W`), files change on /// saves and captures appear under `/crabidy` (`w`/`W`), files change on
/// disk under `/fs` — so a cached listing turns freshly captured content /// disk under `/fs`, and `/orphans` is recomputed from the store on every
/// invisible until a restart. Their listings are cheap local directory /// visit — so a cached listing turns freshly captured content (or a changed
/// orphan set) invisible until a restart. Their listings are cheap local
/// walks on the server; always refetch them. Remote provider nodes (tidal, /// walks on the server; always refetch them. Remote provider nodes (tidal,
/// youtube) keep the cache that makes back-navigation instant. /// youtube) keep the cache that makes back-navigation instant.
fn is_cacheable(path: &str) -> bool { fn is_cacheable(path: &str) -> bool {
const MUTABLE_ROOTS: [&str; 2] = ["/crabidy", "/fs"]; const MUTABLE_ROOTS: [&str; 3] = ["/crabidy", "/fs", "/orphans"];
!MUTABLE_ROOTS.iter().any(|root| { !MUTABLE_ROOTS.iter().any(|root| {
path == *root || (path.starts_with(root) && path.as_bytes().get(root.len()) == Some(&b'/')) path == *root || (path.starts_with(root) && path.as_bytes().get(root.len()) == Some(&b'/'))
}) })
@ -398,6 +399,8 @@ mod tests {
"/crabidy/current", "/crabidy/current",
"/crabidy/road trip", "/crabidy/road trip",
"/fs/music", "/fs/music",
"/orphans",
"/orphans/stray.flac",
] { ] {
assert!(!is_cacheable(path), "{path}"); assert!(!is_cacheable(path), "{path}");
} }
@ -408,5 +411,6 @@ mod tests {
// …and prefix look-alikes are not swept up. // …and prefix look-alikes are not swept up.
assert!(is_cacheable("/fsdy")); assert!(is_cacheable("/fsdy"));
assert!(is_cacheable("/crabidystore")); assert!(is_cacheable("/crabidystore"));
assert!(is_cacheable("/orphansaurus"));
} }
} }

View File

@ -69,9 +69,12 @@ pub fn delete_needs_confirmation(path: &str) -> bool {
/// Whether a library listing may be cached client-side — same rule as /// Whether a library listing may be cached client-side — same rule as
/// the TUI (`cbd-tui/src/rpc.rs`): server-side folder providers mutate /// the TUI (`cbd-tui/src/rpc.rs`): server-side folder providers mutate
/// behind the client's back and are cheap to re-list. /// behind the client's back and are cheap to re-list. `/crabidy` (saves and
/// captures), `/fs` (files on disk), and `/orphans` (recomputed from the
/// store on every visit) all change server-side, so their listings are
/// always refetched.
pub fn is_cacheable(path: &str) -> bool { pub fn is_cacheable(path: &str) -> bool {
const MUTABLE_ROOTS: [&str; 4] = ["/captures", "/queues", "/bookmarks", "/fs"]; const MUTABLE_ROOTS: [&str; 3] = ["/crabidy", "/fs", "/orphans"];
!MUTABLE_ROOTS.iter().any(|root| { !MUTABLE_ROOTS.iter().any(|root| {
path == *root || (path.starts_with(root) && path.as_bytes().get(root.len()) == Some(&b'/')) path == *root || (path.starts_with(root) && path.as_bytes().get(root.len()) == Some(&b'/'))
}) })
@ -465,10 +468,21 @@ mod tests {
#[test] #[test]
fn mutable_roots_are_never_cacheable() { fn mutable_roots_are_never_cacheable() {
for path in ["/captures", "/queues/x", "/bookmarks", "/fs/music"] { for path in [
"/crabidy",
"/crabidy/faves",
"/fs/music",
"/orphans",
"/orphans/stray.flac",
] {
assert!(!is_cacheable(path), "{path}"); assert!(!is_cacheable(path), "{path}");
} }
for path in ["/tidal/playlists", "/youtube/search", "/capturesque"] { for path in [
"/tidal/playlists",
"/youtube/search",
"/crabidystore",
"/orphansaurus",
] {
assert!(is_cacheable(path), "{path}"); assert!(is_cacheable(path), "{path}");
} }
} }