unified-video-framework
Version:
Cross-platform video player framework supporting iOS, Android, Web, Smart TVs (Samsung/LG), Roku, and more
757 lines (743 loc) โข 32.2 kB
JavaScript
export class GoogleAdsManager {
constructor(video, adContainer, config) {
this.adsManager = null;
this.adsLoader = null;
this.adDisplayContainer = null;
this.isAdPlaying = false;
this.isMuted = true;
this.unmuteButton = null;
this.adProgressBar = null;
this.adProgressInterval = null;
this.lastAdTime = 0;
this.periodicAdCheckInterval = null;
this.pendingAdRequest = false;
this.timeupdateHandler = null;
this.volumechangeHandler = null;
this.triggeredMidrollTimes = new Set();
this.video = video;
this.adContainer = adContainer;
this.config = config;
this.focusHandler = () => {
if (this.isAdPlaying) {
console.log('Window focused - resuming ad playback');
this.resumeAdPlayback();
setTimeout(() => this.resumeAdPlayback(), 200);
}
};
this.visibilityHandler = () => {
if (!document.hidden && this.isAdPlaying) {
console.log('Tab became visible - resuming ad playback');
this.resumeAdPlayback();
}
};
this.setupFocusHandler();
}
setupFocusHandler() {
window.addEventListener('focus', this.focusHandler);
document.addEventListener('visibilitychange', this.visibilityHandler);
}
async initialize() {
try {
await this.loadIMASDK();
this.setupAdsLoader();
}
catch (error) {
console.error('Failed to initialize Google Ads:', error);
this.config.onAdError?.(error);
}
}
loadIMASDK() {
return new Promise((resolve, reject) => {
if (window.google?.ima) {
resolve();
return;
}
const script = document.createElement('script');
script.src = 'https://imasdk.googleapis.com/js/sdkloader/ima3.js';
script.async = true;
script.onload = () => resolve();
script.onerror = () => reject(new Error('Failed to load Google IMA SDK'));
document.head.appendChild(script);
});
}
setupAdsLoader() {
const google = window.google;
const rect = this.adContainer.getBoundingClientRect();
if (rect.width === 0 || rect.height === 0) {
console.warn('โ ๏ธ Ad container has zero dimensions:', rect.width, 'x', rect.height);
console.warn('IMA UI may not render correctly. Ensure container is visible before initialization.');
}
this.adDisplayContainer = new google.ima.AdDisplayContainer(this.adContainer, this.video);
this.adsLoader = new google.ima.AdsLoader(this.adDisplayContainer);
this.adsLoader.addEventListener(google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, (event) => this.onAdsManagerLoaded(event), false);
this.adsLoader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, (event) => this.onAdError(event), false);
this.video.addEventListener('ended', () => {
if (!this.isAdPlaying) {
this.adsLoader?.contentComplete();
}
});
this.setupMidrollTriggers();
}
setupMidrollTriggers() {
if (!this.config.midrollTimes || this.config.midrollTimes.length === 0) {
return;
}
console.log(`๐บ Setting up mid-roll triggers for times: ${this.config.midrollTimes.join(', ')}s`);
this.timeupdateHandler = () => {
if (this.isAdPlaying || this.pendingAdRequest)
return;
const currentTime = this.video.currentTime;
for (const triggerTime of this.config.midrollTimes) {
if (!this.triggeredMidrollTimes.has(triggerTime) && currentTime >= triggerTime && currentTime < triggerTime + 2) {
console.log(`โฐ Mid-roll triggered at ${currentTime.toFixed(1)}s (scheduled: ${triggerTime}s)`);
this.triggeredMidrollTimes.add(triggerTime);
this.pendingAdRequest = true;
this.lastAdTime = currentTime;
if (this.config.pauseStreamDuringAd && !this.video.paused) {
this.video.pause();
}
this.requestAds();
break;
}
}
};
this.video.addEventListener('timeupdate', this.timeupdateHandler);
}
setupPeriodicAds() {
if (this.config.liveAdBreakMode !== 'periodic' || !this.config.periodicAdInterval) {
return;
}
if (this.periodicAdCheckInterval !== null) {
return;
}
console.log(`๐บ Setting up periodic ads: interval=${this.config.periodicAdInterval}s`);
this.periodicAdCheckInterval = setInterval(() => {
if (this.isAdPlaying || this.pendingAdRequest) {
return;
}
const currentTime = this.video.currentTime;
const interval = this.config.periodicAdInterval || 30;
if (currentTime - this.lastAdTime >= interval && currentTime > 0) {
console.log(`โฐ Periodic ad triggered at ${currentTime.toFixed(1)}s (interval: ${interval}s)`);
this.triggerPeriodicAd();
}
}, 1000);
console.log('โ
Periodic ad scheduling enabled');
}
triggerPeriodicAd() {
if (this.pendingAdRequest || this.isAdPlaying) {
return;
}
this.pendingAdRequest = true;
this.lastAdTime = this.video.currentTime;
console.log('๐ฌ Triggering periodic ad break...');
if (this.config.pauseStreamDuringAd && !this.video.paused) {
this.video.pause();
}
this.requestAds();
}
requestAds() {
const google = window.google;
if (!this.adsLoader) {
console.warn('requestAds() called but adsLoader is null (destroyed?)');
this.pendingAdRequest = false;
return;
}
try {
const adsRequest = new google.ima.AdsRequest();
adsRequest.adTagUrl = this.config.adTagUrl;
adsRequest.linearAdSlotWidth = this.video.clientWidth;
adsRequest.linearAdSlotHeight = this.video.clientHeight;
adsRequest.nonLinearAdSlotWidth = this.video.clientWidth;
adsRequest.nonLinearAdSlotHeight = Math.floor(this.video.clientHeight / 3);
if (this.config.companionAdSlots && this.config.companionAdSlots.length > 0) {
const companionAdSlots = this.config.companionAdSlots.map(slot => {
const container = document.getElementById(slot.containerId);
if (!container)
return null;
return new google.ima.CompanionAdSelectionSettings();
}).filter(Boolean);
if (companionAdSlots.length > 0) {
adsRequest.companionSlots = companionAdSlots;
}
}
adsRequest.setAdWillAutoPlay(true);
adsRequest.setAdWillPlayMuted(true);
this.adsLoader.requestAds(adsRequest);
}
catch (error) {
console.error('Error requesting ads:', error);
this.pendingAdRequest = false;
this.config.onAdError?.(error);
}
}
initAdDisplayContainer() {
try {
this.adDisplayContainer?.initialize();
}
catch (error) {
console.warn('Ad display container already initialized');
}
}
onAdsManagerLoaded(event) {
const google = window.google;
const adsRenderingSettings = new google.ima.AdsRenderingSettings();
adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true;
adsRenderingSettings.enablePreloading = true;
adsRenderingSettings.mute = this.video.muted;
adsRenderingSettings.useStyledLinearAds = true;
console.log('๐ IMA SDK Info:', {
version: google.ima.VERSION || 'unknown',
hasUiElements: !!google.ima.UiElements,
availableUiElements: google.ima.UiElements ? Object.keys(google.ima.UiElements) : []
});
console.log('โ
IMA AdsRenderingSettings configured:', {
uiElements: adsRenderingSettings.uiElements || 'not set (using IMA defaults)',
enablePreloading: adsRenderingSettings.enablePreloading,
useStyledLinearAds: adsRenderingSettings.useStyledLinearAds,
mute: adsRenderingSettings.mute,
restoreCustomPlaybackStateOnAdBreakComplete: adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete
});
this.adsManager = event.getAdsManager(this.video, adsRenderingSettings);
try {
const cuePoints = this.adsManager.getCuePoints();
if (cuePoints && cuePoints.length > 0) {
const allCuePoints = cuePoints.map((time) => {
if (time === 0)
return 0;
if (time === -1)
return -1;
return time;
});
console.log('๐ Ad cue points detected (pre/mid/post):', allCuePoints);
if (this.config.onAdCuePoints) {
this.config.onAdCuePoints(allCuePoints);
}
}
}
catch (error) {
console.warn('Could not extract ad cue points:', error);
}
this.setupAdsManagerListeners();
try {
this.adsManager.init(this.video.clientWidth, this.video.clientHeight, google.ima.ViewMode.NORMAL);
this.adsManager.start();
this.setupPeriodicAds();
}
catch (error) {
console.error('Error starting ads:', error);
this.pendingAdRequest = false;
this.isAdPlaying = false;
this.video.play().catch(() => { });
}
}
setupAdsManagerListeners() {
const google = window.google;
this.adsManager.addEventListener(google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, () => {
console.log('Ad: Content paused');
this.isAdPlaying = true;
this.pendingAdRequest = false;
this.video.pause();
if (this.adContainer) {
this.adContainer.style.visibility = 'visible';
this.adContainer.style.opacity = '1';
this.adContainer.style.pointerEvents = 'auto';
this.adContainer.style.zIndex = '2147483647';
console.log('โ
Ad container visibility enforced');
setTimeout(() => {
const imaElements = this.adContainer.querySelectorAll('[class*="ima-"], [id*="ima-"]');
const allDivs = this.adContainer.querySelectorAll('div');
let advertisementLabelFound = false;
allDivs.forEach((div) => {
const text = div.textContent?.trim() || '';
if (text === 'Advertisement' || text === 'Ad' || text.startsWith('Advertisement')) {
console.log('๐ข Found Advertisement label:', {
text,
visible: div.offsetWidth > 0 && div.offsetHeight > 0,
display: getComputedStyle(div).display,
position: getComputedStyle(div).position
});
div.style.display = 'block';
div.style.visibility = 'visible';
div.style.opacity = '1';
div.style.position = 'absolute';
div.style.top = '10px';
div.style.left = '10px';
div.style.zIndex = '999999';
div.style.color = 'white';
div.style.background = 'rgba(0, 0, 0, 0.6)';
div.style.padding = '4px 8px';
div.style.fontSize = '12px';
div.style.fontFamily = 'Arial, sans-serif';
advertisementLabelFound = true;
}
});
console.log('๐ IMA UI Diagnostic:', {
totalDivs: allDivs.length,
imaElements: imaElements.length,
containerChildren: this.adContainer.children.length,
adContainerSize: `${this.adContainer.offsetWidth}x${this.adContainer.offsetHeight}`,
advertisementLabelFound
});
if (!advertisementLabelFound) {
console.warn('โ ๏ธ Advertisement label not found in DOM - creating custom overlay');
const adLabel = document.createElement('div');
adLabel.id = 'uvf-custom-ad-label';
adLabel.textContent = 'Advertisement';
adLabel.style.cssText = `
position: absolute !important;
top: 10px !important;
left: 10px !important;
z-index: 2147483647 !important;
color: white !important;
background: rgba(0, 0, 0, 0.7) !important;
padding: 4px 10px !important;
font-size: 12px !important;
font-family: Arial, Roboto, sans-serif !important;
font-weight: 500 !important;
letter-spacing: 0.5px !important;
border-radius: 2px !important;
pointer-events: none !important;
user-select: none !important;
`;
this.adContainer.appendChild(adLabel);
console.log('โ
Custom Advertisement label created and displayed');
}
if (imaElements.length === 0) {
console.warn('โ ๏ธ No IMA UI elements found! UI may not be rendering.');
console.warn('This could mean: 1) Ad creative has no UI, 2) uiElements config issue, 3) IMA SDK version issue');
}
else {
console.log('โ
IMA UI elements detected:', Array.from(imaElements).map(el => el.className || el.id));
}
}, 500);
}
const preventPlayDuringAd = (e) => {
if (this.isAdPlaying) {
e.preventDefault();
this.video.pause();
console.warn('Blocked video play attempt during ad');
}
};
this.video.addEventListener('play', preventPlayDuringAd);
this.video.__adPlayBlocker = preventPlayDuringAd;
this.volumechangeHandler = () => {
if (!this.adsManager || !this.isAdPlaying)
return;
const vol = this.video.muted ? 0 : this.video.volume;
this.adsManager.setVolume(vol);
const wasMuted = this.isMuted;
this.isMuted = this.video.muted || this.video.volume === 0;
if (wasMuted !== this.isMuted) {
this.updateMuteButtonUI();
}
};
this.video.addEventListener('volumechange', this.volumechangeHandler);
this.config.onAdStart?.();
});
this.adsManager.addEventListener(google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, () => {
console.log('Ad: Content resume');
this.isAdPlaying = false;
const customLabel = this.adContainer.querySelector('#uvf-custom-ad-label');
if (customLabel) {
customLabel.remove();
}
this.hideAdProgressBar();
const preventPlayDuringAd = this.video.__adPlayBlocker;
if (preventPlayDuringAd) {
this.video.removeEventListener('play', preventPlayDuringAd);
delete this.video.__adPlayBlocker;
}
if (this.volumechangeHandler) {
this.video.removeEventListener('volumechange', this.volumechangeHandler);
this.volumechangeHandler = null;
}
this.config.onAdEnd?.();
this.video.play().catch(() => { });
});
this.adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, (event) => {
const ad = event.getAd();
console.log('Ad started:', {
type: ad.isLinear() ? 'Linear (video)' : 'Non-linear (overlay)',
duration: ad.getDuration(),
skippable: ad.getSkipTimeOffset() !== -1,
title: ad.getTitle(),
});
this.isMuted = this.video.muted;
console.log(`Ad started - video.muted=${this.video.muted}, isMuted=${this.isMuted}`);
if (this.isMuted) {
this.showUnmuteButton();
}
this.showAdProgressBar(ad.getDuration());
});
this.adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, () => {
console.log('Ad completed');
});
this.adsManager.addEventListener(google.ima.AdEvent.Type.ALL_ADS_COMPLETED, () => {
console.log('All ads completed');
this.config.onAllAdsComplete?.();
});
this.adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, (event) => this.onAdError(event));
this.adsManager.addEventListener(google.ima.AdEvent.Type.SKIPPED, () => {
console.log('Ad skipped by user');
});
this.adsManager.addEventListener(google.ima.AdEvent.Type.PAUSED, () => {
console.log('Ad paused (likely from click-through)');
});
this.adsManager.addEventListener(google.ima.AdEvent.Type.PLAYING, () => {
console.log('Ad resumed playing');
});
}
onAdError(event) {
const error = event.getError?.();
console.error('Ad error:', error?.getMessage?.() || error);
this.config.onAdError?.(error);
if (this.adsManager) {
this.adsManager.destroy();
this.adsManager = null;
}
this.isAdPlaying = false;
this.pendingAdRequest = false;
const blocker = this.video.__adPlayBlocker;
if (blocker) {
this.video.removeEventListener('play', blocker);
delete this.video.__adPlayBlocker;
}
this.hideUnmuteButton();
this.video.play().catch(() => { });
}
pause() {
if (this.adsManager && this.isAdPlaying) {
this.adsManager.pause();
}
}
resume() {
if (this.adsManager && this.isAdPlaying) {
this.adsManager.resume();
}
}
skip() {
if (this.adsManager) {
this.adsManager.skip();
}
}
resize(width, height, viewMode) {
const google = window.google;
if (this.adsManager && google && google.ima) {
const mode = viewMode || google.ima.ViewMode.NORMAL;
console.log(`๐ Resizing ads: ${width}x${height}, ViewMode: ${mode === google.ima.ViewMode.FULLSCREEN ? 'FULLSCREEN' : 'NORMAL'}`);
if (this.adContainer && mode === google.ima.ViewMode.FULLSCREEN) {
this.adContainer.style.position = 'fixed';
this.adContainer.style.top = '0';
this.adContainer.style.left = '0';
this.adContainer.style.width = `${width}px`;
this.adContainer.style.height = `${height}px`;
this.adContainer.style.zIndex = '2147483647';
console.log('โ
Ad container forced to fullscreen dimensions');
}
this.adsManager.resize(width, height, mode);
}
}
setVolume(volume) {
if (this.adsManager) {
this.adsManager.setVolume(volume);
}
}
isPlayingAd() {
return this.isAdPlaying;
}
showUnmuteButton() {
if (this.unmuteButton) {
this.unmuteButton.remove();
}
this.unmuteButton = document.createElement('button');
this.unmuteButton.id = 'ad-unmute-btn';
this.unmuteButton.className = 'uvf-unmute-btn';
this.unmuteButton.setAttribute('aria-label', 'Tap to unmute ad');
this.unmuteButton.innerHTML = `
<svg viewBox="0 0 24 24" class="uvf-unmute-icon">
<path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/>
</svg>
<span class="uvf-unmute-text">Tap to unmute</span>
`;
this.unmuteButton.addEventListener('click', (e) => {
e.stopPropagation();
this.toggleAdMute();
});
if (!document.getElementById('uvf-unmute-styles')) {
const style = document.createElement('style');
style.id = 'uvf-unmute-styles';
style.textContent = `
.uvf-unmute-btn {
position: absolute !important;
bottom: 80px !important;
left: 20px !important;
z-index: 1000 !important;
display: flex !important;
align-items: center !important;
gap: 8px !important;
padding: 12px 16px !important;
background: rgba(0, 0, 0, 0.8) !important;
border: none !important;
border-radius: 4px !important;
color: white !important;
font-size: 14px !important;
font-weight: 500 !important;
cursor: pointer !important;
transition: all 0.2s ease !important;
backdrop-filter: blur(10px) !important;
-webkit-backdrop-filter: blur(10px) !important;
animation: uvf-unmute-pulse 2s ease-in-out infinite !important;
}
.uvf-unmute-btn:hover {
background: rgba(0, 0, 0, 0.9) !important;
transform: scale(1.05) !important;
}
.uvf-unmute-btn:active {
transform: scale(0.95) !important;
}
.uvf-unmute-icon {
width: 20px !important;
height: 20px !important;
fill: white !important;
}
.uvf-unmute-text {
white-space: nowrap !important;
}
uvf-unmute-pulse {
0%, 100% {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3) !important;
}
50% {
box-shadow: 0 2px 16px rgba(255, 255, 255, 0.2) !important;
}
}
(max-width: 767px) {
.uvf-unmute-btn {
bottom: 70px !important;
left: 50% !important;
transform: translateX(-50%) !important;
padding: 10px 14px !important;
font-size: 13px !important;
}
.uvf-unmute-btn:hover {
transform: translateX(-50%) scale(1.05) !important;
}
}
/* Protect IMA native UI elements from being hidden by parent application CSS */
.uvf-ad-container [class*="ima-"],
.uvf-ad-container [id*="ima-"] {
visibility: visible !important;
opacity: 1 !important;
}
/* Ensure IMA control containers are visible */
.uvf-ad-container .ima-ad-container,
.uvf-ad-container .ima-controls-div,
.uvf-ad-container .ima-countdown-div,
.uvf-ad-container .ima-text-div {
display: block !important;
visibility: visible !important;
opacity: 1 !important;
pointer-events: auto !important;
}
/* CRITICAL: Force Advertisement label visibility (usually top-left) */
.uvf-ad-container .ima-ad-container [class*="text"],
.uvf-ad-container .ima-ad-container [class*="label"],
.uvf-ad-container .ima-ad-container [class*="attribution"] {
display: block !important;
visibility: visible !important;
opacity: 1 !important;
position: relative !important;
z-index: 999999 !important;
}
/* CRITICAL: Force countdown timer visibility */
.uvf-ad-container .ima-ad-container [class*="countdown"],
.uvf-ad-container .ima-ad-container [class*="time"],
.uvf-ad-container .ima-ad-container [class*="remaining"] {
display: block !important;
visibility: visible !important;
opacity: 1 !important;
position: relative !important;
z-index: 999999 !important;
}
/* Force all text elements in ad container to be visible */
.uvf-ad-container * {
max-height: none !important;
max-width: none !important;
}
/* CRITICAL: Advertisement label - typically top-left corner */
/* This is required by Google Ads policy and FTC regulations */
.uvf-ad-container div[style*="top"][style*="left"] {
display: block !important;
visibility: visible !important;
opacity: 1 !important;
}
/* Force any element containing "Advertisement" or "Ad" text to show */
.uvf-ad-container div:has-text("Advertisement"),
.uvf-ad-container div:has-text("Ad ยท"),
.uvf-ad-container span:has-text("Advertisement") {
display: block !important;
visibility: visible !important;
opacity: 1 !important;
color: white !important;
background: rgba(0, 0, 0, 0.6) !important;
padding: 4px 8px !important;
}
`;
document.head.appendChild(style);
}
this.adContainer.appendChild(this.unmuteButton);
console.log('Unmute button displayed (matching player style)');
}
toggleAdMute() {
this.isMuted = !this.isMuted;
if (this.adsManager) {
this.adsManager.setVolume(this.isMuted ? 0 : (this.video.volume || 1));
console.log(`Ad ${this.isMuted ? 'muted' : 'unmuted'}`);
}
this.video.muted = this.isMuted;
this.updateMuteButtonUI();
}
updateMuteButtonUI() {
if (!this.unmuteButton) {
if (this.isMuted)
this.showUnmuteButton();
return;
}
if (this.isMuted) {
this.unmuteButton.setAttribute('aria-label', 'Tap to unmute ad');
this.unmuteButton.innerHTML = `
<svg viewBox="0 0 24 24" class="uvf-unmute-icon">
<path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/>
</svg>
<span class="uvf-unmute-text">Tap to unmute</span>
`;
}
else {
this.hideUnmuteButton();
}
}
resumeAdPlayback() {
try {
if (!this.adsManager || !this.isAdPlaying) {
return;
}
console.log('Attempting to resume ad playback...');
try {
this.adsManager.resume();
console.log('โ
Ad resume() called');
}
catch (e) {
console.warn('resume() failed:', e);
}
}
catch (error) {
console.error('Error resuming ad playback:', error);
}
}
hideUnmuteButton() {
if (this.unmuteButton) {
this.unmuteButton.remove();
this.unmuteButton = null;
}
}
showAdProgressBar(duration) {
this.hideAdProgressBar();
const container = document.createElement('div');
container.id = 'uvf-ad-progress-container';
container.style.cssText = `
position: absolute !important;
bottom: 0px !important;
left: 0px !important;
right: 0px !important;
height: 4px !important;
background: rgba(0, 0, 0, 0.4) !important;
overflow: hidden !important;
z-index: 2147483646 !important;
pointer-events: none !important;
`;
const fill = document.createElement('div');
fill.id = 'uvf-ad-progress-fill';
fill.style.cssText = `
position: absolute !important;
left: 0 !important;
top: 0 !important;
height: 100% !important;
width: 0% !important;
background: #FFC107 !important;
box-shadow: 0 0 8px rgba(255, 193, 7, 0.6) !important;
transition: width 0.1s linear !important;
`;
container.appendChild(fill);
this.adContainer.appendChild(container);
this.adProgressBar = container;
console.log('โ
Ad progress bar created');
let currentTime = 0;
this.adProgressInterval = setInterval(() => {
if (!this.adsManager || !this.isAdPlaying) {
this.hideAdProgressBar();
return;
}
try {
const ad = this.adsManager.getCurrentAd();
if (ad) {
currentTime = this.adsManager.getRemainingTime();
const elapsed = duration - currentTime;
const progress = Math.min(100, Math.max(0, (elapsed / duration) * 100));
const fillEl = document.getElementById('uvf-ad-progress-fill');
if (fillEl) {
fillEl.style.width = `${progress}%`;
}
}
}
catch (e) {
}
}, 100);
}
hideAdProgressBar() {
if (this.adProgressInterval) {
clearInterval(this.adProgressInterval);
this.adProgressInterval = null;
}
if (this.adProgressBar) {
this.adProgressBar.remove();
this.adProgressBar = null;
}
}
destroy() {
this.hideUnmuteButton();
this.hideAdProgressBar();
window.removeEventListener('focus', this.focusHandler);
document.removeEventListener('visibilitychange', this.visibilityHandler);
if (this.timeupdateHandler) {
this.video.removeEventListener('timeupdate', this.timeupdateHandler);
this.timeupdateHandler = null;
}
if (this.volumechangeHandler) {
this.video.removeEventListener('volumechange', this.volumechangeHandler);
this.volumechangeHandler = null;
}
const blocker = this.video.__adPlayBlocker;
if (blocker) {
this.video.removeEventListener('play', blocker);
delete this.video.__adPlayBlocker;
}
if (this.periodicAdCheckInterval) {
clearInterval(this.periodicAdCheckInterval);
this.periodicAdCheckInterval = null;
console.log('โ
Periodic ad scheduling stopped');
}
if (this.adsManager) {
this.adsManager.destroy();
this.adsManager = null;
}
if (this.adsLoader) {
this.adsLoader.destroy();
this.adsLoader = null;
}
this.isAdPlaying = false;
this.pendingAdRequest = false;
this.triggeredMidrollTimes.clear();
}
}
//# sourceMappingURL=GoogleAdsManager.js.map