@zezosoft/zezo-ott-react-native-video-player
Version:
Production-ready React Native OTT video player library for Android & iOS. Features: playlists, seasons, auto-next playback, subtitles (SRT/VTT), custom theming, analytics tracking, fullscreen mode, gesture controls, ads player (pre-roll/mid-roll/post-roll
118 lines (94 loc) • 3.02 kB
text/typescript
import { useEffect, useState } from 'react';
import axios from 'axios';
import type { MediaTrack } from '../../store/videoPlayer.type';
export interface StreamInfo {
bandwidth: number;
width: number | string;
height: number | string;
}
async function getHLSBandwidthAndResolutions(
m3u8Url: string
): Promise<StreamInfo[]> {
try {
if (!m3u8Url?.endsWith('.m3u8')) return [];
const { data } = await axios.get<string>(m3u8Url);
if (!data) return [];
const lines = data.split('\n');
const streams: StreamInfo[] = [];
let bandwidth: number | null = null;
let width: number | null = null;
let height: number | null = null;
for (const line of lines) {
if (line.startsWith('#EXT-X-STREAM-INF')) {
const bandwidthMatch = line.match(/BANDWIDTH=(\d+)/);
const resolutionMatch = line.match(/RESOLUTION=(\d+)x(\d+)/);
bandwidth = bandwidthMatch ? Number(bandwidthMatch[1]) : null;
if (resolutionMatch) {
width = Number(resolutionMatch[1]);
height = Number(resolutionMatch[2]);
}
}
if (
line.trim().endsWith('.m3u8') &&
bandwidth !== null &&
width !== null &&
height !== null
) {
streams.push({ bandwidth, width, height });
bandwidth = null;
width = null;
height = null;
}
}
streams.sort((a, b) => (b.height as number) - (a.height as number));
const autoStream: StreamInfo = {
bandwidth: 0,
width: 'auto',
height: 'auto',
};
return [autoStream, ...streams];
} catch (error) {
console.error('Error fetching or parsing HLS stream:', error);
return [];
}
}
export interface Resolution {
height: number | 'auto';
bandwidth: number | null;
}
const resolutionCache: Record<string, Resolution[]> = {};
export const useVideoResolutions = (track: MediaTrack | null) => {
const [resolutions, setResolutions] = useState<Resolution[]>([
{ height: 'auto', bandwidth: null },
]);
useEffect(() => {
if (!track) return;
const source = track.isTrailer ? track.trailerSource : track.sourceLink;
if (!source) return;
if (resolutionCache[source]) {
setResolutions(resolutionCache[source]);
return;
}
const fetchResolutions = async () => {
try {
const data = await getHLSBandwidthAndResolutions(source);
const filteredData = data.filter(
(item) => typeof item.height === 'number'
);
const newResolutions: Resolution[] = [
{ height: 'auto', bandwidth: null },
...filteredData.map((item) => ({
height: item.height as number,
bandwidth: item.bandwidth,
})),
];
resolutionCache[source] = newResolutions;
setResolutions(newResolutions);
} catch (error) {
console.error('Failed to fetch video resolutions', error);
}
};
fetchResolutions();
}, [track]);
return resolutions;
};