Wire up playback reporting, fix duration flash, hide video from audio mini player
Playback reporting (position sync / resume-on-another-device): - player_configure_jellyfin now builds a PlaybackReporter sharing the player controller's Arc, so Start/Progress/Stopped actually reach Jellyfin on every auth path (login/restore/reauth); previously they never did. - The PlaybackReporterWrapper now shares the same Arc the controller and MPV progress loop report through, instead of a dead parallel Option. - Android position callbacks now emit throttled progress reports (30s/item), mirroring the MPV backend. Duration flash on pause: - resolveDuration() prefers the live store duration for the already-loaded track over the runTimeTicks estimate, so pausing no longer clobbers the slider's max to 0 when runTimeTicks is missing. Video leaking into audio mini player: - isVideoItem() also checks the backend PlayerMediaItem mediaType discriminator, so a video started via player_play_item (no Jellyfin `type`, mediaType "video") no longer surfaces in the audio mini player. Middle-truncation of long media names: - New truncateMiddle util applied to track/episode/card/mini-player titles so distinguishing tails (episode numbers, suffixes) stay visible. Adds regression tests for the duration and mini-player fixes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -250,6 +250,29 @@ export const mergedVolume = derived(
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Whether an item is video content and therefore must NOT appear in the audio
|
||||
* mini player. Checks the Jellyfin item type (Movie/Episode/live TV) AND the
|
||||
* backend queue item's `mediaType` discriminator.
|
||||
*
|
||||
* The `mediaType` check is essential: a video started via `player_play_item`
|
||||
* (every Movie/Episode goes through VideoPlayer) is pushed onto the backend
|
||||
* queue as a PlayerMediaItem with NO `type` field but `mediaType: "video"`.
|
||||
* Without this, that queue item's absent `type` slips past the Movie/Episode
|
||||
* check and the video surfaces in the audio mini player after leaving /player.
|
||||
*/
|
||||
function isVideoItem(item: MediaItem | null): boolean {
|
||||
if (!item) return false;
|
||||
const type = item.type;
|
||||
if (type === "Movie" || type === "Episode" || type === "TvChannel") {
|
||||
return true;
|
||||
}
|
||||
// Backend PlayerMediaItem carries a lowercase mediaType discriminator that is
|
||||
// present even when the Jellyfin `type` is absent (video-only play requests).
|
||||
const mediaType = (item as { mediaType?: string }).mediaType;
|
||||
return mediaType === "video";
|
||||
}
|
||||
|
||||
/**
|
||||
* Should show audio miniplayer - state machine gated
|
||||
* Only true when:
|
||||
@@ -268,8 +291,8 @@ export const shouldShowAudioMiniPlayer = derived(
|
||||
|
||||
// Determine media type from the player state, falling back to the queue
|
||||
// item (the player store can momentarily lack media during transitions).
|
||||
const mediaType = $media?.type ?? $queueItem?.type;
|
||||
if (mediaType === "Movie" || mediaType === "Episode") {
|
||||
const item = $media ?? $queueItem;
|
||||
if (isVideoItem(item)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user