pstream is dead; long live pstream taciturnaxolotl.github.io/pstream-ng/
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

add auto segment skipping

Pas 248da370 14d45b4a

+157 -2
+3
src/assets/locales/en.json
··· 1330 1330 "skipCredits": "Skip End Credits", 1331 1331 "skipCreditsDescription": "When enabled, automatically play the next episode at 99% completion to skip end credits. When disabled, wait until the episode is fully completed.", 1332 1332 "skipCreditsLabel": "Skip end credits", 1333 + "autoSkipSegments": "Auto Skip Segments", 1334 + "autoSkipSegmentsDescription": "When enabled, automatically skip intro, recap, and preview segments while watching.", 1335 + "autoSkipSegmentsLabel": "Auto skip segments", 1333 1336 "lowPerformanceMode": "Low performance/bandwidth mode", 1334 1337 "lowPerformanceModeDescription": "Optimizes the application for slower connections and devices by disabling bandwidth-heavy features. This mode reduces data usage and improves performance while keeping the core search and watch functionality intact. ", 1335 1338 "lowPerformanceModeLabel": "Low performance mode",
+2
src/backend/accounts/settings.ts
··· 15 15 enableThumbnails?: boolean; 16 16 enableAutoplay?: boolean; 17 17 enableSkipCredits?: boolean; 18 + enableAutoSkipSegments?: boolean; 18 19 enableDiscover?: boolean; 19 20 enableFeatured?: boolean; 20 21 enableDetailsModal?: boolean; ··· 50 51 enableThumbnails?: boolean; 51 52 enableAutoplay?: boolean; 52 53 enableSkipCredits?: boolean; 54 + enableAutoSkipSegments?: boolean; 53 55 enableDiscover?: boolean; 54 56 enableFeatured?: boolean; 55 57 enableDetailsModal?: boolean;
+2
src/components/player/base/Container.tsx
··· 1 1 import { ReactNode, RefObject, useEffect, useRef } from "react"; 2 2 3 3 import { OverlayDisplay } from "@/components/overlays/OverlayDisplay"; 4 + import { AutoSkipSegments } from "@/components/player/internals/AutoSkipSegments"; 4 5 import { SkipTracker } from "@/components/player/internals/Backend/SkipTracker"; 5 6 import { CastingInternal } from "@/components/player/internals/CastingInternal"; 6 7 import { HeadUpdater } from "@/components/player/internals/HeadUpdater"; ··· 100 101 <WatchPartyReporter /> 101 102 <SkipTracker /> 102 103 <WatchPartyResetter /> 104 + <AutoSkipSegments /> 103 105 <div className="relative h-screen overflow-hidden"> 104 106 <VideoClickTarget showingControls={props.showingControls} /> 105 107 <HeadUpdater />
+87
src/components/player/internals/AutoSkipSegments.tsx
··· 1 + import { useEffect, useRef } from "react"; 2 + 3 + import { useSkipTime } from "@/components/player/hooks/useSkipTime"; 4 + import { usePlayerStore } from "@/stores/player/store"; 5 + import { usePreferencesStore } from "@/stores/preferences"; 6 + 7 + interface SegmentSkipState { 8 + segmentId: string; 9 + hasSkipped: boolean; 10 + } 11 + 12 + /** 13 + * Component that automatically skips segments (intro, recap, preview, credits) 14 + * when the enableAutoSkipSegments preference is enabled. 15 + * For credits segments, only skips if end_ms is null (end of video). 16 + */ 17 + export function AutoSkipSegments() { 18 + const enableAutoSkipSegments = usePreferencesStore( 19 + (s) => s.enableAutoSkipSegments, 20 + ); 21 + const skipCredits = usePreferencesStore((s) => s.enableSkipCredits); 22 + const display = usePlayerStore((s) => s.display); 23 + const time = usePlayerStore((s) => s.progress.time); 24 + const meta = usePlayerStore((s) => s.meta); 25 + const segments = useSkipTime(); 26 + 27 + // Track which segments we've already skipped to avoid re-skipping 28 + const skippedSegmentsRef = useRef<Map<string, SegmentSkipState>>(new Map()); 29 + 30 + // Reset skip state when media changes 31 + useEffect(() => { 32 + skippedSegmentsRef.current.clear(); 33 + }, [meta?.tmdbId, meta?.season?.number, meta?.episode?.number]); 34 + 35 + useEffect(() => { 36 + if (!enableAutoSkipSegments || !display) return; 37 + 38 + const currentSeconds = time; 39 + 40 + for (const segment of segments) { 41 + // For credits, only skip if skipCredits is enabled and end_ms is null (end of video) 42 + const isCreditsSegment = segment.type === "credits"; 43 + if (isCreditsSegment) { 44 + if (!skipCredits) continue; 45 + // Check if credits go to end of video (end_ms is null) 46 + if (segment.end_ms !== null) continue; 47 + } else if (segment.end_ms === null) { 48 + // For intro, recap, preview - skip if enabled and end time is defined 49 + continue; 50 + } 51 + 52 + const startSeconds = (segment.start_ms ?? 0) / 1000; 53 + const endSeconds = segment.end_ms ? segment.end_ms / 1000 : Infinity; 54 + const segmentId = `${segment.type}-${startSeconds}-${endSeconds}`; 55 + 56 + // Check if we're inside the segment 57 + if (currentSeconds >= startSeconds && currentSeconds < endSeconds) { 58 + const skipState = skippedSegmentsRef.current.get(segmentId); 59 + 60 + // Only skip if we haven't skipped this segment yet 61 + if (!skipState || !skipState.hasSkipped) { 62 + // Skip to the end of the segment 63 + display.setTime( 64 + endSeconds === Infinity ? currentSeconds + 10 : endSeconds, 65 + ); 66 + 67 + // Mark this segment as skipped 68 + skippedSegmentsRef.current.set(segmentId, { 69 + segmentId, 70 + hasSkipped: true, 71 + }); 72 + } 73 + } 74 + } 75 + }, [ 76 + enableAutoSkipSegments, 77 + skipCredits, 78 + display, 79 + time, 80 + segments, 81 + meta?.tmdbId, 82 + meta?.season?.number, 83 + meta?.episode?.number, 84 + ]); 85 + 86 + return null; 87 + }
+15 -1
src/hooks/useSettingsState.ts
··· 58 58 | undefined, 59 59 enableThumbnails: boolean, 60 60 enableAutoplay: boolean, 61 + enableSkipCredits: boolean, 62 + enableAutoSkipSegments: boolean, 61 63 enableDiscover: boolean, 62 64 enableFeatured: boolean, 63 65 enableDetailsModal: boolean, ··· 68 70 embedOrder: string[], 69 71 enableEmbedOrder: boolean, 70 72 proxyTmdb: boolean, 71 - enableSkipCredits: boolean, 72 73 enableImageLogos: boolean, 73 74 enableCarouselView: boolean, 74 75 enableMinimalCards: boolean, ··· 144 145 enableSkipCreditsChanged, 145 146 ] = useDerived(enableSkipCredits); 146 147 const [ 148 + enableAutoSkipSegmentsState, 149 + setEnableAutoSkipSegmentsState, 150 + resetEnableAutoSkipSegments, 151 + enableAutoSkipSegmentsChanged, 152 + ] = useDerived(enableAutoSkipSegments); 153 + const [ 147 154 enableDiscoverState, 148 155 setEnableDiscoverState, 149 156 resetEnableDiscover, ··· 282 289 resetEnableThumbnails(); 283 290 resetEnableAutoplay(); 284 291 resetEnableSkipCredits(); 292 + resetEnableAutoSkipSegments(); 285 293 resetEnableDiscover(); 286 294 resetEnableFeatured(); 287 295 resetEnableDetailsModal(); ··· 321 329 enableThumbnailsChanged || 322 330 enableAutoplayChanged || 323 331 enableSkipCreditsChanged || 332 + enableAutoSkipSegmentsChanged || 324 333 enableDiscoverChanged || 325 334 enableFeaturedChanged || 326 335 enableDetailsModalChanged || ··· 420 429 state: enableSkipCreditsState, 421 430 set: setEnableSkipCreditsState, 422 431 changed: enableSkipCreditsChanged, 432 + }, 433 + enableAutoSkipSegments: { 434 + state: enableAutoSkipSegmentsState, 435 + set: setEnableAutoSkipSegmentsState, 436 + changed: enableAutoSkipSegmentsChanged, 423 437 }, 424 438 enableDiscover: { 425 439 state: enableDiscoverState,
+15 -1
src/pages/Settings.tsx
··· 399 399 (s) => s.setEnableSkipCredits, 400 400 ); 401 401 402 + const enableAutoSkipSegments = usePreferencesStore( 403 + (s) => s.enableAutoSkipSegments, 404 + ); 405 + const setEnableAutoSkipSegments = usePreferencesStore( 406 + (s) => s.setEnableAutoSkipSegments, 407 + ); 408 + 402 409 const sourceOrder = usePreferencesStore((s) => s.sourceOrder); 403 410 const setSourceOrder = usePreferencesStore((s) => s.setSourceOrder); 404 411 ··· 558 565 account ? account.profile : undefined, 559 566 enableThumbnails, 560 567 enableAutoplay, 568 + enableSkipCredits, 569 + enableAutoSkipSegments, 561 570 enableDiscover, 562 571 enableFeatured, 563 572 enableDetailsModal, ··· 568 577 embedOrder, 569 578 enableEmbedOrder, 570 579 proxyTmdb, 571 - enableSkipCredits, 572 580 enableImageLogos, 573 581 enableCarouselView, 574 582 enableMinimalCards, ··· 629 637 state.enableThumbnails.changed || 630 638 state.enableAutoplay.changed || 631 639 state.enableSkipCredits.changed || 640 + state.enableAutoSkipSegments.changed || 632 641 state.enableDiscover.changed || 633 642 state.enableFeatured.changed || 634 643 state.enableDetailsModal.changed || ··· 658 667 enableThumbnails: state.enableThumbnails.state, 659 668 enableAutoplay: state.enableAutoplay.state, 660 669 enableSkipCredits: state.enableSkipCredits.state, 670 + enableAutoSkipSegments: state.enableAutoSkipSegments.state, 661 671 enableDiscover: state.enableDiscover.state, 662 672 enableFeatured: state.enableFeatured.state, 663 673 enableDetailsModal: state.enableDetailsModal.state, ··· 706 716 setEnableThumbnails(state.enableThumbnails.state); 707 717 setEnableAutoplay(state.enableAutoplay.state); 708 718 setEnableSkipCredits(state.enableSkipCredits.state); 719 + setEnableAutoSkipSegments(state.enableAutoSkipSegments.state); 709 720 setEnableDiscover(state.enableDiscover.state); 710 721 setEnableFeatured(state.enableFeatured.state); 711 722 setEnableDetailsModal(state.enableDetailsModal.state); ··· 769 780 setTIDBKey, 770 781 setEnableAutoplay, 771 782 setEnableSkipCredits, 783 + setEnableAutoSkipSegments, 772 784 setEnableDiscover, 773 785 setEnableFeatured, 774 786 setEnableDetailsModal, ··· 855 867 setEnableAutoplay={state.enableAutoplay.set} 856 868 enableSkipCredits={state.enableSkipCredits.state} 857 869 setEnableSkipCredits={state.enableSkipCredits.set} 870 + enableAutoSkipSegments={state.enableAutoSkipSegments.state} 871 + setEnableAutoSkipSegments={state.enableAutoSkipSegments.set} 858 872 sourceOrder={availableSources} 859 873 setSourceOrder={state.sourceOrder.set} 860 874 enableSourceOrder={state.enableSourceOrder.state}
+25
src/pages/parts/settings/PreferencesPart.tsx
··· 24 24 setEnableAutoplay: (v: boolean) => void; 25 25 enableSkipCredits: boolean; 26 26 setEnableSkipCredits: (v: boolean) => void; 27 + enableAutoSkipSegments: boolean; 28 + setEnableAutoSkipSegments: (v: boolean) => void; 27 29 sourceOrder: string[]; 28 30 setSourceOrder: (v: string[]) => void; 29 31 enableSourceOrder: boolean; ··· 174 176 <p className="flex-1 text-white font-bold"> 175 177 {t("settings.preferences.skipCreditsLabel")} 176 178 </p> 179 + </div> 180 + 181 + {/* Auto Skip Segments Preference */} 182 + <div className="pt-4 mt-4"> 183 + <p className="text-white font-bold mb-3"> 184 + {t("settings.preferences.autoSkipSegments")} 185 + </p> 186 + <p className="max-w-[25rem] font-medium"> 187 + {t("settings.preferences.autoSkipSegmentsDescription")} 188 + </p> 189 + <div 190 + onClick={() => 191 + props.setEnableAutoSkipSegments( 192 + !props.enableAutoSkipSegments, 193 + ) 194 + } 195 + className="bg-dropdown-background hover:bg-dropdown-hoverBackground select-none my-4 cursor-pointer space-x-3 flex items-center max-w-[25rem] py-3 px-4 rounded-lg" 196 + > 197 + <Toggle enabled={props.enableAutoSkipSegments} /> 198 + <p className="flex-1 text-white font-bold"> 199 + {t("settings.preferences.autoSkipSegmentsLabel")} 200 + </p> 201 + </div> 177 202 </div> 178 203 </div> 179 204 )}
+8
src/stores/preferences/index.tsx
··· 11 11 enableThumbnails: boolean; 12 12 enableAutoplay: boolean; 13 13 enableSkipCredits: boolean; 14 + enableAutoSkipSegments: boolean; 14 15 enableDiscover: boolean; 15 16 enableFeatured: boolean; 16 17 enableDetailsModal: boolean; ··· 42 43 setEnableThumbnails(v: boolean): void; 43 44 setEnableAutoplay(v: boolean): void; 44 45 setEnableSkipCredits(v: boolean): void; 46 + setEnableAutoSkipSegments(v: boolean): void; 45 47 setEnableDiscover(v: boolean): void; 46 48 setEnableFeatured(v: boolean): void; 47 49 setEnableDetailsModal(v: boolean): void; ··· 77 79 enableThumbnails: false, 78 80 enableAutoplay: true, 79 81 enableSkipCredits: true, 82 + enableAutoSkipSegments: false, 80 83 enableDiscover: true, 81 84 enableFeatured: false, 82 85 enableDetailsModal: false, ··· 117 120 setEnableSkipCredits(v) { 118 121 set((s) => { 119 122 s.enableSkipCredits = v; 123 + }); 124 + }, 125 + setEnableAutoSkipSegments(v) { 126 + set((s) => { 127 + s.enableAutoSkipSegments = v; 120 128 }); 121 129 }, 122 130 setEnableDiscover(v) {