All files / src/lib stream-opening.ts

100% Statements 8/8
100% Branches 11/11
100% Functions 1/1
100% Lines 5/5

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42                                                  2x                   249x   235x 225x 80x    
// SPDX-License-Identifier: GPL-3.0-only
// Copyright (C) 2026 Max Boksem. See NOTICE for additional terms under GPLv3 section 7.
 
// Decides when the player shows its "Opening stream" spinner: from the moment
// a load starts until the stream is actually playing. Some servers take a
// minute or more to answer, and without it the player is just a black screen.
// A pause doesn't hide it. mpv goes on opening the stream while paused and
// shows its first frame when that's ready, which ends the spinner.
 
export interface StreamOpeningInput {
	/** `mpvLoad` is still in flight. */
	loadPending: boolean;
	currentUrl: string | null;
	/** From the mpv plugin: the current load has opened and started playing. */
	playbackStarted: boolean;
	/** The renderer has drawn the first frame of the current load. */
	firstFrameReady: boolean;
	/** `mpvLoad` rejected. The player shows the error instead. */
	error: string | null;
	/** The stream failed to open, or the reconnect monitor gave up. */
	failed: boolean;
	/** The reconnect monitor is reloading the stream and shows its own banner. */
	reconnecting: boolean;
}
 
export const isOpeningStream = ({
	loadPending,
	currentUrl,
	playbackStarted,
	firstFrameReady,
	error,
	failed,
	reconnecting,
}: StreamOpeningInput): boolean => {
	// Failures and reconnects have their own UI, which outranks this one.
	if (error || failed || reconnecting) return false;
	// Polled state can still describe the previous stream until the load returns.
	if (loadPending) return true;
	if (!currentUrl) return false;
	return !playbackStarted && !firstFrameReady;
};