minor sub fixes

This commit is contained in:
tapframe 2026-01-21 17:01:40 +05:30
parent 9d0163b4eb
commit afdac8c1e9
5 changed files with 2101 additions and 31 deletions

View file

@ -2207,6 +2207,11 @@ public class ReactExoplayerView extends FrameLayout implements
TrackGroupArray groups = info.getTrackGroups(textRendererIndex);
boolean trackFound = false;
// react-native-video uses a flattened `textTracks` list on the JS side.
// For HLS/DASH, each TrackGroup often contains a single track at index 0,
// so comparing against `trackIndex` alone makes only the first subtitle selectable.
int flattenedIndex = 0;
for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
TrackGroup group = groups.get(groupIndex);
for (int trackIndex = 0; trackIndex < group.length; trackIndex++) {
@ -2219,11 +2224,13 @@ public class ReactExoplayerView extends FrameLayout implements
isMatch = true;
} else if ("index".equals(type)) {
int targetIndex = ReactBridgeUtils.safeParseInt(value, -1);
if (targetIndex == trackIndex) {
if (targetIndex == flattenedIndex) {
isMatch = true;
}
}
flattenedIndex++;
if (isMatch) {
TrackSelectionOverride override = new TrackSelectionOverride(group,
java.util.Arrays.asList(trackIndex));

File diff suppressed because it is too large Load diff

View file

@ -737,6 +737,7 @@ const AndroidVideoPlayer: React.FC = () => {
{!isTransitioningStream && (
<VideoSurface
processedStreamUrl={currentStreamUrl}
videoType={currentVideoType}
headers={headers}
volume={volume}
playbackSpeed={speedControl.playbackSpeed}

View file

@ -33,6 +33,7 @@ const CODEC_ERROR_PATTERNS = [
interface VideoSurfaceProps {
processedStreamUrl: string;
videoType?: string;
headers?: { [key: string]: string };
volume: number;
playbackSpeed: number;
@ -93,6 +94,7 @@ const isCodecError = (errorString: string): boolean => {
export const VideoSurface: React.FC<VideoSurfaceProps> = ({
processedStreamUrl,
videoType,
headers,
volume,
playbackSpeed,
@ -136,6 +138,32 @@ export const VideoSurface: React.FC<VideoSurfaceProps> = ({
// Use the actual stream URL
const streamUrl = currentStreamUrl || processedStreamUrl;
const normalizeRnVideoType = (t?: string): 'm3u8' | 'mpd' | undefined => {
if (!t) return undefined;
const lower = String(t).toLowerCase();
if (lower === 'm3u8' || lower === 'hls') return 'm3u8';
if (lower === 'mpd' || lower === 'dash') return 'mpd';
return undefined;
};
const inferRnVideoTypeFromUrl = (url?: string): 'm3u8' | 'mpd' | undefined => {
if (!url) return undefined;
const lower = url.toLowerCase();
// Strong signals
if (/\.m3u8(\b|$)/i.test(lower) || /(^|[?&])type=(m3u8|hls)(\b|$)/i.test(lower)) return 'm3u8';
if (/\.mpd(\b|$)/i.test(lower) || /(^|[?&])type=(mpd|dash)(\b|$)/i.test(lower)) return 'mpd';
// Heuristics for providers that serve HLS behind extensionless endpoints.
if (/\b(hls|m3u8|m3u)\b/i.test(lower)) return 'm3u8';
if (/\/playlist\//i.test(lower) && (/(^|[?&])token=/.test(lower) || /(^|[?&])expires=/.test(lower))) return 'm3u8';
// Common fallback keywords
if (/\bdash\b/i.test(lower) || /manifest/.test(lower)) return 'mpd';
return undefined;
};
const resolvedRnVideoType = normalizeRnVideoType(videoType) ?? inferRnVideoTypeFromUrl(streamUrl);
// ========== MPV Handlers ==========
const handleMpvLoad = (data: { duration: number; width: number; height: number }) => {
console.log('[VideoSurface] MPV onLoad received:', data);
@ -304,6 +332,7 @@ export const VideoSurface: React.FC<VideoSurfaceProps> = ({
source={{
uri: streamUrl,
headers: headers,
...(resolvedRnVideoType ? { type: resolvedRnVideoType } : null),
}}
paused={paused}
volume={volume}

View file

@ -155,9 +155,19 @@ export const sortStreamsByQuality = (streams: Stream[]): Stream[] => {
export const inferVideoTypeFromUrl = (url?: string): string | undefined => {
if (!url) return undefined;
const lower = url.toLowerCase();
if (/(\.|ext=)(m3u8)(\b|$)/i.test(lower)) return 'm3u8';
if (/(\.|ext=)(mpd)(\b|$)/i.test(lower)) return 'mpd';
if (/(\.|ext=)(mp4)(\b|$)/i.test(lower)) return 'mp4';
// HLS
if (/(\.|ext=)(m3u8)(\b|$)/i.test(lower) || /\.m3u8(\b|$)/i.test(lower)) return 'm3u8';
if (/(^|[?&])type=(m3u8|hls)(\b|$)/i.test(lower)) return 'm3u8';
if (/\b(m3u8|m3u)\b/i.test(lower) || /\bhls\b/i.test(lower)) return 'm3u8';
// Some providers serve HLS playlists behind extensionless endpoints.
// Example: https://<host>/playlist/<id>?token=...&expires=...
if (/\/playlist\//i.test(lower) && (/(^|[?&])token=/.test(lower) || /(^|[?&])expires=/.test(lower))) return 'm3u8';
// DASH
if (/(\.|ext=)(mpd)(\b|$)/i.test(lower) || /\.mpd(\b|$)/i.test(lower)) return 'mpd';
// Progressive
if (/(\.|ext=)(mp4)(\b|$)/i.test(lower) || /\.mp4(\b|$)/i.test(lower)) return 'mp4';
return undefined;
};