- 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.
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.
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.
Backend:
- Add mediahive/players.py with cross-platform player detection:
- Windows: MPC-BE, MPC-HC, VLC, PotPlayer, mpv (registry + known paths)
- macOS: IINA, VLC (/Applications bundle scanning)
- Linux: VLC, SMPlayer (PATH via which)
- Add GET /api/players endpoint returning detected players
- Extend POST /api/roots/{id}/play to accept player_id and player_custom_cmd
- Make MPC-BE web UI port configurable via ?port= query param
Frontend:
- Add player selection to settings (localStorage, per-client)
- Two-column layout: player list (9.5em) + contextual options panel
- Contextual options: Custom Command input, MPC Web UI Port input
- Empty MPC port disables all Web UI polling/connection attempts
- MPC-BE polling only runs when MPC family player is selected and port is set
- useMediaWebSocket: shallowRef mediaIndex/tasks/loading/error/connected
to eliminate Proxy overhead on the entire library data structure.
- useMediaWebSocket: replace per-task setTimeout leak with single 3s
sweep interval for completed task cleanup.
- App.vue: remove {deep:true} watcher on mediaIndex; shallow ref change
is sufficient to trigger search worker sync.
- useGamepadNavigation: rAF only when gamepads are connected; idle
fallback to 500ms setTimeout to stop permanent 60fps CPU drain.
- useKeyboardNavigation: deduplicate synced scroll rAF requests to
prevent overlapping animation frames.
- MediaDetail/SeriesFullView/CollageHero: pause, clear src, and load()
video elements on unmount and before ref replacement to release
decoder/memory resources.
- MediaDetail/SeriesFullView: clear all volume fade intervals on
unmount to stop interval timer leaks.
When clicking a search result, showDetail navigates to the detail page.
The route.fullPath watcher then clears searchQuery, which propagates to
Header's localSearch watcher, which emits @search='', which calls
updateSearchQuery(''). This in turn called clearSearch(), which did a
router.replace() to the category page because it didn't check whether
the current route actually had a search query.
The fix: in clearSearch(), if there's no search query in the current
URL, just clear reactive state and return without navigating. This
breaks the circular flow that was overwriting the detail-page
navigation.
Remove the separate Play and Info buttons from the CollageHero featured
item. All collage items (including the featured one) are now uniform
<a href> links that navigate to the detail page.
- CollageHero: remove hasResumePosition prop, play/info emits, and
associated play button logic
- App.vue: remove @play and @info bindings from CollageHero
- Delete unused HeroSection.vue component and its styles
When typing in the search bar on a detail page, navigating to search
results would trigger restoreFocusForPage(), which focused the 'last
viewed' card and interrupted typing. Skip focus restoration whenever
the search input is currently the active element.
- Add root_id to Torrent type for per-torrent root tracking
- Merge movies: combine all torrents, keep best metadata, sort by quality
- Merge series: combine seasons/episodes/torrents across roots
- Torrents are quality-sorted (resolution, HDR, DV, Atmos) across all roots
- App.vue play/openFolder use torrent-specific root_id when available
- Remove legacy endpoints: /api/change-folder, /api/index, /api/scan, /api/status,
/api/playback/resume-positions
- Remove legacy global scanner module-level API from hivescan/scanner.py
- Defer all filesystem validation to background task in server lifespan (macOS-safe)
- CLI and winmain pass raw paths via MEDIAHIVE_ROOTS; no pre-startup validation
- Enforce POSIX paths everywhere (as_posix(), no backslash leakage)
- Remove MEDIAHIVE_PATH and MEDIAHIVE_DEFER_INITIAL_ROOT env vars
- Update frontend api.ts to use per-root resume positions
- Update docs/API.md and docs/multi-index-plan.md
- Fix Python 2 style except clauses in hivescan/utils.py and scanning.py