mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-21 00:32:04 +00:00
Improved playback logic
This commit is contained in:
parent
bc4bf5a963
commit
0a853e7460
1 changed files with 44 additions and 9 deletions
|
|
@ -86,6 +86,16 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
} as any;
|
} as any;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const defaultIosHeaders = () => {
|
||||||
|
if (Platform.OS !== 'ios') return {} as any;
|
||||||
|
return {
|
||||||
|
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1 Nuvio/1.0',
|
||||||
|
'Accept': '*/*',
|
||||||
|
'Accept-Language': 'en-US,en;q=0.9',
|
||||||
|
'Connection': 'keep-alive',
|
||||||
|
} as any;
|
||||||
|
};
|
||||||
|
|
||||||
// Initialize Trakt autosync
|
// Initialize Trakt autosync
|
||||||
const traktAutosync = useTraktAutosync({
|
const traktAutosync = useTraktAutosync({
|
||||||
id: id || '',
|
id: id || '',
|
||||||
|
|
@ -204,6 +214,10 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
const [errorDetails, setErrorDetails] = useState<string>('');
|
const [errorDetails, setErrorDetails] = useState<string>('');
|
||||||
const errorTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
const errorTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
|
// iOS startup timing diagnostics
|
||||||
|
const loadStartAtRef = useRef<number | null>(null);
|
||||||
|
const firstFrameAtRef = useRef<number | null>(null);
|
||||||
|
|
||||||
// Pause overlay state
|
// Pause overlay state
|
||||||
const [showPauseOverlay, setShowPauseOverlay] = useState(false);
|
const [showPauseOverlay, setShowPauseOverlay] = useState(false);
|
||||||
const pauseOverlayTimerRef = useRef<NodeJS.Timeout | null>(null);
|
const pauseOverlayTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
@ -614,9 +628,10 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
isSeeking.current = false;
|
isSeeking.current = false;
|
||||||
// Resume playback on iOS if we paused for seeking
|
// Resume playback on iOS if we paused for seeking
|
||||||
if (Platform.OS === 'ios') {
|
if (Platform.OS === 'ios') {
|
||||||
const shouldResume = wasPlayingBeforeDragRef.current || iosWasPausedDuringSeekRef.current === false;
|
const shouldResume = wasPlayingBeforeDragRef.current || iosWasPausedDuringSeekRef.current === false || isDragging;
|
||||||
|
// Aggressively resume on iOS after seek if user was playing or this was a drag
|
||||||
if (shouldResume) {
|
if (shouldResume) {
|
||||||
logger.log('[AndroidVideoPlayer] onSeek: resuming after drag/seek (iOS)');
|
logger.log('[AndroidVideoPlayer] onSeek: resuming after seek (iOS)');
|
||||||
setPaused(false);
|
setPaused(false);
|
||||||
} else {
|
} else {
|
||||||
logger.log('[AndroidVideoPlayer] onSeek: staying paused (iOS)');
|
logger.log('[AndroidVideoPlayer] onSeek: staying paused (iOS)');
|
||||||
|
|
@ -659,6 +674,13 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
const seekTime = Math.min(value, duration - END_EPSILON);
|
const seekTime = Math.min(value, duration - END_EPSILON);
|
||||||
seekToTime(seekTime);
|
seekToTime(seekTime);
|
||||||
pendingSeekValue.current = null;
|
pendingSeekValue.current = null;
|
||||||
|
// iOS safety: if the user was playing before drag, ensure resume shortly after seek
|
||||||
|
if (Platform.OS === 'ios' && wasPlayingBeforeDragRef.current) {
|
||||||
|
setTimeout(() => {
|
||||||
|
logger.log('[AndroidVideoPlayer] handleSlidingComplete: forcing resume after seek (iOS)');
|
||||||
|
setPaused(false);
|
||||||
|
}, 60);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Restart auto-hide timer after interaction finishes
|
// Restart auto-hide timer after interaction finishes
|
||||||
if (controlsTimeout.current) {
|
if (controlsTimeout.current) {
|
||||||
|
|
@ -2067,13 +2089,27 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
<Video
|
<Video
|
||||||
ref={videoRef}
|
ref={videoRef}
|
||||||
style={[styles.video, customVideoStyles, { transform: [{ scale: zoomScale }] }]}
|
style={[styles.video, customVideoStyles, { transform: [{ scale: zoomScale }] }]}
|
||||||
source={{ uri: currentStreamUrl, headers: headers || defaultAndroidHeaders(), type: (currentVideoType as any) }}
|
source={{ uri: currentStreamUrl, headers: headers || (Platform.OS === 'android' ? defaultAndroidHeaders() : defaultIosHeaders()), type: (currentVideoType as any) }}
|
||||||
paused={paused}
|
paused={paused}
|
||||||
|
onLoadStart={() => {
|
||||||
|
loadStartAtRef.current = Date.now();
|
||||||
|
logger.log('[AndroidVideoPlayer] onLoadStart');
|
||||||
|
}}
|
||||||
onProgress={handleProgress}
|
onProgress={handleProgress}
|
||||||
onLoad={(e) => {
|
onLoad={(e) => {
|
||||||
logger.log('[AndroidVideoPlayer] onLoad fired', { duration: e?.duration });
|
logger.log('[AndroidVideoPlayer] onLoad fired', { duration: e?.duration });
|
||||||
onLoad(e);
|
onLoad(e);
|
||||||
}}
|
}}
|
||||||
|
onReadyForDisplay={() => {
|
||||||
|
firstFrameAtRef.current = Date.now();
|
||||||
|
const startedAt = loadStartAtRef.current;
|
||||||
|
if (startedAt) {
|
||||||
|
const deltaMs = firstFrameAtRef.current - startedAt;
|
||||||
|
logger.log(`[AndroidVideoPlayer] First frame ready after ${deltaMs} ms (${Platform.OS})`);
|
||||||
|
} else {
|
||||||
|
logger.log('[AndroidVideoPlayer] First frame ready (no start timestamp)');
|
||||||
|
}
|
||||||
|
}}
|
||||||
onSeek={onSeek}
|
onSeek={onSeek}
|
||||||
onEnd={onEnd}
|
onEnd={onEnd}
|
||||||
onError={(err) => {
|
onError={(err) => {
|
||||||
|
|
@ -2096,14 +2132,13 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
ignoreSilentSwitch="ignore"
|
ignoreSilentSwitch="ignore"
|
||||||
mixWithOthers="inherit"
|
mixWithOthers="inherit"
|
||||||
progressUpdateInterval={1000}
|
progressUpdateInterval={1000}
|
||||||
bufferConfig={{
|
|
||||||
minBufferMs: 15000,
|
|
||||||
maxBufferMs: 50000,
|
|
||||||
bufferForPlaybackMs: 2500,
|
|
||||||
bufferForPlaybackAfterRebufferMs: 5000,
|
|
||||||
}}
|
|
||||||
maxBitRate={2000000}
|
maxBitRate={2000000}
|
||||||
disableFocus={true}
|
disableFocus={true}
|
||||||
|
// iOS AVPlayer startup tuning
|
||||||
|
automaticallyWaitsToMinimizeStalling={true as any}
|
||||||
|
preferredForwardBufferDuration={1 as any}
|
||||||
|
allowsExternalPlayback={false as any}
|
||||||
|
preventsDisplaySleepDuringVideoPlayback={true as any}
|
||||||
/>
|
/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue