A real folder drag silently did nothing for a user (the logic is fine — a
simulated drop works — so a real drop was either erroring silently or not
firing). Harden it and give a guaranteed alternative:
- onDrop now captures the entries synchronously, wraps the async walk in
try/catch, and TOASTS on any failure or empty result (no more silent no-op).
- dragEnter preventDefault + dragover dropEffect="copy" for reliable drop-target
registration.
- New "Choose a folder" affordance (a webkitdirectory <input>) that sidesteps
drag entirely — pick the album folder and it collects the files + prefills
artist/album/year from the folder name. Plus "Choose files".
tsc + build clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Manual import only accepted individual audio files — dragging a folder yielded
nothing (dataTransfer.files doesn't recurse). Now the drop zone walks the entry
tree via webkitGetAsEntry, so you can drag a whole album folder (the natural
shape of a ripped CD) and it collects the audio + cover inside. When a single
folder is dropped, artist/album/year are prefilled by parsing its name
("Artist - Album (Year)"), only filling fields you haven't typed into. Clicking
still opens the multi-file picker. No API change — same files reach the route.
web 161 tests, tsc + build clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Second Library Management feature. An "Add album" button on the Library page
opens a modal: drag-drop (or pick) the audio files (+ optional cover image),
enter artist/album/year, and Lyra writes the album into the library.
- POST /api/library/import (auth-gated, multipart): sanitizes the folder name
like the worker's safe_name, assembles into a sibling ".importing" temp dir
then renames into place (never a half-written album), writes a cover.jpg from
any image, and records a source="manual" LibraryItem with the on-disk
trackNames + a rough quality class from the extension (a later Scan re-probes /
MB-resolves for cover art + hi-res detection). Guards: 400 (missing
artist/album or no audio), 409 (folder exists / already in library).
- Library page: ImportAlbum drop-zone modal wired to refresh the grid.
web 161 tests (write-to-disk + trackNames + guards + filename sanitization).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Also: DELETE now prunes an emptied artist folder (best-effort).