mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-21 08:41:57 +00:00
slider change
This commit is contained in:
parent
6405fd2c71
commit
17339d82a6
3 changed files with 65 additions and 146 deletions
|
|
@ -124,8 +124,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
const [rnVideoAudioTracks, setRnVideoAudioTracks] = useState<Array<{id: number, name: string, language?: string}>>([]);
|
const [rnVideoAudioTracks, setRnVideoAudioTracks] = useState<Array<{id: number, name: string, language?: string}>>([]);
|
||||||
const [rnVideoTextTracks, setRnVideoTextTracks] = useState<Array<{id: number, name: string, language?: string}>>([]);
|
const [rnVideoTextTracks, setRnVideoTextTracks] = useState<Array<{id: number, name: string, language?: string}>>([]);
|
||||||
const [isPlayerReady, setIsPlayerReady] = useState(false);
|
const [isPlayerReady, setIsPlayerReady] = useState(false);
|
||||||
const progressAnim = useRef(new Animated.Value(0)).current;
|
// Removed progressAnim and progressBarRef - no longer needed with React Native Community Slider
|
||||||
const progressBarRef = useRef<View>(null);
|
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
const isSeeking = useRef(false);
|
const isSeeking = useRef(false);
|
||||||
const seekDebounceTimer = useRef<NodeJS.Timeout | null>(null);
|
const seekDebounceTimer = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
@ -454,44 +453,29 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
}
|
}
|
||||||
}, [seekTime, duration]);
|
}, [seekTime, duration]);
|
||||||
|
|
||||||
const handleProgressBarTouch = (event: any) => {
|
// Slider callback functions for React Native Community Slider
|
||||||
if (duration > 0) {
|
const handleSliderValueChange = (value: number) => {
|
||||||
const { locationX } = event.nativeEvent;
|
if (isDragging && duration > 0) {
|
||||||
processProgressTouch(locationX);
|
const seekTime = Math.min(value, duration - END_EPSILON);
|
||||||
|
setCurrentTime(seekTime);
|
||||||
|
pendingSeekValue.current = seekTime;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleProgressBarDragStart = () => {
|
const handleSlidingStart = () => {
|
||||||
setIsDragging(true);
|
setIsDragging(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleProgressBarDragMove = (event: any) => {
|
const handleSlidingComplete = (value: number) => {
|
||||||
if (!isDragging || !duration || duration <= 0) return;
|
|
||||||
const { locationX } = event.nativeEvent;
|
|
||||||
processProgressTouch(locationX, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleProgressBarDragEnd = () => {
|
|
||||||
setIsDragging(false);
|
setIsDragging(false);
|
||||||
if (pendingSeekValue.current !== null) {
|
if (duration > 0) {
|
||||||
seekToTime(pendingSeekValue.current);
|
const seekTime = Math.min(value, duration - END_EPSILON);
|
||||||
|
seekToTime(seekTime);
|
||||||
pendingSeekValue.current = null;
|
pendingSeekValue.current = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const processProgressTouch = (locationX: number, isDragging = false) => {
|
// Removed processProgressTouch - no longer needed with React Native Community Slider
|
||||||
progressBarRef.current?.measure((x, y, width, height, pageX, pageY) => {
|
|
||||||
const percentage = Math.max(0, Math.min(locationX / width, 0.999));
|
|
||||||
const seekTime = Math.min(percentage * duration, duration - END_EPSILON);
|
|
||||||
progressAnim.setValue(percentage);
|
|
||||||
if (isDragging) {
|
|
||||||
pendingSeekValue.current = seekTime;
|
|
||||||
setCurrentTime(seekTime);
|
|
||||||
} else {
|
|
||||||
seekToTime(seekTime);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleProgress = (data: any) => {
|
const handleProgress = (data: any) => {
|
||||||
if (isDragging || isSeeking.current) return;
|
if (isDragging || isSeeking.current) return;
|
||||||
|
|
@ -501,12 +485,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
// Update time more frequently for subtitle synchronization (0.1s threshold)
|
// Update time more frequently for subtitle synchronization (0.1s threshold)
|
||||||
if (Math.abs(currentTimeInSeconds - currentTime) > 0.1) {
|
if (Math.abs(currentTimeInSeconds - currentTime) > 0.1) {
|
||||||
safeSetState(() => setCurrentTime(currentTimeInSeconds));
|
safeSetState(() => setCurrentTime(currentTimeInSeconds));
|
||||||
const progressPercent = duration > 0 ? currentTimeInSeconds / duration : 0;
|
// Removed progressAnim animation - no longer needed with React Native Community Slider
|
||||||
Animated.timing(progressAnim, {
|
|
||||||
toValue: progressPercent,
|
|
||||||
duration: 100,
|
|
||||||
useNativeDriver: false,
|
|
||||||
}).start();
|
|
||||||
const bufferedTime = data.playableDuration || currentTimeInSeconds;
|
const bufferedTime = data.playableDuration || currentTimeInSeconds;
|
||||||
safeSetState(() => setBuffered(bufferedTime));
|
safeSetState(() => setBuffered(bufferedTime));
|
||||||
}
|
}
|
||||||
|
|
@ -1199,12 +1178,9 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
setShowAudioModal={setShowAudioModal}
|
setShowAudioModal={setShowAudioModal}
|
||||||
setShowSubtitleModal={setShowSubtitleModal}
|
setShowSubtitleModal={setShowSubtitleModal}
|
||||||
setShowSourcesModal={setShowSourcesModal}
|
setShowSourcesModal={setShowSourcesModal}
|
||||||
progressBarRef={progressBarRef}
|
onSliderValueChange={handleSliderValueChange}
|
||||||
progressAnim={progressAnim}
|
onSlidingStart={handleSlidingStart}
|
||||||
handleProgressBarTouch={handleProgressBarTouch}
|
onSlidingComplete={handleSlidingComplete}
|
||||||
handleProgressBarDragStart={handleProgressBarDragStart}
|
|
||||||
handleProgressBarDragMove={handleProgressBarDragMove}
|
|
||||||
handleProgressBarDragEnd={handleProgressBarDragEnd}
|
|
||||||
buffered={buffered}
|
buffered={buffered}
|
||||||
formatTime={formatTime}
|
formatTime={formatTime}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import { logger } from '../../utils/logger';
|
||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
import { MaterialIcons } from '@expo/vector-icons';
|
import { MaterialIcons } from '@expo/vector-icons';
|
||||||
import { LinearGradient } from 'expo-linear-gradient';
|
import { LinearGradient } from 'expo-linear-gradient';
|
||||||
|
import Slider from '@react-native-community/slider';
|
||||||
import AndroidVideoPlayer from './AndroidVideoPlayer';
|
import AndroidVideoPlayer from './AndroidVideoPlayer';
|
||||||
import { useTraktAutosync } from '../../hooks/useTraktAutosync';
|
import { useTraktAutosync } from '../../hooks/useTraktAutosync';
|
||||||
import { useTraktAutosyncSettings } from '../../hooks/useTraktAutosyncSettings';
|
import { useTraktAutosyncSettings } from '../../hooks/useTraktAutosyncSettings';
|
||||||
|
|
@ -127,8 +128,7 @@ const VideoPlayer: React.FC = () => {
|
||||||
const [vlcAudioTracks, setVlcAudioTracks] = useState<Array<{ id: number, name: string, language?: string }>>([]);
|
const [vlcAudioTracks, setVlcAudioTracks] = useState<Array<{ id: number, name: string, language?: string }>>([]);
|
||||||
const [vlcTextTracks, setVlcTextTracks] = useState<Array<{ id: number, name: string, language?: string }>>([]);
|
const [vlcTextTracks, setVlcTextTracks] = useState<Array<{ id: number, name: string, language?: string }>>([]);
|
||||||
const [isPlayerReady, setIsPlayerReady] = useState(false);
|
const [isPlayerReady, setIsPlayerReady] = useState(false);
|
||||||
const progressAnim = useRef(new Animated.Value(0)).current;
|
// Removed progressAnim and progressBarRef - no longer needed with React Native Community Slider
|
||||||
const progressBarRef = useRef<View>(null);
|
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
const isSeeking = useRef(false);
|
const isSeeking = useRef(false);
|
||||||
const seekDebounceTimer = useRef<NodeJS.Timeout | null>(null);
|
const seekDebounceTimer = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
@ -515,44 +515,29 @@ const VideoPlayer: React.FC = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleProgressBarTouch = (event: any) => {
|
// Slider callback functions for React Native Community Slider
|
||||||
if (duration > 0) {
|
const handleSliderValueChange = (value: number) => {
|
||||||
const { locationX } = event.nativeEvent;
|
if (isDragging && duration > 0) {
|
||||||
processProgressTouch(locationX);
|
const seekTime = Math.min(value, duration - END_EPSILON);
|
||||||
|
setCurrentTime(seekTime);
|
||||||
|
pendingSeekValue.current = seekTime;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleProgressBarDragStart = () => {
|
const handleSlidingStart = () => {
|
||||||
setIsDragging(true);
|
setIsDragging(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleProgressBarDragMove = (event: any) => {
|
const handleSlidingComplete = (value: number) => {
|
||||||
if (!isDragging || !duration || duration <= 0) return;
|
|
||||||
const { locationX } = event.nativeEvent;
|
|
||||||
processProgressTouch(locationX, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleProgressBarDragEnd = () => {
|
|
||||||
setIsDragging(false);
|
setIsDragging(false);
|
||||||
if (pendingSeekValue.current !== null) {
|
if (duration > 0) {
|
||||||
seekToTime(pendingSeekValue.current);
|
const seekTime = Math.min(value, duration - END_EPSILON);
|
||||||
|
seekToTime(seekTime);
|
||||||
pendingSeekValue.current = null;
|
pendingSeekValue.current = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const processProgressTouch = (locationX: number, isDragging = false) => {
|
// Removed processProgressTouch - no longer needed with React Native Community Slider
|
||||||
progressBarRef.current?.measure((x, y, width, height, pageX, pageY) => {
|
|
||||||
const percentage = Math.max(0, Math.min(locationX / width, 0.999));
|
|
||||||
const seekTime = Math.min(percentage * duration, duration - END_EPSILON);
|
|
||||||
progressAnim.setValue(percentage);
|
|
||||||
if (isDragging) {
|
|
||||||
pendingSeekValue.current = seekTime;
|
|
||||||
setCurrentTime(seekTime);
|
|
||||||
} else {
|
|
||||||
seekToTime(seekTime);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleProgress = (event: any) => {
|
const handleProgress = (event: any) => {
|
||||||
if (isDragging || isSeeking.current) return;
|
if (isDragging || isSeeking.current) return;
|
||||||
|
|
@ -562,12 +547,7 @@ const VideoPlayer: React.FC = () => {
|
||||||
// Only update if there's a significant change to avoid unnecessary updates
|
// Only update if there's a significant change to avoid unnecessary updates
|
||||||
if (Math.abs(currentTimeInSeconds - currentTime) > 0.5) {
|
if (Math.abs(currentTimeInSeconds - currentTime) > 0.5) {
|
||||||
safeSetState(() => setCurrentTime(currentTimeInSeconds));
|
safeSetState(() => setCurrentTime(currentTimeInSeconds));
|
||||||
const progressPercent = duration > 0 ? currentTimeInSeconds / duration : 0;
|
// Removed progressAnim animation - no longer needed with React Native Community Slider
|
||||||
Animated.timing(progressAnim, {
|
|
||||||
toValue: progressPercent,
|
|
||||||
duration: 250,
|
|
||||||
useNativeDriver: false,
|
|
||||||
}).start();
|
|
||||||
const bufferedTime = event.bufferTime / 1000 || currentTimeInSeconds;
|
const bufferedTime = event.bufferTime / 1000 || currentTimeInSeconds;
|
||||||
safeSetState(() => setBuffered(bufferedTime));
|
safeSetState(() => setBuffered(bufferedTime));
|
||||||
}
|
}
|
||||||
|
|
@ -1273,12 +1253,9 @@ const VideoPlayer: React.FC = () => {
|
||||||
setShowAudioModal={setShowAudioModal}
|
setShowAudioModal={setShowAudioModal}
|
||||||
setShowSubtitleModal={setShowSubtitleModal}
|
setShowSubtitleModal={setShowSubtitleModal}
|
||||||
setShowSourcesModal={setShowSourcesModal}
|
setShowSourcesModal={setShowSourcesModal}
|
||||||
progressBarRef={progressBarRef}
|
onSliderValueChange={handleSliderValueChange}
|
||||||
progressAnim={progressAnim}
|
onSlidingStart={handleSlidingStart}
|
||||||
handleProgressBarTouch={handleProgressBarTouch}
|
onSlidingComplete={handleSlidingComplete}
|
||||||
handleProgressBarDragStart={handleProgressBarDragStart}
|
|
||||||
handleProgressBarDragMove={handleProgressBarDragMove}
|
|
||||||
handleProgressBarDragEnd={handleProgressBarDragEnd}
|
|
||||||
buffered={buffered}
|
buffered={buffered}
|
||||||
formatTime={formatTime}
|
formatTime={formatTime}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View, Text, TouchableOpacity, Animated, StyleSheet } from 'react-native';
|
import { View, Text, TouchableOpacity, Animated, StyleSheet, Platform } from 'react-native';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
import { LinearGradient } from 'expo-linear-gradient';
|
import { LinearGradient } from 'expo-linear-gradient';
|
||||||
|
import Slider from '@react-native-community/slider';
|
||||||
import { styles } from '../utils/playerStyles';
|
import { styles } from '../utils/playerStyles';
|
||||||
import { getTrackDisplayName } from '../utils/playerUtils';
|
import { getTrackDisplayName } from '../utils/playerUtils';
|
||||||
|
|
||||||
|
|
@ -31,12 +32,10 @@ interface PlayerControlsProps {
|
||||||
setShowAudioModal: (show: boolean) => void;
|
setShowAudioModal: (show: boolean) => void;
|
||||||
setShowSubtitleModal: (show: boolean) => void;
|
setShowSubtitleModal: (show: boolean) => void;
|
||||||
setShowSourcesModal?: (show: boolean) => void;
|
setShowSourcesModal?: (show: boolean) => void;
|
||||||
progressBarRef: React.RefObject<View>;
|
// Slider-specific props
|
||||||
progressAnim: Animated.Value;
|
onSliderValueChange: (value: number) => void;
|
||||||
handleProgressBarTouch: (event: any) => void;
|
onSlidingStart: () => void;
|
||||||
handleProgressBarDragStart: () => void;
|
onSlidingComplete: (value: number) => void;
|
||||||
handleProgressBarDragMove: (event: any) => void;
|
|
||||||
handleProgressBarDragEnd: () => void;
|
|
||||||
buffered: number;
|
buffered: number;
|
||||||
formatTime: (seconds: number) => string;
|
formatTime: (seconds: number) => string;
|
||||||
}
|
}
|
||||||
|
|
@ -67,12 +66,9 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
|
||||||
setShowAudioModal,
|
setShowAudioModal,
|
||||||
setShowSubtitleModal,
|
setShowSubtitleModal,
|
||||||
setShowSourcesModal,
|
setShowSourcesModal,
|
||||||
progressBarRef,
|
onSliderValueChange,
|
||||||
progressAnim,
|
onSlidingStart,
|
||||||
handleProgressBarTouch,
|
onSlidingComplete,
|
||||||
handleProgressBarDragStart,
|
|
||||||
handleProgressBarDragMove,
|
|
||||||
handleProgressBarDragEnd,
|
|
||||||
buffered,
|
buffered,
|
||||||
formatTime,
|
formatTime,
|
||||||
}) => {
|
}) => {
|
||||||
|
|
@ -81,55 +77,25 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
|
||||||
style={[StyleSheet.absoluteFill, { opacity: fadeAnim }]}
|
style={[StyleSheet.absoluteFill, { opacity: fadeAnim }]}
|
||||||
pointerEvents={showControls ? 'auto' : 'none'}
|
pointerEvents={showControls ? 'auto' : 'none'}
|
||||||
>
|
>
|
||||||
{/* Progress bar with enhanced touch handling */}
|
{/* Progress slider with native iOS slider */}
|
||||||
<View style={styles.sliderContainer}>
|
<View style={styles.sliderContainer}>
|
||||||
<View
|
<Slider
|
||||||
style={styles.progressTouchArea}
|
style={{
|
||||||
onTouchStart={handleProgressBarDragStart}
|
width: '100%',
|
||||||
onTouchMove={handleProgressBarDragMove}
|
height: 40,
|
||||||
onTouchEnd={handleProgressBarDragEnd}
|
marginHorizontal: 0,
|
||||||
>
|
}}
|
||||||
<TouchableOpacity
|
minimumValue={0}
|
||||||
activeOpacity={0.8}
|
maximumValue={duration || 1}
|
||||||
onPress={handleProgressBarTouch}
|
value={currentTime}
|
||||||
style={{width: '100%'}}
|
onValueChange={onSliderValueChange}
|
||||||
>
|
onSlidingStart={onSlidingStart}
|
||||||
<View
|
onSlidingComplete={onSlidingComplete}
|
||||||
ref={progressBarRef}
|
minimumTrackTintColor="#FFFFFF"
|
||||||
style={styles.progressBarContainer}
|
maximumTrackTintColor="rgba(255, 255, 255, 0.3)"
|
||||||
>
|
thumbTintColor={Platform.OS === 'android' ? '#FFFFFF' : undefined}
|
||||||
{/* Buffered Progress */}
|
tapToSeek={Platform.OS === 'ios'}
|
||||||
<View style={[styles.bufferProgress, {
|
/>
|
||||||
width: `${(buffered / (duration || 1)) * 100}%`
|
|
||||||
}]} />
|
|
||||||
{/* Animated Progress */}
|
|
||||||
<Animated.View
|
|
||||||
style={[
|
|
||||||
styles.progressBarFill,
|
|
||||||
{
|
|
||||||
width: progressAnim.interpolate({
|
|
||||||
inputRange: [0, 1],
|
|
||||||
outputRange: ['0%', '100%']
|
|
||||||
})
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Progress Thumb - Moved outside the progressBarContainer */}
|
|
||||||
<Animated.View
|
|
||||||
style={[
|
|
||||||
styles.progressThumb,
|
|
||||||
{
|
|
||||||
left: progressAnim.interpolate({
|
|
||||||
inputRange: [0, 1],
|
|
||||||
outputRange: ['0%', '100%']
|
|
||||||
})
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
<View style={styles.timeDisplay}>
|
<View style={styles.timeDisplay}>
|
||||||
<Text style={styles.duration}>{formatTime(currentTime)}</Text>
|
<Text style={styles.duration}>{formatTime(currentTime)}</Text>
|
||||||
<Text style={styles.duration}>{formatTime(duration)}</Text>
|
<Text style={styles.duration}>{formatTime(duration)}</Text>
|
||||||
|
|
@ -244,4 +210,4 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PlayerControls;
|
export default PlayerControls;
|
||||||
Loading…
Reference in a new issue