mirror of
https://github.com/p-stream/p-stream.git
synced 2026-05-09 09:11:14 +00:00
update skip button to support other segments
This commit is contained in:
parent
0b2536486f
commit
c0029577e2
5 changed files with 304 additions and 147 deletions
|
|
@ -968,6 +968,11 @@
|
||||||
"remaining": "{{timeLeft}} left • Finish at {{timeFinished, datetime}}",
|
"remaining": "{{timeLeft}} left • Finish at {{timeFinished, datetime}}",
|
||||||
"shortRegular": "{{timeWatched}}",
|
"shortRegular": "{{timeWatched}}",
|
||||||
"shortRemaining": "-{{timeLeft}}"
|
"shortRemaining": "-{{timeLeft}}"
|
||||||
|
},
|
||||||
|
"skipTime": {
|
||||||
|
"intro": "Skip Intro",
|
||||||
|
"recap": "Skip Recap",
|
||||||
|
"credits": "Skip Credits"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"support": {
|
"support": {
|
||||||
|
|
|
||||||
|
|
@ -1,123 +0,0 @@
|
||||||
import classNames from "classnames";
|
|
||||||
import { useCallback } from "react";
|
|
||||||
|
|
||||||
import { Icon, Icons } from "@/components/Icon";
|
|
||||||
import { useSkipTracking } from "@/components/player/hooks/useSkipTracking";
|
|
||||||
import { Transition } from "@/components/utils/Transition";
|
|
||||||
import { usePlayerStore } from "@/stores/player/store";
|
|
||||||
|
|
||||||
function shouldShowSkipButton(
|
|
||||||
currentTime: number,
|
|
||||||
skipTime?: number | null,
|
|
||||||
): "always" | "hover" | "none" {
|
|
||||||
if (typeof skipTime !== "number") return "none";
|
|
||||||
|
|
||||||
// Only show during the first 10 seconds of the intro section
|
|
||||||
if (currentTime >= 0 && currentTime < skipTime) {
|
|
||||||
if (currentTime <= 10) return "always";
|
|
||||||
return "hover";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
function Button(props: {
|
|
||||||
className: string;
|
|
||||||
onClick?: () => void;
|
|
||||||
children: React.ReactNode;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
className={classNames(
|
|
||||||
"font-bold rounded h-10 w-40 scale-95 hover:scale-100 transition-all duration-200",
|
|
||||||
props.className,
|
|
||||||
)}
|
|
||||||
type="button"
|
|
||||||
onClick={props.onClick}
|
|
||||||
>
|
|
||||||
{props.children}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SkipIntroButton(props: {
|
|
||||||
controlsShowing: boolean;
|
|
||||||
skipTime?: number | null;
|
|
||||||
inControl: boolean;
|
|
||||||
}) {
|
|
||||||
const time = usePlayerStore((s) => s.progress.time);
|
|
||||||
const status = usePlayerStore((s) => s.status);
|
|
||||||
const display = usePlayerStore((s) => s.display);
|
|
||||||
const meta = usePlayerStore((s) => s.meta);
|
|
||||||
const { addSkipEvent } = useSkipTracking(20);
|
|
||||||
const showingState = shouldShowSkipButton(time, props.skipTime);
|
|
||||||
const animation = showingState === "hover" ? "slide-up" : "fade";
|
|
||||||
let bottom = "bottom-[calc(6rem+env(safe-area-inset-bottom))]";
|
|
||||||
if (showingState === "always") {
|
|
||||||
bottom = props.controlsShowing
|
|
||||||
? bottom
|
|
||||||
: "bottom-[calc(3rem+env(safe-area-inset-bottom))]";
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSkip = useCallback(() => {
|
|
||||||
if (typeof props.skipTime === "number" && display) {
|
|
||||||
const startTime = time;
|
|
||||||
const endTime = props.skipTime;
|
|
||||||
const skipDuration = endTime - startTime;
|
|
||||||
|
|
||||||
display.setTime(props.skipTime);
|
|
||||||
|
|
||||||
// Add manual skip event with high confidence (user explicitly clicked skip intro)
|
|
||||||
addSkipEvent({
|
|
||||||
startTime,
|
|
||||||
endTime,
|
|
||||||
skipDuration,
|
|
||||||
confidence: 0.95, // High confidence for explicit user action
|
|
||||||
meta: meta
|
|
||||||
? {
|
|
||||||
title:
|
|
||||||
meta.type === "show" && meta.episode
|
|
||||||
? `${meta.title} - S${meta.season?.number || 0}E${meta.episode.number || 0}`
|
|
||||||
: meta.title,
|
|
||||||
type: meta.type === "movie" ? "Movie" : "TV Show",
|
|
||||||
tmdbId: meta.tmdbId,
|
|
||||||
seasonNumber: meta.season?.number,
|
|
||||||
episodeNumber: meta.episode?.number,
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(`Skip intro button used: ${skipDuration}s total`);
|
|
||||||
}
|
|
||||||
}, [props.skipTime, display, time, addSkipEvent, meta]);
|
|
||||||
if (!props.inControl) return null;
|
|
||||||
|
|
||||||
let show = false;
|
|
||||||
if (showingState === "always") show = true;
|
|
||||||
else if (showingState === "hover" && props.controlsShowing) show = true;
|
|
||||||
if (status !== "playing") show = false;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Transition
|
|
||||||
animation={animation}
|
|
||||||
show={show}
|
|
||||||
className="absolute right-[calc(3rem+env(safe-area-inset-right))] bottom-0"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={classNames([
|
|
||||||
"absolute bottom-0 right-0 transition-[bottom] duration-200 flex items-center space-x-3",
|
|
||||||
bottom,
|
|
||||||
])}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
onClick={handleSkip}
|
|
||||||
className="bg-buttons-primary hover:bg-buttons-primaryHover text-buttons-primaryText flex justify-center items-center"
|
|
||||||
>
|
|
||||||
<Icon className="text-xl mr-1" icon={Icons.SKIP_EPISODE} />
|
|
||||||
Skip Intro
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
211
src/components/player/atoms/SkipSegmentButton.tsx
Normal file
211
src/components/player/atoms/SkipSegmentButton.tsx
Normal file
|
|
@ -0,0 +1,211 @@
|
||||||
|
import classNames from "classnames";
|
||||||
|
import { useCallback } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { Icon, Icons } from "@/components/Icon";
|
||||||
|
import { NextEpisodeButton } from "@/components/player/atoms/NextEpisodeButton";
|
||||||
|
import { SegmentData } from "@/components/player/hooks/useSkipTime";
|
||||||
|
import { useSkipTracking } from "@/components/player/hooks/useSkipTracking";
|
||||||
|
import { Transition } from "@/components/utils/Transition";
|
||||||
|
import { PlayerMeta } from "@/stores/player/slices/source";
|
||||||
|
import { usePlayerStore } from "@/stores/player/store";
|
||||||
|
|
||||||
|
function getSegmentText(
|
||||||
|
type: "intro" | "recap" | "credits",
|
||||||
|
t: (key: string) => string,
|
||||||
|
): string {
|
||||||
|
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 t("player.skipTime.intro");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldShowSkipButton(
|
||||||
|
currentTime: number,
|
||||||
|
segment: SegmentData | null,
|
||||||
|
): "always" | "hover" | "none" {
|
||||||
|
if (!segment) return "none";
|
||||||
|
|
||||||
|
// Convert current time to milliseconds for comparison
|
||||||
|
const currentTimeMs = currentTime * 1000;
|
||||||
|
|
||||||
|
// Handle start time (null means 0/start of video)
|
||||||
|
const startMs = segment.start_ms ?? 0;
|
||||||
|
|
||||||
|
// Handle end time (null means end of video, so we show until the end)
|
||||||
|
const endMs = segment.end_ms ?? Infinity;
|
||||||
|
|
||||||
|
// Check if current time is within the segment
|
||||||
|
if (currentTimeMs >= startMs && currentTimeMs <= endMs) {
|
||||||
|
// Show "always" for the first 10 seconds of the segment, then "hover"
|
||||||
|
const timeInSegment = currentTimeMs - startMs;
|
||||||
|
if (timeInSegment <= 10000) return "always"; // First 10 seconds
|
||||||
|
return "hover";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
function Button(props: {
|
||||||
|
className: string;
|
||||||
|
onClick?: () => void;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={classNames(
|
||||||
|
"font-bold rounded h-10 w-40 scale-95 hover:scale-100 transition-all duration-200",
|
||||||
|
props.className,
|
||||||
|
)}
|
||||||
|
type="button"
|
||||||
|
onClick={props.onClick}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SkipSegmentButton(props: {
|
||||||
|
controlsShowing: boolean;
|
||||||
|
segments: SegmentData[];
|
||||||
|
inControl: boolean;
|
||||||
|
onChangeMeta?: (meta: PlayerMeta) => void;
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const time = usePlayerStore((s) => s.progress.time);
|
||||||
|
const _duration = usePlayerStore((s) => s.progress.duration);
|
||||||
|
const status = usePlayerStore((s) => s.status);
|
||||||
|
const display = usePlayerStore((s) => s.display);
|
||||||
|
const meta = usePlayerStore((s) => s.meta);
|
||||||
|
const { addSkipEvent } = useSkipTracking(20);
|
||||||
|
|
||||||
|
// Check if we should show NextEpisodeButton instead of credits skip button
|
||||||
|
const shouldShowNextEpisodeInsteadOfCredits =
|
||||||
|
meta?.type === "show" &&
|
||||||
|
props.segments.some((segment) => {
|
||||||
|
if (segment.type !== "credits") return false;
|
||||||
|
// Show NextEpisodeButton if credits end at video end (null means end of video)
|
||||||
|
return segment.end_ms === null;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find segments that should be shown at the current time
|
||||||
|
const activeSegments = props.segments.filter((segment) => {
|
||||||
|
// Skip credits segments if we're showing NextEpisodeButton instead
|
||||||
|
if (segment.type === "credits" && shouldShowNextEpisodeInsteadOfCredits) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const showingState = shouldShowSkipButton(time, segment);
|
||||||
|
return showingState !== "none";
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleSkip = useCallback(
|
||||||
|
(segment: SegmentData) => {
|
||||||
|
if (!display) return;
|
||||||
|
|
||||||
|
const startTime = time;
|
||||||
|
// Skip to the end of the segment (or end of video if end_ms is null)
|
||||||
|
const targetTime = segment.end_ms ? segment.end_ms / 1000 : _duration;
|
||||||
|
const skipDuration = targetTime - startTime;
|
||||||
|
display.setTime(targetTime);
|
||||||
|
|
||||||
|
// Add manual skip event with high confidence (user explicitly clicked skip)
|
||||||
|
addSkipEvent({
|
||||||
|
startTime,
|
||||||
|
endTime: targetTime,
|
||||||
|
skipDuration,
|
||||||
|
confidence: 0.95, // High confidence for explicit user action
|
||||||
|
meta: meta
|
||||||
|
? {
|
||||||
|
title:
|
||||||
|
meta.type === "show" && meta.episode
|
||||||
|
? `${meta.title} - S${meta.season?.number || 0}E${meta.episode.number || 0}`
|
||||||
|
: meta.title,
|
||||||
|
type: meta.type === "movie" ? "Movie" : "TV Show",
|
||||||
|
tmdbId: meta.tmdbId,
|
||||||
|
seasonNumber: meta.season?.number,
|
||||||
|
episodeNumber: meta.episode?.number,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(`Skip ${segment.type} button used: ${skipDuration}s total`);
|
||||||
|
},
|
||||||
|
[display, time, _duration, addSkipEvent, meta],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Show NextEpisodeButton instead of credits skip button for TV shows when credits end at video end
|
||||||
|
if (shouldShowNextEpisodeInsteadOfCredits && props.inControl) {
|
||||||
|
return (
|
||||||
|
<NextEpisodeButton
|
||||||
|
controlsShowing={props.controlsShowing}
|
||||||
|
onChange={props.onChangeMeta}
|
||||||
|
inControl={props.inControl}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!props.inControl || activeSegments.length === 0) return null;
|
||||||
|
|
||||||
|
// If status is not playing, don't show buttons
|
||||||
|
if (status !== "playing") return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="absolute right-[calc(3rem+env(safe-area-inset-right))] bottom-0">
|
||||||
|
{activeSegments.map((segment, index) => {
|
||||||
|
const showingState = shouldShowSkipButton(time, segment);
|
||||||
|
const animation = showingState === "hover" ? "slide-up" : "fade";
|
||||||
|
|
||||||
|
let bottom = "bottom-[calc(6rem+env(safe-area-inset-bottom))]";
|
||||||
|
if (showingState === "always") {
|
||||||
|
bottom = props.controlsShowing
|
||||||
|
? bottom
|
||||||
|
: "bottom-[calc(3rem+env(safe-area-inset-bottom))]";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset multiple buttons vertically
|
||||||
|
const verticalOffset = index * 60; // 60px spacing between buttons
|
||||||
|
const adjustedBottom = bottom.replace(
|
||||||
|
/bottom-\[calc\(([^)]+)\)\]/,
|
||||||
|
`bottom-[calc($1 + ${verticalOffset}px)]`,
|
||||||
|
);
|
||||||
|
|
||||||
|
let show = false;
|
||||||
|
if (showingState === "always") show = true;
|
||||||
|
else if (showingState === "hover" && props.controlsShowing) show = true;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Transition
|
||||||
|
key={segment.type}
|
||||||
|
animation={animation}
|
||||||
|
show={show}
|
||||||
|
className="absolute right-0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={classNames([
|
||||||
|
"absolute bottom-0 right-0 transition-[bottom] duration-200 flex items-center space-x-3",
|
||||||
|
adjustedBottom,
|
||||||
|
])}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
onClick={() => handleSkip(segment)}
|
||||||
|
className="bg-buttons-primary hover:bg-buttons-primaryHover text-buttons-primaryText flex justify-center items-center"
|
||||||
|
>
|
||||||
|
<Icon className="text-xl mr-1" icon={Icons.SKIP_EPISODE} />
|
||||||
|
{getSegmentText(segment.type, t)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { SkipSegmentButton };
|
||||||
|
|
@ -27,17 +27,25 @@ export function useSkipTimeSource(): typeof currentSkipTimeSource {
|
||||||
return currentSkipTimeSource;
|
return currentSkipTimeSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SegmentData {
|
||||||
|
type: "intro" | "recap" | "credits";
|
||||||
|
start_ms: number | null;
|
||||||
|
end_ms: number | null;
|
||||||
|
confidence: number | null;
|
||||||
|
submission_count: number;
|
||||||
|
}
|
||||||
|
|
||||||
export function useSkipTime() {
|
export function useSkipTime() {
|
||||||
const { playerMeta: meta } = usePlayerMeta();
|
const { playerMeta: meta } = usePlayerMeta();
|
||||||
const [skiptime, setSkiptime] = useState<number | null>(null);
|
const [segments, setSegments] = useState<SegmentData[]>([]);
|
||||||
const febboxKey = usePreferencesStore((s) => s.febboxKey);
|
const febboxKey = usePreferencesStore((s) => s.febboxKey);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchTheIntroDBTime = async (): Promise<number | null> => {
|
const fetchTheIntroDBSegments = async (): Promise<SegmentData[]> => {
|
||||||
if (!meta?.tmdbId) return null;
|
if (!meta?.tmdbId) return [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let apiUrl = `${THE_INTRO_DB_BASE_URL}/intro?tmdb_id=${meta.tmdbId}`;
|
let apiUrl = `${THE_INTRO_DB_BASE_URL}/media?tmdb_id=${meta.tmdbId}`;
|
||||||
if (
|
if (
|
||||||
meta.type !== "movie" &&
|
meta.type !== "movie" &&
|
||||||
meta.season?.number &&
|
meta.season?.number &&
|
||||||
|
|
@ -48,15 +56,45 @@ export function useSkipTime() {
|
||||||
|
|
||||||
const data = await mwFetch(apiUrl);
|
const data = await mwFetch(apiUrl);
|
||||||
|
|
||||||
if (data && typeof data.end_ms === "number") {
|
const fetchedSegments: SegmentData[] = [];
|
||||||
// Convert milliseconds to seconds
|
|
||||||
return Math.floor(data.end_ms / 1000);
|
// Add intro segment if it has data
|
||||||
|
if (data?.intro && data.intro.submission_count > 0) {
|
||||||
|
fetchedSegments.push({
|
||||||
|
type: "intro",
|
||||||
|
start_ms: data.intro.start_ms,
|
||||||
|
end_ms: data.intro.end_ms,
|
||||||
|
confidence: data.intro.confidence,
|
||||||
|
submission_count: data.intro.submission_count,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
// Add recap segment if it has data
|
||||||
|
if (data?.recap && data.recap.submission_count > 0) {
|
||||||
|
fetchedSegments.push({
|
||||||
|
type: "recap",
|
||||||
|
start_ms: data.recap.start_ms,
|
||||||
|
end_ms: data.recap.end_ms,
|
||||||
|
confidence: data.recap.confidence,
|
||||||
|
submission_count: data.recap.submission_count,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add credits segment if it has data
|
||||||
|
if (data?.credits && data.credits.submission_count > 0) {
|
||||||
|
fetchedSegments.push({
|
||||||
|
type: "credits",
|
||||||
|
start_ms: data.credits.start_ms,
|
||||||
|
end_ms: data.credits.end_ms,
|
||||||
|
confidence: data.credits.confidence,
|
||||||
|
submission_count: data.credits.submission_count,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetchedSegments;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching TIDB time:", error);
|
console.error("Error fetching TIDB segments:", error);
|
||||||
return null;
|
return [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -187,22 +225,31 @@ export function useSkipTime() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchSkipTime = async (): Promise<void> => {
|
const fetchSkipTime = async (): Promise<void> => {
|
||||||
// Reset source
|
// Reset source and segments
|
||||||
currentSkipTimeSource = null;
|
currentSkipTimeSource = null;
|
||||||
|
setSegments([]);
|
||||||
|
|
||||||
// Try TheIntroDB API first (supports both movies and TV shows)
|
// Try TheIntroDB API first (supports both movies and TV shows with full segment data)
|
||||||
const theIntroDBTime = await fetchTheIntroDBTime();
|
const theIntroDBSegments = await fetchTheIntroDBSegments();
|
||||||
if (theIntroDBTime !== null) {
|
if (theIntroDBSegments.length > 0) {
|
||||||
currentSkipTimeSource = "theintrodb";
|
currentSkipTimeSource = "theintrodb";
|
||||||
setSkiptime(theIntroDBTime);
|
setSegments(theIntroDBSegments);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try QuickWatch API (TV shows only)
|
// Try QuickWatch API (TV shows only) - convert to intro segment
|
||||||
const quickWatchTime = await fetchQuickWatchTime();
|
const quickWatchTime = await fetchQuickWatchTime();
|
||||||
if (quickWatchTime !== null) {
|
if (quickWatchTime !== null) {
|
||||||
currentSkipTimeSource = "quickwatch";
|
currentSkipTimeSource = "quickwatch";
|
||||||
setSkiptime(quickWatchTime);
|
setSegments([
|
||||||
|
{
|
||||||
|
type: "intro",
|
||||||
|
start_ms: 0, // Assume starts at beginning
|
||||||
|
end_ms: quickWatchTime * 1000, // Convert seconds to milliseconds
|
||||||
|
confidence: null,
|
||||||
|
submission_count: 1,
|
||||||
|
},
|
||||||
|
]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,7 +259,15 @@ export function useSkipTime() {
|
||||||
const fedSkipsTime = await fetchFedSkipsTime();
|
const fedSkipsTime = await fetchFedSkipsTime();
|
||||||
if (fedSkipsTime !== null) {
|
if (fedSkipsTime !== null) {
|
||||||
currentSkipTimeSource = "fed-skips";
|
currentSkipTimeSource = "fed-skips";
|
||||||
setSkiptime(fedSkipsTime);
|
setSegments([
|
||||||
|
{
|
||||||
|
type: "intro",
|
||||||
|
start_ms: 0, // Assume starts at beginning
|
||||||
|
end_ms: fedSkipsTime * 1000, // Convert seconds to milliseconds
|
||||||
|
confidence: null,
|
||||||
|
submission_count: 1,
|
||||||
|
},
|
||||||
|
]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -221,8 +276,16 @@ export function useSkipTime() {
|
||||||
const introDBTime = await fetchIntroDBTime();
|
const introDBTime = await fetchIntroDBTime();
|
||||||
if (introDBTime !== null) {
|
if (introDBTime !== null) {
|
||||||
currentSkipTimeSource = "introdb";
|
currentSkipTimeSource = "introdb";
|
||||||
|
setSegments([
|
||||||
|
{
|
||||||
|
type: "intro",
|
||||||
|
start_ms: 0, // Assume starts at beginning
|
||||||
|
end_ms: introDBTime * 1000, // Convert seconds to milliseconds
|
||||||
|
confidence: null,
|
||||||
|
submission_count: 1,
|
||||||
|
},
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
setSkiptime(introDBTime);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchSkipTime();
|
fetchSkipTime();
|
||||||
|
|
@ -236,5 +299,5 @@ export function useSkipTime() {
|
||||||
febboxKey,
|
febboxKey,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return skiptime;
|
return segments;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { BrandPill } from "@/components/layout/BrandPill";
|
import { BrandPill } from "@/components/layout/BrandPill";
|
||||||
import { Player } from "@/components/player";
|
import { Player } from "@/components/player";
|
||||||
import { SkipIntroButton } from "@/components/player/atoms/SkipIntroButton";
|
import { SkipSegmentButton } from "@/components/player/atoms/SkipSegmentButton";
|
||||||
import { UnreleasedEpisodeOverlay } from "@/components/player/atoms/UnreleasedEpisodeOverlay";
|
import { UnreleasedEpisodeOverlay } from "@/components/player/atoms/UnreleasedEpisodeOverlay";
|
||||||
import { WatchPartyStatus } from "@/components/player/atoms/WatchPartyStatus";
|
import { WatchPartyStatus } from "@/components/player/atoms/WatchPartyStatus";
|
||||||
import { useShouldShowControls } from "@/components/player/hooks/useShouldShowControls";
|
import { useShouldShowControls } from "@/components/player/hooks/useShouldShowControls";
|
||||||
|
|
@ -74,7 +74,7 @@ export function PlayerPart(props: PlayerPartProps) {
|
||||||
}, 1000);
|
}, 1000);
|
||||||
};
|
};
|
||||||
|
|
||||||
const skiptime = useSkipTime();
|
const segments = useSkipTime();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Player.Container onLoad={props.onLoad} showingControls={showTargets}>
|
<Player.Container onLoad={props.onLoad} showingControls={showTargets}>
|
||||||
|
|
@ -246,10 +246,11 @@ export function PlayerPart(props: PlayerPartProps) {
|
||||||
inControl={inControl}
|
inControl={inControl}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SkipIntroButton
|
<SkipSegmentButton
|
||||||
controlsShowing={showTargets}
|
controlsShowing={showTargets}
|
||||||
skipTime={skiptime}
|
segments={segments}
|
||||||
inControl={inControl}
|
inControl={inControl}
|
||||||
|
onChangeMeta={props.onMetaChange}
|
||||||
/>
|
/>
|
||||||
</Player.Container>
|
</Player.Container>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue