When auto-advancing through a queue or playlist — or stepping with
next/prev — the next video previously jumped to its saved resume
timestamp. A freshly selected track now starts at the beginning, or at
the A marker when an A-B loop is set for it. Resuming still applies when
you reopen a single video directly.
A-B markers set while playing from a playlist are now stored on that
playlist's own copy of the video (entry.ab), so each playlist keeps its
own loop and the markers sync to the database alongside the playlist.
Non-playlist playback keeps using the global per-video marker map.
Bumps service worker to v1.0.3 to bust the client cache.
Adds an About section at the bottom of Settings displaying the app
version. In WEB mode it fetches /api/version and appends the live
server build tag so you can confirm which deploy is running.
Server stamps a BUILD_TAG (stable per process, changes on restart/deploy)
into GET /api/version alongside the existing version string. Cache-Control
is set to no-store so the response is never cached by the SW or browser.
Client (WEB mode only) baselines the tag on first fetch after boot, then
rechecks every 5 minutes. On mismatch it calls the existing showUpdateBanner()
so the user sees the 'Update ready — Reload now' toast and can reload at will.
This mirrors the build-id polling pattern used across the BukidBountyApp
derivatives to avoid stale UI after deploys.
The PWA conversion shipped sw.js, opfs.js, manifest and icons, but app.js
was never updated to use them — it still routed cache_* calls through the
native bridge (call('cache.list', ...)), which throws 'No native bridge
available' in a browser. That surfaced as 'Offline cache unavailable /
not available in this build' on the Saved videos and Settings pages.
- Add WEB mode detection (no Tauri/zero bridge present)
- Route cache_* through OPFS (opfsDownload/Status/List/Delete/Clear)
- Proxy downloads via /api/download so the browser never hits YouTube CDN
- Load fingerprint.js + opfs.js; add manifest link and PWA meta tags
- Relax CSP to allow blob: media/img and worker-src 'self' for the SW
Fixed bar at bottom of window when video is playing. Shows title,
channel, progress, play/pause toggle. Click to restore player view.
Files:
- frontend/index.html: mini-bar markup
- frontend/app.js: showMiniBar/hideMiniBar/updateMiniBar, wiring
- frontend/styles.css: .mini-bar, .mini-bar-inner, .mini-progress styles
Shows upcoming videos in the autoplay queue below now-playing meta.
Clickable to jump to any video. Hidden when queue has 1 item or empty.
Updates on playFromList, playNext, playPrev. Hidden in empty player state.
Files:
- frontend/index.html: upnext section in player pane
- frontend/app.js: renderUpNext(), wired into playFromList/playNext/playPrev
- frontend/styles.css: .upnext, .upnext-item, .upnext-list styles
Cards in playlist view are draggable. Visual feedback: opacity on
source card, accent border on drop target. Reorder persists to store.
Files:
- frontend/app.js: dragSource state, drag/drop handlers on cards,
container-level dragover guard in wireUI
- frontend/styles.css: .card.dragging, .card.drag-over styles
Multiple toasts can appear simultaneously, capped at 3. Oldest
toast auto-evicts when cap is reached. Each toast auto-dismisses
with a fade-up exit animation.
- frontend/app.js: toast() now creates DOM elements in toastContainer
- frontend/index.html: single #toast replaced with #toastContainer
- frontend/styles.css: .toast-container flexbox row, .toast-exit animation
Saves playback position to store on pause and periodically every
10s during playback. Restores position (>1s) when the same video
is loaded again, showing a 'Resumed from 0:42' toast.
Files:
- frontend/app.js: resumePositions in data, save in pause/timeupdate,
restore in Player.afterLoad, boot merge includes resumePositions
Replaces pulse-only state with a visible progress bar animation:
- Indeterminate bar slides across the bottom of card thumbnails during download
- markCardCacheState dynamically adds/removes the bar element
- renderCard includes the bar for newly rendered downloading cards
- Now-playing button already showed '⏳ Saving…' state
Files:
- frontend/app.js: dl-progress in renderCard, markCardCacheState dynamic bar
- frontend/styles.css: .dl-progress, .dl-bar with dlSlide animation
Press '?' to open a styled overlay showing all keyboard shortcuts,
Esc to close. Lists Space (play/pause), arrow keys (seek/volume),
F (fullscreen), M (mute), ? (help), Esc (close).
Files:
- frontend/app.js: toggleShortcutHelp(), wireShortcutHelp(), '?' and Esc handlers
- frontend/index.html: shortcut-overlay markup with .shortcut-grid
- frontend/styles.css: .shortcut-overlay, .shortcut-panel, .shortcut-row, kbd styles
Replace bare 'Searching…' text with 8 animated skeleton cards that
mirror the real card layout (thumbnail, title, channel, menu dot).
Shimmer animation sweeps across placeholder blocks until results arrive.
Files:
- frontend/app.js: showSearchSkeletons() function, wired into search submit
- frontend/styles.css: .skeleton-card, .skeleton-shimmer, .skeleton-line, .skeleton-dot styles with shimmer animation
zero-native has no Windows target yet, so add a parallel Tauri shell
for Windows that reuses the same frontend:
- src-tauri/: Rust commands (yt_search, yt_streams, store_load,
store_save) mirroring the Zig bridge; spawns bundled yt-dlp.exe with
CREATE_NO_WINDOW; data stored in app_data_dir
- frontend bridge now auto-detects window.__TAURI__ vs window.zero and
routes accordingly; CSP updated for Tauri IPC
- tauri.conf.json bundles yt-dlp.exe as a resource and builds nsis/msi
- scripts/make-icon.js generates appicon.png for 'tauri icon'
- README: native Windows build steps + WSLg fallback note