@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,192 @@
1
+ /**
2
+ * @author Naresh
3
+ * @lastModified Sat 16 Aug 2025 at 12:15 PM
4
+ */
5
+
6
+ import {
7
+ type EnumValues,
8
+ type OnLoadData,
9
+ ResizeMode,
10
+ SelectedVideoTrackType,
11
+ } from 'react-native-video';
12
+
13
+ export interface BaseEntity {
14
+ id: string;
15
+ title: string;
16
+ contentId: string;
17
+ }
18
+
19
+ export interface TimeRange {
20
+ start: number;
21
+ end: number;
22
+ }
23
+
24
+ export interface SubtitleTrack {
25
+ title?: string;
26
+ language?: string;
27
+ type:
28
+ | 'application/x-subrip'
29
+ | 'application/ttml+xml'
30
+ | 'text/vtt'
31
+ | 'application/octet-stream';
32
+ url: string;
33
+ }
34
+
35
+ export interface MediaEpisode extends BaseEntity {
36
+ description: string;
37
+ duration: number;
38
+ sourceLink: string;
39
+ sourceType: 'HLS' | 'MP4';
40
+ subtitles: SubtitleTrack[];
41
+ thumbnail: string;
42
+ episodeNumber: number;
43
+ skipIntro?: TimeRange | null;
44
+ nextEpisodeAt?: number | null;
45
+ publishDate?: string | null;
46
+ }
47
+
48
+ export interface MediaSeason extends BaseEntity {
49
+ order: number;
50
+ seasonNumber?: number;
51
+ episodes: MediaEpisode[] | null;
52
+ isActive: boolean;
53
+ createdBy: string;
54
+ }
55
+
56
+ export interface MediaTrack extends BaseEntity {
57
+ description: string;
58
+ duration: number;
59
+ thumbnail: string;
60
+ type: 'movie' | 'series' | 'live_stream';
61
+ isTrailer?: boolean;
62
+ sourceType: 'HLS' | 'MP4';
63
+ sourceLink: string;
64
+ trailerSource?: string | null;
65
+ episodeId?: string | null;
66
+ episodeName?: string | null;
67
+ episodeNumber?: number | null;
68
+ seasonId?: string | null;
69
+ seasonNumber?: number | null;
70
+ skipIntro?: TimeRange | null;
71
+ nextEpisodeAt?: number | null;
72
+ subtitles?: SubtitleTrack[] | null;
73
+ publishDate?: string | null;
74
+ }
75
+
76
+ export interface TrackSelection {
77
+ index?: number;
78
+ type: string;
79
+ value: string | number;
80
+ isExternal: boolean;
81
+ title?: string;
82
+ uri?: string;
83
+ language?: string;
84
+ }
85
+
86
+ export interface IosTrackResolution {
87
+ width: number | string | null;
88
+ height: number | string | null;
89
+ bandwidth: number;
90
+ }
91
+
92
+ export type SettingsAction =
93
+ | 'playbackRate'
94
+ | 'audioOrSubtitle'
95
+ | 'settings'
96
+ | 'episodes'
97
+ | 'speed'
98
+ | 'none';
99
+
100
+ type StateSetter<T> = (value: T) => void;
101
+
102
+ export interface VideoPlayerStore {
103
+ currentTime: number;
104
+ setCurrentTime: StateSetter<number>;
105
+
106
+ duration: number;
107
+ setDuration: StateSetter<number>;
108
+
109
+ isPaused: boolean;
110
+ setIsPaused: StateSetter<boolean>;
111
+
112
+ isBuffering: boolean;
113
+ setIsBuffering: StateSetter<boolean>;
114
+
115
+ resizeMode: EnumValues<ResizeMode>;
116
+ setResizeMode: StateSetter<EnumValues<ResizeMode>>;
117
+
118
+ playableDuration: number;
119
+ setPlayableDuration: StateSetter<number>;
120
+
121
+ onLoad: OnLoadData | null;
122
+ setOnLoad: StateSetter<OnLoadData>;
123
+
124
+ error: string | null;
125
+ setError: StateSetter<string | null>;
126
+
127
+ playBackRate: number;
128
+ playBackRateLabel: string | null;
129
+ setPlayBackRate: (rate: number, label: string) => void;
130
+
131
+ controlsVisible: boolean;
132
+ setControlsVisible: StateSetter<boolean>;
133
+
134
+ controlsTimer: number;
135
+ setControlsTimer: StateSetter<number>;
136
+
137
+ selectedAudioTrack: TrackSelection | null;
138
+ setSelectedAudioTrack: StateSetter<TrackSelection | null>;
139
+
140
+ selectedSubtitleTrack: TrackSelection | null;
141
+ setSelectedSubtitleTrack: StateSetter<TrackSelection | null>;
142
+
143
+ selectedVideoTrack: { type: SelectedVideoTrackType; value: number } | null;
144
+ setSelectedVideoTrack: StateSetter<{
145
+ type: SelectedVideoTrackType;
146
+ value: number;
147
+ } | null>;
148
+
149
+ activeSubtitle: SubtitleTrack | null;
150
+ setActiveSubtitle: StateSetter<SubtitleTrack | null>;
151
+
152
+ activeTrack: MediaTrack | null;
153
+ setActiveTrack: StateSetter<MediaTrack | null>;
154
+
155
+ playList: MediaTrack[];
156
+ setPlayList: StateSetter<MediaTrack[]>;
157
+
158
+ currentTrackIndex: number;
159
+ setCurrentTrackIndex: StateSetter<number>;
160
+
161
+ contentSeasons: MediaSeason[] | null;
162
+ setContentSeasons: StateSetter<MediaSeason[] | null>;
163
+
164
+ activeSeason: MediaSeason | null;
165
+ setActiveSeason: StateSetter<MediaSeason | null>;
166
+
167
+ settingsModal: {
168
+ isVisible: boolean;
169
+ action: SettingsAction;
170
+ };
171
+ setSettingsModal: StateSetter<{
172
+ isVisible: boolean;
173
+ action: SettingsAction;
174
+ }>;
175
+
176
+ maxBitRate: number | null;
177
+ setMaxBitRate: StateSetter<number | null>;
178
+
179
+ startWatchTime: number | null;
180
+ setStartWatchTime: StateSetter<number | null>;
181
+
182
+ isViewCounted: boolean;
183
+ setIsViewCounted: StateSetter<boolean>;
184
+
185
+ isSkipIntroVisible: boolean;
186
+ setIsSkipIntroVisible: StateSetter<boolean>;
187
+
188
+ isNextEpisodeVisible: boolean;
189
+ setIsNextEpisodeVisible: StateSetter<boolean>;
190
+
191
+ resetStore: () => void;
192
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * @author Naresh Dhamu
3
+ * @lastModified Sat 16 Aug 2025 at 10:26 AM
4
+ */
5
+ import { create } from 'zustand';
6
+ import { createJSONStorage, persist } from 'zustand/middleware';
7
+ import { MMKV } from 'react-native-mmkv';
8
+ import { ResizeMode, type EnumValues } from 'react-native-video';
9
+ import type { SettingsAction, VideoPlayerStore } from './videoPlayer.type';
10
+
11
+ export const storage = new MMKV({
12
+ id: 'videoPlayerStorage',
13
+ encryptionKey: 'videoPlayerKey',
14
+ });
15
+
16
+ export const mmkvStorage = {
17
+ setItem: (key: string, value: string) => storage.set(key, value),
18
+ getItem: (key: string) => storage.getString(key) ?? null,
19
+ removeItem: (key: string) => storage.delete(key),
20
+ };
21
+
22
+ const storeDataDefaults = {
23
+ currentTime: 0,
24
+ duration: 0,
25
+ isPaused: false,
26
+ isBuffering: false,
27
+ resizeMode: ResizeMode.CONTAIN,
28
+ controlsVisible: true,
29
+ controlsTimer: 6,
30
+ activeTrack: null,
31
+ playableDuration: 0,
32
+ settingsModal: { isVisible: false, action: 'none' as SettingsAction },
33
+ playBackRate: 1,
34
+ playBackRateLabel: null,
35
+ onLoad: null,
36
+ selectedAudioTrack: null,
37
+ selectedSubtitleTrack: null,
38
+ selectedVideoTrack: null,
39
+ activeSubtitle: null,
40
+ maxBitRate: null,
41
+ startWatchTime: null,
42
+ playList: [],
43
+ currentTrackIndex: 0,
44
+ contentSeasons: null,
45
+ activeSeason: null,
46
+ error: null,
47
+ isViewCounted: false,
48
+ isSkipIntroVisible: false,
49
+ isNextEpisodeVisible: false,
50
+ };
51
+
52
+ export const useVideoPlayerStore = create<VideoPlayerStore>()(
53
+ persist(
54
+ (set) => ({
55
+ ...storeDataDefaults,
56
+
57
+ setCurrentTime: (currentTime) => set({ currentTime }),
58
+ setDuration: (duration) => set({ duration }),
59
+ setIsPaused: (isPaused) => set({ isPaused }),
60
+ setIsBuffering: (isBuffering) => set({ isBuffering }),
61
+ setResizeMode: (resizeMode: EnumValues<ResizeMode>) =>
62
+ set({ resizeMode }),
63
+ setControlsVisible: (controlsVisible) => set({ controlsVisible }),
64
+ setControlsTimer: (controlsTimer) => set({ controlsTimer }),
65
+ setActiveTrack: (activeTrack) => set({ activeTrack }),
66
+ setPlayableDuration: (playableDuration) => set({ playableDuration }),
67
+ setSettingsModal: ({ isVisible, action }) =>
68
+ set({ settingsModal: { isVisible, action } }),
69
+ setPlayBackRate: (playBackRate, playBackRateLabel) =>
70
+ set({ playBackRate, playBackRateLabel }),
71
+ setOnLoad: (onLoad) => set({ onLoad }),
72
+ setSelectedAudioTrack: (selectedAudioTrack) =>
73
+ set({ selectedAudioTrack }),
74
+ setSelectedSubtitleTrack: (selectedSubtitleTrack: any) =>
75
+ set({ selectedSubtitleTrack }),
76
+ setSelectedVideoTrack: (selectedVideoTrack) =>
77
+ set({ selectedVideoTrack }),
78
+ setActiveSubtitle: (activeSubtitle) => set({ activeSubtitle }),
79
+ setMaxBitRate: (maxBitRate) => set({ maxBitRate }),
80
+ setStartWatchTime: (startWatchTime) => set({ startWatchTime }),
81
+ setPlayList: (playList) => set({ playList }),
82
+ setCurrentTrackIndex: (currentTrackIndex) => set({ currentTrackIndex }),
83
+ setContentSeasons: (contentSeasons) => set({ contentSeasons }),
84
+ setActiveSeason: (activeSeason) => set({ activeSeason }),
85
+ setError: (error) => set({ error }),
86
+ setIsViewCounted: (isViewCounted) => set({ isViewCounted }),
87
+ setIsSkipIntroVisible: (isSkipIntroVisible) =>
88
+ set({ isSkipIntroVisible }),
89
+ setIsNextEpisodeVisible: (isNextEpisodeVisible) =>
90
+ set({ isNextEpisodeVisible }),
91
+ resetStore: () => {
92
+ set(storeDataDefaults);
93
+ storage.clearAll();
94
+ },
95
+ }),
96
+ {
97
+ name: 'VideoPlayerStorage',
98
+ storage: createJSONStorage(() => mmkvStorage),
99
+ }
100
+ )
101
+ );
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @author Naresh Dhamu
3
+ * @lastModified Thu 14 Aug 2025 at 09:41 AM
4
+ */
5
+ import { Dimensions } from 'react-native';
6
+
7
+ const { height, width } = Dimensions.get('window');
8
+
9
+ const setHeight = (h: number) => (height / 100) * h;
10
+ const setWidth = (w: number) => (width / 100) * w;
11
+ const fullWidth = Dimensions.get('window').width;
12
+ const fullHeight = Dimensions.get('window').height;
13
+
14
+ export default { setHeight, setWidth, fullWidth, fullHeight };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @author Naresh Dhamu
3
+ * @lastModified Fri 08 Aug 2025 at 08:44 PM
4
+ */
5
+
6
+ import { Platform } from 'react-native';
7
+
8
+ class PlatformSelector {
9
+ public isAndroid(): boolean {
10
+ return Platform.OS === 'android';
11
+ }
12
+
13
+ public isIOS(): boolean {
14
+ return Platform.OS === 'ios';
15
+ }
16
+ }
17
+
18
+ export default new PlatformSelector();
@@ -0,0 +1,2 @@
1
+ export * from './useVideoPlayerBack';
2
+ export * from './useVideoResolutions';
@@ -0,0 +1,66 @@
1
+ import { useEffect, useCallback, useRef } from 'react';
2
+ import {
3
+ BackHandler,
4
+ Platform,
5
+ AppState,
6
+ type AppStateStatus,
7
+ } from 'react-native';
8
+ import { lockToPortrait } from '../lockOrientation';
9
+ import { useVideoPlayerStore } from '../../store/videoPlayerStore';
10
+
11
+ type UseVideoPlayerBackProps = {
12
+ navigation?: any;
13
+ };
14
+
15
+ export const useVideoPlayerBack = ({
16
+ navigation,
17
+ }: UseVideoPlayerBackProps = {}) => {
18
+ const { resetStore } = useVideoPlayerStore();
19
+ const hasClosed = useRef(false); // prevent multiple calls
20
+
21
+ const handleClose = useCallback(() => {
22
+ if (hasClosed.current) return; // prevent loop
23
+ hasClosed.current = true;
24
+
25
+ resetStore();
26
+ lockToPortrait();
27
+
28
+ if (navigation?.canGoBack()) {
29
+ navigation.goBack();
30
+ }
31
+ }, [resetStore, navigation]);
32
+
33
+ useEffect(() => {
34
+ const backHandler =
35
+ Platform.OS === 'android'
36
+ ? BackHandler.addEventListener('hardwareBackPress', () => {
37
+ handleClose();
38
+ return true;
39
+ })
40
+ : undefined;
41
+
42
+ const unsubscribe = navigation?.addListener('beforeRemove', (e: any) => {
43
+ e.preventDefault();
44
+ handleClose();
45
+ navigation.dispatch(e.data.action);
46
+ });
47
+
48
+ const handleAppStateChange = (nextAppState: AppStateStatus) => {
49
+ if (nextAppState === 'inactive') {
50
+ handleClose();
51
+ }
52
+ };
53
+ const appStateListener = AppState.addEventListener(
54
+ 'change',
55
+ handleAppStateChange
56
+ );
57
+
58
+ return () => {
59
+ backHandler?.remove();
60
+ unsubscribe?.();
61
+ appStateListener.remove();
62
+ };
63
+ }, [handleClose, navigation]);
64
+
65
+ return handleClose;
66
+ };
@@ -0,0 +1,119 @@
1
+ import { useEffect, useState } from 'react';
2
+ import axios from 'axios';
3
+ import type { MediaTrack } from '../../store/videoPlayer.type';
4
+
5
+ export interface StreamInfo {
6
+ bandwidth: number;
7
+ width: number | string;
8
+ height: number | string;
9
+ }
10
+
11
+ async function getHLSBandwidthAndResolutions(
12
+ m3u8Url: string
13
+ ): Promise<StreamInfo[]> {
14
+ try {
15
+ if (!m3u8Url?.endsWith('.m3u8')) return [];
16
+
17
+ const { data } = await axios.get<string>(m3u8Url);
18
+ if (!data) return [];
19
+
20
+ const lines = data.split('\n');
21
+ const streams: StreamInfo[] = [];
22
+
23
+ let bandwidth: number | null = null;
24
+ let width: number | null = null;
25
+ let height: number | null = null;
26
+
27
+ for (const line of lines) {
28
+ if (line.startsWith('#EXT-X-STREAM-INF')) {
29
+ const bandwidthMatch = line.match(/BANDWIDTH=(\d+)/);
30
+ const resolutionMatch = line.match(/RESOLUTION=(\d+)x(\d+)/);
31
+
32
+ bandwidth = bandwidthMatch ? Number(bandwidthMatch[1]) : null;
33
+ if (resolutionMatch) {
34
+ width = Number(resolutionMatch[1]);
35
+ height = Number(resolutionMatch[2]);
36
+ }
37
+ }
38
+
39
+ if (
40
+ line.trim().endsWith('.m3u8') &&
41
+ bandwidth !== null &&
42
+ width !== null &&
43
+ height !== null
44
+ ) {
45
+ streams.push({ bandwidth, width, height });
46
+ bandwidth = null;
47
+ width = null;
48
+ height = null;
49
+ }
50
+ }
51
+
52
+ streams.sort((a, b) => (b.height as number) - (a.height as number));
53
+
54
+ const autoStream: StreamInfo = {
55
+ bandwidth: 0,
56
+ width: 'auto',
57
+ height: 'auto',
58
+ };
59
+
60
+ return [autoStream, ...streams];
61
+ } catch (error) {
62
+ console.error('Error fetching or parsing HLS stream:', error);
63
+ return [];
64
+ }
65
+ }
66
+
67
+ export interface Resolution {
68
+ height: number | 'auto';
69
+ bandwidth: number | null;
70
+ }
71
+
72
+ // Simple in-memory cache
73
+ const resolutionCache: Record<string, Resolution[]> = {};
74
+
75
+ export const useVideoResolutions = (track: MediaTrack | null) => {
76
+ const [resolutions, setResolutions] = useState<Resolution[]>([
77
+ { height: 'auto', bandwidth: null },
78
+ ]);
79
+
80
+ useEffect(() => {
81
+ if (!track) return;
82
+
83
+ const source = track.isTrailer ? track.trailerSource : track.sourceLink;
84
+ if (!source) return;
85
+
86
+ // Cache hit
87
+ if (resolutionCache[source]) {
88
+ setResolutions(resolutionCache[source]);
89
+ return;
90
+ }
91
+
92
+ const fetchResolutions = async () => {
93
+ try {
94
+ const data = await getHLSBandwidthAndResolutions(source);
95
+
96
+ const filteredData = data.filter(
97
+ (item) => typeof item.height === 'number'
98
+ );
99
+
100
+ const newResolutions: Resolution[] = [
101
+ { height: 'auto', bandwidth: null },
102
+ ...filteredData.map((item) => ({
103
+ height: item.height as number,
104
+ bandwidth: item.bandwidth,
105
+ })),
106
+ ];
107
+
108
+ resolutionCache[source] = newResolutions; // cache
109
+ setResolutions(newResolutions);
110
+ } catch (error) {
111
+ console.error('Failed to fetch video resolutions', error);
112
+ }
113
+ };
114
+
115
+ fetchResolutions();
116
+ }, [track]);
117
+
118
+ return resolutions;
119
+ };
@@ -0,0 +1,7 @@
1
+ export * from './lockOrientation';
2
+ export * from './hooks';
3
+ export * from './playerEvents';
4
+ export * from './videoControl';
5
+ export * from './videoSource';
6
+ export * from './timeFormatter';
7
+ export * from './videoRef';
@@ -0,0 +1,34 @@
1
+ import { StatusBar, Platform } from 'react-native';
2
+ import Orientation from 'react-native-orientation-locker';
3
+ import SystemNavigationBar from 'react-native-system-navigation-bar';
4
+ import PlatformSelector from './PlatformSelector';
5
+
6
+ export const lockToPortrait = () => {
7
+ try {
8
+ Orientation.lockToPortrait();
9
+ StatusBar.setHidden(false, 'fade');
10
+
11
+ if (PlatformSelector.isAndroid()) {
12
+ SystemNavigationBar.fullScreen(false);
13
+ SystemNavigationBar.navigationShow();
14
+ } else if (Platform.OS === 'ios') {
15
+ }
16
+ } catch (error) {
17
+ console.warn('Error locking to portrait:', error);
18
+ }
19
+ };
20
+
21
+ export const lockToLandscape = () => {
22
+ try {
23
+ Orientation.lockToLandscape();
24
+ StatusBar.setHidden(true, 'fade');
25
+
26
+ if (PlatformSelector.isAndroid()) {
27
+ SystemNavigationBar.fullScreen(true);
28
+ SystemNavigationBar.stickyImmersive();
29
+ } else if (Platform.OS === 'ios') {
30
+ }
31
+ } catch (error) {
32
+ console.warn('Error locking to landscape:', error);
33
+ }
34
+ };
@@ -0,0 +1,97 @@
1
+ import type {
2
+ OnBufferData,
3
+ OnLoadData,
4
+ OnLoadStartData,
5
+ OnProgressData,
6
+ } from 'react-native-video';
7
+ import { mmkvStorage, useVideoPlayerStore } from '../store/videoPlayerStore';
8
+
9
+ import { handleNext } from '.';
10
+ import type { MediaEpisode } from '../store/videoPlayer.type';
11
+ import type { ExtendedWatchProgress } from './useWatchReporter';
12
+
13
+ export const createPlayerEvents = () => {
14
+ const onProgress = ({
15
+ currentTime = 0,
16
+ playableDuration = 0,
17
+ }: OnProgressData) => {
18
+ const store = useVideoPlayerStore.getState(); // always fresh store
19
+ store.setCurrentTime(currentTime);
20
+ store.setPlayableDuration(playableDuration);
21
+
22
+ try {
23
+ mmkvStorage.setItem('currentTime', String(currentTime));
24
+ } catch (e) {
25
+ console.warn('MMKV save failed', e);
26
+ }
27
+
28
+ if (store.isBuffering) store.setIsBuffering(false);
29
+ };
30
+
31
+ const onLoad = (data: OnLoadData) => {
32
+ const store = useVideoPlayerStore.getState();
33
+ store.setDuration(data.duration || 0);
34
+ store.setOnLoad(data);
35
+
36
+ mmkvStorage.setItem('current_watch_time', '0');
37
+
38
+ store.setError(null);
39
+ store.setIsViewCounted(false);
40
+ store.setStartWatchTime(Date.now());
41
+ store.setIsNextEpisodeVisible(true);
42
+ store.setIsSkipIntroVisible(true);
43
+ store.setIsBuffering(false);
44
+ };
45
+
46
+ const onBuffer = ({ isBuffering }: OnBufferData) => {
47
+ const store = useVideoPlayerStore.getState();
48
+ if (isBuffering) store.setIsBuffering(true);
49
+ else setTimeout(() => store.setIsBuffering(false), 150);
50
+ };
51
+
52
+ const onEnd = ({
53
+ onPressEpisode,
54
+ reportProgress,
55
+ autoNext,
56
+ }: {
57
+ reportProgress: (event: ExtendedWatchProgress['event']) => void;
58
+ onPressEpisode: ({
59
+ episode,
60
+ }: {
61
+ episode: MediaEpisode;
62
+ }) => Promise<boolean>;
63
+ autoNext: boolean;
64
+ }) => {
65
+ reportProgress('COMPLETED');
66
+ const store = useVideoPlayerStore.getState();
67
+ if (!store.activeTrack?.isTrailer && store.activeTrack?.type === 'series') {
68
+ handleNext({ onPressEpisode, autoNext });
69
+ } else {
70
+ store.setIsPaused(true);
71
+ }
72
+ };
73
+
74
+ const onLoadStart = (_: OnLoadStartData) => {
75
+ const store = useVideoPlayerStore.getState();
76
+ store.setIsBuffering(true);
77
+ };
78
+
79
+ const LeaveVideoPlayer = () => {
80
+ const store = useVideoPlayerStore.getState();
81
+ store.setDuration(0);
82
+ store.setCurrentTime(0);
83
+ store.setPlayableDuration(0);
84
+ store.setIsPaused(false);
85
+ store.setSettingsModal({ isVisible: false, action: 'none' });
86
+ store.setIsBuffering(false);
87
+ };
88
+
89
+ return {
90
+ onProgress,
91
+ onLoad,
92
+ onBuffer,
93
+ onEnd,
94
+ onLoadStart,
95
+ LeaveVideoPlayer,
96
+ };
97
+ };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @author Naresh Dhamu
3
+ * @lastModified Sat 16 Aug 2025 at 10:32 PM
4
+ * @description: Utility functions for formatting time
5
+ */
6
+
7
+ export const formatDuration = (
8
+ totalSeconds: number,
9
+ hideSeconds = false,
10
+ addSpaces = false
11
+ ): string => {
12
+ const hours = Math.floor(totalSeconds / 3600);
13
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
14
+ const seconds = totalSeconds % 60;
15
+
16
+ const formatUnit = (value: number, label: string) =>
17
+ value ? `${value}${addSpaces ? ` ${label} ` : label}` : '';
18
+
19
+ const result =
20
+ formatUnit(hours, 'h') +
21
+ formatUnit(minutes, 'm') +
22
+ formatUnit(seconds, 's');
23
+
24
+ return hideSeconds
25
+ ? formatUnit(hours, 'h') + formatUnit(minutes, 'm')
26
+ : result;
27
+ };
28
+
29
+ export const formatTime = (seconds: number) => {
30
+ const h = Math.floor(seconds / 3600);
31
+ const m = Math.floor((seconds % 3600) / 60);
32
+ const s = Math.floor(seconds % 60);
33
+ if (h > 0) {
34
+ return `${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
35
+ }
36
+ return `${m}:${String(s).padStart(2, '0')}`;
37
+ };
38
+
39
+ export const formatTimeWithMs = (seconds: number): string => {
40
+ if (isNaN(seconds) || seconds < 0) return '00:00.000';
41
+ const totalMs = Math.floor(seconds * 1000);
42
+ const ms = totalMs % 1000;
43
+ const totalSec = Math.floor(totalMs / 1000);
44
+ const min = Math.floor(totalSec / 60);
45
+ const sec = totalSec % 60;
46
+ return `${String(min).padStart(2, '0')}:${String(sec).padStart(2, '0')}.${String(ms).padStart(3, '0')}`;
47
+ };