Add Overlay Video Player - Portable popup video player system
NEW FEATURE: Overlay Video Player - Created OverlayVideoPlayer class extending BaseVideoPlayer for maximum reusability - Implements popup/modal video player with professional UI design - Random video selection from unified video library with fallback mechanisms - Full video controls inherited from BaseVideoPlayer (play/pause, seek, volume, fullscreen) KEY FEATURES: - Modal overlay with backdrop blur and smooth animations - Dismissible via Escape key, close button, or backdrop click - Random video button () for quick video switching - Auto-displays video metadata (name, size, duration) in header and controls - Responsive design with mobile optimization - Professional glassmorphism styling with dark theme TECHNICAL IMPLEMENTATION: - Extends BaseVideoPlayer for consistent behavior across all video players - Uses unique DOM IDs to avoid selector conflicts - Temporary DOM manipulation for proper BaseVideoPlayer initialization - CSS animations and transitions for smooth user experience - Integration with existing video management and storage systems TESTING: - Accessible via 'Test Overlay Video' button in Video Management screen - Successfully loads random videos from linked directories (E:\Videos\M1K1) - Fully functional video controls and dismissal mechanisms This demonstrates the portability of our BaseVideoPlayer architecture and provides a foundation for future popup/overlay video implementations.
This commit is contained in:
parent
220f347269
commit
0067af93ef
29
index.html
29
index.html
|
|
@ -7,6 +7,7 @@
|
|||
<title>Gooner Training Academy - Master Your Dedication</title>
|
||||
<link rel="stylesheet" href="src/styles/styles.css">
|
||||
<link rel="stylesheet" href="src/styles/base-video-player.css">
|
||||
<link rel="stylesheet" href="src/styles/overlay-video-player.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Audiowide&display=swap" rel="stylesheet">
|
||||
<script src="https://unpkg.com/jszip@3.10.1/dist/jszip.min.js" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
|
|
@ -1657,6 +1658,7 @@
|
|||
<script src="src/features/tts/voiceManager.js"></script>
|
||||
<script src="src/features/media/baseVideoPlayer.js"></script>
|
||||
<script src="src/features/media/focusVideoPlayer.js"></script>
|
||||
<script src="src/features/media/overlayVideoPlayer.js"></script>
|
||||
<script src="src/features/tasks/interactiveTaskManager.js"></script>
|
||||
<script src="src/features/video/videoPlayerManager.js"></script>
|
||||
<script src="src/utils/desktop-file-manager.js"></script>
|
||||
|
|
@ -2854,10 +2856,31 @@
|
|||
}
|
||||
}
|
||||
|
||||
function testVideoPlayback(type) {
|
||||
async function testVideoPlayback(type) {
|
||||
console.log(`Testing ${type} video playback`);
|
||||
if (window.game && window.game.showNotification) {
|
||||
window.game.showNotification(`${type} video test - feature coming soon!`, 'info');
|
||||
|
||||
if (type === 'overlay') {
|
||||
try {
|
||||
// Show overlay video player with random video
|
||||
const overlayPlayer = await OverlayVideoPlayer.showOverlay({
|
||||
showQuality: false,
|
||||
showSpeed: false,
|
||||
minimal: false
|
||||
});
|
||||
|
||||
if (window.game && window.game.showNotification) {
|
||||
window.game.showNotification('🎬 Overlay video player opened!', 'success');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error showing overlay video player:', error);
|
||||
if (window.game && window.game.showNotification) {
|
||||
window.game.showNotification('Error opening overlay video player', 'error');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (window.game && window.game.showNotification) {
|
||||
window.game.showNotification(`${type} video test - feature coming soon!`, 'info');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,318 @@
|
|||
/**
|
||||
* Overlay Video Player
|
||||
* A popup overlay video player that extends BaseVideoPlayer
|
||||
* Used for random video playback in overlay/popup windows
|
||||
*/
|
||||
|
||||
class OverlayVideoPlayer extends BaseVideoPlayer {
|
||||
constructor(options = {}) {
|
||||
// Create the overlay container first
|
||||
const overlayContainer = OverlayVideoPlayer.createOverlayContainer();
|
||||
|
||||
// Add it to DOM temporarily so we can query it
|
||||
document.body.appendChild(overlayContainer);
|
||||
|
||||
// Create a unique ID for the video container
|
||||
const containerId = `overlay-video-container-${Date.now()}`;
|
||||
const videoContainer = overlayContainer.querySelector('.overlay-video-container');
|
||||
videoContainer.id = containerId;
|
||||
|
||||
// Initialize BaseVideoPlayer with the CSS selector
|
||||
super(`#${containerId}`, {
|
||||
showControls: true,
|
||||
autoHide: true,
|
||||
showProgress: true,
|
||||
showVolume: true,
|
||||
showFullscreen: true,
|
||||
showQuality: false, // Keep it simple for overlay
|
||||
showSpeed: false, // Keep it simple for overlay
|
||||
keyboardShortcuts: true,
|
||||
minimal: false,
|
||||
...options
|
||||
});
|
||||
|
||||
this.overlayElement = overlayContainer;
|
||||
this.isVisible = false;
|
||||
this.currentVideo = null;
|
||||
|
||||
// Remove from DOM initially (will be added back when shown)
|
||||
document.body.removeChild(overlayContainer);
|
||||
|
||||
// Setup overlay-specific events
|
||||
this.setupOverlayEvents();
|
||||
|
||||
console.log('🎬 OverlayVideoPlayer created');
|
||||
}
|
||||
|
||||
static createOverlayContainer() {
|
||||
const overlay = document.createElement('div');
|
||||
overlay.className = 'video-overlay-popup';
|
||||
overlay.innerHTML = `
|
||||
<div class="overlay-backdrop" id="overlay-backdrop"></div>
|
||||
<div class="overlay-window">
|
||||
<div class="overlay-header">
|
||||
<h3 class="overlay-title">🎬 Video Player</h3>
|
||||
<button class="overlay-close-btn" id="overlay-close-btn" title="Close (Esc)">✕</button>
|
||||
</div>
|
||||
<div class="overlay-video-container">
|
||||
<video class="main-video" preload="metadata">
|
||||
<source id="video-source" src="" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
|
||||
<!-- Video Overlay Controls (inherited from BaseVideoPlayer) -->
|
||||
<div class="video-overlay">
|
||||
<div class="video-title" id="video-title">Loading video...</div>
|
||||
<div class="video-info" id="video-info"></div>
|
||||
|
||||
<!-- Play Button Overlay -->
|
||||
<div class="play-overlay">
|
||||
<button class="play-button-large">▶</button>
|
||||
</div>
|
||||
|
||||
<!-- Loading Spinner -->
|
||||
<div class="video-loading" style="display: none;">
|
||||
<div class="loading-spinner"></div>
|
||||
<p>Loading video...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Custom Video Controls (inherited from BaseVideoPlayer) -->
|
||||
<div class="video-controls">
|
||||
<div class="progress-container">
|
||||
<div class="progress-bar">
|
||||
<div class="progress-filled"></div>
|
||||
<div class="progress-thumb"></div>
|
||||
</div>
|
||||
<div class="time-display">
|
||||
<span class="current-time">0:00</span>
|
||||
<span class="time-separator">/</span>
|
||||
<span class="total-time">0:00</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls-row">
|
||||
<div class="controls-left">
|
||||
<button class="control-btn play-pause-btn">▶</button>
|
||||
<div class="volume-control">
|
||||
<button class="control-btn mute-btn" title="Mute (M)">🔊</button>
|
||||
<input type="range" class="volume-slider" min="0" max="100" value="70">
|
||||
<span class="volume-percentage">70%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls-center">
|
||||
<span class="video-name"></span>
|
||||
</div>
|
||||
|
||||
<div class="controls-right">
|
||||
<button class="control-btn random-video-btn" title="Random Video">🎲</button>
|
||||
<button class="control-btn fullscreen-btn" title="Fullscreen (F)">⛶</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return overlay;
|
||||
}
|
||||
|
||||
setupOverlayEvents() {
|
||||
// Close button
|
||||
const closeBtn = this.overlayElement.querySelector('#overlay-close-btn');
|
||||
closeBtn.addEventListener('click', () => this.hide());
|
||||
|
||||
// Backdrop click to close
|
||||
const backdrop = this.overlayElement.querySelector('#overlay-backdrop');
|
||||
backdrop.addEventListener('click', () => this.hide());
|
||||
|
||||
// Escape key to close
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && this.isVisible) {
|
||||
this.hide();
|
||||
}
|
||||
});
|
||||
|
||||
// Random video button
|
||||
const randomBtn = this.overlayElement.querySelector('.random-video-btn');
|
||||
if (randomBtn) {
|
||||
randomBtn.addEventListener('click', () => this.playRandomVideo());
|
||||
}
|
||||
|
||||
// Prevent video container clicks from closing overlay
|
||||
const videoContainer = this.overlayElement.querySelector('.overlay-video-container');
|
||||
videoContainer.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
async show() {
|
||||
if (this.isVisible) return;
|
||||
|
||||
// Add to DOM if not already there
|
||||
if (!document.body.contains(this.overlayElement)) {
|
||||
document.body.appendChild(this.overlayElement);
|
||||
}
|
||||
|
||||
// Show overlay
|
||||
this.overlayElement.style.display = 'flex';
|
||||
this.isVisible = true;
|
||||
|
||||
// Fade in animation
|
||||
setTimeout(() => {
|
||||
this.overlayElement.classList.add('visible');
|
||||
}, 10);
|
||||
|
||||
// Load a random video if none is currently loaded
|
||||
if (!this.currentVideo) {
|
||||
await this.playRandomVideo();
|
||||
}
|
||||
|
||||
console.log('🎬 Overlay video player shown');
|
||||
}
|
||||
|
||||
hide() {
|
||||
if (!this.isVisible) return;
|
||||
|
||||
// Fade out animation
|
||||
this.overlayElement.classList.remove('visible');
|
||||
|
||||
// Hide after animation
|
||||
setTimeout(() => {
|
||||
this.overlayElement.style.display = 'none';
|
||||
this.isVisible = false;
|
||||
|
||||
// Pause video when hidden
|
||||
if (this.videoElement) {
|
||||
this.videoElement.pause();
|
||||
}
|
||||
}, 300);
|
||||
|
||||
console.log('🎬 Overlay video player hidden');
|
||||
}
|
||||
|
||||
async playRandomVideo() {
|
||||
try {
|
||||
// Get all available videos
|
||||
let allVideos = [];
|
||||
|
||||
if (window.desktopFileManager) {
|
||||
allVideos = window.desktopFileManager.getAllVideos();
|
||||
}
|
||||
|
||||
// Fallback to unified storage
|
||||
if (allVideos.length === 0) {
|
||||
const unifiedData = JSON.parse(localStorage.getItem('unifiedVideoLibrary') || '{}');
|
||||
allVideos = unifiedData.allVideos || [];
|
||||
}
|
||||
|
||||
// Fallback to legacy storage
|
||||
if (allVideos.length === 0) {
|
||||
const storedVideos = JSON.parse(localStorage.getItem('videoFiles') || '{}');
|
||||
allVideos = Object.values(storedVideos).flat();
|
||||
}
|
||||
|
||||
if (allVideos.length === 0) {
|
||||
this.showError('No videos available for random playback');
|
||||
return;
|
||||
}
|
||||
|
||||
// Select random video
|
||||
const randomIndex = Math.floor(Math.random() * allVideos.length);
|
||||
const randomVideo = allVideos[randomIndex];
|
||||
|
||||
console.log(`🎲 Playing random video: ${randomVideo.name || randomVideo.title}`);
|
||||
|
||||
// Update video info
|
||||
this.updateVideoInfo(randomVideo);
|
||||
|
||||
// Load and play the video
|
||||
this.loadVideo(randomVideo.path || randomVideo.filePath, true);
|
||||
this.currentVideo = randomVideo;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error playing random video:', error);
|
||||
this.showError('Error loading random video');
|
||||
}
|
||||
}
|
||||
|
||||
updateVideoInfo(video) {
|
||||
const titleElement = this.overlayElement.querySelector('.video-title');
|
||||
const infoElement = this.overlayElement.querySelector('.video-info');
|
||||
const nameElement = this.overlayElement.querySelector('.video-name');
|
||||
|
||||
const videoName = video.name || video.title || 'Unknown Video';
|
||||
const videoSize = this.formatFileSize(video.size || 0);
|
||||
const videoDuration = this.formatDuration(video.duration || 0);
|
||||
|
||||
if (titleElement) {
|
||||
titleElement.textContent = videoName;
|
||||
}
|
||||
if (infoElement) {
|
||||
infoElement.textContent = `${videoSize} • ${videoDuration}`;
|
||||
}
|
||||
if (nameElement) {
|
||||
nameElement.textContent = videoName;
|
||||
}
|
||||
|
||||
// Update overlay window title
|
||||
const overlayTitle = this.overlayElement.querySelector('.overlay-title');
|
||||
if (overlayTitle) {
|
||||
overlayTitle.textContent = `🎬 ${videoName}`;
|
||||
}
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
const titleElement = this.overlayElement.querySelector('.video-title');
|
||||
const infoElement = this.overlayElement.querySelector('.video-info');
|
||||
|
||||
if (titleElement) {
|
||||
titleElement.textContent = 'Error';
|
||||
}
|
||||
if (infoElement) {
|
||||
infoElement.textContent = message;
|
||||
}
|
||||
}
|
||||
|
||||
formatFileSize(bytes) {
|
||||
if (!bytes || bytes === 0) return '--';
|
||||
|
||||
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||
return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${sizes[i]}`;
|
||||
}
|
||||
|
||||
formatDuration(seconds) {
|
||||
if (!seconds || seconds === 0) return '--:--';
|
||||
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
const secs = Math.floor(seconds % 60);
|
||||
|
||||
if (hours > 0) {
|
||||
return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
||||
} else {
|
||||
return `${minutes}:${secs.toString().padStart(2, '0')}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Public methods
|
||||
isOpen() {
|
||||
return this.isVisible;
|
||||
}
|
||||
|
||||
getCurrentVideo() {
|
||||
return this.currentVideo;
|
||||
}
|
||||
|
||||
// Static method to create and show overlay
|
||||
static async showOverlay(options = {}) {
|
||||
const overlay = new OverlayVideoPlayer(options);
|
||||
await overlay.show();
|
||||
return overlay;
|
||||
}
|
||||
}
|
||||
|
||||
// Export for global access
|
||||
window.OverlayVideoPlayer = OverlayVideoPlayer;
|
||||
|
|
@ -0,0 +1,391 @@
|
|||
/* Overlay Video Player Styles */
|
||||
|
||||
.video-overlay-popup {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 10000;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.video-overlay-popup.visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.overlay-backdrop {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
.overlay-window {
|
||||
position: relative;
|
||||
width: 90%;
|
||||
max-width: 1200px;
|
||||
max-height: 90%;
|
||||
background: #1a1a1a;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5);
|
||||
overflow: hidden;
|
||||
transform: scale(0.9);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.video-overlay-popup.visible .overlay-window {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.overlay-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 15px 20px;
|
||||
background: #2a2a2a;
|
||||
border-bottom: 1px solid #333;
|
||||
}
|
||||
|
||||
.overlay-title {
|
||||
margin: 0;
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
max-width: calc(100% - 40px);
|
||||
}
|
||||
|
||||
.overlay-close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #999;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s ease;
|
||||
line-height: 1;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.overlay-close-btn:hover {
|
||||
background: #444;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.overlay-video-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
aspect-ratio: 16/9;
|
||||
background: #000;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Ensure the video player inherits BaseVideoPlayer styles */
|
||||
.overlay-video-container .main-video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.overlay-video-container .video-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.overlay-video-container:hover .video-overlay,
|
||||
.overlay-video-container.paused .video-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.overlay-video-container .video-title {
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
margin-bottom: 8px;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
|
||||
max-width: 80%;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.overlay-video-container .video-info {
|
||||
color: #ccc;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.overlay-video-container .play-overlay {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.overlay-video-container .play-button-large {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
font-size: 32px;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.overlay-video-container .play-button-large:hover {
|
||||
background: #fff;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.overlay-video-container .video-loading {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Video Controls for Overlay */
|
||||
.overlay-video-container .video-controls {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: linear-gradient(transparent, rgba(0, 0, 0, 0.8));
|
||||
padding: 20px 15px 15px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.overlay-video-container:hover .video-controls,
|
||||
.overlay-video-container.paused .video-controls {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.overlay-video-container .progress-container {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.overlay-video-container .progress-bar {
|
||||
position: relative;
|
||||
height: 4px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.overlay-video-container .progress-filled {
|
||||
height: 100%;
|
||||
background: #e50914;
|
||||
border-radius: 2px;
|
||||
transition: width 0.1s ease;
|
||||
}
|
||||
|
||||
.overlay-video-container .progress-thumb {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background: #e50914;
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.overlay-video-container .progress-bar:hover .progress-thumb {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.overlay-video-container .time-display {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.overlay-video-container .controls-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.overlay-video-container .controls-left,
|
||||
.overlay-video-container .controls-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.overlay-video-container .controls-center {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.overlay-video-container .video-name {
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.overlay-video-container .control-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.overlay-video-container .control-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.overlay-video-container .volume-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.overlay-video-container .volume-slider {
|
||||
width: 80px;
|
||||
height: 4px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 2px;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.overlay-video-container .volume-slider::-webkit-slider-thumb {
|
||||
appearance: none;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background: #e50914;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.overlay-video-container .volume-percentage {
|
||||
color: #ccc;
|
||||
font-size: 11px;
|
||||
font-family: 'Courier New', monospace;
|
||||
min-width: 30px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Random video button special styling */
|
||||
.overlay-video-container .random-video-btn {
|
||||
background: rgba(255, 215, 0, 0.1);
|
||||
border: 1px solid rgba(255, 215, 0, 0.3);
|
||||
}
|
||||
|
||||
.overlay-video-container .random-video-btn:hover {
|
||||
background: rgba(255, 215, 0, 0.2);
|
||||
border-color: rgba(255, 215, 0, 0.5);
|
||||
}
|
||||
|
||||
/* Loading spinner for overlay */
|
||||
.overlay-video-container .loading-spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid rgba(255, 255, 255, 0.2);
|
||||
border-top: 4px solid #e50914;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Responsive design */
|
||||
@media (max-width: 768px) {
|
||||
.overlay-window {
|
||||
width: 95%;
|
||||
max-height: 95%;
|
||||
}
|
||||
|
||||
.overlay-header {
|
||||
padding: 12px 15px;
|
||||
}
|
||||
|
||||
.overlay-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.overlay-video-container .video-title {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.overlay-video-container .controls-center {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.overlay-video-container .volume-slider {
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.overlay-video-container .controls-center {
|
||||
display: none; /* Hide video name on very small screens */
|
||||
}
|
||||
|
||||
.overlay-video-container .volume-percentage {
|
||||
display: none; /* Hide percentage on very small screens */
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue