usemetadata maximum depth limit reached potential fix

This commit is contained in:
tapframe 2025-09-10 15:06:40 +05:30
parent 43f6f056c0
commit ccde944bfa
2 changed files with 121 additions and 68 deletions

View file

@ -723,6 +723,13 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
const shimmerOpacity = useSharedValue(0.3);
const trailerOpacity = useSharedValue(0);
const thumbnailOpacity = useSharedValue(1);
// Scroll-based pause/resume control
const pausedByScrollSV = useSharedValue(0);
const scrollGuardEnabledSV = useSharedValue(0);
const isPlayingSV = useSharedValue(0);
// Guards to avoid repeated auto-starts
const startedOnFocusRef = useRef(false);
const startedOnReadyRef = useRef(false);
// Animation values for trailer unmute effects
const actionButtonsOpacity = useSharedValue(1);
@ -753,14 +760,18 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
// Smooth transition: fade out thumbnail, fade in trailer
thumbnailOpacity.value = withTiming(0, { duration: 500 });
trailerOpacity.value = withTiming(1, { duration: 500 });
// Enable scroll guard after a brief delay to avoid immediate pause on entry
scrollGuardEnabledSV.value = 0;
setTimeout(() => { scrollGuardEnabledSV.value = 1; }, 1000);
}, [thumbnailOpacity, trailerOpacity, trailerPreloaded]);
// Ensure trailer state is properly synchronized when trailer becomes ready
// Auto-start trailer when ready on initial entry if enabled
useEffect(() => {
if (trailerReady && settings?.showTrailers && !globalTrailerPlaying) {
// Only start trailer if it's the initial load, not when returning from other screens
// This prevents auto-starting when returning from StreamsScreen
logger.info('HeroSection', 'Trailer ready but not playing - not auto-starting to prevent unwanted playback');
if (trailerReady && settings?.showTrailers && !globalTrailerPlaying && !startedOnReadyRef.current) {
startedOnReadyRef.current = true;
logger.info('HeroSection', 'Trailer ready - auto-starting playback');
setTrailerPlaying(true);
isPlayingSV.value = 1;
}
}, [trailerReady, settings?.showTrailers, globalTrailerPlaying, setTrailerPlaying]);
@ -1036,25 +1047,61 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
useCallback(() => {
// Screen is focused - only resume trailer if it was previously playing and got interrupted
logger.info('HeroSection', 'Screen focused');
// Don't automatically resume trailer when returning from other screens
// This prevents the trailer from starting when returning from StreamsScreen
// The trailer should only resume if the user explicitly wants it to play
// If trailers are enabled and not playing, start playback (unless scrolled past resume threshold)
if (settings?.showTrailers) {
setTimeout(() => {
try {
const y = (scrollY as any).value || 0;
const resumeThreshold = heroHeight.value * 0.4;
if (y < resumeThreshold && !startedOnFocusRef.current && isPlayingSV.value === 0) {
setTrailerPlaying(true);
isPlayingSV.value = 1;
startedOnFocusRef.current = true;
}
} catch (_e) {
if (!startedOnFocusRef.current && isPlayingSV.value === 0) {
setTrailerPlaying(true);
isPlayingSV.value = 1;
startedOnFocusRef.current = true;
}
}
}, 50);
}
return () => {
// Stop trailer when leaving this screen to prevent background playback/heat
logger.info('HeroSection', 'Screen unfocused - stopping trailer playback');
setTrailerPlaying(false);
isPlayingSV.value = 0;
startedOnFocusRef.current = false;
startedOnReadyRef.current = false;
};
}, [setTrailerPlaying])
}, [setTrailerPlaying, settings?.showTrailers])
);
// Pause trailer when the hero is scrolled substantially off-screen
// Mirror playing state to shared value to use inside worklets
useEffect(() => {
isPlayingSV.value = globalTrailerPlaying ? 1 : 0;
}, [globalTrailerPlaying]);
// Pause/resume trailer based on scroll with hysteresis and guard
useDerivedValue(() => {
'worklet';
try {
const threshold = heroHeight.value * 0.6;
if (scrollY.value > threshold) {
if (!scrollGuardEnabledSV.value) return;
const pauseThreshold = heroHeight.value * 0.7; // pause when beyond 70%
const resumeThreshold = heroHeight.value * 0.4; // resume when back within 40%
const y = scrollY.value;
if (y > pauseThreshold && isPlayingSV.value === 1 && pausedByScrollSV.value === 0) {
pausedByScrollSV.value = 1;
runOnJS(setTrailerPlaying)(false);
isPlayingSV.value = 0;
} else if (y < resumeThreshold && pausedByScrollSV.value === 1) {
pausedByScrollSV.value = 0;
runOnJS(setTrailerPlaying)(true);
isPlayingSV.value = 1;
}
} catch (e) {
// no-op

View file

@ -1,4 +1,4 @@
import { useState, useEffect, useCallback } from 'react';
import { useState, useEffect, useCallback, useRef } from 'react';
import { StreamingContent } from '../services/catalogService';
import { catalogService } from '../services/catalogService';
import { stremioService } from '../services/stremioService';
@ -134,6 +134,8 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
const [availableStreams, setAvailableStreams] = useState<{ [sourceType: string]: Stream }>({});
const [scraperStatuses, setScraperStatuses] = useState<ScraperStatus[]>([]);
const [activeFetchingScrapers, setActiveFetchingScrapers] = useState<string[]>([]);
// Prevent re-initializing season selection repeatedly for the same series
const initializedSeasonRef = useRef(false);
// Add hook for persistent seasons
const { getSeason, saveSeason } = usePersistentSeasons();
@ -591,12 +593,21 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
setGroupedEpisodes(groupedAddonEpisodes);
// Set the first available season
const seasons = Object.keys(groupedAddonEpisodes).map(Number);
const firstSeason = Math.min(...seasons);
logger.log(`📺 Setting season ${firstSeason} as selected (${groupedAddonEpisodes[firstSeason]?.length || 0} episodes)`);
setSelectedSeason(firstSeason);
setEpisodes(groupedAddonEpisodes[firstSeason] || []);
// Determine initial season only once per series
const seasons = Object.keys(groupedAddonEpisodes).map(Number);
const firstSeason = Math.min(...seasons);
if (!initializedSeasonRef.current) {
const nextSeason = firstSeason;
if (selectedSeason !== nextSeason) {
logger.log(`📺 Setting season ${nextSeason} as selected (${groupedAddonEpisodes[nextSeason]?.length || 0} episodes)`);
setSelectedSeason(nextSeason);
}
setEpisodes(groupedAddonEpisodes[nextSeason] || []);
initializedSeasonRef.current = true;
} else {
// Keep current selection; refresh episode list for selected season
setEpisodes(groupedAddonEpisodes[selectedSeason] || []);
}
// Try to get TMDB ID for additional metadata (cast, etc.) but don't override episodes
const tmdbIdResult = await tmdbService.findTMDBIdByIMDB(id);
@ -640,61 +651,55 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
// Get the first available season as fallback
const firstSeason = Math.min(...Object.keys(allEpisodes).map(Number));
// Check for watch progress to auto-select season
let selectedSeasonNumber = firstSeason;
try {
// Check watch progress for auto-season selection
const allProgress = await storageService.getAllWatchProgress();
// Find the most recently watched episode for this series
let mostRecentEpisodeId = '';
let mostRecentTimestamp = 0;
Object.entries(allProgress).forEach(([key, progress]) => {
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 (!initializedSeasonRef.current) {
// Check for watch progress to auto-select season
let selectedSeasonNumber = firstSeason;
try {
const allProgress = await storageService.getAllWatchProgress();
let mostRecentEpisodeId = '';
let mostRecentTimestamp = 0;
Object.entries(allProgress).forEach(([key, progress]) => {
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}`);
});
if (mostRecentEpisodeId) {
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 {
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 {
// 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}`);
}
selectedSeasonNumber = getSeason(id, firstSeason);
logger.log(`[useMetadata] No watch progress found, using persistent season ${selectedSeasonNumber}`);
}
} else {
// No watch progress found, use persistent storage as fallback
} catch (error) {
logger.error('[useMetadata] Error checking watch progress for season selection:', error);
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);
if (selectedSeason !== selectedSeasonNumber) {
setSelectedSeason(selectedSeasonNumber);
}
setEpisodes(transformedEpisodes[selectedSeasonNumber] || []);
initializedSeasonRef.current = true;
} else {
// Keep existing selection stable and only refresh episode list for it
setEpisodes(transformedEpisodes[selectedSeason] || []);
}
// Set the selected season
setSelectedSeason(selectedSeasonNumber);
// Set episodes for the selected season
setEpisodes(transformedEpisodes[selectedSeasonNumber] || []);
}
} catch (error) {
if (__DEV__) console.error('Failed to load episodes:', error);
@ -1082,6 +1087,7 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
// Reset load attempts when id or type changes
useEffect(() => {
setLoadAttempts(0);
initializedSeasonRef.current = false;
}, [id, type]);
// Auto-retry on error with delay