mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-21 00:32:04 +00:00
fix TEST CP
This commit is contained in:
parent
1ee1a0c3f7
commit
f44c0df168
2 changed files with 152 additions and 56 deletions
|
|
@ -973,29 +973,39 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
if (selectedAudioTrack === -1 && rnVideoAudioTracks.length > 1) {
|
if (selectedAudioTrack === -1 && rnVideoAudioTracks.length > 1) {
|
||||||
logger.warn('[AndroidVideoPlayer] Detected disabled audio track, attempting fallback');
|
logger.warn('[AndroidVideoPlayer] Detected disabled audio track, attempting fallback');
|
||||||
|
|
||||||
// Find a fallback audio track (prefer stereo/standard formats)
|
// Find a fallback audio track (prioritize AAC/stereo over heavy codecs)
|
||||||
const fallbackTrack = rnVideoAudioTracks.find((track, index) => {
|
const fallbackTrack = rnVideoAudioTracks.find((track) => {
|
||||||
|
const trackName = (track.name || '').toLowerCase();
|
||||||
|
// Prefer AAC, stereo, or standard audio formats, avoid heavy codecs
|
||||||
|
const isCompatible = !trackName.includes('truehd') &&
|
||||||
|
!trackName.includes('dts') &&
|
||||||
|
!trackName.includes('atmos') &&
|
||||||
|
track.id !== selectedAudioTrack; // Don't select the same track
|
||||||
|
|
||||||
|
// Prioritize AAC and stereo tracks
|
||||||
|
const isPreferred = trackName.includes('aac') ||
|
||||||
|
trackName.includes('stereo') ||
|
||||||
|
trackName.includes('2.0') ||
|
||||||
|
trackName.includes('2ch');
|
||||||
|
|
||||||
|
return isCompatible && isPreferred;
|
||||||
|
}) || rnVideoAudioTracks.find((track) => {
|
||||||
|
// Fallback: any compatible track (even if not preferred)
|
||||||
const trackName = (track.name || '').toLowerCase();
|
const trackName = (track.name || '').toLowerCase();
|
||||||
const trackLang = (track.language || '').toLowerCase();
|
|
||||||
// Prefer stereo, AAC, or standard audio formats, avoid heavy codecs
|
|
||||||
return !trackName.includes('truehd') &&
|
return !trackName.includes('truehd') &&
|
||||||
!trackName.includes('dts') &&
|
!trackName.includes('dts') &&
|
||||||
!trackName.includes('dolby') &&
|
|
||||||
!trackName.includes('atmos') &&
|
!trackName.includes('atmos') &&
|
||||||
!trackName.includes('7.1') &&
|
track.id !== selectedAudioTrack;
|
||||||
!trackName.includes('5.1') &&
|
|
||||||
index !== selectedAudioTrack; // Don't select the same track
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (fallbackTrack) {
|
if (fallbackTrack) {
|
||||||
const fallbackIndex = rnVideoAudioTracks.indexOf(fallbackTrack);
|
logger.warn(`[AndroidVideoPlayer] Switching to fallback audio track: ${fallbackTrack.name || 'Unknown'} (id: ${fallbackTrack.id})`);
|
||||||
logger.warn(`[AndroidVideoPlayer] Switching to fallback audio track: ${fallbackTrack.name || 'Unknown'} (index: ${fallbackIndex})`);
|
|
||||||
|
|
||||||
// Increment fallback attempts counter
|
// Increment fallback attempts counter
|
||||||
setAudioTrackFallbackAttempts(prev => prev + 1);
|
setAudioTrackFallbackAttempts(prev => prev + 1);
|
||||||
|
|
||||||
// Switch to fallback audio track
|
// Switch to fallback audio track
|
||||||
setSelectedAudioTrack(fallbackIndex);
|
setSelectedAudioTrack(fallbackTrack.id);
|
||||||
|
|
||||||
// Brief pause to allow track switching
|
// Brief pause to allow track switching
|
||||||
setPaused(true);
|
setPaused(true);
|
||||||
|
|
@ -1189,23 +1199,39 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
});
|
});
|
||||||
setRnVideoAudioTracks(formattedAudioTracks);
|
setRnVideoAudioTracks(formattedAudioTracks);
|
||||||
|
|
||||||
// Auto-select English audio track if available, otherwise first track
|
// Auto-select compatible audio track (prioritize AAC/stereo over heavy codecs)
|
||||||
if (selectedAudioTrack === null && formattedAudioTracks.length > 0) {
|
if (selectedAudioTrack === null && formattedAudioTracks.length > 0) {
|
||||||
// Look for English track first
|
// First, try to find a compatible English track
|
||||||
const englishTrack = formattedAudioTracks.find((track: {id: number, name: string, language?: string}) => {
|
const compatibleEnglishTrack = formattedAudioTracks.find((track: {id: number, name: string, language?: string}) => {
|
||||||
const lang = (track.language || '').toLowerCase();
|
const lang = (track.language || '').toLowerCase();
|
||||||
return lang === 'english' || lang === 'en' || lang === 'eng' ||
|
const trackName = (track.name || '').toLowerCase();
|
||||||
(track.name && track.name.toLowerCase().includes('english'));
|
const isEnglish = lang === 'english' || lang === 'en' || lang === 'eng' ||
|
||||||
|
(track.name && track.name.toLowerCase().includes('english'));
|
||||||
|
const isCompatible = !trackName.includes('truehd') &&
|
||||||
|
!trackName.includes('dts') &&
|
||||||
|
!trackName.includes('atmos');
|
||||||
|
return isEnglish && isCompatible;
|
||||||
});
|
});
|
||||||
|
|
||||||
const selectedTrack = englishTrack || formattedAudioTracks[0];
|
// If no compatible English track, find any compatible track
|
||||||
|
const compatibleTrack = compatibleEnglishTrack || formattedAudioTracks.find((track: {id: number, name: string, language?: string}) => {
|
||||||
|
const trackName = (track.name || '').toLowerCase();
|
||||||
|
return !trackName.includes('truehd') &&
|
||||||
|
!trackName.includes('dts') &&
|
||||||
|
!trackName.includes('atmos');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fallback to any track if no compatible ones found
|
||||||
|
const selectedTrack = compatibleTrack || formattedAudioTracks[0];
|
||||||
setSelectedAudioTrack(selectedTrack.id);
|
setSelectedAudioTrack(selectedTrack.id);
|
||||||
|
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
if (englishTrack) {
|
if (compatibleEnglishTrack) {
|
||||||
logger.log(`[AndroidVideoPlayer] Auto-selected English audio track: ${selectedTrack.name} (ID: ${selectedTrack.id})`);
|
logger.log(`[AndroidVideoPlayer] Auto-selected compatible English audio track: ${selectedTrack.name} (ID: ${selectedTrack.id})`);
|
||||||
|
} else if (compatibleTrack) {
|
||||||
|
logger.log(`[AndroidVideoPlayer] Auto-selected compatible audio track: ${selectedTrack.name} (ID: ${selectedTrack.id})`);
|
||||||
} else {
|
} else {
|
||||||
logger.log(`[AndroidVideoPlayer] No English track found, auto-selected first audio track: ${selectedTrack.name} (ID: ${selectedTrack.id})`);
|
logger.log(`[AndroidVideoPlayer] No compatible tracks found, auto-selected first audio track: ${selectedTrack.name} (ID: ${selectedTrack.id})`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1440,31 +1466,41 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
error.error.errorException.includes('audio/eac3')) ||
|
error.error.errorException.includes('audio/eac3')) ||
|
||||||
(error?.error?.errorException &&
|
(error?.error?.errorException &&
|
||||||
error.error.errorException.includes('Dolby Digital Plus')) ||
|
error.error.errorException.includes('Dolby Digital Plus')) ||
|
||||||
(error?.message && /(trhd|truehd|true\s?hd|dts|dolby|atmos|e-ac3|ac3)/i.test(error.message)) ||
|
(error?.message && /(trhd|truehd|true\s?hd|dts|atmos)/i.test(error.message)) ||
|
||||||
(error?.error?.message && /(trhd|truehd|true\s?hd|dts|dolby|atmos|e-ac3|ac3)/i.test(error.error.message)) ||
|
(error?.error?.message && /(trhd|truehd|true\s?hd|dts|atmos)/i.test(error.error.message)) ||
|
||||||
(error?.title && /codec not supported/i.test(error.title));
|
(error?.title && /codec not supported/i.test(error.title));
|
||||||
|
|
||||||
// Handle audio codec errors with automatic fallback
|
// Handle audio codec errors with automatic fallback
|
||||||
if (isAudioCodecError && rnVideoAudioTracks.length > 1) {
|
if (isAudioCodecError && rnVideoAudioTracks.length > 0) {
|
||||||
logger.warn('[AndroidVideoPlayer] Audio codec error detected, attempting audio track fallback');
|
logger.warn('[AndroidVideoPlayer] Audio codec error detected, attempting audio track fallback');
|
||||||
|
|
||||||
// Find a fallback audio track (prefer stereo/standard formats)
|
// Find a fallback audio track (prioritize AAC/stereo over heavy codecs)
|
||||||
const fallbackTrack = rnVideoAudioTracks.find((track, index) => {
|
const fallbackTrack = rnVideoAudioTracks.find((track) => {
|
||||||
|
const trackName = (track.name || '').toLowerCase();
|
||||||
|
// Prefer AAC, stereo, or standard audio formats, avoid heavy codecs
|
||||||
|
const isCompatible = !trackName.includes('truehd') &&
|
||||||
|
!trackName.includes('dts') &&
|
||||||
|
!trackName.includes('atmos') &&
|
||||||
|
track.id !== selectedAudioTrack; // Don't select the same track
|
||||||
|
|
||||||
|
// Prioritize AAC and stereo tracks
|
||||||
|
const isPreferred = trackName.includes('aac') ||
|
||||||
|
trackName.includes('stereo') ||
|
||||||
|
trackName.includes('2.0') ||
|
||||||
|
trackName.includes('2ch');
|
||||||
|
|
||||||
|
return isCompatible && isPreferred;
|
||||||
|
}) || rnVideoAudioTracks.find((track) => {
|
||||||
|
// Fallback: any compatible track (even if not preferred)
|
||||||
const trackName = (track.name || '').toLowerCase();
|
const trackName = (track.name || '').toLowerCase();
|
||||||
const trackLang = (track.language || '').toLowerCase();
|
|
||||||
// Prefer stereo, AAC, or standard audio formats, avoid heavy codecs
|
|
||||||
return !trackName.includes('truehd') &&
|
return !trackName.includes('truehd') &&
|
||||||
!trackName.includes('dts') &&
|
!trackName.includes('dts') &&
|
||||||
!trackName.includes('dolby') &&
|
|
||||||
!trackName.includes('atmos') &&
|
!trackName.includes('atmos') &&
|
||||||
!trackName.includes('7.1') &&
|
track.id !== selectedAudioTrack;
|
||||||
!trackName.includes('5.1') &&
|
|
||||||
index !== selectedAudioTrack; // Don't select the same track
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (fallbackTrack) {
|
if (fallbackTrack) {
|
||||||
const fallbackIndex = rnVideoAudioTracks.indexOf(fallbackTrack);
|
logger.warn(`[AndroidVideoPlayer] Switching to fallback audio track: ${fallbackTrack.name || 'Unknown'} (id: ${fallbackTrack.id})`);
|
||||||
logger.warn(`[AndroidVideoPlayer] Switching to fallback audio track: ${fallbackTrack.name || 'Unknown'} (index: ${fallbackIndex})`);
|
|
||||||
|
|
||||||
// Clear any existing error state
|
// Clear any existing error state
|
||||||
if (errorTimeoutRef.current) {
|
if (errorTimeoutRef.current) {
|
||||||
|
|
@ -1474,7 +1510,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
safeSetState(() => setShowErrorModal(false));
|
safeSetState(() => setShowErrorModal(false));
|
||||||
|
|
||||||
// Switch to fallback audio track
|
// Switch to fallback audio track
|
||||||
setSelectedAudioTrack(fallbackIndex);
|
setSelectedAudioTrack(fallbackTrack.id);
|
||||||
|
|
||||||
// Brief pause to allow track switching
|
// Brief pause to allow track switching
|
||||||
setPaused(true);
|
setPaused(true);
|
||||||
|
|
@ -1484,6 +1520,21 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
return; // Don't show error UI, attempt recovery
|
return; // Don't show error UI, attempt recovery
|
||||||
|
} else {
|
||||||
|
// As a last resort, disable audio entirely to keep video playing
|
||||||
|
logger.warn('[AndroidVideoPlayer] No compatible audio track found. Disabling audio to keep playback.');
|
||||||
|
if (errorTimeoutRef.current) {
|
||||||
|
clearTimeout(errorTimeoutRef.current);
|
||||||
|
errorTimeoutRef.current = null;
|
||||||
|
}
|
||||||
|
safeSetState(() => setShowErrorModal(false));
|
||||||
|
setSelectedAudioTrack(-1); // handled as DISABLED by selectedTextTrack prop
|
||||||
|
setPaused(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!isMounted.current) return;
|
||||||
|
setPaused(false);
|
||||||
|
}, 300);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1631,23 +1682,30 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use safeSetState to prevent crashes on iOS when component is unmounted
|
// For audio codec errors we already attempted recovery; avoid showing the modal
|
||||||
safeSetState(() => {
|
if (!isAudioCodecError) {
|
||||||
setErrorDetails(errorMessage);
|
// Use safeSetState to prevent crashes on iOS when component is unmounted
|
||||||
setShowErrorModal(true);
|
safeSetState(() => {
|
||||||
});
|
setErrorDetails(errorMessage);
|
||||||
|
setShowErrorModal(true);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Clear any existing timeout
|
// Clear any existing timeout
|
||||||
if (errorTimeoutRef.current) {
|
if (errorTimeoutRef.current) {
|
||||||
clearTimeout(errorTimeoutRef.current);
|
clearTimeout(errorTimeoutRef.current);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-exit after 5 seconds if user doesn't dismiss
|
// Auto-exit only when a modal is actually visible
|
||||||
errorTimeoutRef.current = setTimeout(() => {
|
if (showErrorModal) {
|
||||||
if (isMounted.current) {
|
errorTimeoutRef.current = setTimeout(() => {
|
||||||
handleErrorExit();
|
if (isMounted.current) {
|
||||||
}
|
handleErrorExit();
|
||||||
}, 5000);
|
}
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
} catch (handlerError) {
|
} catch (handlerError) {
|
||||||
// Fallback error handling to prevent crashes during error processing
|
// Fallback error handling to prevent crashes during error processing
|
||||||
logger.error('[AndroidVideoPlayer] Error in error handler:', handlerError);
|
logger.error('[AndroidVideoPlayer] Error in error handler:', handlerError);
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,13 @@ export const AudioTrackModal: React.FC<AudioTrackModalProps> = ({
|
||||||
// If no track is selected, show the first track as selected
|
// If no track is selected, show the first track as selected
|
||||||
const isSelected = selectedAudioTrack === track.id ||
|
const isSelected = selectedAudioTrack === track.id ||
|
||||||
(selectedAudioTrack === null && track.id === vlcAudioTracks[0]?.id);
|
(selectedAudioTrack === null && track.id === vlcAudioTracks[0]?.id);
|
||||||
|
|
||||||
|
// Check if track uses unsupported codec
|
||||||
|
const trackName = (track.name || '').toLowerCase();
|
||||||
|
const isUnsupported = trackName.includes('truehd') ||
|
||||||
|
trackName.includes('dts') ||
|
||||||
|
trackName.includes('atmos');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
key={track.id}
|
key={track.id}
|
||||||
|
|
@ -158,8 +165,15 @@ export const AudioTrackModal: React.FC<AudioTrackModalProps> = ({
|
||||||
padding: 16,
|
padding: 16,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: isSelected ? 'rgba(34, 197, 94, 0.3)' : 'rgba(255, 255, 255, 0.1)',
|
borderColor: isSelected ? 'rgba(34, 197, 94, 0.3)' : 'rgba(255, 255, 255, 0.1)',
|
||||||
|
opacity: isUnsupported ? 0.5 : 1,
|
||||||
}}
|
}}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
|
if (isUnsupported) {
|
||||||
|
if (DEBUG_MODE) {
|
||||||
|
logger.log(`[AudioTrackModal] Attempted to select unsupported track: ${track.id} (${track.name})`);
|
||||||
|
}
|
||||||
|
return; // Don't allow selection of unsupported tracks
|
||||||
|
}
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
logger.log(`[AudioTrackModal] Selecting track: ${track.id} (${track.name})`);
|
logger.log(`[AudioTrackModal] Selecting track: ${track.id} (${track.name})`);
|
||||||
}
|
}
|
||||||
|
|
@ -169,30 +183,54 @@ export const AudioTrackModal: React.FC<AudioTrackModalProps> = ({
|
||||||
setShowAudioModal(false);
|
setShowAudioModal(false);
|
||||||
}, 200);
|
}, 200);
|
||||||
}}
|
}}
|
||||||
activeOpacity={0.7}
|
activeOpacity={isUnsupported ? 1 : 0.7}
|
||||||
>
|
>
|
||||||
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
<View style={{ flex: 1 }}>
|
<View style={{ flex: 1 }}>
|
||||||
<Text style={{
|
<View style={{ flexDirection: 'row', alignItems: 'center', marginBottom: 4 }}>
|
||||||
color: '#FFFFFF',
|
<Text style={{
|
||||||
fontSize: 15,
|
color: isUnsupported ? 'rgba(255, 255, 255, 0.4)' : '#FFFFFF',
|
||||||
fontWeight: '500',
|
fontSize: 15,
|
||||||
marginBottom: 4,
|
fontWeight: '500',
|
||||||
}}>
|
flex: 1,
|
||||||
{getTrackDisplayName(track)}
|
}}>
|
||||||
</Text>
|
{getTrackDisplayName(track)}
|
||||||
|
</Text>
|
||||||
|
{isUnsupported && (
|
||||||
|
<View style={{
|
||||||
|
backgroundColor: 'rgba(255, 107, 107, 0.2)',
|
||||||
|
borderRadius: 8,
|
||||||
|
paddingHorizontal: 8,
|
||||||
|
paddingVertical: 2,
|
||||||
|
marginLeft: 8,
|
||||||
|
}}>
|
||||||
|
<Text style={{
|
||||||
|
color: '#FF6B6B',
|
||||||
|
fontSize: 10,
|
||||||
|
fontWeight: '600',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
}}>
|
||||||
|
Unsupported
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
{track.language && (
|
{track.language && (
|
||||||
<Text style={{
|
<Text style={{
|
||||||
color: 'rgba(255, 255, 255, 0.6)',
|
color: isUnsupported ? 'rgba(255, 255, 255, 0.3)' : 'rgba(255, 255, 255, 0.6)',
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
}}>
|
}}>
|
||||||
{track.language.toUpperCase()}
|
{track.language.toUpperCase()}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
{isSelected && (
|
{isSelected && !isUnsupported && (
|
||||||
<MaterialIcons name="check" size={20} color="#22C55E" />
|
<MaterialIcons name="check" size={20} color="#22C55E" />
|
||||||
)}
|
)}
|
||||||
|
{isSelected && isUnsupported && (
|
||||||
|
<MaterialIcons name="warning" size={20} color="#FF6B6B" />
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue