diff --git a/frontend-tools/video-js/src/VideoJS.css b/frontend-tools/video-js/src/VideoJS.css
index c6227285..5229a6ab 100644
--- a/frontend-tools/video-js/src/VideoJS.css
+++ b/frontend-tools/video-js/src/VideoJS.css
@@ -71,10 +71,10 @@
transition: all 0.2s ease !important;
}
-.vjs-autoplay-toggle:hover {
+/* .vjs-autoplay-toggle:hover {
color: #ff4444 !important;
transform: scale(1.1) !important;
-}
+} */
.vjs-autoplay-toggle .vjs-autoplay-icon {
width: 1.2em;
@@ -360,6 +360,17 @@
visibility: visible !important;
}
+/* Hide menus/tooltips when chapters overlay is open */
+.video-js.chapters-open .vjs-menu,
+.video-js.chapters-open .vjs-menu.vjs-lock-showing,
+.video-js.chapters-open .vjs-hover-display,
+.video-js.chapters-open .vjs-time-tooltip,
+.video-js.chapters-open .vjs-progress-holder .vjs-mouse-display {
+ display: none !important;
+ opacity: 0 !important;
+ visibility: hidden !important;
+}
+
/* Responsive adjustments */
@media (max-width: 768px) {
.video-container {
@@ -681,3 +692,55 @@
text-align: center !important;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8) !important;
}
+button{cursor: pointer;}
+
+body{ font-family: Arial, Helvetica, sans-serif; box-sizing:border-box;}
+.video-wrapper{ position:relative; font-family:Arial; height:calc(100vh - 16px);}
+.video-box{ height:100%;}
+.video-wrapper .video-box .video-js{ padding:0; height:100% !important;}
+
+.video-chapter{ position:absolute; top:10px; width:min(360px, calc(100% - 20px)); border:1px solid rgba(255, 255, 255, 0.12); border-radius:12px;
+height:calc(100% - 80px); background:rgba(18, 18, 18, 0.96); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); overflow:hidden;
+box-shadow: 0 12px 30px rgba(0,0,0,0.45); right:10px;}
+.chapter-head{ padding:12px 8px 10px 16px; position:sticky; top:0; left:0; background:linear-gradient(180deg, rgba(28,28,28,0.95), rgba(18,18,18,0.95));
+border-bottom:1px solid rgba(255,255,255,0.08); z-index:2; }
+.playlist-title{ display:flex; align-items:center; gap:10px; }
+.chapter-title{ width: auto; flex: 1; min-width: 0; }
+.chapter-title h3{ margin:0; padding:0;}
+.chapter-title h3 a{color:#fff; font-size: 18px; line-height: 26px; font-weight: 700; text-decoration: none;
+white-space: nowrap; text-overflow: ellipsis; height: 28px; overflow: hidden; display: block;}
+.chapter-title p{ margin:4px 0 0; padding:0; color:#bdbdbd; font-size:12px; font-weight:400; line-height:15px;}
+.chapter-title p a{ color:#bdbdbd; font-size:12px; font-weight:400; line-height:15px; text-decoration: none;}
+
+.chapter-close{ width:40px; margin-left:auto; display:flex; align-items:center; justify-content:flex-end;}
+.chapter-close button{ background:transparent; color:#fff; border: 0; width: 40px; height: 40px; padding: 0; display: flex;
+align-items: center; justify-content: center; border-radius:8px;}
+.chapter-close button:hover{ background:rgba(255,255,255,0.1);}
+.playlist-action-menu{ display:none; justify-content:space-between; gap:10px;}
+.playlist-action-menu button{ background:transparent; border: 0; width: 40px; height: 40px; padding: 0; display: flex;
+align-items: center; justify-content: center; align-items:center; border-radius:100px; }
+.playlist-action-menu button:hover{ background:rgba(0, 0, 0, 0.1);}
+.start-action{ display:flex;}
+
+.chapter-body{ height:calc(100% - 59px); overflow:auto; }
+.chapter-body ul{ margin:0; padding:0;}
+.playlist-items a{ padding:12px; display:flex; align-items:center; text-decoration:none; gap:12px; width:100%; box-sizing:border-box;}
+.playlist-items a:hover{background:rgba(255,255,255,0.06);}
+.playlist-items.selected a{ background:rgba(255,255,255,0.14);}
+.playlist-drag-handle{ width:24px; display:flex; justify-content:center; color:#e0e0e0; font-size:12px;}
+.thumbnail-meta{ flex:1; min-width:0; padding:0;}
+.thumbnail-meta h4{ margin:0 2px 4px 0; font-size:14px; line-height:20px; font-weight:600; overflow:hidden; text-overflow:ellipsis;
+color:#fff; white-space:normal; max-height:40px; -webkit-line-clamp: 2; line-clamp: 2; display: -webkit-box; -webkit-box-orient: vertical;}
+.thumbnail-meta .meta-sub{ display:flex; gap:8px; align-items:center; }
+.thumbnail-meta .meta-sub .meta-dynamic{ color:#bdbdbd; font-size:12px; line-height:18px; }
+.thumbnail-action button{ border:0; background:transparent; color:#fff; opacity:0;}
+.playlist-items a:hover .thumbnail-action button{ opacity:1;}
+
+/* custom scrollbar for chapter list */
+.chapter-body::-webkit-scrollbar{ width:10px; }
+.chapter-body::-webkit-scrollbar-thumb{ background:rgba(255,255,255,0.18); border-radius:8px; }
+.chapter-body::-webkit-scrollbar-track{ background:transparent; }
+
+.video-box .video-js .vjs-control-bar .vjs-spacer-control{ margin-left:auto;}
+.video-js .vjs-control-bar .settings-item-svg{ display:flex;}
+.video-js .vjs-control-bar .settings-item-svg svg{width:auto !important; height: auto !important; transform:inherit !important;}
\ No newline at end of file
diff --git a/frontend-tools/video-js/src/components/chapter/ChapterList.jsx b/frontend-tools/video-js/src/components/chapter/ChapterList.jsx
new file mode 100644
index 00000000..4d9ce26b
--- /dev/null
+++ b/frontend-tools/video-js/src/components/chapter/ChapterList.jsx
@@ -0,0 +1,156 @@
+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 (
+
+ .map(
+ (option) => `
+
${option.label}
- ${option.value === currentRate ? '✓' : ''}
+ ${option.value === currentRate ? '✓' : ""}
`
- )
- .join('')}
+ )
+ .join("")}
`;
- this.settingsOverlay.appendChild(this.speedSubmenu);
+ this.settingsOverlay.appendChild(this.speedSubmenu);
+ }
+
+ createQualitySubmenu(qualities, currentValue) {
+ this.qualitySubmenu = document.createElement("div");
+ this.qualitySubmenu.className = "quality-submenu";
+
+ const header = `
+
+ `;
+
+ const optionsHtml = qualities
+ .map(
+ (q) => `
+
+ ${q.displayLabel || q.label}
+ ${q.value === currentValue ? '✓' : ""}
+
+ `
+ )
+ .join("");
+
+ this.qualitySubmenu.innerHTML = header + optionsHtml;
+ this.settingsOverlay.appendChild(this.qualitySubmenu);
+ }
+
+ getAvailableQualities() {
+ // Priority: provided options -> MEDIA_DATA JSON -> player sources -> default
+ const desiredOrder = [
+ "auto",
+ "144p",
+ "240p",
+ "360p",
+ "480p",
+ "720p",
+ "1080p",
+ ];
+
+ if (
+ Array.isArray(this.providedQualities) &&
+ this.providedQualities.length
+ ) {
+ return this.sortAndDecorateQualities(
+ this.providedQualities,
+ desiredOrder
+ );
}
- positionButton() {
- const controlBar = this.player().getChild('controlBar');
- const fullscreenToggle = controlBar.getChild('fullscreenToggle');
+ try {
+ const md = typeof window !== "undefined" ? window.MEDIA_DATA : null;
+ const jsonQualities = md?.data?.qualities;
+ if (Array.isArray(jsonQualities) && jsonQualities.length) {
+ // Expected format: [{label: '1080p', value: '1080p', src: '...'}]
+ const normalized = jsonQualities.map((q) => ({
+ label: q.label || q.value || "Auto",
+ value: (q.value || q.label || "auto").toString().toLowerCase(),
+ src: q.src || q.url || q.href,
+ type: q.type || "video/mp4",
+ }));
+ return this.sortAndDecorateQualities(normalized, desiredOrder);
+ }
+ } catch (e) {
+ // ignore
+ }
- if (this.settingsButton && fullscreenToggle) {
- // Small delay to ensure all buttons are created
- setTimeout(() => {
- const fullscreenIndex = controlBar.children().indexOf(fullscreenToggle);
- controlBar.removeChild(this.settingsButton);
- controlBar.addChild(this.settingsButton, {}, fullscreenIndex + 1);
- console.log('✓ Settings button positioned after fullscreen toggle');
- }, 50);
+ // Derive from player's current sources
+ const sources = this.player().currentSources
+ ? this.player().currentSources()
+ : this.player().currentSrc();
+ if (Array.isArray(sources) && sources.length > 0) {
+ const mapped = sources.map((s, idx) => {
+ const label =
+ s.label ||
+ s.res ||
+ this.inferLabelFromSrc(s.src) ||
+ (idx === 0 ? "Auto" : `Source ${idx + 1}`);
+ const value = String(label).toLowerCase();
+ return { label, value, src: s.src, type: s.type || "video/mp4" };
+ });
+ return this.sortAndDecorateQualities(mapped, desiredOrder);
+ }
+
+ // Default fallback
+ // Build full ordered list without src so UI is consistent; switching will require src in JSON
+ const fallback = desiredOrder.map((v) => ({
+ label: v === "auto" ? "Auto" : v,
+ value: v,
+ }));
+ return this.sortAndDecorateQualities(fallback, desiredOrder);
+ }
+
+ sortAndDecorateQualities(list, desiredOrder) {
+ const orderIndex = (val) => {
+ const i = desiredOrder.indexOf(String(val).toLowerCase());
+ return i === -1 ? 999 : i;
+ };
+ const decorated = list
+ .map((q) => {
+ const val = (q.value || q.label || "").toString().toLowerCase();
+ const baseLabel = q.label || q.value || "";
+ const is1080 = val === "1080p";
+ const displayLabel = is1080
+ ? `${baseLabel}
HD`
+ : baseLabel;
+ return { ...q, value: val, label: baseLabel, displayLabel };
+ })
+ .sort((a, b) => orderIndex(a.value) - orderIndex(b.value));
+
+ // Ensure all desired labels appear at least once (even if not provided), for consistent menu
+ const have = new Set(decorated.map((q) => q.value));
+ desiredOrder.forEach((val) => {
+ if (!have.has(val)) {
+ const baseLabel = val === "auto" ? "Auto" : val;
+ const displayLabel =
+ val === "1080p"
+ ? `${baseLabel}
HD`
+ : baseLabel;
+ decorated.push({ label: baseLabel, value: val, displayLabel });
+ }
+ });
+ // Re-sort after pushing missing
+ decorated.sort((a, b) => orderIndex(a.value) - orderIndex(b.value));
+ return decorated;
+ }
+
+ inferLabelFromSrc(src) {
+ if (!src) return null;
+ // Try to detect typical resolution markers in file name or query string
+ const match = /(?:_|\.|\/)\D*(1440p|1080p|720p|480p|360p|240p|144p)/i.exec(
+ src
+ );
+ if (match && match[1]) return match[1].toUpperCase();
+ const m2 = /(\b\d{3,4})p\b/i.exec(src);
+ if (m2 && m2[1]) return `${m2[1]}p`;
+ return null;
+ }
+
+ positionButton() {
+ const controlBar = this.player().getChild("controlBar");
+ const fullscreenToggle = controlBar.getChild("fullscreenToggle");
+
+ if (this.settingsButton && fullscreenToggle) {
+ // Small delay to ensure all buttons are created
+ setTimeout(() => {
+ const fullscreenIndex = controlBar.children().indexOf(fullscreenToggle);
+ controlBar.removeChild(this.settingsButton);
+ controlBar.addChild(this.settingsButton, {}, fullscreenIndex + 1);
+ console.log("✓ Settings button positioned after fullscreen toggle");
+ }, 50);
+ }
+ }
+
+ setupEventListeners() {
+ // Settings item clicks
+ this.settingsOverlay.addEventListener("click", (e) => {
+ e.stopPropagation();
+
+ if (e.target.closest('[data-setting="playback-speed"]')) {
+ this.speedSubmenu.style.display = "flex";
+ this.qualitySubmenu.style.display = "none";
+ }
+
+ if (e.target.closest('[data-setting="quality"]')) {
+ this.qualitySubmenu.style.display = "flex";
+ this.speedSubmenu.style.display = "none";
+ }
+ });
+
+ // Speed submenu header (back button)
+ this.speedSubmenu
+ .querySelector(".submenu-header")
+ .addEventListener("click", () => {
+ this.speedSubmenu.style.display = "none";
+ });
+
+ // Quality submenu header (back button)
+ this.qualitySubmenu
+ .querySelector(".submenu-header")
+ .addEventListener("click", () => {
+ this.qualitySubmenu.style.display = "none";
+ });
+
+ // Speed option clicks
+ this.speedSubmenu.addEventListener("click", (e) => {
+ const speedOption = e.target.closest(".speed-option");
+ if (speedOption) {
+ const speed = parseFloat(speedOption.dataset.speed);
+ this.handleSpeedChange(speed, speedOption);
+ }
+ });
+
+ // Quality option clicks
+ this.qualitySubmenu.addEventListener("click", (e) => {
+ const qualityOption = e.target.closest(".quality-option");
+ if (qualityOption) {
+ const value = qualityOption.dataset.quality;
+ this.handleQualityChange(value, qualityOption);
+ }
+ });
+
+ // Close menu when clicking outside
+ document.addEventListener("click", this.handleClickOutside);
+
+ // Add hover effects
+ this.settingsOverlay.addEventListener("mouseover", (e) => {
+ const item = e.target.closest(".settings-item, .speed-option");
+ if (item && !item.style.background.includes("0.1")) {
+ item.style.background = "rgba(255, 255, 255, 0.05)";
+ }
+ });
+
+ this.settingsOverlay.addEventListener("mouseout", (e) => {
+ const item = e.target.closest(".settings-item, .speed-option");
+ if (item && !item.style.background.includes("0.1")) {
+ item.style.background = "transparent";
+ }
+ });
+ }
+
+ toggleSettings(e) {
+ e.stopPropagation();
+ const isVisible = this.settingsOverlay.style.display === "block";
+ this.settingsOverlay.style.display = isVisible ? "none" : "block";
+ this.speedSubmenu.style.display = "none"; // Hide submenu when main menu toggles
+ if (this.qualitySubmenu) this.qualitySubmenu.style.display = "none";
+ }
+
+ handleSpeedChange(speed, speedOption) {
+ // Update player speed
+ this.player().playbackRate(speed);
+
+ // Save preference
+ this.userPreferences.setPreference("playbackRate", speed);
+
+ // Update UI
+ this.speedSubmenu.querySelectorAll(".speed-option").forEach((opt) => {
+ opt.classList.remove("active");
+ opt.style.background = "transparent";
+ const check = opt.querySelector(".checkmark");
+ if (check) check.remove();
+ });
+
+ speedOption.classList.add("active");
+ speedOption.style.background = "rgba(255, 255, 255, 0.1)";
+ speedOption.insertAdjacentHTML(
+ "beforeend",
+ '
✓'
+ );
+
+ // Update main menu display
+ const currentSpeedDisplay =
+ this.settingsOverlay.querySelector(".current-speed");
+ const speedLabel = speed === 1 ? "Normal" : `${speed}`;
+ currentSpeedDisplay.textContent = speedLabel;
+
+ // Close only the speed submenu (keep overlay open)
+ this.speedSubmenu.style.display = "none";
+
+ console.log("Playback speed preference saved:", speed);
+ }
+
+ handleQualityChange(value, qualityOption) {
+ const qualities = this.getAvailableQualities();
+ const selected = qualities.find((q) => String(q.value) === String(value));
+
+ // Save preference
+ this.userPreferences.setQualityPreference(value);
+
+ // Update UI
+ this.qualitySubmenu.querySelectorAll(".quality-option").forEach((opt) => {
+ opt.classList.remove("active");
+ opt.style.background = "transparent";
+ const check = opt.querySelector(".checkmark");
+ if (check) check.remove();
+ });
+
+ qualityOption.classList.add("active");
+ qualityOption.style.background = "rgba(255, 255, 255, 0.1)";
+ qualityOption.insertAdjacentHTML(
+ "beforeend",
+ '
✓'
+ );
+
+ // Update main menu display
+ const currentQualityDisplay =
+ this.settingsOverlay.querySelector(".current-quality");
+ currentQualityDisplay.innerHTML =
+ selected?.displayLabel || selected?.label || String(value);
+
+ // Perform source switch if we have src defined
+ if (selected?.src) {
+ const player = this.player();
+ const wasPaused = player.paused();
+ const currentTime = player.currentTime();
+ const rate = player.playbackRate();
+
+ // Try to preserve active subtitle track
+ const textTracks = player.textTracks();
+ let activeSubtitleLang = null;
+ for (let i = 0; i < textTracks.length; i++) {
+ const track = textTracks[i];
+ if (track.kind === "subtitles" && track.mode === "showing") {
+ activeSubtitleLang = track.language;
+ break;
}
- }
+ }
- setupEventListeners() {
- // Settings item clicks
- this.settingsOverlay.addEventListener('click', (e) => {
- e.stopPropagation();
+ console.log("Switching quality to", selected.label, selected.src);
- if (e.target.closest('[data-setting="playback-speed"]')) {
- this.speedSubmenu.style.display = 'flex';
- }
- });
+ player.addClass("vjs-changing-resolution");
+ player.src({ src: selected.src, type: selected.type || "video/mp4" });
- // Speed submenu header (back button)
- this.speedSubmenu.querySelector('.submenu-header').addEventListener('click', () => {
- this.speedSubmenu.style.display = 'none';
- });
-
- // Speed option clicks
- this.speedSubmenu.addEventListener('click', (e) => {
- const speedOption = e.target.closest('.speed-option');
- if (speedOption) {
- const speed = parseFloat(speedOption.dataset.speed);
- this.handleSpeedChange(speed, speedOption);
- }
- });
-
- // Close menu when clicking outside
- document.addEventListener('click', this.handleClickOutside);
-
- // Add hover effects
- this.settingsOverlay.addEventListener('mouseover', (e) => {
- const item = e.target.closest('.settings-item, .speed-option');
- if (item && !item.style.background.includes('0.1')) {
- item.style.background = 'rgba(255, 255, 255, 0.05)';
- }
- });
-
- this.settingsOverlay.addEventListener('mouseout', (e) => {
- const item = e.target.closest('.settings-item, .speed-option');
- if (item && !item.style.background.includes('0.1')) {
- item.style.background = 'transparent';
- }
- });
- }
-
- toggleSettings(e) {
- e.stopPropagation();
- const isVisible = this.settingsOverlay.style.display === 'block';
- this.settingsOverlay.style.display = isVisible ? 'none' : 'block';
- this.speedSubmenu.style.display = 'none'; // Hide submenu when main menu toggles
- }
-
- handleSpeedChange(speed, speedOption) {
- // Update player speed
- this.player().playbackRate(speed);
-
- // Save preference
- this.userPreferences.setPreference('playbackRate', speed);
-
- // Update UI
- document.querySelectorAll('.speed-option').forEach((opt) => {
- opt.classList.remove('active');
- opt.style.background = 'transparent';
- opt.querySelector('span:last-child')?.remove();
- });
-
- speedOption.classList.add('active');
- speedOption.style.background = 'rgba(255, 255, 255, 0.1)';
- speedOption.insertAdjacentHTML('beforeend', '
✓');
-
- // Update main menu display
- const currentSpeedDisplay = this.settingsOverlay.querySelector('.current-speed');
- const speedLabel = speed === 1 ? 'Normal' : `${speed}`;
- currentSpeedDisplay.textContent = speedLabel;
-
- // Hide menus
- this.settingsOverlay.style.display = 'none';
- this.speedSubmenu.style.display = 'none';
-
- console.log('Playback speed preference saved:', speed);
- }
-
- handleClickOutside(e) {
- if (
- this.settingsOverlay &&
- this.settingsButton &&
- !this.settingsOverlay.contains(e.target) &&
- !this.settingsButton.el().contains(e.target)
- ) {
- this.settingsOverlay.style.display = 'none';
- this.speedSubmenu.style.display = 'none';
- }
- }
-
- dispose() {
- // Remove event listeners
- document.removeEventListener('click', this.handleClickOutside);
-
- // Remove DOM elements
- if (this.settingsOverlay) {
- this.settingsOverlay.remove();
+ const onLoaded = () => {
+ // Restore time, rate, subtitles
+ try {
+ player.playbackRate(rate);
+ } catch (e) {}
+ try {
+ if (!isNaN(currentTime)) player.currentTime(currentTime);
+ } catch (e) {}
+ if (!wasPaused) {
+ player.play().catch(() => {});
}
- super.dispose();
+ // Restore subtitles
+ if (activeSubtitleLang) {
+ const tt = player.textTracks();
+ for (let i = 0; i < tt.length; i++) {
+ const t = tt[i];
+ if (t.kind === "subtitles") {
+ t.mode =
+ t.language === activeSubtitleLang ? "showing" : "disabled";
+ }
+ }
+ }
+
+ // Ensure Subtitles (CC) button remains visible after source switch
+ try {
+ const controlBar = player.getChild("controlBar");
+ const names = [
+ "subtitlesButton",
+ "textTrackButton",
+ "subsCapsButton",
+ ];
+ for (const n of names) {
+ const btn = controlBar && controlBar.getChild(n);
+ if (btn) {
+ if (typeof btn.show === "function") btn.show();
+ const el = btn.el && btn.el();
+ if (el) {
+ el.style.display = "";
+ el.style.visibility = "";
+ }
+ }
+ }
+ } catch (e) {
+ // noop
+ }
+
+ player.removeClass("vjs-changing-resolution");
+ player.off("loadedmetadata", onLoaded);
+ };
+
+ player.on("loadedmetadata", onLoaded);
}
+
+ // Close overlay to avoid covering the CC button
+ if (this.qualitySubmenu) this.qualitySubmenu.style.display = "none";
+ this.settingsOverlay.style.display = "none";
+
+ console.log("Quality preference saved:", value);
+ }
+
+ handleClickOutside(e) {
+ if (
+ this.settingsOverlay &&
+ this.settingsButton &&
+ !this.settingsOverlay.contains(e.target) &&
+ !this.settingsButton.el().contains(e.target)
+ ) {
+ this.settingsOverlay.style.display = "none";
+ this.speedSubmenu.style.display = "none";
+ if (this.qualitySubmenu) this.qualitySubmenu.style.display = "none";
+ }
+ }
+
+ dispose() {
+ // Remove event listeners
+ document.removeEventListener("click", this.handleClickOutside);
+
+ // Remove DOM elements
+ if (this.settingsOverlay) {
+ this.settingsOverlay.remove();
+ }
+
+ super.dispose();
+ }
}
// Set component name for Video.js
-CustomSettingsMenu.prototype.controlText_ = 'Settings Menu';
+CustomSettingsMenu.prototype.controlText_ = "Settings Menu";
// Register the component with Video.js
-videojs.registerComponent('CustomSettingsMenu', CustomSettingsMenu);
+videojs.registerComponent("CustomSettingsMenu", CustomSettingsMenu);
export default CustomSettingsMenu;
diff --git a/frontend-tools/video-js/src/components/video-player/VideoJSPlayer.jsx b/frontend-tools/video-js/src/components/video-player/VideoJSPlayer.jsx
index 81f0e314..decb9d5c 100644
--- a/frontend-tools/video-js/src/components/video-player/VideoJSPlayer.jsx
+++ b/frontend-tools/video-js/src/components/video-player/VideoJSPlayer.jsx
@@ -26,556 +26,561 @@ function VideoJSPlayer() {
typeof window !== 'undefined' && window.MEDIA_DATA
? window.MEDIA_DATA
: {
- data: {
- related_media: [
- {
- friendly_token: 'jgLkic37V',
- url: 'http://localhost/view?m=jgLkic37V',
- api_url: 'http://localhost/api/v1/media/jgLkic37V',
- user: 'admin',
- title: 'Screenshot20250312at15.58.35.png',
- description: '',
- add_date: '2025-03-12T14:28:24.757807Z',
- views: 5,
- media_type: 'image',
- state: 'public',
- duration: 0,
- thumbnail_url:
- 'http://localhost/media/original/thumbnails/user/admin/ad827f53568c4565b69ee59da88966c8.Screenshot20250312at15.58.35.png.jpg',
- is_reviewed: true,
- preview_url: null,
- author_name: '',
- author_profile: 'http://localhost/user/admin/',
- author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
- encoding_status: 'success',
- likes: 1,
- dislikes: 0,
- reported_times: 0,
- featured: false,
- user_featured: false,
- size: null,
- },
- {
- friendly_token: '54Ip78oPK',
- url: 'http://localhost/view?m=54Ip78oPK',
- api_url: 'http://localhost/api/v1/media/54Ip78oPK',
- user: 'admin',
- title: 'att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov (Trimmed) 2',
- description: '',
- add_date: '2025-05-19T01:22:10.354407+01:00',
- views: 8,
- media_type: 'video',
- state: 'public',
- duration: 1,
- thumbnail_url:
- 'http://localhost/media/original/thumbnails/user/admin/ca19044d26af4eada1669c3373c771cb_nhCvKcl.fd1d31460e614ca9b4645228e62c9eec.att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov.jpg',
- is_reviewed: true,
- preview_url:
- '/media/encoded/1/admin/ca19044d26af4eada1669c3373c771cb.fd1d31460e614ca9b4645228e62c9eec.tmpjohj96hu.gif',
- author_name: '',
- author_profile: 'http://localhost/user/admin/',
- author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
- encoding_status: 'success',
- likes: 1,
- dislikes: 0,
- reported_times: 0,
- featured: false,
- user_featured: false,
- size: '0.6MB',
- },
- {
- friendly_token: '0VpZtWduU',
- url: 'http://localhost/view?m=0VpZtWduU',
- api_url: 'http://localhost/api/v1/media/0VpZtWduU',
- user: 'admin',
- title: 'att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov (Trimmed) (Trimmed)',
- description: '',
- add_date: '2025-05-19T01:30:55.374863+01:00',
- views: 13,
- media_type: 'video',
- state: 'public',
- duration: 6,
- thumbnail_url:
- 'http://localhost/media/original/thumbnails/user/admin/46b2bec8d79f496682640ce99b5c53af_RA16Oba.884cac6a306e4ae9995726fcd3c7aa49.fd1d31460e614ca9b4645228e62c9eec.att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov.jpg',
- is_reviewed: true,
- preview_url:
- '/media/encoded/1/admin/46b2bec8d79f496682640ce99b5c53af.884cac6a306e4ae9995726fcd3c7aa49.fd1d31460e614ca9b4645228e62c9eec.tmpjohj96hu.gif',
- author_name: '',
- author_profile: 'http://localhost/user/admin/',
- author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
- encoding_status: 'success',
- likes: 1,
- dislikes: 0,
- reported_times: 0,
- featured: false,
- user_featured: false,
- size: '4.0MB',
- },
- {
- friendly_token: 'efM2aLP4P',
- url: 'http://localhost/view?m=efM2aLP4P',
- api_url: 'http://localhost/api/v1/media/efM2aLP4P',
- user: 'admin',
- title: 'att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov (Trimmed) 1',
- description: '',
- add_date: '2025-05-19T01:22:08.117300+01:00',
- views: 11,
- media_type: 'video',
- state: 'public',
- duration: 8,
- thumbnail_url:
- 'http://localhost/media/original/thumbnails/user/admin/c27f570a84d64cdc857f87c50dace993_Ma3ap4p.fd1d31460e614ca9b4645228e62c9eec.att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov.jpg',
- is_reviewed: true,
- preview_url:
- '/media/encoded/1/admin/c27f570a84d64cdc857f87c50dace993.fd1d31460e614ca9b4645228e62c9eec.tmpjohj96hu.gif',
- author_name: '',
- author_profile: 'http://localhost/user/admin/',
- author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
- encoding_status: 'success',
- likes: 2,
- dislikes: 0,
- reported_times: 0,
- featured: false,
- user_featured: false,
- size: '4.8MB',
- },
- {
- friendly_token: '8RUHRjnji',
- url: 'http://localhost/view?m=8RUHRjnji',
- api_url: 'http://localhost/api/v1/media/8RUHRjnji',
- user: 'admin',
- title: 'att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov (Trimmed)',
- description: '',
- add_date: '2025-05-19T01:29:48.308159+01:00',
- views: 7,
- media_type: 'video',
- state: 'public',
- duration: 7,
- thumbnail_url:
- 'http://localhost/media/original/thumbnails/user/admin/884cac6a306e4ae9995726fcd3c7aa49_fT7pnM4.fd1d31460e614ca9b4645228e62c9eec.att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov.jpg',
- is_reviewed: true,
- preview_url:
- '/media/encoded/1/admin/884cac6a306e4ae9995726fcd3c7aa49.fd1d31460e614ca9b4645228e62c9eec.tmpjohj96hu.gif',
- author_name: '',
- author_profile: 'http://localhost/user/admin/',
- author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
- encoding_status: 'success',
- likes: 1,
- dislikes: 0,
- reported_times: 0,
- featured: false,
- user_featured: false,
- size: '4.6MB',
- },
- {
- friendly_token: 'N5Vpnqfpf',
- url: 'http://localhost/view?m=N5Vpnqfpf',
- api_url: 'http://localhost/api/v1/media/N5Vpnqfpf',
- user: 'admin',
- title: 'att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov',
- description: '',
- add_date: '2025-05-19T01:15:03.594646+01:00',
- views: 11,
- media_type: 'video',
- state: 'public',
- duration: 37,
- thumbnail_url:
- 'http://localhost/media/original/thumbnails/user/admin/e549ebb4b1dc41a48cf76ebccf592836_1xqZHHc.att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov.jpg',
- is_reviewed: true,
- preview_url:
- '/media/encoded/1/admin/e549ebb4b1dc41a48cf76ebccf592836.tmpu6x4f3js.gif',
- author_name: '',
- author_profile: 'http://localhost/user/admin/',
- author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
- encoding_status: 'success',
- likes: 1,
- dislikes: 0,
- reported_times: 0,
- featured: false,
- user_featured: false,
- size: '22.3MB',
- },
- {
- friendly_token: 'MmyuYCEfC',
- url: 'http://localhost/view?m=MmyuYCEfC',
- api_url: 'http://localhost/api/v1/media/MmyuYCEfC',
- user: 'admin',
- title: 'JmailLogIdeasforYiannis.mp4',
- description: 'check the 00:12 and then the 00:39',
- add_date: '2025-03-12T02:37:42Z',
- views: 49,
- media_type: 'video',
- state: 'public',
- duration: 112,
- thumbnail_url:
- 'http://localhost/media/original/thumbnails/user/admin/3d4e6f1c7e014720bc6390ed9d61aba5_BppzBiz.JmailLogIdeasforYiannis.mp4.jpg',
- is_reviewed: true,
- preview_url:
- '/media/encoded/1/admin/3d4e6f1c7e014720bc6390ed9d61aba5.tmpcjd0bvr5.gif',
- author_name: '',
- author_profile: 'http://localhost/user/admin/',
- author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
- encoding_status: 'success',
- likes: 1,
- dislikes: 0,
- reported_times: 0,
- featured: false,
- user_featured: false,
- size: '40.6MB',
- },
- {
- friendly_token: 'E8ia0lfir',
- url: 'http://localhost/view?m=E8ia0lfir',
- api_url: 'http://localhost/api/v1/media/E8ia0lfir',
- user: 'admin',
- title: 'att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov (Trimmed)',
- description: '',
- add_date: '2025-05-19T01:22:27.251447+01:00',
- views: 12,
- media_type: 'video',
- state: 'public',
- duration: 37,
- thumbnail_url:
- 'http://localhost/media/original/thumbnails/user/admin/8572a325a9d649f79cc60181fbece78c_0H5arHy.fd1d31460e614ca9b4645228e62c9eec.att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov.jpg',
- is_reviewed: true,
- preview_url:
- '/media/encoded/1/admin/8572a325a9d649f79cc60181fbece78c.fd1d31460e614ca9b4645228e62c9eec.tmpjohj96hu.gif',
- author_name: '',
- author_profile: 'http://localhost/user/admin/',
- author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
- encoding_status: 'success',
- likes: 1,
- dislikes: 0,
- reported_times: 0,
- featured: false,
- user_featured: false,
- size: '22.4MB',
- },
- {
- friendly_token: 'eZAl84zg6',
- url: 'http://localhost/view?m=eZAl84zg6',
- api_url: 'http://localhost/api/v1/media/eZAl84zg6',
- user: 'admin',
- title: '20257855hd_1920_1080_60fps.mp4',
- description: '',
- add_date: '2025-03-12T02:35:59.779098Z',
- views: 29,
- media_type: 'video',
- state: 'public',
- duration: 90,
- thumbnail_url:
- 'http://localhost/media/original/thumbnails/user/admin/ed9c61a72c0445debfbedee011b6eba1_8HjWGZX.20257855hd_1920_1080_60fps.mp4.jpg',
- is_reviewed: true,
- preview_url:
- '/media/encoded/1/admin/ed9c61a72c0445debfbedee011b6eba1.tmpil72jnbv.gif',
- author_name: '',
- author_profile: 'http://localhost/user/admin/',
- author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
- encoding_status: 'success',
- likes: 1,
- dislikes: 0,
- reported_times: 0,
- featured: false,
- user_featured: false,
- size: '64.4MB',
- },
- {
- friendly_token: '6WShYNxZx',
- url: 'http://localhost/view?m=6WShYNxZx',
- api_url: 'http://localhost/api/v1/media/6WShYNxZx',
- user: 'admin',
- title: 'SampleVideo_1280x720_30mb.mp4 test abc',
- description: 'test 123 yiannis',
- add_date: '2025-04-14T00:00:00+01:00',
- views: 43,
- media_type: 'video',
- state: 'public',
- duration: 171,
- thumbnail_url:
- 'http://localhost/media/original/thumbnails/user/admin/c868df836ac34688b876e741b58ada93_LRvu388.SampleVideo_1280x720_30mb.mp4.jpg',
- is_reviewed: true,
- preview_url:
- '/media/encoded/1/admin/c868df836ac34688b876e741b58ada93.tmpyl_r01_l.gif',
- author_name: '',
- author_profile: 'http://localhost/user/admin/',
- author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
- encoding_status: 'success',
- likes: 3,
- dislikes: 0,
- reported_times: 0,
- featured: false,
- user_featured: false,
- size: '31.5MB',
- },
- {
- friendly_token: 'SOgLTsrAH',
- url: 'http://localhost/view?m=SOgLTsrAH',
- api_url: 'http://localhost/api/v1/media/SOgLTsrAH',
- user: 'admin',
- title: 'att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov (Trimmed) (Trimmed) 1',
- description: '',
- add_date: '2025-05-19T01:31:11.057879+01:00',
- views: 8,
- media_type: 'video',
- state: 'public',
- duration: 2,
- thumbnail_url:
- 'http://localhost/media/original/thumbnails/user/admin/46c75315947e4a5f9dd464c1721206fd_6KJzqBH.884cac6a306e4ae9995726fcd3c7aa49.fd1d31460e614ca9b4645228e62c9eec.att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov.jpg',
- is_reviewed: true,
- preview_url:
- '/media/encoded/1/admin/46c75315947e4a5f9dd464c1721206fd.884cac6a306e4ae9995726fcd3c7aa49.fd1d31460e614ca9b4645228e62c9eec.tmpjohj96hu.gif',
- author_name: '',
- author_profile: 'http://localhost/user/admin/',
- author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
- encoding_status: 'success',
- likes: 1,
- dislikes: 0,
- reported_times: 0,
- featured: false,
- user_featured: false,
- size: '1.5MB',
- },
- {
- friendly_token: 'PoDk009ue',
- url: 'http://localhost/view?m=PoDk009ue',
- api_url: 'http://localhost/api/v1/media/PoDk009ue',
- user: 'admin',
- title: 'att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov',
- description: '',
- add_date: '2025-05-19T01:17:44.296445+01:00',
- views: 12,
- media_type: 'video',
- state: 'public',
- duration: 37,
- thumbnail_url:
- 'http://localhost/media/original/thumbnails/user/admin/fd1d31460e614ca9b4645228e62c9eec_DB8nan8.att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov.jpg',
- is_reviewed: true,
- preview_url:
- '/media/encoded/1/admin/fd1d31460e614ca9b4645228e62c9eec.tmpjohj96hu.gif',
- author_name: '',
- author_profile: 'http://localhost/user/admin/',
- author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
- encoding_status: 'success',
- likes: 1,
- dislikes: 0,
- reported_times: 0,
- featured: false,
- user_featured: false,
- size: '22.4MB',
- },
- {
- friendly_token: 'b7e06rUz5',
- url: 'http://localhost/view?m=b7e06rUz5',
- api_url: 'http://localhost/api/v1/media/b7e06rUz5',
- user: 'admin',
- title: 'Proto.cyTimesheet03_03_202507_03_2025.pdf',
- description: '',
- add_date: '2025-03-12T14:29:15.245599Z',
- views: 16,
- media_type: 'pdf',
- state: 'public',
- duration: 0,
- thumbnail_url: null,
- is_reviewed: true,
- preview_url: null,
- author_name: '',
- author_profile: 'http://localhost/user/admin/',
- author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
- encoding_status: 'success',
- likes: 1,
- dislikes: 0,
- reported_times: 0,
- featured: false,
- user_featured: false,
- size: null,
- },
- {
- friendly_token: 'wUu76zL5a',
- url: 'http://localhost/view?m=wUu76zL5a',
- api_url: 'http://localhost/api/v1/media/wUu76zL5a',
- user: 'admin',
- title: 'att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov (Trimmed) 2 (Trimmed)',
- description: '',
- add_date: '2025-05-19T01:28:52.829907+01:00',
- views: 2,
- media_type: 'video',
- state: 'public',
- duration: 0,
- thumbnail_url:
- 'http://localhost/media/original/thumbnails/user/admin/1b3e7dc2431b46a5b7e40b297662137a_1V0DJz5.ca19044d26af4eada1669c3373c771cb.fd1d31460e614ca9b4645228e62c9eec.att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov.jpg',
- is_reviewed: true,
- preview_url:
- '/media/encoded/1/admin/1b3e7dc2431b46a5b7e40b297662137a.ca19044d26af4eada1669c3373c771cb.fd1d31460e614ca9b4645228e62c9eec.tmpjohj96hu.gif',
- author_name: '',
- author_profile: 'http://localhost/user/admin/',
- author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
- encoding_status: 'success',
- likes: 1,
- dislikes: 0,
- reported_times: 0,
- featured: false,
- user_featured: false,
- size: '0.3MB',
- },
- {
- friendly_token: 'JBjMHLfLZ',
- url: 'http://localhost/view?m=JBjMHLfLZ',
- api_url: 'http://localhost/api/v1/media/JBjMHLfLZ',
- user: 'admin',
- title: 'att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov (Trimmed) (Trimmed) 2',
- description: '',
- add_date: '2025-05-19T01:31:11.848403+01:00',
- views: 19,
- media_type: 'video',
- state: 'public',
- duration: 4,
- thumbnail_url:
- 'http://localhost/media/original/thumbnails/user/admin/7759ca3e0ba1482aa9149201bd14139d_ISBMqD4.884cac6a306e4ae9995726fcd3c7aa49.fd1d31460e614ca9b4645228e62c9eec.att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov.jpg',
- is_reviewed: true,
- preview_url:
- '/media/encoded/1/admin/7759ca3e0ba1482aa9149201bd14139d.884cac6a306e4ae9995726fcd3c7aa49.fd1d31460e614ca9b4645228e62c9eec.tmpjohj96hu.gif',
- author_name: '',
- author_profile: 'http://localhost/user/admin/',
- author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
- encoding_status: 'success',
- likes: 1,
- dislikes: 0,
- reported_times: 0,
- featured: false,
- user_featured: false,
- size: '2.5MB',
- },
- {
- friendly_token: 'w13x5uUXc',
- url: 'http://localhost/view?m=w13x5uUXc',
- api_url: 'http://localhost/api/v1/media/w13x5uUXc',
- user: 'admin',
- title: 'Web357202503112025_05_33_AM.png',
- description: '',
- add_date: '2025-03-12T14:27:55.078360Z',
- views: 3,
- media_type: 'image',
- state: 'public',
- duration: 0,
- thumbnail_url:
- 'http://localhost/media/original/thumbnails/user/admin/1da967bcdca540efa1d2362af2f3a34b.Web357202503112025_05_33_AM.png.jpg',
- is_reviewed: true,
- preview_url: null,
- author_name: '',
- author_profile: 'http://localhost/user/admin/',
- author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
- encoding_status: 'success',
- likes: 1,
- dislikes: 0,
- reported_times: 0,
- featured: false,
- user_featured: false,
- size: null,
- },
- {
- friendly_token: 'sZxPpdon5',
- url: 'http://localhost/view?m=sZxPpdon5',
- api_url: 'http://localhost/api/v1/media/sZxPpdon5',
- user: 'admin',
- title: 'JmailLogIdeasforYiannis.mp4 (Trimmed)',
- description: 'check the 00:12 and then the 00:39',
- add_date: '2025-05-15T02:16:19.580745+01:00',
- views: 9,
- media_type: 'video',
- state: 'public',
- duration: 17,
- thumbnail_url:
- 'http://localhost/media/original/thumbnails/user/admin/7191a59cbfcd402ebed400e604a76840_5WRh0cp.3d4e6f1c7e014720bc6390ed9d61aba5.JmailLogIdeasforYiannis.mp4.jpg',
- is_reviewed: true,
- preview_url:
- '/media/encoded/1/admin/7191a59cbfcd402ebed400e604a76840.3d4e6f1c7e014720bc6390ed9d61aba5.tmpcjd0bvr5.gif',
- author_name: '',
- author_profile: 'http://localhost/user/admin/',
- author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
- encoding_status: 'success',
- likes: 1,
- dislikes: 0,
- reported_times: 0,
- featured: false,
- user_featured: false,
- size: '4.5MB',
- },
- {
- friendly_token: 'TCycDZROI',
- url: 'http://localhost/view?m=TCycDZROI',
- api_url: 'http://localhost/api/v1/media/TCycDZROI',
- user: 'admin',
- title: 'JmailLogIdeasforYiannis.mp4 (Trimmed)',
- description: 'check the 00:12 and then the 00:39',
- add_date: '2025-05-15T02:14:58.855935+01:00',
- views: 8,
- media_type: 'video',
- state: 'public',
- duration: 112,
- thumbnail_url:
- 'http://localhost/media/original/thumbnails/user/admin/5cbfbe93d06c453487c65d9d54749318_CUIZl8y.3d4e6f1c7e014720bc6390ed9d61aba5.JmailLogIdeasforYiannis.mp4.jpg',
- is_reviewed: true,
- preview_url:
- '/media/encoded/1/admin/5cbfbe93d06c453487c65d9d54749318.3d4e6f1c7e014720bc6390ed9d61aba5.tmpcjd0bvr5.gif',
- author_name: '',
- author_profile: 'http://localhost/user/admin/',
- author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
- encoding_status: 'success',
- likes: 1,
- dislikes: 0,
- reported_times: 0,
- featured: false,
- user_featured: false,
- size: '40.6MB',
- },
- {
- friendly_token: 'YjGJafibO',
- url: 'http://localhost/view?m=YjGJafibO',
- api_url: 'http://localhost/api/v1/media/YjGJafibO',
- user: 'admin',
- title: 'file_example_MP4_480_1_5MG.mp4',
- description: '',
- add_date: '2025-04-14T00:07:11.074523+01:00',
- views: 18,
- media_type: 'video',
- state: 'public',
- duration: 30,
- thumbnail_url:
- 'http://localhost/media/original/thumbnails/user/admin/e76f4fff2b3d42adbadb0f1204b21b81_s8EEvSE.file_example_MP4_480_1_5MG.mp4.jpg',
- is_reviewed: true,
- preview_url:
- '/media/encoded/1/admin/e76f4fff2b3d42adbadb0f1204b21b81.tmp04azp_tc.gif',
- author_name: '',
- author_profile: 'http://localhost/user/admin/',
- author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
- encoding_status: 'success',
- likes: 2,
- dislikes: 0,
- reported_times: 0,
- featured: false,
- user_featured: false,
- size: '1.6MB',
- },
- ],
- },
- previewSprite: {
- url: 'https://demo.mediacms.io/media/original/thumbnails/user/markos/fe4933d67b884d4da507dd60e77f7438.VID_20200909_141053.mp4sprites.jpg',
- frame: { width: 160, height: 90, seconds: 10 },
- },
- siteUrl: '',
- nextLink: 'https://demo.mediacms.io/view?m=YjGJafibO',
- },
+ data: {
+ related_media: [
+ {
+ friendly_token: 'jgLkic37V',
+ url: 'http://localhost/view?m=jgLkic37V',
+ api_url: 'http://localhost/api/v1/media/jgLkic37V',
+ user: 'admin',
+ title: 'Screenshot20250312at15.58.35.png',
+ description: '',
+ add_date: '2025-03-12T14:28:24.757807Z',
+ views: 5,
+ media_type: 'image',
+ state: 'public',
+ duration: 0,
+ thumbnail_url:
+ 'http://localhost/media/original/thumbnails/user/admin/ad827f53568c4565b69ee59da88966c8.Screenshot20250312at15.58.35.png.jpg',
+ is_reviewed: true,
+ preview_url: null,
+ author_name: '',
+ author_profile: 'http://localhost/user/admin/',
+ author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
+ encoding_status: 'success',
+ likes: 1,
+ dislikes: 0,
+ reported_times: 0,
+ featured: false,
+ user_featured: false,
+ size: null,
+ },
+ {
+ friendly_token: '54Ip78oPK',
+ url: 'http://localhost/view?m=54Ip78oPK',
+ api_url: 'http://localhost/api/v1/media/54Ip78oPK',
+ user: 'admin',
+ title: 'att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov (Trimmed) 2',
+ description: '',
+ add_date: '2025-05-19T01:22:10.354407+01:00',
+ views: 8,
+ media_type: 'video',
+ state: 'public',
+ duration: 1,
+ thumbnail_url:
+ 'http://localhost/media/original/thumbnails/user/admin/ca19044d26af4eada1669c3373c771cb_nhCvKcl.fd1d31460e614ca9b4645228e62c9eec.att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov.jpg',
+ is_reviewed: true,
+ preview_url:
+ '/media/encoded/1/admin/ca19044d26af4eada1669c3373c771cb.fd1d31460e614ca9b4645228e62c9eec.tmpjohj96hu.gif',
+ author_name: '',
+ author_profile: 'http://localhost/user/admin/',
+ author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
+ encoding_status: 'success',
+ likes: 1,
+ dislikes: 0,
+ reported_times: 0,
+ featured: false,
+ user_featured: false,
+ size: '0.6MB',
+ },
+ {
+ friendly_token: '0VpZtWduU',
+ url: 'http://localhost/view?m=0VpZtWduU',
+ api_url: 'http://localhost/api/v1/media/0VpZtWduU',
+ user: 'admin',
+ title: 'att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov (Trimmed) (Trimmed)',
+ description: '',
+ add_date: '2025-05-19T01:30:55.374863+01:00',
+ views: 13,
+ media_type: 'video',
+ state: 'public',
+ duration: 6,
+ thumbnail_url:
+ 'http://localhost/media/original/thumbnails/user/admin/46b2bec8d79f496682640ce99b5c53af_RA16Oba.884cac6a306e4ae9995726fcd3c7aa49.fd1d31460e614ca9b4645228e62c9eec.att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov.jpg',
+ is_reviewed: true,
+ preview_url:
+ '/media/encoded/1/admin/46b2bec8d79f496682640ce99b5c53af.884cac6a306e4ae9995726fcd3c7aa49.fd1d31460e614ca9b4645228e62c9eec.tmpjohj96hu.gif',
+ author_name: '',
+ author_profile: 'http://localhost/user/admin/',
+ author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
+ encoding_status: 'success',
+ likes: 1,
+ dislikes: 0,
+ reported_times: 0,
+ featured: false,
+ user_featured: false,
+ size: '4.0MB',
+ },
+ {
+ friendly_token: 'efM2aLP4P',
+ url: 'http://localhost/view?m=efM2aLP4P',
+ api_url: 'http://localhost/api/v1/media/efM2aLP4P',
+ user: 'admin',
+ title: 'att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov (Trimmed) 1',
+ description: '',
+ add_date: '2025-05-19T01:22:08.117300+01:00',
+ views: 11,
+ media_type: 'video',
+ state: 'public',
+ duration: 8,
+ thumbnail_url:
+ 'http://localhost/media/original/thumbnails/user/admin/c27f570a84d64cdc857f87c50dace993_Ma3ap4p.fd1d31460e614ca9b4645228e62c9eec.att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov.jpg',
+ is_reviewed: true,
+ preview_url:
+ '/media/encoded/1/admin/c27f570a84d64cdc857f87c50dace993.fd1d31460e614ca9b4645228e62c9eec.tmpjohj96hu.gif',
+ author_name: '',
+ author_profile: 'http://localhost/user/admin/',
+ author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
+ encoding_status: 'success',
+ likes: 2,
+ dislikes: 0,
+ reported_times: 0,
+ featured: false,
+ user_featured: false,
+ size: '4.8MB',
+ },
+ {
+ friendly_token: '8RUHRjnji',
+ url: 'http://localhost/view?m=8RUHRjnji',
+ api_url: 'http://localhost/api/v1/media/8RUHRjnji',
+ user: 'admin',
+ title: 'att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov (Trimmed)',
+ description: '',
+ add_date: '2025-05-19T01:29:48.308159+01:00',
+ views: 7,
+ media_type: 'video',
+ state: 'public',
+ duration: 7,
+ thumbnail_url:
+ 'http://localhost/media/original/thumbnails/user/admin/884cac6a306e4ae9995726fcd3c7aa49_fT7pnM4.fd1d31460e614ca9b4645228e62c9eec.att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov.jpg',
+ is_reviewed: true,
+ preview_url:
+ '/media/encoded/1/admin/884cac6a306e4ae9995726fcd3c7aa49.fd1d31460e614ca9b4645228e62c9eec.tmpjohj96hu.gif',
+ author_name: '',
+ author_profile: 'http://localhost/user/admin/',
+ author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
+ encoding_status: 'success',
+ likes: 1,
+ dislikes: 0,
+ reported_times: 0,
+ featured: false,
+ user_featured: false,
+ size: '4.6MB',
+ },
+ {
+ friendly_token: 'N5Vpnqfpf',
+ url: 'http://localhost/view?m=N5Vpnqfpf',
+ api_url: 'http://localhost/api/v1/media/N5Vpnqfpf',
+ user: 'admin',
+ title: 'att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov',
+ description: '',
+ add_date: '2025-05-19T01:15:03.594646+01:00',
+ views: 11,
+ media_type: 'video',
+ state: 'public',
+ duration: 37,
+ thumbnail_url:
+ 'http://localhost/media/original/thumbnails/user/admin/e549ebb4b1dc41a48cf76ebccf592836_1xqZHHc.att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov.jpg',
+ is_reviewed: true,
+ preview_url:
+ '/media/encoded/1/admin/e549ebb4b1dc41a48cf76ebccf592836.tmpu6x4f3js.gif',
+ author_name: '',
+ author_profile: 'http://localhost/user/admin/',
+ author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
+ encoding_status: 'success',
+ likes: 1,
+ dislikes: 0,
+ reported_times: 0,
+ featured: false,
+ user_featured: false,
+ size: '22.3MB',
+ },
+ {
+ friendly_token: 'MmyuYCEfC',
+ url: 'http://localhost/view?m=MmyuYCEfC',
+ api_url: 'http://localhost/api/v1/media/MmyuYCEfC',
+ user: 'admin',
+ title: 'JmailLogIdeasforYiannis.mp4',
+ description: 'check the 00:12 and then the 00:39',
+ add_date: '2025-03-12T02:37:42Z',
+ views: 49,
+ media_type: 'video',
+ state: 'public',
+ duration: 112,
+ thumbnail_url:
+ 'http://localhost/media/original/thumbnails/user/admin/3d4e6f1c7e014720bc6390ed9d61aba5_BppzBiz.JmailLogIdeasforYiannis.mp4.jpg',
+ is_reviewed: true,
+ preview_url:
+ '/media/encoded/1/admin/3d4e6f1c7e014720bc6390ed9d61aba5.tmpcjd0bvr5.gif',
+ author_name: '',
+ author_profile: 'http://localhost/user/admin/',
+ author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
+ encoding_status: 'success',
+ likes: 1,
+ dislikes: 0,
+ reported_times: 0,
+ featured: false,
+ user_featured: false,
+ size: '40.6MB',
+ },
+ {
+ friendly_token: 'E8ia0lfir',
+ url: 'http://localhost/view?m=E8ia0lfir',
+ api_url: 'http://localhost/api/v1/media/E8ia0lfir',
+ user: 'admin',
+ title: 'att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov (Trimmed)',
+ description: '',
+ add_date: '2025-05-19T01:22:27.251447+01:00',
+ views: 12,
+ media_type: 'video',
+ state: 'public',
+ duration: 37,
+ thumbnail_url:
+ 'http://localhost/media/original/thumbnails/user/admin/8572a325a9d649f79cc60181fbece78c_0H5arHy.fd1d31460e614ca9b4645228e62c9eec.att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov.jpg',
+ is_reviewed: true,
+ preview_url:
+ '/media/encoded/1/admin/8572a325a9d649f79cc60181fbece78c.fd1d31460e614ca9b4645228e62c9eec.tmpjohj96hu.gif',
+ author_name: '',
+ author_profile: 'http://localhost/user/admin/',
+ author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
+ encoding_status: 'success',
+ likes: 1,
+ dislikes: 0,
+ reported_times: 0,
+ featured: false,
+ user_featured: false,
+ size: '22.4MB',
+ },
+ {
+ friendly_token: 'eZAl84zg6',
+ url: 'http://localhost/view?m=eZAl84zg6',
+ api_url: 'http://localhost/api/v1/media/eZAl84zg6',
+ user: 'admin',
+ title: '20257855hd_1920_1080_60fps.mp4',
+ description: '',
+ add_date: '2025-03-12T02:35:59.779098Z',
+ views: 29,
+ media_type: 'video',
+ state: 'public',
+ duration: 90,
+ thumbnail_url:
+ 'http://localhost/media/original/thumbnails/user/admin/ed9c61a72c0445debfbedee011b6eba1_8HjWGZX.20257855hd_1920_1080_60fps.mp4.jpg',
+ is_reviewed: true,
+ preview_url:
+ '/media/encoded/1/admin/ed9c61a72c0445debfbedee011b6eba1.tmpil72jnbv.gif',
+ author_name: '',
+ author_profile: 'http://localhost/user/admin/',
+ author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
+ encoding_status: 'success',
+ likes: 1,
+ dislikes: 0,
+ reported_times: 0,
+ featured: false,
+ user_featured: false,
+ size: '64.4MB',
+ },
+ {
+ friendly_token: '6WShYNxZx',
+ url: 'http://localhost/view?m=6WShYNxZx',
+ api_url: 'http://localhost/api/v1/media/6WShYNxZx',
+ user: 'admin',
+ title: 'SampleVideo_1280x720_30mb.mp4 test abc',
+ description: 'test 123 yiannis',
+ add_date: '2025-04-14T00:00:00+01:00',
+ views: 43,
+ media_type: 'video',
+ state: 'public',
+ duration: 171,
+ thumbnail_url:
+ 'http://localhost/media/original/thumbnails/user/admin/c868df836ac34688b876e741b58ada93_LRvu388.SampleVideo_1280x720_30mb.mp4.jpg',
+ is_reviewed: true,
+ preview_url:
+ '/media/encoded/1/admin/c868df836ac34688b876e741b58ada93.tmpyl_r01_l.gif',
+ author_name: '',
+ author_profile: 'http://localhost/user/admin/',
+ author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
+ encoding_status: 'success',
+ likes: 3,
+ dislikes: 0,
+ reported_times: 0,
+ featured: false,
+ user_featured: false,
+ size: '31.5MB',
+ },
+ {
+ friendly_token: 'SOgLTsrAH',
+ url: 'http://localhost/view?m=SOgLTsrAH',
+ api_url: 'http://localhost/api/v1/media/SOgLTsrAH',
+ user: 'admin',
+ title: 'att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov (Trimmed) (Trimmed) 1',
+ description: '',
+ add_date: '2025-05-19T01:31:11.057879+01:00',
+ views: 8,
+ media_type: 'video',
+ state: 'public',
+ duration: 2,
+ thumbnail_url:
+ 'http://localhost/media/original/thumbnails/user/admin/46c75315947e4a5f9dd464c1721206fd_6KJzqBH.884cac6a306e4ae9995726fcd3c7aa49.fd1d31460e614ca9b4645228e62c9eec.att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov.jpg',
+ is_reviewed: true,
+ preview_url:
+ '/media/encoded/1/admin/46c75315947e4a5f9dd464c1721206fd.884cac6a306e4ae9995726fcd3c7aa49.fd1d31460e614ca9b4645228e62c9eec.tmpjohj96hu.gif',
+ author_name: '',
+ author_profile: 'http://localhost/user/admin/',
+ author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
+ encoding_status: 'success',
+ likes: 1,
+ dislikes: 0,
+ reported_times: 0,
+ featured: false,
+ user_featured: false,
+ size: '1.5MB',
+ },
+ {
+ friendly_token: 'PoDk009ue',
+ url: 'http://localhost/view?m=PoDk009ue',
+ api_url: 'http://localhost/api/v1/media/PoDk009ue',
+ user: 'admin',
+ title: 'att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov',
+ description: '',
+ add_date: '2025-05-19T01:17:44.296445+01:00',
+ views: 12,
+ media_type: 'video',
+ state: 'public',
+ duration: 37,
+ thumbnail_url:
+ 'http://localhost/media/original/thumbnails/user/admin/fd1d31460e614ca9b4645228e62c9eec_DB8nan8.att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov.jpg',
+ is_reviewed: true,
+ preview_url:
+ '/media/encoded/1/admin/fd1d31460e614ca9b4645228e62c9eec.tmpjohj96hu.gif',
+ author_name: '',
+ author_profile: 'http://localhost/user/admin/',
+ author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
+ encoding_status: 'success',
+ likes: 1,
+ dislikes: 0,
+ reported_times: 0,
+ featured: false,
+ user_featured: false,
+ size: '22.4MB',
+ },
+ {
+ friendly_token: 'b7e06rUz5',
+ url: 'http://localhost/view?m=b7e06rUz5',
+ api_url: 'http://localhost/api/v1/media/b7e06rUz5',
+ user: 'admin',
+ title: 'Proto.cyTimesheet03_03_202507_03_2025.pdf',
+ description: '',
+ add_date: '2025-03-12T14:29:15.245599Z',
+ views: 16,
+ media_type: 'pdf',
+ state: 'public',
+ duration: 0,
+ thumbnail_url: null,
+ is_reviewed: true,
+ preview_url: null,
+ author_name: '',
+ author_profile: 'http://localhost/user/admin/',
+ author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
+ encoding_status: 'success',
+ likes: 1,
+ dislikes: 0,
+ reported_times: 0,
+ featured: false,
+ user_featured: false,
+ size: null,
+ },
+ {
+ friendly_token: 'wUu76zL5a',
+ url: 'http://localhost/view?m=wUu76zL5a',
+ api_url: 'http://localhost/api/v1/media/wUu76zL5a',
+ user: 'admin',
+ title: 'att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov (Trimmed) 2 (Trimmed)',
+ description: '',
+ add_date: '2025-05-19T01:28:52.829907+01:00',
+ views: 2,
+ media_type: 'video',
+ state: 'public',
+ duration: 0,
+ thumbnail_url:
+ 'http://localhost/media/original/thumbnails/user/admin/1b3e7dc2431b46a5b7e40b297662137a_1V0DJz5.ca19044d26af4eada1669c3373c771cb.fd1d31460e614ca9b4645228e62c9eec.att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov.jpg',
+ is_reviewed: true,
+ preview_url:
+ '/media/encoded/1/admin/1b3e7dc2431b46a5b7e40b297662137a.ca19044d26af4eada1669c3373c771cb.fd1d31460e614ca9b4645228e62c9eec.tmpjohj96hu.gif',
+ author_name: '',
+ author_profile: 'http://localhost/user/admin/',
+ author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
+ encoding_status: 'success',
+ likes: 1,
+ dislikes: 0,
+ reported_times: 0,
+ featured: false,
+ user_featured: false,
+ size: '0.3MB',
+ },
+ {
+ friendly_token: 'JBjMHLfLZ',
+ url: 'http://localhost/view?m=JBjMHLfLZ',
+ api_url: 'http://localhost/api/v1/media/JBjMHLfLZ',
+ user: 'admin',
+ title: 'att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov (Trimmed) (Trimmed) 2',
+ description: '',
+ add_date: '2025-05-19T01:31:11.848403+01:00',
+ views: 19,
+ media_type: 'video',
+ state: 'public',
+ duration: 4,
+ thumbnail_url:
+ 'http://localhost/media/original/thumbnails/user/admin/7759ca3e0ba1482aa9149201bd14139d_ISBMqD4.884cac6a306e4ae9995726fcd3c7aa49.fd1d31460e614ca9b4645228e62c9eec.att.24Cz35hRdjJ9FV0RlxCPVVtc6XVAdJvrU4eb8Gkykc.mov.jpg',
+ is_reviewed: true,
+ preview_url:
+ '/media/encoded/1/admin/7759ca3e0ba1482aa9149201bd14139d.884cac6a306e4ae9995726fcd3c7aa49.fd1d31460e614ca9b4645228e62c9eec.tmpjohj96hu.gif',
+ author_name: '',
+ author_profile: 'http://localhost/user/admin/',
+ author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
+ encoding_status: 'success',
+ likes: 1,
+ dislikes: 0,
+ reported_times: 0,
+ featured: false,
+ user_featured: false,
+ size: '2.5MB',
+ },
+ {
+ friendly_token: 'w13x5uUXc',
+ url: 'http://localhost/view?m=w13x5uUXc',
+ api_url: 'http://localhost/api/v1/media/w13x5uUXc',
+ user: 'admin',
+ title: 'Web357202503112025_05_33_AM.png',
+ description: '',
+ add_date: '2025-03-12T14:27:55.078360Z',
+ views: 3,
+ media_type: 'image',
+ state: 'public',
+ duration: 0,
+ thumbnail_url:
+ 'http://localhost/media/original/thumbnails/user/admin/1da967bcdca540efa1d2362af2f3a34b.Web357202503112025_05_33_AM.png.jpg',
+ is_reviewed: true,
+ preview_url: null,
+ author_name: '',
+ author_profile: 'http://localhost/user/admin/',
+ author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
+ encoding_status: 'success',
+ likes: 1,
+ dislikes: 0,
+ reported_times: 0,
+ featured: false,
+ user_featured: false,
+ size: null,
+ },
+ {
+ friendly_token: 'sZxPpdon5',
+ url: 'http://localhost/view?m=sZxPpdon5',
+ api_url: 'http://localhost/api/v1/media/sZxPpdon5',
+ user: 'admin',
+ title: 'JmailLogIdeasforYiannis.mp4 (Trimmed)',
+ description: 'check the 00:12 and then the 00:39',
+ add_date: '2025-05-15T02:16:19.580745+01:00',
+ views: 9,
+ media_type: 'video',
+ state: 'public',
+ duration: 17,
+ thumbnail_url:
+ 'http://localhost/media/original/thumbnails/user/admin/7191a59cbfcd402ebed400e604a76840_5WRh0cp.3d4e6f1c7e014720bc6390ed9d61aba5.JmailLogIdeasforYiannis.mp4.jpg',
+ is_reviewed: true,
+ preview_url:
+ '/media/encoded/1/admin/7191a59cbfcd402ebed400e604a76840.3d4e6f1c7e014720bc6390ed9d61aba5.tmpcjd0bvr5.gif',
+ author_name: '',
+ author_profile: 'http://localhost/user/admin/',
+ author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
+ encoding_status: 'success',
+ likes: 1,
+ dislikes: 0,
+ reported_times: 0,
+ featured: false,
+ user_featured: false,
+ size: '4.5MB',
+ },
+ {
+ friendly_token: 'TCycDZROI',
+ url: 'http://localhost/view?m=TCycDZROI',
+ api_url: 'http://localhost/api/v1/media/TCycDZROI',
+ user: 'admin',
+ title: 'JmailLogIdeasforYiannis.mp4 (Trimmed)',
+ description: 'check the 00:12 and then the 00:39',
+ add_date: '2025-05-15T02:14:58.855935+01:00',
+ views: 8,
+ media_type: 'video',
+ state: 'public',
+ duration: 112,
+ thumbnail_url:
+ 'http://localhost/media/original/thumbnails/user/admin/5cbfbe93d06c453487c65d9d54749318_CUIZl8y.3d4e6f1c7e014720bc6390ed9d61aba5.JmailLogIdeasforYiannis.mp4.jpg',
+ is_reviewed: true,
+ preview_url:
+ '/media/encoded/1/admin/5cbfbe93d06c453487c65d9d54749318.3d4e6f1c7e014720bc6390ed9d61aba5.tmpcjd0bvr5.gif',
+ author_name: '',
+ author_profile: 'http://localhost/user/admin/',
+ author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
+ encoding_status: 'success',
+ likes: 1,
+ dislikes: 0,
+ reported_times: 0,
+ featured: false,
+ user_featured: false,
+ size: '40.6MB',
+ },
+ {
+ friendly_token: 'YjGJafibO',
+ url: 'http://localhost/view?m=YjGJafibO',
+ api_url: 'http://localhost/api/v1/media/YjGJafibO',
+ user: 'admin',
+ title: 'file_example_MP4_480_1_5MG.mp4',
+ description: '',
+ add_date: '2025-04-14T00:07:11.074523+01:00',
+ views: 18,
+ media_type: 'video',
+ state: 'public',
+ duration: 30,
+ thumbnail_url:
+ 'http://localhost/media/original/thumbnails/user/admin/e76f4fff2b3d42adbadb0f1204b21b81_s8EEvSE.file_example_MP4_480_1_5MG.mp4.jpg',
+ is_reviewed: true,
+ preview_url:
+ '/media/encoded/1/admin/e76f4fff2b3d42adbadb0f1204b21b81.tmp04azp_tc.gif',
+ author_name: '',
+ author_profile: 'http://localhost/user/admin/',
+ author_thumbnail: 'http://localhost/media/userlogos/user.jpg',
+ encoding_status: 'success',
+ likes: 2,
+ dislikes: 0,
+ reported_times: 0,
+ featured: false,
+ user_featured: false,
+ size: '1.6MB',
+ },
+ ],
+ },
+ previewSprite: {
+ url: 'https://demo.mediacms.io/media/original/thumbnails/user/markos/fe4933d67b884d4da507dd60e77f7438.VID_20200909_141053.mp4sprites.jpg',
+ frame: { width: 160, height: 90, seconds: 10 },
+ },
+ siteUrl: '',
+ nextLink: 'https://demo.mediacms.io/view?m=YjGJafibO',
+ chaptersData: [
+ { startTime: 0, endTime: 5, text: 'Start111' },
+ { startTime: 5, endTime: 10, text: 'Introduction - EuroHPC' },
+ { startTime: 10, endTime: 15, text: 'Planning - EuroHPC' },
+ ],
+ },
[]
);
// Define chapters as JSON object
// Note: The sample-chapters.vtt file is no longer needed as chapters are now loaded from this JSON
const chaptersData = mediaData.chaptersData;
-// [
-// { startTime: 0, endTime: 5, text: 'Start111' },
-// { startTime: 5, endTime: 10, text: 'Introduction - EuroHPC' },
-// { startTime: 10, endTime: 15, text: 'Planning - EuroHPC' },
-// ];
+ /* [
+ { startTime: 0, endTime: 5, text: 'Start111' },
+ { startTime: 5, endTime: 10, text: 'Introduction - EuroHPC' },
+ { startTime: 10, endTime: 15, text: 'Planning - EuroHPC' },
+ ]; */
// Get video data from mediaData
const currentVideo = useMemo(
@@ -588,21 +593,66 @@ function VideoJSPlayer() {
nextLink: mediaData?.nextLink || null,
sources: mediaData.data?.original_media_url
? [
- {
- src: mediaData.siteUrl + mediaData.data.original_media_url,
- type: 'video/mp4',
- },
- ]
+ {
+ src: mediaData.siteUrl + mediaData.data.original_media_url,
+ type: 'video/mp4',
+ },
+ ]
: [
- {
- src: '/videos/sample-video.mp4',
- type: 'video/mp4',
- },
- ],
+ {
+ src: '/videos/sample-video.mp4',
+ type: 'video/mp4',
+ },
+ ],
}),
[mediaData]
);
+ // Compute available qualities. Prefer JSON (mediaData.data.qualities), otherwise build a full ordered list using the current source.
+ const availableQualities = useMemo(() => {
+ const desiredOrder = ['auto', '144p', '240p', '360p', '480p', '720p', '1080p'];
+
+ const normalize = (arr) => {
+ const norm = arr.map((q) => ({
+ label: q.label || q.value || 'Auto',
+ value: (q.value || q.label || 'auto').toString().toLowerCase(),
+ src: q.src || q.url || q.href,
+ type: q.type || 'video/mp4',
+ }));
+ // ensure all desired present
+ const have = new Set(norm.map((q) => q.value));
+ desiredOrder.forEach((v) => {
+ if (!have.has(v)) {
+ norm.push({ label: v === 'auto' ? 'Auto' : v, value: v });
+ }
+ });
+ // sort
+ const idx = (v) => {
+ const i = desiredOrder.indexOf(String(v).toLowerCase());
+ return i === -1 ? 999 : i;
+ };
+ norm.sort((a, b) => idx(a.value) - idx(b.value));
+ return norm;
+ };
+
+ const jsonList = mediaData?.data?.qualities;
+ if (Array.isArray(jsonList) && jsonList.length) {
+ return normalize(jsonList);
+ }
+
+ // Build from current source
+ const baseSrc = (currentVideo?.sources && currentVideo.sources[0]?.src) || null;
+ const type = (currentVideo?.sources && currentVideo.sources[0]?.type) || 'video/mp4';
+
+ const buildFromBase = desiredOrder.map((v) => ({
+ label: v === 'auto' ? 'Auto' : v,
+ value: v,
+ src: baseSrc || undefined,
+ type,
+ }));
+ return normalize(buildFromBase);
+ }, [mediaData, currentVideo]);
+
// Get related videos from mediaData instead of static data
const relatedVideos = useMemo(() => {
if (!mediaData?.data?.related_media) {
@@ -858,16 +908,16 @@ function VideoJSPlayer() {
// Picture-in-picture toggle button
pictureInPictureToggle: true,
- // Playback rate menu button
- playbackRateMenuButton: true,
+ // Remove default playback speed dropdown from control bar
+ playbackRateMenuButton: false,
// Descriptions button
descriptionsButton: true,
- // Subtitles button
- subtitlesButton: false,
+ // Subtitles (CC) button should be visible
+ subtitlesButton: true,
- // Captions button (disabled to avoid duplicate)
+ // Captions button (keep disabled to avoid duplicate with subtitles)
captionsButton: false,
// Audio track button
@@ -982,6 +1032,7 @@ function VideoJSPlayer() {
// END: Add subtitle tracks
// BEGIN: Chapters Implementation
+ console.log('chaptersData1', chaptersData);
if (chaptersData && chaptersData.length > 0) {
const chaptersTrack = playerRef.current.addTextTrack('chapters', 'Chapters', 'en');
// Add cues to the chapters track
@@ -1036,8 +1087,8 @@ function VideoJSPlayer() {
userPreferences: userPreferences.current,
});
// Add it after the play button
- const playToggleIndex = controlBar.children().indexOf(playToggle);
- controlBar.addChild(autoplayToggleButton, {}, playToggleIndex + 1);
+ const fullscreenToggleIndex = controlBar.children().indexOf(fullscreenToggle);
+ controlBar.addChild(autoplayToggleButton, {}, fullscreenToggleIndex - 1);
// Store reference for later use
customComponents.current.autoplayToggleButton = autoplayToggleButton;
@@ -1204,10 +1255,14 @@ function VideoJSPlayer() {
}
// END: Move chapters button after fullscreen toggle
+ console.log('chaptersData', chaptersData);
// BEGIN: Add Chapters Overlay Component
if (chaptersData && chaptersData.length > 0) {
customComponents.current.chaptersOverlay = new CustomChaptersOverlay(playerRef.current, {
chaptersData: chaptersData,
+ seriesTitle: mediaData?.data?.title || 'Chapters',
+ channelName: mediaData?.data?.user || mediaData?.data?.author_name || 'Channel',
+ thumbnail: mediaData?.data?.thumbnail_url || mediaData?.data?.author_thumbnail || '',
});
} else {
console.log('⚠ No chapters data available for overlay');
@@ -1217,6 +1272,7 @@ function VideoJSPlayer() {
// BEGIN: Add Settings Menu Component
customComponents.current.settingsMenu = new CustomSettingsMenu(playerRef.current, {
userPreferences: userPreferences.current,
+ qualities: availableQualities,
});
// END: Add Settings Menu Component
diff --git a/frontend-tools/video-js/src/main.jsx b/frontend-tools/video-js/src/main.jsx
index 68d41f9a..760de73e 100644
--- a/frontend-tools/video-js/src/main.jsx
+++ b/frontend-tools/video-js/src/main.jsx
@@ -3,6 +3,7 @@ import { createRoot } from 'react-dom/client';
import './VideoJS.css';
import VideoJS from './VideoJS.jsx';
+// import ChapterList from './components/chapter/ChapterList.jsx';
// Mount the components when the DOM is ready
const mountComponents = () => {
@@ -11,7 +12,12 @@ const mountComponents = () => {
const root = createRoot(rootContainer);
root.render(
-
+
);
}