YT ▶ PLEX Refactored: 12 Modules, 75 Tests, and a Release That Almost Said Too Much
Why: With the features working and a test suite in place, Damien wanted a second agent to make the code cleaner and easier to understand before building more on it. The same session also added automatic retry and resume for interrupted downloads.
Three things happened in this round. Interrupted downloads became self-healing. An Opus agent refactored the whole app on its own branch, which I reviewed, Damien tried out, and I merged. And the nine small bugs the refactor surfaced got fixed. The live service is unchanged (same unit, port and data), but it now runs the new code.
1. Interrupted downloads
Temporary failures (a dropped connection, a rate limit, a 5xx) now retry by themselves after 30 seconds, 2 minutes and 5 minutes, with a countdown, and each retry continues from the partial file. Otherwise the row says "Download interrupted (X of ~Y kept)" and offers Resume, and a banner offers Resume all whenever interrupted downloads are waiting. I tested it by killing a real 4K download at 46%: Resume picked it back up and was at 84% within seconds.
2. The refactor
core.py(1,114 lines) became 12 single-purpose modules;main.pywent from 998 to 135 lines, with login inauth.pyand the API inroutes/.app.jswas reorganized into sections, with its 136-line row renderer split into small pieces. Duplication was removed and comments explain the reasoning.- Behavior-preserving by construction. The agent wrote tests describing the old behavior before moving anything, including a fake-yt-dlp pipeline test; that took the suite from 38 to 67 tests. It also compared 40 page snapshots between the old and new code on identical data, and they matched exactly.
- My review: all tests passed, the one restructured rule (default library for a new row) is logically identical, and a browser run on a neutralized copy of live data worked end to end.
- Action: for Damien to try it hands-on, I ran the branch as a temporary transient user unit
yt-plex-teston port 8431 (LAN only, data copied to/home/plex/yt-plex-test-datawith in-flight rows neutralized). After his OK, I stopped and removed the unit, the data copy, the agent's worktree and the merged branch.
3. Nine bugs the refactor reported
Fixed, each with a regression test (75 tests now):
- Removing a playlist while one of its videos was being moved into the library could delete the staging files mid-copy. Removal now waits for the move.
- The duplicate warning always said
.mp4. - Failed subtitle fetches waited 45 seconds after their last try.
- A TVDB fetch blocked the event loop.
- Two TVDB failures crashed with a 500 instead of giving a clear error.
- A bad playlist limit crashed with a 500.
- Editing a missing show returned OK.
- A row removed mid-download crashed the failure handler.
- A timed-out lookup left a zombie process.
4. The release that almost said too much
When republishing, the new git archive-based source tarball picked up CLAUDE.md and docs/, the private handoff notes written that day. They describe this server: its LAN IP, paths, firewall rule and proxy. The privacy grep only scanned the code folders, so it missed them, and that tarball sat in /home/plex/www/yt-plex/ for about a minute before I caught it by eye.
The NPM access log for skyhouse.dev shows no download of it: every request for the tarball, ever, came from this server's own checksum checks. Fixes: a .gitattributes export-ignore for CLAUDE.md, docs and itself; a test fixture that named this domain switched to example.com; and a release checklist (in CLAUDE.md) that now scans the built tarball as a whole. The Docker image was never affected, because it only copies the code folders.
Net effect: the same app, now much easier to maintain and self-healing on flaky connections, with a release process that checks what it actually ships.
← Back to Admin Hub