mirror of
https://github.com/p-stream/p-stream.git
synced 2026-03-11 17:55:33 +00:00
Track and skip failed sources during playback
Introduces a mechanism to track failed sources in the player store. When a playback error occurs, the current source is marked as failed and subsequent attempts will skip these sources. Failed sources are cleared when a working source is found. UI text is updated to reflect the new behavior.
This commit is contained in:
parent
4ced25623f
commit
c460c15966
5 changed files with 67 additions and 15 deletions
|
|
@ -841,11 +841,11 @@
|
|||
"errorNetwork": "Some kind of network error occurred which prevented the media from being successfully fetched, despite having previously been available.",
|
||||
"errorNotSupported": "The media or media provider object is not supported."
|
||||
},
|
||||
"autoResumeText": "There was an error trying to play the media 😖. Automatically trying the next source...",
|
||||
"autoResumeText": "There was an error trying to play the media 😖. Automatically trying the other sources...",
|
||||
"copyDebugInfo": "Copy debug info",
|
||||
"debugInfo": "Check console for more details.",
|
||||
"homeButton": "Go home",
|
||||
"resumeButton": "Try next source",
|
||||
"resumeButton": "Try next sources",
|
||||
"text": "There was an error trying to play the media 😖. Please try again or try a different source!",
|
||||
"title": "Failed to play video!"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
} from "@/backend/helpers/providerApi";
|
||||
import { getLoadbalancedProviderApiUrl } from "@/backend/providers/fetchers";
|
||||
import { getProviders } from "@/backend/providers/providers";
|
||||
import { usePlayerStore } from "@/stores/player/store";
|
||||
import { usePreferencesStore } from "@/stores/preferences";
|
||||
|
||||
export interface ScrapingItems {
|
||||
|
|
@ -170,10 +171,15 @@ export function useScrape() {
|
|||
async (media: ScrapeMedia, startFromSourceId?: string) => {
|
||||
const providerInstance = getProviders();
|
||||
const allSources = providerInstance.listSources();
|
||||
const failedSources = usePlayerStore.getState().failedSources;
|
||||
|
||||
// Start with all available sources (filtered by disabled ones)
|
||||
// Start with all available sources (filtered by disabled and failed ones)
|
||||
let baseSourceOrder = allSources
|
||||
.filter((source) => !disabledSources.includes(source.id))
|
||||
.filter(
|
||||
(source) =>
|
||||
!disabledSources.includes(source.id) &&
|
||||
!failedSources.includes(source.id),
|
||||
)
|
||||
.map((source) => source.id);
|
||||
|
||||
// Apply custom source ordering if enabled
|
||||
|
|
|
|||
|
|
@ -182,6 +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
|
||||
const playerStore = usePlayerStore.getState();
|
||||
playerStore.clearFailedSources();
|
||||
|
||||
playMedia(
|
||||
convertRunoutputToSource(out),
|
||||
convertProviderCaption(out.stream.captions),
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ export interface PlaybackErrorPartProps {
|
|||
export function PlaybackErrorPart(props: PlaybackErrorPartProps) {
|
||||
const { t } = useTranslation();
|
||||
const playbackError = usePlayerStore((s) => s.interface.error);
|
||||
const currentSourceId = usePlayerStore((s) => s.sourceId);
|
||||
const addFailedSource = usePlayerStore((s) => s.addFailedSource);
|
||||
const modal = useModal("error");
|
||||
const settingsRouter = useOverlayRouter("settings");
|
||||
const hasOpenedSettings = useRef(false);
|
||||
|
|
@ -33,21 +35,24 @@ export function PlaybackErrorPart(props: PlaybackErrorPartProps) {
|
|||
(s) => s.enableAutoResumeOnPlaybackError,
|
||||
);
|
||||
|
||||
// Automatically open the settings overlay when a playback error occurs (unless auto-resume is enabled)
|
||||
// Mark the failed source and handle UI when a playback error occurs
|
||||
useEffect(() => {
|
||||
if (
|
||||
playbackError &&
|
||||
!hasOpenedSettings.current &&
|
||||
!enableAutoResumeOnPlaybackError
|
||||
) {
|
||||
hasOpenedSettings.current = true;
|
||||
// Reset the last successful source when a playback error occurs
|
||||
setLastSuccessfulSource(null);
|
||||
settingsRouter.open();
|
||||
settingsRouter.navigate("/source");
|
||||
if (playbackError && currentSourceId) {
|
||||
// Mark this source as failed
|
||||
addFailedSource(currentSourceId);
|
||||
|
||||
if (!hasOpenedSettings.current && !enableAutoResumeOnPlaybackError) {
|
||||
hasOpenedSettings.current = true;
|
||||
// Reset the last successful source when a playback error occurs
|
||||
setLastSuccessfulSource(null);
|
||||
settingsRouter.open();
|
||||
settingsRouter.navigate("/source");
|
||||
}
|
||||
}
|
||||
}, [
|
||||
playbackError,
|
||||
currentSourceId,
|
||||
addFailedSource,
|
||||
settingsRouter,
|
||||
setLastSuccessfulSource,
|
||||
enableAutoResumeOnPlaybackError,
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ export interface SourceSlice {
|
|||
asTrack: boolean;
|
||||
};
|
||||
meta: PlayerMeta | null;
|
||||
failedSources: string[];
|
||||
setStatus(status: PlayerStatus): void;
|
||||
setSource(
|
||||
stream: SourceSliceSource,
|
||||
|
|
@ -104,6 +105,9 @@ export interface SourceSlice {
|
|||
redisplaySource(startAt: number): void;
|
||||
setCaptionAsTrack(asTrack: boolean): void;
|
||||
addExternalSubtitles(): Promise<void>;
|
||||
addFailedSource(sourceId: string): void;
|
||||
clearFailedSources(): void;
|
||||
reset(): void;
|
||||
}
|
||||
|
||||
export function metaToScrapeMedia(meta: PlayerMeta): ScrapeMedia {
|
||||
|
|
@ -141,6 +145,7 @@ export const createSourceSlice: MakeSlice<SourceSlice> = (set, get) => ({
|
|||
currentAudioTrack: null,
|
||||
status: playerStatus.IDLE,
|
||||
meta: null,
|
||||
failedSources: [],
|
||||
caption: {
|
||||
selected: null,
|
||||
asTrack: false,
|
||||
|
|
@ -256,6 +261,38 @@ export const createSourceSlice: MakeSlice<SourceSlice> = (set, get) => ({
|
|||
s.caption.asTrack = asTrack;
|
||||
});
|
||||
},
|
||||
addFailedSource(sourceId: string) {
|
||||
set((s) => {
|
||||
if (!s.failedSources.includes(sourceId)) {
|
||||
s.failedSources = [...s.failedSources, sourceId];
|
||||
}
|
||||
});
|
||||
},
|
||||
clearFailedSources() {
|
||||
set((s) => {
|
||||
s.failedSources = [];
|
||||
});
|
||||
},
|
||||
reset() {
|
||||
set((s) => {
|
||||
s.source = null;
|
||||
s.sourceId = null;
|
||||
s.embedId = null;
|
||||
s.qualities = [];
|
||||
s.audioTracks = [];
|
||||
s.captionList = [];
|
||||
s.isLoadingExternalSubtitles = false;
|
||||
s.currentQuality = null;
|
||||
s.currentAudioTrack = null;
|
||||
s.status = playerStatus.IDLE;
|
||||
s.meta = null;
|
||||
s.failedSources = [];
|
||||
s.caption = {
|
||||
selected: null,
|
||||
asTrack: false,
|
||||
};
|
||||
});
|
||||
},
|
||||
async addExternalSubtitles() {
|
||||
const store = get();
|
||||
if (!store.meta) return;
|
||||
|
|
|
|||
Loading…
Reference in a new issue