mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-20 16:22:04 +00:00
cleaned VLC
This commit is contained in:
parent
18815b8233
commit
688ea2b9d3
6 changed files with 65 additions and 172 deletions
105
package-lock.json
generated
105
package-lock.json
generated
|
|
@ -72,7 +72,6 @@
|
||||||
"react-native-svg": "15.8.0",
|
"react-native-svg": "15.8.0",
|
||||||
"react-native-url-polyfill": "^2.0.0",
|
"react-native-url-polyfill": "^2.0.0",
|
||||||
"react-native-video": "^6.12.0",
|
"react-native-video": "^6.12.0",
|
||||||
"react-native-vlc-media-player": "^1.0.87",
|
|
||||||
"react-native-web": "~0.19.13",
|
"react-native-web": "~0.19.13",
|
||||||
"react-native-wheel-color-picker": "^1.3.1"
|
"react-native-wheel-color-picker": "^1.3.1"
|
||||||
},
|
},
|
||||||
|
|
@ -12738,15 +12737,6 @@
|
||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-native-slider": {
|
|
||||||
"version": "0.11.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-native-slider/-/react-native-slider-0.11.0.tgz",
|
|
||||||
"integrity": "sha512-jV9K87eu9uWr0uJIyrSpBLnCKvVlOySC2wynq9TFCdV9oGgjt7Niq8Q1A8R8v+5GHsuBw/s8vEj1AAkkUi+u+w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"prop-types": "^15.5.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-native-svg": {
|
"node_modules/react-native-svg": {
|
||||||
"version": "15.8.0",
|
"version": "15.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.8.0.tgz",
|
||||||
|
|
@ -12898,91 +12888,6 @@
|
||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-native-vector-icons": {
|
|
||||||
"version": "9.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-9.2.0.tgz",
|
|
||||||
"integrity": "sha512-wKYLaFuQST/chH3AJRjmOLoLy3JEs1JR6zMNgTaemFpNoXs0ztRnTxcxFD9xhX7cJe1/zoN5BpQYe7kL0m5yyA==",
|
|
||||||
"deprecated": "react-native-vector-icons package has moved to a new model of per-icon-family packages. See the https://github.com/oblador/react-native-vector-icons/blob/master/MIGRATION.md on how to migrate",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"prop-types": "^15.7.2",
|
|
||||||
"yargs": "^16.1.1"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"fa5-upgrade": "bin/fa5-upgrade.sh",
|
|
||||||
"generate-icon": "bin/generate-icon.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-native-vector-icons/node_modules/cliui": {
|
|
||||||
"version": "7.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
|
||||||
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"string-width": "^4.2.0",
|
|
||||||
"strip-ansi": "^6.0.0",
|
|
||||||
"wrap-ansi": "^7.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-native-vector-icons/node_modules/emoji-regex": {
|
|
||||||
"version": "8.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
|
||||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/react-native-vector-icons/node_modules/string-width": {
|
|
||||||
"version": "4.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
|
||||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"emoji-regex": "^8.0.0",
|
|
||||||
"is-fullwidth-code-point": "^3.0.0",
|
|
||||||
"strip-ansi": "^6.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-native-vector-icons/node_modules/strip-ansi": {
|
|
||||||
"version": "6.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-regex": "^5.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-native-vector-icons/node_modules/yargs": {
|
|
||||||
"version": "16.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
|
|
||||||
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"cliui": "^7.0.2",
|
|
||||||
"escalade": "^3.1.1",
|
|
||||||
"get-caller-file": "^2.0.5",
|
|
||||||
"require-directory": "^2.1.1",
|
|
||||||
"string-width": "^4.2.0",
|
|
||||||
"y18n": "^5.0.5",
|
|
||||||
"yargs-parser": "^20.2.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-native-vector-icons/node_modules/yargs-parser": {
|
|
||||||
"version": "20.2.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
|
|
||||||
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
|
|
||||||
"license": "ISC",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-native-video": {
|
"node_modules/react-native-video": {
|
||||||
"version": "6.16.1",
|
"version": "6.16.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-video/-/react-native-video-6.16.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-video/-/react-native-video-6.16.1.tgz",
|
||||||
|
|
@ -12993,16 +12898,6 @@
|
||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-native-vlc-media-player": {
|
|
||||||
"version": "1.0.94",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-native-vlc-media-player/-/react-native-vlc-media-player-1.0.94.tgz",
|
|
||||||
"integrity": "sha512-6Ee09NY3ir4UN7mSFv8N+4GBiUQzAyVWU54ilwBAOZO8ICU2aJJmOq9ptYxvswIiRcbSz8Z+aB7LnjIlMR6WoQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"react-native-slider": "^0.11.0",
|
|
||||||
"react-native-vector-icons": "^9.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-native-web": {
|
"node_modules/react-native-web": {
|
||||||
"version": "0.19.13",
|
"version": "0.19.13",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.19.13.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.19.13.tgz",
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,6 @@
|
||||||
"react-native-svg": "15.8.0",
|
"react-native-svg": "15.8.0",
|
||||||
"react-native-url-polyfill": "^2.0.0",
|
"react-native-url-polyfill": "^2.0.0",
|
||||||
"react-native-video": "^6.12.0",
|
"react-native-video": "^6.12.0",
|
||||||
"react-native-vlc-media-player": "^1.0.87",
|
|
||||||
"react-native-web": "~0.19.13",
|
"react-native-web": "~0.19.13",
|
||||||
"react-native-wheel-color-picker": "^1.3.1"
|
"react-native-wheel-color-picker": "^1.3.1"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ import * as Brightness from 'expo-brightness';
|
||||||
const VideoPlayer: React.FC = () => {
|
const VideoPlayer: React.FC = () => {
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const route = useRoute<RouteProp<RootStackParamList, 'Player'>>();
|
const route = useRoute<RouteProp<RootStackParamList, 'Player'>>();
|
||||||
const { uri, headers, forceVlc, streamProvider } = route.params as any;
|
const { uri, headers, streamProvider } = route.params as any;
|
||||||
|
|
||||||
// Detect if stream is MKV format
|
// Detect if stream is MKV format
|
||||||
const isMkvFile = isMkvStream(uri, headers);
|
const isMkvFile = isMkvStream(uri, headers);
|
||||||
|
|
@ -150,8 +150,8 @@ const VideoPlayer: React.FC = () => {
|
||||||
const [isBackdropLoaded, setIsBackdropLoaded] = useState(false);
|
const [isBackdropLoaded, setIsBackdropLoaded] = useState(false);
|
||||||
const backdropImageOpacityAnim = useRef(new Animated.Value(0)).current;
|
const backdropImageOpacityAnim = useRef(new Animated.Value(0)).current;
|
||||||
const [isBuffering, setIsBuffering] = useState(false);
|
const [isBuffering, setIsBuffering] = useState(false);
|
||||||
const [vlcAudioTracks, setVlcAudioTracks] = useState<Array<{ id: number, name: string, language?: string }>>([]);
|
const [ksAudioTracks, setKsAudioTracks] = useState<Array<{ id: number, name: string, language?: string }>>([]);
|
||||||
const [vlcTextTracks, setVlcTextTracks] = useState<Array<{ id: number, name: string, language?: string }>>([]);
|
const [ksTextTracks, setKsTextTracks] = useState<Array<{ id: number, name: string, language?: string }>>([]);
|
||||||
const [isPlayerReady, setIsPlayerReady] = useState(false);
|
const [isPlayerReady, setIsPlayerReady] = useState(false);
|
||||||
// Removed progressAnim and progressBarRef - no longer needed with React Native Community Slider
|
// Removed progressAnim and progressBarRef - no longer needed with React Native Community Slider
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
|
|
@ -193,10 +193,10 @@ const VideoPlayer: React.FC = () => {
|
||||||
const [isLoadingSubtitleList, setIsLoadingSubtitleList] = useState<boolean>(false);
|
const [isLoadingSubtitleList, setIsLoadingSubtitleList] = useState<boolean>(false);
|
||||||
const [showSourcesModal, setShowSourcesModal] = useState<boolean>(false);
|
const [showSourcesModal, setShowSourcesModal] = useState<boolean>(false);
|
||||||
const [availableStreams, setAvailableStreams] = useState<{ [providerId: string]: { streams: any[]; addonName: string } }>(passedAvailableStreams || {});
|
const [availableStreams, setAvailableStreams] = useState<{ [providerId: string]: { streams: any[]; addonName: string } }>(passedAvailableStreams || {});
|
||||||
// Decode URLs for VLC compatibility - VLC has issues with encoded URLs
|
// Decode URLs for KSPlayer compatibility - KSPlayer handles encoded URLs better
|
||||||
const decodeUrlForVlc = (url: string): string => {
|
const decodeUrlForKsPlayer = (url: string): string => {
|
||||||
try {
|
try {
|
||||||
// Always decode URLs for VLC as it has trouble with encoded characters
|
// KSPlayer handles encoded URLs well, but decode for consistency
|
||||||
const decoded = decodeURIComponent(url);
|
const decoded = decodeURIComponent(url);
|
||||||
return decoded;
|
return decoded;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -205,7 +205,7 @@ const VideoPlayer: React.FC = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const [currentStreamUrl, setCurrentStreamUrl] = useState<string>(decodeUrlForVlc(uri));
|
const [currentStreamUrl, setCurrentStreamUrl] = useState<string>(decodeUrlForKsPlayer(uri));
|
||||||
const [isChangingSource, setIsChangingSource] = useState<boolean>(false);
|
const [isChangingSource, setIsChangingSource] = useState<boolean>(false);
|
||||||
const [showErrorModal, setShowErrorModal] = useState(false);
|
const [showErrorModal, setShowErrorModal] = useState(false);
|
||||||
const [errorDetails, setErrorDetails] = useState<string>('');
|
const [errorDetails, setErrorDetails] = useState<string>('');
|
||||||
|
|
@ -244,20 +244,20 @@ const VideoPlayer: React.FC = () => {
|
||||||
const castDetailsScale = useRef(new Animated.Value(0.95)).current;
|
const castDetailsScale = useRef(new Animated.Value(0.95)).current;
|
||||||
|
|
||||||
// Volume and brightness controls
|
// Volume and brightness controls
|
||||||
const [volume, setVolume] = useState(100); // VLC uses 0-100 range
|
const [volume, setVolume] = useState(100); // KSPlayer uses 0-100 range
|
||||||
const [brightness, setBrightness] = useState(1.0);
|
const [brightness, setBrightness] = useState(1.0);
|
||||||
const [showVolumeOverlay, setShowVolumeOverlay] = useState(false);
|
const [showVolumeOverlay, setShowVolumeOverlay] = useState(false);
|
||||||
const [showBrightnessOverlay, setShowBrightnessOverlay] = useState(false);
|
const [showBrightnessOverlay, setShowBrightnessOverlay] = useState(false);
|
||||||
const [showVlcVolumeWarning, setShowVlcVolumeWarning] = useState(false);
|
const [showKsVolumeWarning, setShowKsVolumeWarning] = useState(false);
|
||||||
const [hasShownVlcWarning, setHasShownVlcWarning] = useState(false);
|
const [hasShownKsWarning, setHasShownKsWarning] = useState(false);
|
||||||
|
|
||||||
// Load VLC warning state from storage
|
// Load KSPlayer warning state from storage
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadWarningState = async () => {
|
const loadWarningState = async () => {
|
||||||
try {
|
try {
|
||||||
const warningShown = await AsyncStorage.getItem('vlc_volume_warning_shown');
|
const warningShown = await AsyncStorage.getItem('ks_volume_warning_shown');
|
||||||
if (warningShown === 'true') {
|
if (warningShown === 'true') {
|
||||||
setHasShownVlcWarning(true);
|
setHasShownKsWarning(true);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore storage errors
|
// Ignore storage errors
|
||||||
|
|
@ -428,17 +428,17 @@ const VideoPlayer: React.FC = () => {
|
||||||
const { translationY, state } = event.nativeEvent;
|
const { translationY, state } = event.nativeEvent;
|
||||||
|
|
||||||
if (state === State.ACTIVE) {
|
if (state === State.ACTIVE) {
|
||||||
// Show VLC volume warning only once per session
|
// Show KSPlayer volume warning only once per session
|
||||||
if (!showVlcVolumeWarning && !hasShownVlcWarning) {
|
if (!showKsVolumeWarning && !hasShownKsWarning) {
|
||||||
setShowVlcVolumeWarning(true);
|
setShowKsVolumeWarning(true);
|
||||||
setHasShownVlcWarning(true);
|
setHasShownKsWarning(true);
|
||||||
|
|
||||||
// Save to storage that warning has been shown
|
// Save to storage that warning has been shown
|
||||||
AsyncStorage.setItem('vlc_volume_warning_shown', 'true').catch(() => {});
|
AsyncStorage.setItem('ks_volume_warning_shown', 'true').catch(() => {});
|
||||||
|
|
||||||
// Hide warning after 4 seconds
|
// Hide warning after 4 seconds
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setShowVlcVolumeWarning(false);
|
setShowKsVolumeWarning(false);
|
||||||
}, 4000);
|
}, 4000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -552,10 +552,10 @@ const VideoPlayer: React.FC = () => {
|
||||||
startOpeningAnimation();
|
startOpeningAnimation();
|
||||||
|
|
||||||
// Initialize current volume and brightness levels
|
// Initialize current volume and brightness levels
|
||||||
// Volume starts at 100 (full volume) for VLC
|
// Volume starts at 100 (full volume) for KSPlayer
|
||||||
setVolume(100);
|
setVolume(100);
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
logger.log(`[VideoPlayer] Initial volume: 100 (VLC native)`);
|
logger.log(`[VideoPlayer] Initial volume: 100 (KSPlayer native)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -901,11 +901,11 @@ const VideoPlayer: React.FC = () => {
|
||||||
setLastAudioTrackCheck(now);
|
setLastAudioTrackCheck(now);
|
||||||
|
|
||||||
// Check if audio track is disabled (-1) and we have available tracks
|
// Check if audio track is disabled (-1) and we have available tracks
|
||||||
if (selectedAudioTrack === -1 && vlcAudioTracks.length > 1) {
|
if (selectedAudioTrack === -1 && ksAudioTracks.length > 1) {
|
||||||
logger.warn('[VideoPlayer] Detected disabled audio track, attempting fallback');
|
logger.warn('[VideoPlayer] Detected disabled audio track, attempting fallback');
|
||||||
|
|
||||||
// Find a fallback audio track (prefer stereo/standard formats)
|
// Find a fallback audio track (prefer stereo/standard formats)
|
||||||
const fallbackTrack = vlcAudioTracks.find((track, index) => {
|
const fallbackTrack = ksAudioTracks.find((track, index) => {
|
||||||
const trackName = (track.name || '').toLowerCase();
|
const trackName = (track.name || '').toLowerCase();
|
||||||
const trackLang = (track.language || '').toLowerCase();
|
const trackLang = (track.language || '').toLowerCase();
|
||||||
// Prefer stereo, AAC, or standard audio formats, avoid heavy codecs
|
// Prefer stereo, AAC, or standard audio formats, avoid heavy codecs
|
||||||
|
|
@ -919,7 +919,7 @@ const VideoPlayer: React.FC = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (fallbackTrack) {
|
if (fallbackTrack) {
|
||||||
const fallbackIndex = vlcAudioTracks.indexOf(fallbackTrack);
|
const fallbackIndex = ksAudioTracks.indexOf(fallbackTrack);
|
||||||
logger.warn(`[VideoPlayer] Switching to fallback audio track: ${fallbackTrack.name || 'Unknown'} (index: ${fallbackIndex})`);
|
logger.warn(`[VideoPlayer] Switching to fallback audio track: ${fallbackTrack.name || 'Unknown'} (index: ${fallbackIndex})`);
|
||||||
|
|
||||||
// Increment fallback attempts counter
|
// Increment fallback attempts counter
|
||||||
|
|
@ -1073,7 +1073,7 @@ const VideoPlayer: React.FC = () => {
|
||||||
language: trackLanguage,
|
language: trackLanguage,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
setVlcAudioTracks(formattedAudioTracks);
|
setKsAudioTracks(formattedAudioTracks);
|
||||||
|
|
||||||
// Auto-select English audio track if available, otherwise first track
|
// Auto-select English audio track if available, otherwise first track
|
||||||
if (selectedAudioTrack === null && formattedAudioTracks.length > 0) {
|
if (selectedAudioTrack === null && formattedAudioTracks.length > 0) {
|
||||||
|
|
@ -1110,7 +1110,7 @@ const VideoPlayer: React.FC = () => {
|
||||||
isImageSubtitle: track.isImageSubtitle || false
|
isImageSubtitle: track.isImageSubtitle || false
|
||||||
}));
|
}));
|
||||||
|
|
||||||
setVlcTextTracks(formattedTextTracks);
|
setKsTextTracks(formattedTextTracks);
|
||||||
|
|
||||||
// Auto-select English subtitle track if available
|
// Auto-select English subtitle track if available
|
||||||
if (selectedTextTrack === -1 && !useCustomSubtitles && formattedTextTracks.length > 0) {
|
if (selectedTextTrack === -1 && !useCustomSubtitles && formattedTextTracks.length > 0) {
|
||||||
|
|
@ -1351,11 +1351,11 @@ const VideoPlayer: React.FC = () => {
|
||||||
(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 && vlcAudioTracks.length > 1) {
|
if (isAudioCodecError && ksAudioTracks.length > 1) {
|
||||||
logger.warn('[VideoPlayer] Audio codec error detected, attempting audio track fallback');
|
logger.warn('[VideoPlayer] Audio codec error detected, attempting audio track fallback');
|
||||||
|
|
||||||
// Find a fallback audio track (prefer stereo/standard formats)
|
// Find a fallback audio track (prefer stereo/standard formats)
|
||||||
const fallbackTrack = vlcAudioTracks.find((track, index) => {
|
const fallbackTrack = ksAudioTracks.find((track, index) => {
|
||||||
const trackName = (track.name || '').toLowerCase();
|
const trackName = (track.name || '').toLowerCase();
|
||||||
const trackLang = (track.language || '').toLowerCase();
|
const trackLang = (track.language || '').toLowerCase();
|
||||||
// Prefer stereo, AAC, or standard audio formats, avoid heavy codecs
|
// Prefer stereo, AAC, or standard audio formats, avoid heavy codecs
|
||||||
|
|
@ -1369,7 +1369,7 @@ const VideoPlayer: React.FC = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (fallbackTrack) {
|
if (fallbackTrack) {
|
||||||
const fallbackIndex = vlcAudioTracks.indexOf(fallbackTrack);
|
const fallbackIndex = ksAudioTracks.indexOf(fallbackTrack);
|
||||||
logger.warn(`[VideoPlayer] Switching to fallback audio track: ${fallbackTrack.name || 'Unknown'} (index: ${fallbackIndex})`);
|
logger.warn(`[VideoPlayer] Switching to fallback audio track: ${fallbackTrack.name || 'Unknown'} (index: ${fallbackIndex})`);
|
||||||
|
|
||||||
// Clear any existing error state
|
// Clear any existing error state
|
||||||
|
|
@ -1478,18 +1478,18 @@ const VideoPlayer: React.FC = () => {
|
||||||
const selectAudioTrack = (trackId: number) => {
|
const selectAudioTrack = (trackId: number) => {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
logger.log(`[VideoPlayer] Selecting audio track: ${trackId}`);
|
logger.log(`[VideoPlayer] Selecting audio track: ${trackId}`);
|
||||||
logger.log(`[VideoPlayer] Available tracks:`, vlcAudioTracks);
|
logger.log(`[VideoPlayer] Available tracks:`, ksAudioTracks);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate that the track exists
|
// Validate that the track exists
|
||||||
const trackExists = vlcAudioTracks.some(track => track.id === trackId);
|
const trackExists = ksAudioTracks.some(track => track.id === trackId);
|
||||||
if (!trackExists) {
|
if (!trackExists) {
|
||||||
logger.error(`[VideoPlayer] Audio track ${trackId} not found in available tracks`);
|
logger.error(`[VideoPlayer] Audio track ${trackId} not found in available tracks`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the selected track info for logging
|
// Get the selected track info for logging
|
||||||
const selectedTrack = vlcAudioTracks.find(track => track.id === trackId);
|
const selectedTrack = ksAudioTracks.find(track => track.id === trackId);
|
||||||
if (selectedTrack && DEBUG_MODE) {
|
if (selectedTrack && DEBUG_MODE) {
|
||||||
logger.log(`[VideoPlayer] Switching to track: ${selectedTrack.name} (${selectedTrack.language})`);
|
logger.log(`[VideoPlayer] Switching to track: ${selectedTrack.name} (${selectedTrack.language})`);
|
||||||
|
|
||||||
|
|
@ -1541,12 +1541,12 @@ const VideoPlayer: React.FC = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Ensure native VLC text tracks are disabled when using custom (addon) subtitles
|
// Ensure native KSPlayer text tracks are disabled when using custom (addon) subtitles
|
||||||
// and re-applied when switching back to built-in tracks. This prevents double-rendering.
|
// and re-applied when switching back to built-in tracks. This prevents double-rendering.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
if (useCustomSubtitles) {
|
if (useCustomSubtitles) {
|
||||||
// -1 disables native subtitle rendering in VLC
|
// -1 disables native subtitle rendering in KSPlayer
|
||||||
setSelectedTextTrack(-1);
|
setSelectedTextTrack(-1);
|
||||||
} else if (typeof selectedTextTrack === 'number' && selectedTextTrack >= 0) {
|
} else if (typeof selectedTextTrack === 'number' && selectedTextTrack >= 0) {
|
||||||
// KSPlayer picks it up via prop
|
// KSPlayer picks it up via prop
|
||||||
|
|
@ -1692,7 +1692,7 @@ const VideoPlayer: React.FC = () => {
|
||||||
const parsedCues = parseSRT(srtContent);
|
const parsedCues = parseSRT(srtContent);
|
||||||
logger.log(`[VideoPlayer] Parsed cues count=${parsedCues.length}`);
|
logger.log(`[VideoPlayer] Parsed cues count=${parsedCues.length}`);
|
||||||
|
|
||||||
// For VLC on iOS: stop spinner early, then clear-apply and micro-seek nudge
|
// For KSPlayer on iOS: stop spinner early, then clear-apply and micro-seek nudge
|
||||||
setIsLoadingSubtitles(false);
|
setIsLoadingSubtitles(false);
|
||||||
logger.log('[VideoPlayer] isLoadingSubtitles -> false (early)');
|
logger.log('[VideoPlayer] isLoadingSubtitles -> false (early)');
|
||||||
|
|
||||||
|
|
@ -1825,7 +1825,6 @@ const VideoPlayer: React.FC = () => {
|
||||||
streamProvider: addonName,
|
streamProvider: addonName,
|
||||||
streamName: bestStream.name || bestStream.title,
|
streamName: bestStream.name || bestStream.title,
|
||||||
headers: bestStream.headers || undefined,
|
headers: bestStream.headers || undefined,
|
||||||
forceVlc: false,
|
|
||||||
id,
|
id,
|
||||||
type: 'series',
|
type: 'series',
|
||||||
episodeId: nextEpisodeId,
|
episodeId: nextEpisodeId,
|
||||||
|
|
@ -2121,8 +2120,8 @@ const VideoPlayer: React.FC = () => {
|
||||||
|
|
||||||
// Handle audio track changes with proper logging
|
// Handle audio track changes with proper logging
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedAudioTrack !== null && vlcAudioTracks.length > 0) {
|
if (selectedAudioTrack !== null && ksAudioTracks.length > 0) {
|
||||||
const selectedTrack = vlcAudioTracks.find(track => track.id === selectedAudioTrack);
|
const selectedTrack = ksAudioTracks.find(track => track.id === selectedAudioTrack);
|
||||||
if (selectedTrack) {
|
if (selectedTrack) {
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
logger.log(`[VideoPlayer] Audio track selected: ${selectedTrack.name} (${selectedTrack.language}) - ID: ${selectedAudioTrack}`);
|
logger.log(`[VideoPlayer] Audio track selected: ${selectedTrack.name} (${selectedTrack.language}) - ID: ${selectedAudioTrack}`);
|
||||||
|
|
@ -2131,7 +2130,7 @@ const VideoPlayer: React.FC = () => {
|
||||||
logger.warn(`[VideoPlayer] Selected audio track ${selectedAudioTrack} not found in available tracks`);
|
logger.warn(`[VideoPlayer] Selected audio track ${selectedAudioTrack} not found in available tracks`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [selectedAudioTrack, vlcAudioTracks]);
|
}, [selectedAudioTrack, ksAudioTracks]);
|
||||||
|
|
||||||
const increaseSubtitleSize = () => {
|
const increaseSubtitleSize = () => {
|
||||||
const newSize = Math.min(subtitleSize + 2, 32);
|
const newSize = Math.min(subtitleSize + 2, 32);
|
||||||
|
|
@ -2231,8 +2230,8 @@ const VideoPlayer: React.FC = () => {
|
||||||
// Set pending seek state
|
// Set pending seek state
|
||||||
setPendingSeek({ position: savedPosition, shouldPlay: wasPlaying });
|
setPendingSeek({ position: savedPosition, shouldPlay: wasPlaying });
|
||||||
|
|
||||||
// Update the stream URL and details immediately (decode URL for VLC)
|
// Update the stream URL and details immediately (decode URL for KSPlayer)
|
||||||
setCurrentStreamUrl(decodeUrlForVlc(newStream.url));
|
setCurrentStreamUrl(decodeUrlForKsPlayer(newStream.url));
|
||||||
setCurrentQuality(newQuality);
|
setCurrentQuality(newQuality);
|
||||||
setCurrentStreamProvider(newProvider);
|
setCurrentStreamProvider(newProvider);
|
||||||
setCurrentStreamName(newStreamName);
|
setCurrentStreamName(newStreamName);
|
||||||
|
|
@ -2559,7 +2558,7 @@ const VideoPlayer: React.FC = () => {
|
||||||
currentTime={currentTime}
|
currentTime={currentTime}
|
||||||
duration={duration}
|
duration={duration}
|
||||||
zoomScale={zoomScale}
|
zoomScale={zoomScale}
|
||||||
vlcAudioTracks={vlcAudioTracks}
|
ksAudioTracks={ksAudioTracks}
|
||||||
selectedAudioTrack={selectedAudioTrack}
|
selectedAudioTrack={selectedAudioTrack}
|
||||||
availableStreams={availableStreams}
|
availableStreams={availableStreams}
|
||||||
togglePlayback={togglePlayback}
|
togglePlayback={togglePlayback}
|
||||||
|
|
@ -3148,8 +3147,8 @@ const VideoPlayer: React.FC = () => {
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* VLC Volume Warning Overlay */}
|
{/* KSPlayer Volume Warning Overlay */}
|
||||||
{showVlcVolumeWarning && (
|
{showKsVolumeWarning && (
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
|
|
@ -3201,7 +3200,7 @@ const VideoPlayer: React.FC = () => {
|
||||||
lineHeight: 18,
|
lineHeight: 18,
|
||||||
marginBottom: 12,
|
marginBottom: 12,
|
||||||
}}>
|
}}>
|
||||||
VLC player doesn't support volume gestures.{'\n'}Use your device volume buttons instead.
|
KSPlayer doesn't support volume gestures.{'\n'}Use your device volume buttons instead.
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Text style={{
|
<Text style={{
|
||||||
|
|
@ -3223,7 +3222,7 @@ const VideoPlayer: React.FC = () => {
|
||||||
<AudioTrackModal
|
<AudioTrackModal
|
||||||
showAudioModal={showAudioModal}
|
showAudioModal={showAudioModal}
|
||||||
setShowAudioModal={setShowAudioModal}
|
setShowAudioModal={setShowAudioModal}
|
||||||
vlcAudioTracks={vlcAudioTracks}
|
ksAudioTracks={ksAudioTracks}
|
||||||
selectedAudioTrack={selectedAudioTrack}
|
selectedAudioTrack={selectedAudioTrack}
|
||||||
selectAudioTrack={selectAudioTrack}
|
selectAudioTrack={selectAudioTrack}
|
||||||
/>
|
/>
|
||||||
|
|
@ -3236,7 +3235,7 @@ const VideoPlayer: React.FC = () => {
|
||||||
isLoadingSubtitles={isLoadingSubtitles}
|
isLoadingSubtitles={isLoadingSubtitles}
|
||||||
customSubtitles={customSubtitles}
|
customSubtitles={customSubtitles}
|
||||||
availableSubtitles={availableSubtitles}
|
availableSubtitles={availableSubtitles}
|
||||||
vlcTextTracks={vlcTextTracks}
|
ksTextTracks={ksTextTracks}
|
||||||
selectedTextTrack={selectedTextTrack}
|
selectedTextTrack={selectedTextTrack}
|
||||||
useCustomSubtitles={useCustomSubtitles}
|
useCustomSubtitles={useCustomSubtitles}
|
||||||
subtitleSize={subtitleSize}
|
subtitleSize={subtitleSize}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ interface PlayerControlsProps {
|
||||||
duration: number;
|
duration: number;
|
||||||
zoomScale: number;
|
zoomScale: number;
|
||||||
currentResizeMode?: string;
|
currentResizeMode?: string;
|
||||||
vlcAudioTracks: Array<{id: number, name: string, language?: string}>;
|
ksAudioTracks: Array<{id: number, name: string, language?: string}>;
|
||||||
selectedAudioTrack: number | null;
|
selectedAudioTrack: number | null;
|
||||||
availableStreams?: { [providerId: string]: { streams: any[]; addonName: string } };
|
availableStreams?: { [providerId: string]: { streams: any[]; addonName: string } };
|
||||||
togglePlayback: () => void;
|
togglePlayback: () => void;
|
||||||
|
|
@ -58,7 +58,7 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
|
||||||
duration,
|
duration,
|
||||||
zoomScale,
|
zoomScale,
|
||||||
currentResizeMode,
|
currentResizeMode,
|
||||||
vlcAudioTracks,
|
ksAudioTracks,
|
||||||
selectedAudioTrack,
|
selectedAudioTrack,
|
||||||
availableStreams,
|
availableStreams,
|
||||||
togglePlayback,
|
togglePlayback,
|
||||||
|
|
@ -170,17 +170,17 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
{/* Audio Button - Updated to use vlcAudioTracks */}
|
{/* Audio Button - Updated to use ksAudioTracks */}
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.bottomButton}
|
style={styles.bottomButton}
|
||||||
onPress={() => setShowAudioModal(true)}
|
onPress={() => setShowAudioModal(true)}
|
||||||
disabled={vlcAudioTracks.length <= 1}
|
disabled={ksAudioTracks.length <= 1}
|
||||||
>
|
>
|
||||||
<Ionicons name="volume-high" size={20} color={vlcAudioTracks.length <= 1 ? 'grey' : 'white'} />
|
<Ionicons name="volume-high" size={20} color={ksAudioTracks.length <= 1 ? 'grey' : 'white'} />
|
||||||
<Text style={[styles.bottomButtonText, vlcAudioTracks.length <= 1 && {color: 'grey'}]} numberOfLines={1}>
|
<Text style={[styles.bottomButtonText, ksAudioTracks.length <= 1 && {color: 'grey'}]} numberOfLines={1}>
|
||||||
{(() => {
|
{(() => {
|
||||||
const trackName = getTrackDisplayName(
|
const trackName = getTrackDisplayName(
|
||||||
vlcAudioTracks.find(t => t.id === selectedAudioTrack) || { id: -1, name: 'Default' }
|
ksAudioTracks.find(t => t.id === selectedAudioTrack) || { id: -1, name: 'Default' }
|
||||||
);
|
);
|
||||||
// Truncate long audio track names to prevent UI cramping
|
// Truncate long audio track names to prevent UI cramping
|
||||||
const maxLength = 12; // Limit to 12 characters
|
const maxLength = 12; // Limit to 12 characters
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import { logger } from '../../../utils/logger';
|
||||||
interface AudioTrackModalProps {
|
interface AudioTrackModalProps {
|
||||||
showAudioModal: boolean;
|
showAudioModal: boolean;
|
||||||
setShowAudioModal: (show: boolean) => void;
|
setShowAudioModal: (show: boolean) => void;
|
||||||
vlcAudioTracks: Array<{id: number, name: string, language?: string}>;
|
ksAudioTracks: Array<{id: number, name: string, language?: string}>;
|
||||||
selectedAudioTrack: number | null;
|
selectedAudioTrack: number | null;
|
||||||
selectAudioTrack: (trackId: number) => void;
|
selectAudioTrack: (trackId: number) => void;
|
||||||
}
|
}
|
||||||
|
|
@ -24,7 +24,7 @@ const MENU_WIDTH = Math.min(width * 0.85, 400);
|
||||||
export const AudioTrackModal: React.FC<AudioTrackModalProps> = ({
|
export const AudioTrackModal: React.FC<AudioTrackModalProps> = ({
|
||||||
showAudioModal,
|
showAudioModal,
|
||||||
setShowAudioModal,
|
setShowAudioModal,
|
||||||
vlcAudioTracks,
|
ksAudioTracks,
|
||||||
selectedAudioTrack,
|
selectedAudioTrack,
|
||||||
selectAudioTrack,
|
selectAudioTrack,
|
||||||
}) => {
|
}) => {
|
||||||
|
|
@ -36,9 +36,9 @@ export const AudioTrackModal: React.FC<AudioTrackModalProps> = ({
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (showAudioModal && DEBUG_MODE) {
|
if (showAudioModal && DEBUG_MODE) {
|
||||||
logger.log(`[AudioTrackModal] Modal opened with selectedAudioTrack:`, selectedAudioTrack);
|
logger.log(`[AudioTrackModal] Modal opened with selectedAudioTrack:`, selectedAudioTrack);
|
||||||
logger.log(`[AudioTrackModal] Available tracks:`, vlcAudioTracks);
|
logger.log(`[AudioTrackModal] Available tracks:`, ksAudioTracks);
|
||||||
if (typeof selectedAudioTrack === 'number') {
|
if (typeof selectedAudioTrack === 'number') {
|
||||||
const selectedTrack = vlcAudioTracks.find(track => track.id === selectedAudioTrack);
|
const selectedTrack = ksAudioTracks.find(track => track.id === selectedAudioTrack);
|
||||||
if (selectedTrack) {
|
if (selectedTrack) {
|
||||||
logger.log(`[AudioTrackModal] Selected track found: ${selectedTrack.name} (${selectedTrack.language})`);
|
logger.log(`[AudioTrackModal] Selected track found: ${selectedTrack.name} (${selectedTrack.language})`);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -46,7 +46,7 @@ export const AudioTrackModal: React.FC<AudioTrackModalProps> = ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [showAudioModal, selectedAudioTrack, vlcAudioTracks]);
|
}, [showAudioModal, selectedAudioTrack, ksAudioTracks]);
|
||||||
|
|
||||||
if (!showAudioModal) return null;
|
if (!showAudioModal) return null;
|
||||||
|
|
||||||
|
|
@ -143,11 +143,11 @@ export const AudioTrackModal: React.FC<AudioTrackModalProps> = ({
|
||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
letterSpacing: 0.5,
|
letterSpacing: 0.5,
|
||||||
}}>
|
}}>
|
||||||
Available Tracks ({vlcAudioTracks.length})
|
Available Tracks ({ksAudioTracks.length})
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<View style={{ gap: 8 }}>
|
<View style={{ gap: 8 }}>
|
||||||
{vlcAudioTracks.map((track) => {
|
{ksAudioTracks.map((track) => {
|
||||||
// Determine if track is selected
|
// Determine if track is selected
|
||||||
const isSelected = selectedAudioTrack === track.id;
|
const isSelected = selectedAudioTrack === track.id;
|
||||||
|
|
||||||
|
|
@ -203,7 +203,7 @@ export const AudioTrackModal: React.FC<AudioTrackModalProps> = ({
|
||||||
})}
|
})}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{vlcAudioTracks.length === 0 && (
|
{ksAudioTracks.length === 0 && (
|
||||||
<View style={{
|
<View style={{
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
||||||
borderRadius: 16,
|
borderRadius: 16,
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ interface SubtitleModalsProps {
|
||||||
isLoadingSubtitles: boolean;
|
isLoadingSubtitles: boolean;
|
||||||
customSubtitles: SubtitleCue[];
|
customSubtitles: SubtitleCue[];
|
||||||
availableSubtitles: WyzieSubtitle[];
|
availableSubtitles: WyzieSubtitle[];
|
||||||
vlcTextTracks: Array<{id: number, name: string, language?: string}>;
|
ksTextTracks: Array<{id: number, name: string, language?: string}>;
|
||||||
selectedTextTrack: number;
|
selectedTextTrack: number;
|
||||||
useCustomSubtitles: boolean;
|
useCustomSubtitles: boolean;
|
||||||
subtitleSize: number;
|
subtitleSize: number;
|
||||||
|
|
@ -68,7 +68,7 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
|
||||||
isLoadingSubtitles,
|
isLoadingSubtitles,
|
||||||
customSubtitles,
|
customSubtitles,
|
||||||
availableSubtitles,
|
availableSubtitles,
|
||||||
vlcTextTracks,
|
ksTextTracks,
|
||||||
selectedTextTrack,
|
selectedTextTrack,
|
||||||
useCustomSubtitles,
|
useCustomSubtitles,
|
||||||
subtitleSize,
|
subtitleSize,
|
||||||
|
|
@ -286,10 +286,10 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<View style={{ gap: 8 }}>
|
<View style={{ gap: 8 }}>
|
||||||
{vlcTextTracks.map((track) => {
|
{ksTextTracks.map((track) => {
|
||||||
const isSelected = selectedTextTrack === track.id && !useCustomSubtitles;
|
const isSelected = selectedTextTrack === track.id && !useCustomSubtitles;
|
||||||
// Debug logging for subtitle selection
|
// Debug logging for subtitle selection
|
||||||
if (__DEV__ && vlcTextTracks.length > 0) {
|
if (__DEV__ && ksTextTracks.length > 0) {
|
||||||
console.log('[SubtitleModals] Track:', track.id, track.name, 'Selected:', selectedTextTrack, 'isSelected:', isSelected, 'useCustom:', useCustomSubtitles);
|
console.log('[SubtitleModals] Track:', track.id, track.name, 'Selected:', selectedTextTrack, 'isSelected:', isSelected, 'useCustom:', useCustomSubtitles);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue