From 3d08f3b29f0bbbb82157592fc5b61c8b50e434c6 Mon Sep 17 00:00:00 2001 From: Yiannis Christodoulou Date: Sun, 13 Jul 2025 15:34:36 +0300 Subject: [PATCH] feat: Create the component CustomRemainingTime --- frontend-tools/video-js/src/VideoJS.css | 117 +++++++- .../controls/CustomRemainingTime.js | 106 +++++++ .../components/video-player/VideoJSPlayer.jsx | 281 +++++------------- 3 files changed, 293 insertions(+), 211 deletions(-) create mode 100644 frontend-tools/video-js/src/components/controls/CustomRemainingTime.js diff --git a/frontend-tools/video-js/src/VideoJS.css b/frontend-tools/video-js/src/VideoJS.css index 3474b06d..aad845a0 100644 --- a/frontend-tools/video-js/src/VideoJS.css +++ b/frontend-tools/video-js/src/VideoJS.css @@ -35,15 +35,6 @@ box-sizing: border-box; } -/* Responsive App container */ -.App { - width: 100%; - max-width: 1400px; - margin: 0 auto; - padding: 0 20px; - box-sizing: border-box; -} - /* Ensure video.js responsive behavior */ .video-js.vjs-fluid { width: 100% !important; @@ -248,9 +239,8 @@ .vjs-chapter-floating-tooltip { font-family: - -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif !important; + -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", + "Droid Sans", "Helvetica Neue", sans-serif !important; line-height: 1.4 !important; animation: fadeIn 0.2s ease-in-out; } @@ -353,3 +343,106 @@ font-size: 12px !important; } } + +/* Played portion, buffered portion, unplayed portion */ +.vjs-play-progress { + background-color: #019932 !important; +} +.vjs-load-progress { + background: rgba(255, 255, 255, 0.5) !important; +} +.vjs-progress-holder { + background: rgba(255, 255, 255, 0.5) !important; +} + +/* Move progress control out of control bar and position it above */ +.video-js .vjs-progress-control { + position: absolute !important; + bottom: 46px !important; + left: 0 !important; + right: 0 !important; + width: 100% !important; + height: 0 !important; + z-index: 3 !important; + padding: 0 !important; + margin: 0 auto !important; +} + +/* Hide the original progress control from the control bar */ +.video-js .vjs-control-bar .vjs-progress-control { + display: none !important; +} + +/* Optional: Ensure the progress control is visible */ +.video-js .vjs-progress-control.vjs-control { + display: block !important; +} + +/* Make the seek bar thicker */ +/* .video-js .vjs-play-progress, +.video-js .vjs-load-progress, +.video-js .vjs-progress-holder { + height: 4px !important; +} */ + +/* Remove the semi-transparent background from control bar */ +.video-js .vjs-control-bar { + background: transparent !important; + background-color: transparent !important; + background-image: none !important; + display: flex !important; + flex-direction: row !important; + align-items: center !important; + justify-content: flex-start !important; + gap: 6px !important; +} + +/* Push specific buttons to the right */ +.video-js .vjs-playback-rate, +.video-js .vjs-picture-in-picture-control, +.video-js .vjs-fullscreen-control { + margin-left: auto !important; + order: 999 !important; +} +.video-js .vjs-picture-in-picture-control { + margin-left: 0 !important; +} +.video-js .vjs-fullscreen-control { + margin-left: 0 !important; +} + +/* Make all control bar icons bigger */ +.video-js .vjs-control-bar .vjs-icon-placeholder, +.video-js .vjs-control-bar .vjs-button .vjs-icon-placeholder, +.video-js .vjs-control-bar [class*="vjs-icon-"] { + font-size: 1.5em !important; /* 1.5x bigger */ + transform: translateY(-28px) !important; /* Move icons up by 3px */ +} +.video-js .vjs-control-bar svg { + width: 3em !important; + height: 3em !important; + transform: translateY(-16px) !important; +} + +/******** BEGIN: Custom Remaining Time Styles *********/ +.vjs-control-bar .custom-remaining-time .vjs-remaining-time-display { + font-size: 14px !important; /* Increase font size */ + font-weight: 500; + line-height: 1; + display: flex; + align-items: center; + justify-content: center; + height: 100%; + color: #fff; +} + +/* Ensure proper vertical alignment within control bar */ +.vjs-control-bar .custom-remaining-time { + top: -5px; + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} + +/********* END: Custom Remaining Time Styles *********/ diff --git a/frontend-tools/video-js/src/components/controls/CustomRemainingTime.js b/frontend-tools/video-js/src/components/controls/CustomRemainingTime.js new file mode 100644 index 00000000..bdb1a2e2 --- /dev/null +++ b/frontend-tools/video-js/src/components/controls/CustomRemainingTime.js @@ -0,0 +1,106 @@ +// components/controls/CustomRemainingTime.js +import videojs from 'video.js'; + +// Get the Component base class from Video.js +const Component = videojs.getComponent('Component'); + +class CustomRemainingTime extends Component { + constructor(player, options) { + super(player, options); + + // Bind methods to ensure correct 'this' context + this.updateContent = this.updateContent.bind(this); + + // Set up event listeners + this.on(player, 'timeupdate', this.updateContent); + this.on(player, 'durationchange', this.updateContent); + this.on(player, 'loadedmetadata', this.updateContent); + + // Store custom options + this.options_ = { + displayNegative: false, + customPrefix: '', + customSuffix: '', + ...options, + }; + } + + /** + * Create the component's DOM element + */ + createEl() { + const el = videojs.dom.createEl('div', { + className: 'vjs-remaining-time vjs-time-control vjs-control custom-remaining-time', + }); + + // Add ARIA accessibility + el.innerHTML = ` + Time Display  + 0:00 / 0:00 + `; + + return el; + } + + /** + * Update the time display + */ + updateContent() { + const player = this.player(); + const currentTime = player.currentTime(); + const duration = player.duration(); + + const display = this.el().querySelector('.vjs-remaining-time-display'); + + if (display) { + const formattedCurrentTime = this.formatTime(isNaN(currentTime) ? 0 : currentTime); + const formattedDuration = this.formatTime(isNaN(duration) ? 0 : duration); + display.textContent = `${formattedCurrentTime} / ${formattedDuration}`; + } + } + + /** + * Format time with custom logic + */ + formatTime(seconds) { + const { customPrefix, customSuffix } = this.options_; + + const hours = Math.floor(seconds / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + const secs = Math.floor(seconds % 60); + + let timeString; + if (hours > 0) { + timeString = `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; + } else { + timeString = `${minutes}:${secs.toString().padStart(2, '0')}`; + } + + return `${customPrefix}${timeString}${customSuffix}`; + } + + /** + * Add click handler for additional functionality + */ + handleClick() { + // Example: Toggle between different time formats + console.log('Time display clicked'); + // Could toggle between current/duration vs remaining time + } + + /** + * Component disposal cleanup + */ + dispose() { + // Clean up any additional resources if needed + super.dispose(); + } +} + +// Set component name for Video.js +CustomRemainingTime.prototype.controlText_ = 'Time Display'; + +// Register the component with Video.js +videojs.registerComponent('CustomRemainingTime', CustomRemainingTime); + +export default CustomRemainingTime; 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 120d54fd..4f3e54e0 100644 --- a/frontend-tools/video-js/src/components/video-player/VideoJSPlayer.jsx +++ b/frontend-tools/video-js/src/components/video-player/VideoJSPlayer.jsx @@ -6,6 +6,7 @@ import 'video.js/dist/video-js.css'; import EndScreenOverlay from '../overlays/EndScreenOverlay'; import ChapterMarkers from '../markers/ChapterMarkers'; import NextVideoButton from '../controls/NextVideoButton'; +import CustomRemainingTime from '../controls/CustomRemainingTime'; function VideoJSPlayer() { const videoRef = useRef(null); @@ -47,9 +48,7 @@ function VideoJSPlayer() { sources: mediaData.data?.original_media_url ? [ { - src: - mediaData.siteUrl + - mediaData.data.original_media_url, + src: mediaData.siteUrl + mediaData.data.original_media_url, type: 'video/mp4', }, ] @@ -155,11 +154,7 @@ function VideoJSPlayer() { const timer = setTimeout(() => { // Double-check that we still don't have a player and element exists - if ( - !playerRef.current && - videoRef.current && - !videoRef.current.player - ) { + if (!playerRef.current && videoRef.current && !videoRef.current.player) { playerRef.current = videojs(videoRef.current, { // ===== STANDARD