mirror of
https://github.com/p-stream/p-stream.git
synced 2026-01-11 20:10:32 +00:00
Track and handle failed embeds in player sources
Introduces tracking of failed embeds per source in the player store, adds logic to mark embeds as failed on playback errors, and filters out failed embeds when selecting sources. Also ensures failed sources and embeds are cleared when a working source is found. This improves error handling and fallback behavior for sources with multiple embeds.
This commit is contained in:
parent
33b08b86cd
commit
ad592edc65
4 changed files with 57 additions and 7 deletions
|
|
@ -171,7 +171,9 @@ export function useScrape() {
|
|||
async (media: ScrapeMedia, startFromSourceId?: string) => {
|
||||
const providerInstance = getProviders();
|
||||
const allSources = providerInstance.listSources();
|
||||
const failedSources = usePlayerStore.getState().failedSources;
|
||||
const playerState = usePlayerStore.getState();
|
||||
const failedSources = playerState.failedSources;
|
||||
const failedEmbeds = playerState.failedEmbeds;
|
||||
|
||||
// Start with all available sources (filtered by disabled and failed ones)
|
||||
let baseSourceOrder = allSources
|
||||
|
|
@ -220,9 +222,15 @@ export function useScrape() {
|
|||
}
|
||||
}
|
||||
|
||||
// Filter out disabled embeds from the embed order
|
||||
// Collect all failed embed IDs across all sources
|
||||
const allFailedEmbedIds = Object.values(failedEmbeds).flat();
|
||||
|
||||
// Filter out disabled and failed embeds from the embed order
|
||||
const filteredEmbedOrder = enableEmbedOrder
|
||||
? preferredEmbedOrder.filter((id) => !disabledEmbeds.includes(id))
|
||||
? preferredEmbedOrder.filter(
|
||||
(id) =>
|
||||
!disabledEmbeds.includes(id) && !allFailedEmbedIds.includes(id),
|
||||
)
|
||||
: undefined;
|
||||
|
||||
const providerApiUrl = getLoadbalancedProviderApiUrl();
|
||||
|
|
|
|||
|
|
@ -182,9 +182,10 @@ export function RealPlayerView() {
|
|||
let startAt: number | undefined;
|
||||
if (startAtParam) startAt = parseTimestamp(startAtParam) ?? undefined;
|
||||
|
||||
// Clear failed sources when we successfully find a working source
|
||||
// Clear failed sources and embeds when we successfully find a working source
|
||||
const playerStore = usePlayerStore.getState();
|
||||
playerStore.clearFailedSources();
|
||||
playerStore.clearFailedEmbeds();
|
||||
|
||||
playMedia(
|
||||
convertRunoutputToSource(out),
|
||||
|
|
|
|||
|
|
@ -23,7 +23,10 @@ export function PlaybackErrorPart(props: PlaybackErrorPartProps) {
|
|||
const { t } = useTranslation();
|
||||
const playbackError = usePlayerStore((s) => s.interface.error);
|
||||
const currentSourceId = usePlayerStore((s) => s.sourceId);
|
||||
const currentEmbedId = usePlayerStore((s) => s.embedId);
|
||||
const addFailedSource = usePlayerStore((s) => s.addFailedSource);
|
||||
const addFailedEmbed = usePlayerStore((s) => s.addFailedEmbed);
|
||||
const failedEmbeds = usePlayerStore((s) => s.failedEmbeds);
|
||||
const modal = useModal("error");
|
||||
const settingsRouter = useOverlayRouter("settings");
|
||||
const hasOpenedSettings = useRef(false);
|
||||
|
|
@ -35,17 +38,32 @@ export function PlaybackErrorPart(props: PlaybackErrorPartProps) {
|
|||
(s) => s.enableAutoResumeOnPlaybackError,
|
||||
);
|
||||
|
||||
// Mark the failed source and handle UI when a playback error occurs
|
||||
// Mark the failed source/embed and handle UI when a playback error occurs
|
||||
useEffect(() => {
|
||||
if (playbackError && currentSourceId) {
|
||||
// Only mark source as failed for fatal errors
|
||||
// Only mark source/embed as failed for fatal errors
|
||||
const isFatalError =
|
||||
playbackError.type === "hls"
|
||||
? (playbackError.hls?.fatal ?? false)
|
||||
: playbackError.type === "htmlvideo";
|
||||
|
||||
if (isFatalError) {
|
||||
addFailedSource(currentSourceId);
|
||||
// If there's an active embed, disable that embed instead of the source
|
||||
if (currentEmbedId) {
|
||||
addFailedEmbed(currentSourceId, currentEmbedId);
|
||||
|
||||
// Check if all embeds for this source have now failed
|
||||
// If so, disable the entire source
|
||||
const failedEmbedsForSource = failedEmbeds[currentSourceId] || [];
|
||||
// For now, we'll assume if we have 2+ failed embeds for a source, disable it
|
||||
// This is a simple heuristic - we could make it more sophisticated
|
||||
if (failedEmbedsForSource.length >= 2) {
|
||||
addFailedSource(currentSourceId);
|
||||
}
|
||||
} else {
|
||||
// No embed active, disable the source
|
||||
addFailedSource(currentSourceId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasOpenedSettings.current && !enableAutoResumeOnPlaybackError) {
|
||||
|
|
@ -59,7 +77,10 @@ export function PlaybackErrorPart(props: PlaybackErrorPartProps) {
|
|||
}, [
|
||||
playbackError,
|
||||
currentSourceId,
|
||||
currentEmbedId,
|
||||
failedEmbeds,
|
||||
addFailedSource,
|
||||
addFailedEmbed,
|
||||
settingsRouter,
|
||||
setLastSuccessfulSource,
|
||||
enableAutoResumeOnPlaybackError,
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ export interface SourceSlice {
|
|||
};
|
||||
meta: PlayerMeta | null;
|
||||
failedSources: string[];
|
||||
failedEmbeds: Record<string, string[]>; // sourceId -> array of failed embedIds
|
||||
setStatus(status: PlayerStatus): void;
|
||||
setSource(
|
||||
stream: SourceSliceSource,
|
||||
|
|
@ -106,7 +107,9 @@ export interface SourceSlice {
|
|||
setCaptionAsTrack(asTrack: boolean): void;
|
||||
addExternalSubtitles(): Promise<void>;
|
||||
addFailedSource(sourceId: string): void;
|
||||
addFailedEmbed(sourceId: string, embedId: string): void;
|
||||
clearFailedSources(): void;
|
||||
clearFailedEmbeds(): void;
|
||||
reset(): void;
|
||||
}
|
||||
|
||||
|
|
@ -146,6 +149,7 @@ export const createSourceSlice: MakeSlice<SourceSlice> = (set, get) => ({
|
|||
status: playerStatus.IDLE,
|
||||
meta: null,
|
||||
failedSources: [],
|
||||
failedEmbeds: {},
|
||||
caption: {
|
||||
selected: null,
|
||||
asTrack: false,
|
||||
|
|
@ -269,11 +273,26 @@ export const createSourceSlice: MakeSlice<SourceSlice> = (set, get) => ({
|
|||
}
|
||||
});
|
||||
},
|
||||
addFailedEmbed(sourceId: string, embedId: string) {
|
||||
set((s) => {
|
||||
if (!s.failedEmbeds[sourceId]) {
|
||||
s.failedEmbeds[sourceId] = [];
|
||||
}
|
||||
if (!s.failedEmbeds[sourceId].includes(embedId)) {
|
||||
s.failedEmbeds[sourceId] = [...s.failedEmbeds[sourceId], embedId];
|
||||
}
|
||||
});
|
||||
},
|
||||
clearFailedSources() {
|
||||
set((s) => {
|
||||
s.failedSources = [];
|
||||
});
|
||||
},
|
||||
clearFailedEmbeds() {
|
||||
set((s) => {
|
||||
s.failedEmbeds = {};
|
||||
});
|
||||
},
|
||||
reset() {
|
||||
set((s) => {
|
||||
s.source = null;
|
||||
|
|
@ -288,6 +307,7 @@ export const createSourceSlice: MakeSlice<SourceSlice> = (set, get) => ({
|
|||
s.status = playerStatus.IDLE;
|
||||
s.meta = null;
|
||||
s.failedSources = [];
|
||||
s.failedEmbeds = {};
|
||||
s.caption = {
|
||||
selected: null,
|
||||
asTrack: false,
|
||||
|
|
|
|||
Loading…
Reference in a new issue