Skip to content

feat: add playlists manually by URL - #4

Merged
skoelle merged 5 commits into
mainfrom
feat/manual-playlist
Oct 10, 2026
Merged

skoelle merged 5 commits into
mainfrom
feat/manual-playlist

Conversation

@skoelle

@skoelle skoelle commented Oct 10, 2026

Copy link
Copy Markdown
Owner

What

Adds manual playlist entry for playlists that never show up in the channel listing (unlisted, removed from the channel, belonging to someone else) – the feature formerly drafted in MANUAL-PLAYLIST.md.

  • Form above the Sync and Oneshot tabs: playlist URL or bare ID, optional title, Add playlist button. Type follows the active tab; errors appear inline (no alert, no page reload); new rows carry a manual badge. Inputs stack full-width below 768 px.
  • POST /api/playlists validates with yt-dlp --flat-playlist -J (no login, works for unlisted), then creates the row: manual=true, title = provided title or the playlist id, state=new, remote_status=active, remote_item_count from the listing. No auto-enqueue – the existing Download now / Sync now buttons start it. 422 bad input, 400 not accessible, 409 duplicate (incl. unique-constraint race).
  • Schema: new playlists.manual boolean (default false), alembic migration 0003 with an existence guard – safe for existing databases, idempotent on re-run.
  • Nightly sync: manually added sync playlists run in the nightly sync like every other (existing filter does not care about the origin of the row). playlist_id_from_url accepts bare ids, playlist URLs and &list= in any YouTube URL.
  • The test stub now answers unknown playlist ids with exit 1 instead of KeyError.

Docs

SPEC §6.10 (requirement), §7 (manual column), §8 (endpoint), §9 (form + badge), §14 (changelog); README section Adding a playlist manually; MANUAL-PLAYLIST.md shrunk to a completion pointer; PLAN status + decision log.

Tests

6 new tests (row creation incl. no-enqueue, title variant, 422/409/400, stub unknown-id, UI forms, migration idempotence) plus the parser unit tests. ruff check . clean, pytest -q 178 passed, node --check app/static/app.js ok.

- ytdlp.playlist_id_from_url: bare id, playlist URL, or any URL with
  list= (watch/shorts/share); None otherwise
- new playlists.manual boolean (default false, server_default for
  existing rows), exposed as manual in playlist_dict()
- alembic 0003 adds the column with an existence guard (fresh DBs
  already got it via 0001 metadata.create_all)
Validates with yt-dlp --flat-playlist -J (no login, works for unlisted),
then creates a manual row: title = provided title or the playlist id,
state new, remote_status active, remote_item_count from the listing.
No auto-enqueue - the row uses the existing Download now / Sync now
buttons. 422 bad type/url, 409 duplicate (incl. unique-constraint race),
400 not accessible. Stub now answers unknown playlist ids with exit 1
instead of KeyError.
One form above each table: playlist URL or id, optional title, 'Add
playlist' button. Submits to POST /api/playlists, shows the server
error inline (no alert, no page reload), refreshes via tick(true).
New rows carry a 'manual' badge in the title column. Inputs stack to
full width below 768px. English strings only.
SPEC: new 6.10 requirement (form per tab, yt-dlp validation, 409/400/422,
no auto-enqueue, nightly sync included), playlists.manual in 7, POST row
in 8, add form + badge in 9, changelog entry in 14.
README: 'Adding a playlist manually' section.
MANUAL-PLAYLIST.md shrunk to a completion pointer (behaviour lives in
SPEC/README now), references cleaned up, PLAN status + decision log.
@skoelle
skoelle merged commit b256479 into main Oct 10, 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