chore(traceability): shift this branch's ids clear of master's
Master allocated DR-224 and UT-211 while this branch was in flight — the third collision on this work. Everything here moves up by one: DR-224..236 become DR-225..237, UT-211..213 become UT-212..214. UR-079, UR-080 and IR-033 were still free and are unchanged. Mechanical, and matched on each row's own text rather than on its number, so a row cannot be shifted twice or the wrong one caught. Master's DR-224 (the background-audio toggle) and UT-211 are untouched.
This commit is contained in:
+16
-16
@@ -39,7 +39,7 @@ async playerPlayItem(item: PlayItemRequest) : Promise<PlayerStatus> {
|
||||
* playing there is nothing to pause, and an error would make the frontend
|
||||
* handle a case that is not a failure.
|
||||
*
|
||||
* TRACES: UR-040, UR-041 | DR-224 | UT-211
|
||||
* TRACES: UR-040, UR-041 | DR-225 | UT-212
|
||||
*/
|
||||
async playerBackgroundAction(backgroundAudioArmed: boolean, inPictureInPicture: boolean) : Promise<BackgroundAction> {
|
||||
return await TAURI_INVOKE("player_background_action", { backgroundAudioArmed, inPictureInPicture });
|
||||
@@ -273,7 +273,7 @@ async playerGetStreamingQualities() : Promise<([StreamingQuality, string, string
|
||||
* belongs to Settings, and `player_set_video_settings` is the one that writes
|
||||
* to the database.
|
||||
*
|
||||
* TRACES: UR-074, UR-079 | DR-162, DR-225
|
||||
* TRACES: UR-074, UR-079 | DR-162, DR-226
|
||||
*/
|
||||
async playerSetStreamQuality(repositoryHandle: string, quality: StreamingQuality, useHtml5: boolean, currentPosition: number | null, mediaSourceId: string | null, audioStreamIndex: number | null) : Promise<StreamQualityResponse> {
|
||||
return await TAURI_INVOKE("player_set_stream_quality", { repositoryHandle, quality, useHtml5, currentPosition, mediaSourceId, audioStreamIndex });
|
||||
@@ -1048,7 +1048,7 @@ async mediaLocalUrl(path: string) : Promise<string> {
|
||||
* to consume rather than two, and so no caller has to infer a transport from a
|
||||
* loopback URL.
|
||||
*
|
||||
* TRACES: UR-071, UR-079 | DR-224
|
||||
* TRACES: UR-071, UR-079 | DR-225
|
||||
*/
|
||||
async mediaLocalSelection(path: string) : Promise<StreamSelection> {
|
||||
return await TAURI_INVOKE("media_local_selection", { path });
|
||||
@@ -1651,7 +1651,7 @@ async repositoryGetVideoStreamUrl(handle: string, itemId: string, mediaSourceId:
|
||||
* position on an HLS playlist is copied onto every segment URI and the server
|
||||
* rejects each with `400` (DR-181). Callers resume by seeking after load.
|
||||
*
|
||||
* TRACES: UR-070, UR-079 | DR-224, DR-226, DR-227 | UT-212
|
||||
* TRACES: UR-070, UR-079 | DR-225, DR-227, DR-228 | UT-213
|
||||
*/
|
||||
async repositoryGetStreamSelection(handle: string, itemId: string, mediaSourceId: string | null, audioStreamIndex: number | null) : Promise<StreamSelection> {
|
||||
return await TAURI_INVOKE("repository_get_stream_selection", { handle, itemId, mediaSourceId, audioStreamIndex });
|
||||
@@ -2374,9 +2374,9 @@ export type LiveStreamInfo = { streamUrl: string; playSessionId: string | null;
|
||||
* A live channel is always an HLS transcode — the server has to repackage a
|
||||
* broadcast mux into something a browser can play, and there is no static
|
||||
* file to direct-play. Saying so here means the player page never has to
|
||||
* work it out from the URL, which is the whole of DR-224.
|
||||
* work it out from the URL, which is the whole of DR-225.
|
||||
*
|
||||
* TRACES: UR-079 | DR-224
|
||||
* TRACES: UR-079 | DR-225
|
||||
*/
|
||||
transport: Transport }
|
||||
/**
|
||||
@@ -2626,7 +2626,7 @@ needsTranscoding: boolean;
|
||||
* caller falls back to `needs_transcoding` — every transcode this app
|
||||
* requests is HLS (DR-140), so that fallback is exact rather than a guess.
|
||||
*
|
||||
* TRACES: UR-003, UR-004, UR-079 | DR-224, DR-229
|
||||
* TRACES: UR-003, UR-004, UR-079 | DR-225, DR-230
|
||||
*/
|
||||
transport?: Transport | null;
|
||||
/**
|
||||
@@ -2750,7 +2750,7 @@ export type PlaybackInfo = { mediaSourceId: string; playSessionId: string; strea
|
||||
* lets the UI say "this is not costing the server anything" without inferring
|
||||
* it from a URL shape.
|
||||
*
|
||||
* TRACES: UR-079 | DR-227
|
||||
* TRACES: UR-079 | DR-228
|
||||
*/
|
||||
export type PlaybackKind =
|
||||
/**
|
||||
@@ -2875,7 +2875,7 @@ needsTranscoding?: boolean;
|
||||
* caller falls back to `needs_transcoding` — every transcode this app
|
||||
* requests is HLS (DR-140), so that fallback is exact rather than a guess.
|
||||
*
|
||||
* TRACES: UR-003, UR-004, UR-079 | DR-224, DR-229
|
||||
* TRACES: UR-003, UR-004, UR-079 | DR-225, DR-230
|
||||
*/
|
||||
transport?: Transport | null;
|
||||
/**
|
||||
@@ -3168,7 +3168,7 @@ skipped: number }
|
||||
* them indistinguishable from Original. `exceeds_source` is what lets the
|
||||
* frontend render that honestly without knowing anything about bitrates.
|
||||
*
|
||||
* TRACES: UR-070, UR-079 | DR-226, DR-121
|
||||
* TRACES: UR-070, UR-079 | DR-227, DR-121
|
||||
*/
|
||||
export type QualityOption = { quality: StreamingQuality;
|
||||
/**
|
||||
@@ -3207,7 +3207,7 @@ export type RemoteSessionStatus = { position: number; duration: number | null; i
|
||||
* there is no *chosen* rendition in that case, only the file itself, and
|
||||
* reporting the ceiling that happened to be set would misdescribe it.
|
||||
*
|
||||
* TRACES: UR-079 | DR-224, DR-225
|
||||
* TRACES: UR-079 | DR-225, DR-226
|
||||
*/
|
||||
export type Rendition = {
|
||||
/**
|
||||
@@ -3353,7 +3353,7 @@ export type StreamQualityResponse =
|
||||
* that would have used the requested value was never reached. The stream
|
||||
* changed and the menu did not.
|
||||
*
|
||||
* TRACES: UR-074, UR-079 | DR-225, DR-226
|
||||
* TRACES: UR-074, UR-079 | DR-226, DR-227
|
||||
*/
|
||||
{ strategy: "native"; selection: StreamSelection; position: number } |
|
||||
/**
|
||||
@@ -3366,7 +3366,7 @@ export type StreamQualityResponse =
|
||||
*
|
||||
* Replaces the bare `String` URL that `get_video_stream_url` used to return.
|
||||
*
|
||||
* TRACES: UR-079 | DR-224, DR-226, DR-227
|
||||
* TRACES: UR-079 | DR-225, DR-227, DR-228
|
||||
*/
|
||||
export type StreamSelection = {
|
||||
/**
|
||||
@@ -3386,7 +3386,7 @@ playbackKind: PlaybackKind;
|
||||
*/
|
||||
rendition: Rendition | null;
|
||||
/**
|
||||
* What this media source can offer, for the quality picker (DR-226).
|
||||
* What this media source can offer, for the quality picker (DR-227).
|
||||
*/
|
||||
available: QualityOption[];
|
||||
/**
|
||||
@@ -3407,7 +3407,7 @@ playSessionId: string | null;
|
||||
* queue's long-standing `needs_transcoding` flag and the seek strategy both
|
||||
* read this, so there is one answer rather than three.
|
||||
*
|
||||
* TRACES: UR-079 | DR-224, DR-227
|
||||
* TRACES: UR-079 | DR-225, DR-228
|
||||
*/
|
||||
needsTranscoding: boolean }
|
||||
/**
|
||||
@@ -3503,7 +3503,7 @@ export type ThumbnailCacheStats = { totalSizeBytes: number; itemCount: number; l
|
||||
* Tagged (`{"type":"hls"}`) rather than a bare string so the frontend matches a
|
||||
* discriminant instead of comparing text.
|
||||
*
|
||||
* TRACES: UR-079 | DR-224
|
||||
* TRACES: UR-079 | DR-225
|
||||
*/
|
||||
export type Transport =
|
||||
/**
|
||||
|
||||
@@ -260,7 +260,7 @@ export class RepositoryClient {
|
||||
* start position on an HLS playlist makes Jellyfin reject every segment behind
|
||||
* it with `400` (DR-181). Resume by seeking once loaded.
|
||||
*
|
||||
* TRACES: UR-070, UR-079 | DR-224, DR-226, DR-227 | UT-212
|
||||
* TRACES: UR-070, UR-079 | DR-225, DR-227, DR-228 | UT-213
|
||||
*/
|
||||
async getStreamSelection(
|
||||
itemId: string,
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
* which forced this component to re-derive the transport by searching for
|
||||
* `.m3u8`.
|
||||
*
|
||||
* TRACES: UR-079 | DR-224, DR-226
|
||||
* TRACES: UR-079 | DR-225, DR-227
|
||||
*/
|
||||
selection: StreamSelection;
|
||||
mediaSourceId?: string; // Media source ID for subtitle URLs
|
||||
@@ -196,7 +196,7 @@
|
||||
// The selection currently loaded. Starts from the prop and is replaced
|
||||
// wholesale by a reload (quality change, audio-track switch, transcoded seek)
|
||||
// so transport and URL can never disagree.
|
||||
// TRACES: UR-079 | DR-224
|
||||
// TRACES: UR-079 | DR-225
|
||||
let currentSelection = $state<StreamSelection>(untrack(() => selection));
|
||||
const currentStreamUrl = $derived(currentSelection.url);
|
||||
/**
|
||||
@@ -281,11 +281,11 @@
|
||||
* Used by the paths that swap the stream without re-negotiating — the
|
||||
* background-audio handoff and its return. Each states the transport it is
|
||||
* moving to rather than letting it be inferred, which is the whole point of
|
||||
* DR-224: the audio handoff really is a progressive mp3, and the rebuilt
|
||||
* DR-225: the audio handoff really is a progressive mp3, and the rebuilt
|
||||
* video stream really is an HLS transcode, and neither is knowable from the
|
||||
* URL text.
|
||||
*
|
||||
* TRACES: UR-040, UR-079 | DR-224
|
||||
* TRACES: UR-040, UR-079 | DR-225
|
||||
*/
|
||||
function selectionAt(url: string, transport: StreamSelection["transport"]): StreamSelection {
|
||||
// A re-opened stream is a new transcode job; the old session id is stale.
|
||||
@@ -326,14 +326,14 @@
|
||||
|
||||
/**
|
||||
* The rungs to offer for the stream that is playing, straight from the
|
||||
* backend (DR-226). Rungs whose ceiling is at or above the source bitrate are
|
||||
* backend (DR-227). Rungs whose ceiling is at or above the source bitrate are
|
||||
* dropped: they produce the same bytes as Original, so listing five of them is
|
||||
* five ways to spell one choice. Rust decides which those are — this only
|
||||
* decides not to draw them.
|
||||
*
|
||||
* `Original` is always kept; it is the source, never redundant with it.
|
||||
*
|
||||
* TRACES: UR-070, UR-079 | DR-226, DR-121
|
||||
* TRACES: UR-070, UR-079 | DR-227, DR-121
|
||||
*/
|
||||
const qualityOptions = $derived(
|
||||
currentSelection.available.filter((o) => !o.exceedsSource || o.quality === "original"),
|
||||
@@ -654,7 +654,7 @@
|
||||
// teardown for an unchanged stream and left the element showing nothing
|
||||
// until a seek forced another cycle.
|
||||
//
|
||||
// TRACES: UR-079 | DR-224 | UT-213
|
||||
// TRACES: UR-079 | DR-225 | UT-214
|
||||
const loader = loaderForTransport(transportKind, {
|
||||
hlsJsSupported: Hls.isSupported(),
|
||||
nativeHlsSupported: !!videoElement.canPlayType("application/vnd.apple.mpegurl"),
|
||||
@@ -892,7 +892,7 @@
|
||||
});
|
||||
});
|
||||
|
||||
// The quality *ladder* now arrives with the stream selection (DR-226), so all
|
||||
// The quality *ladder* now arrives with the stream selection (DR-227), so all
|
||||
// this still needs is the device default, for the case where the stream is a
|
||||
// direct play and has no rendition of its own.
|
||||
//
|
||||
@@ -901,7 +901,7 @@
|
||||
// HTML5 mode and breaks native seeking, and nothing about playback waits on
|
||||
// this value.
|
||||
//
|
||||
// TRACES: UR-074, UR-079 | DR-162, DR-226
|
||||
// TRACES: UR-074, UR-079 | DR-162, DR-227
|
||||
onMount(() => {
|
||||
commands
|
||||
.playerGetVideoSettings()
|
||||
@@ -974,7 +974,7 @@
|
||||
videoCodec: needsTranscoding ? "hevc" : "h264",
|
||||
needsTranscoding: needsTranscoding,
|
||||
// Carry the negotiated transport onto the queue item so a later seek
|
||||
// reads it instead of falling back. TRACES: UR-079 | DR-229
|
||||
// reads it instead of falling back. TRACES: UR-079 | DR-230
|
||||
transport: currentSelection.transport,
|
||||
// Order matters: player_set_subtitle_track(n) is a position in this
|
||||
// array. Previously this array was built and then dropped, so
|
||||
@@ -1846,7 +1846,7 @@
|
||||
// whether the item has a picture to lose, which is Rust's to know. This used
|
||||
// to be decided implicitly by Kotlin gating the event on the toggle, which
|
||||
// is why the native path -- whose media service keeps playing regardless --
|
||||
// ignored the toggle entirely (DR-224).
|
||||
// ignored the toggle entirely (DR-225).
|
||||
let action: BackgroundAction;
|
||||
try {
|
||||
action = await commands.playerBackgroundAction(
|
||||
@@ -2034,7 +2034,7 @@
|
||||
id: media.id,
|
||||
videoCodec: needsTranscoding ? "hevc" : "h264",
|
||||
needsTranscoding,
|
||||
// TRACES: UR-079 | DR-229
|
||||
// TRACES: UR-079 | DR-230
|
||||
transport: targetSelection.transport,
|
||||
subtitles: nativeSubtitleTracks(sentSubtitleTracks),
|
||||
});
|
||||
@@ -2395,12 +2395,12 @@
|
||||
* only supplies the position to resume at.
|
||||
*
|
||||
* The change applies to this playback alone; the durable Settings default is
|
||||
* untouched (DR-225). Nothing is optimistically assigned here: what the picker
|
||||
* untouched (DR-226). Nothing is optimistically assigned here: what the picker
|
||||
* shows comes from the selection the backend hands back, because what you get
|
||||
* is not always what you asked for — a ceiling above the source bitrate is the
|
||||
* source, and claiming otherwise is the kind of lie the old picker told.
|
||||
*
|
||||
* TRACES: UR-074, UR-079 | DR-162, DR-225, DR-226
|
||||
* TRACES: UR-074, UR-079 | DR-162, DR-226, DR-227
|
||||
*/
|
||||
async function selectQuality(quality: StreamingQuality) {
|
||||
showQualityMenu = false;
|
||||
@@ -2424,7 +2424,7 @@
|
||||
// this the menu stayed on the first stream's rung while the stream itself
|
||||
// changed underneath.
|
||||
//
|
||||
// TRACES: UR-074, UR-079 | DR-225, DR-226
|
||||
// TRACES: UR-074, UR-079 | DR-226, DR-227
|
||||
if (negotiated) {
|
||||
currentSelection = negotiated;
|
||||
}
|
||||
@@ -2883,7 +2883,7 @@
|
||||
|
||||
<!--
|
||||
Streaming quality (bandwidth ceiling), populated from what this media
|
||||
source can actually offer. TRACES: UR-070, UR-074 | DR-162, DR-226
|
||||
source can actually offer. TRACES: UR-070, UR-074 | DR-162, DR-227
|
||||
-->
|
||||
{#if qualityOptions.length > 1}
|
||||
<div class="relative">
|
||||
@@ -2910,7 +2910,7 @@
|
||||
<div class="text-white text-sm font-semibold">Quality</div>
|
||||
<!--
|
||||
What the server is actually doing. Only knowable now that
|
||||
the backend reports it. TRACES: UR-079 | DR-227
|
||||
the backend reports it. TRACES: UR-079 | DR-228
|
||||
-->
|
||||
<div class="text-xs text-gray-400 mt-0.5">{playbackKindLabel}</div>
|
||||
</div>
|
||||
|
||||
@@ -37,7 +37,7 @@ const log = createLogger("Html5PlayerAdapter");
|
||||
* This is the one place a fallback is tolerable, and it is explicitly a
|
||||
* fallback: the negotiated path never reaches it.
|
||||
*
|
||||
* TRACES: UR-079 | DR-224
|
||||
* TRACES: UR-079 | DR-225
|
||||
*/
|
||||
function selectionForLoad(streamUrl: string, options: PlayerLoadOptions): StreamSelection {
|
||||
if (options.selection) return options.selection;
|
||||
@@ -75,9 +75,9 @@ export interface Html5ElementBridge {
|
||||
*
|
||||
* Carries the whole [`StreamSelection`], not just the URL: the component's
|
||||
* effect has to know the transport to choose a loader, and deriving that from
|
||||
* the URL is the substring check DR-224 removes.
|
||||
* the URL is the substring check DR-225 removes.
|
||||
*
|
||||
* TRACES: UR-079 | DR-224
|
||||
* TRACES: UR-079 | DR-225
|
||||
*/
|
||||
setStreamSelection(selection: StreamSelection): void;
|
||||
/** Tear down the component-owned hls.js instance (dual-audio prevention). */
|
||||
|
||||
@@ -51,7 +51,7 @@ export interface PlayerLoadOptions {
|
||||
* plugin's direct URL — where the adapter falls back to what the other
|
||||
* options already say rather than to reading the URL.
|
||||
*
|
||||
* TRACES: UR-079 | DR-224
|
||||
* TRACES: UR-079 | DR-225
|
||||
*/
|
||||
selection?: StreamSelection | null;
|
||||
/** The source is a file on disk (or the loopback server in front of one). */
|
||||
@@ -127,7 +127,7 @@ export interface PlayerAdapter {
|
||||
* already decided to reload, and `selection.transport` says how to open it, so
|
||||
* no adapter has to infer that from the URL.
|
||||
*
|
||||
* TRACES: UR-079 | DR-224
|
||||
* TRACES: UR-079 | DR-225
|
||||
*/
|
||||
reloadSource(selection: StreamSelection, offset: number): Promise<void>;
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ async function switchAudioTrack(
|
||||
* actually got, which is not always what was asked for: a ceiling above the
|
||||
* source bitrate is the source.
|
||||
*
|
||||
* TRACES: UR-074, UR-079 | DR-162, DR-225
|
||||
* TRACES: UR-074, UR-079 | DR-162, DR-226
|
||||
*/
|
||||
async function setStreamQuality(
|
||||
quality: StreamingQuality,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* The loader is chosen from the backend's `transport` tag, never from the URL.
|
||||
*
|
||||
* TRACES: UR-079 | DR-224 | UT-213
|
||||
* TRACES: UR-079 | DR-225 | UT-214
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Extracted from `VideoPlayer.svelte` so the decision can be unit-tested — the
|
||||
* same pattern as `episodeStrip.ts` and `TrackList.logic.test.ts`.
|
||||
*
|
||||
* TRACES: UR-079 | DR-224 | UT-213
|
||||
* TRACES: UR-079 | DR-225 | UT-214
|
||||
*/
|
||||
|
||||
import type { StreamSelection, Transport } from "$lib/api/bindings";
|
||||
@@ -54,11 +54,11 @@ export function videoLoaderFor(
|
||||
* whenever the selection **object** is replaced — even with an identical URL and
|
||||
* transport — and the HLS effect's teardown/rebuild is not idempotent: it
|
||||
* destroys the hls.js instance and reattaches, which leaves the element with no
|
||||
* video until something forces another cycle. The pre-DR-224 code read a plain
|
||||
* video until something forces another cycle. The pre-DR-225 code read a plain
|
||||
* URL *string*, so re-assigning the same value was a no-op and the effect stayed
|
||||
* put. Passing primitives restores that.
|
||||
*
|
||||
* TRACES: UR-079 | DR-224 | UT-213
|
||||
* TRACES: UR-079 | DR-225 | UT-214
|
||||
*/
|
||||
export function loaderForTransport(
|
||||
transport: Transport["type"],
|
||||
|
||||
Reference in New Issue
Block a user