Commit Graph
100 Commits
Author SHA1 Message Date
LeoVasanko f27bb4e7f3 Revert "macOS: WKWebView system backend (variant B)"
This reverts commit 5b40e306d1.
2026-09-23 16:56:50 +00:00
LeoVasanko 5b40e306d1 macOS: WKWebView system backend (variant B)
release / gui-build (linux, bash) (push) Successful in 45s
release / gui-build (windows, cmd) (push) Successful in 1m14s
release / gui-build (macos, bash) (push) Successful in 55s
Default pywebview backend on macOS, no bundled Qt/Chromium — much
smaller installer and updates.
2026-09-23 03:52:40 +00:00
LeoVasanko aea73ccd36 macOS: PyQt6-WebEngine backend (modern Chromium ~122, variant A)
release / gui-build (linux, bash) (push) Successful in 57s
release / gui-build (windows, cmd) (push) Successful in 1m20s
release / gui-build (macos, bash) (push) Successful in 1m57s
PyQt5's QtWebEngine is Chromium 87, too old for aspect-ratio and other
modern CSS. Qt6 WebEngine tracks current Chromium.
2026-09-23 03:51:32 +00:00
LeoVasanko 9e0ccb13bd Tolerate expanded-component pkg layout on newer macOS
release / gui-build (linux, bash) (push) Successful in 44s
release / gui-build (windows, cmd) (push) Successful in 1m16s
release / gui-build (macos, bash) (push) Successful in 1m22s
pkgutil --expand may yield the component as an already-expanded
directory rather than an archived file; handle both. List the expanded
contents in the error message if the layout is unexpected.
2026-09-23 03:33:06 +00:00
LeoVasanko aeb6958aab Add Diagnostics section to settings: version, browser engine, log view
release / gui-build (linux, bash) (push) Successful in 56s
release / gui-build (windows, cmd) (push) Successful in 1m22s
release / gui-build (macos, bash) (push) Failing after 1m29s
For triaging webview rendering issues without devtools: settings panel
now shows app version, the webview user agent, and the application log
(pre-wrap, 80ch, scrollable, refreshable). New endpoints /api/version,
/api/log; client-side JS errors are captured globally and posted to
/api/client-log (client-errors.log in the log dir).
2026-09-23 03:29:12 +00:00
LeoVasanko 3711d3dd3c Fix macOS pkg post-processing breaking the payload
pkgutil --expand-full flattens the component payload to loose files that
--flatten cannot repack, producing a pkg Installer accepts but installs
nothing from (also breaking the postinstall auto-open). Use nested
regular --expand: product for the Distribution domains edit, component
for the postinstall sudo-prefix removal.
2026-09-23 03:03:53 +00:00
LeoVasanko 039824f236 Upload only the consumed feed files; uniform nupkg names
release.py no longer uploads the legacy RELEASES and assets.*.json
manifests — the in-app updater reads only releases.<channel>.json plus
the nupkg it references. The Windows feed nupkg gains its -win channel
marker (vpk omits it for the legacy default channel), so all platforms
are uniformly mediahive-{ver}-{channel}-full.nupkg. The -full suffix is
Velopack's asset type and stays.
2026-09-23 02:37:59 +00:00
LeoVasanko f078e9bec0 Simplify release workflow; node and .NET are runner prerequisites
Drop the per-job uv-installed nodejs-wheel steps — runner hosts must
have git, uv, node/npm and the .NET SDK installed system-wide.
2026-09-23 02:32:14 +00:00
LeoVasanko ff493506ba Rewrite platform naming around a single descriptor
release / gui-build (linux, bash) (push) Successful in 56s
release / gui-build (windows, cmd) (push) Successful in 1m19s
release / gui-build (macos, bash) (push) Successful in 2m57s
Replace _platform_zip_suffix() and its overrides with a _Platform
NamedTuple (tag/rid/dist_dir/icon/main_exe/setup_ext) plus
setup_artifact_name(). create_zip() is Windows-only in practice, so it
is now create_portable_zip() with a fixed -win64-portable.zip name
instead of a generic name patched by string replace.
2026-09-23 02:22:18 +00:00
LeoVasanko 5f3ba47098 Simplify artifact names: -macos-setup.pkg, -linux-setup.AppImage
Arch markers dropped except win64: macOS builds are arm64-only and we
ship a single Linux flavor.
2026-09-23 02:15:21 +00:00
LeoVasanko 01c353502c Lowercase update-feed nupkgs in release assets
MediaHive-*-full.nupkg sorted between the capitalized Setup/AppImage
user downloads on the Gitea release page. Renamed to mediahive-* they
group with the wheel/sdist below the downloads; feed manifests
(RELEASES, releases.*.json, assets.*.json) are patched to match.
2026-09-23 02:08:33 +00:00
LeoVasanko 65fdae1546 Make macOS pkg per-user only (~/Applications)
System installs land in /Applications where Velopack's UpdateMac may not
be able to replace the .app during auto-update. Restricting the
distribution domains to currentUserHome skips the Destination Select
page and needs no admin rights; the postinstall script's sudo prefixes
are dropped since it already runs as the installing user.
2026-09-23 01:54:42 +00:00
LeoVasanko 832da6c315 Keep both macOS install domains (system and per-user)
Collapsing <domains> to localSystem did not remove the Destination
Select page (Installer shows it regardless) and only removed the
~/Applications option. Restore Velopack's default of both domains.
2026-09-23 01:52:18 +00:00
LeoVasanko 3142bc2cb2 Fix doubled platformdirs paths; pick newest dotnet runtime
release / gui-build (linux, bash) (push) Successful in 54s
release / gui-build (windows, cmd) (push) Successful in 1m24s
release / gui-build (macos, bash) (push) Successful in 1m47s
appauthor=False/opinion=False avoids mediahive\mediahive\Cache style
paths on Windows. fetch_dotnet now probes all known dotnet locations and
selects the highest Microsoft.NETCore.App major, requiring the version
vpk's TFM targets (10) — PATH on the Windows CI runner resolves to a
runtime-only .NET 8 while scoop holds the SDK 10.
2026-09-23 00:49:38 +00:00
LeoVasanko e514829737 Top-level imports; catch only expected exceptions
release / gui-build (linux, bash) (push) Successful in 56s
release / gui-build (windows, cmd) (push) Failing after 1m37s
release / gui-build (macos, bash) (push) Failing after 13m21s
velopack/tracerite/fastapi-vue are always available in the GUI build, so
drop the in-function import guards. The update checker now catches only
RuntimeError (not a Velopack install: dev/portable) and OSError
(network). Build script and PyInstaller spec use platformdirs for the
persistent tool cache.
2026-09-23 00:44:10 +00:00
LeoVasanko 50420984f1 Use platformdirs for config and log locations
Config and logs now live in local (non-roaming) app data on Windows
(%LOCALAPPDATA%\mediahive) instead of roaming. Existing files in the
old roaming location are left untouched; no migration.
2026-09-23 00:44:10 +00:00
LeoVasanko f9002b19a2 Require system .NET SDK; cache vpk/ffmpeg persistently
release / gui-build (linux, bash) (push) Failing after 50s
release / gui-build (macos, bash) (push) Successful in 1m39s
release / gui-build (windows, cmd) (push) Failing after 1m40s
Per-build downloads of the dotnet runtime (~80 MB) were slow and flaky
(IncompleteRead killed a CI run). The .NET SDK is now a build-host
prerequisite; vpk and ffmpeg download once into a user-level cache
(~/.cache/mediahive-build, %LOCALAPPDATA%\mediahive-build) that
survives the per-run build dir.
2026-09-23 00:28:11 +00:00
LeoVasanko 37f786f5ed Add welcome page text to macOS pkg installer
release / gui-build (linux, bash) (push) Failing after 51s
release / gui-build (macos, bash) (push) Successful in 1m43s
release / gui-build (windows, cmd) (push) Successful in 1m48s
Tells the user to allow the permission prompts macOS may show during
install and first launch. License page remains absent; Destination
Select was already stripped, leaving Introduction, Install, Summary.
2026-09-23 00:17:51 +00:00
LeoVasanko c64f54fe0f Strip Destination Select page from macOS pkg installer
release / gui-build (linux, bash) (push) Successful in 55s
release / gui-build (windows, cmd) (push) Successful in 1m39s
release / gui-build (macos, bash) (push) Successful in 1m36s
Collapse the Velopack-generated distribution.xml to a single install
domain so macOS Installer skips Destination Select, leaving Apple's
minimum pages (Introduction, Install, Summary). Welcome/license/readme/
conclusion pages are already absent (no --inst* options passed).
2026-09-23 00:12:32 +00:00
LeoVasanko bb88ab7d98 Fix tarfile.open call in dotnet bootstrap (fileobj=)
release / gui-build (linux, bash) (push) Successful in 1m4s
release / gui-build (windows, cmd) (push) Successful in 1m43s
release / gui-build (macos, bash) (push) Successful in 1m42s
2026-09-22 23:52:52 +00:00
LeoVasanko 303aabc181 Velopack packaging on all platforms with in-app auto-updates
release / gui-build (linux, bash) (push) Failing after 50s
release / gui-build (macos, bash) (push) Successful in 1m44s
release / gui-build (windows, cmd) (push) Successful in 1m48s
- macOS: .pkg installer replaces the DMG; Linux: .AppImage replaces the ZIP
- winmain runs velopack.App() first (proper hook handling) and checks for
  updates in the background; downloads are applied on next launch
- release.py uploads the vpk update feed (releases.<channel>.json, nupkgs)
  so GiteaSource finds updates on the latest release
- guibuild.py bootstraps a .NET runtime into build/dotnet when the runner
  host lacks one
2026-09-22 23:50:50 +00:00
LeoVasanko e8d2c5773a Exit fast on Velopack hook args; name ZIP artifact win64-portable
release / gui-build (macos, bash) (push) Successful in 1m14s
release / gui-build (linux, bash) (push) Successful in 1m13s
release / gui-build (windows, cmd) (push) Successful in 1m39s
Velopack runs the app with --veloapp-install/-updated/-uninstall during
those operations; without fast-exit handling the full GUI booted
mid-install (also clobbering config via the first-run setup flow) and
the installer reported hook errors. The post-install launch has no hook
args and still starts the app normally.
2026-09-22 23:28:19 +00:00
LeoVasanko 59d2f157eb Probe scoop dotnet-sdk path explicitly (runner service has stale env)
release / gui-build (linux, bash) (push) Successful in 1m3s
release / gui-build (macos, bash) (push) Successful in 1m5s
release / gui-build (windows, cmd) (push) Successful in 1m34s
2026-09-22 23:14:08 +00:00
LeoVasanko 3377702e98 Build Windows installer with Velopack instead of WiX
release / gui-build (linux, bash) (push) Successful in 1m13s
release / gui-build (windows, cmd) (push) Failing after 1m34s
release / gui-build (macos, bash) (push) Successful in 1m13s
vpk runs on the machine's modern .NET runtime (WiX 3.11's .NET Framework
shim fails under the SYSTEM account) and produces a per-user Setup.exe
that needs no runtime on end-user machines.
2026-09-22 23:11:30 +00:00
LeoVasanko 6dee65b4c6 Remove temporary Windows host probe workflow 2026-09-22 22:40:37 +00:00
LeoVasanko c59040ce52 Surface WiX tool stdout/stderr on MSI build failure
release / gui-build (linux, bash) (push) Successful in 1m11s
release / gui-build (windows, cmd) (push) Failing after 1m31s
release / gui-build (macos, bash) (push) Failing after 11m44s
2026-09-22 22:38:14 +00:00
LeoVasanko 734b7993a2 Add per-user MSI installer for Windows; strip Mark-of-the-Web in frozen ZIP builds
release / gui-build (linux, bash) (push) Successful in 1m12s
release / gui-build (macos, bash) (push) Successful in 1m14s
release / gui-build (windows, cmd) (push) Failing after 1m31s
Files extracted from a downloaded ZIP carry a Zone.Identifier stream and the
.NET Framework CLR refuses to load such assemblies, so pythonnet failed with
'Failed to resolve Python.Runtime.Loader.Initialize'. MSI-installed files have
no MOTW; the winmain strip fixes the ZIP distribution.
2026-09-22 22:28:16 +00:00
LeoVasanko b01e8a5c3d Exclude Python.Runtime.dll from UPX (packing corrupts CLR metadata)
release / gui-build (macos, bash) (push) Successful in 1m14s
release / gui-build (linux, bash) (push) Successful in 1m13s
release / gui-build (windows, cmd) (push) Successful in 1m21s
2026-09-22 22:00:57 +00:00
LeoVasanko b231f450bf Temporary Windows host probe workflow
release / gui-build (linux, bash) (push) Successful in 1m2s
release / gui-build (macos, bash) (push) Successful in 1m2s
release / gui-build (windows, cmd) (push) Successful in 1m42s
2026-09-22 04:59:53 +00:00
LeoVasanko 67489e05bc Use cmd shell for Windows steps; powershell is blocked by execution policy under SYSTEM
release / gui-build (windows, cmd) (push) Failing after 0s
release / gui-build (macos, bash) (push) Successful in 1m0s
release / gui-build (linux, bash) (push) Successful in 1m2s
2026-09-22 04:26:30 +00:00
LeoVasanko 3ae4ce9f9f Fix Windows checkout (powershell, not WSL bash) and install node per-job via uv
release / gui-build (windows) (push) Failing after 0s
release / gui-build (macos) (push) Successful in 1m12s
release / gui-build (linux) (push) Successful in 1m15s
2026-09-22 04:23:48 +00:00
LeoVasanko 99e757bc6f Checkout with plain git clone; JS actions need node on host runners
release / gui-build (windows) (push) Failing after 1s
release / gui-build (macos) (push) Successful in 1m8s
release / gui-build (linux) (push) Successful in 1m11s
2026-09-22 04:13:21 +00:00
LeoVasanko 5300fd0c9c Bundle PyQtWebEngine on macOS and ship the app as a DMG
release / gui-build (linux) (push) Successful in 1m13s
release / gui-build (macos) (push) Successful in 1m15s
release / gui-build (windows) (push) Failing after 1s
pywebview[qt] no longer depends on PyQtWebEngine, so fresh CI builds
produced a macOS app without its Chromium engine that crashed on launch.
Package MediaHive.app as a compressed DMG instead of zipping the raw
onedir folder; release.py accepts .dmg artifacts.
2026-09-22 04:07:30 +00:00
LeoVasanko ff10c86e84 Add Gitea Actions cross-platform release workflow
Build the GUI app on macos/windows/linux host runners on v* tag pushes.
All platform jobs converge on one Gitea release; release.py now reuses an
existing release, skips duplicate assets, and supports --no-dist so only
the linux job uploads the wheel/sdist.
2026-09-22 03:54:05 +00:00
LeoVasanko c11cbd4250 Fix console encoding crash in release script summary output 2026-09-10 21:33:43 +00:00
LeoVasanko 9779857dcd Update documentation for the v0.5.0 release 2026-09-10 21:30:24 +00:00
LeoVasanko 9ec4f877eb Resume tracking: per-episode positions, player-agnostic fallback, series continue point
- Fix merged playback-state dropping season/episode, which broke series
  resume restore entirely.
- Track watch progress per episode (episodes map keyed S..E.., with done
  markers on completion) while keeping one continue point per series as
  the last watched episode, used for spoiler protection and season
  selection. MPC-BE tracker resumes from each episode's own position.
- Assumed-playback fallback for any player and server-only mode: a launch
  starts a session and the guessed position (base + elapsed, capped at
  runtime) is written when frontend activity resumes; the real MPC-BE
  tracker overrides guesses via timestamps. Frontend reports input
  activity (throttled) via POST /api/activity.
- Ignore watches under 5 minutes in both trackers.
- Episode tiles show a small quadrant-circle watch indicator; near end
  counts as fully watched, no data shows nothing.
- Fix episode audio ownership: single audioOwnerKey shared by mouse hover
  and keyboard/gamepad focus, no longer clobbered by playback sync;
  idle fade can be re-armed by continued activity.
2026-09-10 20:56:03 +00:00
LeoVasanko 6a6a012efe Series detail: curtain poster browser for seasons
Replace the 3D ring jukebox with a flat curtain browser: selected season
faces the screen as top of the left stack, side cards rotate about their
outer edge and recede into the screen, each side evenly spaced with pitch
adapting to its card count so cards never leave the stage.

- Mount episode videos only after the season switch settles, then
  preload=auto + autoplay (avoids mid-animation media setup jank)
- Start videos immediately (no stagger) once an episode is pointed at;
  keep staggered starts while navigating the season selector
- Keyboard/gamepad entry into the season row lands on the selected
  season via data-nav-entry-col instead of the x-closest card
- Black episode tiles; hidden (ahead-of-cursor) tiles get a sheen and
  vignette veil that cross-fades on reveal/conceal
- Nudge season info panel 8px left for balanced gaps
2026-09-10 16:47:33 +00:00
LeoVasanko 2c28ab1f25 Series detail: one season at a time with 3D jukebox season selector
Render only the selected season's episodes; switch via a perspective-arc
poster carousel with floating season info, deferred video loading with
still-image fade-in, and spoiler fade for episodes ahead of the cursor.
2026-09-10 03:34:52 +00:00
LeoVasanko e6eadb2ecd Working dev reload on Windows; fast quiet scanner shutdown
uvicorn's reloader is unusable on Windows for this app: it restarts the
server child via CTRL_C_EVENT, which is never delivered to a plain spawn
child (no console process group of its own), so the child kept running
old code and the reloader blocked in process.join() after the first
reload — the scanner included.  And when a child does restart, uvicorn
hands it parent-bound sockets that ProactorEventLoop cannot register
with IOCP (WinError 87 on accept), while the selector loop would break
asyncio subprocesses (ffmpeg/ffprobe showreel generation).

In dev mode on Windows, mediahive now runs a small supervisor instead:
it watches the package with watchfiles and respawns a fresh child
process (which binds its own sockets) on every change, and terminates
the child on Ctrl-C.  POSIX keeps uvicorn's native reload.

Shutdown: RootScanner.stop() no longer does a final best-effort state
save — those to_thread writes ran on the asyncio default executor, whose
non-daemon threads blocked interpreter exit, so log lines appeared after
the shell prompt returned.  All scanner state is already persisted after
each completed scan and when the showreel queue drains.  Shutdown-time
log lines (scan cancelled, showreel worker stopping) move to debug.
2026-09-09 15:15:27 +00:00
LeoVasanko ff6b195973 Fix rescan loop: short-circuited registration dropped files from state
The directory worker registered video files with
'node["changed"] = node["changed"] or await _register_candidate(...)',
so once one file in a directory was found changed, the rest were never
registered: they fell out of known_paths, were pruned from the persisted
seen-mtimes at finalize, and were rediscovered as new on every scan.
That rotating ~10-item subset was reprocessed (metadata, TMDb lookups,
logs) every 30 seconds forever.

Also fix the self-sustaining reprocess loop for items that produce no
index entries (no playable file, no parseable episodes, non-media):
they never appear in the index, so the DB-aware mtime gating alone could
never skip them.  They are now recorded in a persisted empty_mtimes map
(scan-state.json) and skipped while their mtime is unchanged.

Episode/playable/blu-ray finder caches are now cleared at the start of
each scan: an empty result cached before a download finished (or during
a transient network-mount error) previously stuck for the process
lifetime and reported "no episodes found" for series that have episodes.

Logging: a no-change scan now emits a single line with item count and
duration; per-item "up to date" chatter moved to debug; change scans
report the new/changed count plus per-item reason lines (stored vs
current mtime, index/empty-marker membership), finalize prune/commit
traces, and scan-state load/save confirmations.

Add AGENTS.md noting the project's Python 3.14 baseline (PEP 758
unparenthesized except) so tooling does not flag it as a syntax error.
2026-09-09 14:40:32 +00:00
LeoVasanko 36e2fdd5ff Preserve browse scroll positions exactly when returning from details
Skip ensure-visible and synced-row animations on focus restore, and
snapshot/restore panel scrollTop plus synced-row scrollLeft around
detail navigation so the browse page comes back pixel-identical.
2026-09-09 04:51:10 +00:00
LeoVasanko 8f462b9e1d Track backoff state only for large empty subtrees
dir_state now records only subtrees with >= _MIN_TRACKED_DIRS (8)
directories that yielded no items of interest; hot or tiny trees fall
back to default every-pass scheduling with no bookkeeping. Per-directory
classification already works at any depth, so e.g. torrents/Music is
pruned at its own root while torrents/Movies stays hot; backed-off
subtrees are still re-examined when their (exponentially growing, 1h
cap) timer expires.
2026-09-09 04:27:15 +00:00
LeoVasanko 838db5b55c Make rescans robust: DB-aware mtime gating, wipe-proof discovery
- RootScanner takes indexed_paths (store torrent keys); a candidate is
  skipped only when its mtime is unchanged AND it is present in the index,
  so an emptied/partial index self-heals on the next pass (non-indexable
  OTHER candidates still skip on mtime alone)
- discovery: a directory that cannot be listed (transient SMB failure)
  now carries its previously known paths into the Sync set instead of
  dropping them, and an unreadable media root fails the whole scan —
  previously both produced a partial/empty known_paths that made
  sync_torrent_paths delete items that still exist on disk
- worker: broad per-directory exception guard (carry known paths, stay
  hot, keep other workers running); node completion hardened against
  double-decrement; backoff state not updated from cancelled walks
2026-09-09 04:21:17 +00:00
LeoVasanko e5300eaac0 Faster GUI exit, graceful second-instance startup, pretty crash window
- scanner.stop: await cancelled tasks concurrently under one 2s budget
  and persist scan/reel/probe state concurrently (was sequential)
- supervisor.shutdown: stop root contexts concurrently (per-root scanner
  waits and network-mount snapshot flushes no longer add up)
- winmain: retry log-file rotation while a previous instance still holds
  mediahive.log (Windows denies rename of an open file — the old instance
  dying slowly made new instances crash with a PermissionError); after 2s
  show a 'waiting for previous instance' window, retry up to 15s, then
  fall back to a per-pid log file
- winmain: gui_main() wraps winmain() and renders unhandled exceptions as
  a TraceRite HTML page in a pywebview window instead of PyInstaller's
  plain-text fatal-error dialog; __main__ GUI mode uses it too
2026-09-09 03:46:27 +00:00
LeoVasanko d1f1b9ecb8 Parallel adaptive discovery walk; fix PyInstaller tracerite data files
- scanner: replace serial DFS with 16-worker priority work-queue, one
  scandir round trip per directory (DirEntry mtimes), per-subtree
  exponential backoff (persisted in scan-state.json v2), leaf candidates
  gated on max(dir, newest video file) mtime so growing downloads and
  lazy NTFS dir mtimes are handled
- spec: collect_data_files('tracerite') so style.css/script.js ship
- guibuild: tolerate non-UTF-8 console when printing status glyphs
- ruff format pass on indexer.py/index_store.py (hook requirement)
2026-09-09 03:37:31 +00:00
LeoVasanko 26af7c633b Remove rtorrent scripting from the repository. 2026-09-09 02:14:11 +00:00
LeoVasanko c072f15cb5 Make helper scripts directly executable with uv run shebang
guibuild.py and release.py use the project environment; rtorrent-manager.py
is standalone and declares its own dependency via uv script metadata.
2026-09-09 02:08:49 +00:00
LeoVasanko 23030cd1c4 Remove wheel build hook, following upstream fastapi-vue convention
The wheel hook was a local addition (76cd022) that rebuilt the frontend
on every wheel build. Only the sdist build should invoke node; wheel
builds (including wheel-from-sdist) package the prebuilt frontend-build.
Restores scripts/fastapi-vue/buildhook.py to its pristine upstream state.
2026-09-09 02:02:25 +00:00
LeoVasanko 2a39e1f0ea Fix scanner rescan bugs and wasted work
Bugs fixed:
- Partial rescans no longer replace whole index entries: Upsert events now
  carry the scanned torrent paths and IndexStore merges partial rebuilds,
  so touching one season no longer drops the others from listings.
- Deleted torrents are now detected: discovery reports the full candidate
  set via a new Sync event after each completed scan, and the store prunes
  file entries/episodes/seasons/items whose torrents vanished.
- Seen mtimes are committed only after a scan completes successfully, so
  cancelled/failed scans retry their items.
- Showreel worker no longer rebroadcasts stale whole items; it sends narrow
  MovieShowreel/EpisodeReel events that update only reel fields.
- Permanently failing reel generations (e.g. DoVi/libplacebo on GPU-less
  machines) and short-video re-queueing are no longer retried every scan:
  reel outcomes persist in reel-state.json with exponential backoff.

Optimizations:
- Persist scan state (scan-state.json) and ffmpeg probe results
  (probe-cache.json, keyed by mtime+size, failures included) under
  .mediahive/, written only when changed. A warm restart over an unchanged
  library drops from ~57 s + 467 queued reel tasks to ~0.2 s with an empty
  queue (measured on a 163-item root).
- Bounded parallelism (semaphores + gather) for cast-profile downloads,
  TMDb title lookups, and season-detail fetches.
- Incremental TMDb-id index bookkeeping instead of rebuilding per upsert;
  removed dead trigger_scan.

See docs/scanning-review.md for the full findings/fixes/measurements report.
2026-09-04 00:00:53 +00:00
LeoVasanko d3addadf14 Work around Starlette StreamingResponse bug with not aclosing the async generator by implementing a custom StreamingFileResponse class. 2026-09-03 13:20:18 +00:00
LeoVasanko 0a4d54c1b7 Update GUI logging to fastapi-vue too. 2026-09-03 12:30:53 +00:00
LeoVasanko c2776d2e2d fastapi-vue-setup 1.4.1 upgrade, replaces our own access logging and more. 2026-09-03 12:19:50 +00:00
LeoVasanko e5bc736ffa Fix episode preview playback and add staggered stopping
- Observe the video element itself instead of el.closest('.episode-tile'):
  ref callbacks can fire before ancestors are attached, so no tile was
  ever observed and the visibility allowlist blocked all playback.
- Use a blocklist for off-screen culling (play until reported off-screen)
  so observer lag/failure degrades to the old always-on behavior.
- Default to the first season playing on page view again.
- Keep preload="auto" so tiles show a first-frame preview picture;
  playback gating already prevents GPU use from inactive videos.
- Stop videos staggered (same 500ms step as startup) on idle stop,
  season switch and scroll culling; same for movie page showreels.
2026-09-03 03:19:35 +00:00
LeoVasanko 258fc79753 Reduce preview video GPU load: idle stop, browse-gated seasons, viewport culling
- Add useIdlePreviewPlayback composable: stops preview videos after 30s
  without input and restarts them staggered on activity; also stops
  while the tab is hidden.
- SeriesFullView: no episode videos play until a season is browsed
  (keyboard/gamepad focus or mouse hover); only near-viewport tiles of
  the active season play (IntersectionObserver); inactive seasons no
  longer preload video data; sync is incremental so playing tiles are
  not rewound on scroll.
- MediaDetail: header showreel videos use cancellable stagger timers,
  pause when scrolled out of view, and honor the idle stop.
- Images: lazy/async decoding on series page posters; content-visibility:
  auto on media cards to skip rendering off-screen row items.
2026-09-03 02:23:22 +00:00
LeoVasanko 7a60ef5384 Implement Ctrl-R/F5 reload handler with intent to allow reloads in GUI build. 2026-09-02 19:42:17 +00:00
LeoVasanko 22454f2d29 Update README 2026-06-03 00:41:52 +00:00
LeoVasanko 589c789d4d Fix pywebview qt dep on non-Windows platforms. 2026-06-03 00:33:37 +00:00
LeoVasanko 5f2454d8e8 Simpler and cross-platform added_on timestamp determination. 2026-06-03 00:01:06 +00:00
LeoVasanko 2fa15132fb Make default script run with GUI if possible. Add --gui to force GUI. 2026-06-02 23:11:08 +00:00
LeoVasanko 76cd0224de Attempt build fix to allow running from git. 2026-06-02 22:45:09 +00:00
LeoVasanko b0d13a67a0 Fix config saving by not trying to save None default values. 2026-06-02 22:35:44 +00:00
LeoVasanko 568605b09a Add missing file from previous commit 2026-06-02 22:22:49 +00:00
LeoVasanko b09118c8cb Clean up series nested context menus and implement keyboard/gamepad navigation for these functions. 2026-06-02 22:17:47 +00:00
LeoVasanko f79c044250 Tighten media cache headers 2026-05-31 02:45:16 +00:00
LeoVasanko a4b0cd916a Use image logo for HDR10+ badge 2026-05-31 02:31:35 +00:00
LeoVasanko e1a961d21f Detect and badge HDR10+ (SMPTE ST 2094-40) via ffmpeg showinfo 2026-05-31 02:05:10 +00:00
LeoVasanko 1a6a3f0cf2 Don't show collection where the current movie is the only item we have 2026-05-31 01:51:15 +00:00
LeoVasanko 626fb9a0ae Unify playback-state read/write via /api/meta/playback-state 2026-05-31 01:43:56 +00:00
LeoVasanko b6742f0f27 refactor: unify roots updates over single websocket 2026-05-31 00:26:51 +00:00
LeoVasanko 6d0e6d41a7 feat(media): use collection-based recommendations and refine poster display 2026-05-31 00:12:54 +00:00
LeoVasanko 932c5af1ff fix(frontend): scope synced row scrolling by group 2026-05-30 23:54:11 +00:00
LeoVasanko eb49eb713e Hide app scrollbars 2026-05-30 22:59:14 +00:00
LeoVasanko d7eea51334 Align custom access and websocket logging with paskia 2026-05-30 22:46:40 +00:00
LeoVasanko 01f424dbfb Force reel videos to remount on movie change 2026-05-30 22:33:23 +00:00
LeoVasanko df9025760d Add custom FastAPI access logging middleware 2026-05-30 22:25:21 +00:00
LeoVasanko 09e51acd14 Disable spellcheck and autocorrect in search input 2026-05-30 19:18:09 +00:00
LeoVasanko a029eabded Update README 2026-05-30 05:44:43 +00:00
LeoVasanko 4926b72167 Add adjacent detail navigation for keyboard and gamepad 2026-05-30 05:35:56 +00:00
LeoVasanko cd336d0bdb Add fixed-interval hold repeat for gamepad volume 2026-05-30 05:30:15 +00:00
LeoVasanko c75fb30921 Refine scan activity UI and initial-index visibility 2026-05-30 05:18:25 +00:00
LeoVasanko 28c50cab0b Enable SPA on backend (fix 404 accessing other than root) 2026-05-30 05:03:24 +00:00
LeoVasanko 14b76520a6 Stop eager reel loading in cards 2026-05-30 05:02:36 +00:00
LeoVasanko c9d2bcfdcc Fix frontend type mismatches 2026-05-30 04:52:59 +00:00
LeoVasanko 7a0247d78f Refine hover reel audio fades and idle behavior 2026-05-30 04:38:33 +00:00
LeoVasanko bb4bd3723f Improved serie episode preview video handling 2026-05-30 04:28:34 +00:00
LeoVasanko 7675940029 Implement JS-driven season poster sizing from episode grid remainder 2026-05-30 04:19:14 +00:00
LeoVasanko 613b516785 Fix series preview videos to render paused frames and stagger active season 2026-05-30 04:13:05 +00:00
LeoVasanko 2f72d1e7e2 Fix series full view vertical navigation to follow episode rows 2026-05-30 04:05:20 +00:00
LeoVasanko b51a7ea73d Separate scrolling for browse and details page even though they are laid out side by side on the layout (with one hidden out of screen). 2026-05-30 04:00:53 +00:00
LeoVasanko fe5be2087d Refine movie similar strip and desktop nav flow 2026-05-30 03:50:28 +00:00
LeoVasanko 46eecd8dbf Add related movies on Series page. Uses heuristics to show serie movies only. 2026-05-30 03:36:35 +00:00
LeoVasanko c2ee25ba3d Fix synced row deadzone behavior and isolate cast scroll metrics 2026-05-30 02:57:34 +00:00
LeoVasanko 649992a10a Align frontend with new cast and people wire format 2026-05-30 02:40:36 +00:00
LeoVasanko ada61e1400 Remove long hex root ids and use only friendly names 2026-05-30 02:28:58 +00:00
LeoVasanko 3551808351 Fix root-relative playable path normalization 2026-05-30 02:19:33 +00:00
LeoVasanko 0ef6896181 Refactor API to flat root routes and WS/meta structure 2026-05-30 02:09:24 +00:00
LeoVasanko 72963aeb7a Fix loading of index that was bad because of slop code. 2026-05-30 01:43:46 +00:00
LeoVasanko 352835a664 Add logical root asset URLs and harden media path serving 2026-05-30 01:36:47 +00:00
LeoVasanko 900fcea638 refactor index schema and metadata format and directory tree 2026-05-30 01:05:27 +00:00