UNPKG

@zezosoft/zezo-ott-react-native-video-player

Version:

React Native OTT Video Player for Android & iOS. Supports playlists, seasons, auto-next playback, subtitles, theming, analytics, fullscreen mode, and custom controls. 🚀 Powered by ZezoSoft.

257 lines (249 loc) • 7 kB
"use strict"; import React, { useMemo } from 'react'; import { FlatList, Platform, TouchableOpacity, View, Text, StyleSheet } from 'react-native'; import { scale, verticalScale } from 'react-native-size-matters'; import { RFValue } from 'react-native-responsive-fontsize'; import { useVideoPlayerStore } from "../store/index.js"; import { useVideoPlayerConfig } from "../context/index.js"; // --- Constants --- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; const ALLOWED_SUBTITLE_TYPES = ['application/x-subrip', 'text/vtt', 'application/ttml+xml']; // --- Helpers --- const mapSubtitleType = mimeType => { switch (mimeType) { case 'application/x-subrip': return 'srt'; case 'application/ttml+xml': return 'ttml'; case 'text/vtt': return 'vtt'; default: return 'srt'; } }; // --- UI Components --- const Separator = () => /*#__PURE__*/_jsx(View, { style: styles.separator }); const AudioItem = ({ item, isSelected, onPress, colors }) => /*#__PURE__*/_jsx(TouchableOpacity, { onPress: onPress, children: /*#__PURE__*/_jsx(Text, { style: [styles.listItem, { color: colors.text }, isSelected && styles.selected], children: item.title }) }); const SubtitleItem = ({ item, isSelected, onPress, colors }) => /*#__PURE__*/_jsx(TouchableOpacity, { onPress: onPress, children: /*#__PURE__*/_jsx(Text, { style: [styles.listItem, { color: colors.text }, isSelected && styles.selected], children: item.title || 'Unknown' }) }); // --- Main Component --- const AudioAndSubtitles = () => { const { setSettingsModal, onLoad, setSelectedAudioTrack, setSelectedSubtitleTrack, selectedAudioTrack, selectedSubtitleTrack, setActiveSubtitle, activeTrack, activeSubtitle, setIsPaused } = useVideoPlayerStore(); const { colors } = useVideoPlayerConfig(); // Filter audio tracks const audioTracks = useMemo(() => { return onLoad?.audioTracks?.filter(item => item.title !== null && item.title !== '') || []; }, [onLoad?.audioTracks]); // Merge internal & external subtitles const subtitleTracks = useMemo(() => { const internal = onLoad?.textTracks?.filter(track => { const type = String(track.type); const isCEA608 = Platform.OS === 'android' && type === 'application/cea-608'; return !isCEA608 && ALLOWED_SUBTITLE_TYPES.includes(type) && track.title?.trim(); }).map((track, idx) => ({ index: idx, title: track.title, language: track.language, type: track.type ? mapSubtitleType(String(track.type)) : 'srt', value: idx, isExternal: false })) || []; const external = activeTrack?.subtitles?.map((item, idx) => ({ index: internal.length + idx, type: mapSubtitleType(item.type), value: internal.length + idx, language: item.language, title: item.title || item.language || `Subtitle ${idx + 1}`, uri: item.url, isExternal: true })) || []; return [...internal, ...external]; }, [activeTrack?.subtitles, onLoad?.textTracks]); // Handlers const handleSelectAudio = item => { setSelectedAudioTrack({ type: 'index', value: item.index, isExternal: false }); setSettingsModal({ isVisible: false, action: 'none' }); }; const handleSelectSubtitle = item => { if (item.isExternal) { setActiveSubtitle({ type: item.type, url: item.uri, language: item.language, title: item.title }); setSelectedSubtitleTrack({ type: 'index', value: 'Off', isExternal: false }); } else { setSelectedSubtitleTrack({ type: 'index', value: Number(item.value), isExternal: false }); setActiveSubtitle(null); } setIsPaused(false); setSettingsModal({ isVisible: false, action: 'none' }); }; const handleDisableSubtitles = () => { setSelectedSubtitleTrack({ type: 'index', value: 'Off', isExternal: false }); setActiveSubtitle(null); setIsPaused(false); setSettingsModal({ isVisible: false, action: 'none' }); }; return /*#__PURE__*/_jsxs(View, { style: styles.container, children: [/*#__PURE__*/_jsx(View, { style: styles.section, children: audioTracks.length > 0 && /*#__PURE__*/_jsxs(_Fragment, { children: [/*#__PURE__*/_jsx(Text, { style: [styles.sectionTitle, { color: colors.text }], children: "Audio" }), /*#__PURE__*/_jsx(FlatList, { data: audioTracks, keyExtractor: ({ index }) => `audio-${index}`, ItemSeparatorComponent: Separator, renderItem: ({ item }) => /*#__PURE__*/_jsx(AudioItem, { item: item, isSelected: selectedAudioTrack?.value === item.index, onPress: () => handleSelectAudio(item), colors: colors }) })] }) }), /*#__PURE__*/_jsxs(View, { style: styles.section, children: [/*#__PURE__*/_jsx(Text, { style: [styles.sectionTitle, { color: colors.text }], children: "Subtitles" }), /*#__PURE__*/_jsx(TouchableOpacity, { onPress: handleDisableSubtitles, children: /*#__PURE__*/_jsx(Text, { style: [styles.listItem, { color: colors.text }, !activeSubtitle && selectedSubtitleTrack?.value === 'Off' || selectedSubtitleTrack === null ? styles.selected : null], children: "Off" }) }), /*#__PURE__*/_jsx(Separator, {}), /*#__PURE__*/_jsx(FlatList, { data: subtitleTracks, keyExtractor: ({ index }) => `sub-${index}`, ItemSeparatorComponent: Separator, renderItem: ({ item }) => /*#__PURE__*/_jsx(SubtitleItem, { item: item, isSelected: selectedSubtitleTrack?.value === item.index || activeSubtitle?.language === item.language, onPress: () => handleSelectSubtitle(item), colors: { text: colors.text } }) })] })] }); }; export default AudioAndSubtitles; const styles = StyleSheet.create({ container: { flex: 1, flexDirection: 'row', padding: scale(8), gap: scale(12) }, section: { flex: 1, borderRadius: scale(12), padding: scale(8) }, sectionTitle: { fontSize: RFValue(20), fontWeight: '600', marginBottom: verticalScale(8), letterSpacing: 0.6 }, listItem: { fontSize: RFValue(17), paddingVertical: verticalScale(4), paddingHorizontal: scale(12), borderRadius: scale(8), opacity: 0.7 }, selected: { opacity: 1, fontWeight: '700' }, separator: { height: verticalScale(8) } }); //# sourceMappingURL=AudioAndSubtitles.js.map