mirror of
https://github.com/mediacms-io/mediacms.git
synced 2025-10-06 22:32:41 +07:00
feat: Autoplay functionality
This commit is contained in:
@@ -55,6 +55,43 @@
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
/* Autoplay Toggle Button Styles */
|
||||
.vjs-autoplay-toggle {
|
||||
width: 3em !important;
|
||||
height: 3em !important;
|
||||
flex: none;
|
||||
padding: 0 !important;
|
||||
margin: 0 4px !important;
|
||||
line-height: 3em !important;
|
||||
position: relative;
|
||||
border: none !important;
|
||||
background: transparent !important;
|
||||
color: #fff !important;
|
||||
cursor: pointer !important;
|
||||
transition: all 0.2s ease !important;
|
||||
}
|
||||
|
||||
.vjs-autoplay-toggle:hover {
|
||||
color: #ff4444 !important;
|
||||
transform: scale(1.1) !important;
|
||||
}
|
||||
|
||||
.vjs-autoplay-toggle .vjs-autoplay-icon {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: auto;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.vjs-autoplay-toggle .vjs-autoplay-icon svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.vjs-next-video-control .vjs-icon-placeholder {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
@@ -511,9 +548,17 @@
|
||||
gap: 6px !important;
|
||||
}
|
||||
|
||||
/* Spacer element to push buttons to the right */
|
||||
.video-js .vjs-spacer-control {
|
||||
flex: 1 !important;
|
||||
min-width: 1px !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
/* Basic control bar styling - let JavaScript handle positioning */
|
||||
.video-js .vjs-control-bar .vjs-control {
|
||||
/* Natural flex flow */
|
||||
flex: none !important; /* Prevent controls from growing */
|
||||
}
|
||||
|
||||
/* Seek Indicator Styles */
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
import videojs from 'video.js';
|
||||
|
||||
const Button = videojs.getComponent('Button');
|
||||
|
||||
// Custom Autoplay Toggle Button Component using modern Video.js API
|
||||
class AutoplayToggleButton extends Button {
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
this.userPreferences = options.userPreferences;
|
||||
// Get autoplay preference from localStorage, default to false if not set
|
||||
if (this.userPreferences) {
|
||||
const savedAutoplay = this.userPreferences.getPreference('autoplay');
|
||||
this.isAutoplayEnabled = savedAutoplay === true; // Explicit boolean check
|
||||
console.log('Autoplay button initialized with saved preference:', this.isAutoplayEnabled);
|
||||
} else {
|
||||
this.isAutoplayEnabled = false;
|
||||
console.log('Autoplay button initialized with default (no userPreferences):', this.isAutoplayEnabled);
|
||||
}
|
||||
|
||||
// Bind methods
|
||||
this.updateIcon = this.updateIcon.bind(this);
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
|
||||
createEl() {
|
||||
const button = super.createEl('button', {
|
||||
className: 'vjs-autoplay-toggle vjs-control vjs-button',
|
||||
type: 'button',
|
||||
title: this.isAutoplayEnabled ? 'Autoplay is on' : 'Autoplay is off',
|
||||
'aria-label': this.isAutoplayEnabled ? 'Autoplay is on' : 'Autoplay is off',
|
||||
});
|
||||
|
||||
// Create simple text-based icon for now to ensure it works
|
||||
this.iconSpan = videojs.dom.createEl('span', {
|
||||
'aria-hidden': 'true',
|
||||
className: 'vjs-autoplay-icon',
|
||||
});
|
||||
|
||||
// Set initial icon state directly
|
||||
console.log('AutoplayToggleButton createEl: isAutoplayEnabled =', this.isAutoplayEnabled);
|
||||
if (this.isAutoplayEnabled) {
|
||||
this.iconSpan.innerHTML = `<span style="font-size: 1.2em; color: #ff4444;">●</span>`;
|
||||
console.log('Setting RED icon (autoplay ON)');
|
||||
} else {
|
||||
this.iconSpan.innerHTML = `<span style="font-size: 1.2em; color: #ccc;">○</span>`;
|
||||
console.log('Setting GRAY icon (autoplay OFF)');
|
||||
}
|
||||
|
||||
// Create control text span
|
||||
const controlTextSpan = videojs.dom.createEl('span', {
|
||||
className: 'vjs-control-text',
|
||||
});
|
||||
controlTextSpan.textContent = this.isAutoplayEnabled ? 'Autoplay is on' : 'Autoplay is off';
|
||||
|
||||
// Append both spans to button
|
||||
button.appendChild(this.iconSpan);
|
||||
button.appendChild(controlTextSpan);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
updateIcon() {
|
||||
if (this.isAutoplayEnabled) {
|
||||
// Simple text icon for now
|
||||
this.iconSpan.innerHTML = `<span style="font-size: 1.2em; color: #ff4444;">●</span>`;
|
||||
// Only update element properties if element exists
|
||||
if (this.el()) {
|
||||
this.el().title = 'Autoplay is on';
|
||||
this.el().setAttribute('aria-label', 'Autoplay is on');
|
||||
const controlText = this.el().querySelector('.vjs-control-text');
|
||||
if (controlText) {
|
||||
controlText.textContent = 'Autoplay is on';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Simple text icon for now
|
||||
this.iconSpan.innerHTML = `<span style="font-size: 1.2em; color: #ccc;">○</span>`;
|
||||
// Only update element properties if element exists
|
||||
if (this.el()) {
|
||||
this.el().title = 'Autoplay is off';
|
||||
this.el().setAttribute('aria-label', 'Autoplay is off');
|
||||
const controlText = this.el().querySelector('.vjs-control-text');
|
||||
if (controlText) {
|
||||
controlText.textContent = 'Autoplay is off';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleClick() {
|
||||
// Toggle autoplay state
|
||||
this.isAutoplayEnabled = !this.isAutoplayEnabled;
|
||||
|
||||
// Save preference if userPreferences is available
|
||||
if (this.userPreferences) {
|
||||
this.userPreferences.setAutoplayPreference(this.isAutoplayEnabled);
|
||||
console.log('Autoplay preference saved to localStorage:', this.isAutoplayEnabled);
|
||||
}
|
||||
|
||||
// Update icon and accessibility attributes
|
||||
this.updateIcon();
|
||||
|
||||
console.log('Autoplay toggled:', this.isAutoplayEnabled ? 'ON' : 'OFF');
|
||||
|
||||
// Trigger custom event for other components to listen to
|
||||
this.player().trigger('autoplayToggle', { autoplay: this.isAutoplayEnabled });
|
||||
}
|
||||
|
||||
// Method to update button state from external sources
|
||||
setAutoplayState(enabled) {
|
||||
this.isAutoplayEnabled = enabled;
|
||||
this.updateIcon();
|
||||
}
|
||||
}
|
||||
|
||||
// Register the component
|
||||
videojs.registerComponent('AutoplayToggleButton', AutoplayToggleButton);
|
||||
|
||||
export default AutoplayToggleButton;
|
||||
@@ -6,7 +6,7 @@ const Button = videojs.getComponent('Button');
|
||||
class NextVideoButton extends Button {
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
this.nextLink = options.nextLink || '';
|
||||
// this.nextLink = options.nextLink || '';
|
||||
}
|
||||
|
||||
createEl() {
|
||||
@@ -43,7 +43,7 @@ class NextVideoButton extends Button {
|
||||
}
|
||||
|
||||
handleClick() {
|
||||
console.log('NextVideoButton handleClick', this.nextLink);
|
||||
// console.log('NextVideoButton handleClick', this.nextLink);
|
||||
this.player().trigger('nextVideo');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Export all Video.js components
|
||||
export { default as VideoJSPlayer } from './video-player/VideoJSPlayer';
|
||||
export { default as EndScreenOverlay } from './overlays/EndScreenOverlay';
|
||||
export { default as AutoplayCountdownOverlay } from './overlays/AutoplayCountdownOverlay';
|
||||
export { default as ChapterMarkers } from './markers/ChapterMarkers';
|
||||
export { default as NextVideoButton } from './controls/NextVideoButton';
|
||||
export { default as AutoplayToggleButton } from './controls/AutoplayToggleButton';
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
/* Autoplay Countdown Overlay Styles */
|
||||
.vjs-autoplay-countdown-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: calc(100% - 46px); /* Account for control bar */
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
backdrop-filter: blur(4px);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.vjs-autoplay-countdown-overlay.autoplay-countdown-show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.autoplay-countdown-content {
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.autoplay-countdown-header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.autoplay-countdown-header h3 {
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.countdown-timer {
|
||||
color: #ff4444;
|
||||
font-weight: 700;
|
||||
font-size: 20px;
|
||||
animation: countdownPulse 1s infinite;
|
||||
}
|
||||
|
||||
@keyframes countdownPulse {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.autoplay-countdown-video-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 24px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.next-video-thumbnail {
|
||||
flex-shrink: 0;
|
||||
width: 80px;
|
||||
height: 45px;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
background: #333;
|
||||
}
|
||||
|
||||
.next-video-thumbnail img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.next-video-details {
|
||||
flex-grow: 1;
|
||||
min-width: 0; /* Allow text truncation */
|
||||
}
|
||||
|
||||
.next-video-title {
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
margin: 0 0 4px 0;
|
||||
line-height: 1.3;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.next-video-author {
|
||||
color: #aaa;
|
||||
font-size: 12px;
|
||||
margin: 0 0 2px 0;
|
||||
line-height: 1.2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.next-video-duration {
|
||||
color: #ccc;
|
||||
font-size: 11px;
|
||||
margin: 0;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.autoplay-countdown-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.autoplay-play-button,
|
||||
.autoplay-cancel-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
padding: 10px 18px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.autoplay-play-button {
|
||||
background: #ff0000;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.autoplay-play-button:hover {
|
||||
background: #e60000;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.autoplay-play-button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.autoplay-cancel-button {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: #fff;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.autoplay-cancel-button:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.autoplay-cancel-button:active {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.autoplay-play-button svg,
|
||||
.autoplay-cancel-button svg {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Autoplay Toggle Button Styles */
|
||||
.vjs-autoplay-toggle {
|
||||
width: 3em !important;
|
||||
height: 3em !important;
|
||||
flex: none;
|
||||
padding: 0 !important;
|
||||
margin: 0 4px !important;
|
||||
line-height: 3em !important;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.vjs-autoplay-toggle .vjs-autoplay-icon {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.vjs-autoplay-toggle .vjs-autoplay-icon svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Responsive design */
|
||||
@media (max-width: 480px) {
|
||||
.autoplay-countdown-content {
|
||||
padding: 20px;
|
||||
max-width: 320px;
|
||||
}
|
||||
|
||||
.autoplay-countdown-header h3 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.countdown-timer {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.autoplay-countdown-video-info {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.next-video-thumbnail {
|
||||
width: 60px;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.next-video-title {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.autoplay-countdown-actions {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.autoplay-play-button,
|
||||
.autoplay-cancel-button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
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.countdownInterval = 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';
|
||||
const nextVideoThumbnail = this.nextVideoData?.thumbnail || '';
|
||||
|
||||
overlay.innerHTML = `
|
||||
<div class="autoplay-countdown-content">
|
||||
<div class="autoplay-countdown-header">
|
||||
<h3>Up next in <span class="countdown-timer">${this.countdownSeconds}</span></h3>
|
||||
</div>
|
||||
|
||||
<div class="autoplay-countdown-video-info">
|
||||
${
|
||||
nextVideoThumbnail
|
||||
? `<div class="next-video-thumbnail">
|
||||
<img src="${nextVideoThumbnail}" alt="${nextVideoTitle}" />
|
||||
</div>`
|
||||
: ''
|
||||
}
|
||||
<div class="next-video-details">
|
||||
<h4 class="next-video-title">${nextVideoTitle}</h4>
|
||||
${this.nextVideoData?.author ? `<p class="next-video-author">${this.nextVideoData.author}</p>` : ''}
|
||||
${this.nextVideoData?.duration ? `<p class="next-video-duration">${this.formatDuration(this.nextVideoData.duration)}</p>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="autoplay-countdown-actions">
|
||||
<button class="autoplay-play-button" type="button">
|
||||
<svg viewBox="0 0 24 24" width="1.2em" height="1.2em" fill="currentColor">
|
||||
<path d="M8 5v14l11-7z"/>
|
||||
</svg>
|
||||
Play Now
|
||||
</button>
|
||||
<button class="autoplay-cancel-button" type="button">
|
||||
<svg viewBox="0 0 24 24" width="1.2em" height="1.2em" fill="currentColor">
|
||||
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
|
||||
</svg>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Add event listeners with explicit binding
|
||||
const playButton = overlay.querySelector('.autoplay-play-button');
|
||||
const cancelButton = overlay.querySelector('.autoplay-cancel-button');
|
||||
|
||||
if (playButton) {
|
||||
playButton.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.handlePlayNext();
|
||||
});
|
||||
}
|
||||
|
||||
if (cancelButton) {
|
||||
cancelButton.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.show();
|
||||
this.updateCountdownDisplay();
|
||||
|
||||
// Start countdown interval
|
||||
this.countdownInterval = setInterval(() => {
|
||||
this.currentCountdown--;
|
||||
this.updateCountdownDisplay();
|
||||
|
||||
if (this.currentCountdown <= 0) {
|
||||
this.stopCountdown();
|
||||
// Auto-play next video when countdown reaches 0
|
||||
this.handlePlayNext();
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
console.log('Autoplay countdown started:', this.countdownSeconds, 'seconds');
|
||||
}
|
||||
|
||||
stopCountdown() {
|
||||
this.isActive = false;
|
||||
if (this.countdownInterval) {
|
||||
clearInterval(this.countdownInterval);
|
||||
this.countdownInterval = null;
|
||||
}
|
||||
this.hide();
|
||||
console.log('Autoplay countdown stopped');
|
||||
}
|
||||
|
||||
updateCountdownDisplay() {
|
||||
const timerElement = this.el().querySelector('.countdown-timer');
|
||||
if (timerElement) {
|
||||
timerElement.textContent = this.currentCountdown;
|
||||
}
|
||||
}
|
||||
|
||||
handlePlayNext() {
|
||||
console.log('Autoplay: Playing next video immediately');
|
||||
try {
|
||||
this.stopCountdown();
|
||||
this.onPlayNext();
|
||||
} catch (error) {
|
||||
console.error('Error in handlePlayNext:', error);
|
||||
}
|
||||
}
|
||||
|
||||
handleCancel() {
|
||||
console.log('Autoplay: Cancelled by user');
|
||||
try {
|
||||
this.stopCountdown();
|
||||
this.onCancel();
|
||||
} catch (error) {
|
||||
console.error('Error in handleCancel:', error);
|
||||
}
|
||||
}
|
||||
|
||||
show() {
|
||||
if (this.el()) {
|
||||
this.el().style.display = 'flex';
|
||||
// Add animation class for smooth entrance
|
||||
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 nextVideoThumbnail = this.nextVideoData?.thumbnail || '';
|
||||
|
||||
const videoInfoElement = this.el().querySelector('.autoplay-countdown-video-info');
|
||||
if (videoInfoElement) {
|
||||
videoInfoElement.innerHTML = `
|
||||
${
|
||||
nextVideoThumbnail
|
||||
? `<div class="next-video-thumbnail">
|
||||
<img src="${nextVideoThumbnail}" alt="${nextVideoTitle}" />
|
||||
</div>`
|
||||
: ''
|
||||
}
|
||||
<div class="next-video-details">
|
||||
<h4 class="next-video-title">${nextVideoTitle}</h4>
|
||||
${this.nextVideoData?.author ? `<p class="next-video-author">${this.nextVideoData.author}</p>` : ''}
|
||||
${this.nextVideoData?.duration ? `<p class="next-video-duration">${this.formatDuration(this.nextVideoData.duration)}</p>` : ''}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup method
|
||||
dispose() {
|
||||
this.stopCountdown();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// Register the component
|
||||
videojs.registerComponent('AutoplayCountdownOverlay', AutoplayCountdownOverlay);
|
||||
|
||||
export default AutoplayCountdownOverlay;
|
||||
@@ -4,8 +4,10 @@ import 'video.js/dist/video-js.css';
|
||||
|
||||
// Import the separated components
|
||||
import EndScreenOverlay from '../overlays/EndScreenOverlay';
|
||||
import AutoplayCountdownOverlay from '../overlays/AutoplayCountdownOverlay';
|
||||
import ChapterMarkers from '../markers/ChapterMarkers';
|
||||
import NextVideoButton from '../controls/NextVideoButton';
|
||||
import AutoplayToggleButton from '../controls/AutoplayToggleButton';
|
||||
import CustomRemainingTime from '../controls/CustomRemainingTime';
|
||||
import CustomChaptersOverlay from '../controls/CustomChaptersOverlay';
|
||||
import CustomSettingsMenu from '../controls/CustomSettingsMenu';
|
||||
@@ -561,7 +563,6 @@ function VideoJSPlayer() {
|
||||
frame: { width: 160, height: 90, seconds: 10 },
|
||||
},
|
||||
siteUrl: '',
|
||||
hasNextLink: true,
|
||||
nextLink: 'https://demo.mediacms.io/view?m=YjGJafibO',
|
||||
},
|
||||
[]
|
||||
@@ -589,7 +590,7 @@ function VideoJSPlayer() {
|
||||
poster: mediaData.siteUrl + mediaData.data?.poster_url || '',
|
||||
previewSprite: mediaData?.previewSprite || {},
|
||||
related_media: mediaData.data?.related_media || [],
|
||||
nextLink: mediaData.nextLink || '',
|
||||
nextLink: mediaData?.nextLink || null,
|
||||
sources: mediaData.data?.original_media_url
|
||||
? [
|
||||
{
|
||||
@@ -661,8 +662,8 @@ function VideoJSPlayer() {
|
||||
|
||||
// Player dimensions - removed for responsive design
|
||||
|
||||
// Autoplay behavior: false, true, 'muted', 'play', 'any'
|
||||
autoplay: true,
|
||||
// Autoplay behavior: Use 'muted' to comply with browser policies
|
||||
autoplay: 'muted',
|
||||
|
||||
// Start video over when it ends
|
||||
loop: false,
|
||||
@@ -951,7 +952,7 @@ function VideoJSPlayer() {
|
||||
setTimeout(() => {
|
||||
if (playerRef.current && !playerRef.current.isDisposed()) {
|
||||
playerRef.current.play().catch((error) => {
|
||||
console.log('Autoplay was prevented:', error);
|
||||
console.log('ℹ️ Browser prevented autoplay (normal behavior):', error.message);
|
||||
});
|
||||
}
|
||||
}, 100);
|
||||
@@ -1024,7 +1025,8 @@ function VideoJSPlayer() {
|
||||
// END: Implement custom time display component
|
||||
|
||||
// BEGIN: Implement custom next video button
|
||||
if (mediaData.hasNextLink) {
|
||||
if (mediaData?.nextLink) {
|
||||
console.log('mediaData.nextLink edw', mediaData.nextLink);
|
||||
const nextVideoButton = new NextVideoButton(playerRef.current, {
|
||||
nextLink: mediaData.nextLink,
|
||||
});
|
||||
@@ -1033,6 +1035,30 @@ function VideoJSPlayer() {
|
||||
}
|
||||
// END: Implement custom next video button
|
||||
|
||||
// BEGIN: Implement autoplay toggle button - simplified
|
||||
try {
|
||||
const autoplayToggleButton = new AutoplayToggleButton(playerRef.current, {
|
||||
userPreferences: userPreferences.current,
|
||||
});
|
||||
// Add it after the play button
|
||||
const playToggleIndex = controlBar.children().indexOf(playToggle);
|
||||
controlBar.addChild(autoplayToggleButton, {}, playToggleIndex + 1);
|
||||
|
||||
// Store reference for later use
|
||||
customComponents.current.autoplayToggleButton = autoplayToggleButton;
|
||||
|
||||
// Force update icon after adding to DOM to ensure correct display
|
||||
setTimeout(() => {
|
||||
autoplayToggleButton.updateIcon();
|
||||
console.log('✓ Autoplay toggle button icon updated after DOM insertion');
|
||||
}, 100);
|
||||
|
||||
console.log('✓ Autoplay toggle button added successfully');
|
||||
} catch (error) {
|
||||
console.error('✗ Failed to add autoplay toggle button:', error);
|
||||
}
|
||||
// END: Implement autoplay toggle button
|
||||
|
||||
// Remove duplicate captions button and move chapters to end
|
||||
/* const cleanupControls = () => {
|
||||
// Log all current children for debugging
|
||||
@@ -1148,66 +1174,28 @@ function VideoJSPlayer() {
|
||||
}
|
||||
// END: Add chapter markers to progress control
|
||||
|
||||
// BEGIN: Move CC (subtitles) and PiP buttons to the right side
|
||||
// BEGIN: Simple button layout fix - use CSS approach
|
||||
setTimeout(() => {
|
||||
// Create a spacer element to push buttons to the right
|
||||
const spacer = videojs.dom.createEl('div', {
|
||||
className: 'vjs-spacer-control vjs-control',
|
||||
});
|
||||
spacer.style.flex = '1';
|
||||
spacer.style.minWidth = '1px';
|
||||
console.log('Setting up simplified button layout...');
|
||||
|
||||
// Find insertion point after time displays
|
||||
// Add a simple spacer div using DOM manipulation (simpler approach)
|
||||
const spacerDiv = document.createElement('div');
|
||||
spacerDiv.className = 'vjs-spacer-control vjs-control';
|
||||
spacerDiv.style.flex = '1';
|
||||
spacerDiv.style.minWidth = '1px';
|
||||
spacerDiv.style.height = '100%';
|
||||
|
||||
// Find insertion point after duration display
|
||||
const durationDisplay = controlBar.getChild('durationDisplay');
|
||||
const customRemainingTime = controlBar.getChild('customRemainingTime');
|
||||
const insertAfter = customRemainingTime || durationDisplay;
|
||||
|
||||
if (insertAfter) {
|
||||
const insertIndex = controlBar.children().indexOf(insertAfter) + 1;
|
||||
controlBar.el().insertBefore(spacer, controlBar.children()[insertIndex]?.el() || null);
|
||||
console.log('✓ Spacer added after time displays');
|
||||
if (durationDisplay && durationDisplay.el()) {
|
||||
const controlBarEl = controlBar.el();
|
||||
const durationEl = durationDisplay.el();
|
||||
const nextSibling = durationEl.nextSibling;
|
||||
controlBarEl.insertBefore(spacerDiv, nextSibling);
|
||||
console.log('✓ Simple spacer added after duration display');
|
||||
}
|
||||
|
||||
// Find the subtitles/captions button (CC button)
|
||||
const possibleTextTrackButtons = ['subtitlesButton', 'captionsButton', 'subsCapsButton'];
|
||||
let textTrackButton = null;
|
||||
|
||||
for (const buttonName of possibleTextTrackButtons) {
|
||||
const button = controlBar.getChild(buttonName);
|
||||
if (button) {
|
||||
textTrackButton = button;
|
||||
console.log(`Found text track button: ${buttonName}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Find other buttons to move to the right
|
||||
const pipButton = controlBar.getChild('pictureInPictureToggle');
|
||||
const playbackRateButton = controlBar.getChild('playbackRateMenuButton');
|
||||
const chaptersButton = controlBar.getChild('chaptersButton');
|
||||
|
||||
// Move buttons to the right side (after spacer)
|
||||
const buttonsToMove = [
|
||||
playbackRateButton,
|
||||
textTrackButton,
|
||||
pipButton,
|
||||
chaptersButton,
|
||||
fullscreenToggle,
|
||||
].filter(Boolean);
|
||||
|
||||
buttonsToMove.forEach((button) => {
|
||||
if (button) {
|
||||
try {
|
||||
controlBar.removeChild(button);
|
||||
controlBar.addChild(button);
|
||||
console.log(`✓ Moved ${button.name_ || 'button'} to right side`);
|
||||
} catch (e) {
|
||||
console.log(`✗ Failed to move button:`, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
// END: Move CC (subtitles) and PiP buttons to the right side
|
||||
}, 300);
|
||||
// END: Simple button layout fix
|
||||
|
||||
// BEGIN: Move chapters button after fullscreen toggle
|
||||
if (chaptersButton && fullscreenToggle) {
|
||||
@@ -1492,8 +1480,9 @@ function VideoJSPlayer() {
|
||||
console.log('Video paused');
|
||||
});
|
||||
|
||||
// Store reference to end screen for cleanup
|
||||
// Store reference to end screen and autoplay countdown for cleanup
|
||||
let endScreen = null;
|
||||
let autoplayCountdown = null;
|
||||
|
||||
playerRef.current.on('ended', () => {
|
||||
console.log('Video ended');
|
||||
@@ -1516,37 +1505,115 @@ function VideoJSPlayer() {
|
||||
}
|
||||
}, 50);
|
||||
|
||||
// Prevent creating multiple end screens
|
||||
if (endScreen) {
|
||||
console.log('End screen already exists, removing previous one');
|
||||
playerRef.current.removeChild(endScreen);
|
||||
endScreen = null;
|
||||
console.log('mediaData.previewSprite', mediaData.previewSprite);
|
||||
console.log('mediaData.nextLink', mediaData.nextLink);
|
||||
console.log('userPreferences', userPreferences);
|
||||
|
||||
// Check if autoplay is enabled and there's a next video
|
||||
const isAutoplayEnabled = userPreferences.current.getAutoplayPreference();
|
||||
const hasNextVideo = mediaData.nextLink !== null;
|
||||
|
||||
console.log('isAutoplayEnabled', isAutoplayEnabled);
|
||||
console.log('hasNextVideo', hasNextVideo);
|
||||
|
||||
if (isAutoplayEnabled && hasNextVideo) {
|
||||
// Get next video data for countdown display - find the next video in related videos
|
||||
let nextVideoData = {
|
||||
title: 'Next Video',
|
||||
author: '',
|
||||
duration: 0,
|
||||
thumbnail: '',
|
||||
};
|
||||
|
||||
// Try to find the next video by URL matching or just use the first related video
|
||||
if (relatedVideos.length > 0) {
|
||||
// For now, use the first related video as the next video
|
||||
// In a real implementation, you might want to find the specific next video
|
||||
const nextVideo = relatedVideos[0];
|
||||
nextVideoData = {
|
||||
title: nextVideo.title || 'Next Video',
|
||||
author: nextVideo.author || '',
|
||||
duration: nextVideo.duration || 0,
|
||||
thumbnail: nextVideo.thumbnail || '',
|
||||
};
|
||||
}
|
||||
|
||||
// Clean up any existing overlays
|
||||
if (endScreen) {
|
||||
playerRef.current.removeChild(endScreen);
|
||||
endScreen = null;
|
||||
}
|
||||
if (autoplayCountdown) {
|
||||
playerRef.current.removeChild(autoplayCountdown);
|
||||
autoplayCountdown = null;
|
||||
}
|
||||
|
||||
// Show autoplay countdown
|
||||
autoplayCountdown = new AutoplayCountdownOverlay(playerRef.current, {
|
||||
nextVideoData: nextVideoData,
|
||||
countdownSeconds: 5,
|
||||
onPlayNext: () => {
|
||||
console.log('Autoplay: Navigating to next video');
|
||||
goToNextVideo();
|
||||
},
|
||||
onCancel: () => {
|
||||
console.log('Autoplay: User cancelled, showing related videos');
|
||||
// Hide countdown and show end screen instead
|
||||
if (autoplayCountdown) {
|
||||
playerRef.current.removeChild(autoplayCountdown);
|
||||
autoplayCountdown = null;
|
||||
}
|
||||
showEndScreen();
|
||||
},
|
||||
});
|
||||
|
||||
playerRef.current.addChild(autoplayCountdown);
|
||||
autoplayCountdown.startCountdown();
|
||||
} else {
|
||||
// Autoplay disabled or no next video - show regular end screen
|
||||
showEndScreen();
|
||||
}
|
||||
|
||||
// Show end screen with related videos
|
||||
endScreen = new EndScreenOverlay(playerRef.current, {
|
||||
relatedVideos: relatedVideos,
|
||||
});
|
||||
// Function to show the regular end screen
|
||||
function showEndScreen() {
|
||||
// Prevent creating multiple end screens
|
||||
if (endScreen) {
|
||||
console.log('End screen already exists, removing previous one');
|
||||
playerRef.current.removeChild(endScreen);
|
||||
endScreen = null;
|
||||
}
|
||||
|
||||
// Also store the data directly on the component as backup
|
||||
endScreen.relatedVideos = relatedVideos;
|
||||
// Show end screen with related videos
|
||||
endScreen = new EndScreenOverlay(playerRef.current, {
|
||||
relatedVideos: relatedVideos,
|
||||
});
|
||||
|
||||
playerRef.current.addChild(endScreen);
|
||||
endScreen.show();
|
||||
// Also store the data directly on the component as backup
|
||||
endScreen.relatedVideos = relatedVideos;
|
||||
|
||||
playerRef.current.addChild(endScreen);
|
||||
endScreen.show();
|
||||
}
|
||||
});
|
||||
|
||||
// Hide end screen when user wants to replay
|
||||
// Hide end screen and autoplay countdown when user wants to replay
|
||||
playerRef.current.on('play', () => {
|
||||
if (endScreen) {
|
||||
endScreen.hide();
|
||||
}
|
||||
if (autoplayCountdown) {
|
||||
autoplayCountdown.stopCountdown();
|
||||
}
|
||||
});
|
||||
|
||||
// Hide end screen when user seeks
|
||||
// Hide end screen and autoplay countdown when user seeks
|
||||
playerRef.current.on('seeking', () => {
|
||||
if (endScreen) {
|
||||
endScreen.hide();
|
||||
}
|
||||
if (autoplayCountdown) {
|
||||
autoplayCountdown.stopCountdown();
|
||||
}
|
||||
});
|
||||
|
||||
// Handle replay button functionality
|
||||
@@ -1602,7 +1669,7 @@ function VideoJSPlayer() {
|
||||
// Start playing the video immediately if autoplay is enabled
|
||||
if (playerRef.current.autoplay()) {
|
||||
playerRef.current.play().catch((error) => {
|
||||
console.log('Autoplay prevented by browser:', error);
|
||||
console.log('ℹ️ Browser prevented autoplay (normal behavior):', error.message);
|
||||
// If autoplay fails, we can still focus the element
|
||||
// so the user can manually start and use keyboard controls
|
||||
});
|
||||
@@ -1663,12 +1730,7 @@ function VideoJSPlayer() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<video ref={videoRef} className="video-js vjs-default-skin" tabIndex="0" />
|
||||
<em>nextLink: {currentVideo.nextLink}</em>
|
||||
</>
|
||||
);
|
||||
return <video ref={videoRef} className="video-js vjs-default-skin" tabIndex="0" />;
|
||||
}
|
||||
|
||||
export default VideoJSPlayer;
|
||||
|
||||
@@ -11,6 +11,7 @@ class UserPreferences {
|
||||
quality: 'auto', // Auto quality
|
||||
subtitleLanguage: null, // No subtitles by default
|
||||
muted: false,
|
||||
autoplay: false, // Autoplay disabled by default
|
||||
};
|
||||
}
|
||||
|
||||
@@ -517,6 +518,23 @@ class UserPreferences {
|
||||
console.error('❌ Error force saving subtitle language:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get autoplay preference
|
||||
* @returns {boolean} Autoplay preference
|
||||
*/
|
||||
getAutoplayPreference() {
|
||||
return this.getPreference('autoplay');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set autoplay preference
|
||||
* @param {boolean} autoplay - Autoplay setting
|
||||
*/
|
||||
setAutoplayPreference(autoplay) {
|
||||
this.setPreference('autoplay', autoplay);
|
||||
console.log('Autoplay preference saved:', autoplay);
|
||||
}
|
||||
}
|
||||
|
||||
export default UserPreferences;
|
||||
|
||||
Generated
+19
-5
@@ -26,6 +26,7 @@
|
||||
"@babel/core": "^7.26.9",
|
||||
"@babel/preset-env": "^7.26.9",
|
||||
"@babel/preset-react": "^7.26.3",
|
||||
"@types/minimatch": "^5.1.2",
|
||||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"autoprefixer": "^10.4.21",
|
||||
@@ -13864,13 +13865,26 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/path2d-polyfill": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path2d-polyfill/-/path2d-polyfill-2.0.1.tgz",
|
||||
"integrity": "sha512-ad/3bsalbbWhmBo0D6FZ4RNMwsLsPpL6gnvhuSaU5Vm7b06Kr5ubSltQQ0T7YKsiJQO+g22zJ4dJKNTXIyOXtA==",
|
||||
"node_modules/path2d": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path2d/-/path2d-0.1.1.tgz",
|
||||
"integrity": "sha512-/+S03c8AGsDYKKBtRDqieTJv2GlkMb0bWjnqOgtF6MkjdUQ9a8ARAtxWf9NgKLGm2+WQr6+/tqJdU8HNGsIDoA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/path2d-polyfill": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path2d-polyfill/-/path2d-polyfill-2.1.1.tgz",
|
||||
"integrity": "sha512-4Rka5lN+rY/p0CdD8+E+BFv51lFaFvJOrlOhyQ+zjzyQrzyh3ozmxd1vVGGDdIbUFSBtIZLSnspxTgPT0iJhvA==",
|
||||
"deprecated": "this package has been deprecated",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"path2d": "0.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/pbkdf2": {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"@babel/core": "^7.26.9",
|
||||
"@babel/preset-env": "^7.26.9",
|
||||
"@babel/preset-react": "^7.26.3",
|
||||
"@types/minimatch": "^5.1.2",
|
||||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"autoprefixer": "^10.4.21",
|
||||
@@ -40,18 +41,18 @@
|
||||
"webpack": "^5.98.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-pdf-viewer/core": "^3.9.0",
|
||||
"@react-pdf-viewer/default-layout": "^3.9.0",
|
||||
"axios": "^1.8.2",
|
||||
"flux": "^4.0.4",
|
||||
"mediacms-player": "file:packages/player",
|
||||
"normalize.css": "^8.0.1",
|
||||
"pdfjs-dist": "3.4.120",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-mentions": "^4.3.1",
|
||||
"sortablejs": "^1.13.0",
|
||||
"timeago.js": "^4.0.2",
|
||||
"url-parse": "^1.5.10",
|
||||
"pdfjs-dist": "3.4.120",
|
||||
"@react-pdf-viewer/core": "^3.9.0",
|
||||
"@react-pdf-viewer/default-layout": "^3.9.0"
|
||||
"url-parse": "^1.5.10"
|
||||
}
|
||||
}
|
||||
|
||||
+15
-15
@@ -1302,9 +1302,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-regenerator": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.0.tgz",
|
||||
"integrity": "sha512-LOAozRVbqxEVjSKfhGnuLoE4Kz4Oc5UJzuvFUhSsQzdCdaAQu06mG8zDv2GFSerM62nImUZ7K92vxnQcLSDlCQ==",
|
||||
"version": "7.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.1.tgz",
|
||||
"integrity": "sha512-P0QiV/taaa3kXpLY+sXla5zec4E+4t4Aqc9ggHlfZ7a2cp8/x/Gv08jfwEtn9gnnYIMvHx6aoOZ8XJL8eU71Dg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1643,9 +1643,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.0.tgz",
|
||||
"integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==",
|
||||
"version": "7.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz",
|
||||
"integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2111,9 +2111,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.10.tgz",
|
||||
"integrity": "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==",
|
||||
"version": "24.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.15.tgz",
|
||||
"integrity": "sha512-oaeTSbCef7U/z7rDeJA138xpG3NuKc64/rZ2qmUFkFJmnMsAPaluIifqyWd8hSSMxyP9oie3dLAqYPblag9KgA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3766,9 +3766,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.180",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.180.tgz",
|
||||
"integrity": "sha512-ED+GEyEh3kYMwt2faNmgMB0b8O5qtATGgR4RmRsIp4T6p7B8vdMbIedYndnvZfsaXvSzegtpfqRMDNCjjiSduA==",
|
||||
"version": "1.5.189",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.189.tgz",
|
||||
"integrity": "sha512-y9D1ntS1ruO/pZ/V2FtLE+JXLQe28XoRpZ7QCCo0T8LdQladzdcOVQZH/IWLVJvCw12OGMb6hYOeOAjntCmJRQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
@@ -5885,9 +5885,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup-plugin-visualizer/node_modules/picomatch": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
||||
+48
-35
@@ -1386,9 +1386,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-regenerator": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.0.tgz",
|
||||
"integrity": "sha512-LOAozRVbqxEVjSKfhGnuLoE4Kz4Oc5UJzuvFUhSsQzdCdaAQu06mG8zDv2GFSerM62nImUZ7K92vxnQcLSDlCQ==",
|
||||
"version": "7.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.1.tgz",
|
||||
"integrity": "sha512-P0QiV/taaa3kXpLY+sXla5zec4E+4t4Aqc9ggHlfZ7a2cp8/x/Gv08jfwEtn9gnnYIMvHx6aoOZ8XJL8eU71Dg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.27.1"
|
||||
@@ -1770,9 +1770,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.0.tgz",
|
||||
"integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==",
|
||||
"version": "7.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz",
|
||||
"integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.27.1",
|
||||
@@ -2536,9 +2536,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express-serve-static-core": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz",
|
||||
"integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==",
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.7.tgz",
|
||||
"integrity": "sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
@@ -2597,9 +2597,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.10.tgz",
|
||||
"integrity": "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==",
|
||||
"version": "24.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.15.tgz",
|
||||
"integrity": "sha512-oaeTSbCef7U/z7rDeJA138xpG3NuKc64/rZ2qmUFkFJmnMsAPaluIifqyWd8hSSMxyP9oie3dLAqYPblag9KgA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.8.0"
|
||||
@@ -3000,6 +3000,18 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-import-phases": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz",
|
||||
"integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"acorn": "^8.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-walk": {
|
||||
"version": "8.3.4",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
|
||||
@@ -4534,16 +4546,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/compression": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz",
|
||||
"integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==",
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
|
||||
"integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"compressible": "~2.0.18",
|
||||
"debug": "2.6.9",
|
||||
"negotiator": "~0.6.4",
|
||||
"on-headers": "~1.0.2",
|
||||
"on-headers": "~1.1.0",
|
||||
"safe-buffer": "5.2.1",
|
||||
"vary": "~1.1.2"
|
||||
},
|
||||
@@ -5744,9 +5756,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.180",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.180.tgz",
|
||||
"integrity": "sha512-ED+GEyEh3kYMwt2faNmgMB0b8O5qtATGgR4RmRsIp4T6p7B8vdMbIedYndnvZfsaXvSzegtpfqRMDNCjjiSduA==",
|
||||
"version": "1.5.189",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.189.tgz",
|
||||
"integrity": "sha512-y9D1ntS1ruO/pZ/V2FtLE+JXLQe28XoRpZ7QCCo0T8LdQladzdcOVQZH/IWLVJvCw12OGMb6hYOeOAjntCmJRQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/elliptic": {
|
||||
@@ -9178,9 +9190,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nan": {
|
||||
"version": "2.22.2",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz",
|
||||
"integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==",
|
||||
"version": "2.23.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.23.0.tgz",
|
||||
"integrity": "sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
@@ -9611,9 +9623,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/on-headers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
|
||||
"integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
@@ -11609,9 +11621,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup-plugin-visualizer/node_modules/picomatch": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -13921,21 +13933,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.99.9",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.9.tgz",
|
||||
"integrity": "sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg==",
|
||||
"version": "5.100.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.100.2.tgz",
|
||||
"integrity": "sha512-QaNKAvGCDRh3wW1dsDjeMdDXwZm2vqq3zn6Pvq4rHOEOGSaUMgOOjG2Y9ZbIGzpfkJk9ZYTHpDqgDfeBDcnLaw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.7",
|
||||
"@types/estree": "^1.0.6",
|
||||
"@types/estree": "^1.0.8",
|
||||
"@types/json-schema": "^7.0.15",
|
||||
"@webassemblyjs/ast": "^1.14.1",
|
||||
"@webassemblyjs/wasm-edit": "^1.14.1",
|
||||
"@webassemblyjs/wasm-parser": "^1.14.1",
|
||||
"acorn": "^8.14.0",
|
||||
"acorn": "^8.15.0",
|
||||
"acorn-import-phases": "^1.0.3",
|
||||
"browserslist": "^4.24.0",
|
||||
"chrome-trace-event": "^1.0.2",
|
||||
"enhanced-resolve": "^5.17.1",
|
||||
"enhanced-resolve": "^5.17.2",
|
||||
"es-module-lexer": "^1.2.1",
|
||||
"eslint-scope": "5.1.1",
|
||||
"events": "^3.2.0",
|
||||
@@ -13949,7 +13962,7 @@
|
||||
"tapable": "^2.1.1",
|
||||
"terser-webpack-plugin": "^5.3.11",
|
||||
"watchpack": "^2.4.1",
|
||||
"webpack-sources": "^3.2.3"
|
||||
"webpack-sources": "^3.3.3"
|
||||
},
|
||||
"bin": {
|
||||
"webpack": "bin/webpack.js"
|
||||
|
||||
@@ -541,7 +541,6 @@ export default class VideoViewer extends React.PureComponent {
|
||||
onLoad: () => console.log('Video js loaded in VideoViewer'),
|
||||
onError: (error) => console.error('Video js error in VideoViewer:', error),
|
||||
})} */}
|
||||
|
||||
<div
|
||||
key={(this.props.inEmbed ? 'embed-' : '') + 'player-container'}
|
||||
className={'player-container' + (this.videoSources.length ? '' : ' player-container-error')}
|
||||
@@ -558,6 +557,7 @@ export default class VideoViewer extends React.PureComponent {
|
||||
<SiteConsumer>
|
||||
{(site) => {
|
||||
return React.createElement(VideoJSEmbed, {
|
||||
nextLink: nextLink,
|
||||
data: this.props.data,
|
||||
playerVolume: this.browserCache.get('player-volume'),
|
||||
playerSoundMuted: this.browserCache.get('player-sound-muted'),
|
||||
@@ -592,7 +592,6 @@ export default class VideoViewer extends React.PureComponent {
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* <div
|
||||
key={(this.props.inEmbed ? 'embed-' : '') + 'player-container'}
|
||||
className={'player-container' + (this.videoSources.length ? '' : ' player-container-error')}
|
||||
|
||||
File diff suppressed because one or more lines are too long
+222
-92
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user