From 74bcc041af14d20dca09e82e695285a0235391b2 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Thu, 26 Feb 2026 12:50:05 +0530 Subject: [PATCH 01/27] add codec, channel count info to track selector --- .../android/components/VideoSurface.tsx | 105 +++++++++++++++++- 1 file changed, 103 insertions(+), 2 deletions(-) diff --git a/src/components/player/android/components/VideoSurface.tsx b/src/components/player/android/components/VideoSurface.tsx index 57321d56..816a08c0 100644 --- a/src/components/player/android/components/VideoSurface.tsx +++ b/src/components/player/android/components/VideoSurface.tsx @@ -221,16 +221,117 @@ export const VideoSurface: React.FC = ({ onEnd(); }; + // Maps ExoPlayer mimeType strings to human-readable codec names + const exoMimeToCodec = (mimeType?: string): string | null => { + if (!mimeType) return null; + const mime = mimeType.toLowerCase(); + if (mime.includes('eac3') || mime.includes('ec-3')) return 'EAC3'; + if (mime.includes('ac3') || mime.includes('ac-3')) return 'AC3'; + if (mime.includes('truehd')) return 'TrueHD'; + if (mime.includes('dts-hd') || mime.includes('dtshd')) return 'DTS-HD'; + if (mime.includes('dts')) return 'DTS'; + if (mime.includes('aac')) return 'AAC'; + if (mime.includes('opus')) return 'Opus'; + if (mime.includes('vorbis')) return 'Vorbis'; + if (mime.includes('mp4a') || mime.includes('mpeg')) return 'MP3'; + if (mime.includes('flac')) return 'FLAC'; + return null; + }; + + const LANG_MAP: Record = { + en: 'English', eng: 'English', + es: 'Spanish', spa: 'Spanish', + fr: 'French', fre: 'French', + de: 'German', ger: 'German', + it: 'Italian', ita: 'Italian', + ja: 'Japanese', jpn: 'Japanese', + ko: 'Korean', kor: 'Korean', + zh: 'Chinese', chi: 'Chinese', + ru: 'Russian', rus: 'Russian', + pt: 'Portuguese', por: 'Portuguese', + hi: 'Hindi', hin: 'Hindi', + ar: 'Arabic', ara: 'Arabic', + nl: 'Dutch', dut: 'Dutch', + pl: 'Polish', pol: 'Polish', + tr: 'Turkish', tur: 'Turkish', + }; + + // Builds a rich audio track label e.g. "English EAC3 5.1 640 kbps" + const buildAudioTrackName = (t: any, i: number): string => { + const parts: string[] = []; + + // Parse channel count encoded as "|ch:N" appended to title by Java + let rawTitle: string = t.title ?? ''; + let channelCount: number | null = null; + const chMatch = rawTitle.match(/\|ch:(\d+)$/); + if (chMatch) { + channelCount = parseInt(chMatch[1], 10); + rawTitle = rawTitle.replace(/\|ch:\d+$/, '').trim(); + } + + if (rawTitle) { + parts.push(rawTitle); + } else if (t.language) { + parts.push(LANG_MAP[t.language.toLowerCase()] ?? t.language.toUpperCase()); + } + + const codec = exoMimeToCodec(t.mimeType); + if (codec) parts.push(codec); + + const ch = channelCount ?? t.channelCount ?? null; + if (ch != null && ch > 0) { + if (ch === 8) parts.push('7.1'); + else if (ch === 6) parts.push('5.1'); + else if (ch === 2) parts.push('2.0'); + else if (ch === 1) parts.push('Mono'); + else parts.push(`${ch}ch`); + } + + if (t.bitrate != null && t.bitrate > 0) { + const kbps = Math.round(t.bitrate / 1000); + parts.push(`${kbps} kbps`); + } + + return parts.length > 0 ? parts.join(' ') : `Track ${i + 1}`; + }; + + // Builds a rich subtitle track label e.g. "English SDH" or "French Forced" + const buildSubtitleTrackName = (t: any, i: number): string => { + const parts: string[] = []; + const titleLower = (t.title ?? '').toLowerCase(); + + if (t.title && t.title.trim()) { + parts.push(t.title.trim()); + } else if (t.language) { + parts.push(LANG_MAP[t.language.toLowerCase()] ?? t.language.toUpperCase()); + } + + if ( + t.isHearingImpaired || + titleLower.includes('sdh') || + titleLower.includes('hearing impaired') || + titleLower.includes('cc') + ) { + if (!titleLower.includes('sdh')) parts.push('SDH'); + } + + if (t.isForced || titleLower.includes('forced')) { + if (!titleLower.includes('forced')) parts.push('Forced'); + } + + return parts.length > 0 ? parts.join(' ') : `Track ${i + 1}`; + }; + const handleExoLoad = (data: any) => { const audioTracks = data.audioTracks?.map((t: any, i: number) => ({ id: i, - name: t.title || t.language || `Track ${i + 1}`, + name: buildAudioTrackName(t, i), language: t.language, })) ?? []; const subtitleTracks = data.textTracks?.map((t: any, i: number) => ({ id: i, - name: t.title || t.language || `Track ${i + 1}`, + name: buildSubtitleTrackName(t, i), language: t.language, })) ?? []; From b4dc7cdd884811701bfd7eb3689a2fd92b1c21f5 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Thu, 26 Feb 2026 12:53:38 +0530 Subject: [PATCH 02/27] add audio channel count extractor --- .../com/brentvatne/exoplayer/ReactExoplayerView.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 72007063..7e4629f4 100644 --- a/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -1599,6 +1599,11 @@ public class ReactExoplayerView extends FrameLayout implements Track audioTrack = exoplayerTrackToGenericTrack(format, groupIndex, selection, group); audioTrack.setBitrate(format.bitrate == Format.NO_VALUE ? 0 : format.bitrate); audioTrack.setSelected(isSelected); + // Encode channel count into title so JS can read it e.g. "English|ch:6" + if (format.channelCount != Format.NO_VALUE && format.channelCount > 0) { + String existing = audioTrack.getTitle() != null ? audioTrack.getTitle() : ""; + audioTrack.setTitle(existing + "|ch:" + format.channelCount); + } audioTracks.add(audioTrack); } @@ -1794,7 +1799,11 @@ public class ReactExoplayerView extends FrameLayout implements Track track = new Track(); track.setIndex(groupIndex); track.setLanguage(format.language != null ? format.language : "unknown"); - track.setTitle(format.label != null ? format.label : "Track " + (groupIndex + 1)); + String baseTitle = format.label != null ? format.label : ""; + if (format.channelCount != Format.NO_VALUE && format.channelCount > 0) { + baseTitle = baseTitle + "|ch:" + format.channelCount; + } + track.setTitle(baseTitle); track.setSelected(false); // Don't report selection status - let PlayerView handle it if (format.sampleMimeType != null) track.setMimeType(format.sampleMimeType); From f606ef04317bf53bb713b40db1b76cba9f168a18 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Thu, 26 Feb 2026 13:57:55 +0530 Subject: [PATCH 03/27] crash fix --- .../android/components/VideoSurface.tsx | 186 ++++++++---------- 1 file changed, 83 insertions(+), 103 deletions(-) diff --git a/src/components/player/android/components/VideoSurface.tsx b/src/components/player/android/components/VideoSurface.tsx index 816a08c0..1e13dbfa 100644 --- a/src/components/player/android/components/VideoSurface.tsx +++ b/src/components/player/android/components/VideoSurface.tsx @@ -78,6 +78,87 @@ const isCodecError = (errorString: string): boolean => { }); }; +const EXOPLAYER_LANG_MAP: Record = { + en: 'English', eng: 'English', + es: 'Spanish', spa: 'Spanish', + fr: 'French', fre: 'French', + de: 'German', ger: 'German', + it: 'Italian', ita: 'Italian', + ja: 'Japanese', jpn: 'Japanese', + ko: 'Korean', kor: 'Korean', + zh: 'Chinese', chi: 'Chinese', + ru: 'Russian', rus: 'Russian', + pt: 'Portuguese', por: 'Portuguese', + hi: 'Hindi', hin: 'Hindi', + ar: 'Arabic', ara: 'Arabic', + nl: 'Dutch', dut: 'Dutch', + pl: 'Polish', pol: 'Polish', + tr: 'Turkish', tur: 'Turkish', +}; + +const exoMimeToCodec = (mimeType?: string): string | null => { + if (!mimeType) return null; + const mime = mimeType.toLowerCase(); + if (mime.includes('eac3') || mime.includes('ec-3')) return 'EAC3'; + if (mime.includes('ac3') || mime.includes('ac-3')) return 'AC3'; + if (mime.includes('truehd')) return 'TrueHD'; + if (mime.includes('dts-hd') || mime.includes('dtshd')) return 'DTS-HD'; + if (mime.includes('dts')) return 'DTS'; + if (mime.includes('aac')) return 'AAC'; + if (mime.includes('opus')) return 'Opus'; + if (mime.includes('vorbis')) return 'Vorbis'; + if (mime.includes('mp4a') || mime.includes('mpeg')) return 'MP3'; + if (mime.includes('flac')) return 'FLAC'; + return null; +}; + +const buildExoAudioTrackName = (t: any, i: number): string => { + const parts: string[] = []; + let rawTitle: string = t.title ?? ''; + let channelCount: number | null = null; + const chMatch = rawTitle.match(/\|ch:(\d+)$/); + if (chMatch) { + channelCount = parseInt(chMatch[1], 10); + rawTitle = rawTitle.replace(/\|ch:\d+$/, '').trim(); + } + if (rawTitle) { + parts.push(rawTitle); + } else if (t.language) { + parts.push(EXOPLAYER_LANG_MAP[t.language.toLowerCase()] ?? t.language.toUpperCase()); + } + const codec = exoMimeToCodec(t.mimeType); + if (codec) parts.push(codec); + const ch = channelCount ?? t.channelCount ?? null; + if (ch != null && ch > 0) { + if (ch === 8) parts.push('7.1'); + else if (ch === 6) parts.push('5.1'); + else if (ch === 2) parts.push('2.0'); + else if (ch === 1) parts.push('Mono'); + else parts.push(`${ch}ch`); + } + if (t.bitrate != null && t.bitrate > 0) { + parts.push(`${Math.round(t.bitrate / 1000)} kbps`); + } + return parts.length > 0 ? parts.join(' ') : `Track ${i + 1}`; +}; + +const buildExoSubtitleTrackName = (t: any, i: number): string => { + const parts: string[] = []; + const titleLower = (t.title ?? '').toLowerCase(); + if (t.title && t.title.trim()) { + parts.push(t.title.trim()); + } else if (t.language) { + parts.push(EXOPLAYER_LANG_MAP[t.language.toLowerCase()] ?? t.language.toUpperCase()); + } + if (t.isHearingImpaired || titleLower.includes('sdh') || titleLower.includes('hearing impaired') || titleLower.includes('cc')) { + if (!titleLower.includes('sdh')) parts.push('SDH'); + } + if (t.isForced || titleLower.includes('forced')) { + if (!titleLower.includes('forced')) parts.push('Forced'); + } + return parts.length > 0 ? parts.join(' ') : `Track ${i + 1}`; +}; + export const VideoSurface: React.FC = ({ processedStreamUrl, videoType, @@ -221,117 +302,16 @@ export const VideoSurface: React.FC = ({ onEnd(); }; - // Maps ExoPlayer mimeType strings to human-readable codec names - const exoMimeToCodec = (mimeType?: string): string | null => { - if (!mimeType) return null; - const mime = mimeType.toLowerCase(); - if (mime.includes('eac3') || mime.includes('ec-3')) return 'EAC3'; - if (mime.includes('ac3') || mime.includes('ac-3')) return 'AC3'; - if (mime.includes('truehd')) return 'TrueHD'; - if (mime.includes('dts-hd') || mime.includes('dtshd')) return 'DTS-HD'; - if (mime.includes('dts')) return 'DTS'; - if (mime.includes('aac')) return 'AAC'; - if (mime.includes('opus')) return 'Opus'; - if (mime.includes('vorbis')) return 'Vorbis'; - if (mime.includes('mp4a') || mime.includes('mpeg')) return 'MP3'; - if (mime.includes('flac')) return 'FLAC'; - return null; - }; - - const LANG_MAP: Record = { - en: 'English', eng: 'English', - es: 'Spanish', spa: 'Spanish', - fr: 'French', fre: 'French', - de: 'German', ger: 'German', - it: 'Italian', ita: 'Italian', - ja: 'Japanese', jpn: 'Japanese', - ko: 'Korean', kor: 'Korean', - zh: 'Chinese', chi: 'Chinese', - ru: 'Russian', rus: 'Russian', - pt: 'Portuguese', por: 'Portuguese', - hi: 'Hindi', hin: 'Hindi', - ar: 'Arabic', ara: 'Arabic', - nl: 'Dutch', dut: 'Dutch', - pl: 'Polish', pol: 'Polish', - tr: 'Turkish', tur: 'Turkish', - }; - - // Builds a rich audio track label e.g. "English EAC3 5.1 640 kbps" - const buildAudioTrackName = (t: any, i: number): string => { - const parts: string[] = []; - - // Parse channel count encoded as "|ch:N" appended to title by Java - let rawTitle: string = t.title ?? ''; - let channelCount: number | null = null; - const chMatch = rawTitle.match(/\|ch:(\d+)$/); - if (chMatch) { - channelCount = parseInt(chMatch[1], 10); - rawTitle = rawTitle.replace(/\|ch:\d+$/, '').trim(); - } - - if (rawTitle) { - parts.push(rawTitle); - } else if (t.language) { - parts.push(LANG_MAP[t.language.toLowerCase()] ?? t.language.toUpperCase()); - } - - const codec = exoMimeToCodec(t.mimeType); - if (codec) parts.push(codec); - - const ch = channelCount ?? t.channelCount ?? null; - if (ch != null && ch > 0) { - if (ch === 8) parts.push('7.1'); - else if (ch === 6) parts.push('5.1'); - else if (ch === 2) parts.push('2.0'); - else if (ch === 1) parts.push('Mono'); - else parts.push(`${ch}ch`); - } - - if (t.bitrate != null && t.bitrate > 0) { - const kbps = Math.round(t.bitrate / 1000); - parts.push(`${kbps} kbps`); - } - - return parts.length > 0 ? parts.join(' ') : `Track ${i + 1}`; - }; - - // Builds a rich subtitle track label e.g. "English SDH" or "French Forced" - const buildSubtitleTrackName = (t: any, i: number): string => { - const parts: string[] = []; - const titleLower = (t.title ?? '').toLowerCase(); - - if (t.title && t.title.trim()) { - parts.push(t.title.trim()); - } else if (t.language) { - parts.push(LANG_MAP[t.language.toLowerCase()] ?? t.language.toUpperCase()); - } - - if ( - t.isHearingImpaired || - titleLower.includes('sdh') || - titleLower.includes('hearing impaired') || - titleLower.includes('cc') - ) { - if (!titleLower.includes('sdh')) parts.push('SDH'); - } - - if (t.isForced || titleLower.includes('forced')) { - if (!titleLower.includes('forced')) parts.push('Forced'); - } - - return parts.length > 0 ? parts.join(' ') : `Track ${i + 1}`; - }; - const handleExoLoad = (data: any) => { const audioTracks = data.audioTracks?.map((t: any, i: number) => ({ id: i, - name: buildAudioTrackName(t, i), + name: buildExoAudioTrackName(t, i), language: t.language, })) ?? []; const subtitleTracks = data.textTracks?.map((t: any, i: number) => ({ id: i, - name: buildSubtitleTrackName(t, i), + name: buildExoSubtitleTrackName(t, i), language: t.language, })) ?? []; From 2730a27702e761e7fbfb3af0f25156fe801903ae Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Thu, 26 Feb 2026 14:00:38 +0530 Subject: [PATCH 04/27] fix From f84d88bf4dd121c4a1d5fb5499c11cf9279da7e3 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Thu, 26 Feb 2026 21:15:37 +0530 Subject: [PATCH 05/27] pass the audio track and subtitle track jnfo --- src/components/player/AndroidVideoPlayer.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/player/AndroidVideoPlayer.tsx b/src/components/player/AndroidVideoPlayer.tsx index d8317ecf..21d11b3f 100644 --- a/src/components/player/AndroidVideoPlayer.tsx +++ b/src/components/player/AndroidVideoPlayer.tsx @@ -60,6 +60,7 @@ import { storageService } from '../../services/storageService'; import stremioService from '../../services/stremioService'; import { WyzieSubtitle, SubtitleCue } from './utils/playerTypes'; import { findBestSubtitleTrack, findBestAudioTrack } from './utils/trackSelectionUtils'; +import { buildExoAudioTrackName, buildExoSubtitleTrackName } from './android/components/VideoSurface'; import { useTheme } from '../../contexts/ThemeContext'; import axios from 'axios'; @@ -350,7 +351,7 @@ const AndroidVideoPlayer: React.FC = () => { const formatted = data.audioTracks.map((t: any, i: number) => ({ // react-native-video selectedAudioTrack {type:'index'} uses 0-based list index. id: i, - name: t.title || t.name || `Track ${i + 1}`, + name: buildExoAudioTrackName(t, i), language: t.language })); tracksHook.setRnVideoAudioTracks(formatted); @@ -360,7 +361,7 @@ const AndroidVideoPlayer: React.FC = () => { // react-native-video selectedTextTrack {type:'index'} uses 0-based list index. // Using `t.index` can be non-unique/misaligned and breaks selection/rendering. id: i, - name: t.title || t.name || `Track ${i + 1}`, + name: buildExoSubtitleTrackName(t, i), language: t.language })); tracksHook.setRnVideoTextTracks(formatted); From 7882f66a677c01a96e4d1b0520fd96e3188c9af9 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Thu, 26 Feb 2026 21:17:15 +0530 Subject: [PATCH 06/27] update to parse the track info --- src/components/player/android/components/VideoSurface.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/player/android/components/VideoSurface.tsx b/src/components/player/android/components/VideoSurface.tsx index 1e13dbfa..ebcc04e0 100644 --- a/src/components/player/android/components/VideoSurface.tsx +++ b/src/components/player/android/components/VideoSurface.tsx @@ -112,7 +112,7 @@ const exoMimeToCodec = (mimeType?: string): string | null => { return null; }; -const buildExoAudioTrackName = (t: any, i: number): string => { +export const buildExoAudioTrackName = (t: any, i: number): string => { const parts: string[] = []; let rawTitle: string = t.title ?? ''; let channelCount: number | null = null; @@ -142,7 +142,7 @@ const buildExoAudioTrackName = (t: any, i: number): string => { return parts.length > 0 ? parts.join(' ') : `Track ${i + 1}`; }; -const buildExoSubtitleTrackName = (t: any, i: number): string => { +export const buildExoSubtitleTrackName = (t: any, i: number): string => { const parts: string[] = []; const titleLower = (t.title ?? '').toLowerCase(); if (t.title && t.title.trim()) { From 234ff23d1fecbd74383241bc6220da4e47e87a1a Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 13:43:07 +0530 Subject: [PATCH 07/27] add logs to debug mimetypes --- src/components/player/AndroidVideoPlayer.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/player/AndroidVideoPlayer.tsx b/src/components/player/AndroidVideoPlayer.tsx index 21d11b3f..1591f2f4 100644 --- a/src/components/player/AndroidVideoPlayer.tsx +++ b/src/components/player/AndroidVideoPlayer.tsx @@ -348,6 +348,7 @@ const AndroidVideoPlayer: React.FC = () => { } if (data.audioTracks) { + console.log('[TrackDebug] raw audioTracks:', JSON.stringify(data.audioTracks)); const formatted = data.audioTracks.map((t: any, i: number) => ({ // react-native-video selectedAudioTrack {type:'index'} uses 0-based list index. id: i, @@ -357,6 +358,7 @@ const AndroidVideoPlayer: React.FC = () => { tracksHook.setRnVideoAudioTracks(formatted); } if (data.textTracks) { + console.log('[TrackDebug] raw textTracks:', JSON.stringify(data.textTracks)); const formatted = data.textTracks.map((t: any, i: number) => ({ // react-native-video selectedTextTrack {type:'index'} uses 0-based list index. // Using `t.index` can be non-unique/misaligned and breaks selection/rendering. From fe49d12df3a1eb07b16d1632a94d6c0812f2cccb Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 14:57:34 +0530 Subject: [PATCH 08/27] remove hardcoded track info --- src/components/player/utils/playerUtils.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/components/player/utils/playerUtils.ts b/src/components/player/utils/playerUtils.ts index 8514eac5..3fe20489 100644 --- a/src/components/player/utils/playerUtils.ts +++ b/src/components/player/utils/playerUtils.ts @@ -102,18 +102,19 @@ export const getTrackDisplayName = (track: { name?: string, id: number, language return track.name; } - // If the track name contains detailed information (like codec, bitrate, etc.), use it as-is + // If the track name contains detailed information, use it as-is if (track.name && (track.name.includes('DDP') || track.name.includes('DTS') || track.name.includes('AAC') || - track.name.includes('Kbps') || track.name.includes('Atmos') || track.name.includes('~'))) { + track.name.includes('EAC3') || track.name.includes('AC3') || track.name.includes('TrueHD') || + track.name.includes('Kbps') || track.name.includes('kbps') || track.name.includes('Atmos') || + track.name.includes('5.1') || track.name.includes('7.1') || track.name.includes('6.1') || track.name.includes('2.0') || + track.name.includes('SDH') || track.name.includes('Forced') || track.name.includes('~'))) { return track.name; } - // If we have a language field, use that for better display (only for simple track names) - if (track.language && track.language !== 'Unknown') { - const formattedLanguage = formatLanguage(track.language); - if (formattedLanguage !== 'Unknown' && !formattedLanguage.includes('Unknown')) { - return formattedLanguage; - } + // If name is a rich multi-word label (more than one word and not a generic track name), use it as-is + const genericTrackMatch = track.name.match(/^(Audio|Track|Subtitle)\s+(\d+)$/i); + if (!genericTrackMatch && track.name.trim().includes(' ')) { + return track.name; } // Try to extract language from name like "Some Info - [English]" @@ -231,4 +232,4 @@ export const getHlsHeaders = () => { ...defaultAndroidHeaders, 'Accept': 'application/x-mpegURL, application/vnd.apple.mpegurl, application/json, text/plain', }; -}; \ No newline at end of file +}; From dfb63e996531c1e34f27d33da21bc4b61f7640f0 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 14:58:48 +0530 Subject: [PATCH 09/27] fix for edge cases with 6.1 ch track --- src/components/player/android/components/VideoSurface.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/player/android/components/VideoSurface.tsx b/src/components/player/android/components/VideoSurface.tsx index ebcc04e0..e34d7d60 100644 --- a/src/components/player/android/components/VideoSurface.tsx +++ b/src/components/player/android/components/VideoSurface.tsx @@ -131,6 +131,7 @@ export const buildExoAudioTrackName = (t: any, i: number): string => { const ch = channelCount ?? t.channelCount ?? null; if (ch != null && ch > 0) { if (ch === 8) parts.push('7.1'); + else if (ch === 7) parts.push('6.1'); else if (ch === 6) parts.push('5.1'); else if (ch === 2) parts.push('2.0'); else if (ch === 1) parts.push('Mono'); From 24c9090ff1f5ca83241ab63991f10f69c61f3528 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 15:00:06 +0530 Subject: [PATCH 10/27] fix for edge cases with 6.1 ch track From 34d89889154a48d614582d083a3f99cb3c9ce547 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 15:37:54 +0530 Subject: [PATCH 11/27] fix duplicate --- src/components/player/utils/playerUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/player/utils/playerUtils.ts b/src/components/player/utils/playerUtils.ts index 3fe20489..62708df3 100644 --- a/src/components/player/utils/playerUtils.ts +++ b/src/components/player/utils/playerUtils.ts @@ -124,7 +124,6 @@ export const getTrackDisplayName = (track: { name?: string, id: number, language } // Handle generic VLC track names like "Audio 1", "Track 1" - const genericTrackMatch = track.name.match(/^(Audio|Track)\s+(\d+)$/i); if (genericTrackMatch) { return `Audio ${genericTrackMatch[2]}`; } From de1bf7f74697ba7f79b0604ab3a1f097b3369d43 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 16:39:13 +0530 Subject: [PATCH 12/27] fix for when player does not return lang --- .../player/android/components/VideoSurface.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/components/player/android/components/VideoSurface.tsx b/src/components/player/android/components/VideoSurface.tsx index e34d7d60..dd43af7e 100644 --- a/src/components/player/android/components/VideoSurface.tsx +++ b/src/components/player/android/components/VideoSurface.tsx @@ -122,6 +122,13 @@ export const buildExoAudioTrackName = (t: any, i: number): string => { rawTitle = rawTitle.replace(/\|ch:\d+$/, '').trim(); } if (rawTitle) { + // Prepend language if available and not already in the title + if (t.language) { + const lang = EXOPLAYER_LANG_MAP[t.language.toLowerCase()] ?? t.language.toUpperCase(); + if (!rawTitle.toLowerCase().includes(lang.toLowerCase())) { + parts.push(lang); + } + } parts.push(rawTitle); } else if (t.language) { parts.push(EXOPLAYER_LANG_MAP[t.language.toLowerCase()] ?? t.language.toUpperCase()); @@ -147,6 +154,13 @@ export const buildExoSubtitleTrackName = (t: any, i: number): string => { const parts: string[] = []; const titleLower = (t.title ?? '').toLowerCase(); if (t.title && t.title.trim()) { + // Prepend language if available and not already in the title + if (t.language) { + const lang = EXOPLAYER_LANG_MAP[t.language.toLowerCase()] ?? t.language.toUpperCase(); + if (!t.title.toLowerCase().includes(lang.toLowerCase())) { + parts.push(lang); + } + } parts.push(t.title.trim()); } else if (t.language) { parts.push(EXOPLAYER_LANG_MAP[t.language.toLowerCase()] ?? t.language.toUpperCase()); From 8232a1406f116567b51c0cfdd637fa195eb9ec5d Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 16:41:36 +0530 Subject: [PATCH 13/27] fix for when player does not send lang info --- src/components/player/AndroidVideoPlayer.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/player/AndroidVideoPlayer.tsx b/src/components/player/AndroidVideoPlayer.tsx index 1591f2f4..73f6fcf5 100644 --- a/src/components/player/AndroidVideoPlayer.tsx +++ b/src/components/player/AndroidVideoPlayer.tsx @@ -49,7 +49,7 @@ import { CustomAlert } from '../CustomAlert'; // Android-specific components -import { VideoSurface } from './android/components/VideoSurface'; +import { VideoSurface, buildExoAudioTrackName, buildExoSubtitleTrackName } from './android/components/VideoSurface'; import { MpvPlayerRef } from './android/MpvPlayer'; // Utils @@ -60,7 +60,6 @@ import { storageService } from '../../services/storageService'; import stremioService from '../../services/stremioService'; import { WyzieSubtitle, SubtitleCue } from './utils/playerTypes'; import { findBestSubtitleTrack, findBestAudioTrack } from './utils/trackSelectionUtils'; -import { buildExoAudioTrackName, buildExoSubtitleTrackName } from './android/components/VideoSurface'; import { useTheme } from '../../contexts/ThemeContext'; import axios from 'axios'; From da6ad87370943e25709b2496712d858dd5824057 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 16:44:08 +0530 Subject: [PATCH 14/27] fix --- src/components/player/utils/playerUtils.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/player/utils/playerUtils.ts b/src/components/player/utils/playerUtils.ts index 62708df3..e18ca074 100644 --- a/src/components/player/utils/playerUtils.ts +++ b/src/components/player/utils/playerUtils.ts @@ -123,9 +123,12 @@ export const getTrackDisplayName = (track: { name?: string, id: number, language return languageMatch[1]; } - // Handle generic VLC track names like "Audio 1", "Track 1" + // Handle generic VLC track names like "Audio 1", "Track 1" — use language if available if (genericTrackMatch) { - return `Audio ${genericTrackMatch[2]}`; + if (track.language && track.language !== 'Unknown') { + return formatLanguage(track.language); + } + return track.name; } // Check for common language patterns in the name From 86c528c199d82e3693e17eea59ac31b6b053ae05 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 18:33:20 +0530 Subject: [PATCH 15/27] fix for dts track info --- src/components/player/android/components/VideoSurface.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/player/android/components/VideoSurface.tsx b/src/components/player/android/components/VideoSurface.tsx index dd43af7e..4aa302bc 100644 --- a/src/components/player/android/components/VideoSurface.tsx +++ b/src/components/player/android/components/VideoSurface.tsx @@ -102,7 +102,8 @@ const exoMimeToCodec = (mimeType?: string): string | null => { if (mime.includes('eac3') || mime.includes('ec-3')) return 'EAC3'; if (mime.includes('ac3') || mime.includes('ac-3')) return 'AC3'; if (mime.includes('truehd')) return 'TrueHD'; - if (mime.includes('dts-hd') || mime.includes('dtshd')) return 'DTS-HD'; + if (mime.includes('dts.hd') || mime.includes('dts-hd') || mime.includes('dtshd')) return 'DTS-HD'; + if (mime.includes('dts.uhd') || mime.includes('dts:x')) return 'DTS:X'; if (mime.includes('dts')) return 'DTS'; if (mime.includes('aac')) return 'AAC'; if (mime.includes('opus')) return 'Opus'; From 39ddd236e66a4e8a3300b451c2c11366bfb17db0 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 18:42:10 +0530 Subject: [PATCH 16/27] fix for ac3 edge cases --- .../android/components/VideoSurface.tsx | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/components/player/android/components/VideoSurface.tsx b/src/components/player/android/components/VideoSurface.tsx index 4aa302bc..d0a7e2d9 100644 --- a/src/components/player/android/components/VideoSurface.tsx +++ b/src/components/player/android/components/VideoSurface.tsx @@ -115,13 +115,16 @@ const exoMimeToCodec = (mimeType?: string): string | null => { export const buildExoAudioTrackName = (t: any, i: number): string => { const parts: string[] = []; - let rawTitle: string = t.title ?? ''; + + // Check both title and name fields for the |ch: encoding from Java + let rawTitle: string = t.title ?? t.name ?? ''; let channelCount: number | null = null; const chMatch = rawTitle.match(/\|ch:(\d+)$/); if (chMatch) { channelCount = parseInt(chMatch[1], 10); rawTitle = rawTitle.replace(/\|ch:\d+$/, '').trim(); } + if (rawTitle) { // Prepend language if available and not already in the title if (t.language) { @@ -134,9 +137,22 @@ export const buildExoAudioTrackName = (t: any, i: number): string => { } else if (t.language) { parts.push(EXOPLAYER_LANG_MAP[t.language.toLowerCase()] ?? t.language.toUpperCase()); } + const codec = exoMimeToCodec(t.mimeType); if (codec) parts.push(codec); - const ch = channelCount ?? t.channelCount ?? null; + + // Use parsed channel count, fall back to bitrate-based guess for AC3/EAC3 + let ch = channelCount ?? t.channelCount ?? null; + if (ch == null && t.bitrate != null && t.bitrate > 0) { + const mime = (t.mimeType ?? '').toLowerCase(); + if (mime.includes('ac3') || mime.includes('eac3') || mime.includes('ec-3')) { + // AC3: 640kbps = 5.1, 192-256kbps = 2.0 + // EAC3: 768kbps+ = 5.1, 192-256kbps = 2.0 + if (t.bitrate >= 500000) ch = 6; + else if (t.bitrate <= 320000) ch = 2; + } + } + if (ch != null && ch > 0) { if (ch === 8) parts.push('7.1'); else if (ch === 7) parts.push('6.1'); @@ -145,9 +161,11 @@ export const buildExoAudioTrackName = (t: any, i: number): string => { else if (ch === 1) parts.push('Mono'); else parts.push(`${ch}ch`); } + if (t.bitrate != null && t.bitrate > 0) { parts.push(`${Math.round(t.bitrate / 1000)} kbps`); } + return parts.length > 0 ? parts.join(' ') : `Track ${i + 1}`; }; From 4203ad18be3dca291dd7a863697af8834970372c Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 18:49:51 +0530 Subject: [PATCH 17/27] fix for bitrate --- .../player/android/components/VideoSurface.tsx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/components/player/android/components/VideoSurface.tsx b/src/components/player/android/components/VideoSurface.tsx index d0a7e2d9..e85cefef 100644 --- a/src/components/player/android/components/VideoSurface.tsx +++ b/src/components/player/android/components/VideoSurface.tsx @@ -119,10 +119,17 @@ export const buildExoAudioTrackName = (t: any, i: number): string => { // Check both title and name fields for the |ch: encoding from Java let rawTitle: string = t.title ?? t.name ?? ''; let channelCount: number | null = null; - const chMatch = rawTitle.match(/\|ch:(\d+)$/); + let encodedBitrate: number | null = null; + + const chMatch = rawTitle.match(/\|ch:(\d+)/); if (chMatch) { channelCount = parseInt(chMatch[1], 10); - rawTitle = rawTitle.replace(/\|ch:\d+$/, '').trim(); + rawTitle = rawTitle.replace(/\|ch:\d+/, '').trim(); + } + const brMatch = rawTitle.match(/\|br:(\d+)/); + if (brMatch) { + encodedBitrate = parseInt(brMatch[1], 10); + rawTitle = rawTitle.replace(/\|br:\d+/, '').trim(); } if (rawTitle) { @@ -162,8 +169,10 @@ export const buildExoAudioTrackName = (t: any, i: number): string => { else parts.push(`${ch}ch`); } - if (t.bitrate != null && t.bitrate > 0) { - parts.push(`${Math.round(t.bitrate / 1000)} kbps`); + // Use encoded bitrate first, fall back to t.bitrate / t.bitRate field + const bitrate = encodedBitrate ?? t.bitrate ?? t.bitRate ?? null; + if (bitrate != null && bitrate > 0) { + parts.push(`${Math.round(bitrate / 1000)} kbps`); } return parts.length > 0 ? parts.join(' ') : `Track ${i + 1}`; From 9b65bc95a0e435a65aede560808ef8dc81596d04 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 18:50:52 +0530 Subject: [PATCH 18/27] fix --- .../brentvatne/exoplayer/ReactExoplayerView.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 7e4629f4..41a887b7 100644 --- a/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -1599,10 +1599,17 @@ public class ReactExoplayerView extends FrameLayout implements Track audioTrack = exoplayerTrackToGenericTrack(format, groupIndex, selection, group); audioTrack.setBitrate(format.bitrate == Format.NO_VALUE ? 0 : format.bitrate); audioTrack.setSelected(isSelected); - // Encode channel count into title so JS can read it e.g. "English|ch:6" + // Encode channel count and bitrate into title so JS can read them reliably + // e.g. "English|ch:6|br:640000" + String existing = audioTrack.getTitle() != null ? audioTrack.getTitle() : ""; if (format.channelCount != Format.NO_VALUE && format.channelCount > 0) { - String existing = audioTrack.getTitle() != null ? audioTrack.getTitle() : ""; - audioTrack.setTitle(existing + "|ch:" + format.channelCount); + existing = existing + "|ch:" + format.channelCount; + } + if (format.bitrate != Format.NO_VALUE && format.bitrate > 0) { + existing = existing + "|br:" + format.bitrate; + } + if (!existing.isEmpty()) { + audioTrack.setTitle(existing); } audioTracks.add(audioTrack); } @@ -1803,6 +1810,9 @@ public class ReactExoplayerView extends FrameLayout implements if (format.channelCount != Format.NO_VALUE && format.channelCount > 0) { baseTitle = baseTitle + "|ch:" + format.channelCount; } + if (format.bitrate != Format.NO_VALUE && format.bitrate > 0) { + baseTitle = baseTitle + "|br:" + format.bitrate; + } track.setTitle(baseTitle); track.setSelected(false); // Don't report selection status - let PlayerView handle it if (format.sampleMimeType != null) From d2b4d0d745095af19d8af98d61704c55249ac728 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 18:53:06 +0530 Subject: [PATCH 19/27] dix --- src/components/player/AndroidVideoPlayer.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/player/AndroidVideoPlayer.tsx b/src/components/player/AndroidVideoPlayer.tsx index 73f6fcf5..1591f2f4 100644 --- a/src/components/player/AndroidVideoPlayer.tsx +++ b/src/components/player/AndroidVideoPlayer.tsx @@ -49,7 +49,7 @@ import { CustomAlert } from '../CustomAlert'; // Android-specific components -import { VideoSurface, buildExoAudioTrackName, buildExoSubtitleTrackName } from './android/components/VideoSurface'; +import { VideoSurface } from './android/components/VideoSurface'; import { MpvPlayerRef } from './android/MpvPlayer'; // Utils @@ -60,6 +60,7 @@ import { storageService } from '../../services/storageService'; import stremioService from '../../services/stremioService'; import { WyzieSubtitle, SubtitleCue } from './utils/playerTypes'; import { findBestSubtitleTrack, findBestAudioTrack } from './utils/trackSelectionUtils'; +import { buildExoAudioTrackName, buildExoSubtitleTrackName } from './android/components/VideoSurface'; import { useTheme } from '../../contexts/ThemeContext'; import axios from 'axios'; From fbe0d9fc399fc62bec183ed8b77377e7ae5a657d Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 18:55:08 +0530 Subject: [PATCH 20/27] fix From c63af981ab091fbbbadbc855de1b3a24683c4315 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 18:55:51 +0530 Subject: [PATCH 21/27] fix From b3d4953da2a5f74d65d99d4bb55948b73b8462d2 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 18:59:00 +0530 Subject: [PATCH 22/27] add estimated bitrate for known formats --- .../exoplayer/ReactExoplayerView.java | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 41a887b7..95cbeb9e 100644 --- a/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -1605,8 +1605,16 @@ public class ReactExoplayerView extends FrameLayout implements if (format.channelCount != Format.NO_VALUE && format.channelCount > 0) { existing = existing + "|ch:" + format.channelCount; } - if (format.bitrate != Format.NO_VALUE && format.bitrate > 0) { - existing = existing + "|br:" + format.bitrate; + // Use bitrate, fall back to averageBitrate then peakBitrate + int effectiveBitrate = format.bitrate; + if (effectiveBitrate == Format.NO_VALUE || effectiveBitrate <= 0) { + effectiveBitrate = format.averageBitrate; + } + if (effectiveBitrate == Format.NO_VALUE || effectiveBitrate <= 0) { + effectiveBitrate = format.peakBitrate; + } + if (effectiveBitrate != Format.NO_VALUE && effectiveBitrate > 0) { + existing = existing + "|br:" + effectiveBitrate; } if (!existing.isEmpty()) { audioTrack.setTitle(existing); @@ -1810,8 +1818,16 @@ public class ReactExoplayerView extends FrameLayout implements if (format.channelCount != Format.NO_VALUE && format.channelCount > 0) { baseTitle = baseTitle + "|ch:" + format.channelCount; } - if (format.bitrate != Format.NO_VALUE && format.bitrate > 0) { - baseTitle = baseTitle + "|br:" + format.bitrate; + // Use bitrate, fall back to averageBitrate then peakBitrate + int effectiveBitrate = format.bitrate; + if (effectiveBitrate == Format.NO_VALUE || effectiveBitrate <= 0) { + effectiveBitrate = format.averageBitrate; + } + if (effectiveBitrate == Format.NO_VALUE || effectiveBitrate <= 0) { + effectiveBitrate = format.peakBitrate; + } + if (effectiveBitrate != Format.NO_VALUE && effectiveBitrate > 0) { + baseTitle = baseTitle + "|br:" + effectiveBitrate; } track.setTitle(baseTitle); track.setSelected(false); // Don't report selection status - let PlayerView handle it From a75de21584c1c7eaebf0e17ec9308e2e35aea72c Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 19:02:30 +0530 Subject: [PATCH 23/27] fix --- .../com/brentvatne/exoplayer/ReactExoplayerView.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 95cbeb9e..7db96bd3 100644 --- a/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -1599,8 +1599,8 @@ public class ReactExoplayerView extends FrameLayout implements Track audioTrack = exoplayerTrackToGenericTrack(format, groupIndex, selection, group); audioTrack.setBitrate(format.bitrate == Format.NO_VALUE ? 0 : format.bitrate); audioTrack.setSelected(isSelected); - // Encode channel count and bitrate into title so JS can read them reliably - // e.g. "English|ch:6|br:640000" + // Encode channel count, bitrate and mimeType into title so JS can read them reliably + // e.g. "English|ch:6|br:640000|mt:audio/ac3" String existing = audioTrack.getTitle() != null ? audioTrack.getTitle() : ""; if (format.channelCount != Format.NO_VALUE && format.channelCount > 0) { existing = existing + "|ch:" + format.channelCount; @@ -1616,6 +1616,9 @@ public class ReactExoplayerView extends FrameLayout implements if (effectiveBitrate != Format.NO_VALUE && effectiveBitrate > 0) { existing = existing + "|br:" + effectiveBitrate; } + if (format.sampleMimeType != null && !format.sampleMimeType.isEmpty()) { + existing = existing + "|mt:" + format.sampleMimeType; + } if (!existing.isEmpty()) { audioTrack.setTitle(existing); } @@ -1829,6 +1832,9 @@ public class ReactExoplayerView extends FrameLayout implements if (effectiveBitrate != Format.NO_VALUE && effectiveBitrate > 0) { baseTitle = baseTitle + "|br:" + effectiveBitrate; } + if (format.sampleMimeType != null && !format.sampleMimeType.isEmpty()) { + baseTitle = baseTitle + "|mt:" + format.sampleMimeType; + } track.setTitle(baseTitle); track.setSelected(false); // Don't report selection status - let PlayerView handle it if (format.sampleMimeType != null) From 62994d58258ecba367629b92539b327e9161bcb0 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 19:03:29 +0530 Subject: [PATCH 24/27] fix --- .../android/components/VideoSurface.tsx | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/components/player/android/components/VideoSurface.tsx b/src/components/player/android/components/VideoSurface.tsx index e85cefef..c3879cc5 100644 --- a/src/components/player/android/components/VideoSurface.tsx +++ b/src/components/player/android/components/VideoSurface.tsx @@ -116,24 +116,26 @@ const exoMimeToCodec = (mimeType?: string): string | null => { export const buildExoAudioTrackName = (t: any, i: number): string => { const parts: string[] = []; - // Check both title and name fields for the |ch: encoding from Java + // Check both title and name fields for encoded metadata from Java let rawTitle: string = t.title ?? t.name ?? ''; let channelCount: number | null = null; let encodedBitrate: number | null = null; + let encodedMimeType: string | null = null; + // Extract |ch:N, |br:N, |mt:mime from title const chMatch = rawTitle.match(/\|ch:(\d+)/); - if (chMatch) { - channelCount = parseInt(chMatch[1], 10); - rawTitle = rawTitle.replace(/\|ch:\d+/, '').trim(); - } + if (chMatch) channelCount = parseInt(chMatch[1], 10); + const brMatch = rawTitle.match(/\|br:(\d+)/); - if (brMatch) { - encodedBitrate = parseInt(brMatch[1], 10); - rawTitle = rawTitle.replace(/\|br:\d+/, '').trim(); - } + if (brMatch) encodedBitrate = parseInt(brMatch[1], 10); + + const mtMatch = rawTitle.match(/\|mt:([^|]+)/); + if (mtMatch) encodedMimeType = mtMatch[1]; + + // Strip all encoded metadata from the display title + rawTitle = rawTitle.replace(/\|ch:\d+/g, '').replace(/\|br:\d+/g, '').replace(/\|mt:[^|]+/g, '').trim(); if (rawTitle) { - // Prepend language if available and not already in the title if (t.language) { const lang = EXOPLAYER_LANG_MAP[t.language.toLowerCase()] ?? t.language.toUpperCase(); if (!rawTitle.toLowerCase().includes(lang.toLowerCase())) { @@ -145,18 +147,19 @@ export const buildExoAudioTrackName = (t: any, i: number): string => { parts.push(EXOPLAYER_LANG_MAP[t.language.toLowerCase()] ?? t.language.toUpperCase()); } - const codec = exoMimeToCodec(t.mimeType); + // Use mimeType from track object, fall back to encoded mimeType from title + const mimeType = t.mimeType ?? encodedMimeType ?? null; + const codec = exoMimeToCodec(mimeType); if (codec) parts.push(codec); // Use parsed channel count, fall back to bitrate-based guess for AC3/EAC3 let ch = channelCount ?? t.channelCount ?? null; - if (ch == null && t.bitrate != null && t.bitrate > 0) { - const mime = (t.mimeType ?? '').toLowerCase(); + if (ch == null) { + const mime = (mimeType ?? '').toLowerCase(); + const br = encodedBitrate ?? t.bitrate ?? 0; if (mime.includes('ac3') || mime.includes('eac3') || mime.includes('ec-3')) { - // AC3: 640kbps = 5.1, 192-256kbps = 2.0 - // EAC3: 768kbps+ = 5.1, 192-256kbps = 2.0 - if (t.bitrate >= 500000) ch = 6; - else if (t.bitrate <= 320000) ch = 2; + if (br >= 500000) ch = 6; + else if (br > 0 && br <= 320000) ch = 2; } } @@ -169,7 +172,6 @@ export const buildExoAudioTrackName = (t: any, i: number): string => { else parts.push(`${ch}ch`); } - // Use encoded bitrate first, fall back to t.bitrate / t.bitRate field const bitrate = encodedBitrate ?? t.bitrate ?? t.bitRate ?? null; if (bitrate != null && bitrate > 0) { parts.push(`${Math.round(bitrate / 1000)} kbps`); @@ -178,6 +180,7 @@ export const buildExoAudioTrackName = (t: any, i: number): string => { return parts.length > 0 ? parts.join(' ') : `Track ${i + 1}`; }; + export const buildExoSubtitleTrackName = (t: any, i: number): string => { const parts: string[] = []; const titleLower = (t.title ?? '').toLowerCase(); From 231a32de2f04c677698c7fcdd628a4a39f830019 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 19:06:29 +0530 Subject: [PATCH 25/27] fix --- .../player/android/components/VideoSurface.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/components/player/android/components/VideoSurface.tsx b/src/components/player/android/components/VideoSurface.tsx index c3879cc5..ed89ba36 100644 --- a/src/components/player/android/components/VideoSurface.tsx +++ b/src/components/player/android/components/VideoSurface.tsx @@ -99,8 +99,8 @@ const EXOPLAYER_LANG_MAP: Record = { const exoMimeToCodec = (mimeType?: string): string | null => { if (!mimeType) return null; const mime = mimeType.toLowerCase(); - if (mime.includes('eac3') || mime.includes('ec-3')) return 'EAC3'; - if (mime.includes('ac3') || mime.includes('ac-3')) return 'AC3'; + if (mime.includes('eac3') || mime.includes('ec-3')) return 'Dolby Digital Plus'; + if (mime.includes('ac3') || mime.includes('ac-3')) return 'Dolby Digital'; if (mime.includes('truehd')) return 'TrueHD'; if (mime.includes('dts.hd') || mime.includes('dts-hd') || mime.includes('dtshd')) return 'DTS-HD'; if (mime.includes('dts.uhd') || mime.includes('dts:x')) return 'DTS:X'; @@ -150,7 +150,15 @@ export const buildExoAudioTrackName = (t: any, i: number): string => { // Use mimeType from track object, fall back to encoded mimeType from title const mimeType = t.mimeType ?? encodedMimeType ?? null; const codec = exoMimeToCodec(mimeType); - if (codec) parts.push(codec); + // Only append codec if title doesn't already mention it + const titleLowerForCodec = rawTitle.toLowerCase(); + const codecAlreadyInTitle = titleLowerForCodec.includes('dolby') || + titleLowerForCodec.includes('dts') || + titleLowerForCodec.includes('atmos') || + titleLowerForCodec.includes('aac') || + titleLowerForCodec.includes('truehd') || + titleLowerForCodec.includes('flac'); + if (codec && !codecAlreadyInTitle) parts.push(codec); // Use parsed channel count, fall back to bitrate-based guess for AC3/EAC3 let ch = channelCount ?? t.channelCount ?? null; From d5b34872984edd9ca880ff3b83d25227ae743cab Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 19:07:05 +0530 Subject: [PATCH 26/27] fix From 18b6a51a9ee468efa496a2ef789c6d51fede2d59 Mon Sep 17 00:00:00 2001 From: chrisk325 Date: Fri, 27 Feb 2026 19:08:09 +0530 Subject: [PATCH 27/27] fix --- src/components/player/utils/playerUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/player/utils/playerUtils.ts b/src/components/player/utils/playerUtils.ts index e18ca074..e2124358 100644 --- a/src/components/player/utils/playerUtils.ts +++ b/src/components/player/utils/playerUtils.ts @@ -105,6 +105,7 @@ export const getTrackDisplayName = (track: { name?: string, id: number, language // If the track name contains detailed information, use it as-is if (track.name && (track.name.includes('DDP') || track.name.includes('DTS') || track.name.includes('AAC') || track.name.includes('EAC3') || track.name.includes('AC3') || track.name.includes('TrueHD') || + track.name.includes('Dolby') || track.name.includes('FLAC') || track.name.includes('Opus') || track.name.includes('Kbps') || track.name.includes('kbps') || track.name.includes('Atmos') || track.name.includes('5.1') || track.name.includes('7.1') || track.name.includes('6.1') || track.name.includes('2.0') || track.name.includes('SDH') || track.name.includes('Forced') || track.name.includes('~'))) {