From c0a63b3c531e2d8be4f41a6c8aca50e9f3d98874 Mon Sep 17 00:00:00 2001 From: tapframe Date: Fri, 20 Jun 2025 01:58:48 +0530 Subject: [PATCH] Refactor watch progress management and enhance episode scrolling functionality This update improves the watch progress management by incorporating last updated timestamps for episodes, allowing for more accurate tracking of the most recently watched content. The SeriesContent component now automatically selects the season based on the most recent watch progress, enhancing user experience. Additionally, the scrolling functionality to the most recently watched episode has been implemented, ensuring users can easily continue watching from where they left off. UI adjustments in the ContinueWatchingSection and HomeScreen have also been made for better layout consistency. --- .../home/ContinueWatchingSection.tsx | 20 ++--- src/components/metadata/SeriesContent.tsx | 79 ++++++++++++++++++- src/hooks/useMetadata.ts | 58 ++++++++++++-- src/hooks/useWatchProgress.ts | 49 +++++------- src/screens/HomeScreen.tsx | 6 +- 5 files changed, 161 insertions(+), 51 deletions(-) diff --git a/src/components/home/ContinueWatchingSection.tsx b/src/components/home/ContinueWatchingSection.tsx index 7763914d..2539ed59 100644 --- a/src/components/home/ContinueWatchingSection.tsx +++ b/src/components/home/ContinueWatchingSection.tsx @@ -393,16 +393,16 @@ const ContinueWatchingSection = React.forwardRef((props, re Season {item.season} - - {item.episodeTitle && ( + + {item.episodeTitle && ( - {item.episodeTitle} - - )} - + {item.episodeTitle} + + )} + ); } else { return ( @@ -416,15 +416,15 @@ const ContinueWatchingSection = React.forwardRef((props, re {/* Progress Bar */} - + ]} + /> {Math.round(item.progress)}% watched diff --git a/src/components/metadata/SeriesContent.tsx b/src/components/metadata/SeriesContent.tsx index 37e0cf7a..82086fd5 100644 --- a/src/components/metadata/SeriesContent.tsx +++ b/src/components/metadata/SeriesContent.tsx @@ -40,16 +40,17 @@ export const SeriesContent: React.FC = ({ const { width } = useWindowDimensions(); const isTablet = width > 768; const isDarkMode = useColorScheme() === 'dark'; - const [episodeProgress, setEpisodeProgress] = useState<{ [key: string]: { currentTime: number; duration: number } }>({}); + const [episodeProgress, setEpisodeProgress] = useState<{ [key: string]: { currentTime: number; duration: number; lastUpdated: number } }>({}); - // Add ref for the season selector ScrollView + // Add refs for the scroll views const seasonScrollViewRef = useRef(null); + const episodeScrollViewRef = useRef(null); const loadEpisodesProgress = async () => { if (!metadata?.id) return; const allProgress = await storageService.getAllWatchProgress(); - const progress: { [key: string]: { currentTime: number; duration: number } } = {}; + const progress: { [key: string]: { currentTime: number; duration: number; lastUpdated: number } } = {}; episodes.forEach(episode => { const episodeId = episode.stremioId || `${metadata.id}:${episode.season_number}:${episode.episode_number}`; @@ -57,7 +58,8 @@ export const SeriesContent: React.FC = ({ if (allProgress[key]) { progress[episodeId] = { currentTime: allProgress[key].currentTime, - duration: allProgress[key].duration + duration: allProgress[key].duration, + lastUpdated: allProgress[key].lastUpdated }; } }); @@ -65,6 +67,67 @@ export const SeriesContent: React.FC = ({ setEpisodeProgress(progress); }; + // Function to find and scroll to the most recently watched episode + const scrollToMostRecentEpisode = () => { + if (!metadata?.id || !episodeScrollViewRef.current || settings.episodeLayoutStyle !== 'horizontal') { + console.log('[SeriesContent] Scroll conditions not met:', { + hasMetadataId: !!metadata?.id, + hasScrollRef: !!episodeScrollViewRef.current, + isHorizontal: settings.episodeLayoutStyle === 'horizontal' + }); + return; + } + + const currentSeasonEpisodes = groupedEpisodes[selectedSeason] || []; + if (currentSeasonEpisodes.length === 0) { + console.log('[SeriesContent] No episodes in current season:', selectedSeason); + return; + } + + // Find the most recently watched episode in the current season + let mostRecentEpisodeIndex = -1; + let mostRecentTimestamp = 0; + let mostRecentEpisodeName = ''; + + currentSeasonEpisodes.forEach((episode, index) => { + const episodeId = episode.stremioId || `${metadata.id}:${episode.season_number}:${episode.episode_number}`; + const progress = episodeProgress[episodeId]; + + if (progress && progress.lastUpdated > mostRecentTimestamp && progress.currentTime > 0) { + mostRecentTimestamp = progress.lastUpdated; + mostRecentEpisodeIndex = index; + mostRecentEpisodeName = episode.name; + } + }); + + console.log('[SeriesContent] Episode scroll analysis:', { + totalEpisodes: currentSeasonEpisodes.length, + mostRecentIndex: mostRecentEpisodeIndex, + mostRecentEpisode: mostRecentEpisodeName, + selectedSeason + }); + + // Scroll to the most recently watched episode if found + if (mostRecentEpisodeIndex >= 0) { + const cardWidth = isTablet ? width * 0.4 + 16 : width * 0.85 + 16; + const scrollPosition = mostRecentEpisodeIndex * cardWidth; + + console.log('[SeriesContent] Scrolling to episode:', { + index: mostRecentEpisodeIndex, + cardWidth, + scrollPosition, + episodeName: mostRecentEpisodeName + }); + + setTimeout(() => { + episodeScrollViewRef.current?.scrollTo({ + x: scrollPosition, + animated: true + }); + }, 500); // Delay to ensure the season has loaded + } + }; + // Initial load of watch progress useEffect(() => { loadEpisodesProgress(); @@ -96,6 +159,13 @@ export const SeriesContent: React.FC = ({ } }, [selectedSeason, groupedEpisodes]); + // Add effect to scroll to most recently watched episode when season changes or progress loads + useEffect(() => { + if (Object.keys(episodeProgress).length > 0 && selectedSeason) { + scrollToMostRecentEpisode(); + } + }, [selectedSeason, episodeProgress, settings.episodeLayoutStyle, groupedEpisodes]); + if (loadingSeasons) { return ( @@ -480,6 +550,7 @@ export const SeriesContent: React.FC = ({ {settings.episodeLayoutStyle === 'horizontal' ? ( // Horizontal Layout (Netflix-style) { + if (key.includes(`series:${id}:`)) { + const episodeId = key.split(`series:${id}:`)[1]; + if (progress.lastUpdated > mostRecentTimestamp && progress.currentTime > 0) { + mostRecentTimestamp = progress.lastUpdated; + mostRecentEpisodeId = episodeId; + } + } + }); + + if (mostRecentEpisodeId) { + // Parse season number from episode ID + const parts = mostRecentEpisodeId.split(':'); + if (parts.length === 3) { + const watchProgressSeason = parseInt(parts[1], 10); + if (transformedEpisodes[watchProgressSeason]) { + selectedSeasonNumber = watchProgressSeason; + logger.log(`[useMetadata] Auto-selected season ${selectedSeasonNumber} based on most recent watch progress for ${mostRecentEpisodeId}`); + } + } else { + // Try to find episode by stremioId to get season + const allEpisodesList = Object.values(transformedEpisodes).flat(); + const episode = allEpisodesList.find(ep => ep.stremioId === mostRecentEpisodeId); + if (episode) { + selectedSeasonNumber = episode.season_number; + logger.log(`[useMetadata] Auto-selected season ${selectedSeasonNumber} based on most recent watch progress for episode with stremioId ${mostRecentEpisodeId}`); + } + } + } else { + // No watch progress found, use persistent storage as fallback + selectedSeasonNumber = getSeason(id, firstSeason); + logger.log(`[useMetadata] No watch progress found, using persistent season ${selectedSeasonNumber}`); + } + } catch (error) { + logger.error('[useMetadata] Error checking watch progress for season selection:', error); + // Fall back to persistent storage + selectedSeasonNumber = getSeason(id, firstSeason); + } + + // Set the selected season + setSelectedSeason(selectedSeasonNumber); // Set episodes for the selected season - setEpisodes(transformedEpisodes[persistedSeason] || []); + setEpisodes(transformedEpisodes[selectedSeasonNumber] || []); } } catch (error) { console.error('Failed to load episodes:', error); diff --git a/src/hooks/useWatchProgress.ts b/src/hooks/useWatchProgress.ts index b17e4345..ff47bf49 100644 --- a/src/hooks/useWatchProgress.ts +++ b/src/hooks/useWatchProgress.ts @@ -103,36 +103,27 @@ export const useWatchProgress = ( setWatchProgress(null); } } else { - // Find the first unfinished episode - const unfinishedEpisode = episodes.find(ep => { - const epId = ep.stremioId || `${id}:${ep.season_number}:${ep.episode_number}`; - const progress = seriesProgresses.find(p => p.episodeId === epId); - if (!progress) return true; - const percent = (progress.progress.currentTime / progress.progress.duration) * 100; - return percent < 95; - }); - - if (unfinishedEpisode) { - const epId = unfinishedEpisode.stremioId || - `${id}:${unfinishedEpisode.season_number}:${unfinishedEpisode.episode_number}`; - const progress = await storageService.getWatchProgress(id, type, epId); - if (progress) { - setWatchProgress({ - ...progress, - episodeId: epId, - traktSynced: progress.traktSynced, - traktProgress: progress.traktProgress - }); - } else { - setWatchProgress({ - currentTime: 0, - duration: 0, - lastUpdated: Date.now(), - episodeId: epId, - traktSynced: false - }); - } + // FIXED: Find the most recently watched episode instead of first unfinished + // Sort by lastUpdated timestamp (most recent first) + const sortedProgresses = seriesProgresses.sort((a, b) => + b.progress.lastUpdated - a.progress.lastUpdated + ); + + if (sortedProgresses.length > 0) { + // Use the most recently watched episode + const mostRecentProgress = sortedProgresses[0]; + const progress = mostRecentProgress.progress; + + logger.log(`[useWatchProgress] Using most recent progress for ${mostRecentProgress.episodeId}, updated at ${new Date(progress.lastUpdated).toLocaleString()}`); + + setWatchProgress({ + ...progress, + episodeId: mostRecentProgress.episodeId, + traktSynced: progress.traktSynced, + traktProgress: progress.traktProgress + }); } else { + // No watched episodes found setWatchProgress(null); } } diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx index 52efa750..9a248eeb 100644 --- a/src/screens/HomeScreen.tsx +++ b/src/screens/HomeScreen.tsx @@ -533,9 +533,9 @@ const HomeScreen = () => { console.log('[HomeScreen] Refreshing continue watching...'); if (continueWatchingRef.current) { try { - const hasContent = await continueWatchingRef.current.refresh(); + const hasContent = await continueWatchingRef.current.refresh(); console.log(`[HomeScreen] Continue watching has content: ${hasContent}`); - setHasContinueWatching(hasContent); + setHasContinueWatching(hasContent); // Debug: Let's check what's in storage const allProgress = await storageService.getAllWatchProgress(); @@ -667,7 +667,7 @@ const HomeScreen = () => { - + {catalogs.length > 0 ? ( catalogs.map((catalog, index) => (