mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-08-16 04:18:08 +00:00
minor sub fixes
This commit is contained in:
parent
9d0163b4eb
commit
afdac8c1e9
5 changed files with 2101 additions and 31 deletions
|
|
@ -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
|
|
@ -737,6 +737,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
|||
{!isTransitioningStream && (
|
||||
<VideoSurface
|
||||
processedStreamUrl={currentStreamUrl}
|
||||
videoType={currentVideoType}
|
||||
headers={headers}
|
||||
volume={volume}
|
||||
playbackSpeed={speedControl.playbackSpeed}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue