mirror of
https://github.com/p-stream/p-stream.git
synced 2026-03-11 17:55:33 +00:00
watchparty goes to next episode with host
This commit is contained in:
parent
bf530902cc
commit
83840594a6
2 changed files with 136 additions and 17 deletions
|
|
@ -1,5 +1,6 @@
|
|||
import { t } from "i18next";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { getRoomStatuses, sendPlayerStatus } from "@/backend/player/status";
|
||||
import { usePlayerStatusPolling } from "@/components/player/hooks/usePlayerStatusPolling";
|
||||
|
|
@ -24,6 +25,8 @@ export function WatchPartyReporter() {
|
|||
const lastReportTime = useRef<number>(0);
|
||||
const lastReportedStateRef = useRef<string>("");
|
||||
const contentValidatedRef = useRef<boolean>(false);
|
||||
const hostEpisodeRef = useRef<{ seasonId?: number; episodeId?: number }>({});
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Auth data
|
||||
const account = useAuthStore((s) => s.account);
|
||||
|
|
@ -45,6 +48,7 @@ export function WatchPartyReporter() {
|
|||
useEffect(() => {
|
||||
if (!watchPartyEnabled) {
|
||||
contentValidatedRef.current = false;
|
||||
hostEpisodeRef.current = {};
|
||||
}
|
||||
}, [watchPartyEnabled]);
|
||||
|
||||
|
|
@ -93,6 +97,14 @@ export function WatchPartyReporter() {
|
|||
? parseInt(meta.episode.tmdbId, 10)
|
||||
: undefined;
|
||||
|
||||
// Initialize host episode tracking
|
||||
if (hostSeasonId && hostEpisodeId) {
|
||||
hostEpisodeRef.current = {
|
||||
seasonId: hostSeasonId,
|
||||
episodeId: hostEpisodeId,
|
||||
};
|
||||
}
|
||||
|
||||
// Validate episode match (if host has this info)
|
||||
if (
|
||||
(hostSeasonId &&
|
||||
|
|
@ -144,6 +156,112 @@ export function WatchPartyReporter() {
|
|||
disable,
|
||||
]);
|
||||
|
||||
// Monitor for episode changes from host and auto-navigate guests
|
||||
useEffect(() => {
|
||||
if (
|
||||
!watchPartyEnabled ||
|
||||
!roomCode ||
|
||||
isHost ||
|
||||
!meta?.tmdbId ||
|
||||
meta.type !== "show"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const checkForEpisodeChange = async () => {
|
||||
try {
|
||||
const roomData = await getRoomStatuses(backendUrl, account, roomCode);
|
||||
const users = Object.values(roomData.users).flat();
|
||||
const hostUser = users.find((user) => user.isHost);
|
||||
|
||||
if (!hostUser || hostUser.content.type !== "TV Show") return;
|
||||
|
||||
const hostSeasonId = hostUser.content.seasonId;
|
||||
const hostEpisodeId = hostUser.content.episodeId;
|
||||
|
||||
// Initialize host episode ref on first check
|
||||
if (
|
||||
!hostEpisodeRef.current.seasonId &&
|
||||
!hostEpisodeRef.current.episodeId
|
||||
) {
|
||||
hostEpisodeRef.current = {
|
||||
seasonId: hostSeasonId,
|
||||
episodeId: hostEpisodeId,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if host has changed episodes
|
||||
const hasHostChangedEpisode =
|
||||
hostSeasonId !== hostEpisodeRef.current.seasonId ||
|
||||
hostEpisodeId !== hostEpisodeRef.current.episodeId;
|
||||
|
||||
if (hasHostChangedEpisode && hostSeasonId && hostEpisodeId) {
|
||||
// Update our reference
|
||||
hostEpisodeRef.current = {
|
||||
seasonId: hostSeasonId,
|
||||
episodeId: hostEpisodeId,
|
||||
};
|
||||
|
||||
// Check if we're already on the correct episode
|
||||
const currentSeasonId = meta.season?.tmdbId
|
||||
? parseInt(meta.season.tmdbId, 10)
|
||||
: undefined;
|
||||
const currentEpisodeId = meta.episode?.tmdbId
|
||||
? parseInt(meta.episode.tmdbId, 10)
|
||||
: undefined;
|
||||
|
||||
if (
|
||||
currentSeasonId === hostSeasonId &&
|
||||
currentEpisodeId === hostEpisodeId
|
||||
) {
|
||||
// Already on the correct episode
|
||||
return;
|
||||
}
|
||||
|
||||
// Navigate to the new episode
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("Host changed episode, following to new episode:", {
|
||||
seasonId: hostSeasonId,
|
||||
episodeId: hostEpisodeId,
|
||||
});
|
||||
|
||||
const url = new URL(
|
||||
`/media/tmdb-tv-${meta.tmdbId}/${hostSeasonId}/${hostEpisodeId}`,
|
||||
window.location.origin,
|
||||
);
|
||||
url.searchParams.set("watchparty", roomCode);
|
||||
|
||||
// Reset content validation so it re-validates on the new episode
|
||||
contentValidatedRef.current = false;
|
||||
|
||||
navigate(url.pathname + url.search);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to check for episode change:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// Check every 3 seconds for episode changes
|
||||
const interval = setInterval(checkForEpisodeChange, 3000);
|
||||
|
||||
// Initial check
|
||||
checkForEpisodeChange();
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [
|
||||
watchPartyEnabled,
|
||||
roomCode,
|
||||
isHost,
|
||||
meta?.tmdbId,
|
||||
meta?.season?.tmdbId,
|
||||
meta?.episode?.tmdbId,
|
||||
meta?.type,
|
||||
backendUrl,
|
||||
account,
|
||||
navigate,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
// Skip if watch party is not enabled
|
||||
if (
|
||||
|
|
|
|||
|
|
@ -11,41 +11,42 @@ export function WatchPartyResetter() {
|
|||
const meta = usePlayerStore((s) => s.meta);
|
||||
const { disable } = useWatchPartyStore();
|
||||
|
||||
// Store the current meta to track changes
|
||||
const previousMetaRef = useRef<string | null>(null);
|
||||
// Store the current base media to track changes
|
||||
const previousBaseMediaRef = useRef<string | null>(null);
|
||||
|
||||
// Memoize the metaId calculation
|
||||
const metaId = useMemo(() => {
|
||||
// Memoize the base media ID (without episode details for shows)
|
||||
// This allows episode changes within the same show to keep the room active
|
||||
const baseMediaId = useMemo(() => {
|
||||
if (!meta) return null;
|
||||
|
||||
return meta.type === "show"
|
||||
? `${meta.type}-${meta.tmdbId}-s${meta.season?.tmdbId || "0"}-e${meta.episode?.tmdbId || "0"}`
|
||||
: `${meta.type}-${meta.tmdbId}`;
|
||||
// For shows, only track the show ID, not the episode
|
||||
// This allows episode navigation within the same show
|
||||
return `${meta.type}-${meta.tmdbId}`;
|
||||
}, [meta]);
|
||||
|
||||
useEffect(() => {
|
||||
// If meta exists but has changed, reset watch party
|
||||
// If base media has changed (different show/movie), reset watch party
|
||||
if (
|
||||
metaId &&
|
||||
previousMetaRef.current &&
|
||||
metaId !== previousMetaRef.current
|
||||
baseMediaId &&
|
||||
previousBaseMediaRef.current &&
|
||||
baseMediaId !== previousBaseMediaRef.current
|
||||
) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("Media changed, disabling watch party:", {
|
||||
previous: previousMetaRef.current,
|
||||
current: metaId,
|
||||
console.log("Base media changed, disabling watch party:", {
|
||||
previous: previousBaseMediaRef.current,
|
||||
current: baseMediaId,
|
||||
});
|
||||
disable();
|
||||
}
|
||||
|
||||
// Update the ref with current meta
|
||||
previousMetaRef.current = metaId;
|
||||
// Update the ref with current base media
|
||||
previousBaseMediaRef.current = baseMediaId;
|
||||
|
||||
// Also reset when component unmounts (player exited)
|
||||
return () => {
|
||||
disable();
|
||||
};
|
||||
}, [metaId, disable]);
|
||||
}, [baseMediaId, disable]);
|
||||
|
||||
return null; // This component doesn't render anything
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue