trailer scroll fix

This commit is contained in:
tapframe 2025-10-24 20:07:26 +05:30
parent b81435be29
commit 665ff06ad1
4 changed files with 113 additions and 200 deletions

View file

@ -1106,6 +1106,9 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
// Guards to avoid repeated auto-starts
const startedOnFocusRef = useRef(false);
const startedOnReadyRef = useRef(false);
// Debounced pause/resume flag to avoid blocking scroll
const pendingPauseResumeSV = useSharedValue(0); // 0 = no action, 1 = pause, 2 = resume
const pauseResumeTimerRef = useRef<any>(null);
// Animation values for trailer unmute effects
const actionButtonsOpacity = useSharedValue(1);
@ -1672,6 +1675,7 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
}, [isFocused, setTrailerPlaying]);
// Ultra-optimized scroll-based pause/resume with cached calculations
// This worklet only sets flags - actual pause/resume happens asynchronously to avoid blocking scroll
useDerivedValue(() => {
'worklet';
try {
@ -1684,21 +1688,49 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
const isPlaying = isPlayingSV.value === 1;
const isPausedByScroll = pausedByScrollSV.value === 1;
// Optimized pause/resume logic with minimal branching
// Set flags for pause/resume - don't execute immediately to keep scroll smooth
if (y > pauseThreshold && isPlaying && !isPausedByScroll) {
pausedByScrollSV.value = 1;
runOnJS(setTrailerPlaying)(false);
isPlayingSV.value = 0;
pendingPauseResumeSV.value = 1; // Request pause
} else if (y < resumeThreshold && isPausedByScroll) {
pausedByScrollSV.value = 0;
runOnJS(setTrailerPlaying)(true);
isPlayingSV.value = 1;
pendingPauseResumeSV.value = 2; // Request resume
}
} catch (e) {
// Silent error handling for performance
}
});
// Debounced pause/resume effect - executes asynchronously to keep scroll smooth
useEffect(() => {
const checkPendingAction = () => {
const pendingAction = pendingPauseResumeSV.value;
if (pendingAction === 1) {
// Execute pause
pausedByScrollSV.value = 1;
setTrailerPlaying(false);
isPlayingSV.value = 0;
pendingPauseResumeSV.value = 0; // Clear flag
} else if (pendingAction === 2) {
// Execute resume
pausedByScrollSV.value = 0;
setTrailerPlaying(true);
isPlayingSV.value = 1;
pendingPauseResumeSV.value = 0; // Clear flag
}
};
// Set up a recurring check with small delay to avoid blocking scroll
const intervalId = setInterval(checkPendingAction, 100);
return () => {
clearInterval(intervalId);
if (pauseResumeTimerRef.current) {
clearTimeout(pauseResumeTimerRef.current);
pauseResumeTimerRef.current = null;
}
};
}, [setTrailerPlaying, pendingPauseResumeSV, pausedByScrollSV, isPlayingSV]);
// Memory management and cleanup
useEffect(() => {
return () => {

View file

@ -132,16 +132,14 @@ export function useFeaturedContent() {
genres: (item as any).genres,
inLibrary: Boolean((item as any).inLibrary),
};
try {
// If enrichment is disabled, use addon logo if available
if (!settings.enrichMetadataWithTMDB) {
if (base.logo && !isTmdbUrl(base.logo)) {
return base;
}
return { ...base, logo: undefined };
// When enrichment is OFF, keep addon logo or undefined
return { ...base, logo: base.logo || undefined };
}
// Only proceed with TMDB enrichment if enrichment is enabled
// When enrichment is ON, fetch from TMDB with language preference
const rawId = String(item.id);
const isTmdb = rawId.startsWith('tmdb:');
const isImdb = rawId.startsWith('tt');
@ -154,17 +152,15 @@ export function useFeaturedContent() {
const found = await tmdbService.findTMDBIdByIMDB(imdbId);
tmdbId = found ? String(found) : null;
}
if (!tmdbId && !imdbId) return base;
// Try TMDB if we have a TMDB id
if (tmdbId) {
const logoUrl = await tmdbService.getContentLogo(item.type === 'series' ? 'tv' : 'movie', tmdbId as string, preferredLanguage);
if (logoUrl) {
return { ...base, logo: logoUrl };
}
return { ...base, logo: logoUrl || undefined }; // TMDB logo or undefined (no addon fallback)
}
return base;
return { ...base, logo: undefined }; // No TMDB ID means no logo
} catch (error) {
return base;
return { ...base, logo: undefined }; // Error means no logo
}
};

View file

@ -599,6 +599,17 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
logger.error('Failed to fetch credits for movie:', error);
}
// Fetch movie logo from TMDB
try {
const preferredLanguage = settings.tmdbLanguagePreference || 'en';
const logoUrl = await tmdbService.getContentLogo('movie', tmdbId, preferredLanguage);
formattedMovie.logo = logoUrl || undefined; // TMDB logo or undefined (no addon fallback)
if (__DEV__) logger.log(`Successfully fetched logo for movie ${tmdbId} from TMDB`);
} catch (error) {
logger.error('Failed to fetch logo from TMDB:', error);
formattedMovie.logo = undefined; // Error means no logo
}
setMetadata(formattedMovie);
cacheService.setMetadata(id, type, formattedMovie);
const isInLib = catalogService.getLibraryItems().some(item => item.id === id);
@ -664,14 +675,13 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
// Fetch TV show logo from TMDB
try {
const logoUrl = await tmdbService.getTvShowImages(tmdbId);
if (logoUrl) {
formattedShow.logo = logoUrl;
if (__DEV__) logger.log(`Successfully fetched logo for TV show ${tmdbId} from TMDB`);
}
const preferredLanguage = settings.tmdbLanguagePreference || 'en';
const logoUrl = await tmdbService.getContentLogo('tv', tmdbId, preferredLanguage);
formattedShow.logo = logoUrl || undefined; // TMDB logo or undefined (no addon fallback)
if (__DEV__) logger.log(`Successfully fetched logo for TV show ${tmdbId} from TMDB`);
} catch (error) {
logger.error('Failed to fetch logo from TMDB:', error);
// Continue with execution, logo is optional
formattedShow.logo = undefined; // Error means no logo
}
setMetadata(formattedShow);
@ -878,6 +888,55 @@ export const useMetadata = ({ id, type, addonId }: UseMetadataProps): UseMetadat
if (__DEV__) console.log('[useMetadata] failed to merge localized TMDB text', e);
}
// Centralized logo fetching logic
try {
if (settings.enrichMetadataWithTMDB) {
// Only use TMDB logos when enrichment is ON
const tmdbService = TMDBService.getInstance();
const preferredLanguage = settings.tmdbLanguagePreference || 'en';
const contentType = type === 'series' ? 'tv' : 'movie';
// Get TMDB ID
let tmdbIdForLogo = null;
if (tmdbId) {
tmdbIdForLogo = String(tmdbId);
} else if (finalMetadata.imdb_id) {
const foundId = await tmdbService.findTMDBIdByIMDB(finalMetadata.imdb_id);
tmdbIdForLogo = foundId ? String(foundId) : null;
}
if (tmdbIdForLogo) {
const logoUrl = await tmdbService.getContentLogo(contentType, tmdbIdForLogo, preferredLanguage);
finalMetadata.logo = logoUrl || undefined; // TMDB logo or undefined (no addon fallback)
if (__DEV__) {
console.log('[useMetadata] Logo fetch result:', {
contentType,
tmdbIdForLogo,
preferredLanguage,
logoUrl: !!logoUrl,
enrichmentEnabled: true
});
}
} else {
finalMetadata.logo = undefined; // No TMDB ID means no logo
if (__DEV__) console.log('[useMetadata] No TMDB ID found for logo, will show text title');
}
} else {
// When enrichment is OFF, keep addon logo or undefined
finalMetadata.logo = finalMetadata.logo || undefined;
if (__DEV__) {
console.log('[useMetadata] TMDB enrichment disabled, using addon logo:', {
hasAddonLogo: !!finalMetadata.logo,
enrichmentEnabled: false
});
}
}
} catch (error) {
// Handle error silently, keep existing logo behavior
if (__DEV__) console.error('[useMetadata] Unexpected error in logo fetch:', error);
finalMetadata.logo = undefined;
}
// Commit final metadata once and cache it
// Clear banner field if TMDB enrichment is enabled to prevent flash
if (settings.enrichMetadataWithTMDB) {

View file

@ -62,13 +62,6 @@ export const useMetadataAssets = (
// Add source tracking to prevent mixing sources
const [bannerSource, setBannerSource] = useState<'tmdb' | 'metahub' | 'default' | null>(null);
// State for logo loading
const [logoLoadError, setLogoLoadError] = useState(false);
const logoFetchInProgress = useRef<boolean>(false);
const logoRefreshCounter = useRef<number>(0);
const MAX_LOGO_REFRESHES = 2;
const forcedLogoRefreshDone = useRef<boolean>(false);
// For TMDB ID tracking
const [foundTmdbId, setFoundTmdbId] = useState<string | null>(null);
@ -78,173 +71,8 @@ export const useMetadataAssets = (
setBannerImage(null);
setBannerSource(null);
forcedBannerRefreshDone.current = false;
forcedLogoRefreshDone.current = false;
logoRefreshCounter.current = 0;
// Mark that we need to refetch logo but DON'T clear it yet
// This prevents text from showing during the transition
logoFetchInProgress.current = false;
}, [settings.logoSourcePreference]);
// Original reset logo load error effect
useEffect(() => {
setLogoLoadError(false);
}, [metadata?.logo]);
// Optimized logo fetching
useEffect(() => {
const logoPreference = settings.logoSourcePreference || 'tmdb';
if (__DEV__) {
console.log('[useMetadataAssets] Logo fetch triggered:', {
id,
type,
logoPreference,
hasImdbId: !!imdbId,
tmdbEnrichmentEnabled: settings.enrichMetadataWithTMDB,
logoFetchInProgress: logoFetchInProgress.current
});
}
const currentLogoUrl = metadata?.logo;
let shouldFetchLogo = false;
// If enrichment is disabled, use addon logo and don't fetch from external sources
if (!settings.enrichMetadataWithTMDB) {
// If we have an addon logo, preload it immediately for instant display
if (metadata?.logo && !isTmdbUrl(metadata.logo)) {
// Preload addon logo for instant display
FastImage.preload([{ uri: metadata.logo }]);
// This is an addon logo, keep it
return;
}
// If no addon logo, don't fetch external logos when enrichment is disabled
return;
}
// When TMDB enrichment is ON, remove addon logos immediately
// We don't want to show addon logos briefly before TMDB logos load
if (settings.enrichMetadataWithTMDB && currentLogoUrl && !isTmdbUrl(currentLogoUrl)) {
// Clear addon logo when enrichment is enabled
setMetadata((prevMetadata: any) => ({ ...prevMetadata!, logo: undefined }));
shouldFetchLogo = true;
}
// Determine if we need to fetch a new logo (only when enrichment is enabled)
else if (!currentLogoUrl) {
shouldFetchLogo = true;
} else {
const isCurrentLogoTmdb = isTmdbUrl(currentLogoUrl);
if (logoPreference === 'tmdb' && !isCurrentLogoTmdb) {
shouldFetchLogo = true;
}
}
// Guard against infinite loops by checking if we're already fetching
if (shouldFetchLogo && !logoFetchInProgress.current) {
logoFetchInProgress.current = true;
const fetchLogo = async () => {
// Store the original logo to restore if needed
const originalLogoUrl = currentLogoUrl;
try {
const preferredLanguage = settings.tmdbLanguagePreference || 'en';
if (logoPreference === 'tmdb') {
// TMDB path - optimized flow
let tmdbId: string | null = null;
let contentType = type === 'series' ? 'tv' : 'movie';
// Extract or find TMDB ID in one step
if (id.startsWith('tmdb:')) {
tmdbId = id.split(':')[1];
} else if (imdbId && settings.enrichMetadataWithTMDB) {
try {
const tmdbService = TMDBService.getInstance();
const foundId = await tmdbService.findTMDBIdByIMDB(imdbId);
if (foundId) {
tmdbId = String(foundId);
setFoundTmdbId(tmdbId); // Save for banner fetching
} else if (__DEV__) {
console.log('[useMetadataAssets] Could not find TMDB ID for IMDB:', imdbId);
}
} catch (error) {
if (__DEV__) console.error('[useMetadataAssets] Error finding TMDB ID:', error);
}
} else {
const parsedId = parseInt(id, 10);
if (!isNaN(parsedId)) {
tmdbId = String(parsedId);
}
}
if (tmdbId) {
try {
// Direct fetch - avoid multiple service calls
const tmdbService = TMDBService.getInstance();
const logoUrl = await tmdbService.getContentLogo(contentType as 'tv' | 'movie', tmdbId, preferredLanguage);
if (__DEV__) {
console.log('[useMetadataAssets] Logo fetch result:', {
contentType,
tmdbId,
preferredLanguage,
logoUrl,
logoPreference
});
}
if (logoUrl) {
// Preload the image before setting it
await FastImage.preload([{ uri: logoUrl }]);
// Only update if we got a valid logo
setMetadata((prevMetadata: any) => ({ ...prevMetadata!, logo: logoUrl }));
} else {
// TMDB logo not found
// When enrichment is ON, don't fallback to addon logos - show text instead
if (__DEV__) {
console.log('[useMetadataAssets] No TMDB logo found for ID:', tmdbId);
}
// Keep logo as undefined to show text title
}
} catch (error) {
// TMDB logo fetch failed
// When enrichment is ON, don't fallback to addon logos - show text instead
if (__DEV__) {
console.error('[useMetadataAssets] Logo fetch error:', error);
}
// Keep logo as undefined to show text title
}
} else {
// No TMDB ID found
// When enrichment is ON, don't use addon logos - show text instead
if (__DEV__) console.log('[useMetadataAssets] No TMDB ID found, will show text title');
// Keep logo as undefined to show text title
}
}
} catch (error) {
// Handle error silently, keep existing logo
if (__DEV__) console.error('[useMetadataAssets] Unexpected error in logo fetch:', error);
} finally {
logoFetchInProgress.current = false;
}
};
// Execute fetch without awaiting
fetchLogo();
}
}, [
id,
type,
imdbId,
metadata?.logo,
settings.logoSourcePreference,
settings.tmdbLanguagePreference,
settings.enrichMetadataWithTMDB,
setMetadata
]);
// Optimized banner fetching
const fetchBanner = useCallback(async () => {
if (!metadata) return;
@ -354,9 +182,7 @@ export const useMetadataAssets = (
return {
bannerImage,
loadingBanner,
logoLoadError,
foundTmdbId,
setLogoLoadError,
setBannerImage,
bannerSource,
};