Stray pinches and double-taps kept zooming the installed app. On iOS only the
viewport gets maximum-scale=1/user-scalable=no, Safari's gesture events are
cancelled and the page uses touch-action: manipulation. The player's own pinch
zoom (pointer events) still works.
Shown as soon as an update is offered, so Later, a backdrop tap or a failed
download all leave a one-tap way to update; it targets the newest build and
disappears while updating. The search form and box now shrink so the icon
never pushes Search off a phone screen.
In service mode's Video view the overlay lives inside the player pane, which
the home-over rule hid, while service mode kept the bottom bar and mini bar
hidden. Going home now leaves service mode first, and the rule spares the
service overlay.
- fitLyricLines measures text on a canvas with the line's own font and writes
every --fit after measuring (it used an in-list DOM probe and wrote inside
the loop, re-laying-out the whole list per line). 1200 lines: 600 ms -> 28 ms;
sizes match the old method within 1 px. Service mode on long sermons with
hundreds of transcribed lines opened slowly enough on phones to look broken.
- Bottom-nav Search while something plays shows the home screen (playlist
board / hero) over the player pane; the media keeps playing unseen and the
mini bar brings the player back (its close button too, so there is always a
way back). Starting another video restores the player.
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.