From a66d823d1d9ffe49ba70ff996c2af75396f72f92 Mon Sep 17 00:00:00 2001 From: Yiannis Christodoulou Date: Mon, 21 Jul 2025 19:45:19 +0300 Subject: [PATCH] feat: Related media at the end of video --- frontend-tools/video-js/src/VideoJS.css | 94 ++- .../components/overlays/EndScreenOverlay.js | 56 +- .../components/video-player/VideoJSPlayer.jsx | 691 ++++++++++++++++-- 3 files changed, 723 insertions(+), 118 deletions(-) diff --git a/frontend-tools/video-js/src/VideoJS.css b/frontend-tools/video-js/src/VideoJS.css index 06470d46..16232338 100644 --- a/frontend-tools/video-js/src/VideoJS.css +++ b/frontend-tools/video-js/src/VideoJS.css @@ -74,14 +74,14 @@ top: 0; left: 0; width: 100%; - height: 91%; + height: calc(100% - 46px); background: rgba(0, 0, 0, 0.8); display: none; flex-direction: column; justify-content: center; align-items: center; z-index: 1000; - padding: 0 20px; + padding: 0; box-sizing: border-box; } @@ -95,44 +95,51 @@ .vjs-related-videos-grid { display: grid; - grid-template-columns: repeat(3, 1fr); + grid-template-columns: repeat(4, 1fr); grid-template-rows: repeat(3, 1fr); - gap: 12px; - max-width: 800px; + gap: 0; width: 100%; - margin: 0 auto; + height: 100%; + margin: 0; } .vjs-related-video-item { position: relative; cursor: pointer; - border-radius: 8px; + border-radius: 0; overflow: hidden; - transition: transform 0.2s ease; + transition: none; background: #1a1a1a; + border: 1px solid #000; } .vjs-related-video-item:hover { - transform: scale(1.05); + transform: none; } .vjs-related-video-thumbnail { width: 100%; - height: 120px; + height: 100%; object-fit: cover; display: block; + border-radius: 0; } .vjs-related-video-overlay { position: absolute; - bottom: 0; + top: 0; left: 0; right: 0; - background: linear-gradient(transparent, rgba(0, 0, 0, 0.8)); + bottom: 0; + background: linear-gradient(transparent, rgba(0, 0, 0, 0.9)); color: white; - padding: 10px; + padding: 12px; opacity: 0; transition: opacity 0.3s ease; + display: flex; + flex-direction: column; + justify-content: flex-start; + height: 100%; } .vjs-related-video-item:hover .vjs-related-video-overlay { @@ -142,14 +149,21 @@ .vjs-related-video-title { font-size: 14px; font-weight: bold; + line-height: 1.3; + color: white; margin-bottom: 4px; - line-height: 1.2; +} + +.vjs-related-video-meta { + display: flex; + flex-direction: row; + gap: 8px; + align-items: center; } .vjs-related-video-author { font-size: 12px; color: #ccc; - margin-bottom: 2px; } .vjs-related-video-views { @@ -157,6 +171,30 @@ color: #aaa; } +.vjs-related-video-author::after { + content: "•"; + margin-left: 8px; + color: #666; +} + +.vjs-related-video-duration { + position: absolute; + bottom: 8px; + right: 8px; + background: rgba(0, 0, 0, 0.8); + color: white; + padding: 2px 6px; + font-size: 11px; + font-weight: bold; + border-radius: 2px; + opacity: 0; + transition: opacity 0.3s ease; +} + +.vjs-related-video-item:hover .vjs-related-video-duration { + opacity: 1; +} + /* Keep entire control bar active when video ends */ .video-js.vjs-ended .vjs-control-bar { opacity: 1 !important; @@ -290,18 +328,20 @@ } .vjs-related-videos-grid { - grid-template-columns: repeat(2, 1fr); - grid-template-rows: repeat(auto, 1fr); - gap: 8px; - max-width: 100%; + grid-template-columns: repeat(3, 1fr); + grid-template-rows: repeat(2, 1fr); + gap: 0; + width: 100%; + height: 100%; } .vjs-end-screen-overlay { - padding: 0 15px; + padding: 0; + height: calc(100% - 46px); } .vjs-related-video-thumbnail { - height: 100px; + height: 100%; } .vjs-chapter-floating-tooltip { @@ -317,16 +357,20 @@ } .vjs-related-videos-grid { - grid-template-columns: 1fr; - gap: 6px; + grid-template-columns: repeat(2, 1fr); + grid-template-rows: repeat(3, 1fr); + gap: 0; + width: 100%; + height: 100%; } .vjs-end-screen-overlay { - padding: 0 10px; + padding: 0; + height: calc(100% - 46px); } .vjs-related-video-thumbnail { - height: 120px; + height: 100%; } /* Make sure video info panel is responsive */ diff --git a/frontend-tools/video-js/src/components/overlays/EndScreenOverlay.js b/frontend-tools/video-js/src/components/overlays/EndScreenOverlay.js index eca2fef5..9654c67f 100644 --- a/frontend-tools/video-js/src/components/overlays/EndScreenOverlay.js +++ b/frontend-tools/video-js/src/components/overlays/EndScreenOverlay.js @@ -13,8 +13,7 @@ class EndScreenOverlay extends Component { super(player, options); // 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', @@ -25,14 +24,16 @@ class EndScreenOverlay extends Component { createEl() { // Get relatedVideos from options since createEl is called during super() - const relatedVideos = - this.options_ && this.options_._relatedVideos - ? this.options_._relatedVideos - : []; + const relatedVideos = this.options_ && this.options_._relatedVideos ? this.options_._relatedVideos : []; + + // Limit items based on screen size + const isMobile = window.innerWidth <= 768; + const maxItems = isMobile ? 6 : 12; + const videosToShow = relatedVideos.slice(0, maxItems); // console.log( // 'Creating end screen with', - // relatedVideos.length, + // videosToShow.length, // 'related videos' // ); @@ -46,12 +47,8 @@ class EndScreenOverlay extends Component { }); // Create video items - if ( - relatedVideos && - Array.isArray(relatedVideos) && - relatedVideos.length > 0 - ) { - relatedVideos.forEach((video) => { + if (videosToShow && Array.isArray(videosToShow) && videosToShow.length > 0) { + videosToShow.forEach((video) => { const videoItem = this.createVideoItem(video); grid.appendChild(videoItem); }); @@ -91,6 +88,11 @@ class EndScreenOverlay extends Component { }); title.textContent = video.title; + // Create meta container for author and views + const meta = videojs.dom.createEl('div', { + className: 'vjs-related-video-meta', + }); + const author = videojs.dom.createEl('div', { className: 'vjs-related-video-author', }); @@ -101,13 +103,37 @@ class EndScreenOverlay extends Component { }); views.textContent = video.views; + // Add author and views to meta container + meta.appendChild(author); + meta.appendChild(views); + + // Add duration display (positioned absolutely in bottom right) + const duration = videojs.dom.createEl('div', { + className: 'vjs-related-video-duration', + }); + + // Format duration from seconds to MM:SS + const formatDuration = (seconds) => { + if (!seconds || seconds === 0) return ''; + const mins = Math.floor(seconds / 60); + const secs = Math.floor(seconds % 60); + return `${mins}:${secs.toString().padStart(2, '0')}`; + }; + + duration.textContent = formatDuration(video.duration); + + // Structure: title at top, meta at bottom overlay.appendChild(title); - overlay.appendChild(author); - overlay.appendChild(views); + overlay.appendChild(meta); item.appendChild(thumbnail); item.appendChild(overlay); + // Add duration to the item (positioned absolutely) + if (video.duration && video.duration > 0) { + item.appendChild(duration); + } + // Add click handler item.addEventListener('click', () => { window.location.href = `/view?m=${video.id}`; 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 1d98997b..9532939c 100644 --- a/frontend-tools/video-js/src/components/video-player/VideoJSPlayer.jsx +++ b/frontend-tools/video-js/src/components/video-player/VideoJSPlayer.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useState, useMemo } from 'react'; +import React, { useEffect, useRef, useMemo } from 'react'; import videojs from 'video.js'; import 'video.js/dist/video-js.css'; @@ -22,7 +22,538 @@ function VideoJSPlayer() { typeof window !== 'undefined' && window.MEDIA_DATA ? window.MEDIA_DATA : { - 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 }, @@ -54,6 +585,7 @@ function VideoJSPlayer() { title: mediaData.data?.title || 'Video', poster: mediaData.siteUrl + mediaData.data?.poster_url || '', previewSprite: mediaData?.previewSprite || {}, + related_media: mediaData.data?.related_media || [], sources: mediaData.data?.original_media_url ? [ { @@ -71,81 +603,30 @@ function VideoJSPlayer() { [mediaData] ); - // Mock related videos data (would come from API) - const [relatedVideos] = useState([ - { - id: 'Otbc37Yj4', - title: 'Amazing Ocean Depths', - author: 'Marine Explorer', - views: '2.1M views', - thumbnail: 'https://picsum.photos/320/180?random=1', - category: 'nature', - }, - { - id: 'Kt9m2Pv8x', - title: 'Deep Sea Creatures', - author: 'Aquatic Life', - views: '854K views', - thumbnail: 'https://picsum.photos/320/180?random=2', - category: 'nature', - }, - { - id: 'Ln5q8Bw3r', - title: 'Coral Reef Paradise', - author: 'Ocean Films', - views: '1.7M views', - thumbnail: 'https://picsum.photos/320/180?random=3', - category: 'nature', - }, - { - id: 'Mz4x7Cy9p', - title: 'Underwater Adventure', - author: 'Sea Documentaries', - views: '3.2M views', - thumbnail: 'https://picsum.photos/320/180?random=4', - category: 'nature', - }, - { - id: 'Nx8v2Fk6w', - title: 'Marine Wildlife', - author: 'Nature Plus', - views: '967K views', - thumbnail: 'https://picsum.photos/320/180?random=5', - category: 'nature', - }, - { - id: 'Py7t4Mn1q', - title: 'Ocean Mysteries', - author: 'Discovery Zone', - views: '1.4M views', - thumbnail: 'https://picsum.photos/320/180?random=6', - category: 'nature', - }, - { - id: 'Qw5e8Rt2n', - title: 'Whales and Dolphins', - author: 'Ocean Planet', - views: '2.8M views', - thumbnail: 'https://picsum.photos/320/180?random=7', - category: 'nature', - }, - { - id: 'Uv3k9Lp7m', - title: 'Tropical Fish Paradise', - author: 'Aquatic World', - views: '1.2M views', - thumbnail: 'https://picsum.photos/320/180?random=8', - category: 'nature', - }, - { - id: 'Zx6c4Mn8b', - title: 'Deep Ocean Exploration', - author: 'Marine Science', - views: '3.7M views', - thumbnail: 'https://picsum.photos/320/180?random=9', - category: 'nature', - }, - ]); + // Get related videos from mediaData instead of static data + const relatedVideos = useMemo(() => { + if (!mediaData?.data?.related_media) { + return []; + } + + return mediaData.data.related_media + .slice(0, 12) // Limit to maximum 12 items + .map((media) => ({ + id: media.friendly_token, + title: media.title, + author: media.user || media.author_name || 'Unknown', + views: `${media.views} views`, + thumbnail: media.thumbnail_url || media.author_thumbnail, + category: media.media_type, + url: media.url, + duration: media.duration, + size: media.size, + likes: media.likes, + dislikes: media.dislikes, + add_date: media.add_date, + description: media.description, + })); + }, [mediaData]); // Function to navigate to next video const goToNextVideo = () => { @@ -652,6 +1133,55 @@ function VideoJSPlayer() { } // END: Add chapter markers to progress control + // BEGIN: Move CC (subtitles) and PiP buttons to the right side + setTimeout(() => { + // Find the subtitles/captions button (CC button) + const possibleTextTrackButtons = ['subtitlesButton', 'captionsButton', 'subsCapsButton']; + let textTrackButton = null; + + for (const buttonName of possibleTextTrackButtons) { + const button = controlBar.getChild(buttonName); + if (button) { + textTrackButton = button; + console.log(`Found text track button: ${buttonName}`); + break; + } + } + + // Find the picture-in-picture button + const pipButton = controlBar.getChild('pictureInPictureToggle'); + + // Move buttons to the right side in desired order + if (fullscreenToggle) { + const fullscreenIndex = controlBar.children().indexOf(fullscreenToggle); + let insertIndex = fullscreenIndex; + + // Move PiP button first (will be rightmost before fullscreen) + if (pipButton) { + try { + controlBar.removeChild(pipButton); + controlBar.addChild(pipButton, {}, insertIndex); + console.log('✓ Picture-in-Picture button moved to right side'); + insertIndex = controlBar.children().indexOf(fullscreenToggle); // Update index + } catch (e) { + console.log('✗ Failed to move PiP button:', e); + } + } + + // Move CC button (will be before PiP) + if (textTrackButton) { + try { + controlBar.removeChild(textTrackButton); + controlBar.addChild(textTrackButton, {}, insertIndex); + console.log('✓ CC button moved to right side'); + } catch (e) { + console.log('✗ Failed to move CC button:', e); + } + } + } + }, 100); + // END: Move CC (subtitles) and PiP buttons to the right side + // BEGIN: Move chapters button after fullscreen toggle if (chaptersButton && fullscreenToggle) { try { @@ -1053,7 +1583,12 @@ function VideoJSPlayer() { }; }, []); - return