Skip to content

feat: unlink a playlist from the database keeping all files - #7

Merged
skoelle merged 1 commit into
mainfrom
feat/unlink-playlist
Oct 11, 2026
Merged

skoelle merged 1 commit into
mainfrom
feat/unlink-playlist

Conversation

@skoelle

@skoelle skoelle commented Oct 11, 2026

Copy link
Copy Markdown
Owner

What

Adds a Danger zone card at the bottom of the playlist detail page with an Unlink from database button, backed by a new endpoint POST /api/playlists/{id}/unlink.

Unlink removes a playlist from the database only (playlist_entries → jobs → playlists in FK order; runs stay). Videos, sidecars and the download archive stay on disk.

Before the rows go away:

  1. Guard – 409 playlist has an open job when state is queued/running or an open job row exists; the guard is re-checked right before the delete (race with a concurrent enqueue).
  2. Manifest – when the folder exists and has no manifest.json, one is written from the playlist_entries snapshot in the exact shape app.ta_import reads. An existing manifest (imported playlists) is never overwritten – import detection without a migration.
  3. Archive – ids of the local video files are merged append-only into config/archives/<playlist_id>.txt (same rule as the importer), so re-adding the playlist later downloads nothing twice.

File work runs before the DB delete: a failure there leaves the playlist untouched and the call retryable.

UI

  • Static Danger zone card under Job history, button.danger styling.
  • confirm() dialog naming what stays on disk and that channel playlists come back through the next discovery run.
  • On success the UI navigates via location.hash = state.detailFrom (no page reload, no stale 404 poll).
  • The button's data-id is cleared while another detail page loads, so it can never act on the previously shown playlist.

Docs

never delete now has two documented exceptions: SPEC §15 is retitled to Unlink und Wartungs-CLI app.forget and covers both, with follow-up edits in §2, §8, §9, §14 plus README (intro bullet, behaviour note, forgetting section), AGENTS.md (rule 1, api/forget rows) and a PLAN.md decision log entry.

Tests

6 new tests in tests/test_unlink.py: open-job/404 guards, rows gone + files kept + manifest shape + archive merged, existing import manifest byte-identical, no-folder case, and a full unlink → scan_manifests → apply_import roundtrip (state done, no duplicate archive lines).

pytest -q = 184 green, ruff check . clean, node --check app/static/app.js ok.

Add a Danger zone to the playlist detail page with an Unlink from
database button backed by POST /api/playlists/{id}/unlink. The endpoint
refuses playlists with an open job (409, re-checked after the file
work), writes a re-import manifest.json from the playlist_entries
snapshot when the folder has none (an existing manifest from an import
is never overwritten), merges the local video ids append-only into the
download archive, and then removes playlist_entries, jobs and the
playlist row in FK order. Videos, sidecars and archives stay on disk,
so app.ta_import can restore the playlist without re-downloads.

This is the second documented exception to the never delete rule next
to the offline forget CLI: SPEC section 15 now covers both, section 2,
section 8, section 9 and the changelog are updated, plus README,
AGENTS and the PLAN decision log. Six new tests cover the guards, the
kept files and manifest shapes, and a full unlink -> import roundtrip.
@skoelle
skoelle merged commit 40d8269 into main Oct 11, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant