mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-03-11 17:45:38 +00:00
improved trailer playback logic
This commit is contained in:
parent
6f24275ff0
commit
220fc6aa21
4 changed files with 85 additions and 9 deletions
|
|
@ -8,6 +8,7 @@ import {
|
|||
Platform,
|
||||
InteractionManager,
|
||||
} from 'react-native';
|
||||
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import { Image } from 'expo-image';
|
||||
|
|
@ -709,6 +710,7 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
|
|||
const [isTrailerPlaying, setIsTrailerPlaying] = useState(false);
|
||||
const [trailerReady, setTrailerReady] = useState(false);
|
||||
const [trailerPreloaded, setTrailerPreloaded] = useState(false);
|
||||
const trailerVideoRef = useRef<any>(null);
|
||||
const imageOpacity = useSharedValue(1);
|
||||
const imageLoadOpacity = useSharedValue(0);
|
||||
const shimmerOpacity = useSharedValue(0.3);
|
||||
|
|
@ -747,6 +749,18 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
|
|||
trailerOpacity.value = withTiming(1, { duration: 500 });
|
||||
}, [thumbnailOpacity, trailerOpacity, trailerPreloaded]);
|
||||
|
||||
// Handle fullscreen toggle
|
||||
const handleFullscreenToggle = useCallback(async () => {
|
||||
try {
|
||||
if (trailerVideoRef.current) {
|
||||
// Use the native fullscreen player
|
||||
await trailerVideoRef.current.presentFullscreenPlayer();
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('HeroSection', 'Error toggling fullscreen:', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Handle trailer error - fade back to thumbnail
|
||||
const handleTrailerError = useCallback(() => {
|
||||
setTrailerError(true);
|
||||
|
|
@ -973,6 +987,8 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
|
|||
imageLoadOpacity.value = 0;
|
||||
shimmerOpacity.value = 0.3;
|
||||
interactionComplete.current = false;
|
||||
|
||||
// Cleanup on unmount
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
|
@ -990,6 +1006,8 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Animated.View style={[styles.heroSection, heroAnimatedStyle]}>
|
||||
{/* Optimized Background */}
|
||||
|
|
@ -1045,11 +1063,13 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
|
|||
opacity: trailerOpacity
|
||||
}]}>
|
||||
<TrailerPlayer
|
||||
ref={trailerVideoRef}
|
||||
trailerUrl={trailerUrl}
|
||||
autoPlay={true}
|
||||
muted={trailerMuted}
|
||||
style={styles.absoluteFill}
|
||||
hideLoadingSpinner={true}
|
||||
onFullscreenToggle={handleFullscreenToggle}
|
||||
onLoad={handleTrailerReady}
|
||||
onError={handleTrailerError}
|
||||
onPlaybackStatusUpdate={(status) => {
|
||||
|
|
@ -1061,15 +1081,35 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
|
|||
</Animated.View>
|
||||
)}
|
||||
|
||||
{/* Unmute button for trailer */}
|
||||
{/* Trailer control buttons (unmute and fullscreen) */}
|
||||
{settings?.showTrailers && trailerReady && trailerUrl && (
|
||||
<Animated.View style={{
|
||||
position: 'absolute',
|
||||
top: Platform.OS === 'android' ? 40 : 50,
|
||||
right: width >= 768 ? 32 : 16,
|
||||
zIndex: 10,
|
||||
opacity: trailerOpacity
|
||||
opacity: trailerOpacity,
|
||||
flexDirection: 'row',
|
||||
gap: 8,
|
||||
}}>
|
||||
{/* Fullscreen button */}
|
||||
<TouchableOpacity
|
||||
onPress={handleFullscreenToggle}
|
||||
activeOpacity={0.7}
|
||||
style={{
|
||||
padding: 8,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
borderRadius: 20,
|
||||
}}
|
||||
>
|
||||
<MaterialIcons
|
||||
name="fullscreen"
|
||||
size={24}
|
||||
color="white"
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
|
||||
{/* Unmute button */}
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
setTrailerMuted(!trailerMuted);
|
||||
|
|
@ -1207,6 +1247,7 @@ const styles = StyleSheet.create({
|
|||
backgroundColor: '#000',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
|
||||
absoluteFill: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
|
|
@ -12,6 +12,7 @@ import Animated, {
|
|||
FadeIn,
|
||||
} from 'react-native-reanimated';
|
||||
import { useTheme } from '../../contexts/ThemeContext';
|
||||
import { isMDBListEnabled } from '../../screens/MDBListSettingsScreen';
|
||||
// MetadataSourceSelector removed
|
||||
|
||||
interface MetadataDetailsProps {
|
||||
|
|
@ -34,6 +35,20 @@ const MetadataDetails: React.FC<MetadataDetailsProps> = ({
|
|||
}) => {
|
||||
const { currentTheme } = useTheme();
|
||||
const [isFullDescriptionOpen, setIsFullDescriptionOpen] = useState(false);
|
||||
const [isMDBEnabled, setIsMDBEnabled] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const checkMDBListEnabled = async () => {
|
||||
try {
|
||||
const enabled = await isMDBListEnabled();
|
||||
setIsMDBEnabled(enabled);
|
||||
} catch (error) {
|
||||
setIsMDBEnabled(false); // Default to disabled if there's an error
|
||||
}
|
||||
};
|
||||
|
||||
checkMDBListEnabled();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -60,7 +75,7 @@ const MetadataDetails: React.FC<MetadataDetailsProps> = ({
|
|||
{metadata.certification && (
|
||||
<Text style={[styles.metaText, { color: currentTheme.colors.text }]}>{metadata.certification}</Text>
|
||||
)}
|
||||
{metadata.imdbRating && (
|
||||
{metadata.imdbRating && !isMDBEnabled && (
|
||||
<View style={styles.ratingContainer}>
|
||||
<Image
|
||||
source={{ uri: 'https://upload.wikimedia.org/wikipedia/commons/thumb/6/69/IMDB_Logo_2016.svg/575px-IMDB_Logo_2016.svg.png' }}
|
||||
|
|
|
|||
|
|
@ -34,9 +34,10 @@ interface TrailerPlayerProps {
|
|||
onPlaybackStatusUpdate?: (status: { isLoaded: boolean; didJustFinish: boolean }) => void;
|
||||
style?: any;
|
||||
hideLoadingSpinner?: boolean;
|
||||
onFullscreenToggle?: () => void;
|
||||
}
|
||||
|
||||
const TrailerPlayer: React.FC<TrailerPlayerProps> = memo(({
|
||||
const TrailerPlayer = React.forwardRef<any, TrailerPlayerProps>(({
|
||||
trailerUrl,
|
||||
autoPlay = true,
|
||||
muted = true,
|
||||
|
|
@ -47,7 +48,8 @@ const TrailerPlayer: React.FC<TrailerPlayerProps> = memo(({
|
|||
onPlaybackStatusUpdate,
|
||||
style,
|
||||
hideLoadingSpinner = false,
|
||||
}) => {
|
||||
onFullscreenToggle,
|
||||
}, ref) => {
|
||||
const { currentTheme } = useTheme();
|
||||
const videoRef = useRef<VideoRef>(null);
|
||||
|
||||
|
|
@ -171,6 +173,15 @@ const TrailerPlayer: React.FC<TrailerPlayerProps> = memo(({
|
|||
};
|
||||
}, []);
|
||||
|
||||
// Forward the ref to the video element
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
presentFullscreenPlayer: () => {
|
||||
if (videoRef.current) {
|
||||
return videoRef.current.presentFullscreenPlayer();
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
// Animated styles
|
||||
const controlsAnimatedStyle = useAnimatedStyle(() => ({
|
||||
opacity: controlsOpacity.value,
|
||||
|
|
@ -287,6 +298,16 @@ const TrailerPlayer: React.FC<TrailerPlayerProps> = memo(({
|
|||
color="white"
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
|
||||
{onFullscreenToggle && (
|
||||
<TouchableOpacity style={styles.controlButton} onPress={onFullscreenToggle}>
|
||||
<MaterialIcons
|
||||
name="fullscreen"
|
||||
size={isTablet ? 32 : 24}
|
||||
color="white"
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</LinearGradient>
|
||||
|
|
@ -296,6 +317,8 @@ const TrailerPlayer: React.FC<TrailerPlayerProps> = memo(({
|
|||
);
|
||||
});
|
||||
|
||||
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
|
|
|
|||
|
|
@ -735,7 +735,6 @@ const LibraryScreen = () => {
|
|||
numColumns={numColumns}
|
||||
contentContainerStyle={styles.listContainer}
|
||||
showsVerticalScrollIndicator={false}
|
||||
columnWrapperStyle={styles.columnWrapper}
|
||||
onEndReachedThreshold={0.7}
|
||||
onEndReached={() => {}}
|
||||
/>
|
||||
|
|
@ -776,7 +775,6 @@ const LibraryScreen = () => {
|
|||
renderItem={({ item }) => renderTraktItem({ item })}
|
||||
keyExtractor={(item) => `${item.type}-${item.id}`}
|
||||
numColumns={numColumns}
|
||||
columnWrapperStyle={styles.row}
|
||||
style={styles.traktContainer}
|
||||
contentContainerStyle={{ paddingBottom: insets.bottom + 80 }}
|
||||
showsVerticalScrollIndicator={false}
|
||||
|
|
@ -874,7 +872,6 @@ const LibraryScreen = () => {
|
|||
numColumns={numColumns}
|
||||
contentContainerStyle={styles.listContainer}
|
||||
showsVerticalScrollIndicator={false}
|
||||
columnWrapperStyle={styles.columnWrapper}
|
||||
onEndReachedThreshold={0.7}
|
||||
onEndReached={() => {}}
|
||||
/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue