import videojs from 'video.js';
import './AutoplayCountdownOverlay.css';
// Get the Component base class from Video.js
const Component = videojs.getComponent('Component');
class AutoplayCountdownOverlay extends Component {
constructor(player, options) {
super(player, options);
this.nextVideoData = options.nextVideoData || null;
this.countdownSeconds = options.countdownSeconds || 5;
this.onPlayNext = options.onPlayNext || (() => {});
this.onCancel = options.onCancel || (() => {});
this.currentCountdown = this.countdownSeconds;
this.startTime = null;
this.isActive = false;
// Bind methods
this.startCountdown = this.startCountdown.bind(this);
this.stopCountdown = this.stopCountdown.bind(this);
this.handlePlayNext = this.handlePlayNext.bind(this);
this.handleCancel = this.handleCancel.bind(this);
this.updateCountdownDisplay = this.updateCountdownDisplay.bind(this);
}
createEl() {
const overlay = super.createEl('div', {
className: 'vjs-autoplay-countdown-overlay',
});
// Get next video title or fallback
const nextVideoTitle = this.nextVideoData?.title || 'Next Video';
overlay.innerHTML = `
Up Next
${nextVideoTitle}
${this.nextVideoData?.author ? `
${this.nextVideoData.author}
` : ''}
CANCEL
`;
// Add event listeners with explicit binding
const circularCountdown = overlay.querySelector('.circular-countdown');
const cancelButton = overlay.querySelector('.autoplay-cancel-button');
const closeButton = overlay.querySelector('.autoplay-close-button');
if (circularCountdown) {
circularCountdown.addEventListener('click', (e) => {
e.preventDefault();
this.handlePlayNext();
});
}
if (cancelButton) {
cancelButton.addEventListener('click', (e) => {
e.preventDefault();
this.handleCancel();
});
}
if (closeButton) {
closeButton.addEventListener('click', (e) => {
e.preventDefault();
this.handleCancel();
});
}
// Initially hide the overlay
overlay.style.display = 'none';
return overlay;
}
startCountdown() {
this.isActive = true;
this.currentCountdown = this.countdownSeconds;
this.startTime = Date.now();
// Show immediately and start countdown without delay
this.show();
this.updateCountdownDisplay();
// Use requestAnimationFrame for smooth animation
const animate = () => {
if (!this.isActive) return;
const elapsed = (Date.now() - this.startTime) / 1000;
this.currentCountdown = Math.max(0, this.countdownSeconds - elapsed);
this.updateCountdownDisplay();
if (this.currentCountdown <= 0) {
this.stopCountdown();
// Auto-play next video when countdown reaches 0
this.handlePlayNext();
} else {
requestAnimationFrame(animate);
}
};
// Start the animation
requestAnimationFrame(animate);
}
stopCountdown() {
this.isActive = false;
this.hide();
}
updateCountdownDisplay() {
const progressCircle = this.el().querySelector('.countdown-progress');
if (progressCircle) {
// Calculate progress (282.74 is the circumference of the circle with radius 45)
const circumference = 2 * Math.PI * 45; // 282.74
const progress = (this.countdownSeconds - this.currentCountdown) / this.countdownSeconds;
const offset = circumference - circumference * progress;
// Apply the animation
progressCircle.style.strokeDashoffset = offset;
}
}
handlePlayNext() {
try {
this.stopCountdown();
this.onPlayNext();
} catch (error) {
console.error('Error in handlePlayNext:', error);
}
}
handleCancel() {
try {
this.stopCountdown();
this.onCancel();
} catch (error) {
console.error('Error in handleCancel:', error);
}
}
show() {
if (this.el()) {
this.el().style.display = 'flex';
// Force immediate display and add animation class
requestAnimationFrame(() => {
if (this.el()) {
this.el().classList.add('autoplay-countdown-show');
}
});
}
}
hide() {
if (this.el()) {
this.el().style.display = 'none';
this.el().classList.remove('autoplay-countdown-show');
}
}
formatDuration(seconds) {
if (!seconds || seconds === 0) return '';
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = seconds % 60;
if (hours > 0) {
return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
} else {
return `${minutes}:${secs.toString().padStart(2, '0')}`;
}
}
// Update next video data
updateNextVideoData(nextVideoData) {
this.nextVideoData = nextVideoData;
// Re-render the content if the overlay exists
if (this.el()) {
const nextVideoTitle = this.nextVideoData?.title || 'Next Video';
const titleElement = this.el().querySelector('.next-video-title');
const authorElement = this.el().querySelector('.next-video-author');
if (titleElement) {
titleElement.textContent = nextVideoTitle;
}
if (authorElement && this.nextVideoData?.author) {
authorElement.textContent = this.nextVideoData.author;
}
}
}
// Cleanup method
dispose() {
this.stopCountdown();
super.dispose();
}
}
// Register the component
videojs.registerComponent('AutoplayCountdownOverlay', AutoplayCountdownOverlay);
export default AutoplayCountdownOverlay;