@zezosoft/zezo-ott-react-native-video-player 1.0.3 → 1.0.5
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.
- package/lib/module/AdsPlayer/MediaControls/AdBottomControls.js +2 -2
- package/lib/module/AdsPlayer/MediaControls/AdBottomControls.js.map +1 -1
- package/package.json +1 -2
- package/src/AdsPlayer/AdsPlayer.tsx +0 -311
- package/src/AdsPlayer/MediaControls/AdBottomControls.tsx +0 -191
- package/src/AdsPlayer/MediaControls/AdMediaControls.tsx +0 -104
- package/src/AdsPlayer/MediaControls/AdMediaControlsProvider.tsx +0 -62
- package/src/AdsPlayer/MediaControls/AdMiddleControls.tsx +0 -63
- package/src/AdsPlayer/MediaControls/AdTopControls.tsx +0 -191
- package/src/AdsPlayer/MediaControls/index.ts +0 -5
- package/src/AdsPlayer/components/RotatingLoader.tsx +0 -79
- package/src/AdsPlayer/index.ts +0 -4
- package/src/AdsPlayer/store/adsPlayer.type.ts +0 -29
- package/src/AdsPlayer/store/adsPlayerStore.ts +0 -59
- package/src/AdsPlayer/store/index.ts +0 -2
- package/src/AdsPlayer/utils/adStateReset.ts +0 -29
- package/src/AdsPlayer/utils/controls.ts +0 -69
- package/src/AdsPlayer/utils/useAdControlsAutoHide.ts +0 -32
- package/src/AdsPlayer/utils/useAdInitialization.ts +0 -86
- package/src/AdsPlayer/utils/useAdTracking.ts +0 -89
- package/src/AdsPlayer/utils/useAdsManager.ts +0 -215
- package/src/VideoPlayer/MediaControls/BottomControls.tsx +0 -210
- package/src/VideoPlayer/MediaControls/MediaControls.tsx +0 -30
- package/src/VideoPlayer/MediaControls/MediaControlsProvider.tsx +0 -104
- package/src/VideoPlayer/MediaControls/MiddleControls.tsx +0 -259
- package/src/VideoPlayer/MediaControls/TopControls.tsx +0 -100
- package/src/VideoPlayer/Settings/AudioAndSubtitles.tsx +0 -295
- package/src/VideoPlayer/Settings/Episodes.tsx +0 -297
- package/src/VideoPlayer/Settings/SettingModal.tsx +0 -127
- package/src/VideoPlayer/Settings/SpeedControls.tsx +0 -130
- package/src/VideoPlayer/Settings/VideoPlayerSettings.tsx +0 -141
- package/src/VideoPlayer/VideoPlayerCore.tsx +0 -356
- package/src/VideoPlayer/components/ProgressBar.tsx +0 -211
- package/src/VideoPlayer/components/SkipAndNextControls.tsx +0 -192
- package/src/VideoPlayer/components/SubtitleView.tsx +0 -53
- package/src/VideoPlayer/components/Toast.tsx +0 -61
- package/src/VideoPlayer/context/VideoPlayerConfig.tsx +0 -65
- package/src/VideoPlayer/context/index.ts +0 -5
- package/src/VideoPlayer/index.ts +0 -4
- package/src/VideoPlayer/store/index.ts +0 -2
- package/src/VideoPlayer/store/videoPlayer.type.ts +0 -214
- package/src/VideoPlayer/store/videoPlayerStore.ts +0 -97
- package/src/VideoPlayer/styles/globalStyles.ts +0 -73
- package/src/VideoPlayer/utils/display/Display.ts +0 -10
- package/src/VideoPlayer/utils/display/index.ts +0 -1
- package/src/VideoPlayer/utils/format/index.ts +0 -1
- package/src/VideoPlayer/utils/format/timeFormatter.ts +0 -44
- package/src/VideoPlayer/utils/hooks/index.ts +0 -5
- package/src/VideoPlayer/utils/hooks/useAdEventHandler.ts +0 -95
- package/src/VideoPlayer/utils/hooks/useOrientationLock.ts +0 -29
- package/src/VideoPlayer/utils/hooks/usePauseVideoOnAd.ts +0 -46
- package/src/VideoPlayer/utils/hooks/useVideoPlayerBack.ts +0 -66
- package/src/VideoPlayer/utils/hooks/useVideoResolutions.ts +0 -125
- package/src/VideoPlayer/utils/index.ts +0 -6
- package/src/VideoPlayer/utils/platform/PlatformSelector.ts +0 -13
- package/src/VideoPlayer/utils/platform/index.ts +0 -2
- package/src/VideoPlayer/utils/platform/lockOrientation.ts +0 -40
- package/src/VideoPlayer/utils/player/index.ts +0 -2
- package/src/VideoPlayer/utils/player/playerEvents.ts +0 -97
- package/src/VideoPlayer/utils/player/useWatchReporter.ts +0 -105
- package/src/VideoPlayer/utils/video/index.ts +0 -5
- package/src/VideoPlayer/utils/video/videoControl.ts +0 -185
- package/src/VideoPlayer/utils/video/videoRef.ts +0 -21
- package/src/VideoPlayer/utils/video/videoResume.ts +0 -23
- package/src/VideoPlayer/utils/video/videoSource.ts +0 -23
- package/src/VideoPlayer.tsx +0 -181
- package/src/index.tsx +0 -3
|
@@ -1,125 +0,0 @@
|
|
|
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
|
-
headers?: Record<string, string>
|
|
14
|
-
): Promise<StreamInfo[]> {
|
|
15
|
-
try {
|
|
16
|
-
if (!m3u8Url?.endsWith('.m3u8')) return [];
|
|
17
|
-
|
|
18
|
-
const { data } = await axios.get<string>(m3u8Url, {
|
|
19
|
-
headers: headers ?? undefined,
|
|
20
|
-
});
|
|
21
|
-
if (!data) return [];
|
|
22
|
-
|
|
23
|
-
const lines = data.split('\n');
|
|
24
|
-
const streams: StreamInfo[] = [];
|
|
25
|
-
|
|
26
|
-
let bandwidth: number | null = null;
|
|
27
|
-
let width: number | null = null;
|
|
28
|
-
let height: number | null = null;
|
|
29
|
-
|
|
30
|
-
for (const line of lines) {
|
|
31
|
-
if (line.startsWith('#EXT-X-STREAM-INF')) {
|
|
32
|
-
const bandwidthMatch = line.match(/BANDWIDTH=(\d+)/);
|
|
33
|
-
const resolutionMatch = line.match(/RESOLUTION=(\d+)x(\d+)/);
|
|
34
|
-
|
|
35
|
-
bandwidth = bandwidthMatch ? Number(bandwidthMatch[1]) : null;
|
|
36
|
-
if (resolutionMatch) {
|
|
37
|
-
width = Number(resolutionMatch[1]);
|
|
38
|
-
height = Number(resolutionMatch[2]);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (
|
|
43
|
-
line.trim().endsWith('.m3u8') &&
|
|
44
|
-
bandwidth !== null &&
|
|
45
|
-
width !== null &&
|
|
46
|
-
height !== null
|
|
47
|
-
) {
|
|
48
|
-
streams.push({ bandwidth, width, height });
|
|
49
|
-
bandwidth = null;
|
|
50
|
-
width = null;
|
|
51
|
-
height = null;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
streams.sort((a, b) => (b.height as number) - (a.height as number));
|
|
56
|
-
|
|
57
|
-
const autoStream: StreamInfo = {
|
|
58
|
-
bandwidth: 0,
|
|
59
|
-
width: 'auto',
|
|
60
|
-
height: 'auto',
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
return [autoStream, ...streams];
|
|
64
|
-
} catch {
|
|
65
|
-
return [];
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export interface Resolution {
|
|
70
|
-
height: number | 'auto';
|
|
71
|
-
bandwidth: number | null;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const resolutionCache: Record<string, Resolution[]> = {};
|
|
75
|
-
|
|
76
|
-
export const useVideoResolutions = (track: MediaTrack | null) => {
|
|
77
|
-
const [resolutions, setResolutions] = useState<Resolution[]>([
|
|
78
|
-
{ height: 'auto', bandwidth: null },
|
|
79
|
-
]);
|
|
80
|
-
|
|
81
|
-
useEffect(() => {
|
|
82
|
-
if (!track) return;
|
|
83
|
-
|
|
84
|
-
const source = track.isTrailer ? track.trailerSource : track.sourceLink;
|
|
85
|
-
if (!source) return;
|
|
86
|
-
|
|
87
|
-
if (resolutionCache[source]) {
|
|
88
|
-
setResolutions(resolutionCache[source]);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
let cancelled = false;
|
|
93
|
-
|
|
94
|
-
const fetchResolutions = async () => {
|
|
95
|
-
try {
|
|
96
|
-
const data = await getHLSBandwidthAndResolutions(source);
|
|
97
|
-
if (cancelled) return;
|
|
98
|
-
|
|
99
|
-
const filteredData = data.filter(
|
|
100
|
-
(item): item is StreamInfo & { height: number } =>
|
|
101
|
-
typeof item.height === 'number'
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
const newResolutions: Resolution[] = [
|
|
105
|
-
{ height: 'auto', bandwidth: null },
|
|
106
|
-
...filteredData.map((item) => ({
|
|
107
|
-
height: item.height,
|
|
108
|
-
bandwidth: item.bandwidth,
|
|
109
|
-
})),
|
|
110
|
-
];
|
|
111
|
-
|
|
112
|
-
resolutionCache[source] = newResolutions;
|
|
113
|
-
setResolutions(newResolutions);
|
|
114
|
-
} catch {}
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
fetchResolutions();
|
|
118
|
-
|
|
119
|
-
return () => {
|
|
120
|
-
cancelled = true;
|
|
121
|
-
};
|
|
122
|
-
}, [track]);
|
|
123
|
-
|
|
124
|
-
return resolutions;
|
|
125
|
-
};
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Platform } from 'react-native';
|
|
2
|
-
|
|
3
|
-
class PlatformSelector {
|
|
4
|
-
public isAndroid(): boolean {
|
|
5
|
-
return Platform.OS === 'android';
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
public isIOS(): boolean {
|
|
9
|
-
return Platform.OS === 'ios';
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export default new PlatformSelector();
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { StatusBar } 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
|
-
// Lock orientation immediately for fast response
|
|
9
|
-
Orientation.lockToPortrait();
|
|
10
|
-
|
|
11
|
-
// Update UI instantly without fade animation for smooth, fast rotation
|
|
12
|
-
StatusBar.setHidden(false, 'none');
|
|
13
|
-
|
|
14
|
-
if (PlatformSelector.isAndroid()) {
|
|
15
|
-
// Execute Android-specific updates synchronously for maximum speed
|
|
16
|
-
SystemNavigationBar.fullScreen(false);
|
|
17
|
-
SystemNavigationBar.navigationShow();
|
|
18
|
-
}
|
|
19
|
-
} catch (error) {
|
|
20
|
-
console.warn('Error locking to portrait:', error);
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export const lockToLandscape = () => {
|
|
25
|
-
try {
|
|
26
|
-
// Lock orientation immediately for fast response
|
|
27
|
-
Orientation.lockToLandscape();
|
|
28
|
-
|
|
29
|
-
// Update UI instantly without fade animation for smooth, fast rotation
|
|
30
|
-
StatusBar.setHidden(true, 'none');
|
|
31
|
-
|
|
32
|
-
if (PlatformSelector.isAndroid()) {
|
|
33
|
-
// Execute Android-specific updates synchronously for maximum speed
|
|
34
|
-
SystemNavigationBar.fullScreen(true);
|
|
35
|
-
SystemNavigationBar.stickyImmersive();
|
|
36
|
-
}
|
|
37
|
-
} catch (error) {
|
|
38
|
-
console.warn('Error locking to landscape:', error);
|
|
39
|
-
}
|
|
40
|
-
};
|
|
@@ -1,97 +0,0 @@
|
|
|
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 '../video/videoControl';
|
|
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();
|
|
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
|
-
};
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { useCallback } from 'react';
|
|
2
|
-
import { useVideoPlayerStore, mmkvStorage } from '../../store/videoPlayerStore';
|
|
3
|
-
import type { MediaTrack } from '../../store/videoPlayer.type';
|
|
4
|
-
|
|
5
|
-
export type WatchEventType =
|
|
6
|
-
| 'VIEW_COUNT'
|
|
7
|
-
| 'COMPLETED'
|
|
8
|
-
| 'PROGRESS'
|
|
9
|
-
| 'EPISODE_CHANGE'
|
|
10
|
-
| 'VIDEO_CLOSE';
|
|
11
|
-
|
|
12
|
-
export interface WatchProgressDetails {
|
|
13
|
-
contentId: string | null;
|
|
14
|
-
episodeId?: string | null;
|
|
15
|
-
contentType?: MediaTrack['type'];
|
|
16
|
-
currentTime: number;
|
|
17
|
-
totalWatchTime: number;
|
|
18
|
-
duration: number;
|
|
19
|
-
isVideoCompleted: boolean;
|
|
20
|
-
activeTrack: MediaTrack | null;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface ExtendedWatchProgress extends WatchProgressDetails {
|
|
24
|
-
event?: WatchEventType;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Internal function - not exported to avoid conflict with videoControl.countWatchTime
|
|
28
|
-
const countWatchTime = (
|
|
29
|
-
forceFlush = false
|
|
30
|
-
): {
|
|
31
|
-
totalWatchTime: number;
|
|
32
|
-
completed: boolean;
|
|
33
|
-
currentTime: number;
|
|
34
|
-
} => {
|
|
35
|
-
const { duration, setStartWatchTime, startWatchTime } =
|
|
36
|
-
useVideoPlayerStore.getState();
|
|
37
|
-
|
|
38
|
-
const currentTimeFromStorage = Number(
|
|
39
|
-
mmkvStorage.getItem('currentTime') || 0
|
|
40
|
-
);
|
|
41
|
-
const prevWatchTime = Number(mmkvStorage.getItem('current_watch_time') || 0);
|
|
42
|
-
|
|
43
|
-
const completed =
|
|
44
|
-
duration > 0 && Math.round(currentTimeFromStorage / duration) >= 0.95;
|
|
45
|
-
|
|
46
|
-
let totalTimeWatched = prevWatchTime;
|
|
47
|
-
|
|
48
|
-
if (startWatchTime) {
|
|
49
|
-
const elapsedTime = (Date.now() - startWatchTime) / 1000;
|
|
50
|
-
totalTimeWatched = prevWatchTime + elapsedTime;
|
|
51
|
-
mmkvStorage.setItem('current_watch_time', totalTimeWatched.toString());
|
|
52
|
-
|
|
53
|
-
setStartWatchTime(null);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (forceFlush) {
|
|
57
|
-
const latest = Number(mmkvStorage.getItem('current_watch_time') || '0');
|
|
58
|
-
return {
|
|
59
|
-
totalWatchTime: latest,
|
|
60
|
-
completed,
|
|
61
|
-
currentTime: currentTimeFromStorage,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return {
|
|
66
|
-
totalWatchTime: totalTimeWatched,
|
|
67
|
-
completed,
|
|
68
|
-
currentTime: currentTimeFromStorage,
|
|
69
|
-
};
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
export const useWatchReporter = ({
|
|
73
|
-
onWatchProgress,
|
|
74
|
-
}: {
|
|
75
|
-
onWatchProgress?: (progress: ExtendedWatchProgress) => void;
|
|
76
|
-
}) => {
|
|
77
|
-
const reportProgress = useCallback(
|
|
78
|
-
(event: WatchEventType = 'PROGRESS', forcedCurrentTime?: number) => {
|
|
79
|
-
const { activeTrack, duration } = useVideoPlayerStore.getState();
|
|
80
|
-
if (!activeTrack) return;
|
|
81
|
-
|
|
82
|
-
const { totalWatchTime, completed, currentTime } = countWatchTime(
|
|
83
|
-
event === 'VIDEO_CLOSE' ||
|
|
84
|
-
event === 'EPISODE_CHANGE' ||
|
|
85
|
-
event === 'COMPLETED'
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
const progress: WatchProgressDetails = {
|
|
89
|
-
contentId: activeTrack?.contentId || null,
|
|
90
|
-
episodeId: activeTrack?.episodeId || null,
|
|
91
|
-
contentType: activeTrack?.type,
|
|
92
|
-
currentTime: forcedCurrentTime ?? currentTime,
|
|
93
|
-
totalWatchTime,
|
|
94
|
-
duration: duration || 0,
|
|
95
|
-
isVideoCompleted: completed,
|
|
96
|
-
activeTrack,
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
onWatchProgress?.({ ...progress, event });
|
|
100
|
-
},
|
|
101
|
-
[onWatchProgress]
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
return { reportProgress };
|
|
105
|
-
};
|
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
import { mmkvStorage, useVideoPlayerStore } from '../../store/videoPlayerStore';
|
|
2
|
-
import type { MediaEpisode } from '../../store/videoPlayer.type';
|
|
3
|
-
|
|
4
|
-
export const handlePause = () => {
|
|
5
|
-
const { setIsPaused, startWatchTime, setStartWatchTime } =
|
|
6
|
-
useVideoPlayerStore.getState();
|
|
7
|
-
|
|
8
|
-
setIsPaused(true);
|
|
9
|
-
|
|
10
|
-
if (!startWatchTime) return;
|
|
11
|
-
|
|
12
|
-
const elapsedTime = (Date.now() - startWatchTime) / 1000;
|
|
13
|
-
const storedTime = Number(mmkvStorage.getItem('current_watch_time') ?? 0);
|
|
14
|
-
|
|
15
|
-
mmkvStorage.setItem(
|
|
16
|
-
'current_watch_time',
|
|
17
|
-
(storedTime + elapsedTime).toString()
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
setStartWatchTime(null);
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
interface HandleNextOptions {
|
|
24
|
-
onPressEpisode: ({ episode }: { episode: MediaEpisode }) => Promise<boolean>;
|
|
25
|
-
autoNext?: boolean;
|
|
26
|
-
}
|
|
27
|
-
export const handleNext = async ({
|
|
28
|
-
onPressEpisode,
|
|
29
|
-
autoNext,
|
|
30
|
-
}: HandleNextOptions): Promise<number | null> => {
|
|
31
|
-
const {
|
|
32
|
-
playList: playlist,
|
|
33
|
-
currentTrackIndex,
|
|
34
|
-
setCurrentTrackIndex,
|
|
35
|
-
setActiveTrack,
|
|
36
|
-
setCurrentTime,
|
|
37
|
-
setIsPaused,
|
|
38
|
-
setActiveSubtitle,
|
|
39
|
-
setPlayBackRate,
|
|
40
|
-
contentSeasons,
|
|
41
|
-
setActiveSeason,
|
|
42
|
-
setControlsVisible,
|
|
43
|
-
setSelectedSubtitleTrack,
|
|
44
|
-
setSelectedVideoTrack,
|
|
45
|
-
setMaxBitRate,
|
|
46
|
-
setDuration,
|
|
47
|
-
} = useVideoPlayerStore.getState();
|
|
48
|
-
|
|
49
|
-
if (!playlist?.length) return null;
|
|
50
|
-
|
|
51
|
-
if (!autoNext) {
|
|
52
|
-
setIsPaused(true);
|
|
53
|
-
setControlsVisible(true);
|
|
54
|
-
return currentTrackIndex;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
let nextIndex = currentTrackIndex + 1;
|
|
58
|
-
|
|
59
|
-
if (nextIndex >= playlist.length) {
|
|
60
|
-
setIsPaused(true);
|
|
61
|
-
setControlsVisible(true);
|
|
62
|
-
return currentTrackIndex;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const nextTrack = playlist[nextIndex];
|
|
66
|
-
if (!nextTrack) return currentTrackIndex;
|
|
67
|
-
|
|
68
|
-
let isSuccess = true;
|
|
69
|
-
if (onPressEpisode) {
|
|
70
|
-
isSuccess = await onPressEpisode({
|
|
71
|
-
episode: {
|
|
72
|
-
id: nextTrack.episodeId || '',
|
|
73
|
-
title: nextTrack.episodeName ?? '',
|
|
74
|
-
contentId: nextTrack.contentId || '',
|
|
75
|
-
sourceLink: nextTrack.sourceLink,
|
|
76
|
-
thumbnail: nextTrack.thumbnail,
|
|
77
|
-
nextEpisodeAt: nextTrack.nextEpisodeAt,
|
|
78
|
-
publishDate: nextTrack.publishDate,
|
|
79
|
-
skipIntro: nextTrack.skipIntro,
|
|
80
|
-
description: nextTrack?.description || '',
|
|
81
|
-
duration: nextTrack?.duration || 0,
|
|
82
|
-
sourceType: nextTrack?.sourceType || 'HLS',
|
|
83
|
-
episodeNumber: nextTrack?.episodeNumber || 0,
|
|
84
|
-
subtitles: nextTrack?.subtitles ?? [],
|
|
85
|
-
},
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (!isSuccess) {
|
|
90
|
-
setIsPaused(true);
|
|
91
|
-
setControlsVisible(true);
|
|
92
|
-
return currentTrackIndex;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
setIsPaused(false);
|
|
96
|
-
setCurrentTime(0);
|
|
97
|
-
setDuration(0);
|
|
98
|
-
setCurrentTrackIndex(nextIndex);
|
|
99
|
-
setActiveTrack(nextTrack);
|
|
100
|
-
setActiveSubtitle(null);
|
|
101
|
-
setSelectedVideoTrack(null);
|
|
102
|
-
setMaxBitRate(null);
|
|
103
|
-
setPlayBackRate(1, 'Normal');
|
|
104
|
-
setSelectedSubtitleTrack(null);
|
|
105
|
-
|
|
106
|
-
if (nextTrack.seasonId) {
|
|
107
|
-
const activeSeason = contentSeasons?.find(
|
|
108
|
-
(s) => s.id === nextTrack.seasonId
|
|
109
|
-
);
|
|
110
|
-
if (activeSeason) {
|
|
111
|
-
setActiveSeason(activeSeason);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return nextIndex;
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
export const viewCount = (): boolean => {
|
|
119
|
-
const { isViewCounted, setIsViewCounted, activeTrack } =
|
|
120
|
-
useVideoPlayerStore.getState();
|
|
121
|
-
if (!isViewCounted) {
|
|
122
|
-
if (activeTrack?.id) {
|
|
123
|
-
setIsViewCounted(true);
|
|
124
|
-
return true;
|
|
125
|
-
}
|
|
126
|
-
} else {
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
return false;
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
export const countWatchTime = (): {
|
|
133
|
-
totalWatchTime: number;
|
|
134
|
-
completed: boolean;
|
|
135
|
-
currentTime: number;
|
|
136
|
-
} => {
|
|
137
|
-
const { activeTrack, duration, setStartWatchTime, startWatchTime } =
|
|
138
|
-
useVideoPlayerStore.getState();
|
|
139
|
-
|
|
140
|
-
const currentTimeFromStorage = Number(
|
|
141
|
-
mmkvStorage.getItem('currentTime') || 0
|
|
142
|
-
);
|
|
143
|
-
const prevWatchTime = Number(mmkvStorage.getItem('current_watch_time') || 0);
|
|
144
|
-
|
|
145
|
-
const completed =
|
|
146
|
-
duration > 0 && Math.round(currentTimeFromStorage / duration) >= 0.95;
|
|
147
|
-
|
|
148
|
-
if (!startWatchTime) {
|
|
149
|
-
return {
|
|
150
|
-
totalWatchTime: prevWatchTime,
|
|
151
|
-
completed,
|
|
152
|
-
currentTime: currentTimeFromStorage,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const elapsedTime = (Date.now() - startWatchTime) / 1000;
|
|
157
|
-
|
|
158
|
-
const totalTimeWatched = prevWatchTime + elapsedTime;
|
|
159
|
-
mmkvStorage.setItem('current_watch_time', totalTimeWatched.toString());
|
|
160
|
-
|
|
161
|
-
setStartWatchTime(null);
|
|
162
|
-
|
|
163
|
-
if (totalTimeWatched < 30) {
|
|
164
|
-
return {
|
|
165
|
-
totalWatchTime: 0,
|
|
166
|
-
completed: false,
|
|
167
|
-
currentTime: currentTimeFromStorage,
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (activeTrack?.id) {
|
|
172
|
-
return {
|
|
173
|
-
totalWatchTime: totalTimeWatched,
|
|
174
|
-
completed,
|
|
175
|
-
currentTime: currentTimeFromStorage,
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
mmkvStorage.setItem('current_watch_time', '0');
|
|
180
|
-
return {
|
|
181
|
-
totalWatchTime: 0,
|
|
182
|
-
completed: false,
|
|
183
|
-
currentTime: 0,
|
|
184
|
-
};
|
|
185
|
-
};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { createRef } from 'react';
|
|
2
|
-
import type { VideoRef } from 'react-native-video';
|
|
3
|
-
import type { AdsPlayerRef } from '../../../AdsPlayer/AdsPlayer';
|
|
4
|
-
|
|
5
|
-
export const hideControlsStyles = {
|
|
6
|
-
hideForward: true,
|
|
7
|
-
hideDuration: true,
|
|
8
|
-
hideFullscreen: true,
|
|
9
|
-
hidePlayPause: true,
|
|
10
|
-
hideNavigationBarOnFullScreenMode: true,
|
|
11
|
-
hideNotificationBarOnFullScreenMode: true,
|
|
12
|
-
hideNext: true,
|
|
13
|
-
hidePosition: true,
|
|
14
|
-
hidePrevious: true,
|
|
15
|
-
hideRewind: true,
|
|
16
|
-
hideSeekBar: true,
|
|
17
|
-
hideSettingButton: true,
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export const videoRef = createRef<VideoRef>();
|
|
21
|
-
export const adVideoRef = createRef<AdsPlayerRef>();
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { videoRef } from './videoRef';
|
|
2
|
-
import { useVideoPlayerStore } from '../../store/videoPlayerStore';
|
|
3
|
-
import { useAdsPlayerStore } from '../../../AdsPlayer/store/adsPlayerStore';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Reusable function to resume video from ad
|
|
7
|
-
* Handles seeking to resume time and unpausing the video
|
|
8
|
-
*/
|
|
9
|
-
export const resumeVideoFromAd = (resumeTimeValue: number | null) => {
|
|
10
|
-
const { setIsPaused } = useVideoPlayerStore.getState();
|
|
11
|
-
const { setResumeTime } = useAdsPlayerStore.getState();
|
|
12
|
-
|
|
13
|
-
if (videoRef.current && resumeTimeValue !== null && resumeTimeValue > 0) {
|
|
14
|
-
setTimeout(() => {
|
|
15
|
-
videoRef.current?.seek(resumeTimeValue);
|
|
16
|
-
setIsPaused(false);
|
|
17
|
-
setResumeTime(null);
|
|
18
|
-
}, 100);
|
|
19
|
-
} else {
|
|
20
|
-
setIsPaused(false);
|
|
21
|
-
setResumeTime(null);
|
|
22
|
-
}
|
|
23
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { MediaTrack } from '../../store/videoPlayer.type';
|
|
2
|
-
import type { ReactVideoSource } from 'react-native-video';
|
|
3
|
-
|
|
4
|
-
interface UrlHandlerProps {
|
|
5
|
-
playList: MediaTrack[];
|
|
6
|
-
currentTrackIndex: number;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const getVideoSource = ({
|
|
10
|
-
playList,
|
|
11
|
-
currentTrackIndex,
|
|
12
|
-
}: UrlHandlerProps): ReactVideoSource => {
|
|
13
|
-
const track = playList?.[currentTrackIndex];
|
|
14
|
-
if (!track) return { uri: '' };
|
|
15
|
-
|
|
16
|
-
const uri = track.isTrailer ? track.trailerSource : track.sourceLink;
|
|
17
|
-
return { uri: encodeURI(uri?.trim() || '') ?? '' };
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export const getEpisodeIndex = (
|
|
21
|
-
playList: MediaTrack[],
|
|
22
|
-
episodeId: string
|
|
23
|
-
): number => playList.findIndex((item) => item.episodeId === episodeId);
|