A library keeps none of the options the dialog posted, including its watch #167

Closed
opened 2026-08-21 11:27:38 +02:00 by Claude · 2 comments
Collaborator

A library made through the wizard or the dashboard keeps its name and its
paths. Every other option the dialog posted is dropped, and there is no route
that can put one back.

crates/ephyra-http/src/routes/library/structure/editing.rs::add reads
PathInfos off the posted LibraryOptions and hardcodes the seven flags that
follow it — fetch_metadata, measure_loudness, extract_chapter_images,
extract_trickplay_images, save_trickplay_with_media, extract_images,
monitor. ::options, which is POST /Library/VirtualFolders/LibraryOptions
and the only way a client edits an existing library, keeps the paths and
returns 204 for the rest. All seven are read by the server, so all seven are
reachable by hand-editing config.toml and by nothing else.

The comment there is right about the defaults and wrong about what arrives.
LibraryOptions.EnableRealtimeMonitor is an auto-property with no initialiser,
so an options object built in code has it off — but the pinned bundle's library
dialog spells the checkbox checked="checked", so every library created through
the UI posts EnableRealtimeMonitor: true. Upstream stores what arrived:
LibraryManager.AddVirtualFolder ends in
CollectionFolder.SaveLibraryOptions(virtualFolderPath, options).

What it costs, on a library that changes underneath the server. A film was
replaced on the share between the scan and the playback — same directory, a
different release, so a different file name. The row still names the old file:

Error opening input: No such file or directory
file:/…/TRaumschiff Surprise Periode 1 (2004) …[Bluray-720p]…-ShitBusters.mkv

ffmpeg exits, the segment request answers 500, and nothing notices, because
the library the wizard made is watched by nobody — so the row stays wrong until
the twelve-hourly scan comes round. The watch itself is written and working
(LibraryConfig::monitor, watch_libraries in crates/ephyra-server/src/serve.rs);
it is only ever off because the route that creates a library says so.

Where to start

add is the one that matters: take the flags off the posted options instead of
spelling them, and keep upstream's defaults for a request that posts none.
options is the same change on the editing side, and without it a library
cannot be corrected from the dashboard.

Where an option's behaviour is not ported, storing what was asked for and doing
nothing is still better than reporting a checkbox nobody kept — but the five
that are read should be honoured.

How to know it is done

A library created through the wizard with realtime monitoring ticked has
monitor = true in config.toml, and a file replaced under it is noticed
without a scheduled scan. Unticking it in the dashboard turns it off. A test
that posts options and reads the configuration back, failing today.

House rules

CLAUDE.md is the short version and these are the parts that bite: tests run
under cargo nextest; upstream's source is the checkout in upstream/ at the
pin
; generated files are regenerated rather than edited; a comment says what
the code does, not how it got there.

A test that cannot fail is worse than none.

A library made through the wizard or the dashboard keeps its name and its paths. Every other option the dialog posted is dropped, and there is no route that can put one back. `crates/ephyra-http/src/routes/library/structure/editing.rs::add` reads `PathInfos` off the posted `LibraryOptions` and hardcodes the seven flags that follow it — `fetch_metadata`, `measure_loudness`, `extract_chapter_images`, `extract_trickplay_images`, `save_trickplay_with_media`, `extract_images`, `monitor`. `::options`, which is `POST /Library/VirtualFolders/LibraryOptions` and the only way a client edits an existing library, keeps the paths and returns `204` for the rest. All seven are read by the server, so all seven are reachable by hand-editing `config.toml` and by nothing else. **The comment there is right about the defaults and wrong about what arrives.** `LibraryOptions.EnableRealtimeMonitor` is an auto-property with no initialiser, so an options object built in code has it off — but the pinned bundle's library dialog spells the checkbox `checked="checked"`, so every library created through the UI posts `EnableRealtimeMonitor: true`. Upstream stores what arrived: `LibraryManager.AddVirtualFolder` ends in `CollectionFolder.SaveLibraryOptions(virtualFolderPath, options)`. **What it costs, on a library that changes underneath the server.** A film was replaced on the share between the scan and the playback — same directory, a different release, so a different file name. The row still names the old file: Error opening input: No such file or directory file:/…/TRaumschiff Surprise Periode 1 (2004) …[Bluray-720p]…-ShitBusters.mkv ffmpeg exits, the segment request answers `500`, and nothing notices, because the library the wizard made is watched by nobody — so the row stays wrong until the twelve-hourly scan comes round. The watch itself is written and working (`LibraryConfig::monitor`, `watch_libraries` in `crates/ephyra-server/src/serve.rs`); it is only ever off because the route that creates a library says so. ## Where to start `add` is the one that matters: take the flags off the posted options instead of spelling them, and keep upstream's defaults for a request that posts none. `options` is the same change on the editing side, and without it a library cannot be corrected from the dashboard. Where an option's behaviour is not ported, storing what was asked for and doing nothing is still better than reporting a checkbox nobody kept — but the five that are read should be honoured. ## How to know it is done A library created through the wizard with realtime monitoring ticked has `monitor = true` in `config.toml`, and a file replaced under it is noticed without a scheduled scan. Unticking it in the dashboard turns it off. A test that posts options and reads the configuration back, failing today. ### House rules `CLAUDE.md` is the short version and these are the parts that bite: tests run under `cargo nextest`; upstream's source is the checkout in `upstream/` **at the pin**; generated files are regenerated rather than edited; a comment says what the code does, not how it got there. A test that cannot fail is worse than none.
Author
Collaborator

Landed in 7cd13b3, with a review fix in 9f43537.

add takes all seven flags off the posted options instead of spelling them, and
keeps upstream's defaults for a request that posts none — every flag off, the
image extractors on. options is the same change on the editing side, so a
library can be corrected from the dashboard; covering became one case of
amending a library rather than the only way to touch one.

Two of the seven have no boolean in LibraryOptions and are read off the posted
type options instead:

  • extract_images — the three in-box extractors are image fetchers like any
    other, and unticking them all is how a library says it wants no picture taken
    out of a file. One setting for every type here, so a library that keeps an
    extractor for one type keeps it for all of them; the narrower choice the
    dialog can express is not one this server can honour, and the comment says so.
  • fetch_metadata — this was mapped from EnableInternetProviders and the
    review caught it. Upstream marks that field [Obsolete("Disable remote providers in TypeOptions instead")] and reads it nowhere, and the pinned
    bundle posts it as a literal true
    (EnableInternetProviders:!0 in the bundle). So unticking every downloader in
    the dashboard would have left the library still asking TMDb, with no request
    that could turn it off. It comes off the posted MetadataFetchers now.

The realtime watch works out of this: a library created through the wizard has
monitor = true in config.toml, and unticking it in the dashboard turns it
off. Tests over both, each checked against the mutation it exists to catch.

Landed in 7cd13b3, with a review fix in 9f43537. `add` takes all seven flags off the posted options instead of spelling them, and keeps upstream's defaults for a request that posts none — every flag off, the image extractors on. `options` is the same change on the editing side, so a library can be corrected from the dashboard; `covering` became one case of `amending` a library rather than the only way to touch one. Two of the seven have no boolean in `LibraryOptions` and are read off the posted type options instead: - **`extract_images`** — the three in-box extractors are image fetchers like any other, and unticking them all is how a library says it wants no picture taken out of a file. One setting for every type here, so a library that keeps an extractor for one type keeps it for all of them; the narrower choice the dialog can express is not one this server can honour, and the comment says so. - **`fetch_metadata`** — this was mapped from `EnableInternetProviders` and the review caught it. Upstream marks that field `[Obsolete("Disable remote providers in TypeOptions instead")]` and reads it **nowhere**, and the pinned bundle posts it as a literal `true` (`EnableInternetProviders:!0` in the bundle). So unticking every downloader in the dashboard would have left the library still asking TMDb, with no request that could turn it off. It comes off the posted `MetadataFetchers` now. The realtime watch works out of this: a library created through the wizard has `monitor = true` in `config.toml`, and unticking it in the dashboard turns it off. Tests over both, each checked against the mutation it exists to catch.
Author
Collaborator

Reported again: a library still could not be set to monitored from the dashboard.

The write half landed here and works — add and options both keep all seven
flags. What was missing is the half a dialog reads. GET /Library/VirtualFolders
answered:

"LibraryOptions":{"EnableRealtimeMonitor":false,"Enabled":false,"EnablePhotos":false,
  "SeasonZeroDisplayName":"","TypeOptions":[],"PathInfos":[{"Path":"…"}], …}

— the paths filled and every other field left at the DTO's default, whatever the
library was configured with. The bundle sets each checkbox from that object
(.chkEnableRealtimeMonitor.checked = t.EnableRealtimeMonitor), so the dialog
opened with the watch unticked however it had been saved, and getLibraryOptions
posted that blank straight back over the configuration on the next save.

Fixed in 46931ff: the seven flags are read back out of LibraryConfig, and the
settings this server does not keep answer upstream's own constructor rather than
false. The round trip — save, reopen, save again untouched — is a test.

Reported again: a library still could not be set to monitored from the dashboard. The write half landed here and works — `add` and `options` both keep all seven flags. What was missing is the half a dialog reads. `GET /Library/VirtualFolders` answered: "LibraryOptions":{"EnableRealtimeMonitor":false,"Enabled":false,"EnablePhotos":false, "SeasonZeroDisplayName":"","TypeOptions":[],"PathInfos":[{"Path":"…"}], …} — the paths filled and every other field left at the DTO's default, whatever the library was configured with. The bundle sets each checkbox from that object (`.chkEnableRealtimeMonitor.checked = t.EnableRealtimeMonitor`), so the dialog opened with the watch unticked however it had been saved, and `getLibraryOptions` posted that blank straight back over the configuration on the next save. Fixed in 46931ff: the seven flags are read back out of `LibraryConfig`, and the settings this server does not keep answer upstream's own constructor rather than `false`. The round trip — save, reopen, save again untouched — is a test.
Sign in to join this conversation.
No description provided.