Commit graph

184 commits

Author SHA1 Message Date
Luna
095f1d48b0
feat(remote): PeerTube browse passthroughs on RemoteClientKind
pt_channels/pt_channel_videos/pt_video_media/pt_watch_url dispatch to the
PeerTubeClient; Err for a catacomb remote. RemoteChannelInfo now Serialize.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 05:33:06 -07:00
Luna
90e0d4231e
docs: implementation plan for PeerTube browse + archive (phase 3)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 02:14:38 -07:00
Luna
e735692736
docs: spec for PeerTube browse + archive (federation phase 3)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 02:09:07 -07:00
Luna
b9271e4ab1
feat(desktop): federation peers editor (add/edit/remove/test, kind-aware)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 01:13:11 -07:00
Luna
476efcf129
feat(web-ui): federation peers editor (add/edit/remove/test, kind-aware)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 00:58:01 -07:00
Luna
a3d133562e
feat(web): PUT /api/remotes + test endpoint, kind-aware, live-apply
URL-keyed write-only password merge; whole-list replace rebuilds the live
RwLock client list; POST /api/remotes/test checks reachability per kind. GET
/api/remotes now reports kind + has_password (never plaintext). Integration
test covers the PUT/GET round-trip, removal, and password preservation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 07:00:36 -07:00
Luna
db0c74ffca
feat(remote): RemoteClientKind enum + kind-aware live remotes list
Both front-ends hold Vec<Arc<RemoteClientKind>> (web behind RwLock). Catacomb
browse unchanged; PeerTube remotes report a phase-3 stopgap when browsed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 06:56:26 -07:00
Luna
be3271ab32
docs: implementation plan for kind-aware remote editor (phase 2)
Four tasks: RemoteClientKind enum + storage migration + browse stopgap; web
PUT/test endpoints + URL-keyed password merge; web UI editor section; desktop
editor section + test thread.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 06:48:56 -07:00
Luna
afaeaa0d43
docs: spec for kind-aware remote editor (phase 2)
Inherits the federation-editor spec; adds RemoteClientKind enum (catacomb |
peertube), kind selector + conditional username field in both editors, kind in
the API, kind-branched test-connection, and a phase-3 stopgap for browsing
PeerTube remotes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 06:44:03 -07:00
Luna
d4305b4fc7
docs: handoff — PeerTube phase 1 done, phases 2-3 outlined
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 06:19:17 -07:00
Luna
00ae19e666
feat(peertube): OAuth2 + list channels / videos / media over REST
Completes the phase-1 backend client: password-grant OAuth with refresh-token
renewal (falling back to a full re-grant), anonymous public browsing when no
creds are set, list_channels, paginated channel_videos, and on-demand
video_media (direct MP4, None for HLS-only). Wires the tested pure mapping fns
to the API. The client is dead-code-allowed until the editor (phase 2) and
browse UI (phase 3) construct it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 06:18:13 -07:00
Luna
f7f36d9bca
feat(peertube): pure JSON mapping (channels, videos, media, tokens)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 04:46:07 -07:00
Luna
e69ebc7a75
feat(peertube): module skeleton + URL/target parsing
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 04:42:08 -07:00
Luna
bf3f2b0030
feat(config): RemoteKind + username for PeerTube remotes
Non-breaking: existing [[remote]] entries default to kind=catacomb.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 04:39:44 -07:00
Luna
96965aaa32
docs: implementation plan for PeerTube client backend (phase 1)
Four TDD tasks: config RemoteKind/username, peertube module + URL parsing,
pure JSON mapping fns (fixture-tested), and the OAuth2 + REST HTTP methods.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 04:35:57 -07:00
Luna
6f2376932a
docs: spec for PeerTube client + multi-kind config (phase 1)
First of three phased sub-projects for PeerTube federation. Backend-only:
RemoteKind enum + kind/username config fields (non-breaking), and a blocking
PeerTubeClient (OAuth2, list channels, paginated channel videos, media
resolution, watch URL) mapped into the existing remote types. Headless,
fixture-tested. Editor (phase 2) and browse UI (phase 3) follow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 04:23:02 -07:00
Luna
112a10991d
docs: spec for in-UI federation remote editor
Add/edit/remove/test federation peers from Settings in both UIs, applied live.
Web gets RwLock<Vec<Arc<RemoteClient>>> + dedicated /api/remotes PUT/test
endpoints; passwords masked/write-only on the web (kept-by-URL on blank save),
shown in the clear on the local desktop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 04:03:58 -07:00
Luna
cafcf0cbe2
docs: handoff — empty-library bug resolved (scanning state + snapshot)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 21:21:06 -07:00
Luna
3e6bcfb564
feat(desktop): instant startup from persisted library snapshot
Seed self.library from load_library_snapshot before spawning the scan thread,
so a warm launch renders the library immediately (status '… refreshing…')
instead of a blank/scanning window. Write the snapshot after every scan (the
startup thread and rescan). The background scan is unchanged and still swaps
in authoritative data; the snapshot only ever shows briefly-stale state.

Verified live against the real 5574-video library: second launch renders the
full list within 2s (well under the ~64s cold scan) with the refreshing
status; deleting the snapshot row falls back to the scanning spinner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 21:19:13 -07:00
Luna
5a55c15209
feat(db): persist library snapshot as JSON blob keyed by root
Add serde derives to the library structs and a library_snapshot table with
save_library_snapshot/load_library_snapshot. Load returns None on a missing
row or unparsable JSON (with #[serde(default)] fields tolerating struct
evolution). Groundwork for desktop instant startup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 19:20:39 -07:00
Luna
cfed8118f7
docs: implementation plan for persistent library snapshot
Two-task TDD plan: (1) serde-derive the library structs + library_snapshot
table with save/load + unit tests; (2) desktop wiring to seed self.library at
launch and write the snapshot after every scan.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 17:47:36 -07:00
Luna
deeccfe337
docs: spec for persistent library snapshot (desktop instant startup)
Design for Step 2: persist the scanned Vec<Channel> as a JSON blob keyed by
library root, seed self.library from it at launch for instant render, then
background-rescan and swap. Desktop-only; blob over normalized tables (YAGNI).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 17:43:44 -07:00
Luna
843fee58ed
fix: show scanning state instead of empty library on cold launch
The desktop GUI's startup library scan runs on a background thread; on a
cold filesystem cache a large library on encrypted/compressed storage takes
~60s to walk + stat, during which the content area rendered an empty video
list. That looked like a broken/empty library and prompted a needless Rescan
(which only appeared to help because the OS cache was warm by then).

Render a centered spinner + "Scanning library…" while the initial scan is
in flight (library_load_rx still open and library empty). The scan/drain
pipeline is unchanged; this is display-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 17:40:02 -07:00
Luna
be5634a2f9
docs: handoff — empty-library-at-launch findings, remote-editor brainstorm state
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 03:52:22 -07:00
Luna
b74dc0924d
docs: retire player click-through watch-out (verified in daily use)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 03:35:59 -07:00
Luna
2c9031bcf3
fix: stop narrow windows shoving the library under the sidebar
At the ~1000px window minimum, the toolbar's right-aligned sort group
ran out of row width and overflowed leftward past the panel edge; egui
then advances the vertical cursor from the overflowed rect, so every
row below (view toggle, the whole list) shifted left and was clipped
under the sidebar.

The sort chips now right-align only when a font-metrics measurement
says they fit, and otherwise drop to their own wrapped row. The top
bar likewise wraps instead of pushing nav buttons off-screen, with the
right-aligned status label width-guarded the same way (truncating
fallback).

Verified via live GUI screenshots at 1000x700 and 1400x800; the wide
layout is unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 03:31:56 -07:00
Luna
23ed80a3b7
perf: desktop row virtualization, phase 2
Render List/Card/Grid through ScrollArea::show_rows so only the visible
row range is laid out per frame. Fixed-size cells (allocate_ui_with_layout
+ set_min_size) enforce a uniform lattice; titles truncate to one line with
the full title on hover. List separators are painted hlines inside the
cell; Grid is a manual horizontal run of fixed cells per lattice row with
cols computed from available width before show_rows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 02:52:04 -07:00
Luna
d4bed019b2
perf: runtime performance pass, phase 1 (DB pragmas, batched scan writes)
Executes docs/superpowers/specs/2026-07-07-runtime-performance-pass-design.md:

- database.rs: every pooled connection now runs WAL + synchronous=NORMAL
  + busy_timeout=5000 + foreign_keys=ON via with_init (in-memory pools
  get the latter two). Fixes the SQLITE_BUSY failure mode under the
  parallel scanner and makes FK cascades work on every connection, not
  just whichever one last ran the old one-shot pragma in delete_folder.
- database.rs: prepare_cached for the per-video info_cache queries; new
  info_cache_put_many bulk upsert (single transaction) replaces the
  per-row autocommitted info_cache_put, with a round-trip unit test.
- library.rs: enrich_with_cache collects cache misses and flushes them
  once per channel; title sort uses sort_by_cached_key.
- app.rs: Title/ChannelAsc sorts use sort_by_cached_key instead of
  allocating two lowercased strings per comparison.
- Cargo.toml: panic=abort in release (crash-hook still fires; Cargo
  forces unwind for test harnesses). PACKAGING.md documents the local
  RUSTFLAGS target-cpu=native opt-in; repo stays portable.
- .gitignore: catacomb.db-wal / catacomb.db-shm sidecars.

Verified: 146 tests pass (integration suite exercises the pragmas
end-to-end), x86_64-pc-windows-gnu check green, live smoke shows
journal_mode=wal + batched cache writes landing correct rows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 01:22:40 -07:00
Luna
891b1e0d3c
feat(web): fix mobile scaling regressions and ship installable PWA
Mobile fixes (all in the embedded SPA):
- Move the mobile media queries to the bottom of the stylesheet — the
  cinematheque design layer was appended after them, so its base rules
  (header padding, video heights) silently overrode the phone layout by
  source order. A comment now pins the ordering invariant.
- The design layer's uiDrop entry animation (fill-mode:both) permanently
  overrode the sidebar's translateX(-100%), leaving the drawer stuck open
  over the content on phones; the mobile block now disables it.
- Lower the single-column breakpoint 380px -> 350px: it was catching
  360/375px phones and blowing every card up to full width.
- viewport-fit=cover so the env(safe-area-inset-*) paddings actually
  resolve on iPhones; notch-safe padding on header/content/modals.
- 16px inputs on small screens (kills iOS Safari's focus auto-zoom),
  same for the login page's password field.
- Mobile rules for the design-layer components that had none: full-height
  command-palette sheet, clamped stats numerals, coarse-pointer touch
  targets for the custom player (taller scrub track, always-visible
  thumb, no volume slider).
- CSP font-src now allows data: — the embedded base64 woff2 fonts were
  being blocked and silently fell back to system faces.

PWA:
- manifest.webmanifest, minimal service worker, and a new Catacomb arch
  icon set (SVG source + rendered PNGs incl. maskable + apple-touch),
  all include_str!/include_bytes!-embedded like the HTML.
- sw.js only intercepts GET navigations to "/" (network-first, cached
  offline fallback) and the static assets; /api, /ws, /files,
  /music-files, /feed are never touched, and it's served no-store so
  binary upgrades keep propagating.
- Routes + auth_middleware allowlist so the browser can fetch the
  statics pre-login; theme-color meta tracks the active theme's panel.
- tests/api.rs covers the new endpoints incl. the ungated-when-password
  invariant.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 00:04:17 -07:00
Luna
52c561b47b feat(web): enhance layout and responsiveness of UI components 2026-07-04 02:54:00 -07:00
Luna
3a55ff5b8a feat(android): Compose app with bundled yt-dlp, theme pack, nav & progress
Turn android/ into a real Gradle app (Kotlin + Jetpack Compose + Material3)
that bundles an on-device yt-dlp engine and reuses the Phase-4 Rust JNI core.

- Bundled engine: youtubedl-android (Python + yt-dlp + ffmpeg + aria2c) via
  Engine.kt; initialises off the main thread on first launch. Requires legacy
  jniLib packaging so the bundled .zip.so payloads extract to disk.
- Intuitive navigation: bottom NavigationBar — Download / Files / Settings.
- Settings section: dedicated screen with a live theme picker, default-quality
  chips, engine info, and a Rust-core demo.
- All 19 desktop themes ported from src/theme.rs into Theme.kt as Material3
  colour schemes, shown as tappable swatches; selection persisted (Prefs.kt).
- Download screen: live platform detection through the Rust core as you type,
  plus an improved animated determinate progress indicator (%/ETA/status/
  success-error) and a scrollable log.

Toolchain pinned for reproducibility: Gradle 8.9 wrapper, AGP 8.5.2, Kotlin
1.9.24, Compose BOM 2024.06.00, compileSdk 34, minSdk 24, ABIs arm64-v8a +
x86_64. build-apk.sh runs Gradle on a JDK <= 21 (system JDK may break R8) and
builds the Rust libs first.

Verified on an Android 14 x86_64 emulator: installs, launches, engine reports
"yt-dlp ready", Rust .so loads, theme switching re-themes live and persists
across restart, all screens render. (A real YouTube download still depends on
the anti-bot/POT question — Phases 2-3, device-only.)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-04 02:54:00 -07:00
Luna
1790fd1a22 feat(android): installable demo APK exercising the Rust JNI core
Add android/demo — a minimal, dependency-free app (plain Android Views,
no Gradle/AGP/AndroidX/Compose) that calls the Phase-4 JNI core on-device
and displays the results. build-apk.sh drives the SDK build-tools directly
(aapt2 -> javac -> d8 -> zip -> zipalign -> apksigner), compiling against
android.jar alone so it needs no network and tolerates a very new system
JDK by routing the SDK tools through a JDK <= 21.

The app has three buttons -> platformFromUrl/platformDirName, classifyError,
and vttParse, each calling libcatacomb_core.so and showing the JSON.

Verified on an Android 14 (API 34) x86_64 emulator: installs, launches,
loads the .so, and returns correct JSON on-device
({"dir_name":"channels","display_name":"YouTube","icon":"..."} for a
YouTube URL). Output APK (out/, git-ignored): catacomb-spike-debug.apk,
v2+v3 signed, minSdk 24, native code for arm64-v8a + x86_64.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-04 02:54:00 -07:00
Luna
9a613239f2 feat(android): Phase 4 — Rust core to Android .so via JNI
Implement the shared-Rust-core leg of the Android Stage-1 prototype
(docs/superpowers/specs/2026-06-27-android-stage1-prototype-plan.md).

android/rust/catacomb_core is a cdylib that reuses the pure desktop
modules (vtt, error_class, platform) verbatim via #[path] includes — no
logic fork — and exposes them to Kotlin over JNI as String-in/String-out
entry points:

  RustCore.vttParse(vtt)        -> JSON [{start,text},...]
  RustCore.classifyError(log)   -> JSON {class,label,hint}
  RustCore.platformFromUrl(url) -> JSON {dir_name,display_name,icon}
  RustCore.platformDirName(url) -> folder name

Built with panic=abort + per-entry catch_unwind so a Rust panic can never
unwind into the JVM. build.sh locates the NDK portably and cross-compiles
to arm64-v8a + x86_64, depositing the .so into app/src/main/jniLibs/.

Verified: cargo test (38 pass), llvm-readelf confirms all 4 JNI symbols
exported in the arm64 .so, and an end-to-end host-JVM round-trip
(System.load + call each native method) returns correct JSON.

Phases 2/3/5 (WebView-BotGuard anti-bot) remain device-only and untouched.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-04 02:54:00 -07:00
Luna
f3e6d3b5b7 feat(app): add egui context for background thread repaint requests 2026-07-04 02:54:00 -07:00
Luna
4de452c503 feat(tray): integrate egui context for system tray events to ensure immediate UI updates 2026-07-04 02:54:00 -07:00
Luna
f185e7062f Trigger mirror sync 20260629 2026-06-29 11:19:54 -07:00
Luna
cbade2cfd7 feat: desktop visual refresh + 12-theme pack
- 12 new themes (neon/goth/dev/cozy) + hand-tuned Dark/Light (19 total)
- theme-aware semantic accents replacing hardcoded selection rings
- List/Card/Grid video-list modes with global default + per-view override
- unified placeholder thumbnails + typography/spacing refresh
2026-06-28 00:57:28 -07:00
Luna
5091d0de96 fix(app): Grid mode no longer paints thumbnail twice; drop unused ThemeAccents::LEGACY 2026-06-27 19:30:02 -07:00
Luna
29d449eb9b style(app): bump base spacing + card title size for legibility 2026-06-27 19:21:10 -07:00
Luna
5aaa760ab4 feat(app): Settings picker for default view mode 2026-06-27 19:19:12 -07:00
Luna
4b011f8584 feat(app): view-mode toolbar toggle with per-view override 2026-06-27 19:17:00 -07:00
Luna
ada5b6d802 feat(app): List/Card/Grid video list render modes 2026-06-27 19:14:45 -07:00
Luna
91f3d8500d feat(app): unified theme-tinted placeholder thumbnails 2026-06-27 10:58:29 -07:00
Luna
0336615fa3 feat(app): ViewMode enum + global default + per-view overrides 2026-06-27 10:55:53 -07:00
Luna
1c858a3271 feat(app): use theme-aware accent rings for select/play/bulk 2026-06-27 10:51:03 -07:00
Luna
5fb8cc7036 feat(app): wire ThemeAccents snapshot, recompute on theme change 2026-06-27 10:48:27 -07:00
Luna
bb85d1fac0 feat(config): add [ui] default_view_mode field 2026-06-27 10:45:05 -07:00
Luna
0a58f8ae40 feat(theme): add 12 new themes (neon, goth, dev, cozy) 2026-06-27 10:42:43 -07:00
Luna
01c258e6d2 feat(theme): hand-tune default Dark/Light themes 2026-06-27 10:39:04 -07:00
Luna
ee1a02e20d feat(theme): add ThemeAccents struct + accents_for() lookup 2026-06-27 10:33:58 -07:00