From 170f92bd79086cdb32af926059ebf59226a56507 Mon Sep 17 00:00:00 2001 From: Pas <74743263+Pasithea0@users.noreply.github.com> Date: Mon, 19 Jan 2026 17:37:40 -0700 Subject: [PATCH] Finish skip segments integration --- public/notifications.xml | 16 +++ src/assets/locales/en.json | 10 ++ src/components/player/TIDBSubmissionForm.tsx | 19 ++- src/components/player/atoms/Settings.tsx | 41 +++--- .../player/atoms/ThumbsFeedback.tsx | 4 +- .../player/atoms/settings/SettingsMenu.tsx | 5 + .../atoms/settings/SkipSegmentsView.tsx | 125 ++++++++++++++++++ 7 files changed, 196 insertions(+), 24 deletions(-) create mode 100644 src/components/player/atoms/settings/SkipSegmentsView.tsx diff --git a/public/notifications.xml b/public/notifications.xml index 68de2639..655e0456 100644 --- a/public/notifications.xml +++ b/public/notifications.xml @@ -8,6 +8,22 @@ Mon, 29 Sep 2025 18:00:00 MST + + notification-058 + New TheIntroDB Skip Segments feature! + You can now quickly leave feedback, correct, and submit skip times for movies and TV shows using your TheIntroDB API key right within P-Stream! + +If you haven't made an account on TheIntroDB yet, you can do so below! Generate your API key and add it to your P-Stream connectionssettings! + +Then, the next time you encounter a skip button, you can leave feedback and correct the skip time if it's incorrect! + +And if an episode does't have a segment yet, you can submit it yourself from the new Skip Segments menu on the player! + + https://theintrodb.org/sign-up + Mon, 19 Jan 2026 17:40:00 MST + feature + + notification-057 P-Stream v5.3.4 released! diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json index 9e43dfb5..a4f4a0d2 100644 --- a/src/assets/locales/en.json +++ b/src/assets/locales/en.json @@ -978,6 +978,16 @@ "intro": "Skip Intro", "recap": "Skip Recap", "credits": "Skip Credits", + "skipSegments": "Skip Segments", + "submitSegment": "Submit Segment", + "noSegments": "No skip segments available", + "startsAt": "Starts at {{time}}", + "endsAt": "Ends at {{time}}", + "timeUnknown": "Time unknown", + "confidence": "confidence", + "submissions": "{{count}} submissions", + "connectApiKeyMessage": "To submit new segments, enter your TheIntroDB API key in the connections settings", + "endOfVideo": "End", "feedback": { "title": "Was this skip correct?", "modal": { diff --git a/src/components/player/TIDBSubmissionForm.tsx b/src/components/player/TIDBSubmissionForm.tsx index e0913ec5..3825e40e 100644 --- a/src/components/player/TIDBSubmissionForm.tsx +++ b/src/components/player/TIDBSubmissionForm.tsx @@ -64,17 +64,17 @@ function parseTimeToSeconds(timeStr: string): number | null { return parsed; } -interface SubmissionFormProps { +interface TIDBSubmissionFormProps { segment: SegmentData; onSuccess?: () => void; onCancel?: () => void; } -export function SubmissionForm({ +export function TIDBSubmissionForm({ segment, onSuccess, onCancel, -}: SubmissionFormProps) { +}: TIDBSubmissionFormProps) { const { t } = useTranslation(); const meta = usePlayerStore((s) => s.meta); const tidbKey = usePreferencesStore((s) => s.tidbKey); @@ -336,15 +336,20 @@ export function SubmissionForm({ > {t("player.skipTime.feedback.modal.cancel")} - + diff --git a/src/components/player/atoms/Settings.tsx b/src/components/player/atoms/Settings.tsx index 07591ffd..f344346d 100644 --- a/src/components/player/atoms/Settings.tsx +++ b/src/components/player/atoms/Settings.tsx @@ -23,6 +23,7 @@ import { LanguageSubtitlesView } from "./settings/LanguageSubtitlesView"; import { PlaybackSettingsView } from "./settings/PlaybackSettingsView"; import { QualityView } from "./settings/QualityView"; import { SettingsMenu } from "./settings/SettingsMenu"; +import { SkipSegmentsView } from "./settings/SkipSegmentsView"; import { TranscriptView } from "./settings/TranscriptView"; import { TranslateSubtitleView } from "./settings/TranslateSubtitleView"; import { WatchPartyView } from "./settings/WatchPartyView"; @@ -49,20 +50,20 @@ function SettingsOverlay({ id }: { id: string }) { return ( - + - + - + - + {/* This is used by the captions shortcut in bottomControls of player */} - + @@ -81,7 +82,7 @@ function SettingsOverlay({ id }: { id: string }) { id={id} path="/captionsOverlay/languagesOverlay" width={443} - height={452} + height={496} > {chosenLanguage && ( @@ -98,7 +99,7 @@ function SettingsOverlay({ id }: { id: string }) { id={id} path="/captionsOverlay/languagesOverlay/translateSubtitleOverlay" width={443} - height={452} + height={496} > {captionToTranslate && ( @@ -110,7 +111,7 @@ function SettingsOverlay({ id }: { id: string }) { )} - + @@ -120,18 +121,18 @@ function SettingsOverlay({ id }: { id: string }) { id={id} path="/captions/settingsOverlay" width={343} - height={452} + height={496} > - + - + @@ -141,11 +142,21 @@ function SettingsOverlay({ id }: { id: string }) { + + + + + @@ -155,7 +166,7 @@ function SettingsOverlay({ id }: { id: string }) { id={id} path="/captions/languages" width={443} - height={452} + height={496} > {chosenLanguage && ( @@ -171,7 +182,7 @@ function SettingsOverlay({ id }: { id: string }) { id={id} path="/captions/languages/translateSubtitleOverlay" width={443} - height={452} + height={496} > {captionToTranslate && ( @@ -180,7 +191,7 @@ function SettingsOverlay({ id }: { id: string }) { - + diff --git a/src/components/player/atoms/ThumbsFeedback.tsx b/src/components/player/atoms/ThumbsFeedback.tsx index 868856bc..516d6204 100644 --- a/src/components/player/atoms/ThumbsFeedback.tsx +++ b/src/components/player/atoms/ThumbsFeedback.tsx @@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next"; import { Icon, Icons } from "@/components/Icon"; import { SegmentData } from "@/components/player/hooks/useSkipTime"; -import { SubmissionForm } from "@/components/player/TIDBSubmissionForm"; +import { TIDBSubmissionForm } from "@/components/player/TIDBSubmissionForm"; import { Transition } from "@/components/utils/Transition"; import { useOverlayStack } from "@/stores/interface/overlayStack"; import { usePlayerStore } from "@/stores/player/store"; @@ -153,7 +153,7 @@ export function ThumbsFeedback({ {showSubmissionModal && feedbackData && ( - router.navigate("/playback")}> {t("player.menus.settings.playbackItem")} + router.navigate("/playback/skip-segments")} + > + {t("player.skipTime.skipSegments")} + ); diff --git a/src/components/player/atoms/settings/SkipSegmentsView.tsx b/src/components/player/atoms/settings/SkipSegmentsView.tsx new file mode 100644 index 00000000..37b2dc35 --- /dev/null +++ b/src/components/player/atoms/settings/SkipSegmentsView.tsx @@ -0,0 +1,125 @@ +import { useState } from "react"; +import { useTranslation } from "react-i18next"; + +import { Button } from "@/components/buttons/Button"; +import { useSkipTime } from "@/components/player/hooks/useSkipTime"; +import { Menu } from "@/components/player/internals/ContextMenu"; +import { TIDBSubmissionForm } from "@/components/player/TIDBSubmissionForm"; +import { useOverlayRouter } from "@/hooks/useOverlayRouter"; +import { usePlayerStore } from "@/stores/player/store"; +import { usePreferencesStore } from "@/stores/preferences"; +import { formatSeconds } from "@/utils/formatSeconds"; + +export function SkipSegmentsView({ id }: { id: string }) { + const { t } = useTranslation(); + const router = useOverlayRouter(id); + const display = usePlayerStore((s) => s.display); + const segments = useSkipTime(); + const tidbKey = usePreferencesStore((s) => s.tidbKey); + const [showSubmissionForm, setShowSubmissionForm] = useState(false); + + const handleSeek = (seconds: number) => { + display?.setTime(seconds); + }; + + const getSegmentTypeLabel = (type: "intro" | "recap" | "credits") => { + switch (type) { + case "intro": + return t("player.skipTime.intro"); + case "recap": + return t("player.skipTime.recap"); + case "credits": + return t("player.skipTime.credits"); + default: + return type; + } + }; + + return ( + <> + router.navigate("/")}> + {t("player.skipTime.skipSegments")} + + +
+ {tidbKey ? ( + + ) : ( +
+ {t("player.skipTime.connectApiKeyMessage")} +
+ )} +
+
+ {segments.length === 0 ? ( +
+ {t("player.skipTime.noSegments")} +
+ ) : ( + segments.map((segment) => { + // Handle start time (null means 0/start of video) + const startTime = (segment.start_ms ?? 0) / 1000; + // Handle end time (null means end of video) + const endTime = segment.end_ms ? segment.end_ms / 1000 : null; + + return ( + + ); + }) + )} +
+
+ + {showSubmissionForm && tidbKey && ( + { + setShowSubmissionForm(false); + // Optionally refresh segments here + }} + onCancel={() => setShowSubmissionForm(false)} + /> + )} + + ); +}