| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
`video_root` (a string), `audio_root` (a string) and `photo_roots` (a list)
said the same thing three ways: three roster accessors, three ops, three MNP
messages, three admin-op subjects. They become `set_app_directories(app_key,
paths)` and its single-directory wrapper, stored under `<app>_directories` and
keyed by the app's registry name — so an application can be added without
touching this layer, which is the whole claim of the plugin architecture.
The three old names still work. Their MNP messages are handled, and the roster
falls back to the old key when the new one is unset, so a node upgraded into
this keeps working with no migration step — the plan called for a script, and
a script nobody runs on the machine where it matters is worse than a fallback.
Two things are new rather than moved:
The paths are validated. The setters this replaces accepted anything, so a
typo — or a path left behind when a root was removed — was stored happily and
then matched no entry, leaving an app showing an empty tab with nothing to
distinguish "misconfigured" from "no files yet". Deliberately not
`RootSet.resolve()`: that also refuses a currently-unavailable root, and an
operator must be able to point an app at a library on a drive they ejected.
The legacy scalar is derived, never stored. `video_root` still rides on the
handshake ack for MNP 1.0 clients; kept as a second stored value it would
drift from the list within one run, which reads as "it works after a restart".
Also here: chat's own two settings (a directory, which must be on a read-write
root because it is a destination rather than a view, and a link-preview switch
gating the unfurl path — checked before the cache, or turning it off would
still serve every preview already fetched), the `app_directories`,
`chat_directory` and `chat_link_preview` MNP messages, the plural
`<app>_directories` on the handshake ack, and `music` as the app's one
identifier where storage said `audio` and the registry said `music`.
The Music enricher now resolves a boundary per configured directory rather
than one for the group: with several, a single boundary is wrong for all but
one of them, and for Music that is the difference between reading a folder as
an artist and reading it as a release.
Suite: 11 failures, all pre-existing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011pvMdvLBG92jyhvD5pD6us
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Major finding: entry.id is a content hash, so the exact same physical
file — the same MP3, byte-for-byte — indexed into two different groups
(a shared library reused across several demo/test groups, or genuinely
the same folder shared into two groups) produces the *same id* in both.
_enriched_attempted was a single flat set of bare ids shared across every
group this node hosts. The moment one group's copy got enriched, every
other group's otherwise-identical copy read as "already attempted" and
was skipped forever — nothing else ever revisits an id once it's in this
set. That group's Music tab (or Videos tab, same bug, same set) showed
every affected file at duration 0 with no artist/album/thumbnail,
permanently, no matter how long you waited or how many times you
reloaded — group A having been enriched first was enough to silently
starve every later group of the same content.
Now keyed by (group_id, entry.id) throughout — the enrichment gate, the
sweep, and the rename re-enrichment path, for both video and audio (they
already shared the one set, and the collision risk is identical for
both). New regression test constructs two groups with byte-identical
audio content and confirms both enrich independently.
|
|
|
musicbay.md's original call — Music needs no root, tag reads are cheap so
just cover the whole shared tree — didn't hold up against a real messy
library: everything under every shared folder got mixed together with no
way to scope Music down to an actual music collection. This adds an
audio_root setting, symmetric to video_root in every respect: signed
operator op (audio_root/audio_root_ack, MNP bumped to 0.10), validated
against a real directory in the group's own roots before a signature is
even asked for, gates tag/cover enrichment exactly like video_root gates
ffprobe/TMDB (nothing runs until it's set, only files under it once it
is), and a set/change fires a one-off sweep of whatever the folder already
contains.
The old trigger — sweep everything the instant "music" joins enabled_apps
— is gone along with the root-less design it belonged to; setting
audio_root is now the trigger, mirroring set_video_root's
enrich_video_root_fn exactly.
Test coverage mirrors the video_root suite: policy (refuse before a
signature round trip, accept/store correctly) and the enrichment gate
itself (nothing without a root, only files under it, sweep on set).
|