@zezosoft/zezo-ott-react-native-video-player
Version:
React Native OTT Video Player for Android & iOS. Supports playlists, seasons, auto-next playback, subtitles, theming, analytics, fullscreen mode, and custom controls. π Powered by ZezoSoft.
193 lines (164 loc) β’ 4.78 kB
text/typescript
import { mmkvStorage, useVideoPlayerStore } from '../store/videoPlayerStore';
import type { MediaEpisode } from '../store/videoPlayer.type';
export const handlePause = () => {
const { setIsPaused, startWatchTime, setStartWatchTime } =
useVideoPlayerStore.getState();
setIsPaused(true);
if (!startWatchTime) return;
const elapsedTime = (Date.now() - startWatchTime) / 1000;
const storedTime = Number(mmkvStorage.getItem('current_watch_time') ?? 0);
mmkvStorage.setItem(
'current_watch_time',
(storedTime + elapsedTime).toString()
);
setStartWatchTime(null);
};
interface HandleNextOptions {
onPressEpisode: ({ episode }: { episode: MediaEpisode }) => Promise<boolean>;
autoNext?: boolean;
}
export const handleNext = async ({
onPressEpisode,
autoNext,
}: HandleNextOptions): Promise<number | null> => {
const {
playList: playlist,
currentTrackIndex,
setCurrentTrackIndex,
setActiveTrack,
setCurrentTime,
setIsPaused,
setActiveSubtitle,
setPlayBackRate,
contentSeasons,
setActiveSeason,
setControlsVisible,
setSelectedSubtitleTrack,
setSelectedVideoTrack,
setMaxBitRate,
setDuration,
} = useVideoPlayerStore.getState();
if (!playlist?.length) return null;
// π Check autoNext
if (!autoNext) {
// Just pause at the end, donβt go next
setIsPaused(true);
setControlsVisible(true);
return currentTrackIndex;
}
let nextIndex = currentTrackIndex + 1;
// π No next episode? stop here
if (nextIndex >= playlist.length) {
setIsPaused(true);
setControlsVisible(true);
return currentTrackIndex;
}
const nextTrack = playlist[nextIndex];
if (!nextTrack) return currentTrackIndex;
// π Trigger hook
let isSuccess = true;
if (onPressEpisode) {
isSuccess = await onPressEpisode({
episode: {
id: nextTrack.episodeId || '',
title: nextTrack.episodeName ?? '',
contentId: nextTrack.contentId || '',
sourceLink: nextTrack.sourceLink,
thumbnail: nextTrack.thumbnail,
nextEpisodeAt: nextTrack.nextEpisodeAt,
publishDate: nextTrack.publishDate,
skipIntro: nextTrack.skipIntro,
description: nextTrack?.description || '',
duration: nextTrack?.duration || 0,
sourceType: nextTrack?.sourceType || 'HLS',
episodeNumber: nextTrack?.episodeNumber || 0,
subtitles: nextTrack?.subtitles ?? [],
},
});
}
if (!isSuccess) {
setIsPaused(true);
setControlsVisible(true);
return currentTrackIndex;
}
// π Play next episode
setIsPaused(false);
setCurrentTime(0);
setDuration(0);
setCurrentTrackIndex(nextIndex);
setActiveTrack(nextTrack);
setActiveSubtitle(null);
setSelectedVideoTrack(null);
setMaxBitRate(null);
setPlayBackRate(1, 'Normal');
setSelectedSubtitleTrack(null);
if (nextTrack.seasonId) {
const activeSeason = contentSeasons?.find(
(s) => s.id === nextTrack.seasonId
);
if (activeSeason) {
setActiveSeason(activeSeason);
}
}
return nextIndex;
};
export const viewCount = (): boolean => {
const { isViewCounted, setIsViewCounted, activeTrack } =
useVideoPlayerStore.getState();
if (!isViewCounted) {
if (activeTrack?.id) {
setIsViewCounted(true);
return true;
}
} else {
return false;
}
return false;
};
export const countWatchTime = (): {
totalWatchTime: number;
completed: boolean;
currentTime: number;
} => {
const { activeTrack, duration, setStartWatchTime, startWatchTime } =
useVideoPlayerStore.getState();
const currentTimeFromStorage = Number(
mmkvStorage.getItem('currentTime') || 0
);
const prevWatchTime = Number(mmkvStorage.getItem('current_watch_time') || 0);
// β
Completed check (>= 95%)
const completed =
duration > 0 && Math.round(currentTimeFromStorage / duration) >= 0.95;
// β± If no start time (pause/close before resuming)
if (!startWatchTime) {
return {
totalWatchTime: prevWatchTime,
completed,
currentTime: currentTimeFromStorage,
};
}
const elapsedTime = (Date.now() - startWatchTime) / 1000;
const totalTimeWatched = prevWatchTime + elapsedTime;
mmkvStorage.setItem('current_watch_time', totalTimeWatched.toString());
setStartWatchTime(null);
if (totalTimeWatched < 30) {
return {
totalWatchTime: 0,
completed: false,
currentTime: currentTimeFromStorage,
};
}
if (activeTrack?.id) {
return {
totalWatchTime: totalTimeWatched,
completed,
currentTime: currentTimeFromStorage,
};
}
mmkvStorage.setItem('current_watch_time', '0');
return {
totalWatchTime: 0,
completed: false,
currentTime: 0,
};
};