@zezosoft/zezo-ott-react-native-video-player
Version:
Production-ready React Native OTT video player library for Android & iOS. Features: playlists, seasons, auto-next playback, subtitles (SRT/VTT), custom theming, analytics tracking, fullscreen mode, gesture controls, ads player (pre-roll/mid-roll/post-roll
165 lines (156 loc) • 5.99 kB
JavaScript
;
import { useEffect, useRef } from 'react';
import { useVideoPlayerStore } from "../../VideoPlayer/store/videoPlayerStore.js";
import { useAdsPlayerStore } from "../store/adsPlayerStore.js";
import { useAdInitialization } from "./useAdInitialization.js";
export const useAdsManager = (ads = [], onAdTracking) => {
const {
currentTime,
activeTrack
} = useVideoPlayerStore();
const {
currentAd,
isAdPlaying,
pendingMidRollAds,
addPendingMidRollAd,
clearPendingMidRollAds
} = useAdsPlayerStore();
const {
startAd
} = useAdInitialization({
onAdTracking
});
const adsInitializedRef = useRef(false);
const previousAdsRef = useRef('');
const playedMidRollAdsRef = useRef(new Set());
const playedPreRollAdsRef = useRef(new Set());
const playedPostRollAdsRef = useRef(new Set());
const previousTimeRef = useRef(0);
const isResettingRef = useRef(false);
useEffect(() => {
// Prevent infinite loops
if (isResettingRef.current) {
return;
}
const adsKey = JSON.stringify(ads?.map(ad => ad.id) || []);
const hadAds = previousAdsRef.current !== '';
const hasAds = ads && ads.length > 0;
if (hasAds && adsKey !== previousAdsRef.current) {
previousAdsRef.current = adsKey;
// Don't initialize ads if it's a trailer
if (activeTrack?.isTrailer) {
return;
}
const preRollAds = ads.filter(ad => ad.position === 'pre');
const midRollAds = ads.filter(ad => ad.position === 'mid');
if (preRollAds.length > 0 && preRollAds[0] && !currentAd) {
try {
playedPreRollAdsRef.current.add(preRollAds[0].id);
startAd(preRollAds[0], {
trackImpression: false
}); // Impression tracked in AdsPlayer
} catch (error) {
console.error('Error starting pre-roll ad:', error);
}
}
if (!adsInitializedRef.current) {
midRollAds.forEach(ad => {
if (ad.time !== undefined) {
addPendingMidRollAd(ad);
}
});
adsInitializedRef.current = true;
}
} else if (!hasAds && hadAds) {
// Only reset when ads change from non-empty to empty
isResettingRef.current = true;
// Clear all ad state when ads array becomes empty
adsInitializedRef.current = false;
previousAdsRef.current = '';
clearPendingMidRollAds();
// Reset played ads tracking
playedMidRollAdsRef.current.clear();
playedPreRollAdsRef.current.clear();
playedPostRollAdsRef.current.clear();
// If an ad is currently playing, stop it
const {
isAdPlaying: currentIsAdPlaying,
currentAd: currentAdState,
resetAdsStore
} = useAdsPlayerStore.getState();
if (currentIsAdPlaying || currentAdState) {
resetAdsStore();
}
// Reset the flag after a short delay to allow state to settle
setTimeout(() => {
isResettingRef.current = false;
}, 0);
} else if (!hasAds) {
// If ads are empty and were already empty, just update the ref
previousAdsRef.current = '';
}
}, [ads, currentAd, addPendingMidRollAd, clearPendingMidRollAds, startAd, activeTrack?.isTrailer]);
useEffect(() => {
// Don't trigger mid-roll ads if it's a trailer
if (activeTrack?.isTrailer) {
return;
}
if (!isAdPlaying && pendingMidRollAds.length > 0 && currentTime > 0 && !currentAd) {
const previousTime = previousTimeRef.current;
const timeDifference = Math.abs(currentTime - previousTime);
const isSeeking = timeDifference > 2; // If time jumps more than 2 seconds, it's a seek
// Check for ads that should be triggered
let nextMidRollAd = pendingMidRollAds.find(ad => ad.time && !playedMidRollAdsRef.current.has(ad.id) && currentTime >= ad.time - 1 && currentTime <= ad.time + 1);
// If seeking, check if we skipped past any ads
if (!nextMidRollAd && isSeeking && previousTime < currentTime) {
const skippedAd = pendingMidRollAds.find(ad => ad.time && !playedMidRollAdsRef.current.has(ad.id) && ad.time > previousTime && ad.time <= currentTime);
if (skippedAd) {
nextMidRollAd = skippedAd;
}
}
if (nextMidRollAd) {
try {
playedMidRollAdsRef.current.add(nextMidRollAd.id);
const timeBeforeAd = isSeeking ? nextMidRollAd.time || currentTime : currentTime;
startAd(nextMidRollAd, {
resumeTime: timeBeforeAd,
trackImpression: false // Impression tracked in AdsPlayer
});
const remainingAds = pendingMidRollAds.filter(ad => ad.id !== nextMidRollAd.id);
clearPendingMidRollAds();
remainingAds.forEach(ad => addPendingMidRollAd(ad));
} catch (error) {
console.error('Error starting mid-roll ad:', error);
}
}
// Update previous time
previousTimeRef.current = currentTime;
} else if (currentTime > 0) {
// Update previous time even if conditions aren't met
previousTimeRef.current = currentTime;
}
}, [currentTime, isAdPlaying, pendingMidRollAds, currentAd, ads, addPendingMidRollAd, clearPendingMidRollAds, startAd, activeTrack?.isTrailer]);
return {
checkPostRollAds: () => {
try {
// Don't show post-roll ads if it's a trailer
if (activeTrack?.isTrailer) {
return false;
}
const postRollAds = ads.filter(ad => ad.position === 'post');
if (postRollAds.length > 0 && postRollAds[0]) {
playedPostRollAdsRef.current.add(postRollAds[0].id);
startAd(postRollAds[0], {
trackImpression: false
}); // Impression tracked in AdsPlayer
return true;
}
return false;
} catch (error) {
console.error('Error checking post-roll ads:', error);
return false;
}
}
};
};
//# sourceMappingURL=useAdsManager.js.map