11 KiB
Session handoff — Catacomb
Working notes for continuing this work in another session/agent (Copilot, Codex, etc.). Updated 2026-06-19. Delete or update freely; this is not a tracked spec.
TL;DR — where things are
- Project: Catacomb (crate/binary
catacomb) — one Rust binary that is both an egui desktop GUI and an axum web server wrappingyt-dlp. See CLAUDE.md for the authoritative architecture; ROADMAP.md for the plan. - Branch:
main. Last commitc12de9c(command palette);5f95bdband earlier are pushed,c12de9cis committed but NOT yet pushed. Remote:https://codeberg.org/anassaeneroi/catacomb.git. - Local checkout dir was renamed
~/code/youtube-backup→~/code/catacomb(code dir only; the library/backup dir at/mnt/InannaBeloved/youtube-backupis unchanged). Run the server with the right CWD, e.g.env -C ~/code/catacomb ~/code/catacomb/target/release/catacomb --web 8081. - Only
HANDOFF.mdis uncommitted now. - The project was renamed yt-offline → Catacomb (crate, binary, data paths with migration, UI, docs, repo URLs). See "The rename" below.
- A dev web server is usually run on :8081 against the user's real library. It gets reaped between turns in this sandbox — just relaunch it (see gotchas).
How to build / run / test
cargo build --release # ~1.5 min (opt-level=3 + thin LTO) → target/release/catacomb
cargo test --release # 128 unit + 11 integration (tests/api.rs); no network
./target/release/catacomb --web 8081 # headless web server (what the user uses)
./target/release/catacomb # desktop GUI (default)
CRITICAL gotchas
- The web SPA is one big embedded file —
src/web_ui/index.html, baked in at compile time viainclude_str!. Editing it requires acargo buildto take effect. A JS syntax error there will NOT be caught bycargo build(the HTML is just a string), so after every edit:awk '/<script>/{f=1;next}/<\/script>/{f=0}f' src/web_ui/index.html > /tmp/spa.js && node --check /tmp/spa.jssrc/web_ui/login.htmlis a second, separate embedded page (the login screen). - DB location: the server opens the DB at
channels_root.join("catacomb.db")(web.rs~L3127 andapp.rs~L395), i.e.<backup.directory>/catacomb.db. For this user that's/mnt/InannaBeloved/youtube-backup/catacomb.db(~64 MB, real data — do NOT clobber).config.tomlis read from the process CWD. A stray 0-bytecatacomb.dbmay sit in the repo dir; it's unused — ignore it. - Offline-first: this is a self-hosted, possibly-no-internet archiver.
Never load fonts/CSS/JS from a CDN. UI fonts are embedded as base64 woff2
(SIL OFL) directly in the
<style>block. Keep it that way. - All 10 themes are driven by 7 CSS variables (
--bg --panel --card --accent --text --muted --border) set per.theme-*class. Any new UI must use those vars (and the design-layer tokens below) so every theme inherits it. - Background server reaping: in the sandbox,
run_in_backgroundservers get killed between turns and shell&/nohupis unreliable. Just relaunch./target/release/catacomb --web 8081;pkill -f "web 8081"first. - Verifying UI visually without a browser driver (no puppeteer/playwright):
extract the relevant CSS/JS from
index.htmlinto a/tmp/*.htmlharness with a mock payload and screenshot with headless chromium:chromium --headless=new --no-sandbox --disable-gpu --hide-scrollbars \ --window-size=1100,1300 --virtual-time-budget=3000 \ --screenshot=/tmp/out.png "file:///tmp/harness.html"--virtual-time-budgetcan capture entry animations mid-flight; add a "settle" override (disable transitions, force final state) for a clean final shot.
The rename (yt-offline → Catacomb)
Done and pushed across 404362b (code), 3f61184 (docs), 5f95bdb (repo URLs):
- Crate/binary
catacomb(Cargo.toml, deb/rpm assets, PKGBUILD, package.sh,catacomb.desktop, launch.json, release CI,tests/api.rs→CARGO_BIN_EXE_catacomb). - Data paths: DB
yt-offline.db→catacomb.db; venv~/.local/share/yt-offline→~/.local/share/catacomb.migrate_legacy_paths()inmain.rsadopts the old names on first run (renames DB + WAL/SHM sidecars + venv dir; best-effort, no-op once migrated). Keep this function's OLD-name args asyt-offline— a blunt rename sed will wrongly make both sidescatacomband break migration (happened once; watch for it). - Display: window/tray/title/feed/login/SPA wordmarks → "Catacomb". Env
override
YT_OFFLINE_RENDERER→CATACOMB_RENDERER. - Verified: builds, 128+11 tests pass, scratch-dir migration preserved bytes, and the live cutover migrated the real 64 MB DB + venv (server served the library).
- Repo URLs point at
…/anassaeneroi/catacomb(Cargo.toml, PKGBUILD, package.sh).
Settings flow (the easy thing to get wrong)
Adding a setting touches five places (grep an existing one like
dedup_enabled or sponsorblock_mode end-to-end first):
config.rs— field +Default+default_with_dir().download_options.rs—Option<…>per-channel override (None = use global).downloader.rs— resolver merging global+override + apubfield onDownloader.- Both UIs:
app.rs(egui Settings + channel-options dialog) ANDweb_ui/index.html(Settings modal + channel dialog) ANDweb.rsSettingsPayload(GET reads config, POST writes config + pushes to liveDownloader). - Seed the
Downloaderfield at construction AND on settings-save, in BOTHapp.rsandweb.rs.
What this session shipped (committed, newest first)
5f95bdbrepo URLs → renamed Codeberg repo.3f61184/404362bRename → Catacomb (see above).6d2261bLogin page reskin —src/web_ui/login.html: serif wordmark + recording dot, aurora + grain, accent focus ring; system-serif (offline-safe).2f95e7fUnified cinematic video player. Replaced native<video controls>with ONE custom player for both direct + transcode. Custom scrubber (played/buffered/chapter-ticks/hover tooltip, drag-to-seek via pointer events, commits on release), speed popover (0.5–2×), persistent volume/speed/captions (localStorageplPrefs), CC toggle, PiP, fullscreen, auto-hiding controls + center flash, expanded keyboard (space/k j/l ←→ ↑↓ m c p f<>0–9). All seeking routes throughplayerSeek/effTime(transcode reload-at-offset intact). CSS prefix.pl-*; JS aroundplayVideo/updateVctrl/plBindScrubber.816da05Persist login sessions in SQLite —sessions(token, issued_at)table; map switchedInstant→u64; insert/delete/clear + rehydrate at startup. Fixes "restart logs everyone out".dd48e89api() 401 → reload to login instead of a cryptic "error" toast.615c088Maintenance modal → "diagnostics console" (CSS.mx-*): pulsing health verdict + status tiles + instrument panels. Presentation only; handler ids/classes untouched.6f61821Stats modal → "observatory" dashboard (CSS.sx-*): count-up metrics, self-drawing SVG area chart, growing histogram, ranked leaderboard.c8cb700"Cinematheque" reskin: embedded Instrument Serif + Hanken Grotesk (base64 OFL), accent aurora + film-grain, cinematic card hover, recording dot.983864fmacOS osxcross packaging;207013eWindows shippable .zip + CI;8b25787library sorting (download-date + grouped options).
Design-layer conventions (web UI)
- CSS is appended near the end of the
<style>block, AFTER the functional rules, so equal-specificity overrides win by source order. Prefixes:.sx-*= stats observatory,.mx-*= maintenance console,.pl-*= player. - Reveal animations gate behind a class added post-layout (e.g.
.sx-go) so they play once per open, not on re-render. All honorprefers-reduced-motion(global rule at the end of<style>). - Fonts:
--font-display(Instrument Serif),--font-body(Hanken Grotesk). Extra tokens:--radius,--radius-sm,--ring,--glow,--shadow,--hair.
Command-palette search overlay — SHIPPED (c12de9c)
openSearch() is now a .cp-* command palette (CSS .cp-*; JS openSearch,
runCommandSearch, renderCommandPalette, cpKeydown, updateSelected,
scrollIntoSelected, cpSelect, cpOpenRecent, cpClearRecents,
closeCommandPalette, ftsSnippet). Blurred backdrop, centered input, results
grouped by channel with highlighted FTS snippets (char(2)/char(3) → <mark>
via ftsSnippet), ↑↓ nav, Enter to open (cpSelect→selectVideo), Esc to
close, recents + quick-actions in localStorage['cp-recents']. Debounced
queries to /api/search?limit=60&q=… with a cpSeq guard against stale
responses. Upload date is looked up client-side via findVideo (SearchHit has
no date). Triggers: 🔍 header button + f hotkey. Verified by build + headless
harness screenshot. Built into the running :8081 binary.
Suggested next steps (pick up here)
- Push —
c12de9c(command palette) is committed but not yet pushed. - Any rough edges from real daily use (the player + palette especially — both verified via harness screenshots, not live click-through).
- A fresh web-UI surface to reskin/upgrade, or a roadmap item below.
Roadmap "surpass" items still open (see ROADMAP.md §3):
- 3.1 macOS binary — osxcross scaffolding done (
scripts/package.sh mac); needs the toolchain + SDK installed, then verify. Windows already ships. - 3.2 Android client (big), 3.8 plugin/scripting hooks (architectural).
- Phase 4 blue-sky: AI transcript summarisation (FTS transcript index already exists), TV-mode layout, multi-user accounts.
- Federation follow-up (3.5): in-UI "add remote" editor (peers are config-only).
Watch-outs / open questions
- The video player's seek/speed/captions are verified by code-path review + static chrome screenshot, NOT live playback (no headless video). Worth a real click-through in the app.
- After the rename + session-persistence work, the user's existing browser cookie predates both, so they must log in once more; logins after that survive restarts. A download password may or may not be set (toggled during testing) — that's user-driven, not a bug.
- The user's local
config.tomlsource_urlstill points at the old…/yt-offlinerepo (gitignored, user-specific) — change it in Settings if the UI "source" link should match the renamed repo. - Don't commit
cookies.txt,config.toml, orcatacomb.db(all gitignored; contain creds / the Argon2 password hash).