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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +308 -0
  3. package/lib/module/VideoPlayer/MediaControls/BottomControls.js +156 -0
  4. package/lib/module/VideoPlayer/MediaControls/BottomControls.js.map +1 -0
  5. package/lib/module/VideoPlayer/MediaControls/MediaControls.js +27 -0
  6. package/lib/module/VideoPlayer/MediaControls/MediaControls.js.map +1 -0
  7. package/lib/module/VideoPlayer/MediaControls/MediaControlsProvider.js +85 -0
  8. package/lib/module/VideoPlayer/MediaControls/MediaControlsProvider.js.map +1 -0
  9. package/lib/module/VideoPlayer/MediaControls/MiddleControls.js +208 -0
  10. package/lib/module/VideoPlayer/MediaControls/MiddleControls.js.map +1 -0
  11. package/lib/module/VideoPlayer/MediaControls/TopControls.js +94 -0
  12. package/lib/module/VideoPlayer/MediaControls/TopControls.js.map +1 -0
  13. package/lib/module/VideoPlayer/Styles/fonts.js +58 -0
  14. package/lib/module/VideoPlayer/Styles/fonts.js.map +1 -0
  15. package/lib/module/VideoPlayer/Styles/globalStyles.js +75 -0
  16. package/lib/module/VideoPlayer/Styles/globalStyles.js.map +1 -0
  17. package/lib/module/VideoPlayer/VideoPlayer.js +180 -0
  18. package/lib/module/VideoPlayer/VideoPlayer.js.map +1 -0
  19. package/lib/module/VideoPlayer/components/ProgressBar.js +84 -0
  20. package/lib/module/VideoPlayer/components/ProgressBar.js.map +1 -0
  21. package/lib/module/VideoPlayer/components/SkipAndNextControls.js +154 -0
  22. package/lib/module/VideoPlayer/components/SkipAndNextControls.js.map +1 -0
  23. package/lib/module/VideoPlayer/components/SubtitleView.js +57 -0
  24. package/lib/module/VideoPlayer/components/SubtitleView.js.map +1 -0
  25. package/lib/module/VideoPlayer/context/VideoPlayerConfig.js +47 -0
  26. package/lib/module/VideoPlayer/context/VideoPlayerConfig.js.map +1 -0
  27. package/lib/module/VideoPlayer/context/index.js +4 -0
  28. package/lib/module/VideoPlayer/context/index.js.map +1 -0
  29. package/lib/module/VideoPlayer/index.js +7 -0
  30. package/lib/module/VideoPlayer/index.js.map +1 -0
  31. package/lib/module/VideoPlayer/model/AudioAndSubtitles.js +257 -0
  32. package/lib/module/VideoPlayer/model/AudioAndSubtitles.js.map +1 -0
  33. package/lib/module/VideoPlayer/model/Episodes.js +272 -0
  34. package/lib/module/VideoPlayer/model/Episodes.js.map +1 -0
  35. package/lib/module/VideoPlayer/model/SettingModal.js +115 -0
  36. package/lib/module/VideoPlayer/model/SettingModal.js.map +1 -0
  37. package/lib/module/VideoPlayer/model/SpeedControls.js +140 -0
  38. package/lib/module/VideoPlayer/model/SpeedControls.js.map +1 -0
  39. package/lib/module/VideoPlayer/model/VideoPlayerSettings.js +113 -0
  40. package/lib/module/VideoPlayer/model/VideoPlayerSettings.js.map +1 -0
  41. package/lib/module/VideoPlayer/store/index.js +5 -0
  42. package/lib/module/VideoPlayer/store/index.js.map +1 -0
  43. package/lib/module/VideoPlayer/store/videoPlayer.type.js +4 -0
  44. package/lib/module/VideoPlayer/store/videoPlayer.type.js.map +1 -0
  45. package/lib/module/VideoPlayer/store/videoPlayerStore.js +147 -0
  46. package/lib/module/VideoPlayer/store/videoPlayerStore.js.map +1 -0
  47. package/lib/module/VideoPlayer/utils/Display.js +22 -0
  48. package/lib/module/VideoPlayer/utils/Display.js.map +1 -0
  49. package/lib/module/VideoPlayer/utils/PlatformSelector.js +18 -0
  50. package/lib/module/VideoPlayer/utils/PlatformSelector.js.map +1 -0
  51. package/lib/module/VideoPlayer/utils/hooks/index.js +5 -0
  52. package/lib/module/VideoPlayer/utils/hooks/index.js.map +1 -0
  53. package/lib/module/VideoPlayer/utils/hooks/useVideoPlayerBack.js +48 -0
  54. package/lib/module/VideoPlayer/utils/hooks/useVideoPlayerBack.js.map +1 -0
  55. package/lib/module/VideoPlayer/utils/hooks/useVideoResolutions.js +88 -0
  56. package/lib/module/VideoPlayer/utils/hooks/useVideoResolutions.js.map +1 -0
  57. package/lib/module/VideoPlayer/utils/index.js +10 -0
  58. package/lib/module/VideoPlayer/utils/index.js.map +1 -0
  59. package/lib/module/VideoPlayer/utils/lockOrientation.js +31 -0
  60. package/lib/module/VideoPlayer/utils/lockOrientation.js.map +1 -0
  61. package/lib/module/VideoPlayer/utils/playerEvents.js +79 -0
  62. package/lib/module/VideoPlayer/utils/playerEvents.js.map +1 -0
  63. package/lib/module/VideoPlayer/utils/timeFormatter.js +35 -0
  64. package/lib/module/VideoPlayer/utils/timeFormatter.js.map +1 -0
  65. package/lib/module/VideoPlayer/utils/useWatchReporter.js +68 -0
  66. package/lib/module/VideoPlayer/utils/useWatchReporter.js.map +1 -0
  67. package/lib/module/VideoPlayer/utils/videoControl.js +166 -0
  68. package/lib/module/VideoPlayer/utils/videoControl.js.map +1 -0
  69. package/lib/module/VideoPlayer/utils/videoRef.js +5 -0
  70. package/lib/module/VideoPlayer/utils/videoRef.js.map +1 -0
  71. package/lib/module/VideoPlayer/utils/videoSource.js +17 -0
  72. package/lib/module/VideoPlayer/utils/videoSource.js.map +1 -0
  73. package/lib/module/index.js +4 -0
  74. package/lib/module/index.js.map +1 -0
  75. package/lib/module/package.json +1 -0
  76. package/lib/typescript/package.json +1 -0
  77. package/lib/typescript/src/VideoPlayer/MediaControls/BottomControls.d.ts +3 -0
  78. package/lib/typescript/src/VideoPlayer/MediaControls/BottomControls.d.ts.map +1 -0
  79. package/lib/typescript/src/VideoPlayer/MediaControls/MediaControls.d.ts +6 -0
  80. package/lib/typescript/src/VideoPlayer/MediaControls/MediaControls.d.ts.map +1 -0
  81. package/lib/typescript/src/VideoPlayer/MediaControls/MediaControlsProvider.d.ts +14 -0
  82. package/lib/typescript/src/VideoPlayer/MediaControls/MediaControlsProvider.d.ts.map +1 -0
  83. package/lib/typescript/src/VideoPlayer/MediaControls/MiddleControls.d.ts +3 -0
  84. package/lib/typescript/src/VideoPlayer/MediaControls/MiddleControls.d.ts.map +1 -0
  85. package/lib/typescript/src/VideoPlayer/MediaControls/TopControls.d.ts +7 -0
  86. package/lib/typescript/src/VideoPlayer/MediaControls/TopControls.d.ts.map +1 -0
  87. package/lib/typescript/src/VideoPlayer/Styles/fonts.d.ts +54 -0
  88. package/lib/typescript/src/VideoPlayer/Styles/fonts.d.ts.map +1 -0
  89. package/lib/typescript/src/VideoPlayer/Styles/globalStyles.d.ts +70 -0
  90. package/lib/typescript/src/VideoPlayer/Styles/globalStyles.d.ts.map +1 -0
  91. package/lib/typescript/src/VideoPlayer/VideoPlayer.d.ts +22 -0
  92. package/lib/typescript/src/VideoPlayer/VideoPlayer.d.ts.map +1 -0
  93. package/lib/typescript/src/VideoPlayer/components/ProgressBar.d.ts +12 -0
  94. package/lib/typescript/src/VideoPlayer/components/ProgressBar.d.ts.map +1 -0
  95. package/lib/typescript/src/VideoPlayer/components/SkipAndNextControls.d.ts +12 -0
  96. package/lib/typescript/src/VideoPlayer/components/SkipAndNextControls.d.ts.map +1 -0
  97. package/lib/typescript/src/VideoPlayer/components/SubtitleView.d.ts +3 -0
  98. package/lib/typescript/src/VideoPlayer/components/SubtitleView.d.ts.map +1 -0
  99. package/lib/typescript/src/VideoPlayer/context/VideoPlayerConfig.d.ts +25 -0
  100. package/lib/typescript/src/VideoPlayer/context/VideoPlayerConfig.d.ts.map +1 -0
  101. package/lib/typescript/src/VideoPlayer/context/index.d.ts +3 -0
  102. package/lib/typescript/src/VideoPlayer/context/index.d.ts.map +1 -0
  103. package/lib/typescript/src/VideoPlayer/index.d.ts +5 -0
  104. package/lib/typescript/src/VideoPlayer/index.d.ts.map +1 -0
  105. package/lib/typescript/src/VideoPlayer/model/AudioAndSubtitles.d.ts +4 -0
  106. package/lib/typescript/src/VideoPlayer/model/AudioAndSubtitles.d.ts.map +1 -0
  107. package/lib/typescript/src/VideoPlayer/model/Episodes.d.ts +12 -0
  108. package/lib/typescript/src/VideoPlayer/model/Episodes.d.ts.map +1 -0
  109. package/lib/typescript/src/VideoPlayer/model/SettingModal.d.ts +12 -0
  110. package/lib/typescript/src/VideoPlayer/model/SettingModal.d.ts.map +1 -0
  111. package/lib/typescript/src/VideoPlayer/model/SpeedControls.d.ts +4 -0
  112. package/lib/typescript/src/VideoPlayer/model/SpeedControls.d.ts.map +1 -0
  113. package/lib/typescript/src/VideoPlayer/model/VideoPlayerSettings.d.ts +7 -0
  114. package/lib/typescript/src/VideoPlayer/model/VideoPlayerSettings.d.ts.map +1 -0
  115. package/lib/typescript/src/VideoPlayer/store/index.d.ts +3 -0
  116. package/lib/typescript/src/VideoPlayer/store/index.d.ts.map +1 -0
  117. package/lib/typescript/src/VideoPlayer/store/videoPlayer.type.d.ts +144 -0
  118. package/lib/typescript/src/VideoPlayer/store/videoPlayer.type.d.ts.map +1 -0
  119. package/lib/typescript/src/VideoPlayer/store/videoPlayerStore.d.ts +20 -0
  120. package/lib/typescript/src/VideoPlayer/store/videoPlayerStore.d.ts.map +1 -0
  121. package/lib/typescript/src/VideoPlayer/utils/Display.d.ts +8 -0
  122. package/lib/typescript/src/VideoPlayer/utils/Display.d.ts.map +1 -0
  123. package/lib/typescript/src/VideoPlayer/utils/PlatformSelector.d.ts +11 -0
  124. package/lib/typescript/src/VideoPlayer/utils/PlatformSelector.d.ts.map +1 -0
  125. package/lib/typescript/src/VideoPlayer/utils/hooks/index.d.ts +3 -0
  126. package/lib/typescript/src/VideoPlayer/utils/hooks/index.d.ts.map +1 -0
  127. package/lib/typescript/src/VideoPlayer/utils/hooks/useVideoPlayerBack.d.ts +6 -0
  128. package/lib/typescript/src/VideoPlayer/utils/hooks/useVideoPlayerBack.d.ts.map +1 -0
  129. package/lib/typescript/src/VideoPlayer/utils/hooks/useVideoResolutions.d.ts +12 -0
  130. package/lib/typescript/src/VideoPlayer/utils/hooks/useVideoResolutions.d.ts.map +1 -0
  131. package/lib/typescript/src/VideoPlayer/utils/index.d.ts +8 -0
  132. package/lib/typescript/src/VideoPlayer/utils/index.d.ts.map +1 -0
  133. package/lib/typescript/src/VideoPlayer/utils/lockOrientation.d.ts +3 -0
  134. package/lib/typescript/src/VideoPlayer/utils/lockOrientation.d.ts.map +1 -0
  135. package/lib/typescript/src/VideoPlayer/utils/playerEvents.d.ts +18 -0
  136. package/lib/typescript/src/VideoPlayer/utils/playerEvents.d.ts.map +1 -0
  137. package/lib/typescript/src/VideoPlayer/utils/timeFormatter.d.ts +9 -0
  138. package/lib/typescript/src/VideoPlayer/utils/timeFormatter.d.ts.map +1 -0
  139. package/lib/typescript/src/VideoPlayer/utils/useWatchReporter.d.ts +26 -0
  140. package/lib/typescript/src/VideoPlayer/utils/useWatchReporter.d.ts.map +1 -0
  141. package/lib/typescript/src/VideoPlayer/utils/videoControl.d.ts +17 -0
  142. package/lib/typescript/src/VideoPlayer/utils/videoControl.d.ts.map +1 -0
  143. package/lib/typescript/src/VideoPlayer/utils/videoRef.d.ts +3 -0
  144. package/lib/typescript/src/VideoPlayer/utils/videoRef.d.ts.map +1 -0
  145. package/lib/typescript/src/VideoPlayer/utils/videoSource.d.ts +10 -0
  146. package/lib/typescript/src/VideoPlayer/utils/videoSource.d.ts.map +1 -0
  147. package/lib/typescript/src/index.d.ts +2 -0
  148. package/lib/typescript/src/index.d.ts.map +1 -0
  149. package/package.json +191 -0
  150. package/src/VideoPlayer/MediaControls/BottomControls.tsx +185 -0
  151. package/src/VideoPlayer/MediaControls/MediaControls.tsx +29 -0
  152. package/src/VideoPlayer/MediaControls/MediaControlsProvider.tsx +99 -0
  153. package/src/VideoPlayer/MediaControls/MiddleControls.tsx +232 -0
  154. package/src/VideoPlayer/MediaControls/TopControls.tsx +92 -0
  155. package/src/VideoPlayer/Styles/fonts.ts +106 -0
  156. package/src/VideoPlayer/Styles/globalStyles.ts +74 -0
  157. package/src/VideoPlayer/VideoPlayer.tsx +217 -0
  158. package/src/VideoPlayer/components/ProgressBar.tsx +98 -0
  159. package/src/VideoPlayer/components/SkipAndNextControls.tsx +195 -0
  160. package/src/VideoPlayer/components/SubtitleView.tsx +53 -0
  161. package/src/VideoPlayer/context/VideoPlayerConfig.tsx +65 -0
  162. package/src/VideoPlayer/context/index.ts +5 -0
  163. package/src/VideoPlayer/index.ts +4 -0
  164. package/src/VideoPlayer/model/AudioAndSubtitles.tsx +302 -0
  165. package/src/VideoPlayer/model/Episodes.tsx +294 -0
  166. package/src/VideoPlayer/model/SettingModal.tsx +128 -0
  167. package/src/VideoPlayer/model/SpeedControls.tsx +134 -0
  168. package/src/VideoPlayer/model/VideoPlayerSettings.tsx +141 -0
  169. package/src/VideoPlayer/store/index.ts +2 -0
  170. package/src/VideoPlayer/store/videoPlayer.type.ts +192 -0
  171. package/src/VideoPlayer/store/videoPlayerStore.ts +101 -0
  172. package/src/VideoPlayer/utils/Display.ts +14 -0
  173. package/src/VideoPlayer/utils/PlatformSelector.ts +18 -0
  174. package/src/VideoPlayer/utils/hooks/index.ts +2 -0
  175. package/src/VideoPlayer/utils/hooks/useVideoPlayerBack.ts +66 -0
  176. package/src/VideoPlayer/utils/hooks/useVideoResolutions.ts +119 -0
  177. package/src/VideoPlayer/utils/index.ts +7 -0
  178. package/src/VideoPlayer/utils/lockOrientation.ts +34 -0
  179. package/src/VideoPlayer/utils/playerEvents.ts +97 -0
  180. package/src/VideoPlayer/utils/timeFormatter.ts +47 -0
  181. package/src/VideoPlayer/utils/useWatchReporter.ts +104 -0
  182. package/src/VideoPlayer/utils/videoControl.ts +192 -0
  183. package/src/VideoPlayer/utils/videoRef.ts +4 -0
  184. package/src/VideoPlayer/utils/videoSource.ts +23 -0
  185. package/src/index.tsx +1 -0
@@ -0,0 +1,53 @@
1
+ import { StyleSheet, View } from 'react-native';
2
+ import { RFValue } from 'react-native-responsive-fontsize';
3
+ import { scale } from 'react-native-size-matters';
4
+ import Subtitles from 'react-native-subtitles';
5
+ import { useVideoPlayerStore } from '../store/videoPlayerStore';
6
+ import Display from '../utils/Display';
7
+ import { useVideoPlayerConfig } from '../context';
8
+
9
+ const SubtitleView = () => {
10
+ const { activeSubtitle, currentTime, controlsVisible, settingsModal } =
11
+ useVideoPlayerStore();
12
+ const { colors } = useVideoPlayerConfig();
13
+ if (
14
+ !activeSubtitle ||
15
+ !activeSubtitle.url ||
16
+ controlsVisible ||
17
+ settingsModal.isVisible
18
+ ) {
19
+ return null;
20
+ }
21
+ return (
22
+ <View style={styles.container}>
23
+ <Subtitles
24
+ containerStyle={styles.subtitleContainer}
25
+ textStyle={{ ...styles.text, color: colors.text }}
26
+ currentTime={currentTime}
27
+ selectedsubtitle={{
28
+ file: activeSubtitle.url,
29
+ }}
30
+ />
31
+ </View>
32
+ );
33
+ };
34
+
35
+ export default SubtitleView;
36
+
37
+ const styles = StyleSheet.create({
38
+ container: {
39
+ width: '100%',
40
+ position: 'absolute',
41
+ bottom: Display.setHeight(3),
42
+ zIndex: 10,
43
+ },
44
+ subtitleContainer: {
45
+ padding: scale(10),
46
+ },
47
+ text: {
48
+ fontSize: RFValue(14),
49
+ paddingHorizontal: scale(10),
50
+ paddingVertical: scale(5),
51
+ borderRadius: scale(5),
52
+ },
53
+ });
@@ -0,0 +1,65 @@
1
+ import { createContext, useContext, type ReactNode } from 'react';
2
+ import { scale } from 'react-native-size-matters';
3
+
4
+ export interface VideoPlayerTheme {
5
+ colors: {
6
+ primary: string;
7
+ secondary: string;
8
+ background: string;
9
+ onBackground: string;
10
+ text: string;
11
+ white: string;
12
+ black: string;
13
+ track: string;
14
+ buffer: string;
15
+ };
16
+ metrics: {
17
+ thumbSize: number;
18
+ sliderHeight: number;
19
+ controlSize: number;
20
+ };
21
+ }
22
+
23
+ const defaultTheme: VideoPlayerTheme = {
24
+ colors: {
25
+ primary: '#ff0000',
26
+ secondary: '#999999',
27
+ background: '#000000',
28
+ onBackground: '#E2E8F0',
29
+ text: '#ffffff',
30
+ white: '#ffffff',
31
+ black: '#000000',
32
+ track: '#868686f0',
33
+ buffer: '#ffffffd1',
34
+ },
35
+ metrics: {
36
+ thumbSize: scale(14),
37
+ sliderHeight: scale(4),
38
+ controlSize: scale(28),
39
+ },
40
+ };
41
+
42
+ const VideoPlayerConfigContext = createContext<VideoPlayerTheme>(defaultTheme);
43
+
44
+ export const VideoPlayerConfigProvider = ({
45
+ children,
46
+ theme,
47
+ }: {
48
+ children: ReactNode;
49
+ theme?: Partial<VideoPlayerTheme>;
50
+ }) => {
51
+ const mergedTheme: VideoPlayerTheme = {
52
+ ...defaultTheme,
53
+ ...theme,
54
+ colors: { ...defaultTheme.colors, ...theme?.colors },
55
+ metrics: { ...defaultTheme.metrics, ...theme?.metrics },
56
+ };
57
+
58
+ return (
59
+ <VideoPlayerConfigContext.Provider value={mergedTheme}>
60
+ {children}
61
+ </VideoPlayerConfigContext.Provider>
62
+ );
63
+ };
64
+
65
+ export const useVideoPlayerConfig = () => useContext(VideoPlayerConfigContext);
@@ -0,0 +1,5 @@
1
+ export {
2
+ VideoPlayerConfigProvider,
3
+ useVideoPlayerConfig,
4
+ } from './VideoPlayerConfig';
5
+ export type { VideoPlayerTheme } from './VideoPlayerConfig';
@@ -0,0 +1,4 @@
1
+ export { default as VideoPlayer } from './VideoPlayer';
2
+ export * from './store';
3
+ export * from './utils';
4
+ export * from './context';
@@ -0,0 +1,302 @@
1
+ import React, { useMemo } from 'react';
2
+ import {
3
+ FlatList,
4
+ Platform,
5
+ TouchableOpacity,
6
+ View,
7
+ Text,
8
+ StyleSheet,
9
+ } from 'react-native';
10
+ import { scale, verticalScale } from 'react-native-size-matters';
11
+ import { RFValue } from 'react-native-responsive-fontsize';
12
+ import type { OnLoadData } from 'react-native-video';
13
+ import {
14
+ useVideoPlayerStore,
15
+ type SubtitleTrack,
16
+ type TrackSelection,
17
+ } from '../store';
18
+ import { useVideoPlayerConfig } from '../context';
19
+
20
+ // --- Constants ---
21
+ const ALLOWED_SUBTITLE_TYPES = [
22
+ 'application/x-subrip',
23
+ 'text/vtt',
24
+ 'application/ttml+xml',
25
+ ];
26
+
27
+ // --- Helpers ---
28
+ const mapSubtitleType = (mimeType: string): 'srt' | 'ttml' | 'vtt' => {
29
+ switch (mimeType) {
30
+ case 'application/x-subrip':
31
+ return 'srt';
32
+ case 'application/ttml+xml':
33
+ return 'ttml';
34
+ case 'text/vtt':
35
+ return 'vtt';
36
+ default:
37
+ return 'srt';
38
+ }
39
+ };
40
+
41
+ // --- UI Components ---
42
+ const Separator = () => <View style={styles.separator} />;
43
+
44
+ interface AudioItemProps {
45
+ item: OnLoadData['audioTracks'][0];
46
+ isSelected: boolean;
47
+ onPress: () => void;
48
+ colors: { text: string };
49
+ }
50
+ const AudioItem: React.FC<AudioItemProps> = ({
51
+ item,
52
+ isSelected,
53
+ onPress,
54
+ colors,
55
+ }) => (
56
+ <TouchableOpacity onPress={onPress}>
57
+ <Text
58
+ style={[
59
+ styles.listItem,
60
+ { color: colors.text },
61
+ isSelected && styles.selected,
62
+ ]}
63
+ >
64
+ {item.title}
65
+ </Text>
66
+ </TouchableOpacity>
67
+ );
68
+
69
+ interface SubtitleItemProps {
70
+ item: TrackSelection;
71
+ isSelected: boolean;
72
+ onPress: () => void;
73
+ colors: { text: string };
74
+ }
75
+ const SubtitleItem: React.FC<SubtitleItemProps> = ({
76
+ item,
77
+ isSelected,
78
+ onPress,
79
+ colors,
80
+ }) => (
81
+ <TouchableOpacity onPress={onPress}>
82
+ <Text
83
+ style={[
84
+ styles.listItem,
85
+ { color: colors.text },
86
+ isSelected && styles.selected,
87
+ ]}
88
+ >
89
+ {item.title || 'Unknown'}
90
+ </Text>
91
+ </TouchableOpacity>
92
+ );
93
+
94
+ // --- Main Component ---
95
+ const AudioAndSubtitles: React.FC = () => {
96
+ const {
97
+ setSettingsModal,
98
+ onLoad,
99
+ setSelectedAudioTrack,
100
+ setSelectedSubtitleTrack,
101
+ selectedAudioTrack,
102
+ selectedSubtitleTrack,
103
+ setActiveSubtitle,
104
+ activeTrack,
105
+ activeSubtitle,
106
+ setIsPaused,
107
+ } = useVideoPlayerStore();
108
+ const { colors } = useVideoPlayerConfig();
109
+ // Filter audio tracks
110
+
111
+ const audioTracks = useMemo(() => {
112
+ return (
113
+ onLoad?.audioTracks?.filter(
114
+ (item: OnLoadData['audioTracks'][0]) =>
115
+ item.title !== null && item.title !== ''
116
+ ) || []
117
+ );
118
+ }, [onLoad?.audioTracks]);
119
+
120
+ // Merge internal & external subtitles
121
+ const subtitleTracks = useMemo(() => {
122
+ const internal =
123
+ onLoad?.textTracks
124
+ ?.filter((track) => {
125
+ const type = String(track.type);
126
+ const isCEA608 =
127
+ Platform.OS === 'android' && type === 'application/cea-608';
128
+ return (
129
+ !isCEA608 &&
130
+ ALLOWED_SUBTITLE_TYPES.includes(type) &&
131
+ track.title?.trim()
132
+ );
133
+ })
134
+ .map((track, idx) => ({
135
+ index: idx,
136
+ title: track.title,
137
+ language: track.language,
138
+ type: track.type ? mapSubtitleType(String(track.type)) : 'srt',
139
+ value: idx,
140
+ isExternal: false,
141
+ })) || [];
142
+
143
+ const external =
144
+ activeTrack?.subtitles?.map((item, idx) => ({
145
+ index: internal.length + idx,
146
+ type: mapSubtitleType(item.type),
147
+ value: internal.length + idx,
148
+ language: item.language,
149
+ title: item.title || item.language || `Subtitle ${idx + 1}`,
150
+ uri: item.url,
151
+ isExternal: true,
152
+ })) || [];
153
+
154
+ return [...internal, ...external];
155
+ }, [activeTrack?.subtitles, onLoad?.textTracks]);
156
+
157
+ // Handlers
158
+ const handleSelectAudio = (item: OnLoadData['audioTracks'][0]) => {
159
+ setSelectedAudioTrack({
160
+ type: 'index',
161
+ value: item.index,
162
+ isExternal: false,
163
+ });
164
+ setSettingsModal({ isVisible: false, action: 'none' });
165
+ };
166
+
167
+ const handleSelectSubtitle = (item: TrackSelection) => {
168
+ if (item.isExternal) {
169
+ setActiveSubtitle({
170
+ type: item.type as SubtitleTrack['type'],
171
+ url: item.uri!,
172
+ language: item.language,
173
+ title: item.title,
174
+ });
175
+ setSelectedSubtitleTrack({
176
+ type: 'index',
177
+ value: 'Off',
178
+ isExternal: false,
179
+ });
180
+ } else {
181
+ setSelectedSubtitleTrack({
182
+ type: 'index',
183
+ value: Number(item.value),
184
+ isExternal: false,
185
+ });
186
+ setActiveSubtitle(null);
187
+ }
188
+ setIsPaused(false);
189
+ setSettingsModal({ isVisible: false, action: 'none' });
190
+ };
191
+
192
+ const handleDisableSubtitles = () => {
193
+ setSelectedSubtitleTrack({
194
+ type: 'index',
195
+ value: 'Off',
196
+ isExternal: false,
197
+ });
198
+ setActiveSubtitle(null);
199
+ setIsPaused(false);
200
+ setSettingsModal({ isVisible: false, action: 'none' });
201
+ };
202
+
203
+ return (
204
+ <View style={styles.container}>
205
+ <View style={styles.section}>
206
+ {audioTracks.length > 0 && (
207
+ <>
208
+ <Text style={[styles.sectionTitle, { color: colors.text }]}>
209
+ Audio
210
+ </Text>
211
+ <FlatList
212
+ data={audioTracks}
213
+ keyExtractor={({ index }) => `audio-${index}`}
214
+ ItemSeparatorComponent={Separator}
215
+ renderItem={({ item }) => (
216
+ <AudioItem
217
+ item={item}
218
+ isSelected={selectedAudioTrack?.value === item.index}
219
+ onPress={() => handleSelectAudio(item)}
220
+ colors={colors}
221
+ />
222
+ )}
223
+ />
224
+ </>
225
+ )}
226
+ </View>
227
+
228
+ <View style={styles.section}>
229
+ <Text style={[styles.sectionTitle, { color: colors.text }]}>
230
+ Subtitles
231
+ </Text>
232
+ <TouchableOpacity onPress={handleDisableSubtitles}>
233
+ <Text
234
+ style={[
235
+ styles.listItem,
236
+ { color: colors.text },
237
+ (!activeSubtitle && selectedSubtitleTrack?.value === 'Off') ||
238
+ selectedSubtitleTrack === null
239
+ ? styles.selected
240
+ : null,
241
+ ]}
242
+ >
243
+ Off
244
+ </Text>
245
+ </TouchableOpacity>
246
+ <Separator />
247
+ <FlatList
248
+ data={subtitleTracks}
249
+ keyExtractor={({ index }) => `sub-${index}`}
250
+ ItemSeparatorComponent={Separator}
251
+ renderItem={({ item }) => (
252
+ <SubtitleItem
253
+ item={item}
254
+ isSelected={
255
+ selectedSubtitleTrack?.value === item.index ||
256
+ activeSubtitle?.language === item.language
257
+ }
258
+ onPress={() => handleSelectSubtitle(item)}
259
+ colors={{ text: colors.text }}
260
+ />
261
+ )}
262
+ />
263
+ </View>
264
+ </View>
265
+ );
266
+ };
267
+
268
+ export default AudioAndSubtitles;
269
+
270
+ const styles = StyleSheet.create({
271
+ container: {
272
+ flex: 1,
273
+ flexDirection: 'row',
274
+ padding: scale(8),
275
+ gap: scale(12),
276
+ },
277
+ section: {
278
+ flex: 1,
279
+ borderRadius: scale(12),
280
+ padding: scale(8),
281
+ },
282
+ sectionTitle: {
283
+ fontSize: RFValue(20),
284
+ fontWeight: '600',
285
+ marginBottom: verticalScale(8),
286
+ letterSpacing: 0.6,
287
+ },
288
+ listItem: {
289
+ fontSize: RFValue(17),
290
+ paddingVertical: verticalScale(4),
291
+ paddingHorizontal: scale(12),
292
+ borderRadius: scale(8),
293
+ opacity: 0.7,
294
+ },
295
+ selected: {
296
+ opacity: 1,
297
+ fontWeight: '700',
298
+ },
299
+ separator: {
300
+ height: verticalScale(8),
301
+ },
302
+ });