mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 23:59:08 +00:00
feat: add comprehensive segment skip with all 5 types and settings submenu
- Add SegmentSkipMode type ('none', 'ask', 'auto') in settings.ts
- Create 5 segment skip settings: skipIntro, skipOutro, skipRecap, skipCommercial, skipPreview
- Update segments.ts to fetch all 5 segment types from Jellyfin MediaSegments API (10.11+)
- Create unified useSegmentSkipper hook supporting all segment types with 3 modes
- Update video player Controls.tsx with priority system (Commercial > Recap > Intro > Preview > Outro)
- Add dynamic skip button text in BottomControls.tsx
- Create dedicated settings submenu at settings/segment-skip/page.tsx
- Simplify PlaybackControlsSettings.tsx with navigation to submenu
- Extend DownloadedItem interface with all segment types for offline support
- Add 13+ translation keys for segment skip UI
This commit is contained in:
@@ -134,6 +134,9 @@ export enum VideoPlayer {
|
||||
MPV = 0,
|
||||
}
|
||||
|
||||
// Segment skip behavior options
|
||||
export type SegmentSkipMode = "none" | "ask" | "auto";
|
||||
|
||||
// Audio transcoding mode - controls how surround audio is handled
|
||||
// This controls server-side transcoding behavior for audio streams.
|
||||
// MPV decodes via FFmpeg and supports most formats, but mobile devices
|
||||
@@ -181,6 +184,12 @@ export type Settings = {
|
||||
maxAutoPlayEpisodeCount: MaxAutoPlayEpisodeCount;
|
||||
autoPlayEpisodeCount: number;
|
||||
autoPlayNextEpisode: boolean;
|
||||
// Media segment skip preferences
|
||||
skipIntro: SegmentSkipMode;
|
||||
skipOutro: SegmentSkipMode;
|
||||
skipRecap: SegmentSkipMode;
|
||||
skipCommercial: SegmentSkipMode;
|
||||
skipPreview: SegmentSkipMode;
|
||||
// Playback speed settings
|
||||
defaultPlaybackSpeed: number;
|
||||
playbackSpeedPerMedia: Record<string, number>;
|
||||
@@ -266,6 +275,12 @@ export const defaultValues: Settings = {
|
||||
maxAutoPlayEpisodeCount: { key: "3", value: 3 },
|
||||
autoPlayEpisodeCount: 0,
|
||||
autoPlayNextEpisode: true,
|
||||
// Media segment skip defaults
|
||||
skipIntro: "ask",
|
||||
skipOutro: "ask",
|
||||
skipRecap: "ask",
|
||||
skipCommercial: "ask",
|
||||
skipPreview: "ask",
|
||||
// Playback speed defaults
|
||||
defaultPlaybackSpeed: 1.0,
|
||||
playbackSpeedPerMedia: {},
|
||||
|
||||
@@ -74,10 +74,16 @@ export const getSegmentsForItem = (
|
||||
): {
|
||||
introSegments: MediaTimeSegment[];
|
||||
creditSegments: MediaTimeSegment[];
|
||||
recapSegments: MediaTimeSegment[];
|
||||
commercialSegments: MediaTimeSegment[];
|
||||
previewSegments: MediaTimeSegment[];
|
||||
} => {
|
||||
return {
|
||||
introSegments: item.introSegments || [],
|
||||
creditSegments: item.creditSegments || [],
|
||||
recapSegments: item.recapSegments || [],
|
||||
commercialSegments: item.commercialSegments || [],
|
||||
previewSegments: item.previewSegments || [],
|
||||
};
|
||||
};
|
||||
|
||||
@@ -95,6 +101,9 @@ const fetchMediaSegments = async (
|
||||
): Promise<{
|
||||
introSegments: MediaTimeSegment[];
|
||||
creditSegments: MediaTimeSegment[];
|
||||
recapSegments: MediaTimeSegment[];
|
||||
commercialSegments: MediaTimeSegment[];
|
||||
previewSegments: MediaTimeSegment[];
|
||||
} | null> => {
|
||||
try {
|
||||
const response = await api.axiosInstance.get<MediaSegmentsResponse>(
|
||||
@@ -102,13 +111,22 @@ const fetchMediaSegments = async (
|
||||
{
|
||||
headers: getAuthHeaders(api),
|
||||
params: {
|
||||
includeSegmentTypes: ["Intro", "Outro"],
|
||||
includeSegmentTypes: [
|
||||
"Intro",
|
||||
"Outro",
|
||||
"Recap",
|
||||
"Commercial",
|
||||
"Preview",
|
||||
],
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const introSegments: MediaTimeSegment[] = [];
|
||||
const creditSegments: MediaTimeSegment[] = [];
|
||||
const recapSegments: MediaTimeSegment[] = [];
|
||||
const commercialSegments: MediaTimeSegment[] = [];
|
||||
const previewSegments: MediaTimeSegment[] = [];
|
||||
|
||||
response.data.Items.forEach((segment) => {
|
||||
const timeSegment: MediaTimeSegment = {
|
||||
@@ -124,13 +142,27 @@ const fetchMediaSegments = async (
|
||||
case "Outro":
|
||||
creditSegments.push(timeSegment);
|
||||
break;
|
||||
// Optionally handle other types like Recap, Commercial, Preview
|
||||
case "Recap":
|
||||
recapSegments.push(timeSegment);
|
||||
break;
|
||||
case "Commercial":
|
||||
commercialSegments.push(timeSegment);
|
||||
break;
|
||||
case "Preview":
|
||||
previewSegments.push(timeSegment);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return { introSegments, creditSegments };
|
||||
return {
|
||||
introSegments,
|
||||
creditSegments,
|
||||
recapSegments,
|
||||
commercialSegments,
|
||||
previewSegments,
|
||||
};
|
||||
} catch (_error) {
|
||||
// Return null to indicate we should try legacy endpoints
|
||||
return null;
|
||||
@@ -146,6 +178,9 @@ const fetchLegacySegments = async (
|
||||
): Promise<{
|
||||
introSegments: MediaTimeSegment[];
|
||||
creditSegments: MediaTimeSegment[];
|
||||
recapSegments: MediaTimeSegment[];
|
||||
commercialSegments: MediaTimeSegment[];
|
||||
previewSegments: MediaTimeSegment[];
|
||||
}> => {
|
||||
const introSegments: MediaTimeSegment[] = [];
|
||||
const creditSegments: MediaTimeSegment[] = [];
|
||||
@@ -184,7 +219,13 @@ const fetchLegacySegments = async (
|
||||
console.error("Failed to fetch legacy segments", error);
|
||||
}
|
||||
|
||||
return { introSegments, creditSegments };
|
||||
return {
|
||||
introSegments,
|
||||
creditSegments,
|
||||
recapSegments: [],
|
||||
commercialSegments: [],
|
||||
previewSegments: [],
|
||||
};
|
||||
};
|
||||
|
||||
export const fetchAndParseSegments = async (
|
||||
@@ -193,6 +234,9 @@ export const fetchAndParseSegments = async (
|
||||
): Promise<{
|
||||
introSegments: MediaTimeSegment[];
|
||||
creditSegments: MediaTimeSegment[];
|
||||
recapSegments: MediaTimeSegment[];
|
||||
commercialSegments: MediaTimeSegment[];
|
||||
previewSegments: MediaTimeSegment[];
|
||||
}> => {
|
||||
// Try new API first (Jellyfin 10.11+)
|
||||
const newSegments = await fetchMediaSegments(itemId, api);
|
||||
|
||||
Reference in New Issue
Block a user