mirror of
https://github.com/p-stream/p-stream.git
synced 2026-01-11 12:00:45 +00:00
add Find Next Source button
replace edit order button
This commit is contained in:
parent
45e5abd00e
commit
02a179b1d8
5 changed files with 76 additions and 14 deletions
|
|
@ -823,7 +823,7 @@
|
|||
},
|
||||
"title": "Sources",
|
||||
"unknownOption": "Unknown",
|
||||
"editOrder": "Edit order"
|
||||
"findNextSource": "Find next source"
|
||||
},
|
||||
"subtitles": {
|
||||
"customChoice": "Drop or upload file",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
import { Menu } from "@/components/player/internals/ContextMenu";
|
||||
import { SelectableLink } from "@/components/player/internals/ContextMenu/Links";
|
||||
import { useOverlayRouter } from "@/hooks/useOverlayRouter";
|
||||
import { playerStatus } from "@/stores/player/slices/source";
|
||||
import { usePlayerStore } from "@/stores/player/store";
|
||||
import { usePreferencesStore } from "@/stores/preferences";
|
||||
|
||||
|
|
@ -156,6 +157,8 @@ export function SourceSelectionView({
|
|||
const router = useOverlayRouter(id);
|
||||
const metaType = usePlayerStore((s) => s.meta?.type);
|
||||
const currentSourceId = usePlayerStore((s) => s.sourceId);
|
||||
const setResumeFromSourceId = usePlayerStore((s) => s.setResumeFromSourceId);
|
||||
const setStatus = usePlayerStore((s) => s.setStatus);
|
||||
const preferredSourceOrder = usePreferencesStore((s) => s.sourceOrder);
|
||||
const enableSourceOrder = usePreferencesStore((s) => s.enableSourceOrder);
|
||||
const lastSuccessfulSource = usePreferencesStore(
|
||||
|
|
@ -164,6 +167,9 @@ export function SourceSelectionView({
|
|||
const enableLastSuccessfulSource = usePreferencesStore(
|
||||
(s) => s.enableLastSuccessfulSource,
|
||||
);
|
||||
const manualSourceSelection = usePreferencesStore(
|
||||
(s) => s.manualSourceSelection,
|
||||
);
|
||||
|
||||
const sources = useMemo(() => {
|
||||
if (!metaType) return [];
|
||||
|
|
@ -221,20 +227,32 @@ export function SourceSelectionView({
|
|||
enableLastSuccessfulSource,
|
||||
]);
|
||||
|
||||
const handleFindNextSource = () => {
|
||||
if (!currentSourceId) return;
|
||||
// Set the resume source ID in the store
|
||||
setResumeFromSourceId(currentSourceId);
|
||||
// Close the settings overlay
|
||||
router.close();
|
||||
// Set status to SCRAPING to trigger scraping from next source
|
||||
setStatus(playerStatus.SCRAPING);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Menu.BackLink
|
||||
onClick={() => router.navigate("/")}
|
||||
rightSide={
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
window.location.href = "/settings#source-order";
|
||||
}}
|
||||
className="-mr-2 -my-1 px-2 p-[0.4em] rounded tabbable hover:bg-video-context-light hover:bg-opacity-10"
|
||||
>
|
||||
{t("player.menus.sources.editOrder")}
|
||||
</button>
|
||||
<div className="flex items-center gap-2">
|
||||
{currentSourceId && !manualSourceSelection && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleFindNextSource}
|
||||
className="-mr-2 -my-1 px-2 p-[0.4em] rounded tabbable hover:bg-video-context-light hover:bg-opacity-10"
|
||||
>
|
||||
{t("player.menus.sources.findNextSource")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{t("player.menus.sources.title")}
|
||||
|
|
|
|||
|
|
@ -211,7 +211,12 @@ export function useScrape() {
|
|||
}
|
||||
|
||||
// If we have a last successful source and the feature is enabled, prioritize it
|
||||
if (enableLastSuccessfulSource && lastSuccessfulSource) {
|
||||
// BUT only if we're not resuming from a specific source (to preserve custom order)
|
||||
if (
|
||||
enableLastSuccessfulSource &&
|
||||
lastSuccessfulSource &&
|
||||
!startFromSourceId
|
||||
) {
|
||||
const lastSourceIndex = baseSourceOrder.indexOf(lastSuccessfulSource);
|
||||
if (lastSourceIndex !== -1) {
|
||||
baseSourceOrder = [
|
||||
|
|
@ -222,6 +227,7 @@ export function useScrape() {
|
|||
}
|
||||
|
||||
// If starting from a specific source ID, filter the order to start AFTER that source
|
||||
// This preserves the custom order while starting from the next source
|
||||
let filteredSourceOrder = baseSourceOrder;
|
||||
if (startFromSourceId) {
|
||||
const startIndex = filteredSourceOrder.indexOf(startFromSourceId);
|
||||
|
|
|
|||
|
|
@ -47,6 +47,10 @@ export function RealPlayerView() {
|
|||
const [resumeFromSourceId, setResumeFromSourceId] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const storeResumeFromSourceId = usePlayerStore((s) => s.resumeFromSourceId);
|
||||
const setResumeFromSourceIdInStore = usePlayerStore(
|
||||
(s) => s.setResumeFromSourceId,
|
||||
);
|
||||
const [startAtParam] = useQueryParam("t");
|
||||
const {
|
||||
status,
|
||||
|
|
@ -77,6 +81,14 @@ export function RealPlayerView() {
|
|||
};
|
||||
}, [setLastSuccessfulSource]);
|
||||
|
||||
// Reset resume from source ID when leaving the player
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
setResumeFromSourceId(null);
|
||||
setResumeFromSourceIdInStore(null);
|
||||
};
|
||||
}, [setResumeFromSourceIdInStore]);
|
||||
|
||||
const paramsData = JSON.stringify({
|
||||
media: params.media,
|
||||
season: params.season,
|
||||
|
|
@ -169,14 +181,28 @@ export function RealPlayerView() {
|
|||
(startFromSourceId: string) => {
|
||||
// Set resume source first
|
||||
setResumeFromSourceId(startFromSourceId);
|
||||
setResumeFromSourceIdInStore(startFromSourceId);
|
||||
// Then change status in next tick to ensure re-render
|
||||
setTimeout(() => {
|
||||
setStatus(playerStatus.SCRAPING);
|
||||
}, 0);
|
||||
},
|
||||
[setStatus],
|
||||
[setStatus, setResumeFromSourceIdInStore],
|
||||
);
|
||||
|
||||
// Sync store value to local state when it changes (e.g., from settings)
|
||||
// or when status changes to SCRAPING
|
||||
useEffect(() => {
|
||||
if (storeResumeFromSourceId && status === playerStatus.SCRAPING) {
|
||||
if (
|
||||
!resumeFromSourceId ||
|
||||
resumeFromSourceId !== storeResumeFromSourceId
|
||||
) {
|
||||
setResumeFromSourceId(storeResumeFromSourceId);
|
||||
}
|
||||
}
|
||||
}, [storeResumeFromSourceId, resumeFromSourceId, status]);
|
||||
|
||||
const playAfterScrape = useCallback(
|
||||
(out: RunOutput | null) => {
|
||||
if (!out) return;
|
||||
|
|
@ -223,9 +249,11 @@ export function RealPlayerView() {
|
|||
<SourceSelectPart media={scrapeMedia} />
|
||||
) : (
|
||||
<ScrapingPart
|
||||
key={`scraping-${resumeFromSourceId || "default"}`}
|
||||
key={`scraping-${resumeFromSourceId || storeResumeFromSourceId || "default"}`}
|
||||
media={scrapeMedia}
|
||||
startFromSourceId={resumeFromSourceId || undefined}
|
||||
startFromSourceId={
|
||||
resumeFromSourceId || storeResumeFromSourceId || undefined
|
||||
}
|
||||
onResult={(sources, sourceOrder) => {
|
||||
setErrorData({
|
||||
sourceOrder,
|
||||
|
|
@ -234,6 +262,7 @@ export function RealPlayerView() {
|
|||
setScrapeNotFound();
|
||||
// Clear resume state after scraping
|
||||
setResumeFromSourceId(null);
|
||||
setResumeFromSourceIdInStore(null);
|
||||
}}
|
||||
onGetStream={playAfterScrape}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ export interface SourceSlice {
|
|||
meta: PlayerMeta | null;
|
||||
failedSourcesPerMedia: Record<string, string[]>; // mediaKey -> array of failed sourceIds
|
||||
failedEmbedsPerMedia: Record<string, Record<string, string[]>>; // mediaKey -> sourceId -> array of failed embedIds
|
||||
resumeFromSourceId: string | null;
|
||||
setStatus(status: PlayerStatus): void;
|
||||
setSource(
|
||||
stream: SourceSliceSource,
|
||||
|
|
@ -129,6 +130,7 @@ export interface SourceSlice {
|
|||
addFailedEmbed(sourceId: string, embedId: string): void;
|
||||
clearFailedSources(mediaKey?: string): void;
|
||||
clearFailedEmbeds(mediaKey?: string): void;
|
||||
setResumeFromSourceId(sourceId: string | null): void;
|
||||
reset(): void;
|
||||
}
|
||||
|
||||
|
|
@ -190,6 +192,7 @@ export const createSourceSlice: MakeSlice<SourceSlice> = (set, get) => ({
|
|||
meta: null,
|
||||
failedSourcesPerMedia: {},
|
||||
failedEmbedsPerMedia: {},
|
||||
resumeFromSourceId: null,
|
||||
caption: {
|
||||
selected: null,
|
||||
asTrack: false,
|
||||
|
|
@ -387,6 +390,11 @@ export const createSourceSlice: MakeSlice<SourceSlice> = (set, get) => ({
|
|||
}
|
||||
});
|
||||
},
|
||||
setResumeFromSourceId(sourceId: string | null) {
|
||||
set((s) => {
|
||||
s.resumeFromSourceId = sourceId;
|
||||
});
|
||||
},
|
||||
reset() {
|
||||
set((s) => {
|
||||
s.source = null;
|
||||
|
|
@ -402,6 +410,7 @@ export const createSourceSlice: MakeSlice<SourceSlice> = (set, get) => ({
|
|||
s.meta = null;
|
||||
s.failedSourcesPerMedia = {};
|
||||
s.failedEmbedsPerMedia = {};
|
||||
s.resumeFromSourceId = null;
|
||||
this.clearTranslateTask();
|
||||
s.caption = {
|
||||
selected: null,
|
||||
|
|
|
|||
Loading…
Reference in a new issue