4 Commits

Author SHA1 Message Date
andrew a9e1263b01 Render playlist configs directly in-app; retire regen.sh and playlists.json (R3)
The app owns every input to a playlist's sldl .conf (playlists in its DB,
credentials in its encrypted store), so the old
DB -> playlists.json -> regen.sh subprocess -> regex credential patch-back
chain was three serialization hops for no reason. Collapse it:

- credential_service.render_playlist_confs(db) renders each <playlist>.conf
  from _template.conf in one pass: path placeholders substituted as literal
  text, then the four credential lines set, written 0600. This is now the
  single source of .conf rendering.
- playlist_service loses _write_playlists_json and regenerate_confs; _sync_to_disk
  just calls render_playlist_confs and syncs the scheduler. No subprocess, no
  intermediate JSON file.
- render_scope for spotify/soulseek re-renders confs via the same function
  (spotify still also writes _spotify.env for the python scripts). The dead
  _patch_conf_field / _every_playlist_conf helpers are removed.
- Delete pipeline/configs/regen.sh and drop it from the Dockerfile chmod;
  update _template.conf's comments.

Nothing outside playlist_service consumed regen.sh or playlists.json (verified).
Also closes the last remnants of the S2/S3 injection surface: names are
re-validated and path/credential values are substituted as literals, never
through sed or a shell.

Verified: end-to-end render in a throwaway DB (paths, creds incl. a password
with shell metacharacters, 0600, bad-name rejection) and a live re-render of
all 17 playlist configs with credentials preserved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 15:34:53 -06:00
andrew 2d7243332d Archive dead/manual scripts to pipeline/lib/manual (R8)
Move six scripts that are not wired into the app, scheduler, or web UI out of
the active pipeline/lib into pipeline/lib/manual, so the scheduled scripts are
easy to see and these stay available for hands-on use: convert-m4a.sh,
fix-empty-album.sh, backfill-spotify-tags.sh, backfill-buy-url.py,
recover-azuracast-playlists.py, import-dj-collection.py. Adds a README
explaining each, and extends the Dockerfile chmod to cover the new folder.
Nothing referenced these from active code (verified), so no wiring changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 10:52:44 -06:00
andrew 92e5326437 Fix dedup apply lock-hang, mawk-broken tag passes; add track delete, fix button/select CSS
confirm_and_apply no longer marks candidates confirmed before their apply
job actually succeeds (a stuck/skipped_lock run was silently vanishing
confirmed deletes without deleting anything), switches the fuzzy pass to
--apply-pairs to avoid a full rescan on every confirm, and adds a job
timeout so a hung subprocess can't hold the global pipeline lock forever.

dedup-library.sh's Pass 2-4 use gawk-only features (gensub, POSIX interval
regex) but were running under mawk (Debian's default awk) in the deployed
image, throwing silent syntax errors on every run -- switched those
invocations to gawk explicitly and added it to the Dockerfile.

Also: per-track delete button on the library list/detail pages, and two
CSS fixes (the primary button's hover gradient was getting clobbered by
the base .btn:hover rule's plain background, and the select dropdown
arrow had no background-size so it rendered oversized).
2026-07-09 09:12:02 -06:00
andrew 68cb007e4c Stage 0: migrate pipeline scripts from /opt/sldl, scaffold repo
Moves all ~30 pipeline scripts, configs, and the vendored sldl binary into
this repo (source /opt/sldl left untouched). Removes all docker exec/docker
compose dependencies now that beets and sldl run in-process/as a subprocess
of this container instead of via soulbeet/on-demand sldl containers.
Replaces hardcoded host paths, Navidrome credentials, and Spotify credential
sourcing with env-var-driven paths and shared credential loaders. Adds
Dockerfile, entrypoint.sh, requirements.txt, docker-compose.snippet.yml, and
the initial app DB schema.
2026-07-08 13:10:55 -06:00