Commit graph

175 commits

Author SHA1 Message Date
Luna
4d83999edd Add statistics view, editable source_url, rewrite security audit
Statistics
- New `stats` module computes totals, top-N channels, per-year upload
  histogram, and per-week download activity from the in-memory library
  + watched/positions data.
- GET /api/stats endpoint returns the full report as JSON.
- Web UI: 📊 button in the header opens a stats modal with summary
  tiles, two CSS bar charts (weeks + years), and side-by-side top-N
  tables.
- Desktop GUI: 📊 Stats toggle in the top bar opens a window with
  the same data, rendered via egui rects (stdlib calendar math so
  there's no chrono dep).
- Bar chart helper `draw_bars` is reusable across stats sections.

Source URL (AGPL §13)
- `web.source_url` is now editable in both Settings UIs — was
  previously documented-only and required hand-editing config.toml.
- Settings POST persists it and the footer link updates immediately.
- Default config.toml ships with the Codeberg URL set.

SECURITY_AUDIT.md
- Re-audited the codebase as of commit 5999673; rewrote the file to
  document the threat model and which adversary each defense addresses.
- Resolved-from-2026-05-17 column shows what landed: session expiry,
  Secure cookie, rate-limit, body cap, CSP, X-Frame-Options,
  X-Content-Type-Options, safeUrl(), SHA-256 verify, chmod 0600 db.
- Documented accepted risks (plain-HTTP LAN, cookies.txt umask,
  job log stderr verbatim).
- Future-hardening checklist preserved for posterity.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 05:34:29 -07:00
Luna
59996735f3 Add bundled yt-dlp, music library, Plex metadata, sort by date; security + perf hardening
Bug fixes
- Desktop auto-rescan: snapshot was_running before check_notifications
  updates prev_job_states, so the post-download rescan actually fires.
- web::run shutdown: replace `let _ = tx;` (which dropped the sender
  immediately and short-circuited the shutdown select!) with
  std::mem::forget(tx); add an idle fallback so the `-> !` is honored.
- /api/preview now resolves the bundled yt-dlp path and extends PATH
  for deno discovery, matching the rest of the downloader.
- Scheduler interval clamped to >=1h on read so a manually-edited
  config.toml with 0 hours can't trigger every tick.
- maintenance::is_within canonicalizes the parent + filename when the
  target is missing; remove_files treats NotFound as success.

Security
- Session tokens stored with issued-at Instant and pruned past 30 day
  TTL on every touch (was: unbounded HashSet).
- Login rate-limited per source IP: 5 failures → 60s lockout (429).
- Secure cookie flag added when X-Forwarded-Proto: https is present.
- 4 MiB body size cap via DefaultBodyLimit.
- Security headers middleware: CSP, X-Content-Type-Options,
  X-Frame-Options, Referrer-Policy.
- JS safeUrl() defangs javascript:/vbscript:/data:text URLs before
  interpolation into src= attributes.
- Bundled yt-dlp install verifies SHA-256 against the release's
  SHA2-256SUMS; deno hash printed for visual inspection. Install also
  reports byte counts every 3s so users see live progress.
- yt-offline.db chmod 0600 at open time on Unix.

New features
- Bundled yt-dlp + deno mode: settings toggle (System/Bundled) plus
  one-click Install/Update from settings. Ensures executable bit on
  every launch in case the install chmod was missed.
- Download queue with configurable max_concurrent (1–10); pending
  jobs surfaced in both UIs.
- Music download mode: --extract-audio into music/<artist>/, separate
  sidebar entry, /api/music + /music-files/ endpoints, inline <audio>.
- Download quality selector: Best / 1080p / 720p / 480p / 360p.
- Sort videos by Newest / Oldest (upload_date from info.json with
  release_date fallback).
- Plex metadata: per-episode .nfo (title, season/episode, aired,
  runtime, plot from .description), <stem>-thumb.jpg symlink, and
  show-level tvshow.nfo.
- yt-dlp retry/throttle defaults: --retries 30 --fragment-retries 30
  --retry-sleep linear=1:30:2 --sleep-requests 1 to ride out the
  "Connection reset by peer" failures.

Performance
- password_required cached in AtomicBool; refreshed only on password
  change. Eliminates a SQLite query per static-file fetch.
- Job::log → VecDeque for O(1) front-pop instead of O(n) drain.
- Library scan parallelized across cores via stdlib parallel_map.
- Web UI poll: 600ms while active, 5s when idle, 2s after errors.
- Music scan now recursive (Artist/Album/Track.opus).
- percent_encode_segment uses write! to avoid per-byte String allocs.

Code quality
- 27 unit tests covering parse_progress, parse_stem, extract_after,
  detect_url_kind, srt_to_vtt, count_cookies, percent_encode_segment,
  bind_mode_of, hash_password, lang_label, is_within, sanitize,
  year_of, aired_date, xml_escape.
- Unified library::find_video + Channel::all_videos() iterator,
  replacing three duplicated linear searches.
- Downloader::browser wired to --cookies-from-browser as a fallback
  when no cookies.txt is present.
- mpv detection now matches the binary basename, not substring.
- Settings modal scroll fix; web settings expose all new fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 04:54:26 -07:00
Luna
1d72069913 Add full-scan mode, persist password in DB, fix channel re-check URLs, SRT subs, cookie clear, UI fixes
- Full-scan download toggle: omits --break-on-existing so all unarchived
  videos are fetched regardless of order, filling gaps in partial archives
- Filter "Aborting remaining downloads" from job logs (break-on-existing noise)
- Password hash moved from config.toml to SQLite settings table; survives
  git checkouts and rebuilds. config.toml and yt-offline.db added to .gitignore;
  config auto-generated on first run if absent
- Channel re-check URLs derived from folder name (/@handle or /channel/UCxxx)
  instead of info.json's canonical channel_url, preventing duplicate UCxxx folders
- SRT subtitles detected by library scanner and served via /api/sub-vtt/* endpoint
  that converts to WebVTT on the fly; browser <track> element can now play them
- Cookie clear button in both desktop and web UI (DELETE /api/cookies)
- Per-job X close button on finished download jobs in desktop GUI
- Fix widget ID clash when multiple download jobs are shown (push_id per job)
- Watched videos excluded from Continue Watching list and badge count
- settings table added to SQLite schema (key/value store)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 05:49:55 -07:00
Luna
5242b06ee5 Ignore cookies.txt (contains YouTube session cookies)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 03:48:07 -07:00
Luna
3d317797ec Add cookie paste UI, treat break-on-existing as success, skip playlist metafiles
- Update cookies.txt by pasting Netscape-format contents in web UI settings and
  the desktop GUI settings window (shared write/validation in web.rs)
- Treat yt-dlp exit code 101 (--break-on-existing reached an archived video) as
  a successful "up to date" outcome instead of a failed job
- Add --no-write-playlist-metafiles so channel avatars/info aren't written as
  phantom "Title [CHANNEL_ID]" files

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 03:47:53 -07:00
Luna
1ef3fe56c6 Add web themes, network binding, UI auth, library maintenance; optimize + fix rescan
Features:
- Port all 7 desktop themes (Dracula, Trans, Emo variants, etc.) to the web UI
- Configurable bind interface (localhost/Tailscale/LAN/all) with detected IPs,
  settable from both the web UI and desktop GUI
- Optional password that gates the whole UI and all API access via session
  cookies (Argon2), with a login page, logout, and middleware; settable from
  both UIs
- Library maintenance: scan for duplicate video IDs and missing assets
  (thumbnail/info.json/description), remove duplicates (scan-and-confirm, with
  out-of-library path guard), and re-fetch missing sidecars via yt-dlp.
  Surfaced in both the web UI and a desktop GUI window.

Optimizations / cleanup:
- Cache has_chapters at scan time (library.rs) instead of re-reading and
  parsing every info.json on each /api/library request
- Use the cached channel size in get_library
- Pool a single SQLite connection in WebState instead of opening one per request
- Remove a 1.2 GB stray nested src/yt-offline duplicate tree

Fixes:
- Rescan no longer clears all thumbnail textures (caused thumbnails to unload);
  prune only removed entries and bump library_generation so the card grid
  refreshes correctly

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 02:58:09 -07:00
Luna
74b3efd990 Major update with lots of new features and fixes 2026-05-17 05:36:04 -07:00
Luna
17c149c21a PKGBUILD: disable LTO to fix bundled sqlite link errors
Default makepkg.conf has OPTIONS=(... lto ...) which enables LTO globally.
With LTO, the cc crate compiles bundled sqlite3 as LLVM IR objects, and
rust-lld then fails to find any sqlite3_* symbols when linking the final
binary (undefined symbol errors for sqlite3_prepare_v3, sqlite3_finalize,
sqlite3_column_*, etc.).

Adding options=('!lto') tells makepkg to not enable LTO for this package,
which lets the bundled sqlite link normally as machine-code static lib.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 08:28:56 -07:00
Luna
8b4796b518 Fix web UI, sort labels, add channel right-click menu
Web UI:
- Replace SSE with simple 600ms polling (GET /api/progress) — SSE added
  complexity with no real benefit for a local tool and was the source of
  connection failures
- Poll task moved into request handler (dl.poll() on each /api/progress call)
  so no background goroutine needed; simplifies state greatly
- Remove tokio-stream dependency (no longer needed)
- Fix: create channels dir before opening DB in web serve()
- Rescan also refreshes watched set from DB
- Bulk watched, sort, channel download button all working in web UI

Sort labels:
- Rename "Dur ↑" / "Dur ↓" → "Shortest" / "Longest"
- Rename "Size ↑" / "Size ↓" → "Smallest" / "Largest"

Channel right-click context menu:
- Right-click any channel in sidebar → "Check for new videos" starts a
  yt-dlp job on the stored channel_url from its info.json
- Also shows "Open folder" shortcut
- Grayed out with tooltip if no URL is stored yet (channel has no info.json)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-14 07:25:54 -07:00
Luna
56f1f1bd80 Fix PKGBUILD and notify-rust backend to eliminate libdbus build dep
- notify-rust: switch feature d → z (zbus pure-Rust backend); removes the
  system libdbus/pkgconf dependency that caused makepkg build failure
- PKGBUILD: fix license GPL3 → AGPL-3.0-only, makedepends rustup → rust

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-14 07:10:17 -07:00
Luna
80cce00b99 Add web UI, AGPL3, notifications, continue watching, bulk watched, storage stats, scheduler
License:
- GPL3 → AGPL3

New features:
- Web interface (--web [port]): axum/tokio server with SSE progress, library browser,
  download trigger, watched toggle; embedded single-page HTML/JS UI
- Desktop notifications via notify-rust when downloads complete or fail
- Continue Watching sidebar entry showing videos with saved resume positions
- Bulk select mode: select multiple videos, mark all watched/unwatched at once
- Storage stats: channel disk usage shown in sidebar next to video count
- Scheduled channel checks: configurable interval (hours), auto re-downloads all
  tracked channels using channel_url from info.json; enabled in Settings

Theme fixes:
- Scene Queen: remove override_text_color so active button text uses fg_stroke
  (dark navy on neon green) instead of near-white — fixes unreadable contrast
- Trans: override_text_color → hot pink #cc0066 for pink-tinted text throughout

Settings additions:
- Auto-check channels toggle + interval_hours DragValue
- Web UI port setting

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-11 04:27:04 -07:00
Luna
5b3b8fc901 Add watched tracking, resume positions, sort, density slider, channel metadata, mpv IPC, browser selector, auto-rescan
- SQLite DB for watched/position persistence (database.rs)
- Mark watched toggle on cards and detail panel
- mpv IPC socket integration for resume position tracking (unix)
- Sort by title, duration, file size
- Thumbnail density slider in top bar
- Channel metadata banner (subscriber count, uploader, channel URL)
- Cookie browser selector dropdown in settings
- Auto-rescan library when a download job finishes
- Fix yt-dlp --no-progress-bar flag (does not exist; removed)
- Browser field wired through config → downloader

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-11 03:47:29 -07:00
Luna
95a73b0980 add icon to README for link preview 2026-05-11 03:27:08 -07:00
Luna
5efc100fec add cross-platform build instructions (Debian, macOS, Windows) 2026-05-11 03:26:23 -07:00
Luna
8eec4fe094 fix PKGBUILD: rename package to yt-offline, fix binary and install paths 2026-05-11 03:23:38 -07:00
Luna
3a69628b42 changed it lol 2026-05-11 03:19:53 -07:00
Luna
c11d1c3366 add themes, settings GUI, URL-aware download routing, and playlist view 2026-05-11 03:18:16 -07:00
Luna
bc06520bc8 update PKGBUILD to use git source from Codeberg 2026-05-11 02:52:17 -07:00
Luna
c2f5edae89 merge master into main 2026-05-11 02:47:31 -07:00
Luna
f3c136a018 add .claude to gitignore 2026-05-11 02:45:52 -07:00
Luna
8d879ccd0d initial commit 2026-05-11 02:45:06 -07:00
Luna
00de44ef7c Initial commit 2026-05-11 11:38:42 +02:00
Luna
abf3af5768 First version of youtube-backup, a tool to backup your YouTube channel. 2026-05-11 02:33:52 -07:00
luna
acf188738a Add the Rust/egui app sources
- library.rs: scans channels/<name>/ into channels + videos by filename stem
- downloader.rs: runs yt-dlp in a background thread, streams progress to the UI
- app.rs / main.rs: channel sidebar, searchable thumbnail list, detail/description
  panel, downloads panel; plays videos via mpv (falls back to xdg-open)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 01:35:22 -07:00
luna
ce503e50e2 Initial commit: yt-dlp front-end with channel browser
Rust/egui desktop app:
- library.rs: scans channels/<name>/ into channels + videos by filename stem
- downloader.rs: runs yt-dlp in a background thread, streams progress to the UI
- app.rs: channel sidebar, searchable video list with thumbnails, detail/description
  panel, downloads panel, plays videos via mpv (falls back to xdg-open)

channels/ is gitignored (holds the downloaded media).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 01:35:16 -07:00