diff --git a/src/hooks/useMetadataAnimations.ts b/src/hooks/useMetadataAnimations.ts index e6698a95..eef76726 100644 --- a/src/hooks/useMetadataAnimations.ts +++ b/src/hooks/useMetadataAnimations.ts @@ -32,18 +32,18 @@ const easings = { }; export const useMetadataAnimations = (safeAreaTop: number, watchProgress: any) => { - // Consolidated entrance animations - fewer shared values - const screenOpacity = useSharedValue(0); - const contentOpacity = useSharedValue(0); + // Consolidated entrance animations - start with visible values for Android compatibility + const screenOpacity = useSharedValue(1); + const contentOpacity = useSharedValue(1); // Combined hero animations - const heroOpacity = useSharedValue(0); - const heroScale = useSharedValue(0.95); // Combined scale for micro-animation + const heroOpacity = useSharedValue(1); + const heroScale = useSharedValue(1); // Start at 1 for Android compatibility const heroHeightValue = useSharedValue(height * 0.5); // Combined UI element animations - const uiElementsOpacity = useSharedValue(0); - const uiElementsTranslateY = useSharedValue(10); + const uiElementsOpacity = useSharedValue(1); + const uiElementsTranslateY = useSharedValue(0); // Progress animation - simplified to single value const progressOpacity = useSharedValue(0); @@ -57,10 +57,11 @@ export const useMetadataAnimations = (safeAreaTop: number, watchProgress: any) = // Ultra-fast entrance sequence - batch animations for better performance useEffect(() => { - 'worklet'; - // Batch all entrance animations to run simultaneously const enterAnimations = () => { + 'worklet'; + + // Start with slightly reduced values and animate to full visibility screenOpacity.value = withTiming(1, { duration: 250, easing: easings.fast @@ -92,14 +93,17 @@ export const useMetadataAnimations = (safeAreaTop: number, watchProgress: any) = // Optimized watch progress animation useEffect(() => { - 'worklet'; - const hasProgress = watchProgress && watchProgress.duration > 0; - progressOpacity.value = withTiming(hasProgress ? 1 : 0, { - duration: hasProgress ? 200 : 150, - easing: easings.fast - }); + const updateProgress = () => { + 'worklet'; + progressOpacity.value = withTiming(hasProgress ? 1 : 0, { + duration: hasProgress ? 200 : 150, + easing: easings.fast + }); + }; + + runOnUI(updateProgress)(); }, [watchProgress]); // Ultra-optimized scroll handler with minimal calculations diff --git a/src/screens/MDBListSettingsScreen.tsx b/src/screens/MDBListSettingsScreen.tsx index 7dd3ddab..93126f5d 100644 --- a/src/screens/MDBListSettingsScreen.tsx +++ b/src/screens/MDBListSettingsScreen.tsx @@ -540,7 +540,7 @@ const MDBListSettingsScreen = () => { const openMDBListWebsite = () => { logger.log('[MDBListSettingsScreen] Opening MDBList website'); - Linking.openURL('https://mdblist.com/settings').catch(error => { + Linking.openURL('https://mdblist.com/preferences').catch(error => { logger.error('[MDBListSettingsScreen] Error opening website:', error); }); }; diff --git a/src/screens/SettingsScreen.tsx b/src/screens/SettingsScreen.tsx index 9b1dcf16..7f442260 100644 --- a/src/screens/SettingsScreen.tsx +++ b/src/screens/SettingsScreen.tsx @@ -697,19 +697,20 @@ const styles = StyleSheet.create({ borderRadius: 8, overflow: 'hidden', height: 36, - width: 160, + width: 180, marginRight: 8, }, selectorButton: { flex: 1, justifyContent: 'center', alignItems: 'center', - paddingHorizontal: 12, + paddingHorizontal: 8, backgroundColor: 'rgba(255,255,255,0.08)', }, selectorText: { - fontSize: 14, + fontSize: 13, fontWeight: '500', + textAlign: 'center', }, profileLockContainer: { padding: 16, diff --git a/src/screens/StreamsScreen.tsx b/src/screens/StreamsScreen.tsx index e1b14e37..941500d2 100644 --- a/src/screens/StreamsScreen.tsx +++ b/src/screens/StreamsScreen.tsx @@ -679,39 +679,18 @@ export const StreamsScreen = () => { navigateToPlayer(stream); }); } else { - // For direct video URLs, use the S.Browser.ACTION_VIEW approach - // This is a more reliable way to force Android to show all video apps + // For direct video URLs, use the VideoPlayerService to show the Android app chooser + const success = await VideoPlayerService.playVideo(stream.url, { + useExternalPlayer: true, + title: metadata?.name || 'Video', + episodeTitle: type === 'series' ? currentEpisode?.name : undefined, + episodeNumber: type === 'series' && currentEpisode ? `S${currentEpisode.season_number}E${currentEpisode.episode_number}` : undefined, + }); - // Strip query parameters if they exist as they can cause issues with some apps - let cleanUrl = stream.url; - if (cleanUrl.includes('?')) { - cleanUrl = cleanUrl.split('?')[0]; + if (!success) { + console.log('VideoPlayerService failed, falling back to built-in player'); + navigateToPlayer(stream); } - - // Create an Android intent URL that forces the chooser - // Set component=null to ensure chooser is shown - // Set action=android.intent.action.VIEW to open the content - const intentUrl = `intent:${cleanUrl}#Intent;action=android.intent.action.VIEW;category=android.intent.category.DEFAULT;component=;type=video/*;launchFlags=0x10000000;end`; - - console.log(`Using intent URL: ${intentUrl}`); - - Linking.openURL(intentUrl) - .then(() => console.log('Successfully opened with intent URL')) - .catch(err => { - console.error('Failed to open with intent URL:', err); - - // First fallback: Try direct URL with regular Linking API - console.log('Trying plain URL as fallback'); - Linking.openURL(stream.url) - .then(() => console.log('Opened with direct URL')) - .catch(directErr => { - console.error('Failed to open direct URL:', directErr); - - // Final fallback: Use built-in player - console.log('All external player attempts failed, using built-in player'); - navigateToPlayer(stream); - }); - }); } } catch (error) { console.error('Error with external player:', error);