fix: Chapter title, next video icon, play video with sound, dark opacity, fix hls, cleanup logs

This commit is contained in:
Yiannis Christodoulou 2025-09-20 02:39:40 +03:00
parent 8d31ff71e0
commit 5fda4610da
18 changed files with 535 additions and 1002 deletions

4
.gitignore vendored
View File

@ -30,6 +30,4 @@ static/video_editor/videos/sample-video-37s.mp4
.DS_Store .DS_Store
static/video_editor/videos/sample-video-10m.mp4 static/video_editor/videos/sample-video-10m.mp4
static/video_editor/videos/sample-video-10s.mp4 static/video_editor/videos/sample-video-10s.mp4
/frontend-tools/chapters-editor/node_modules frontend-tools/video-js/public/videos/sample-video-white.mp4
frontend-tools/chapters-editor/client/public/videos/sample-video.mp4
frontend-tools/video-js/

View File

@ -46,6 +46,70 @@ html {
justify-content: flex-start !important; justify-content: flex-start !important;
padding: 0 12px; padding: 0 12px;
height: 48px; height: 48px;
/* position: relative !important; */
}
/* YouTube-style bottom gradient overlay - covers entire video bottom when controls active */
.video-js::after {
content: "";
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 120px;
background: linear-gradient(
to top,
rgba(0, 0, 0, 0.8) 0%,
rgba(0, 0, 0, 0.6) 25%,
rgba(0, 0, 0, 0.4) 50%,
rgba(0, 0, 0, 0.2) 75%,
transparent 100%
);
pointer-events: none;
opacity: 0;
transition: opacity 0.3s ease;
z-index: 2;
}
/* Show overlay when controls are active - YouTube style */
.video-js.vjs-user-active::after,
.video-js.vjs-paused::after,
.video-js.vjs-ended::after {
opacity: 1;
}
/* Ensure control bar is above the overlay */
.video-js .vjs-control-bar {
z-index: 6 !important;
}
/* Progress control above overlay */
.video-js .vjs-progress-control.vjs-control {
z-index: 7 !important;
}
/* Clean white icons - overlay provides the contrast */
.video-js .vjs-control-bar .vjs-button .vjs-icon-placeholder::before {
color: #ffffff !important;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6) !important;
}
/* Clean white time displays */
.video-js .vjs-control-bar .vjs-time-control {
color: #ffffff !important;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6) !important;
font-weight: 500 !important;
}
/* Keep original progress bar styling */
/* Volume control visibility */
.video-js .vjs-volume-control .vjs-volume-bar {
background: rgba(255, 255, 255, 0.3) !important;
}
.video-js .vjs-volume-control .vjs-volume-level {
background: #ffffff !important;
} }
.video-container { .video-container {
@ -93,6 +157,7 @@ html {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
margin: auto; margin: auto;
display: none !important;
} }
.vjs-next-video-control .vjs-icon-placeholder svg { .vjs-next-video-control .vjs-icon-placeholder svg {
width: 100%; width: 100%;
@ -577,8 +642,7 @@ html {
background-color: rgba(255, 255, 255, 0.1) !important; background-color: rgba(255, 255, 255, 0.1) !important;
} }
.video-js .vjs-settings-button:focus { .video-js .vjs-settings-button:focus {
outline: 2px solid #fff !important; outline: none !important;
outline-offset: 2px !important;
} }
.video-js .vjs-settings-button .vjs-icon-cog { .video-js .vjs-settings-button .vjs-icon-cog {

View File

@ -1,207 +0,0 @@
import React from 'react';
function ChapterList() {
const playlistData = [
{
id: 1,
title: 'Class 12 Chapter 1 || Electric Charges and Fields 01 || Quantisation and Conservation of Charge',
channel: 'Physics Wallah - Alakh Pandey',
duration: '40:13',
thumbnail:
'https://i.ytimg.com/vi/m5VbK66a254/hqdefault.jpg?sqp=-oaymwEmCKgBEF5IWvKriqkDGQgBFQAAiEIYAdgBAeIBCggYEAIYBjgBQAE=&rs=AOn4CLCt2rMJW2jZAYkcDi9wLQOGVkLSTw',
selected: true,
},
{
id: 2,
title: 'Class 12 Chapter 1 || Electric Charges and Fields 01 || Quantisation and Conservation of Charge',
channel: 'Physics Wallah - Alakh Pandey',
duration: '40:13',
thumbnail:
'https://i.ytimg.com/vi/m5VbK66a254/hqdefault.jpg?sqp=-oaymwEmCKgBEF5IWvKriqkDGQgBFQAAiEIYAdgBAeIBCggYEAIYBjgBQAE=&rs=AOn4CLCt2rMJW2jZAYkcDi9wLQOGVkLSTw',
selected: false,
},
{
id: 3,
title: 'Class 12 Chapter 1 || Electric Charges and Fields 01 || Quantisation and Conservation of Charge',
channel: 'Physics Wallah - Alakh Pandey',
duration: '40:13',
thumbnail:
'https://i.ytimg.com/vi/m5VbK66a254/hqdefault.jpg?sqp=-oaymwEmCKgBEF5IWvKriqkDGQgBFQAAiEIYAdgBAeIBCggYEAIYBjgBQAE=&rs=AOn4CLCt2rMJW2jZAYkcDi9wLQOGVkLSTw',
selected: false,
},
{
id: 4,
title: 'Class 12 Chapter 1 || Electric Charges and Fields 01 || Quantisation and Conservation of Charge',
channel: 'Physics Wallah - Alakh Pandey',
duration: '40:13',
thumbnail:
'https://i.ytimg.com/vi/m5VbK66a254/hqdefault.jpg?sqp=-oaymwEmCKgBEF5IWvKriqkDGQgBFQAAiEIYAdgBAeIBCggYEAIYBjgBQAE=&rs=AOn4CLCt2rMJW2jZAYkcDi9wLQOGVkLSTw',
selected: false,
},
{
id: 5,
title: 'Class 12 Chapter 1 || Electric Charges and Fields 01 || Quantisation and Conservation of Charge',
channel: 'Physics Wallah - Alakh Pandey',
duration: '40:13',
thumbnail:
'https://i.ytimg.com/vi/m5VbK66a254/hqdefault.jpg?sqp=-oaymwEmCKgBEF5IWvKriqkDGQgBFQAAiEIYAdgBAeIBCggYEAIYBjgBQAE=&rs=AOn4CLCt2rMJW2jZAYkcDi9wLQOGVkLSTw',
selected: false,
},
{
id: 6,
title: 'Class 12 Chapter 1 || Electric Charges and Fields 01 || Quantisation and Conservation of Charge',
channel: 'Physics Wallah - Alakh Pandey',
duration: '40:13',
thumbnail:
'https://i.ytimg.com/vi/m5VbK66a254/hqdefault.jpg?sqp=-oaymwEmCKgBEF5IWvKriqkDGQgBFQAAiEIYAdgBAeIBCggYEAIYBjgBQAE=&rs=AOn4CLCt2rMJW2jZAYkcDi9wLQOGVkLSTw',
selected: false,
},
{
id: 7,
title: 'Class 12 Chapter 1 || Electric Charges and Fields 01 || Quantisation and Conservation of Charge',
channel: 'Physics Wallah - Alakh Pandey',
duration: '40:13',
thumbnail:
'https://i.ytimg.com/vi/m5VbK66a254/hqdefault.jpg?sqp=-oaymwEmCKgBEF5IWvKriqkDGQgBFQAAiEIYAdgBAeIBCggYEAIYBjgBQAE=&rs=AOn4CLCt2rMJW2jZAYkcDi9wLQOGVkLSTw',
selected: false,
},
{
id: 8,
title: 'Class 12 Chapter 1 || Electric Charges and Fields 01 || Quantisation and Conservation of Charge',
channel: 'Physics Wallah - Alakh Pandey',
duration: '40:13',
thumbnail:
'https://i.ytimg.com/vi/m5VbK66a254/hqdefault.jpg?sqp=-oaymwEmCKgBEF5IWvKriqkDGQgBFQAAiEIYAdgBAeIBCggYEAIYBjgBQAE=&rs=AOn4CLCt2rMJW2jZAYkcDi9wLQOGVkLSTw',
selected: false,
},
{
id: 9,
title: 'Class 12 Chapter 1 || Electric Charges and Fields 01 || Quantisation and Conservation of Charge',
channel: 'Physics Wallah - Alakh Pandey',
duration: '40:13',
thumbnail:
'https://i.ytimg.com/vi/m5VbK66a254/hqdefault.jpg?sqp=-oaymwEmCKgBEF5IWvKriqkDGQgBFQAAiEIYAdgBAeIBCggYEAIYBjgBQAE=&rs=AOn4CLCt2rMJW2jZAYkcDi9wLQOGVkLSTw',
selected: false,
},
];
return (
<div className="video-chapter">
<div className="chapter-head">
<div className="playlist-title">
<div className="chapter-title">
<h3>
<a href="">12 chapter 1 II Electri charges and Fields JEE MAINS/NEET</a>
</h3>
<p>
<a href="">Physics Wallah - Alakh Pandey</a> <span>1 / 17</span>
</p>
</div>
<div className="chapter-close">
<button>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12.7096 12L20.8596 20.15L20.1496 20.86L11.9996 12.71L3.84965 20.86L3.13965 20.15L11.2896 12L3.14965 3.85001L3.85965 3.14001L11.9996 11.29L20.1496 3.14001L20.8596 3.85001L12.7096 12Z"
fill="black"
/>
</svg>
</button>
</div>
</div>
<div className="playlist-action-menu">
<div className="start-action">
<button>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M21.0002 13H22.0002V18L3.93023 18.03L6.55023 20.65L5.84023 21.36L1.99023 17.5L5.84023 13.65L6.55023 14.36L3.88023 17.03L21.0002 17V13ZM3.00023 7.00002L20.1202 6.97002L17.4502 9.64002L18.1602 10.35L22.0102 6.50002L18.1602 2.65002L17.4502 3.36002L20.0702 5.98002L2.00023 6.00002V11H3.00023V7.00002Z"
fill="black"
/>
</svg>
</button>
<button>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M18.15 13.65L22 17.5L18.15 21.35L17.44 20.64L20.09 18H19C16.16 18 13.47 16.77 11.61 14.62L12.37 13.97C14.03 15.89 16.45 17 19 17H20.09L17.44 14.35L18.15 13.65ZM19 7.00003H20.09L17.44 9.65003L18.15 10.36L22 6.51003L18.15 2.66003L17.44 3.37003L20.09 6.00003H19C15.42 6.00003 12.14 7.95003 10.43 11.09L9.7 12.43C8.16 15.25 5.21 17 2 17V18C5.58 18 8.86 16.05 10.57 12.91L11.3 11.57C12.84 8.75003 15.79 7.00003 19 7.00003ZM8.59 9.98003L9.34 9.32003C7.49 7.21003 4.81 6.00003 2 6.00003V7.00003C4.52 7.00003 6.92 8.09003 8.59 9.98003Z"
fill="black"
/>
</svg>
</button>
</div>
<div className="end-action">
<button>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 16.5C12.83 16.5 13.5 17.17 13.5 18C13.5 18.83 12.83 19.5 12 19.5C11.17 19.5 10.5 18.83 10.5 18C10.5 17.17 11.17 16.5 12 16.5ZM10.5 12C10.5 12.83 11.17 13.5 12 13.5C12.83 13.5 13.5 12.83 13.5 12C13.5 11.17 12.83 10.5 12 10.5C11.17 10.5 10.5 11.17 10.5 12ZM10.5 6C10.5 6.83 11.17 7.5 12 7.5C12.83 7.5 13.5 6.83 13.5 6C13.5 5.17 12.83 4.5 12 4.5C11.17 4.5 10.5 5.17 10.5 6Z"
fill="black"
/>
</svg>
</button>
</div>
</div>
</div>
<div className="chapter-body">
<ul>
{playlistData.map((item) => (
<li key={item.id}>
<div className={`playlist-items ${item.selected ? 'selected' : ''}`}>
<a href="#">
<div className="playlist-drag-handle">{item.selected ? '▶' : item.id}</div>
<div className="thumbnail-container">
<img src={item.thumbnail} alt={item.title} />
<span>{item.duration}</span>
</div>
<div className="thumbnail-meta">
<h4>{item.title}</h4>
<span>{item.channel}</span>
</div>
<div className="thumbnail-action">
<button>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 16.5C12.83 16.5 13.5 17.17 13.5 18C13.5 18.83 12.83 19.5 12 19.5C11.17 19.5 10.5 18.83 10.5 18C10.5 17.17 11.17 16.5 12 16.5ZM10.5 12C10.5 12.83 11.17 13.5 12 13.5C12.83 13.5 13.5 12.83 13.5 12C13.5 11.17 12.83 10.5 12 10.5C11.17 10.5 10.5 11.17 10.5 12ZM10.5 6C10.5 6.83 11.17 7.5 12 7.5C12.83 7.5 13.5 6.83 13.5 6C13.5 5.17 12.83 4.5 12 4.5C11.17 4.5 10.5 5.17 10.5 6Z"
fill="currentColor"
/>
</svg>
</button>
</div>
</a>
</div>
</li>
))}
</ul>
</div>
</div>
);
}
export default ChapterList;

View File

@ -11,10 +11,8 @@ class AutoplayToggleButton extends Button {
if (this.userPreferences) { if (this.userPreferences) {
const savedAutoplay = this.userPreferences.getPreference('autoplay'); const savedAutoplay = this.userPreferences.getPreference('autoplay');
this.isAutoplayEnabled = savedAutoplay === true; // Explicit boolean check this.isAutoplayEnabled = savedAutoplay === true; // Explicit boolean check
console.log('Autoplay button initialized with saved preference:', this.isAutoplayEnabled);
} else { } else {
this.isAutoplayEnabled = false; this.isAutoplayEnabled = false;
console.log('Autoplay button initialized with default (no userPreferences):', this.isAutoplayEnabled);
} }
// Bind methods // Bind methods
@ -37,13 +35,10 @@ class AutoplayToggleButton extends Button {
}); });
// Set initial icon state directly // Set initial icon state directly
console.log('AutoplayToggleButton createEl: isAutoplayEnabled =', this.isAutoplayEnabled);
if (this.isAutoplayEnabled) { if (this.isAutoplayEnabled) {
this.iconSpan.innerHTML = `<span style="font-size: 1.2em; color: #ff4444;">●</span>`; this.iconSpan.innerHTML = `<span style="font-size: 1.2em; color: #ff4444;">●</span>`;
console.log('Setting RED icon (autoplay ON)');
} else { } else {
this.iconSpan.innerHTML = `<span style="font-size: 1.2em; color: #ccc;">○</span>`; this.iconSpan.innerHTML = `<span style="font-size: 1.2em; color: #ccc;">○</span>`;
console.log('Setting GRAY icon (autoplay OFF)');
} }
// Create control text span // Create control text span
@ -52,8 +47,6 @@ class AutoplayToggleButton extends Button {
}); });
controlTextSpan.textContent = this.isAutoplayEnabled ? 'Autoplay is on' : 'Autoplay is off'; controlTextSpan.textContent = this.isAutoplayEnabled ? 'Autoplay is on' : 'Autoplay is off';
console.log('✓ Autoplay button created with initial tooltip:', this.isAutoplayEnabled ? 'Autoplay is on' : 'Autoplay is off');
// Append both spans to button // Append both spans to button
button.appendChild(this.iconSpan); button.appendChild(this.iconSpan);
button.appendChild(controlTextSpan); button.appendChild(controlTextSpan);
@ -65,49 +58,46 @@ class AutoplayToggleButton extends Button {
} }
updateIcon() { updateIcon() {
// Add transition and start fade-out // Add transition and start fade-out
this.iconSpan.style.transition = 'opacity 0.1s ease'; this.iconSpan.style.transition = 'opacity 0.1s ease';
this.iconSpan.style.opacity = '0'; this.iconSpan.style.opacity = '0';
// After fade-out complete, update innerHTML and fade back in // After fade-out complete, update innerHTML and fade back in
setTimeout(() => { setTimeout(() => {
if (this.isAutoplayEnabled) { if (this.isAutoplayEnabled) {
this.iconSpan.innerHTML = `<span style="transform: inherit !important; margin: 20px 0 0; font-size: 1.2em; color: #ff4444;"> this.iconSpan.innerHTML = `<span style="transform: inherit !important; margin: 20px 0 0; font-size: 1.2em; color: #ff4444;">
<svg width="198" height="100" viewBox="0 0 198 100" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="198" height="100" viewBox="0 0 198 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect y="18" width="180" height="64" rx="32" fill="white"/> <rect y="18" width="180" height="64" rx="32" fill="white"/>
<rect x="98" width="100" height="100" rx="50" fill="white"/> <rect x="98" width="100" height="100" rx="50" fill="white"/>
<path d="M133 69L163 50L133 31V69ZM138.455 59.0929V40.9071L152.773 50L138.455 59.0929Z" fill="#1C1B1F"/> <path d="M133 69L163 50L133 31V69ZM138.455 59.0929V40.9071L152.773 50L138.455 59.0929Z" fill="#1C1B1F"/>
</svg> </svg>
</span>`; </span>`;
if (this.el()) { if (this.el()) {
this.el().title = 'Autoplay is on'; this.el().title = 'Autoplay is on';
this.el().setAttribute('aria-label', 'Autoplay is on'); this.el().setAttribute('aria-label', 'Autoplay is on');
const controlText = this.el().querySelector('.vjs-control-text'); const controlText = this.el().querySelector('.vjs-control-text');
if (controlText) controlText.textContent = 'Autoplay is on'; if (controlText) controlText.textContent = 'Autoplay is on';
console.log('✓ Autoplay tooltip updated to: "Autoplay is on"'); }
} } else {
} else { this.iconSpan.innerHTML = `<span style="transform: inherit !important; margin: 20px 0 0; font-size: 1.2em; color: #ccc;">
this.iconSpan.innerHTML = `<span style="transform: inherit !important; margin: 20px 0 0; font-size: 1.2em; color: #ccc;">
<svg width="198" height="100" viewBox="0 0 198 100" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="198" height="100" viewBox="0 0 198 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="18" y="18" width="180" height="64" rx="32" fill="white"/> <rect x="18" y="18" width="180" height="64" rx="32" fill="white"/>
<rect width="100" height="100" rx="50" fill="white"/> <rect width="100" height="100" rx="50" fill="white"/>
<path d="M52.1429 65V35H65V65H52.1429ZM35 65V35H47.8571V65H35ZM56.4286 60.7143H60.7143V39.2857H56.4286V60.7143ZM39.2857 60.7143H43.5714V39.2857H39.2857V60.7143Z" fill="#1C1B1F"/> <path d="M52.1429 65V35H65V65H52.1429ZM35 65V35H47.8571V65H35ZM56.4286 60.7143H60.7143V39.2857H56.4286V60.7143ZM39.2857 60.7143H43.5714V39.2857H39.2857V60.7143Z" fill="#1C1B1F"/>
</svg> </svg>
</span>`; </span>`;
if (this.el()) { if (this.el()) {
this.el().title = 'Autoplay is off'; this.el().title = 'Autoplay is off';
this.el().setAttribute('aria-label', 'Autoplay is off'); this.el().setAttribute('aria-label', 'Autoplay is off');
const controlText = this.el().querySelector('.vjs-control-text'); const controlText = this.el().querySelector('.vjs-control-text');
if (controlText) controlText.textContent = 'Autoplay is off'; if (controlText) controlText.textContent = 'Autoplay is off';
console.log('✓ Autoplay tooltip updated to: "Autoplay is off"'); }
} }
}
// Fade back in
this.iconSpan.style.opacity = '1';
}, 100);
}
// Fade back in
this.iconSpan.style.opacity = '1';
}, 100);
}
handleClick() { handleClick() {
// Toggle autoplay state // Toggle autoplay state
@ -116,15 +106,11 @@ class AutoplayToggleButton extends Button {
// Save preference if userPreferences is available // Save preference if userPreferences is available
if (this.userPreferences) { if (this.userPreferences) {
this.userPreferences.setAutoplayPreference(this.isAutoplayEnabled); this.userPreferences.setAutoplayPreference(this.isAutoplayEnabled);
console.log('Autoplay preference saved to localStorage:', this.isAutoplayEnabled);
} }
// Update icon and accessibility attributes // Update icon and accessibility attributes
this.updateIcon(); this.updateIcon();
console.log('Autoplay toggled:', this.isAutoplayEnabled ? 'ON' : 'OFF');
console.log('✓ Tooltip should now show:', this.isAutoplayEnabled ? 'Autoplay is on' : 'Autoplay is off');
// Trigger custom event for other components to listen to // Trigger custom event for other components to listen to
this.player().trigger('autoplayToggle', { autoplay: this.isAutoplayEnabled }); this.player().trigger('autoplayToggle', { autoplay: this.isAutoplayEnabled });
} }
@ -141,38 +127,38 @@ class AutoplayToggleButton extends Button {
let touchHandled = false; let touchHandled = false;
// Touch start // Touch start
button.addEventListener('touchstart', (e) => { button.addEventListener(
touchStartTime = Date.now(); 'touchstart',
touchHandled = false; (e) => {
}, { passive: true }); touchStartTime = Date.now();
touchHandled = false;
},
{ passive: true }
);
// Touch end // Touch end
button.addEventListener('touchend', (e) => { button.addEventListener(
const touchDuration = Date.now() - touchStartTime; 'touchend',
(e) => {
const touchDuration = Date.now() - touchStartTime;
// Only show tooltip for quick taps (not swipes) // Only show tooltip for quick taps (not swipes)
if (touchDuration < 500) { if (touchDuration < 500) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
// Show tooltip // Show tooltip
button.classList.add('touch-active'); button.classList.add('touch-active');
touchHandled = true; touchHandled = true;
// Hide tooltip after delay // Hide tooltip after delay
setTimeout(() => { setTimeout(() => {
button.classList.remove('touch-active'); button.classList.remove('touch-active');
}, 2000); }, 2000);
} }
}, { passive: false }); },
{ passive: false }
// Click fallback for desktop );
button.addEventListener('click', (e) => {
if (!touchHandled) {
// This is a desktop click, tooltip will show on hover
console.log('Desktop click on autoplay button');
}
});
} }
} }

View File

@ -47,7 +47,6 @@ class CustomChaptersOverlay extends Component {
createOverlay() { createOverlay() {
if (!this.chaptersData || this.chaptersData.length === 0) { if (!this.chaptersData || this.chaptersData.length === 0) {
console.log('⚠ No chapters data available for overlay');
return; return;
} }
@ -232,8 +231,6 @@ class CustomChaptersOverlay extends Component {
playerEl.appendChild(this.overlay); playerEl.appendChild(this.overlay);
this.player().on('timeupdate', this.updateCurrentChapter); this.player().on('timeupdate', this.updateCurrentChapter);
console.log('✓ Custom chapters overlay created');
} }
setupChaptersButton() { setupChaptersButton() {

View File

@ -79,15 +79,6 @@ class CustomRemainingTime extends Component {
return `${customPrefix}${timeString}${customSuffix}`; return `${customPrefix}${timeString}${customSuffix}`;
} }
/**
* Add click handler for additional functionality
*/
handleClick() {
// Example: Toggle between different time formats
console.log('Time display clicked');
// Could toggle between current/duration vs remaining time
}
/** /**
* Component disposal cleanup * Component disposal cleanup
*/ */

View File

@ -363,32 +363,19 @@ class CustomSettingsMenu extends Component {
const player = this.player(); const player = this.player();
const tracks = player.textTracks(); const tracks = player.textTracks();
let currentSubtitleLabel = 'Off'; let currentSubtitleLabel = 'Off';
let activeTrack = null;
// Find the active subtitle track // Find the active subtitle track
for (let i = 0; i < tracks.length; i++) { for (let i = 0; i < tracks.length; i++) {
const t = tracks[i]; const t = tracks[i];
if (t.kind === 'subtitles' && t.mode === 'showing') { if (t.kind === 'subtitles' && t.mode === 'showing') {
currentSubtitleLabel = t.label || t.language || 'Subtitles'; currentSubtitleLabel = t.label || t.language || 'Subtitles';
activeTrack = t;
break; break;
} }
} }
const currentSubtitlesDisplay = this.settingsOverlay.querySelector('.current-subtitles'); const currentSubtitlesDisplay = this.settingsOverlay.querySelector('.current-subtitles');
if (currentSubtitlesDisplay) { if (currentSubtitlesDisplay) {
const oldValue = currentSubtitlesDisplay.textContent;
currentSubtitlesDisplay.textContent = currentSubtitleLabel; currentSubtitlesDisplay.textContent = currentSubtitleLabel;
// Only log if the value actually changed
if (oldValue !== currentSubtitleLabel) {
console.log(`Updated current subtitle display: "${oldValue}" → "${currentSubtitleLabel}"`);
if (activeTrack) {
console.log(
`Active track details: language="${activeTrack.language}", label="${activeTrack.label}", mode="${activeTrack.mode}"`
);
}
}
} }
} catch (error) { } catch (error) {
console.error('Error updating current subtitle display:', error); console.error('Error updating current subtitle display:', error);
@ -402,7 +389,6 @@ class CustomSettingsMenu extends Component {
// Listen for real-time subtitle changes // Listen for real-time subtitle changes
this.player().on('texttrackchange', () => { this.player().on('texttrackchange', () => {
console.log('Text track changed - updating subtitle display');
this.updateCurrentSubtitleDisplay(); this.updateCurrentSubtitleDisplay();
// Also refresh the subtitle submenu to show correct selection // Also refresh the subtitle submenu to show correct selection
this.refreshSubtitlesSubmenu(); this.refreshSubtitlesSubmenu();
@ -516,7 +502,6 @@ class CustomSettingsMenu extends Component {
const fullscreenIndex = controlBar.children().indexOf(fullscreenToggle); const fullscreenIndex = controlBar.children().indexOf(fullscreenToggle);
controlBar.removeChild(this.settingsButton); controlBar.removeChild(this.settingsButton);
controlBar.addChild(this.settingsButton, {}, fullscreenIndex + 1); controlBar.addChild(this.settingsButton, {}, fullscreenIndex + 1);
console.log('✓ Settings button positioned after fullscreen toggle');
}, 50); }, 50);
} }
} }
@ -872,8 +857,6 @@ class CustomSettingsMenu extends Component {
// Close only the speed submenu (keep overlay open) // Close only the speed submenu (keep overlay open)
this.speedSubmenu.style.display = 'none'; this.speedSubmenu.style.display = 'none';
console.log('Playback speed preference saved:', speed);
} }
handleQualityChange(value, qualityOption) { handleQualityChange(value, qualityOption) {
@ -1063,8 +1046,6 @@ class CustomSettingsMenu extends Component {
// Close only the quality submenu (keep overlay open) // Close only the quality submenu (keep overlay open)
if (this.qualitySubmenu) this.qualitySubmenu.style.display = 'none'; if (this.qualitySubmenu) this.qualitySubmenu.style.display = 'none';
console.log('Quality preference saved:', value);
} }
handleSubtitleChange(lang, optionEl) { handleSubtitleChange(lang, optionEl) {

View File

@ -24,7 +24,7 @@ class NextVideoButton extends Button {
// Create SVG that matches Video.js icon dimensions // Create SVG that matches Video.js icon dimensions
iconSpan.innerHTML = ` iconSpan.innerHTML = `
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="34" height="34" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 34L28.1667 24L14 14V34ZM30.6667 14V34H34V14H30.6667Z" fill="currentColor"/> <path d="M14 34L28.1667 24L14 14V34ZM30.6667 14V34H34V14H30.6667Z" fill="currentColor"/>
</svg> </svg>
@ -44,7 +44,6 @@ class NextVideoButton extends Button {
} }
handleClick() { handleClick() {
// console.log('NextVideoButton handleClick', this.nextLink);
this.player().trigger('nextVideo'); this.player().trigger('nextVideo');
} }
} }

View File

@ -49,7 +49,7 @@ class ChapterMarkers extends Component {
this.chaptersData.push({ this.chaptersData.push({
startTime: cue.startTime, startTime: cue.startTime,
endTime: cue.endTime, endTime: cue.endTime,
chapterTitle: cue.chapterTitle, chapterTitle: cue.text,
}); });
} }
@ -217,17 +217,17 @@ class ChapterMarkers extends Component {
// Update text content without rebuilding DOM // Update text content without rebuilding DOM
this.chapterTitle.textContent = currentChapter.chapterTitle; this.chapterTitle.textContent = currentChapter.chapterTitle;
this.chapterInfo.textContent = `Chapter: ${startTime} - ${endTime}`; this.chapterInfo.textContent = `${startTime} - ${endTime}`;
// this.positionInfo.textContent = `Position: ${timeAtPosition}`; // this.positionInfo.textContent = `Position: ${timeAtPosition}`;
// Update sprite thumbnail // Update sprite thumbnail
this.updateSpriteThumbnail(currentTime); this.updateSpriteThumbnail(currentTime);
this.chapterImage.style.display = 'block'; this.chapterImage.style.display = 'block';
} else { } else {
const timeAtPosition = this.formatTime(currentTime); // const timeAtPosition = this.formatTime(currentTime);
this.chapterTitle.textContent = 'No Chapter'; this.chapterTitle.textContent = '';
this.chapterInfo.textContent = ''; this.chapterInfo.textContent = '';
this.positionInfo.textContent = `Position: ${timeAtPosition}`; // this.positionInfo.textContent = `Position: ${timeAtPosition}`;
// Still show sprite thumbnail even when not in a chapter // Still show sprite thumbnail even when not in a chapter
this.updateSpriteThumbnail(currentTime); this.updateSpriteThumbnail(currentTime);
@ -274,7 +274,6 @@ class ChapterMarkers extends Component {
if (!this.previewSprite || !this.previewSprite.url) { if (!this.previewSprite || !this.previewSprite.url) {
// Hide image if no sprite data available // Hide image if no sprite data available
this.chapterImage.style.display = 'none'; this.chapterImage.style.display = 'none';
console.log('No sprite data available:', this.previewSprite);
return; return;
} }
@ -304,10 +303,6 @@ class ChapterMarkers extends Component {
const xPos = -(frameCol * width); const xPos = -(frameCol * width);
const yPos = -(frameRow * height); const yPos = -(frameRow * height);
console.log(
`Time: ${currentTime}s, Duration: ${this.player().duration()}s, Interval: ${frameInterval}s, Frame: ${frameIndex}/${maxFrames - 1}, Row: ${frameRow}, Col: ${frameCol}, Pos: ${xPos}px ${yPos}px, URL: ${url}`
);
// Apply sprite background // Apply sprite background
this.chapterImage.style.backgroundImage = `url("${url}")`; this.chapterImage.style.backgroundImage = `url("${url}")`;
this.chapterImage.style.backgroundPosition = `${xPos}px ${yPos}px`; this.chapterImage.style.backgroundPosition = `${xPos}px ${yPos}px`;
@ -321,7 +316,6 @@ class ChapterMarkers extends Component {
if (frameIndex >= 3 && currentTime > 30) { if (frameIndex >= 3 && currentTime > 30) {
const fallbackYPos = -(2 * height); // Frame 2 (20-30s range) const fallbackYPos = -(2 * height); // Frame 2 (20-30s range)
this.chapterImage.style.backgroundPosition = `${xPos}px ${fallbackYPos}px`; this.chapterImage.style.backgroundPosition = `${xPos}px ${fallbackYPos}px`;
console.log(`Fallback: Using frame 2 instead of frame ${frameIndex} for time ${currentTime}s`);
} }
} }

View File

@ -33,7 +33,6 @@ class SpritePreview extends Component {
// Only setup if we have sprite data // Only setup if we have sprite data
if (!this.previewSprite || !this.previewSprite.url) { if (!this.previewSprite || !this.previewSprite.url) {
console.log('No sprite data available for preview:', this.previewSprite);
return; return;
} }
@ -161,7 +160,6 @@ class SpritePreview extends Component {
if (!this.previewSprite || !this.previewSprite.url) { if (!this.previewSprite || !this.previewSprite.url) {
// Hide image if no sprite data available // Hide image if no sprite data available
this.spriteImage.style.display = 'none'; this.spriteImage.style.display = 'none';
console.log('No sprite data available:', this.previewSprite);
return; return;
} }
@ -191,10 +189,6 @@ class SpritePreview extends Component {
const xPos = -(frameCol * width); const xPos = -(frameCol * width);
const yPos = -(frameRow * height); const yPos = -(frameRow * height);
console.log(
`Sprite Preview - Time: ${currentTime}s, Duration: ${this.player().duration()}s, Interval: ${frameInterval}s, Frame: ${frameIndex}/${maxFrames - 1}, Row: ${frameRow}, Col: ${frameCol}, Pos: ${xPos}px ${yPos}px, URL: ${url}`
);
// Apply sprite background // Apply sprite background
this.spriteImage.style.backgroundImage = `url("${url}")`; this.spriteImage.style.backgroundImage = `url("${url}")`;
this.spriteImage.style.backgroundPosition = `${xPos}px ${yPos}px`; this.spriteImage.style.backgroundPosition = `${xPos}px ${yPos}px`;
@ -211,7 +205,6 @@ class SpritePreview extends Component {
if (frameIndex >= 3 && currentTime > 30) { if (frameIndex >= 3 && currentTime > 30) {
const fallbackYPos = -(2 * height); // Frame 2 (20-30s range) const fallbackYPos = -(2 * height); // Frame 2 (20-30s range)
this.spriteImage.style.backgroundPosition = `${xPos}px ${fallbackYPos}px`; this.spriteImage.style.backgroundPosition = `${xPos}px ${fallbackYPos}px`;
console.log(`Fallback: Using frame 2 instead of frame ${frameIndex} for time ${currentTime}s`);
} }
} }

View File

@ -118,8 +118,6 @@ class AutoplayCountdownOverlay extends Component {
this.handlePlayNext(); this.handlePlayNext();
} }
}, 1000); }, 1000);
console.log('Autoplay countdown started:', this.countdownSeconds, 'seconds');
} }
stopCountdown() { stopCountdown() {
@ -129,7 +127,6 @@ class AutoplayCountdownOverlay extends Component {
this.countdownInterval = null; this.countdownInterval = null;
} }
this.hide(); this.hide();
console.log('Autoplay countdown stopped');
} }
updateCountdownDisplay() { updateCountdownDisplay() {
@ -140,7 +137,6 @@ class AutoplayCountdownOverlay extends Component {
} }
handlePlayNext() { handlePlayNext() {
console.log('Autoplay: Playing next video immediately');
try { try {
this.stopCountdown(); this.stopCountdown();
this.onPlayNext(); this.onPlayNext();
@ -150,7 +146,6 @@ class AutoplayCountdownOverlay extends Component {
} }
handleCancel() { handleCancel() {
console.log('Autoplay: Cancelled by user');
try { try {
this.stopCountdown(); this.stopCountdown();
this.onCancel(); this.onCancel();

View File

@ -14,12 +14,6 @@ class EndScreenOverlay extends Component {
// Now set the instance property after super() completes // Now set the instance property after super() completes
this.relatedVideos = options && options.relatedVideos ? options.relatedVideos : []; this.relatedVideos = options && options.relatedVideos ? options.relatedVideos : [];
// console.log(
// 'EndScreenOverlay created with',
// this.relatedVideos.length,
// 'related videos'
// );
} }
createEl() { createEl() {
@ -30,12 +24,6 @@ class EndScreenOverlay extends Component {
const maxVideos = this.getMaxVideosForScreen(); const maxVideos = this.getMaxVideosForScreen();
const videosToShow = relatedVideos.slice(0, maxVideos); const videosToShow = relatedVideos.slice(0, maxVideos);
// console.log(
// 'Creating end screen with',
// videosToShow.length,
// 'related videos'
// );
const overlay = super.createEl('div', { const overlay = super.createEl('div', {
className: 'vjs-end-screen-overlay', className: 'vjs-end-screen-overlay',
}); });
@ -88,7 +76,7 @@ class EndScreenOverlay extends Component {
onerror: () => { onerror: () => {
// Fallback to placeholder if image fails to load // Fallback to placeholder if image fails to load
thumbnail.src = this.getPlaceholderImage(video.title); thumbnail.src = this.getPlaceholderImage(video.title);
} },
}); });
const overlay = videojs.dom.createEl('div', { const overlay = videojs.dom.createEl('div', {
@ -158,8 +146,16 @@ class EndScreenOverlay extends Component {
// Generate a placeholder image using a service or create a data URL // Generate a placeholder image using a service or create a data URL
// For now, we'll use a simple colored placeholder based on the title // For now, we'll use a simple colored placeholder based on the title
const colors = [ const colors = [
'#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#FF6B6B',
'#DDA0DD', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E9' '#4ECDC4',
'#45B7D1',
'#96CEB4',
'#FFEAA7',
'#DDA0DD',
'#98D8C8',
'#F7DC6F',
'#BB8FCE',
'#85C1E9',
]; ];
// Use title hash to consistently assign colors // Use title hash to consistently assign colors
@ -205,11 +201,11 @@ class EndScreenOverlay extends Component {
if (width >= 1200) { if (width >= 1200) {
return 12; // 4x3 grid for large desktop return 12; // 4x3 grid for large desktop
} else if (width >= 1024) { } else if (width >= 1024) {
return 9; // 3x3 grid for desktop return 9; // 3x3 grid for desktop
} else if (width >= 768) { } else if (width >= 768) {
return 6; // 3x2 grid for tablet return 6; // 3x2 grid for tablet
} else { } else {
return 4; // 2x2 grid for mobile return 4; // 2x2 grid for mobile
} }
} }
@ -221,7 +217,7 @@ class EndScreenOverlay extends Component {
author: 'Bro Code', author: 'Bro Code',
views: '2.1M views', views: '2.1M views',
duration: 1800, duration: 1800,
thumbnail: 'https://img.youtube.com/vi/dGcsHMXbSOA/maxresdefault.jpg' thumbnail: 'https://img.youtube.com/vi/dGcsHMXbSOA/maxresdefault.jpg',
}, },
{ {
id: 'sample2', id: 'sample2',
@ -229,7 +225,7 @@ class EndScreenOverlay extends Component {
author: 'Tech Tutorials', author: 'Tech Tutorials',
views: '850K views', views: '850K views',
duration: 1200, duration: 1200,
thumbnail: 'https://img.youtube.com/vi/WZQc7RUAg18/maxresdefault.jpg' thumbnail: 'https://img.youtube.com/vi/WZQc7RUAg18/maxresdefault.jpg',
}, },
{ {
id: 'sample3', id: 'sample3',
@ -237,7 +233,7 @@ class EndScreenOverlay extends Component {
author: 'Web Dev Academy', author: 'Web Dev Academy',
views: '1.2M views', views: '1.2M views',
duration: 2400, duration: 2400,
thumbnail: 'https://img.youtube.com/vi/0xMQfnTU6oo/maxresdefault.jpg' thumbnail: 'https://img.youtube.com/vi/0xMQfnTU6oo/maxresdefault.jpg',
}, },
{ {
id: 'sample4', id: 'sample4',
@ -245,7 +241,7 @@ class EndScreenOverlay extends Component {
author: 'Code Master', author: 'Code Master',
views: '650K views', views: '650K views',
duration: 3600, duration: 3600,
thumbnail: 'https://img.youtube.com/vi/fBNz6F-Cowg/maxresdefault.jpg' thumbnail: 'https://img.youtube.com/vi/fBNz6F-Cowg/maxresdefault.jpg',
}, },
{ {
id: 'sample5', id: 'sample5',
@ -253,7 +249,7 @@ class EndScreenOverlay extends Component {
author: 'Frontend Pro', author: 'Frontend Pro',
views: '980K views', views: '980K views',
duration: 2800, duration: 2800,
thumbnail: 'https://img.youtube.com/vi/qZXt1Aom3Cs/maxresdefault.jpg' thumbnail: 'https://img.youtube.com/vi/qZXt1Aom3Cs/maxresdefault.jpg',
}, },
{ {
id: 'sample6', id: 'sample6',
@ -261,7 +257,7 @@ class EndScreenOverlay extends Component {
author: 'Data Academy', author: 'Data Academy',
views: '1.5M views', views: '1.5M views',
duration: 4200, duration: 4200,
thumbnail: 'https://img.youtube.com/vi/ua-CiDNNj30/maxresdefault.jpg' thumbnail: 'https://img.youtube.com/vi/ua-CiDNNj30/maxresdefault.jpg',
}, },
{ {
id: 'sample7', id: 'sample7',
@ -269,7 +265,7 @@ class EndScreenOverlay extends Component {
author: 'TypeScript Expert', author: 'TypeScript Expert',
views: '720K views', views: '720K views',
duration: 2100, duration: 2100,
thumbnail: 'https://img.youtube.com/vi/BwuLxPH8IDs/maxresdefault.jpg' thumbnail: 'https://img.youtube.com/vi/BwuLxPH8IDs/maxresdefault.jpg',
}, },
{ {
id: 'sample8', id: 'sample8',
@ -277,7 +273,7 @@ class EndScreenOverlay extends Component {
author: 'Database Pro', author: 'Database Pro',
views: '890K views', views: '890K views',
duration: 1800, duration: 1800,
thumbnail: 'https://img.youtube.com/vi/-56x56UppqQ/maxresdefault.jpg' thumbnail: 'https://img.youtube.com/vi/-56x56UppqQ/maxresdefault.jpg',
}, },
{ {
id: 'sample9', id: 'sample9',
@ -285,7 +281,7 @@ class EndScreenOverlay extends Component {
author: 'DevOps Master', author: 'DevOps Master',
views: '1.1M views', views: '1.1M views',
duration: 3200, duration: 3200,
thumbnail: 'https://img.youtube.com/vi/pTFZFxd4hOI/maxresdefault.jpg' thumbnail: 'https://img.youtube.com/vi/pTFZFxd4hOI/maxresdefault.jpg',
}, },
{ {
id: 'sample10', id: 'sample10',
@ -293,7 +289,7 @@ class EndScreenOverlay extends Component {
author: 'Cloud Expert', author: 'Cloud Expert',
views: '1.3M views', views: '1.3M views',
duration: 4500, duration: 4500,
thumbnail: 'https://img.youtube.com/vi/ITcXLS3h2qU/maxresdefault.jpg' thumbnail: 'https://img.youtube.com/vi/ITcXLS3h2qU/maxresdefault.jpg',
}, },
{ {
id: 'sample11', id: 'sample11',
@ -301,7 +297,7 @@ class EndScreenOverlay extends Component {
author: 'API Specialist', author: 'API Specialist',
views: '680K views', views: '680K views',
duration: 2600, duration: 2600,
thumbnail: 'https://img.youtube.com/vi/ed8SzALpx1Q/maxresdefault.jpg' thumbnail: 'https://img.youtube.com/vi/ed8SzALpx1Q/maxresdefault.jpg',
}, },
{ {
id: 'sample12', id: 'sample12',
@ -309,8 +305,8 @@ class EndScreenOverlay extends Component {
author: 'AI Academy', author: 'AI Academy',
views: '2.3M views', views: '2.3M views',
duration: 5400, duration: 5400,
thumbnail: 'https://img.youtube.com/vi/i_LwzRVP7bg/maxresdefault.jpg' thumbnail: 'https://img.youtube.com/vi/i_LwzRVP7bg/maxresdefault.jpg',
} },
]; ];
} }

View File

@ -26,9 +26,6 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
// Environment-based development mode configuration // Environment-based development mode configuration
const isDevMode = import.meta.env.VITE_DEV_MODE === 'true' || window.location.hostname.includes('vercel.app'); const isDevMode = import.meta.env.VITE_DEV_MODE === 'true' || window.location.hostname.includes('vercel.app');
console.log('isDevMode', isDevMode);
console.log('window.location.hostname', window.location.hostname);
// Safely access window.MEDIA_DATA with fallback using useMemo // Safely access window.MEDIA_DATA with fallback using useMemo
const mediaData = useMemo( const mediaData = useMemo(
() => () =>
@ -40,7 +37,7 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
poster_url: poster_url:
'https://demo.mediacms.io/media/original/thumbnails/user/markos/7dedcb56bde9463dbc0766768a99be0f_C8E5GFY.20250605_110647.mp4.jpg', 'https://demo.mediacms.io/media/original/thumbnails/user/markos/7dedcb56bde9463dbc0766768a99be0f_C8E5GFY.20250605_110647.mp4.jpg',
chapter_data: [ chapter_data: [
{ startTime: '00:00:00.000', endTime: '00:00:24.295', chapterTitle: 'A1 test' }, { startTime: '00:00:00.000', endTime: '00:00:08.295', chapterTitle: 'A1 test' },
{ startTime: '00:00:24.295', endTime: '00:00:48.590', chapterTitle: 'A2 of Marine Life' }, { startTime: '00:00:24.295', endTime: '00:00:48.590', chapterTitle: 'A2 of Marine Life' },
{ {
startTime: '00:00:48.590', startTime: '00:00:48.590',
@ -1052,6 +1049,7 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
siteUrl: '', siteUrl: '',
nextLink: 'https://demo.mediacms.io/view?m=YjGJafibO', nextLink: 'https://demo.mediacms.io/view?m=YjGJafibO',
urlAutoplay: true, urlAutoplay: true,
urlMuted: false,
}, },
[] []
); );
@ -1083,14 +1081,6 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
return hours * 3600 + minutes * 60 + seconds; return hours * 3600 + minutes * 60 + seconds;
}; };
// Test the conversion function
if (isDevMode) {
console.log('Testing time conversion:');
console.log('00:00:24.295 ->', convertTimeStringToSeconds('00:00:24.295')); // Should be 24.295
console.log('00:01:30.500 ->', convertTimeStringToSeconds('00:01:30.500')); // Should be 90.5
console.log('01:00:00.000 ->', convertTimeStringToSeconds('01:00:00.000')); // Should be 3600
}
// Convert chapters data from backend format to required format with memoization // Convert chapters data from backend format to required format with memoization
const convertChaptersData = useMemo(() => { const convertChaptersData = useMemo(() => {
return (rawChaptersData) => { return (rawChaptersData) => {
@ -1098,8 +1088,6 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
return []; return [];
} }
console.log('Converting raw chapters data:', rawChaptersData);
const convertedData = rawChaptersData.map((chapter) => ({ const convertedData = rawChaptersData.map((chapter) => ({
startTime: convertTimeStringToSeconds(chapter.startTime), startTime: convertTimeStringToSeconds(chapter.startTime),
endTime: convertTimeStringToSeconds(chapter.endTime), endTime: convertTimeStringToSeconds(chapter.endTime),
@ -1178,26 +1166,80 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
return 'video/mp4'; return 'video/mp4';
}; };
// Get user's quality preference for dependency tracking
const userQualityPreference = userPreferences.current.getQualityPreference();
// Get video data from mediaData // Get video data from mediaData
const currentVideo = useMemo(() => { const currentVideo = useMemo(() => {
// Get video sources based on available data // Get video sources based on available data and user preferences
const getVideoSources = () => { const getVideoSources = () => {
// Use the extracted quality preference
const userQuality = userQualityPreference;
// Check if HLS info is available and not empty // Check if HLS info is available and not empty
if (mediaData.data?.hls_info && mediaData.data.hls_info.master_file) { if (mediaData.data?.hls_info) {
// Use master file as the primary source (auto quality) // If user prefers auto quality or master file doesn't exist for specific quality
return [ if (userQuality === 'auto' && mediaData.data.hls_info.master_file) {
{ return [
src: mediaData.siteUrl + mediaData.data.hls_info.master_file, {
type: 'application/x-mpegURL', // HLS MIME type src: mediaData.siteUrl + mediaData.data.hls_info.master_file,
label: 'Auto', type: 'application/x-mpegURL', // HLS MIME type
}, label: 'Auto',
]; },
];
}
// If user has selected a specific quality, try to use that playlist
if (userQuality !== 'auto') {
const qualityKey = `${userQuality.replace('p', '')}_playlist`;
if (mediaData.data.hls_info[qualityKey]) {
return [
{
src: mediaData.data.hls_info[qualityKey],
type: 'application/x-mpegURL', // HLS MIME type
label: `${userQuality}p`,
},
];
}
}
// Fallback to master file if specific quality not available
if (mediaData.data.hls_info.master_file) {
return [
{
src: mediaData.siteUrl + mediaData.data.hls_info.master_file,
type: 'application/x-mpegURL', // HLS MIME type
label: 'Auto',
},
];
}
} }
// Fallback to encoded qualities if available // Fallback to encoded qualities if available
if (mediaData.data?.encodings_info) { if (mediaData.data?.encodings_info) {
const sources = [];
const encodings = mediaData.data.encodings_info; const encodings = mediaData.data.encodings_info;
const userQuality = userQualityPreference;
// If user has selected a specific quality, try to use that encoding first
if (userQuality !== 'auto') {
const qualityNumber = userQuality.replace('p', ''); // Remove 'p' from '240p' -> '240'
if (
encodings[qualityNumber] &&
encodings[qualityNumber].h264 &&
encodings[qualityNumber].h264.url
) {
return [
{
src: encodings[qualityNumber].h264.url,
type: getMimeType(encodings[qualityNumber].h264.url, mediaData.data?.media_type),
label: `${qualityNumber}p`,
},
];
}
}
// If auto quality or specific quality not available, return all available qualities
const sources = [];
// Get available qualities dynamically from encodings_info // Get available qualities dynamically from encodings_info
const availableQualities = Object.keys(encodings) const availableQualities = Object.keys(encodings)
@ -1231,14 +1273,14 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
// Default sample video // Default sample video
return [ return [
/* {
src: '/videos/sample-video.mp4',
type: 'video/mp4',
}, */
{ {
src: '/videos/sample-video-white.mp4',
type: 'video/mp4',
},
/* {
src: '/videos/sample-video.mp3', src: '/videos/sample-video.mp3',
type: 'audio/mpeg', type: 'audio/mpeg',
}, }, */
]; ];
}; };
@ -1250,9 +1292,10 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
related_media: mediaData.data?.related_media || [], related_media: mediaData.data?.related_media || [],
nextLink: mediaData?.nextLink || null, nextLink: mediaData?.nextLink || null,
urlAutoplay: mediaData?.urlAutoplay || true, urlAutoplay: mediaData?.urlAutoplay || true,
urlMuted: mediaData?.urlMuted || false,
sources: getVideoSources(), sources: getVideoSources(),
}; };
}, [mediaData]); }, [mediaData, userQualityPreference]);
// Compute available qualities. Prefer JSON (mediaData.data.qualities), otherwise build from encodings_info or current source. // Compute available qualities. Prefer JSON (mediaData.data.qualities), otherwise build from encodings_info or current source.
const availableQualities = useMemo(() => { const availableQualities = useMemo(() => {
@ -1446,14 +1489,8 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
})) }))
: []; : [];
console.log('mediaData?.data?.thumbnail_time', mediaData?.data?.thumbnail_time);
console.log('mediaData?.data?.sprites_url', mediaData?.data?.sprites_url);
console.log('mediaData', mediaData);
// Function to navigate to next video // Function to navigate to next video
const goToNextVideo = () => { const goToNextVideo = () => {
console.log('Next video functionality disabled for single video mode');
if (mediaData.onClickNextCallback && typeof mediaData.onClickNextCallback === 'function') { if (mediaData.onClickNextCallback && typeof mediaData.onClickNextCallback === 'function') {
mediaData.onClickNextCallback(); mediaData.onClickNextCallback();
} }
@ -1464,7 +1501,6 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
if (videoRef.current && !playerRef.current) { if (videoRef.current && !playerRef.current) {
// Check if element is already a Video.js player // Check if element is already a Video.js player
if (videoRef.current.player) { if (videoRef.current.player) {
// console.log('Video.js already initialized on this element');
return; return;
} }
@ -1478,8 +1514,8 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
controls: true, controls: true,
// Player dimensions - removed for responsive design // Player dimensions - removed for responsive design
// Autoplay behavior: Use 'muted' to comply with browser policies // Autoplay behavior: Try unmuted first, fallback to muted if needed
autoplay: 'muted', // Auto-start muted to comply with browser policies (true/false, play, muted, any) autoplay: true, // Try unmuted autoplay first (true/false, play, muted, any)
// Start video over when it ends // Start video over when it ends
loop: false, loop: false,
@ -1741,9 +1777,6 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
}); });
// Event listeners // Event listeners
/* playerRef.current.on('ready', () => {
console.log('Video.js player ready');
}); */
playerRef.current.ready(() => { playerRef.current.ready(() => {
// Apply user preferences to player // Apply user preferences to player
userPreferences.current.applyToPlayer(playerRef.current); userPreferences.current.applyToPlayer(playerRef.current);
@ -1788,15 +1821,89 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
} }
} }
// Handle URL autoplay parameter or auto-start on page load // Detect if user has interacted with the page
const hasUserInteracted = () => {
// Check various indicators of user interaction
return (
document.hasFocus() ||
document.visibilityState === 'visible' ||
sessionStorage.getItem('userInteracted') === 'true'
);
};
// Handle autoplay while respecting user's saved preferences
const handleAutoplay = async () => {
const userInteracted = hasUserInteracted();
const savedMuteState = userPreferences.current.getPreference('muted');
try {
// Respect user's saved mute preference, but try unmuted if user interacted and hasn't explicitly muted
if (!mediaData.urlMuted && userInteracted && savedMuteState !== true) {
playerRef.current.muted(false);
}
// First attempt: try to play with current mute state
await playerRef.current.play();
} catch (error) {
// Fallback to muted autoplay unless user explicitly wants to stay unmuted
if (!playerRef.current.muted()) {
try {
playerRef.current.muted(true);
await playerRef.current.play();
// Only try to restore sound if user hasn't explicitly saved mute=true
if (savedMuteState !== true) {
// Aggressively try to restore sound
const restoreSound = () => {
if (playerRef.current && !playerRef.current.isDisposed()) {
playerRef.current.muted(false);
playerRef.current.trigger('notify', '🔊 Sound enabled!');
}
};
// Try to restore sound immediately if user has interacted
if (userInteracted) {
setTimeout(restoreSound, 100);
} else {
// Show notification for manual interaction
setTimeout(() => {
if (playerRef.current && !playerRef.current.isDisposed()) {
playerRef.current.trigger(
'notify',
'🔇 Click anywhere to enable sound'
);
}
}, 1000);
// Set up interaction listeners
const enableSound = () => {
restoreSound();
// Mark user interaction for future videos
sessionStorage.setItem('userInteracted', 'true');
// Remove listeners
document.removeEventListener('click', enableSound);
document.removeEventListener('keydown', enableSound);
document.removeEventListener('touchstart', enableSound);
};
document.addEventListener('click', enableSound, { once: true });
document.addEventListener('keydown', enableSound, { once: true });
document.addEventListener('touchstart', enableSound, { once: true });
}
}
} catch (mutedError) {
console.error('❌ Even muted autoplay was blocked:', mutedError.message);
}
}
}
};
if (mediaData?.urlAutoplay) { if (mediaData?.urlAutoplay) {
playerRef.current.play(); // Explicit autoplay requested via URL parameter
handleAutoplay();
} else { } else {
// Auto-start video on page load/reload (muted to comply with browser policies) // Auto-start video on page load/reload with fallback strategy
playerRef.current.play().catch((error) => { handleAutoplay();
console.log(' Browser prevented autoplay (normal behavior):', error.message);
// Fallback: ensure video is ready to play when user interacts
});
} }
const setupMobilePlayPause = () => { const setupMobilePlayPause = () => {
@ -1857,18 +1964,28 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
const progressControl = controlBar.getChild('progressControl'); const progressControl = controlBar.getChild('progressControl');
const seekBar = progressControl.getChild('seekBar'); const seekBar = progressControl.getChild('seekBar');
const chaptersButton = controlBar.getChild('chaptersButton'); const chaptersButton = controlBar.getChild('chaptersButton');
const fullscreenToggle = controlBar.getChild('fullscreenToggle');
// Auto-play video when navigating from next button // Auto-play video when navigating from next button
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
const hasVideoParam = urlParams.get('m'); const hasVideoParam = urlParams.get('m');
if (hasVideoParam) { if (hasVideoParam) {
// Small delay to ensure everything is loaded // Small delay to ensure everything is loaded
setTimeout(() => { setTimeout(async () => {
if (playerRef.current && !playerRef.current.isDisposed()) { if (playerRef.current && !playerRef.current.isDisposed()) {
playerRef.current.play().catch((error) => { try {
console.log(' Browser prevented autoplay (normal behavior):', error.message); await playerRef.current.play();
}); } catch (error) {
console.error(' Browser prevented play:', error.message);
// Try muted playback as fallback
if (!playerRef.current.muted()) {
try {
playerRef.current.muted(true);
await playerRef.current.play();
} catch (mutedError) {
console.error(' Even muted play was blocked:', mutedError.message);
}
}
}
} }
}, 100); }, 100);
} }
@ -1918,7 +2035,7 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
const cue = new (window.VTTCue || window.TextTrackCue)( const cue = new (window.VTTCue || window.TextTrackCue)(
chapter.startTime, chapter.startTime,
chapter.endTime, chapter.endTime,
chapter.text chapter.chapterTitle
); );
chaptersTrack.addCue(cue); chaptersTrack.addCue(cue);
}); });
@ -1965,9 +2082,11 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
const autoplayToggleButton = new AutoplayToggleButton(playerRef.current, { const autoplayToggleButton = new AutoplayToggleButton(playerRef.current, {
userPreferences: userPreferences.current, userPreferences: userPreferences.current,
}); });
// Add it after the play button // Add it before the chapters button (or at a suitable position)
const fullscreenToggleIndex = controlBar.children().indexOf(fullscreenToggle); const chaptersButtonIndex = controlBar.children().indexOf(chaptersButton);
controlBar.addChild(autoplayToggleButton, {}, fullscreenToggleIndex - 1); const insertIndex =
chaptersButtonIndex > 0 ? chaptersButtonIndex : controlBar.children().length - 3;
controlBar.addChild(autoplayToggleButton, {}, insertIndex);
// Store reference for later use // Store reference for later use
customComponents.current.autoplayToggleButton = autoplayToggleButton; customComponents.current.autoplayToggleButton = autoplayToggleButton;
@ -1975,62 +2094,13 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
// Force update icon after adding to DOM to ensure correct display // Force update icon after adding to DOM to ensure correct display
setTimeout(() => { setTimeout(() => {
autoplayToggleButton.updateIcon(); autoplayToggleButton.updateIcon();
console.log('✓ Autoplay toggle button icon updated after DOM insertion');
}, 100); }, 100);
console.log('✓ Autoplay toggle button added successfully');
} catch (error) { } catch (error) {
console.error('✗ Failed to add autoplay toggle button:', error); console.error('✗ Failed to add autoplay toggle button:', error);
} }
} }
// END: Implement autoplay toggle button // END: Implement autoplay toggle button
// Remove duplicate captions button and move chapters to end
/* const cleanupControls = () => {
// Log all current children for debugging
const allChildren = controlBar.children();
// Try to find and remove captions/subs-caps button (but keep subtitles)
const possibleCaptionButtons = ['captionsButton', 'subsCapsButton'];
possibleCaptionButtons.forEach((buttonName) => {
const button = controlBar.getChild(buttonName);
if (button) {
try {
controlBar.removeChild(button);
console.log(`✓ Removed ${buttonName}`);
} catch (e) {
console.log(`✗ Failed to remove ${buttonName}:`, e);
}
}
});
// Alternative: hide buttons we can't remove
allChildren.forEach((child, index) => {
const name = (child.name_ || child.constructor.name || '').toLowerCase();
if (name.includes('caption') && !name.includes('subtitle')) {
child.hide();
console.log(`✓ Hidden button at index ${index}: ${name}`);
}
});
// Move chapters button to the very end
const chaptersButton = controlBar.getChild('chaptersButton');
if (chaptersButton) {
try {
controlBar.removeChild(chaptersButton);
controlBar.addChild(chaptersButton);
console.log('✓ Chapters button moved to last position');
} catch (e) {
console.log('✗ Failed to move chapters button:', e);
}
}
}; */
// Try multiple times with different delays
/* setTimeout(cleanupControls, 200);
setTimeout(cleanupControls, 500);
setTimeout(cleanupControls, 1000); */
// Make menus clickable instead of hover-only // Make menus clickable instead of hover-only
setTimeout(() => { setTimeout(() => {
const setupClickableMenus = () => { const setupClickableMenus = () => {
@ -2057,8 +2127,6 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
this.menu.show(); this.menu.show();
} }
}); });
console.log(`✓ Made ${buttonName} clickable`);
} else if (button) { } else if (button) {
// For buttons without menuButton_ property // For buttons without menuButton_ property
const buttonEl = button.el(); const buttonEl = button.el();
@ -2081,8 +2149,6 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
} }
} }
}); });
console.log(`✓ Added click handler to ${buttonName}`);
} }
} }
}); });
@ -2232,27 +2298,17 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
// BEGIN: Add chapter markers and sprite preview to progress control // BEGIN: Add chapter markers and sprite preview to progress control
if (progressControl && seekBar) { if (progressControl && seekBar) {
console.log('Setting up sprite preview and chapter markers...');
console.log('mediaData.previewSprite:', mediaData.previewSprite);
console.log('chaptersData:', chaptersData);
// Check if we have chapters // Check if we have chapters
const hasChapters = chaptersData && chaptersData.length > 0; const hasChapters = chaptersData && chaptersData.length > 0;
if (hasChapters) { if (hasChapters) {
// Use original ChapterMarkers with sprite functionality when chapters exist // Use original ChapterMarkers with sprite functionality when chapters exist
console.log(
'✓ Adding ChapterMarkers component with sprite functionality (chapters exist)'
);
const chapterMarkers = new ChapterMarkers(playerRef.current, { const chapterMarkers = new ChapterMarkers(playerRef.current, {
previewSprite: mediaData.previewSprite, previewSprite: mediaData.previewSprite,
}); });
seekBar.addChild(chapterMarkers); seekBar.addChild(chapterMarkers);
} else if (mediaData.previewSprite) { } else if (mediaData.previewSprite) {
// Use separate SpritePreview component only when no chapters but sprite data exists // Use separate SpritePreview component only when no chapters but sprite data exists
console.log(
'✓ Adding SpritePreview component (no chapters, but sprite data available)'
);
const spritePreview = new SpritePreview(playerRef.current, { const spritePreview = new SpritePreview(playerRef.current, {
previewSprite: mediaData.previewSprite, previewSprite: mediaData.previewSprite,
}); });
@ -2260,19 +2316,14 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
// Setup sprite preview hover functionality // Setup sprite preview hover functionality
setTimeout(() => { setTimeout(() => {
console.log('✓ Setting up sprite preview hover functionality');
spritePreview.setupProgressBarHover(); spritePreview.setupProgressBarHover();
}, 100); }, 100);
} else {
console.log('✗ No chapters and no sprite data available');
} }
} }
// END: Add chapter markers and sprite preview to progress control // END: Add chapter markers and sprite preview to progress control
// BEGIN: Simple button layout fix - use CSS approach // BEGIN: Simple button layout fix - use CSS approach
setTimeout(() => { setTimeout(() => {
console.log('Setting up simplified button layout...');
// Add a simple spacer div using DOM manipulation (simpler approach) // Add a simple spacer div using DOM manipulation (simpler approach)
const spacerDiv = document.createElement('div'); const spacerDiv = document.createElement('div');
spacerDiv.className = 'vjs-spacer-control vjs-control'; spacerDiv.className = 'vjs-spacer-control vjs-control';
@ -2287,22 +2338,32 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
const durationEl = durationDisplay.el(); const durationEl = durationDisplay.el();
const nextSibling = durationEl.nextSibling; const nextSibling = durationEl.nextSibling;
controlBarEl.insertBefore(spacerDiv, nextSibling); controlBarEl.insertBefore(spacerDiv, nextSibling);
console.log('✓ Simple spacer added after duration display');
} }
}, 300); }, 300);
// END: Simple button layout fix // END: Simple button layout fix
// BEGIN: Move chapters button after fullscreen toggle // BEGIN: Move Picture-in-Picture and Fullscreen buttons to the very end
if (chaptersButton && fullscreenToggle) { setTimeout(() => {
try { try {
const fullscreenIndex = controlBar.children().indexOf(fullscreenToggle); const pictureInPictureToggle = controlBar.getChild('pictureInPictureToggle');
controlBar.addChild(chaptersButton, {}, fullscreenIndex + 1); const fullscreenToggle = controlBar.getChild('fullscreenToggle');
console.log('✓ Chapters button moved after fullscreen toggle');
// Move Picture-in-Picture button to the very end (if it exists)
if (pictureInPictureToggle) {
controlBar.removeChild(pictureInPictureToggle);
controlBar.addChild(pictureInPictureToggle);
}
// Move Fullscreen button to the very end (after PiP)
if (fullscreenToggle) {
controlBar.removeChild(fullscreenToggle);
controlBar.addChild(fullscreenToggle);
}
} catch (e) { } catch (e) {
console.log('✗ Failed to move chapters button:', e); console.error('✗ Failed to move PiP/Fullscreen buttons to end:', e);
} }
} }, 100);
// END: Move chapters button after fullscreen toggle // END: Move Picture-in-Picture and Fullscreen buttons to the very end
// BEGIN: Add Chapters Overlay Component // BEGIN: Add Chapters Overlay Component
if (chaptersData && chaptersData.length > 0) { if (chaptersData && chaptersData.length > 0) {
@ -2312,8 +2373,6 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
channelName: 'Chapter', channelName: 'Chapter',
thumbnail: mediaData?.data?.thumbnail_url || mediaData?.data?.author_thumbnail || '', thumbnail: mediaData?.data?.thumbnail_url || mediaData?.data?.author_thumbnail || '',
}); });
} else {
console.log('⚠ No chapters data available for overlay');
} }
// END: Add Chapters Overlay Component // END: Add Chapters Overlay Component
@ -2399,199 +2458,14 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
}; };
// END: Add custom arrow key seek functionality // END: Add custom arrow key seek functionality
// Log current user preferences
console.log('Current user preferences:', userPreferences.current.getPreferences());
window.debugSubtitles = {
showTracks: () => {
const textTracks = playerRef.current.textTracks();
console.log('=== Available Text Tracks ===');
for (let i = 0; i < textTracks.length; i++) {
const track = textTracks[i];
console.log(
`${i}: ${track.kind} | ${track.language} | ${track.label} | mode: ${track.mode}`
);
}
},
enableEnglish: () => {
const textTracks = playerRef.current.textTracks();
for (let i = 0; i < textTracks.length; i++) {
const track = textTracks[i];
if (track.kind === 'subtitles' && track.language === 'en') {
track.mode = 'showing';
console.log('Enabled English subtitles');
break;
}
}
},
enableGreek: () => {
const textTracks = playerRef.current.textTracks();
for (let i = 0; i < textTracks.length; i++) {
const track = textTracks[i];
if (track.kind === 'subtitles' && track.language === 'el') {
track.mode = 'showing';
console.log('Enabled Greek subtitles');
break;
}
}
},
disableAll: () => {
const textTracks = playerRef.current.textTracks();
for (let i = 0; i < textTracks.length; i++) {
const track = textTracks[i];
if (track.kind === 'subtitles') {
track.mode = 'disabled';
}
}
console.log('Disabled all subtitles');
},
getPrefs: () => {
console.log('Saved preferences:', userPreferences.current.getPreferences());
},
reapplyPrefs: () => {
userPreferences.current.applySubtitlePreference(playerRef.current);
},
showMenu: () => {
const controlBar = playerRef.current.getChild('controlBar');
// Try different button names
const possibleNames = ['subtitlesButton', 'captionsButton', 'subsCapsButton'];
let subtitlesButton = null;
for (const name of possibleNames) {
const button = controlBar.getChild(name);
if (button) {
console.log(`Found subtitle button: ${name}`);
subtitlesButton = button;
break;
}
}
if (subtitlesButton && subtitlesButton.menu) {
console.log('=== Subtitle Menu Items ===');
subtitlesButton.menu.children_.forEach((item, index) => {
if (item.track) {
console.log(
`${index}: ${item.track.label} (${item.track.language}) - selected: ${item.selected()}`
);
} else {
console.log(
`${index}: ${item.label || 'Unknown'} - selected: ${item.selected()}`
);
}
});
} else {
console.log('No subtitle menu found, checking DOM...');
// Check DOM for subtitle menu items
const menuItems = playerRef.current.el().querySelectorAll('.vjs-menu-item');
console.log(`Found ${menuItems.length} menu items in DOM`);
menuItems.forEach((item, index) => {
if (
item.textContent.toLowerCase().includes('subtitle') ||
item.textContent.toLowerCase().includes('caption') ||
item.textContent.toLowerCase().includes('off')
) {
console.log(
`DOM item ${index}: ${item.textContent} - classes: ${item.className}`
);
}
});
}
},
testMenuClick: (index) => {
const controlBar = playerRef.current.getChild('controlBar');
const possibleNames = ['subtitlesButton', 'captionsButton', 'subsCapsButton'];
let subtitlesButton = null;
for (const name of possibleNames) {
const button = controlBar.getChild(name);
if (button) {
subtitlesButton = button;
break;
}
}
if (subtitlesButton && subtitlesButton.menu && subtitlesButton.menu.children_[index]) {
const menuItem = subtitlesButton.menu.children_[index];
console.log('Simulating click on menu item:', index);
menuItem.handleClick();
} else {
console.log('Menu item not found at index:', index, 'trying DOM approach...');
// Try DOM approach
const menuItems = playerRef.current.el().querySelectorAll('.vjs-menu-item');
const subtitleItems = Array.from(menuItems).filter(
(item) =>
item.textContent.toLowerCase().includes('subtitle') ||
item.textContent.toLowerCase().includes('caption') ||
item.textContent.toLowerCase().includes('off')
);
if (subtitleItems[index]) {
console.log('Clicking DOM element:', subtitleItems[index].textContent);
subtitleItems[index].click();
} else {
console.log('No DOM subtitle item found at index:', index);
}
}
},
forceEnableEnglish: () => {
console.log('Force enabling English subtitles...');
const textTracks = playerRef.current.textTracks();
for (let i = 0; i < textTracks.length; i++) {
const track = textTracks[i];
if (track.kind === 'subtitles') {
track.mode = track.language === 'en' ? 'showing' : 'disabled';
}
}
userPreferences.current.setPreference('subtitleLanguage', 'en');
console.log('English subtitles enabled and saved');
},
watchSubtitleChanges: () => {
console.log('👀 Watching subtitle preference changes...');
const originalSetPreference = userPreferences.current.setPreference;
userPreferences.current.setPreference = function (key, value) {
if (key === 'subtitleLanguage') {
console.log(`🎯 SUBTITLE CHANGE: ${value} at ${new Date().toISOString()}`);
console.trace('Change origin:');
}
return originalSetPreference.call(this, key, value);
};
console.log('Subtitle change monitoring activated');
},
checkRestorationFlag: () => {
console.log('Restoration flag:', userPreferences.current.isRestoringSubtitles);
console.log('Auto-save disabled:', userPreferences.current.subtitleAutoSaveDisabled);
},
forceSaveGreek: () => {
console.log('🚀 Force saving Greek subtitle preference...');
userPreferences.current.forceSetSubtitleLanguage('el');
console.log('Check result:', userPreferences.current.getPreferences());
},
forceSaveEnglish: () => {
console.log('🚀 Force saving English subtitle preference...');
userPreferences.current.forceSetSubtitleLanguage('en');
console.log('Check result:', userPreferences.current.getPreferences());
},
forceSaveNull: () => {
console.log('🚀 Force saving null subtitle preference...');
userPreferences.current.forceSetSubtitleLanguage(null);
console.log('Check result:', userPreferences.current.getPreferences());
},
};
}); });
// Listen for next video event // Listen for next video event
playerRef.current.on('nextVideo', () => { playerRef.current.on('nextVideo', () => {
console.log('Next video requested');
goToNextVideo(); goToNextVideo();
}); });
playerRef.current.on('play', () => { playerRef.current.on('play', () => {
console.log('Video started playing');
// Only show play indicator if not changing quality // Only show play indicator if not changing quality
if (!playerRef.current.isChangingQuality && customComponents.current.seekIndicator) { if (!playerRef.current.isChangingQuality && customComponents.current.seekIndicator) {
customComponents.current.seekIndicator.show('play'); customComponents.current.seekIndicator.show('play');
@ -2599,7 +2473,6 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
}); });
playerRef.current.on('pause', () => { playerRef.current.on('pause', () => {
console.log('Video paused');
// Only show pause indicator if not changing quality // Only show pause indicator if not changing quality
if (!playerRef.current.isChangingQuality && customComponents.current.seekIndicator) { if (!playerRef.current.isChangingQuality && customComponents.current.seekIndicator) {
customComponents.current.seekIndicator.show('pause'); customComponents.current.seekIndicator.show('pause');
@ -2611,9 +2484,6 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
let autoplayCountdown = null; let autoplayCountdown = null;
playerRef.current.on('ended', () => { playerRef.current.on('ended', () => {
console.log('Video ended');
console.log('Available relatedVideos:', relatedVideos);
// Keep controls active after video ends // Keep controls active after video ends
setTimeout(() => { setTimeout(() => {
if (playerRef.current && !playerRef.current.isDisposed()) { if (playerRef.current && !playerRef.current.isDisposed()) {
@ -2631,18 +2501,10 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
} }
}, 50); }, 50);
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 // Check if autoplay is enabled and there's a next video
const isAutoplayEnabled = userPreferences.current.getAutoplayPreference(); const isAutoplayEnabled = userPreferences.current.getAutoplayPreference();
const hasNextVideo = mediaData.nextLink !== null; const hasNextVideo = mediaData.nextLink !== null;
console.log('isAutoplayEnabled', isAutoplayEnabled);
console.log('hasNextVideo', hasNextVideo);
console.log('isEmbedPlayer', isEmbedPlayer);
if (!isEmbedPlayer && isAutoplayEnabled && hasNextVideo) { if (!isEmbedPlayer && isAutoplayEnabled && hasNextVideo) {
// Get next video data for countdown display - find the next video in related videos // Get next video data for countdown display - find the next video in related videos
let nextVideoData = { let nextVideoData = {
@ -2680,11 +2542,9 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
nextVideoData: nextVideoData, nextVideoData: nextVideoData,
countdownSeconds: 5, countdownSeconds: 5,
onPlayNext: () => { onPlayNext: () => {
console.log('Autoplay: Navigating to next video');
goToNextVideo(); goToNextVideo();
}, },
onCancel: () => { onCancel: () => {
console.log('Autoplay: User cancelled, showing related videos');
// Hide countdown and show end screen instead // Hide countdown and show end screen instead
if (autoplayCountdown) { if (autoplayCountdown) {
playerRef.current.removeChild(autoplayCountdown); playerRef.current.removeChild(autoplayCountdown);
@ -2705,7 +2565,6 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
function showEndScreen() { function showEndScreen() {
// Prevent creating multiple end screens // Prevent creating multiple end screens
if (endScreen) { if (endScreen) {
console.log('End screen already exists, removing previous one');
playerRef.current.removeChild(endScreen); playerRef.current.removeChild(endScreen);
endScreen = null; endScreen = null;
} }
@ -2753,35 +2612,23 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
}); });
playerRef.current.on('error', (error) => { playerRef.current.on('error', (error) => {
console.error('Video.js error:', error); // console.error('Video.js error:', error);
}); });
playerRef.current.on('fullscreenchange', () => { playerRef.current.on('fullscreenchange', () => {
console.log('Fullscreen changed:', playerRef.current.isFullscreen()); // console.log('Fullscreen changed:', playerRef.current.isFullscreen());
}); });
playerRef.current.on('volumechange', () => { playerRef.current.on('volumechange', () => {
console.log('Volume changed:', playerRef.current.volume(), 'Muted:', playerRef.current.muted()); // console.log('Volume changed:', playerRef.current.volume(), 'Muted:', playerRef.current.muted());
}); });
playerRef.current.on('ratechange', () => { playerRef.current.on('ratechange', () => {
console.log('Playback rate changed:', playerRef.current.playbackRate()); // console.log('Playback rate changed:', playerRef.current.playbackRate());
}); });
playerRef.current.on('texttrackchange', () => { playerRef.current.on('texttrackchange', () => {
console.log('Text track changed'); // console.log('Text track changed');
const textTracks = playerRef.current.textTracks();
for (let i = 0; i < textTracks.length; i++) {
console.log(
'Track',
i,
':',
textTracks[i].kind,
textTracks[i].label,
'Mode:',
textTracks[i].mode
);
}
}); });
// Focus the player element so keyboard controls work // Focus the player element so keyboard controls work
@ -2793,7 +2640,6 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
const videoElement = playerRef.current.el(); const videoElement = playerRef.current.el();
videoElement.setAttribute('tabindex', '0'); videoElement.setAttribute('tabindex', '0');
videoElement.focus(); videoElement.focus();
console.log('Video player focused for keyboard controls');
// Add custom keyboard event handler for space key // Add custom keyboard event handler for space key
const handleKeyPress = (event) => { const handleKeyPress = (event) => {
@ -2828,15 +2674,6 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
document.removeEventListener('keydown', handleKeyPress); document.removeEventListener('keydown', handleKeyPress);
}; };
} }
// Start playing the video immediately if autoplay is enabled
if (playerRef.current.autoplay()) {
playerRef.current.play().catch((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
});
}
}); });
} }
}, 0); }, 0);
@ -2867,7 +2704,6 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
const videoElement = playerRef.current.el(); const videoElement = playerRef.current.el();
videoElement.setAttribute('tabindex', '0'); videoElement.setAttribute('tabindex', '0');
videoElement.focus(); videoElement.focus();
console.log('Video element focused for keyboard controls');
} }
}; };

View File

@ -3,7 +3,6 @@ import { createRoot } from 'react-dom/client';
import './VideoJS.css'; import './VideoJS.css';
import VideoJS from './VideoJS.jsx'; import VideoJS from './VideoJS.jsx';
// import ChapterList from './components/chapter/ChapterList.jsx';
// Mount the components when the DOM is ready // Mount the components when the DOM is ready
const mountComponents = () => { const mountComponents = () => {
@ -16,7 +15,6 @@ const mountComponents = () => {
<VideoJS videoId="video-main" /> <VideoJS videoId="video-main" />
</StrictMode> </StrictMode>
); );
console.log('Mounted main VideoJS player');
} }
// Mount embed video player // Mount embed video player
@ -28,7 +26,6 @@ const mountComponents = () => {
<VideoJS videoId="video-embed" /> <VideoJS videoId="video-embed" />
</StrictMode> </StrictMode>
); );
console.log('Mounted embed VideoJS player');
} }
}; };
@ -37,7 +34,6 @@ window.triggerVideoJSMount = mountComponents;
// Listen for custom events to trigger mounting // Listen for custom events to trigger mounting
document.addEventListener('triggerVideoJSMount', () => { document.addEventListener('triggerVideoJSMount', () => {
console.log('Received triggerVideoJSMount event, attempting to mount VideoJS components...');
mountComponents(); mountComponents();
}); });
@ -52,7 +48,6 @@ if (document.readyState === 'loading') {
setInterval(() => { setInterval(() => {
const embedContainer = document.getElementById('video-js-root-embed'); const embedContainer = document.getElementById('video-js-root-embed');
if (embedContainer && !embedContainer.hasChildNodes()) { if (embedContainer && !embedContainer.hasChildNodes()) {
console.log('Found unmounted embed container during periodic check, mounting...');
mountComponents(); mountComponents();
} }
}, 1000); }, 1000);

View File

@ -12,7 +12,7 @@ class UserPreferences {
subtitleLanguage: null, // No subtitles by default subtitleLanguage: null, // No subtitles by default
subtitleEnabled: false, // Subtitles off by default subtitleEnabled: false, // Subtitles off by default
muted: false, muted: false,
autoplay: false, // Autoplay disabled by default autoplay: true, // Autoplay disabled by default
}; };
} }
@ -43,7 +43,6 @@ class UserPreferences {
const currentPrefs = this.getPreferences(); const currentPrefs = this.getPreferences();
const updatedPrefs = { ...currentPrefs, ...preferences }; const updatedPrefs = { ...currentPrefs, ...preferences };
localStorage.setItem(this.storageKey, JSON.stringify(updatedPrefs)); localStorage.setItem(this.storageKey, JSON.stringify(updatedPrefs));
console.log('User preferences saved:', updatedPrefs);
} catch (error) { } catch (error) {
console.warn('Error saving user preferences to localStorage:', error); console.warn('Error saving user preferences to localStorage:', error);
} }
@ -68,21 +67,13 @@ class UserPreferences {
setPreference(key, value, forceSet = false) { setPreference(key, value, forceSet = false) {
// Add special logging for subtitle language changes // Add special logging for subtitle language changes
if (key === 'subtitleLanguage') { if (key === 'subtitleLanguage') {
console.log(
`🔄 Setting subtitleLanguage: ${value} (restoring: ${this.isRestoringSubtitles}, autoSaveDisabled: ${this.subtitleAutoSaveDisabled}, forceSet: ${forceSet})`
);
// Block subtitle language changes during restoration, but allow forced sets // Block subtitle language changes during restoration, but allow forced sets
if (this.isRestoringSubtitles) { if (this.isRestoringSubtitles) {
console.log('🚫 BLOCKED: Subtitle language change during restoration');
return; // Don't save during restoration return; // Don't save during restoration
} }
// Allow forced sets even if auto-save is disabled (for direct user clicks) // Allow forced sets even if auto-save is disabled (for direct user clicks)
if (this.subtitleAutoSaveDisabled && !forceSet) { if (this.subtitleAutoSaveDisabled && !forceSet) {
console.log(
'🚫 BLOCKED: Subtitle language change during auto-save disabled period (use forceSet=true to override)'
);
return; // Don't save if disabled unless forced return; // Don't save if disabled unless forced
} }
@ -97,7 +88,6 @@ class UserPreferences {
resetPreferences() { resetPreferences() {
try { try {
localStorage.removeItem(this.storageKey); localStorage.removeItem(this.storageKey);
console.log('User preferences reset to defaults');
} catch (error) { } catch (error) {
console.warn('Error resetting user preferences:', error); console.warn('Error resetting user preferences:', error);
} }
@ -112,12 +102,10 @@ class UserPreferences {
// DISABLE subtitle auto-save completely during initial load // DISABLE subtitle auto-save completely during initial load
this.subtitleAutoSaveDisabled = true; this.subtitleAutoSaveDisabled = true;
console.log('🔒 Subtitle auto-save DISABLED during initial load');
// Re-enable after 3 seconds to ensure everything has settled // Re-enable after 3 seconds to ensure everything has settled
setTimeout(() => { setTimeout(() => {
this.subtitleAutoSaveDisabled = false; this.subtitleAutoSaveDisabled = false;
console.log('🔓 Subtitle auto-save RE-ENABLED after initial load period');
}, 3000); }, 3000);
// Apply volume and mute state // Apply volume and mute state
@ -133,11 +121,6 @@ class UserPreferences {
if (typeof prefs.playbackRate === 'number' && prefs.playbackRate > 0) { if (typeof prefs.playbackRate === 'number' && prefs.playbackRate > 0) {
player.playbackRate(prefs.playbackRate); player.playbackRate(prefs.playbackRate);
} }
// Apply subtitle language (will be handled separately for text tracks)
// Quality setting will be handled by the settings menu component
console.log('Applied user preferences to player:', prefs);
} }
/** /**
@ -160,7 +143,6 @@ class UserPreferences {
player.on('texttrackchange', () => { player.on('texttrackchange', () => {
// Skip saving if we're currently restoring subtitles // Skip saving if we're currently restoring subtitles
if (this.isRestoringSubtitles) { if (this.isRestoringSubtitles) {
console.log('Skipping subtitle save - currently restoring preferences');
return; return;
} }
@ -173,24 +155,16 @@ class UserPreferences {
const track = textTracks[i]; const track = textTracks[i];
if (track.kind === 'subtitles' && track.mode === 'showing') { if (track.kind === 'subtitles' && track.mode === 'showing') {
activeLanguage = track.language; activeLanguage = track.language;
console.log('Active subtitle language detected:', activeLanguage);
break; break;
} }
} }
// If no subtitles are active, save null
if (!activeLanguage) {
console.log('No subtitles active, saving null');
}
this.setPreference('subtitleLanguage', activeLanguage); this.setPreference('subtitleLanguage', activeLanguage);
}, 100); }, 100);
}); });
// Also hook into subtitle menu clicks directly // Also hook into subtitle menu clicks directly
this.setupSubtitleMenuListeners(player); this.setupSubtitleMenuListeners(player);
console.log('Auto-save preferences listeners set up');
} }
/** /**
@ -201,7 +175,6 @@ class UserPreferences {
// Wait for the control bar to be ready // Wait for the control bar to be ready
setTimeout(() => { setTimeout(() => {
const controlBar = player.getChild('controlBar'); const controlBar = player.getChild('controlBar');
console.log('=== Searching for subtitle controls ===');
// Check all possible subtitle button names // Check all possible subtitle button names
const possibleNames = ['subtitlesButton', 'captionsButton', 'subsCapsButton', 'textTrackButton']; const possibleNames = ['subtitlesButton', 'captionsButton', 'subsCapsButton', 'textTrackButton'];
@ -210,7 +183,6 @@ class UserPreferences {
for (const name of possibleNames) { for (const name of possibleNames) {
const button = controlBar.getChild(name); const button = controlBar.getChild(name);
if (button) { if (button) {
console.log(`Found subtitle button: ${name}`);
subtitlesButton = button; subtitlesButton = button;
break; break;
} }
@ -218,26 +190,21 @@ class UserPreferences {
// Also try to find by scanning all children // Also try to find by scanning all children
if (!subtitlesButton) { if (!subtitlesButton) {
console.log('Scanning all control bar children...');
const children = controlBar.children(); const children = controlBar.children();
children.forEach((child, index) => { children.forEach((child) => {
const name = child.name_ || child.constructor.name || 'Unknown'; const name = child.name_ || child.constructor.name || 'Unknown';
console.log(`Child ${index}: ${name}`);
if ( if (
name.toLowerCase().includes('subtitle') || name.toLowerCase().includes('subtitle') ||
name.toLowerCase().includes('caption') || name.toLowerCase().includes('caption') ||
name.toLowerCase().includes('text') name.toLowerCase().includes('text')
) { ) {
console.log(`Potential subtitle button found: ${name}`);
subtitlesButton = child; subtitlesButton = child;
} }
}); });
} }
if (subtitlesButton) { if (subtitlesButton) {
console.log('Found subtitles button, setting up menu listeners');
// Wait a bit more for the menu to be created // Wait a bit more for the menu to be created
setTimeout(() => { setTimeout(() => {
this.attachMenuItemListeners(player, subtitlesButton); this.attachMenuItemListeners(player, subtitlesButton);
@ -248,8 +215,6 @@ class UserPreferences {
this.attachMenuItemListeners(player, subtitlesButton); this.attachMenuItemListeners(player, subtitlesButton);
}, 2000); }, 2000);
} else { } else {
console.log('No subtitles button found after exhaustive search');
// Try alternative approach - listen to DOM changes // Try alternative approach - listen to DOM changes
this.setupDOMBasedListeners(player); this.setupDOMBasedListeners(player);
} }
@ -261,8 +226,6 @@ class UserPreferences {
* @param {Object} player - Video.js player instance * @param {Object} player - Video.js player instance
*/ */
setupDOMBasedListeners(player) { setupDOMBasedListeners(player) {
console.log('Setting up DOM-based subtitle listeners as fallback');
// Wait for DOM to be ready // Wait for DOM to be ready
setTimeout(() => { setTimeout(() => {
const playerEl = player.el(); const playerEl = player.el();
@ -277,8 +240,6 @@ class UserPreferences {
target.closest('.vjs-captions-menu-item') || target.closest('.vjs-captions-menu-item') ||
(target.closest('.vjs-menu-item') && target.textContent.toLowerCase().includes('subtitle')) (target.closest('.vjs-menu-item') && target.textContent.toLowerCase().includes('subtitle'))
) { ) {
console.log('Subtitle menu item clicked via DOM listener:', target.textContent);
// Extract language from the clicked item // Extract language from the clicked item
setTimeout(() => { setTimeout(() => {
this.detectActiveSubtitleFromDOM(player, true); // Force set for user clicks this.detectActiveSubtitleFromDOM(player, true); // Force set for user clicks
@ -287,14 +248,11 @@ class UserPreferences {
// Also handle "captions off" clicks // Also handle "captions off" clicks
if (target.closest('.vjs-menu-item') && target.textContent.toLowerCase().includes('off')) { if (target.closest('.vjs-menu-item') && target.textContent.toLowerCase().includes('off')) {
console.log('Captions off clicked via DOM listener');
setTimeout(() => { setTimeout(() => {
this.setPreference('subtitleLanguage', null, true); // Force set for user clicks this.setPreference('subtitleLanguage', null, true); // Force set for user clicks
}, 200); }, 200);
} }
}); });
console.log('DOM-based subtitle listeners attached');
} }
}, 1500); }, 1500);
} }
@ -307,7 +265,6 @@ class UserPreferences {
detectActiveSubtitleFromDOM(player, forceSet = false) { detectActiveSubtitleFromDOM(player, forceSet = false) {
// Skip saving if we're currently restoring subtitles // Skip saving if we're currently restoring subtitles
if (this.isRestoringSubtitles) { if (this.isRestoringSubtitles) {
console.log('Skipping DOM subtitle save - currently restoring preferences');
return; return;
} }
@ -318,7 +275,6 @@ class UserPreferences {
const track = textTracks[i]; const track = textTracks[i];
if (track.kind === 'subtitles' && track.mode === 'showing') { if (track.kind === 'subtitles' && track.mode === 'showing') {
activeLanguage = track.language; activeLanguage = track.language;
console.log('DOM detection - Active subtitle language:', activeLanguage, track.label);
break; break;
} }
} }
@ -335,49 +291,37 @@ class UserPreferences {
try { try {
const menu = subtitlesButton.menu; const menu = subtitlesButton.menu;
if (menu && menu.children_) { if (menu && menu.children_) {
console.log('Found subtitle menu with', menu.children_.length, 'items'); menu.children_.forEach((menuItem) => {
menu.children_.forEach((menuItem, index) => {
if (menuItem.track) { if (menuItem.track) {
const track = menuItem.track; const track = menuItem.track;
console.log(`Menu item ${index}: ${track.label} (${track.language})`);
// Override the handleClick method // Override the handleClick method
const originalHandleClick = menuItem.handleClick.bind(menuItem); const originalHandleClick = menuItem.handleClick.bind(menuItem);
menuItem.handleClick = () => { menuItem.handleClick = () => {
console.log('Subtitle menu item clicked:', track.label, track.language);
// Call original click handler // Call original click handler
originalHandleClick(); originalHandleClick();
// Save the preference after a short delay // Save the preference after a short delay
setTimeout(() => { setTimeout(() => {
if (track.mode === 'showing') { if (track.mode === 'showing') {
console.log('Saving subtitle preference:', track.language);
this.setPreference('subtitleLanguage', track.language, true); // Force set for user clicks this.setPreference('subtitleLanguage', track.language, true); // Force set for user clicks
} else { } else {
console.log('Subtitle disabled, saving null');
this.setPreference('subtitleLanguage', null, true); // Force set for user clicks this.setPreference('subtitleLanguage', null, true); // Force set for user clicks
} }
}, 100); }, 100);
}; };
} else if (menuItem.label && menuItem.label.toLowerCase().includes('off')) { } else if (menuItem.label && menuItem.label.toLowerCase().includes('off')) {
// Handle "captions off" option // Handle "captions off" option
console.log('Found captions off menu item');
const originalHandleClick = menuItem.handleClick.bind(menuItem); const originalHandleClick = menuItem.handleClick.bind(menuItem);
menuItem.handleClick = () => { menuItem.handleClick = () => {
console.log('Captions off clicked');
originalHandleClick(); originalHandleClick();
setTimeout(() => { setTimeout(() => {
console.log('Saving subtitle preference: null (off)');
this.setPreference('subtitleLanguage', null, true); // Force set for user clicks this.setPreference('subtitleLanguage', null, true); // Force set for user clicks
}, 100); }, 100);
}; };
} }
}); });
} else {
console.log('Could not find subtitle menu or menu items');
} }
} catch (error) { } catch (error) {
console.error('Error setting up subtitle menu listeners:', error); console.error('Error setting up subtitle menu listeners:', error);
@ -395,20 +339,9 @@ class UserPreferences {
if (savedLanguage) { if (savedLanguage) {
// Set flag to prevent auto-save during restoration // Set flag to prevent auto-save during restoration
this.isRestoringSubtitles = true; this.isRestoringSubtitles = true;
console.log('isRestoringSubtitles', this.isRestoringSubtitles);
// Multiple attempts with increasing delays to ensure text tracks are loaded // Multiple attempts with increasing delays to ensure text tracks are loaded
const attemptToApplySubtitles = (attempt = 1) => { const attemptToApplySubtitles = (attempt = 1) => {
const textTracks = player.textTracks(); const textTracks = player.textTracks();
console.log(`Subtitle application attempt ${attempt}, found ${textTracks.length} text tracks`);
// Log all available tracks for debugging
for (let i = 0; i < textTracks.length; i++) {
const track = textTracks[i];
console.log(
`Track ${i}: kind=${track.kind}, language=${track.language}, label=${track.label}, mode=${track.mode}`
);
}
// First, disable all subtitle tracks // First, disable all subtitle tracks
for (let i = 0; i < textTracks.length; i++) { for (let i = 0; i < textTracks.length; i++) {
@ -432,7 +365,6 @@ class UserPreferences {
const track = textTracks[i]; const track = textTracks[i];
if (track.kind === 'subtitles' && matchesLang(track, savedLanguage)) { if (track.kind === 'subtitles' && matchesLang(track, savedLanguage)) {
track.mode = 'showing'; track.mode = 'showing';
console.log('✓ Applied saved subtitle language:', savedLanguage, track.label);
found = true; found = true;
// Also update the menu UI to reflect the selection // Also update the menu UI to reflect the selection
@ -452,10 +384,7 @@ class UserPreferences {
const track = textTracks[i]; const track = textTracks[i];
if (track.kind === 'subtitles') { if (track.kind === 'subtitles') {
track.mode = 'showing'; track.mode = 'showing';
console.log(
'Fallback ✓ Enabled first available subtitles track:',
track.label || track.language || track.srclang
);
// Save back the language we actually enabled for future precise matches // Save back the language we actually enabled for future precise matches
const langToSave = track.language || track.srclang || null; const langToSave = track.language || track.srclang || null;
if (langToSave) this.setPreference('subtitleLanguage', langToSave, true); if (langToSave) this.setPreference('subtitleLanguage', langToSave, true);
@ -472,12 +401,10 @@ class UserPreferences {
// Clear the restoration flag after a longer delay to ensure all events have settled // Clear the restoration flag after a longer delay to ensure all events have settled
setTimeout(() => { setTimeout(() => {
this.isRestoringSubtitles = false; this.isRestoringSubtitles = false;
console.log('✅ Subtitle restoration complete, auto-save re-enabled');
}, 600); // Increased to 3 seconds }, 600); // Increased to 3 seconds
// If not found and we haven't tried too many times, try again // If not found and we haven't tried too many times, try again
if (!found && attempt < 5) { if (!found && attempt < 5) {
console.log(`Subtitle language ${savedLanguage} not found, retrying in ${attempt * 50}ms...`);
setTimeout(() => attemptToApplySubtitles(attempt + 1), attempt * 50); setTimeout(() => attemptToApplySubtitles(attempt + 1), attempt * 50);
} else if (!found) { } else if (!found) {
console.warn('Could not find subtitle track for language:', savedLanguage); console.warn('Could not find subtitle track for language:', savedLanguage);
@ -518,8 +445,9 @@ class UserPreferences {
// Update custom settings menu to show "Off" as selected // Update custom settings menu to show "Off" as selected
this.updateCustomSettingsMenuUI(player); this.updateCustomSettingsMenuUI(player);
} catch (e) {} } catch (e) {
console.log('No subtitle auto-apply on load (disabled or no language).'); console.error('Error applying subtitle preference:', e);
}
} }
} }
@ -538,13 +466,9 @@ class UserPreferences {
if (enabled) { if (enabled) {
buttonEl.classList.add('vjs-subs-active'); buttonEl.classList.add('vjs-subs-active');
console.log('✓ Added vjs-subs-active class to subtitle button');
} else { } else {
buttonEl.classList.remove('vjs-subs-active'); buttonEl.classList.remove('vjs-subs-active');
console.log('✓ Removed vjs-subs-active class from subtitle button');
} }
} else {
console.log('Subtitle button not found for visual state update');
} }
} catch (error) { } catch (error) {
console.error('Error updating subtitle button visual state:', error); console.error('Error updating subtitle button visual state:', error);
@ -569,7 +493,6 @@ class UserPreferences {
if (menuItem.track) { if (menuItem.track) {
if (menuItem.track === activeTrack) { if (menuItem.track === activeTrack) {
menuItem.selected(true); menuItem.selected(true);
console.log('Updated menu UI for:', menuItem.track.label);
} else { } else {
menuItem.selected(false); menuItem.selected(false);
} }
@ -598,16 +521,11 @@ class UserPreferences {
const customSettingsMenu = controlBar.getChild('CustomSettingsMenu'); const customSettingsMenu = controlBar.getChild('CustomSettingsMenu');
if (customSettingsMenu && customSettingsMenu.refreshSubtitlesSubmenu) { if (customSettingsMenu && customSettingsMenu.refreshSubtitlesSubmenu) {
console.log('Updating custom settings menu UI...');
customSettingsMenu.refreshSubtitlesSubmenu(); customSettingsMenu.refreshSubtitlesSubmenu();
} else if (attempt < 5) { } else if (attempt < 5) {
// Retry after a short delay if menu not found // Retry after a short delay if menu not found
console.log(
`Custom settings menu not found, retrying in ${attempt * 200}ms... (attempt ${attempt})`
);
setTimeout(() => attemptUpdate(attempt + 1), attempt * 200); setTimeout(() => attemptUpdate(attempt + 1), attempt * 200);
} else {
console.log('Custom settings menu not found after multiple attempts');
} }
} catch (error) { } catch (error) {
console.error('Error updating custom settings menu UI:', error); console.error('Error updating custom settings menu UI:', error);
@ -639,12 +557,10 @@ class UserPreferences {
* @param {string} language - Subtitle language code * @param {string} language - Subtitle language code
*/ */
forceSetSubtitleLanguage(language) { forceSetSubtitleLanguage(language) {
console.log(`🚀 FORCE SAVING subtitle language: ${language}`);
const currentPrefs = this.getPreferences(); const currentPrefs = this.getPreferences();
const updatedPrefs = { ...currentPrefs, subtitleLanguage: language }; const updatedPrefs = { ...currentPrefs, subtitleLanguage: language };
try { try {
localStorage.setItem(this.storageKey, JSON.stringify(updatedPrefs)); localStorage.setItem(this.storageKey, JSON.stringify(updatedPrefs));
console.log('✅ Force saved subtitle language:', language);
} catch (error) { } catch (error) {
console.error('❌ Error force saving subtitle language:', error); console.error('❌ Error force saving subtitle language:', error);
} }
@ -664,7 +580,6 @@ class UserPreferences {
*/ */
setAutoplayPreference(autoplay) { setAutoplayPreference(autoplay) {
this.setPreference('autoplay', autoplay); this.setPreference('autoplay', autoplay);
console.log('Autoplay preference saved:', autoplay);
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long