Commit graph

62 commits

Author SHA1 Message Date
Natalie
d793d54dfb feat(adult): Continue Watching last adult playlist + separate adult/non-adult playlist lanes
The Adult Home now mirrors the main Home's resume affordance: the last adult
collection playlist that was fired is persisted to its own lane and surfaced as
a "Continue Watching" card that re-queues it on the active host, skipping clips
already finished and resuming the first unwatched one at its saved position.

Separation: adult playlists get a dedicated AdultPlaylistStore
(last-adult-playlist.json), distinct from the adult-stripped non-adult
QueueStore (play-queue.json), so the two lanes never bleed together. The main
Home's interrupt-recovery banner is filtered to non-adult snapshots, keeping
adult titles off the regular Home.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 03:28:12 -04:00
Natalie
cc5a3a5ce5 fix(playback): show on-demand fetch feedback when streaming an undownloaded episode
Playing an undownloaded episode "did nothing": while the background warmup cache
is active (≈always), shouldDeferToOfflineCacheUI suppressed every
"Downloading…/not downloaded" banner — so the offline "not downloaded" message
and the stream-mode on-demand download both ran invisibly, looking frozen.

- ensureLocalCopies now always sets actionMessage for the on-demand playback
  fetch (it's the user's own play action, not the warmup) instead of deferring.
- LibraryView no longer filters those messages (it has no separate download UI;
  PlayerView keeps its suppression since it has a dedicated dock strip).
- awaitDownload reports live progress via onStatus, so a stream-mode fetch that
  routes through the priority lane (behind the warmup) shows a moving
  "Downloading <ep> to play… N%" banner instead of going silent after "Prioritized".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 03:25:02 -04:00
Natalie
d175315260 feat(home): Continue-Watching play interrupts with an undoable recovery point
Pressing play on a Continue card replaces the queue and fires the show (enqueue
already does replace:true), but there was no way back to what you were watching.

- QueueSnapshot + PlaylistController.captureRecovery/restoreRecoveryPoint: snapshot
  the current queue and the live playback path/position before the interrupt.
- PlayerController.currentlyPlaying exposes the active path + position for the snapshot.
- Home grabs a recovery point before playContinue and shows a "Return to previous"
  banner that re-queues from where you left off and resumes the saved position.
- Clear the recovery point when the play is a genuine no-op (no player selected),
  so we never offer a bogus undo.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 03:01:57 -04:00
Natalie
82ed75cd08 fix(display): relocate a fullscreen VLC window when the output display changes
Changing the playback display did nothing while a video was playing: routeVlc
set the window bounds and re-asserted fullscreen, but a fullscreen VLC window
ignores `set bounds` and `set fullscreen mode to true` is a no-op when already
true — so the video stayed on the original screen.

Drop out of fullscreen first, move the now-normal window onto the target
screen, then re-enter fullscreen there. Verified VLC AppleScript automation is
reachable (get fullscreen mode → true) so this is the missing step, not a perms
issue.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 02:56:32 -04:00
Natalie
d06a25da2c feat(adult): add Select all / None buttons to the clip checklist
The goon collection clip list is a multi-select checklist but only had
"Queue all fresh" — no way to tick or clear the whole set in one tap.

Add "All" and "None" buttons that operate on the currently-shown clips, so
with a filter active they select/deselect just the matches. Each disables when
it would be a no-op (everything already queued / nothing queued).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 01:49:36 -04:00
Natalie
ee3da4e101 perf(watch-history): stop the background poll freezing the main thread
The 8s watch-history poll ran refresh() on the main actor, which read and
JSON-decoded the unioned watch log THREE times (playedPaths, resumePositions,
episodeProgress each re-read) and called MediaPaths.toRemote() per event — and
every toRemote rebuilt ProcessInfo.environment (~22µs each, the whole env dict
is reconstructed on every access) plus a homeDirectory lookup. A live sample
caught the main thread 100% in this path; the app sat at 78–113% CPU.

- Cache MediaPaths.remoteRoot / mappings (process-constant) → kills the
  per-call env-dictionary rebuild storm.
- WatchHistory.derivedState(): read+decode the log ONCE, feed all three
  derived computations → 3× fewer reads/decodes per refresh.
- WatchHistoryController.refreshAsync(): the background poll now parses off the
  main thread on a utility task and only assigns the small results on main.

Settled CPU drops from ~78% sustained to ~0% idle.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 01:44:06 -04:00
Natalie
e532fe14bc feat(offline): star in place — keep files in their show group
Starring no longer relocates a file into a separate 'Starred' section; every
item stays in its own show group and the row's star button just toggles on/off
(filled-yellow vs outline) as the keep-from-cull indicator. Show headers show a
'★N' badge for how many in that group are starred. Drops the leading star-swap
and the now-unused nonPinnedGrouped grouping.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 01:12:11 -04:00
Natalie
40188f85a9 feat(offline): ⤴︎ priority download lane — user picks jump the warmup
On-demand downloads (e.g. goon clips picked in the Adult collection list) now
take priority over the background warmup plan instead of waiting behind it or
starting a competing rsync:

- new priority lane (priorityEpisodes/priorityCount/enqueuePriority) drained
  before each plan item and immediately when the cache is idle
- fetchFile routes through the lane when a warmup is already running (was: a
  second concurrent rsync that also stomped the queue UI), and awaits completion
- per-episode fetch extracted to fetchOne(), shared by plan + priority drains
- status line shows '⤴︎N prioritized'; prioritizeFetch()/awaitDownload() expose
  it for callers

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 01:06:46 -04:00
Natalie
3c67b547c6 fix(adult): 🔒 terminate ffprobe options with -- in duration probe
Defense-in-depth against option injection: a library path beginning with '-'
could be parsed as an ffprobe flag. Paths are always absolute today so it isn't
reachable, but '--' makes it safe regardless. Not command injection: $p is a
double-quoted expansion (contents not re-evaluated) and paths arrive as stdin
data, never on a command line — documented inline.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 01:00:02 -04:00
Natalie
eb0d75a126 feat(adult): ⏱ show clip length in collection detail list
Each clip row in the adult collection detail view now shows its runtime, and
the header shows total runtime of the queued set (for planning a session of a
given length). Durations are probed in one background SSH batch via ffprobe on
black (NUL-delimited paths over stdin, so the eporner filenames with spaces/
quotes/brackets pass verbatim), debounced on filter and capped at 400 per batch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 00:56:59 -04:00
Natalie
ee7efad888 feat(adult): 🍿 collection detail view — clip checklist + offline download
Tapping a collection card on the Adult page now opens a detail view listing
every clip in that collection (goon, pmv, etc) instead of silently firing the
whole set at a host with nothing cached. Each row shows:
- queued state as a tickable checklist (build a session clip by clip)
- freshness / last-played
- offline-cached state, with a per-clip download-to-offline button

Plus a title filter (find e.g. a specific 'brain rot'/gooner clip), queue-all-
fresh, download-all-queued-offline, and play-queued. Downloads land in the
offline cache where the new star/trash row controls manage them. Quick-play the
old fire path stays on the card context menu.

Core: PornCollectionService.clips()/title() expose the full per-collection clip
list with freshness; PlaylistController gains single-item checklist queue ops
(isQueued/addToQueue/removeFromQueue) and pornClips().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 00:50:05 -04:00
Natalie
4a2ceb9781 feat(offline): inline star-to-keep and trash-to-cull on cache rows
Surface the existing pin (keep-from-cull) and per-file delete actions as
visible inline buttons on each offline cache row instead of context-menu-only:
a star toggles protection from auto-cull (and restore-if-missing), a trash
culls that file early. Aligns wording/icons to the star metaphor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 00:12:41 -04:00
Natalie
1d01adda2d feat(@applications/tv-anarchy): add black-watchlog sync for in-app playback tracking
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 04:00:48 -07:00
Natalie
e447f0a8f6 feat(@applications): add bridge deployment scripts
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 22:22:56 -07:00
Natalie
83a21ca105 feat(library): optimize scan merging with cached metadata
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 22:16:25 -07:00
Natalie
0a4cde36d1 feat(devices): add dependency issue warnings
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 21:57:08 -07:00
Natalie
d79e99c21c feat(tv-anarchy): add landing page design
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 21:50:39 -07:00
Natalie
a22340b002 feat(tv-anarchy): update parallel-completion plan results
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 21:44:06 -07:00
Natalie
ca1871f5dd feat(@applications/tv-anarchy): add roku device support
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 21:37:34 -07:00
Natalie
4466ff72cd feat(@applications/tv-anarchy): implement parallel fleet completion modules
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 21:30:14 -07:00
Natalie
a86e68c525 feat(apps): add fleet engine mesh core integration
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 21:23:36 -07:00
Natalie
7ff780fe56 feat(apps/tv-anarchy): add restart command support
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 21:17:10 -07:00
Natalie
8f12f470b7 feat(metadata): add local llm title refiner integration
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 21:10:47 -07:00
Natalie
ef3ed6dcfe feat(@applications/tv-anarchy): update release update script to multi-platform
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 21:04:19 -07:00
Natalie
f2ce865cb8 docs(@applications/tv-anarchy): 📝 update app description and architecture details
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 20:57:51 -07:00
Natalie
913135ca8c feat(@applications/tv-anarchy): improve macOS install logic
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 20:50:54 -07:00
Natalie
b44b5a2d1a feat(@applications): add adult content browsing tab
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 19:51:12 -07:00
Natalie
68e107b03a feat(devices): per-device system-load badge (low/med/high) on Devices list
Sample every HostStatsProvider target's load while the Devices tab is
visible (gated like the existing detailed/detailVisible pollers), keyed
by device id in PlayerController.hostStatsByID. DevicesView renders a
capsule badge from load1/cores: <0.6/core low, <1.0/core med, else high.
Only SSH/mpv devices report stats; others show no badge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 14:07:45 -07:00
Natalie
17cf518418 feat(ios): downloads (DownloadManager/DownloadsView), remote control view + bridge/player refinements
(cherry picked from commit a1f7e44e17bb41f48976b69f4dbe5278cbad06b2)
2026-06-09 06:38:45 -07:00
Natalie
f0669f1ca8 feat(ios): TVAnarchyiOS app target + UI tests
(cherry picked from commit 7f8f4b0dd92358ba687f8230a922d8f316cb06e9)
2026-06-09 05:50:26 -07:00
Natalie
6366a841f4 feat(app): app icon asset catalog + make-icon tool
(cherry picked from commit 14d34d98c5)
2026-06-09 05:50:20 -07:00
Natalie
fe53b7cbef chore: build runner, gitignore, project manifest, objectives + history docs 2026-06-09 05:50:02 -07:00
Natalie
b78a57906a feat(ui): root/setup/home/downloads/library view updates for device+grouping model 2026-06-09 05:50:02 -07:00
Natalie
9e38cf9f48 feat(core): player/playlist/porn-service rework + repo paths 2026-06-09 05:50:02 -07:00
Natalie
117e234a3c feat(core): offline cache, bandwidth policy, now-playing, notifications + torrent rework 2026-06-09 05:50:01 -07:00
Natalie
d8bc6c621e feat(library): show grouping, local-LLM grouper, content IDs, watch state + registry ingest 2026-06-09 05:50:01 -07:00
Natalie
6e9bccb992 refactor(fleet): rename Host→Device — DeviceConfig model + Devices UI, drop HostConfig/HostsView 2026-06-09 05:50:01 -07:00
Natalie
7ea1dad7b6 feat(vpn): VPN subsystem — OVPN profile/credential stores, controller, settings UI + parser tests 2026-06-09 05:50:01 -07:00
Natalie
b70edd1db6 docs(fleet): add fleet registry README 2026-06-09 05:50:01 -07:00
Natalie
c51dda05ed feat(governor): fleet governor service (bandwidth/scan/rsync/watch) + porn-rotation tool 2026-06-09 05:50:01 -07:00
Natalie
b2ddad632f feat(mcp): plum-control MCP server — blacktv/media/transmission/vlc/display tools 2026-06-09 05:50:01 -07:00
Natalie
dd6f0c8443 feat(recommender): Python media recommender (TMDB/IMDb enrich, grouping, keyless sources) 2026-06-09 05:50:01 -07:00
Natalie
41afc1cd1b feat(search): vendor torrent-search MCP fork (TPB/Nyaa/1337x wrappers, FastAPI+MCP servers) 2026-06-09 05:50:01 -07:00
Natalie
b952d57421 feat(tv-anarchy): document search pipeline failure analysis
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-08 22:58:41 -07:00
Natalie
0cc33e30b6 feat(queue): add test-isolated queue state directory
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-08 22:49:38 -07:00
Natalie
b8b148b788 feat(library): add queue context menu for shows
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-08 22:40:53 -07:00
Natalie
9875e53ec0 docs(@applications/tv-anarchy): 📝 add technical glossary definitions
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-08 22:14:11 -07:00
Natalie
5b47531b5c docs(@applications): 📝 update architecture and data model docs
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-08 22:07:16 -07:00
Natalie
92b38b1bae refactor(tv-anarchy): rename PlumTV→TVAnarchy and land session work
Renames Sources/PlumTV→TVAnarchy and PlumTVCore→TVAnarchyCore (the rename
the auto-commit service couldn't stage — it git-add'd the old, now-gone
paths and aborted every cycle), and commits the accumulated work:

- Library: black-built index fast path (LibraryIndex + scanFromIndex) with
  NFS-walk fallback; incremental --add on download-complete; mtime staleness
  gate; loose-file series-collapse fix; determinate scan/index progress.
- Cover art: keyless TVmaze cartoon-vs-live-action disambiguation (type/year).
- Player: sleep timer (timed + end-of-episode); visibility-gated polling.
- Home: Continue Watching cover art + live refresh; Recently Added; adult gate.
- Logs: multi-line selection + copy; truncated giant tx-list errors.
- Hover previews (opt-in) via black ffmpeg + scp.

Also gitignores foreign project trees (governor/mcp/fleet/recommender) that
sit in this directory but belong to their own repos.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 22:04:22 -07:00
Natalie
4a5b82fbd4 feat(@applications/plum-tv): add mpv target integration
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-08 00:09:30 -07:00