@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,106 @@
1
+ /**
2
+ * @author Naresh Dhamu
3
+ * @lastModified Fri 08 Aug 2025 at 06:55 PM
4
+ */
5
+
6
+ interface IFonts {
7
+ // inter font
8
+ inter: {
9
+ black: string;
10
+ bold: string;
11
+ extraBold: string;
12
+ extraLight: string;
13
+ light: string;
14
+ medium: string;
15
+ regular: string;
16
+ semiBold: string;
17
+ thin: string;
18
+ };
19
+ // poppins font
20
+ poppins: {
21
+ black: string;
22
+ blackItalic: string;
23
+ bold: string;
24
+ boldItalic: string;
25
+ extraBold: string;
26
+ extraBoldItalic: string;
27
+ extraLight: string;
28
+ extraLightItalic: string;
29
+ italic: string;
30
+ light: string;
31
+ lightItalic: string;
32
+ medium: string;
33
+ mediumItalic: string;
34
+ regular: string;
35
+ semiBold: string;
36
+ semiBoldItalic: string;
37
+ thin: string;
38
+ thinItalic: string;
39
+ };
40
+ // roboto font
41
+ roboto: {
42
+ black: string;
43
+ blackItalic: string;
44
+ bold: string;
45
+ boldItalic: string;
46
+ italic: string;
47
+ light: string;
48
+ lightItalic: string;
49
+ medium: string;
50
+ mediumItalic: string;
51
+ regular: string;
52
+ thin: string;
53
+ thinItalic: string;
54
+ };
55
+ }
56
+
57
+ export const Fonts: IFonts = {
58
+ // inter font
59
+ inter: {
60
+ black: 'Inter-Black',
61
+ bold: 'Inter-Bold',
62
+ extraBold: 'Inter-ExtraBold',
63
+ extraLight: 'Inter-ExtraLight',
64
+ light: 'Inter-Light',
65
+ medium: 'Inter-Medium',
66
+ regular: 'Inter-Regular',
67
+ semiBold: 'Inter-SemiBold',
68
+ thin: 'Inter-Thin',
69
+ },
70
+ // poppins font
71
+ poppins: {
72
+ black: 'Poppins-Black',
73
+ blackItalic: 'Poppins-BlackItalic',
74
+ bold: 'Poppins-Bold',
75
+ boldItalic: 'Poppins-BoldItalic',
76
+ extraBold: 'Poppins-ExtraBold',
77
+ extraBoldItalic: 'Poppins-ExtraBoldItalic',
78
+ extraLight: 'Poppins-ExtraLight',
79
+ extraLightItalic: 'Poppins-ExtraLightItalic',
80
+ italic: 'Poppins-Italic',
81
+ light: 'Poppins-Light',
82
+ lightItalic: 'Poppins-LightItalic',
83
+ medium: 'Poppins-Medium',
84
+ mediumItalic: 'Poppins-MediumItalic',
85
+ regular: 'Poppins-Regular',
86
+ semiBold: 'Poppins-SemiBold',
87
+ semiBoldItalic: 'Poppins-SemiBoldItalic',
88
+ thin: 'Poppins-Thin',
89
+ thinItalic: 'Poppins-ThinItalic',
90
+ },
91
+ // roboto font
92
+ roboto: {
93
+ black: 'Roboto-Black',
94
+ blackItalic: 'Roboto-BlackItalic',
95
+ bold: 'Roboto-Bold',
96
+ boldItalic: 'Roboto-BoldItalic',
97
+ italic: 'Roboto-Italic',
98
+ light: 'Roboto-Light',
99
+ lightItalic: 'Roboto-LightItalic',
100
+ medium: 'Roboto-Medium',
101
+ mediumItalic: 'Roboto-MediumItalic',
102
+ regular: 'Roboto-Regular',
103
+ thin: 'Roboto-Thin',
104
+ thinItalic: 'Roboto-ThinItalic',
105
+ },
106
+ };
@@ -0,0 +1,74 @@
1
+ /**
2
+ * @author Naresh Dhamu
3
+ * @lastModified Fri 08 Aug 2025 at 06:53 PM
4
+ */
5
+
6
+ import { StyleSheet } from 'react-native';
7
+
8
+ const globalStyles = StyleSheet.create({
9
+ centerContainer: {
10
+ flex: 1,
11
+ alignItems: 'center',
12
+ justifyContent: 'center',
13
+ },
14
+ container: {
15
+ flex: 1,
16
+ },
17
+ newContainer: {
18
+ flex: 1,
19
+ },
20
+ // new styles
21
+ flexOneJustifyContentAndAlignItemsCenter: {
22
+ flex: 1,
23
+ justifyContent: 'center',
24
+ alignItems: 'center',
25
+ },
26
+ flexOneJustifyContentCenterAndAlignItemsCenter: {
27
+ flex: 1,
28
+ justifyContent: 'center',
29
+ alignItems: 'center',
30
+ },
31
+ hundredPercentWidth: {
32
+ width: '100%',
33
+ },
34
+ hundredPercentHeight: {
35
+ height: '100%',
36
+ },
37
+ marginZeroAndPaddingZero: {
38
+ margin: 0,
39
+ padding: 0,
40
+ },
41
+ overflowHidden: {
42
+ overflow: 'hidden',
43
+ },
44
+ flexOne: {
45
+ flex: 1,
46
+ },
47
+ flexZero: {
48
+ flex: 0,
49
+ },
50
+ positionAbsolute: {
51
+ position: 'absolute',
52
+ },
53
+ twentyPercentWidth: {
54
+ width: '20%',
55
+ },
56
+ alignItemsCenterAndJustifyContentCenter: {
57
+ alignItems: 'center',
58
+ justifyContent: 'center',
59
+ },
60
+ textAlignCenter: {
61
+ textAlign: 'center',
62
+ },
63
+ flexRow_alignCenter_justifyBetween: {
64
+ flexDirection: 'row',
65
+ alignItems: 'center',
66
+ justifyContent: 'space-between',
67
+ },
68
+ flexOneWithBlackBackground: {
69
+ flex: 1,
70
+ backgroundColor: 'black',
71
+ },
72
+ });
73
+
74
+ export default globalStyles;
@@ -0,0 +1,217 @@
1
+ import React, { useCallback, useEffect } from 'react';
2
+ import { AppState, StyleSheet, View, type AppStateStatus } from 'react-native';
3
+ import Video, {
4
+ type OnProgressData,
5
+ type SelectedTrack,
6
+ } from 'react-native-video';
7
+ import { SafeAreaProvider } from 'react-native-safe-area-context';
8
+ import {
9
+ Gesture,
10
+ GestureDetector,
11
+ GestureHandlerRootView,
12
+ } from 'react-native-gesture-handler';
13
+ import { runOnJS } from 'react-native-reanimated';
14
+
15
+ import { videoRef } from './utils/videoRef';
16
+ import { lockToLandscape, lockToPortrait } from './utils/lockOrientation';
17
+ import { getVideoSource, handlePause } from './utils';
18
+ import MediaControlsProvider from './MediaControls/MediaControlsProvider';
19
+ import { useVideoPlayerStore } from './store/videoPlayerStore';
20
+ import { createPlayerEvents } from './utils/playerEvents';
21
+ import type { MediaEpisode } from './store/videoPlayer.type';
22
+ import globalStyles from './Styles/globalStyles';
23
+ import { VideoPlayerConfigProvider, type VideoPlayerTheme } from './context';
24
+ import {
25
+ useWatchReporter,
26
+ type ExtendedWatchProgress,
27
+ } from './utils/useWatchReporter';
28
+
29
+ export interface VideoPlayerProps {
30
+ onClose?: () => void;
31
+ isFocused?: boolean;
32
+ seekTime?: number | null;
33
+ mode?: 'fullscreen' | 'normal';
34
+ onSeek?: (time: number) => void;
35
+ autoNext?: boolean;
36
+ event?: {
37
+ onPressEpisode?: ({
38
+ episode,
39
+ }: {
40
+ episode: MediaEpisode;
41
+ }) => Promise<boolean>;
42
+ };
43
+ theme?: Partial<VideoPlayerTheme>;
44
+ onWatchProgress?: (progress: ExtendedWatchProgress) => void;
45
+ }
46
+
47
+ const controlsStyles = {
48
+ hideForward: true,
49
+ hideDuration: true,
50
+ hideFullscreen: true,
51
+ hidePlayPause: true,
52
+ hideNavigationBarOnFullScreenMode: true,
53
+ hideNotificationBarOnFullScreenMode: true,
54
+ hideNext: true,
55
+ hidePosition: true,
56
+ hidePrevious: true,
57
+ hideRewind: true,
58
+ hideSeekBar: true,
59
+ hideSettingButton: true,
60
+ };
61
+
62
+ const VideoPlayer: React.FC<VideoPlayerProps> = ({
63
+ onClose,
64
+ isFocused = true,
65
+ mode = 'fullscreen',
66
+ seekTime,
67
+ event,
68
+ autoNext = true,
69
+ theme,
70
+ onWatchProgress,
71
+ }) => {
72
+ const {
73
+ isPaused,
74
+ resizeMode,
75
+ setResizeMode,
76
+ setControlsVisible,
77
+ controlsVisible,
78
+ playBackRate,
79
+ selectedAudioTrack,
80
+ selectedSubtitleTrack,
81
+ maxBitRate,
82
+ selectedVideoTrack,
83
+ setIsPaused,
84
+ playList,
85
+ currentTrackIndex,
86
+ activeTrack,
87
+ setError,
88
+ } = useVideoPlayerStore();
89
+
90
+ const playerEvents = createPlayerEvents();
91
+ const { reportProgress } = useWatchReporter({ onWatchProgress });
92
+
93
+ useEffect(() => {
94
+ if (isFocused) {
95
+ if (mode === 'fullscreen') {
96
+ lockToLandscape();
97
+ } else {
98
+ lockToPortrait();
99
+ }
100
+ } else {
101
+ lockToPortrait();
102
+ }
103
+ }, [isFocused, mode]);
104
+
105
+ const handleCombinedProgress = useCallback(
106
+ (progressEvent: OnProgressData) => {
107
+ playerEvents.onProgress(progressEvent);
108
+ },
109
+ [playerEvents]
110
+ );
111
+
112
+ useEffect(() => {
113
+ if (seekTime && !activeTrack?.isTrailer && videoRef?.current) {
114
+ videoRef.current?.seek(seekTime);
115
+ }
116
+
117
+ // eslint-disable-next-line react-hooks/exhaustive-deps
118
+ }, [seekTime, videoRef?.current]);
119
+
120
+ useEffect(() => {
121
+ const handleAppStateChange = (nextState: AppStateStatus) => {
122
+ if (nextState === 'inactive' || nextState === 'background') {
123
+ handlePause();
124
+ setIsPaused(true);
125
+ reportProgress('PROGRESS');
126
+ }
127
+ };
128
+ const sub = AppState.addEventListener('change', handleAppStateChange);
129
+ return () => sub.remove();
130
+ }, [reportProgress, setIsPaused]);
131
+
132
+ const trigger = (type: 'Pinch' | 'Tap') => {
133
+ switch (type) {
134
+ case 'Pinch':
135
+ if (resizeMode === 'cover') {
136
+ setResizeMode('contain');
137
+ } else {
138
+ setResizeMode('cover');
139
+ }
140
+ break;
141
+ case 'Tap': {
142
+ setControlsVisible(!controlsVisible);
143
+ }
144
+ }
145
+ };
146
+ const composedGestures = Gesture.Race(
147
+ Gesture.Pinch().onEnd(() => runOnJS(trigger)('Pinch')),
148
+ Gesture.Tap().onEnd(() => runOnJS(trigger)('Tap'))
149
+ );
150
+
151
+ return (
152
+ <VideoPlayerConfigProvider theme={theme}>
153
+ <SafeAreaProvider style={globalStyles.flexOne}>
154
+ <GestureHandlerRootView style={globalStyles.flexOne}>
155
+ <MediaControlsProvider
156
+ onClose={() => {
157
+ reportProgress('VIDEO_CLOSE');
158
+ onClose?.();
159
+ }}
160
+ onPressEpisode={async ({ episode }) => {
161
+ if (event?.onPressEpisode) {
162
+ return await event.onPressEpisode({ episode });
163
+ }
164
+ return Promise.resolve(true);
165
+ }}
166
+ reportProgress={reportProgress}
167
+ >
168
+ <GestureDetector gesture={composedGestures}>
169
+ <View style={globalStyles.flexOneWithBlackBackground}>
170
+ <Video
171
+ ref={videoRef}
172
+ source={getVideoSource({ playList, currentTrackIndex })}
173
+ paused={isPaused}
174
+ onProgress={handleCombinedProgress}
175
+ onLoad={playerEvents.onLoad}
176
+ onBuffer={playerEvents.onBuffer}
177
+ onEnd={() => {
178
+ playerEvents.onEnd({
179
+ reportProgress,
180
+ onPressEpisode:
181
+ event?.onPressEpisode ?? (async () => true),
182
+ autoNext,
183
+ });
184
+ }}
185
+ onLoadStart={playerEvents.onLoadStart}
186
+ onError={() => setError("Video couldn't be loaded")}
187
+ controls={false}
188
+ resizeMode={resizeMode || 'contain'}
189
+ rate={playBackRate || 1}
190
+ selectedAudioTrack={selectedAudioTrack as SelectedTrack}
191
+ selectedTextTrack={
192
+ selectedSubtitleTrack?.value === 'Off'
193
+ ? undefined
194
+ : (selectedSubtitleTrack as SelectedTrack)
195
+ }
196
+ selectedVideoTrack={
197
+ selectedVideoTrack
198
+ ? {
199
+ type: selectedVideoTrack.type,
200
+ value: selectedVideoTrack.value,
201
+ }
202
+ : undefined
203
+ }
204
+ maxBitRate={maxBitRate ?? undefined}
205
+ style={StyleSheet.absoluteFillObject}
206
+ controlsStyles={controlsStyles}
207
+ />
208
+ </View>
209
+ </GestureDetector>
210
+ </MediaControlsProvider>
211
+ </GestureHandlerRootView>
212
+ </SafeAreaProvider>
213
+ </VideoPlayerConfigProvider>
214
+ );
215
+ };
216
+
217
+ export default VideoPlayer;
@@ -0,0 +1,98 @@
1
+ /* eslint-disable react-hooks/exhaustive-deps */
2
+ import React, { useEffect } from 'react';
3
+ import { View, StyleSheet } from 'react-native';
4
+ import { Slider } from 'react-native-awesome-slider';
5
+ import { useSharedValue, withTiming } from 'react-native-reanimated';
6
+ import { verticalScale } from 'react-native-size-matters';
7
+ import { useVideoPlayerConfig } from '../context';
8
+
9
+ interface Props {
10
+ duration: number;
11
+ currentTime: number;
12
+ bufferedTime: number;
13
+ onSeek: (time: number) => void;
14
+ showThumb?: boolean;
15
+ height?: number;
16
+ }
17
+
18
+ const ProgressBar: React.FC<Props> = React.memo(
19
+ ({ duration, currentTime, bufferedTime, onSeek, showThumb = true }) => {
20
+ const { colors, metrics } = useVideoPlayerConfig();
21
+ const progress = useSharedValue(currentTime);
22
+ const cache = useSharedValue(bufferedTime);
23
+ const min = useSharedValue(0);
24
+ const max = useSharedValue(duration);
25
+
26
+ // Smoothly animate progress
27
+ useEffect(() => {
28
+ progress.value = withTiming(currentTime, { duration: 200 });
29
+ }, [currentTime]);
30
+
31
+ useEffect(() => {
32
+ cache.value = withTiming(bufferedTime, { duration: 200 });
33
+ }, [bufferedTime]);
34
+
35
+ useEffect(() => {
36
+ max.value = duration;
37
+ }, [duration]);
38
+
39
+ return (
40
+ <View style={styles.container}>
41
+ <Slider
42
+ progress={progress}
43
+ minimumValue={min}
44
+ maximumValue={max}
45
+ cache={cache}
46
+ onValueChange={onSeek}
47
+ containerStyle={[
48
+ styles.sliderContainer,
49
+ {
50
+ borderRadius: metrics.sliderHeight / 2,
51
+ height: metrics.sliderHeight,
52
+ },
53
+ ]}
54
+ renderThumb={
55
+ showThumb
56
+ ? () => (
57
+ <View
58
+ style={{
59
+ backgroundColor: colors.primary,
60
+ width: metrics.thumbSize,
61
+ height: metrics.thumbSize,
62
+ borderRadius: metrics.thumbSize / 2,
63
+ }}
64
+ />
65
+ )
66
+ : () => null
67
+ }
68
+ renderBubble={() => null}
69
+ thumbWidth={metrics.thumbSize}
70
+ theme={{
71
+ minimumTrackTintColor: colors.primary,
72
+ bubbleBackgroundColor: colors.white,
73
+ maximumTrackTintColor: colors.secondary,
74
+ cacheTrackTintColor: colors.buffer,
75
+ }}
76
+ />
77
+ </View>
78
+ );
79
+ },
80
+ (prevProps, nextProps) =>
81
+ prevProps.currentTime === nextProps.currentTime &&
82
+ prevProps.bufferedTime === nextProps.bufferedTime &&
83
+ prevProps.duration === nextProps.duration
84
+ );
85
+
86
+ export default ProgressBar;
87
+
88
+ const styles = StyleSheet.create({
89
+ container: {
90
+ position: 'relative',
91
+ width: '100%',
92
+ height: verticalScale(20),
93
+ justifyContent: 'center',
94
+ },
95
+ sliderContainer: {
96
+ overflow: 'hidden',
97
+ },
98
+ });
@@ -0,0 +1,195 @@
1
+ import React, { useEffect } from 'react';
2
+ import {
3
+ TouchableOpacity,
4
+ View,
5
+ StyleSheet,
6
+ Text,
7
+ type TextStyle,
8
+ } from 'react-native';
9
+ import Animated, {
10
+ useSharedValue,
11
+ useAnimatedStyle,
12
+ withTiming,
13
+ } from 'react-native-reanimated';
14
+ import { scale } from 'react-native-size-matters';
15
+ import { useVideoPlayerStore } from '../store/videoPlayerStore';
16
+ import { videoRef } from '../utils/videoRef';
17
+ import Display from '../utils/Display';
18
+ import { handleNext } from '../utils';
19
+ import type { MediaEpisode } from '../store/videoPlayer.type';
20
+ import { useVideoPlayerConfig } from '../context';
21
+ import type { ExtendedWatchProgress } from '../utils/useWatchReporter';
22
+
23
+ type SkipAndNextControlsProps = {
24
+ onPressEpisode: ({ episode }: { episode: MediaEpisode }) => Promise<boolean>;
25
+ reportProgress: (event: ExtendedWatchProgress['event']) => void;
26
+ };
27
+
28
+ const SkipAndNextControls: React.FC<SkipAndNextControlsProps> = ({
29
+ onPressEpisode,
30
+ reportProgress,
31
+ }) => {
32
+ const {
33
+ activeTrack,
34
+ currentTime,
35
+ isSkipIntroVisible,
36
+ isNextEpisodeVisible,
37
+ setIsNextEpisodeVisible,
38
+ setIsSkipIntroVisible,
39
+ settingsModal,
40
+ controlsVisible,
41
+ } = useVideoPlayerStore();
42
+ const { colors } = useVideoPlayerConfig();
43
+
44
+ if (activeTrack?.type !== 'series') return null;
45
+
46
+ const showSkipIntro =
47
+ isSkipIntroVisible &&
48
+ !settingsModal.isVisible &&
49
+ activeTrack.skipIntro &&
50
+ activeTrack.skipIntro.start < currentTime &&
51
+ currentTime < activeTrack.skipIntro.end;
52
+
53
+ const showNextEpisode =
54
+ isNextEpisodeVisible &&
55
+ !settingsModal.isVisible &&
56
+ activeTrack.nextEpisodeAt &&
57
+ activeTrack.nextEpisodeAt < currentTime;
58
+
59
+ return (
60
+ <>
61
+ {showSkipIntro && (
62
+ <ActionButton
63
+ label="Skip intro"
64
+ onPress={() => {
65
+ if (activeTrack.skipIntro?.end) {
66
+ videoRef?.current?.seek(activeTrack.skipIntro.end);
67
+ }
68
+ }}
69
+ onCancel={() => setIsSkipIntroVisible(false)}
70
+ position="left"
71
+ colors={{ text: colors.black, onBackground: colors.onBackground }}
72
+ controlsVisible={controlsVisible}
73
+ />
74
+ )}
75
+
76
+ {showNextEpisode && (
77
+ <ActionButton
78
+ label="Next episode"
79
+ onPress={() => {
80
+ reportProgress('EPISODE_CHANGE');
81
+ handleNext({ onPressEpisode, autoNext: true });
82
+ }}
83
+ onCancel={() => setIsNextEpisodeVisible(false)}
84
+ position="right"
85
+ colors={{ text: colors.black, onBackground: colors.onBackground }}
86
+ controlsVisible={controlsVisible}
87
+ />
88
+ )}
89
+ </>
90
+ );
91
+ };
92
+
93
+ export default SkipAndNextControls;
94
+
95
+ type ActionButtonColors = {
96
+ text: string;
97
+ onBackground: string;
98
+ };
99
+
100
+ type ActionButtonProps = {
101
+ label: string;
102
+ onPress: () => void;
103
+ onCancel: () => void;
104
+ position: 'left' | 'right';
105
+ colors: ActionButtonColors;
106
+ controlsVisible: boolean;
107
+ };
108
+
109
+ const ActionButton: React.FC<ActionButtonProps> = ({
110
+ label,
111
+ onPress,
112
+ onCancel,
113
+ position,
114
+ colors,
115
+ controlsVisible,
116
+ }) => {
117
+ const opacity = useSharedValue(0);
118
+ const translateY = useSharedValue(10);
119
+ const bottom = useSharedValue(
120
+ controlsVisible ? Display.setHeight(12) : Display.setHeight(10)
121
+ );
122
+
123
+ // Animate show
124
+ useEffect(() => {
125
+ opacity.value = withTiming(1, { duration: 100 });
126
+ translateY.value = withTiming(0, { duration: 100 });
127
+ }, [opacity, translateY]);
128
+
129
+ // Animate bottom when controls toggle
130
+ useEffect(() => {
131
+ bottom.value = withTiming(
132
+ controlsVisible ? Display.setHeight(12) : Display.setHeight(10),
133
+ { duration: 100 }
134
+ );
135
+ }, [bottom, controlsVisible]);
136
+
137
+ const animatedStyle = useAnimatedStyle(() => ({
138
+ opacity: opacity.value,
139
+ transform: [{ translateY: translateY.value }],
140
+ bottom: bottom.value,
141
+ }));
142
+
143
+ return (
144
+ <Animated.View
145
+ style={[
146
+ styles.overlayButton,
147
+ animatedStyle,
148
+ position === 'left' ? { left: scale(58) } : { right: scale(58) },
149
+ ]}
150
+ >
151
+ <View style={styles.buttonRow}>
152
+ <TouchableOpacity
153
+ style={[
154
+ styles.actionButton,
155
+ { backgroundColor: colors.onBackground },
156
+ ]}
157
+ onPress={onPress}
158
+ >
159
+ <Text style={makeTextStyle(colors.text)}>{label}</Text>
160
+ </TouchableOpacity>
161
+ <TouchableOpacity
162
+ style={[
163
+ styles.actionButton,
164
+ { backgroundColor: colors.onBackground },
165
+ ]}
166
+ onPress={onCancel}
167
+ >
168
+ <Text style={makeTextStyle(colors.text)}>Cancel</Text>
169
+ </TouchableOpacity>
170
+ </View>
171
+ </Animated.View>
172
+ );
173
+ };
174
+
175
+ const styles = StyleSheet.create({
176
+ overlayButton: {
177
+ position: 'absolute', // fixed, not animated
178
+ zIndex: 10,
179
+ },
180
+ buttonRow: {
181
+ flexDirection: 'row',
182
+ gap: scale(10),
183
+ },
184
+ actionButton: {
185
+ paddingHorizontal: scale(20),
186
+ paddingVertical: scale(8),
187
+ borderRadius: scale(8),
188
+ alignSelf: 'flex-start',
189
+ },
190
+ });
191
+
192
+ const makeTextStyle = (color: string): TextStyle => ({
193
+ color,
194
+ fontWeight: '600',
195
+ });