Commit Graph

61 Commits

Author SHA1 Message Date
Jonathan Sykes
16e2b24c31 Resumable saves: prepare reports the mp4's real size, not the cache row's mp4+m4a total
The media_cache row's size counts the audio sidecar too, so a device asked for
bytes past the end of the file and could never finish a ranged save.
2026-10-03 00:49:24 +08:00
Jonathan Sykes
56b3f4e449 Resumable saves: ranged downloads that survive dropped connections, reloads and app kills
Server
- /api/download/:id answers Range with a strong ETag ("<id>.<gen>") and honours
  If-Range; a stale partial gets the whole current file (streamed, so Bun does
  not re-apply the Range itself). Uploads get the same treatment.
- GET /api/download/:id/prepare never blocks: ready {gen,size,sha256,etag,ext},
  working (server still fetching), legacy (HEVC / too long / cache offline),
  failed. Recently refused prepares are remembered for 10 minutes.

Browser
- The OPFS worker saves in 8 MiB ranges, writes at the byte offset, flushes
  each chunk, retries each chunk 6 times with backoff (30 s idle timeout) and
  keeps the .part plus a .part.json sidecar naming the server copy it belongs
  to. A changed copy restarts cleanly; the finished file is hashed once and
  checked against the server's SHA-256.
- SaveQueue remembers unfinished saves and resumes them on start, online,
  return to the foreground and every 2 minutes while visible; one at a time.
- Downloads shows live MB progress, 'Preparing on server', 'Verifying', and
  paused saves with Resume and Cancel (confirmed).
- listVideos ignores the sidecars; new listPartials/discardPartial helpers.

Verified in Chromium through a connection-dropping proxy: paused at 8 MiB,
auto-resumed after a reload from byte 8388608, final SHA-256 matched.
2026-10-03 00:46:52 +08:00
Jonathan Sykes
11ddb8a970 Uploads library on the USB drive with a nightly stable-disk copy; Topic channels resolve by name
- UPLOAD_DIR moves to the USB drive; reads fall back to UPLOAD_BACKUP_DIR on the
  ytplayer-data volume, and while the drive is offline new uploads land there
- scripts/ops/uploads-backup.sh (homelab cron, 03:30) copies both ways, never
  deletes; the server removes a deleted upload from both places
- /api/channel resolves a bare channel name ('Artist - Topic') to its id via a
  search, and the client falls back to the channel name for old saved entries
2026-10-02 22:38:05 +08:00
Jonathan Sykes
1f9a97a6ae Media cache on the homelab USB drive with an offline guard; Topic channels list their tracks
- MEDIA_DIR moves to /mnt/data/ytplayer (bind-mounted at /app/bulk); DB, lyrics,
  notes and uploads stay on the ytplayer-data volume
- MEDIA_VOLUME_MARKER: while the marker on the drive is missing the cache writes,
  serves and deletes nothing, so playback streams and an unmounted drive never
  fills the disk under its mountpoint or loses its index on a reboot
- Budget 250 GiB, keep 20 GiB free on the drive
- /api/channel falls back to the uploads playlist and the channel home page when
  a channel has no Videos tab (auto-generated '- Topic' music channels)
2026-10-02 21:21:23 +08:00
Claude
ee248d2303 IndexedDB search library (1000x100, 15-day copies, Update search), server video metadata for instant local results 2026-10-02 02:15:28 +00:00
Claude
1b10a994dd Version-stamped static URLs cached forever, thumbnail cache 25000 with throttled trimming 2026-10-02 02:11:38 +00:00
Claude
c69cc3a4bc Persistent server search cache: 200 results per query, 250k queries, 15-day stale-while-revalidate, refresh=1 2026-10-02 02:10:39 +00:00
Claude
14e2307a92 Keep search results with thumbnails until cleared in Settings; add stale-while-revalidate search and longer asset caching 2026-10-01 22:04:51 +00:00
Claude
02b337fcbc Add save-to-device export, server export route and an On other devices P2P list 2026-10-01 18:53:00 +00:00
Claude
08e95814e5 Support hour-long uploads and videos: raise body limit, stream uploads to disk, cache up to 3 h, add deferred-ideas note 2026-10-01 18:48:37 +00:00
Claude
04a74ab45d Redesign service-mode controls and add per-profile wrong-lyric-line reports 2026-09-30 15:15:07 +00:00
Claude
84cd00368d Add a P2P panel to the admin page 2026-09-30 08:02:52 +00:00
Claude
60a28fc593 Restore an evicted server copy from an online holder 2026-09-30 07:58:42 +00:00
Claude
8d57a654c1 Let a device hand a file to the server for hashing and validation 2026-09-30 07:50:38 +00:00
Claude
6fa930ad62 Add the /ws/p2p presence and signalling hub and the holders endpoint 2026-09-30 07:42:35 +00:00
Claude
c629ff5836 Register devices and report verified holdings to the server 2026-09-30 07:38:47 +00:00
Claude
eb3fe45761 Count views and evict server copies by retention criteria before LRU 2026-09-30 07:25:56 +00:00
Claude
ea56d286b3 Hash every validated server copy and register it as verified content 2026-09-30 07:18:52 +00:00
Claude
167df35a97 Add P2P tables, config flags and db helpers 2026-09-30 07:11:55 +00:00
Claude
9cac21afce Keep one long-lived yt-dlp worker process instead of spawning per call 2026-09-30 07:04:53 +00:00
Claude
556436da34 Answer searches from YouTube InnerTube directly with yt-dlp fallback 2026-09-30 06:58:05 +00:00
Claude
b24366d6c0 Warm the stream cache for likely next plays 2026-09-30 06:53:48 +00:00
Claude
e2a60792b9 Coalesce concurrent resolveStreams calls for the same video 2026-09-30 06:50:31 +00:00
Claude
794410916a Serve the app shell gzip/brotli-compressed with ETags 2026-09-30 06:45:11 +00:00
Claude
7781e0a116 Add startup, search and play timing marks plus yt-dlp duration logs 2026-09-30 06:41:50 +00:00
Claude
c17bd4c9ac Queue executable speed and peer-to-peer sharing plans with tested patches and harnesses 2026-09-29 19:03:12 +00:00
Jonathan Sykes
6ff9f86bd8 cache /api/search results for 3 minutes to skip repeat yt-dlp spawns 2026-09-30 00:17:44 +08:00
Jonathan Sykes
b983c6ca3c Fetch lyrics from LRCLIB (synced when available) and serve an admin media library of uploaded video and audio with cover art and embedded lyrics 2026-09-20 05:59:50 +08:00
Jonathan Sykes
9c1ec4ac51 Add presenter view, stats, lyrics worker, watch party with voice chat, timestamp sharing, soundbites, notes, transcript search, PiP, EQ, sleep fade, gestures and external players; fix the crossfade end-of-song race 2026-09-20 00:07:12 +08:00
Jonathan Sykes
ca07e1fdf1 Add gapless and crossfade playlist transitions with a mode toggle, and a waveform seek bar for audio-only playback 2026-09-19 16:12:33 +08:00
Jonathan Sykes
71fc51fb50 Add a lyrics-only service mode view with the sung line centred and auto-sized, and a free local faster-whisper script to transcribe saved songs 2026-09-19 08:33:00 +08:00
Jonathan Sykes
b82f17cd46 Add phone remote control: pair a phone with a 6-digit code or QR to drive a desktop instance's playback, queue, volume, sleep timer and service mode 2026-09-18 20:19:00 +08:00
Jonathan Sykes
524db986d2 Add shared synced lyrics, chapters and personal bookmarks with revision history, API tokens and an admin page 2026-09-18 18:10:10 +08:00
Jonathan Sykes
e4ac04cb5c Sample two short windows before re-encoding a cached video and skip the full encode when HEVC will not save 15 percent 2026-09-14 00:51:14 +08:00
Jonathan Sykes
8b0060d42a Re-encode cached videos to HEVC when it saves at least 15 percent and serve them only to devices that can decode HEVC 2026-09-13 21:09:15 +08:00
Jonathan Sykes
91dab289c3 Show the update banner only when the running build differs from the server and make Refresh UI land the new shell on flaky links 2026-09-13 17:42:50 +08:00
Jonathan Sykes
5fb863ba25 Cache every played or saved video on the server via background jobs with a validation gate and a Broken re-download button 2026-09-13 09:45:01 +08:00
Jonathan Sykes
3e1b56610f Let a shared playlist be sent to another user by name 2026-09-08 15:52:46 +08:00
Jonathan Sykes
06f80debc0 Prefer h264 formats for saves so cached copies play on every device 2026-09-01 07:02:57 +08:00
Jonathan Sykes
d073d20060 Retry yt-dlp against alternate player clients when YouTube demands sign-in 2026-08-27 07:22:02 +08:00
Jonathan Sykes
94fff8f862 Report live streams as unsupported instead of an empty quality list 2026-08-24 01:07:18 +08:00
Jonathan Sykes
c141959654 Serialize saves, disable idle timeout, cap savable length at 3h 2026-08-24 00:21:18 +08:00
Jonathan Sykes
04c419d151 Coalesce save downloads, kill them on disconnect, refuse live and over-long videos 2026-08-23 23:50:10 +08:00
Jonathan Sykes
ea9e372c1e Rate-limit save downloads so they no longer starve playback 2026-08-23 23:13:45 +08:00
Jonathan Sykes
d1ba9d1e9c Fix play proxy cancel crashing the server on client disconnect 2026-08-23 22:54:31 +08:00
Jonathan Sykes
8177f5c665 Chunk playback proxy at 10MB and merge adaptive streams for saves 2026-08-23 15:43:01 +08:00
Jonathan Sykes
c8a9755e93 Restrict playback proxy to direct https streams, prefer adaptive
Newer yt-dlp surfaces HLS (m3u8) and DASH-segment formats whose url is a
manifest, not media bytes; proxying those served the video element an
unplayable m3u8 and broke Range seeking (416). Keep only protocol=https
formats, and prefer adaptive video-only streams (paired with the separate
audio) over progressive muxed formats, which are more reliable.
2026-08-23 15:15:02 +08:00
Jonathan Sykes
defc8a7f1a Proxy YouTube playback through server to fix intermittent 403s
Direct googlevideo URLs are bound to the extractor's IP and expire, so the
browser fetching them from a different IP got intermittent 403s on playback.
Route playback through a same-origin /api/play proxy that fetches the stream
server-side (matching the extractor IP) with yt-dlp's own http_headers and
forwards Range headers for seeking; fall back to piping yt-dlp for SABR/itag-18
formats a plain GET can't fetch. Prefer adaptive streams over progressive in
the playback fallback order.
2026-08-23 15:04:18 +08:00
Jonathan Sykes
b868dabe27 Add bulk import of a YouTube playlist URL 2026-08-16 13:39:02 +00:00
Jonathan Sykes
27b28f767b Add shareable single-playlist links with additive import 2026-08-16 13:32:18 +00:00