Implement Porn Cinema media player
- Add dedicated porn-cinema.html page with professional media player UI - Create PornCinema class with full video player functionality - Implement VideoLibrary class for grid/list video management - Add comprehensive one-handed keyboard shortcuts - Support playlist creation, management, and auto-advance - Include quality selection and theater/fullscreen modes - Add cinema-specific CSS styling with dark theme - Integrate with existing video management system - Add button on home screen to launch cinema mode Features: Separate dedicated page Video thumbnails using existing patterns Playlist support from start Multiple quality options for performance One-handed keyboard shortcuts for easy control Professional media player controls Grid/list library views Theater mode and fullscreen support
This commit is contained in:
parent
f703f428d5
commit
c524f3bc46
11
index.html
11
index.html
|
|
@ -92,6 +92,7 @@
|
|||
<!-- Main Action Buttons -->
|
||||
<div class="main-actions">
|
||||
<button id="start-btn" class="btn btn-primary">Start Game</button>
|
||||
<button id="porn-cinema-btn" class="btn btn-primary">🎬 Porn Cinema</button>
|
||||
<button id="manage-tasks-btn" class="btn btn-secondary">Manage Tasks</button>
|
||||
<button id="manage-images-btn" class="btn btn-secondary">Manage Images</button>
|
||||
<button id="manage-audio-btn" class="btn btn-secondary">🎵 Manage Audio</button>
|
||||
|
|
@ -2626,6 +2627,16 @@
|
|||
});
|
||||
}
|
||||
|
||||
// Set up porn cinema button (only once)
|
||||
const pornCinemaBtn = document.getElementById('porn-cinema-btn');
|
||||
if (pornCinemaBtn && !pornCinemaBtn.hasAttribute('data-handler-attached')) {
|
||||
pornCinemaBtn.setAttribute('data-handler-attached', 'true');
|
||||
pornCinemaBtn.addEventListener('click', () => {
|
||||
console.log('🎬 Opening Porn Cinema...');
|
||||
window.location.href = 'porn-cinema.html';
|
||||
});
|
||||
}
|
||||
|
||||
// Set up clear overall XP button (debug tool)
|
||||
const clearXpBtn = document.getElementById('clear-overall-xp-btn');
|
||||
if (clearXpBtn && !clearXpBtn.hasAttribute('data-handler-attached')) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,229 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline' 'unsafe-eval' data: file: blob: http://localhost:* https:; connect-src 'self' http://localhost:* https: ws://localhost:*; img-src 'self' data: file: blob:; media-src 'self' data: file: blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https:;">
|
||||
<title>Porn Cinema - Gooner Training Academy</title>
|
||||
<link rel="stylesheet" href="src/styles/styles.css">
|
||||
<link rel="stylesheet" href="src/styles/porn-cinema.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Audiowide&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
<body class="cinema-mode">
|
||||
<!-- Loading Overlay -->
|
||||
<div id="cinema-loading" class="cinema-loading">
|
||||
<div class="loading-content">
|
||||
<div class="loading-spinner"></div>
|
||||
<h2>Loading Cinema...</h2>
|
||||
<p>Preparing your video library...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Cinema Header -->
|
||||
<header class="cinema-header">
|
||||
<div class="cinema-nav">
|
||||
<button id="back-to-home" class="btn btn-secondary">← Back to Home</button>
|
||||
<h1>🎬 Porn Cinema</h1>
|
||||
<div class="cinema-controls">
|
||||
<button id="theater-mode" class="btn btn-secondary" title="Theater Mode (Dim UI)">🎭</button>
|
||||
<button id="fullscreen-toggle" class="btn btn-secondary" title="Fullscreen (F)">⛶</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Keyboard Shortcuts Help -->
|
||||
<div class="shortcuts-help" id="shortcuts-help">
|
||||
<div class="shortcuts-content">
|
||||
<h3>🎹 One-Handed Controls</h3>
|
||||
<div class="shortcuts-grid">
|
||||
<div class="shortcut-item"><kbd>Space</kbd> Play/Pause</div>
|
||||
<div class="shortcut-item"><kbd>←</kbd><kbd>→</kbd> Seek ±10s</div>
|
||||
<div class="shortcut-item"><kbd>↑</kbd><kbd>↓</kbd> Volume ±10%</div>
|
||||
<div class="shortcut-item"><kbd>F</kbd> Fullscreen</div>
|
||||
<div class="shortcut-item"><kbd>M</kbd> Mute/Unmute</div>
|
||||
<div class="shortcut-item"><kbd>1-4</kbd> Quality</div>
|
||||
<div class="shortcut-item"><kbd>Enter</kbd> Add to Playlist</div>
|
||||
<div class="shortcut-item"><kbd>N</kbd> Next Video</div>
|
||||
<div class="shortcut-item"><kbd>P</kbd> Previous Video</div>
|
||||
<div class="shortcut-item"><kbd>S</kbd> Shuffle Playlist</div>
|
||||
<div class="shortcut-item"><kbd>Escape</kbd> Exit Fullscreen</div>
|
||||
<div class="shortcut-item"><kbd>?</kbd> Toggle This Help</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Cinema Content -->
|
||||
<main class="cinema-main">
|
||||
<!-- Video Player Section -->
|
||||
<section class="video-player-section">
|
||||
<div class="video-container" id="video-container">
|
||||
<video id="main-video-player" 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 -->
|
||||
<div class="video-overlay" id="video-overlay">
|
||||
<div class="video-title" id="video-title">Select a video to begin</div>
|
||||
<div class="video-info" id="video-info"></div>
|
||||
|
||||
<!-- Play Button Overlay -->
|
||||
<div class="play-overlay" id="play-overlay">
|
||||
<button class="play-button-large" id="play-button-large">▶</button>
|
||||
</div>
|
||||
|
||||
<!-- Loading Spinner -->
|
||||
<div class="video-loading" id="video-loading" style="display: none;">
|
||||
<div class="loading-spinner"></div>
|
||||
<p>Loading video...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Custom Video Controls -->
|
||||
<div class="video-controls" id="video-controls">
|
||||
<div class="progress-container">
|
||||
<div class="progress-bar" id="progress-bar">
|
||||
<div class="progress-filled" id="progress-filled"></div>
|
||||
<div class="progress-thumb" id="progress-thumb"></div>
|
||||
</div>
|
||||
<div class="time-display">
|
||||
<span id="current-time">0:00</span>
|
||||
<span class="time-separator">/</span>
|
||||
<span id="total-time">0:00</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls-row">
|
||||
<div class="controls-left">
|
||||
<button id="play-pause-btn" class="control-btn">▶</button>
|
||||
<button id="prev-video-btn" class="control-btn" title="Previous Video (P)">⏮</button>
|
||||
<button id="next-video-btn" class="control-btn" title="Next Video (N)">⏭</button>
|
||||
<div class="volume-control">
|
||||
<button id="mute-btn" class="control-btn" title="Mute (M)">🔊</button>
|
||||
<input type="range" id="volume-slider" class="volume-slider" min="0" max="100" value="70">
|
||||
<span id="volume-percentage">70%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls-center">
|
||||
<div class="quality-selector">
|
||||
<select id="quality-select" class="quality-dropdown">
|
||||
<option value="auto">Auto Quality</option>
|
||||
<option value="1080p">1080p (1)</option>
|
||||
<option value="720p">720p (2)</option>
|
||||
<option value="480p">480p (3)</option>
|
||||
<option value="360p">360p (4)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="speed-control">
|
||||
<select id="speed-select" class="speed-dropdown">
|
||||
<option value="0.5">0.5x</option>
|
||||
<option value="0.75">0.75x</option>
|
||||
<option value="1" selected>1x</option>
|
||||
<option value="1.25">1.25x</option>
|
||||
<option value="1.5">1.5x</option>
|
||||
<option value="2">2x</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls-right">
|
||||
<button id="add-to-playlist-btn" class="control-btn" title="Add to Playlist (Enter)">➕</button>
|
||||
<button id="theater-mode-btn" class="control-btn" title="Theater Mode">🎭</button>
|
||||
<button id="fullscreen-btn" class="control-btn" title="Fullscreen (F)">⛶</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Playlist Section -->
|
||||
<section class="playlist-section" id="playlist-section">
|
||||
<div class="playlist-header">
|
||||
<h3>📝 Current Playlist</h3>
|
||||
<div class="playlist-controls">
|
||||
<button id="shuffle-playlist" class="btn btn-mini" title="Shuffle (S)">🔀</button>
|
||||
<button id="clear-playlist" class="btn btn-mini btn-danger">🗑️ Clear</button>
|
||||
<button id="save-playlist" class="btn btn-mini">💾 Save</button>
|
||||
<button id="load-playlist" class="btn btn-mini">📁 Load</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="playlist-content" id="playlist-content">
|
||||
<div class="playlist-empty">
|
||||
<p>Playlist is empty. Add videos by clicking ➕ or pressing Enter while a video is selected.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Video Library Section -->
|
||||
<section class="video-library-section">
|
||||
<div class="library-header">
|
||||
<h3>📁 Video Library</h3>
|
||||
<div class="library-controls">
|
||||
<div class="view-toggle">
|
||||
<button id="grid-view-btn" class="view-btn active" title="Grid View">⊞</button>
|
||||
<button id="list-view-btn" class="view-btn" title="List View">☰</button>
|
||||
</div>
|
||||
<div class="sort-controls">
|
||||
<select id="sort-select" class="sort-dropdown">
|
||||
<option value="name">Sort by Name</option>
|
||||
<option value="date">Sort by Date</option>
|
||||
<option value="duration">Sort by Duration</option>
|
||||
<option value="size">Sort by Size</option>
|
||||
</select>
|
||||
<button id="sort-direction" class="btn btn-mini" title="Sort Direction">↕️</button>
|
||||
</div>
|
||||
<div class="search-controls">
|
||||
<input type="text" id="library-search" class="search-input" placeholder="Search videos...">
|
||||
<button id="refresh-library" class="btn btn-mini" title="Refresh Library">🔄</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="library-content" id="library-content">
|
||||
<div class="library-loading">
|
||||
<div class="loading-spinner"></div>
|
||||
<p>Loading video library...</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="src/utils/desktop-file-manager.js"></script>
|
||||
<script src="src/features/media/videoLibrary.js"></script>
|
||||
<script src="src/features/media/pornCinema.js"></script>
|
||||
|
||||
<script>
|
||||
// Initialize cinema when page loads
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('🎬 Initializing Porn Cinema...');
|
||||
|
||||
// Initialize the cinema
|
||||
window.pornCinema = new PornCinema();
|
||||
window.pornCinema.initialize();
|
||||
|
||||
// Hide loading overlay
|
||||
setTimeout(() => {
|
||||
document.getElementById('cinema-loading').style.display = 'none';
|
||||
}, 1500);
|
||||
});
|
||||
|
||||
// Back to home functionality
|
||||
document.getElementById('back-to-home').addEventListener('click', () => {
|
||||
if (confirm('Return to home screen? Current playback will stop.')) {
|
||||
window.location.href = 'index.html';
|
||||
}
|
||||
});
|
||||
|
||||
// Keyboard shortcut to toggle help
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === '?' || (e.shiftKey && e.key === '/')) {
|
||||
const help = document.getElementById('shortcuts-help');
|
||||
help.style.display = help.style.display === 'none' ? 'block' : 'none';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,738 @@
|
|||
/**
|
||||
* Porn Cinema Media Player
|
||||
* Full-featured video player with playlist support and one-handed controls
|
||||
*/
|
||||
|
||||
class PornCinema {
|
||||
constructor() {
|
||||
this.currentVideo = null;
|
||||
this.currentVideoElement = null;
|
||||
this.isPlaying = false;
|
||||
this.isFullscreen = false;
|
||||
this.theaterMode = false;
|
||||
this.volume = 0.7;
|
||||
this.playbackRate = 1.0;
|
||||
this.currentQuality = 'auto';
|
||||
|
||||
// Playlist
|
||||
this.playlist = [];
|
||||
this.currentPlaylistIndex = -1;
|
||||
this.shuffleMode = false;
|
||||
this.originalPlaylistOrder = [];
|
||||
|
||||
// Video library
|
||||
this.videoLibrary = null;
|
||||
|
||||
// Control elements
|
||||
this.controls = {};
|
||||
|
||||
// Keyboard shortcuts
|
||||
this.shortcuts = {
|
||||
' ': () => this.togglePlayPause(),
|
||||
'ArrowLeft': () => this.seek(-10),
|
||||
'ArrowRight': () => this.seek(10),
|
||||
'ArrowUp': () => this.adjustVolume(0.1),
|
||||
'ArrowDown': () => this.adjustVolume(-0.1),
|
||||
'f': () => this.toggleFullscreen(),
|
||||
'F': () => this.toggleFullscreen(),
|
||||
'm': () => this.toggleMute(),
|
||||
'M': () => this.toggleMute(),
|
||||
'n': () => this.nextVideo(),
|
||||
'N': () => this.nextVideo(),
|
||||
'p': () => this.previousVideo(),
|
||||
'P': () => this.previousVideo(),
|
||||
's': () => this.shufflePlaylist(),
|
||||
'S': () => this.shufflePlaylist(),
|
||||
'1': () => this.setQuality('1080p'),
|
||||
'2': () => this.setQuality('720p'),
|
||||
'3': () => this.setQuality('480p'),
|
||||
'4': () => this.setQuality('360p'),
|
||||
'Enter': () => this.addCurrentToPlaylist(),
|
||||
'Escape': () => this.exitFullscreen()
|
||||
};
|
||||
|
||||
this.initializeElements();
|
||||
this.attachEventListeners();
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
console.log('🎬 Initializing Porn Cinema...');
|
||||
|
||||
// Initialize video library
|
||||
this.videoLibrary = new VideoLibrary(this);
|
||||
await this.videoLibrary.loadVideoLibrary();
|
||||
|
||||
// Set initial volume
|
||||
this.setVolume(this.volume);
|
||||
|
||||
console.log('✅ Porn Cinema initialized');
|
||||
}
|
||||
|
||||
initializeElements() {
|
||||
// Video elements
|
||||
this.videoContainer = document.getElementById('video-container');
|
||||
this.videoElement = document.getElementById('main-video-player');
|
||||
this.videoSource = document.getElementById('video-source');
|
||||
this.videoOverlay = document.getElementById('video-overlay');
|
||||
this.videoTitle = document.getElementById('video-title');
|
||||
this.videoInfo = document.getElementById('video-info');
|
||||
this.playOverlay = document.getElementById('play-overlay');
|
||||
this.playButtonLarge = document.getElementById('play-button-large');
|
||||
this.videoLoading = document.getElementById('video-loading');
|
||||
|
||||
// Control elements
|
||||
this.controls.container = document.getElementById('video-controls');
|
||||
this.controls.playPause = document.getElementById('play-pause-btn');
|
||||
this.controls.prevVideo = document.getElementById('prev-video-btn');
|
||||
this.controls.nextVideo = document.getElementById('next-video-btn');
|
||||
this.controls.mute = document.getElementById('mute-btn');
|
||||
this.controls.volume = document.getElementById('volume-slider');
|
||||
this.controls.volumePercentage = document.getElementById('volume-percentage');
|
||||
this.controls.progressBar = document.getElementById('progress-bar');
|
||||
this.controls.progressFilled = document.getElementById('progress-filled');
|
||||
this.controls.progressThumb = document.getElementById('progress-thumb');
|
||||
this.controls.currentTime = document.getElementById('current-time');
|
||||
this.controls.totalTime = document.getElementById('total-time');
|
||||
this.controls.quality = document.getElementById('quality-select');
|
||||
this.controls.speed = document.getElementById('speed-select');
|
||||
this.controls.addToPlaylist = document.getElementById('add-to-playlist-btn');
|
||||
this.controls.theater = document.getElementById('theater-mode-btn');
|
||||
this.controls.fullscreen = document.getElementById('fullscreen-btn');
|
||||
|
||||
// Playlist elements
|
||||
this.playlistContent = document.getElementById('playlist-content');
|
||||
this.shuffleBtn = document.getElementById('shuffle-playlist');
|
||||
this.clearPlaylistBtn = document.getElementById('clear-playlist');
|
||||
this.savePlaylistBtn = document.getElementById('save-playlist');
|
||||
this.loadPlaylistBtn = document.getElementById('load-playlist');
|
||||
}
|
||||
|
||||
attachEventListeners() {
|
||||
// Video events
|
||||
this.videoElement.addEventListener('loadstart', () => this.onLoadStart());
|
||||
this.videoElement.addEventListener('loadedmetadata', () => this.onLoadedMetadata());
|
||||
this.videoElement.addEventListener('loadeddata', () => this.onLoadedData());
|
||||
this.videoElement.addEventListener('canplay', () => this.onCanPlay());
|
||||
this.videoElement.addEventListener('play', () => this.onPlay());
|
||||
this.videoElement.addEventListener('pause', () => this.onPause());
|
||||
this.videoElement.addEventListener('ended', () => this.onEnded());
|
||||
this.videoElement.addEventListener('timeupdate', () => this.onTimeUpdate());
|
||||
this.videoElement.addEventListener('volumechange', () => this.onVolumeChange());
|
||||
this.videoElement.addEventListener('error', (e) => this.onError(e));
|
||||
|
||||
// Large play button
|
||||
this.playButtonLarge.addEventListener('click', () => this.togglePlayPause());
|
||||
|
||||
// Control buttons
|
||||
this.controls.playPause.addEventListener('click', () => this.togglePlayPause());
|
||||
this.controls.prevVideo.addEventListener('click', () => this.previousVideo());
|
||||
this.controls.nextVideo.addEventListener('click', () => this.nextVideo());
|
||||
this.controls.mute.addEventListener('click', () => this.toggleMute());
|
||||
this.controls.addToPlaylist.addEventListener('click', () => this.addCurrentToPlaylist());
|
||||
this.controls.theater.addEventListener('click', () => this.toggleTheaterMode());
|
||||
this.controls.fullscreen.addEventListener('click', () => this.toggleFullscreen());
|
||||
|
||||
// Volume control
|
||||
this.controls.volume.addEventListener('input', (e) => this.setVolume(e.target.value / 100));
|
||||
|
||||
// Progress bar
|
||||
this.controls.progressBar.addEventListener('click', (e) => this.seekToPosition(e));
|
||||
this.controls.progressBar.addEventListener('mousedown', () => this.startSeeking());
|
||||
|
||||
// Quality and speed controls
|
||||
this.controls.quality.addEventListener('change', (e) => this.setQuality(e.target.value));
|
||||
this.controls.speed.addEventListener('change', (e) => this.setPlaybackRate(e.target.value));
|
||||
|
||||
// Playlist controls
|
||||
this.shuffleBtn.addEventListener('click', () => this.shufflePlaylist());
|
||||
this.clearPlaylistBtn.addEventListener('click', () => this.clearPlaylist());
|
||||
this.savePlaylistBtn.addEventListener('click', () => this.savePlaylist());
|
||||
this.loadPlaylistBtn.addEventListener('click', () => this.loadPlaylist());
|
||||
|
||||
// Keyboard shortcuts
|
||||
document.addEventListener('keydown', (e) => this.handleKeyboardShortcut(e));
|
||||
|
||||
// Fullscreen events
|
||||
document.addEventListener('fullscreenchange', () => this.onFullscreenChange());
|
||||
document.addEventListener('webkitfullscreenchange', () => this.onFullscreenChange());
|
||||
document.addEventListener('mozfullscreenchange', () => this.onFullscreenChange());
|
||||
|
||||
// Theater mode buttons
|
||||
document.getElementById('theater-mode').addEventListener('click', () => this.toggleTheaterMode());
|
||||
document.getElementById('fullscreen-toggle').addEventListener('click', () => this.toggleFullscreen());
|
||||
|
||||
// Video container hover for controls
|
||||
this.videoContainer.addEventListener('mouseenter', () => this.showControls());
|
||||
this.videoContainer.addEventListener('mouseleave', () => this.hideControls());
|
||||
this.videoContainer.addEventListener('mousemove', () => this.showControls());
|
||||
}
|
||||
|
||||
// Video loading and playback methods
|
||||
selectVideo(video) {
|
||||
this.currentVideo = video;
|
||||
this.updateVideoInfo();
|
||||
this.updateVideoTitle();
|
||||
}
|
||||
|
||||
async playVideo(video) {
|
||||
try {
|
||||
console.log(`🎬 Playing video: ${video.name}`);
|
||||
|
||||
this.currentVideo = video;
|
||||
this.showLoading();
|
||||
|
||||
// Update video source
|
||||
this.videoSource.src = video.path;
|
||||
this.videoSource.type = `video/${video.format || 'mp4'}`;
|
||||
|
||||
// Load and play
|
||||
this.videoElement.load();
|
||||
|
||||
// Update UI
|
||||
this.updateVideoInfo();
|
||||
this.updateVideoTitle();
|
||||
this.updatePlaylistSelection();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error playing video:', error);
|
||||
this.showError('Failed to load video');
|
||||
}
|
||||
}
|
||||
|
||||
togglePlayPause() {
|
||||
if (!this.videoElement.src) {
|
||||
// No video loaded, try to play first video from library or playlist
|
||||
this.playFirstAvailable();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.videoElement.paused) {
|
||||
this.play();
|
||||
} else {
|
||||
this.pause();
|
||||
}
|
||||
}
|
||||
|
||||
play() {
|
||||
const playPromise = this.videoElement.play();
|
||||
|
||||
if (playPromise !== undefined) {
|
||||
playPromise.then(() => {
|
||||
this.isPlaying = true;
|
||||
this.updatePlayButton();
|
||||
this.hidePlayOverlay();
|
||||
}).catch(error => {
|
||||
console.error('Error playing video:', error);
|
||||
this.showError('Failed to play video');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pause() {
|
||||
this.videoElement.pause();
|
||||
this.isPlaying = false;
|
||||
this.updatePlayButton();
|
||||
this.showPlayOverlay();
|
||||
}
|
||||
|
||||
seek(seconds) {
|
||||
if (this.videoElement.duration) {
|
||||
const newTime = Math.max(0, Math.min(this.videoElement.duration, this.videoElement.currentTime + seconds));
|
||||
this.videoElement.currentTime = newTime;
|
||||
}
|
||||
}
|
||||
|
||||
seekToPosition(event) {
|
||||
if (this.videoElement.duration) {
|
||||
const rect = this.controls.progressBar.getBoundingClientRect();
|
||||
const pos = (event.clientX - rect.left) / rect.width;
|
||||
this.videoElement.currentTime = pos * this.videoElement.duration;
|
||||
}
|
||||
}
|
||||
|
||||
// Volume and audio controls
|
||||
setVolume(volume) {
|
||||
this.volume = Math.max(0, Math.min(1, volume));
|
||||
this.videoElement.volume = this.volume;
|
||||
this.controls.volume.value = this.volume * 100;
|
||||
this.controls.volumePercentage.textContent = Math.round(this.volume * 100) + '%';
|
||||
this.updateMuteButton();
|
||||
}
|
||||
|
||||
adjustVolume(delta) {
|
||||
this.setVolume(this.volume + delta);
|
||||
}
|
||||
|
||||
toggleMute() {
|
||||
if (this.videoElement.muted) {
|
||||
this.videoElement.muted = false;
|
||||
this.setVolume(this.volume > 0 ? this.volume : 0.7);
|
||||
} else {
|
||||
this.videoElement.muted = true;
|
||||
}
|
||||
this.updateMuteButton();
|
||||
}
|
||||
|
||||
updateMuteButton() {
|
||||
const isMuted = this.videoElement.muted || this.volume === 0;
|
||||
this.controls.mute.textContent = isMuted ? '🔇' : '🔊';
|
||||
}
|
||||
|
||||
// Quality and playback controls
|
||||
setQuality(quality) {
|
||||
this.currentQuality = quality;
|
||||
this.controls.quality.value = quality;
|
||||
|
||||
// In a real implementation, you would switch video sources here
|
||||
// For now, we'll just update the UI
|
||||
console.log(`Quality set to: ${quality}`);
|
||||
}
|
||||
|
||||
setPlaybackRate(rate) {
|
||||
this.playbackRate = parseFloat(rate);
|
||||
this.videoElement.playbackRate = this.playbackRate;
|
||||
this.controls.speed.value = rate;
|
||||
}
|
||||
|
||||
// Fullscreen and theater mode
|
||||
toggleFullscreen() {
|
||||
if (!this.isFullscreen) {
|
||||
this.enterFullscreen();
|
||||
} else {
|
||||
this.exitFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
enterFullscreen() {
|
||||
const element = this.videoContainer;
|
||||
|
||||
if (element.requestFullscreen) {
|
||||
element.requestFullscreen();
|
||||
} else if (element.webkitRequestFullscreen) {
|
||||
element.webkitRequestFullscreen();
|
||||
} else if (element.mozRequestFullScreen) {
|
||||
element.mozRequestFullScreen();
|
||||
}
|
||||
}
|
||||
|
||||
exitFullscreen() {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.webkitExitFullscreen) {
|
||||
document.webkitExitFullscreen();
|
||||
} else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen();
|
||||
}
|
||||
}
|
||||
|
||||
toggleTheaterMode() {
|
||||
this.theaterMode = !this.theaterMode;
|
||||
document.body.classList.toggle('theater-mode', this.theaterMode);
|
||||
|
||||
const button = document.getElementById('theater-mode');
|
||||
if (button) {
|
||||
button.textContent = this.theaterMode ? '💡' : '🎭';
|
||||
button.title = this.theaterMode ? 'Exit Theater Mode' : 'Theater Mode (Dim UI)';
|
||||
}
|
||||
}
|
||||
|
||||
// Playlist management
|
||||
addToPlaylist(video) {
|
||||
if (!this.playlist.find(v => v.path === video.path)) {
|
||||
this.playlist.push({...video});
|
||||
this.updatePlaylistDisplay();
|
||||
console.log(`➕ Added to playlist: ${video.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
addCurrentToPlaylist() {
|
||||
if (this.currentVideo) {
|
||||
this.addToPlaylist(this.currentVideo);
|
||||
} else if (this.videoLibrary && this.videoLibrary.getSelectedVideo()) {
|
||||
this.addToPlaylist(this.videoLibrary.getSelectedVideo());
|
||||
}
|
||||
}
|
||||
|
||||
removeFromPlaylist(index) {
|
||||
if (index >= 0 && index < this.playlist.length) {
|
||||
const removed = this.playlist.splice(index, 1)[0];
|
||||
console.log(`➖ Removed from playlist: ${removed.name}`);
|
||||
|
||||
// Adjust current index if necessary
|
||||
if (this.currentPlaylistIndex > index) {
|
||||
this.currentPlaylistIndex--;
|
||||
} else if (this.currentPlaylistIndex === index) {
|
||||
this.currentPlaylistIndex = -1;
|
||||
}
|
||||
|
||||
this.updatePlaylistDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
clearPlaylist() {
|
||||
if (confirm('Clear entire playlist?')) {
|
||||
this.playlist = [];
|
||||
this.currentPlaylistIndex = -1;
|
||||
this.updatePlaylistDisplay();
|
||||
console.log('🗑️ Playlist cleared');
|
||||
}
|
||||
}
|
||||
|
||||
shufflePlaylist() {
|
||||
if (this.playlist.length <= 1) return;
|
||||
|
||||
this.shuffleMode = !this.shuffleMode;
|
||||
|
||||
if (this.shuffleMode) {
|
||||
// Save original order
|
||||
this.originalPlaylistOrder = [...this.playlist];
|
||||
|
||||
// Shuffle playlist
|
||||
for (let i = this.playlist.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[this.playlist[i], this.playlist[j]] = [this.playlist[j], this.playlist[i]];
|
||||
}
|
||||
|
||||
this.shuffleBtn.textContent = '🔀';
|
||||
this.shuffleBtn.title = 'Disable Shuffle';
|
||||
console.log('🔀 Playlist shuffled');
|
||||
} else {
|
||||
// Restore original order
|
||||
this.playlist = [...this.originalPlaylistOrder];
|
||||
this.shuffleBtn.textContent = '🔀';
|
||||
this.shuffleBtn.title = 'Shuffle Playlist';
|
||||
console.log('📝 Playlist order restored');
|
||||
}
|
||||
|
||||
this.currentPlaylistIndex = this.playlist.findIndex(v =>
|
||||
this.currentVideo && v.path === this.currentVideo.path
|
||||
);
|
||||
|
||||
this.updatePlaylistDisplay();
|
||||
}
|
||||
|
||||
nextVideo() {
|
||||
if (this.playlist.length === 0) return;
|
||||
|
||||
let nextIndex = this.currentPlaylistIndex + 1;
|
||||
if (nextIndex >= this.playlist.length) {
|
||||
nextIndex = 0; // Loop back to start
|
||||
}
|
||||
|
||||
this.playVideoFromPlaylist(nextIndex);
|
||||
}
|
||||
|
||||
previousVideo() {
|
||||
if (this.playlist.length === 0) return;
|
||||
|
||||
let prevIndex = this.currentPlaylistIndex - 1;
|
||||
if (prevIndex < 0) {
|
||||
prevIndex = this.playlist.length - 1; // Loop to end
|
||||
}
|
||||
|
||||
this.playVideoFromPlaylist(prevIndex);
|
||||
}
|
||||
|
||||
playVideoFromPlaylist(index) {
|
||||
if (index >= 0 && index < this.playlist.length) {
|
||||
this.currentPlaylistIndex = index;
|
||||
this.playVideo(this.playlist[index]);
|
||||
}
|
||||
}
|
||||
|
||||
updatePlaylistDisplay() {
|
||||
if (this.playlist.length === 0) {
|
||||
this.playlistContent.innerHTML = `
|
||||
<div class="playlist-empty">
|
||||
<p>Playlist is empty. Add videos by clicking ➕ or pressing Enter while a video is selected.</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
this.playlistContent.innerHTML = this.playlist.map((video, index) => `
|
||||
<div class="playlist-item ${index === this.currentPlaylistIndex ? 'current' : ''}"
|
||||
data-index="${index}">
|
||||
<div class="playlist-thumbnail">
|
||||
${video.thumbnail ?
|
||||
`<img src="${video.thumbnail}" alt="${video.name}" class="playlist-thumbnail">` :
|
||||
`<div class="playlist-thumbnail" style="display: flex; align-items: center; justify-content: center; font-size: 1.2rem;">🎬</div>`
|
||||
}
|
||||
</div>
|
||||
<div class="playlist-details">
|
||||
<div class="playlist-title">${video.name}</div>
|
||||
<div class="playlist-duration">${this.formatDuration(video.duration)}</div>
|
||||
</div>
|
||||
<div class="playlist-actions">
|
||||
<button class="btn btn-mini play-playlist-item" title="Play">▶</button>
|
||||
<button class="btn btn-mini remove-playlist-item" title="Remove">❌</button>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
// Attach playlist item events
|
||||
this.attachPlaylistEvents();
|
||||
}
|
||||
|
||||
attachPlaylistEvents() {
|
||||
const playlistItems = this.playlistContent.querySelectorAll('.playlist-item');
|
||||
playlistItems.forEach((item, index) => {
|
||||
item.addEventListener('click', (e) => {
|
||||
if (e.target.closest('.playlist-actions')) return;
|
||||
this.playVideoFromPlaylist(index);
|
||||
});
|
||||
});
|
||||
|
||||
const playButtons = this.playlistContent.querySelectorAll('.play-playlist-item');
|
||||
playButtons.forEach((button, index) => {
|
||||
button.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
this.playVideoFromPlaylist(index);
|
||||
});
|
||||
});
|
||||
|
||||
const removeButtons = this.playlistContent.querySelectorAll('.remove-playlist-item');
|
||||
removeButtons.forEach((button, index) => {
|
||||
button.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
this.removeFromPlaylist(index);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
updatePlaylistSelection() {
|
||||
if (this.currentVideo) {
|
||||
this.currentPlaylistIndex = this.playlist.findIndex(v => v.path === this.currentVideo.path);
|
||||
this.updatePlaylistDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
// Keyboard shortcuts
|
||||
handleKeyboardShortcut(event) {
|
||||
// Don't handle shortcuts if typing in an input
|
||||
if (event.target.tagName === 'INPUT' || event.target.tagName === 'TEXTAREA') {
|
||||
return;
|
||||
}
|
||||
|
||||
const key = event.key;
|
||||
if (this.shortcuts[key]) {
|
||||
event.preventDefault();
|
||||
this.shortcuts[key]();
|
||||
}
|
||||
}
|
||||
|
||||
// Video event handlers
|
||||
onLoadStart() {
|
||||
this.showLoading();
|
||||
}
|
||||
|
||||
onLoadedMetadata() {
|
||||
this.updateTimeDisplay();
|
||||
this.hideLoading();
|
||||
}
|
||||
|
||||
onLoadedData() {
|
||||
this.hideLoading();
|
||||
}
|
||||
|
||||
onCanPlay() {
|
||||
this.hideLoading();
|
||||
this.updatePlayButton();
|
||||
}
|
||||
|
||||
onPlay() {
|
||||
this.isPlaying = true;
|
||||
this.updatePlayButton();
|
||||
this.hidePlayOverlay();
|
||||
}
|
||||
|
||||
onPause() {
|
||||
this.isPlaying = false;
|
||||
this.updatePlayButton();
|
||||
this.showPlayOverlay();
|
||||
}
|
||||
|
||||
onEnded() {
|
||||
this.isPlaying = false;
|
||||
this.updatePlayButton();
|
||||
this.showPlayOverlay();
|
||||
|
||||
// Auto-play next video in playlist
|
||||
if (this.playlist.length > 0 && this.currentPlaylistIndex >= 0) {
|
||||
setTimeout(() => this.nextVideo(), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
onTimeUpdate() {
|
||||
this.updateProgressBar();
|
||||
this.updateTimeDisplay();
|
||||
}
|
||||
|
||||
onVolumeChange() {
|
||||
this.updateMuteButton();
|
||||
}
|
||||
|
||||
onError(event) {
|
||||
console.error('Video error:', event);
|
||||
this.hideLoading();
|
||||
this.showError('Error loading video');
|
||||
}
|
||||
|
||||
onFullscreenChange() {
|
||||
this.isFullscreen = !!(document.fullscreenElement ||
|
||||
document.webkitFullscreenElement ||
|
||||
document.mozFullScreenElement);
|
||||
|
||||
this.controls.fullscreen.textContent = this.isFullscreen ? '🗗' : '⛶';
|
||||
this.controls.fullscreen.title = this.isFullscreen ? 'Exit Fullscreen (Esc)' : 'Fullscreen (F)';
|
||||
}
|
||||
|
||||
// UI update methods
|
||||
updateVideoInfo() {
|
||||
if (this.currentVideo) {
|
||||
const info = `${this.currentVideo.resolution || 'Unknown'} • ${this.formatFileSize(this.currentVideo.size)}`;
|
||||
this.videoInfo.textContent = info;
|
||||
} else {
|
||||
this.videoInfo.textContent = '';
|
||||
}
|
||||
}
|
||||
|
||||
updateVideoTitle() {
|
||||
if (this.currentVideo) {
|
||||
this.videoTitle.textContent = this.currentVideo.name;
|
||||
} else {
|
||||
this.videoTitle.textContent = 'Select a video to begin';
|
||||
}
|
||||
}
|
||||
|
||||
updatePlayButton() {
|
||||
const playText = this.isPlaying ? '⏸' : '▶';
|
||||
this.controls.playPause.textContent = playText;
|
||||
this.playButtonLarge.textContent = this.isPlaying ? '⏸' : '▶';
|
||||
}
|
||||
|
||||
updateProgressBar() {
|
||||
if (this.videoElement.duration) {
|
||||
const progress = (this.videoElement.currentTime / this.videoElement.duration) * 100;
|
||||
this.controls.progressFilled.style.width = progress + '%';
|
||||
this.controls.progressThumb.style.left = progress + '%';
|
||||
}
|
||||
}
|
||||
|
||||
updateTimeDisplay() {
|
||||
this.controls.currentTime.textContent = this.formatDuration(this.videoElement.currentTime);
|
||||
this.controls.totalTime.textContent = this.formatDuration(this.videoElement.duration);
|
||||
}
|
||||
|
||||
// UI state methods
|
||||
showLoading() {
|
||||
this.videoLoading.style.display = 'block';
|
||||
}
|
||||
|
||||
hideLoading() {
|
||||
this.videoLoading.style.display = 'none';
|
||||
}
|
||||
|
||||
showPlayOverlay() {
|
||||
this.playOverlay.style.display = 'block';
|
||||
}
|
||||
|
||||
hidePlayOverlay() {
|
||||
this.playOverlay.style.display = 'none';
|
||||
}
|
||||
|
||||
showControls() {
|
||||
this.controls.container.classList.add('visible');
|
||||
}
|
||||
|
||||
hideControls() {
|
||||
if (!this.videoElement.paused) {
|
||||
this.controls.container.classList.remove('visible');
|
||||
}
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
this.hideLoading();
|
||||
this.videoTitle.textContent = message;
|
||||
this.videoInfo.textContent = '';
|
||||
}
|
||||
|
||||
// Utility methods
|
||||
playFirstAvailable() {
|
||||
if (this.playlist.length > 0) {
|
||||
this.playVideoFromPlaylist(0);
|
||||
} else if (this.videoLibrary && this.videoLibrary.getFilteredVideos().length > 0) {
|
||||
this.playVideo(this.videoLibrary.getFilteredVideos()[0]);
|
||||
}
|
||||
}
|
||||
|
||||
startSeeking() {
|
||||
// Add mouse move listener for seeking
|
||||
// This would be implemented for smooth seeking while dragging
|
||||
}
|
||||
|
||||
savePlaylist() {
|
||||
if (this.playlist.length === 0) {
|
||||
alert('Playlist is empty');
|
||||
return;
|
||||
}
|
||||
|
||||
const playlistData = {
|
||||
name: prompt('Enter playlist name:') || 'Untitled Playlist',
|
||||
videos: this.playlist,
|
||||
created: new Date().toISOString()
|
||||
};
|
||||
|
||||
// Save to localStorage (in a real app, you might save to a file)
|
||||
const savedPlaylists = JSON.parse(localStorage.getItem('pornCinemaPlaylists') || '[]');
|
||||
savedPlaylists.push(playlistData);
|
||||
localStorage.setItem('pornCinemaPlaylists', JSON.stringify(savedPlaylists));
|
||||
|
||||
console.log(`💾 Playlist saved: ${playlistData.name}`);
|
||||
}
|
||||
|
||||
loadPlaylist() {
|
||||
const savedPlaylists = JSON.parse(localStorage.getItem('pornCinemaPlaylists') || '[]');
|
||||
|
||||
if (savedPlaylists.length === 0) {
|
||||
alert('No saved playlists found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a simple selection dialog
|
||||
const playlistNames = savedPlaylists.map((p, i) => `${i + 1}. ${p.name}`).join('\n');
|
||||
const selection = prompt(`Select playlist to load:\n${playlistNames}\n\nEnter number:`);
|
||||
|
||||
if (selection) {
|
||||
const index = parseInt(selection) - 1;
|
||||
if (index >= 0 && index < savedPlaylists.length) {
|
||||
this.playlist = savedPlaylists[index].videos;
|
||||
this.currentPlaylistIndex = -1;
|
||||
this.updatePlaylistDisplay();
|
||||
console.log(`📁 Playlist loaded: ${savedPlaylists[index].name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
formatDuration(seconds) {
|
||||
if (!seconds || isNaN(seconds)) return '0:00';
|
||||
|
||||
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')}`;
|
||||
}
|
||||
}
|
||||
|
||||
formatFileSize(bytes) {
|
||||
if (!bytes || bytes === 0) return '0 B';
|
||||
|
||||
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]}`;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,402 @@
|
|||
/**
|
||||
* Video Library Manager for Porn Cinema
|
||||
* Handles video library display, search, and organization
|
||||
*/
|
||||
|
||||
class VideoLibrary {
|
||||
constructor(pornCinema) {
|
||||
this.pornCinema = pornCinema;
|
||||
this.videos = [];
|
||||
this.filteredVideos = [];
|
||||
this.currentView = 'grid'; // 'grid' or 'list'
|
||||
this.currentSort = 'name';
|
||||
this.sortDirection = 'asc';
|
||||
this.searchQuery = '';
|
||||
this.selectedVideo = null;
|
||||
|
||||
this.initializeElements();
|
||||
this.attachEventListeners();
|
||||
}
|
||||
|
||||
initializeElements() {
|
||||
// View toggle buttons
|
||||
this.gridViewBtn = document.getElementById('grid-view-btn');
|
||||
this.listViewBtn = document.getElementById('list-view-btn');
|
||||
|
||||
// Sort controls
|
||||
this.sortSelect = document.getElementById('sort-select');
|
||||
this.sortDirectionBtn = document.getElementById('sort-direction');
|
||||
|
||||
// Search controls
|
||||
this.searchInput = document.getElementById('library-search');
|
||||
this.refreshBtn = document.getElementById('refresh-library');
|
||||
|
||||
// Content container
|
||||
this.libraryContent = document.getElementById('library-content');
|
||||
}
|
||||
|
||||
attachEventListeners() {
|
||||
// View toggle
|
||||
this.gridViewBtn.addEventListener('click', () => this.setView('grid'));
|
||||
this.listViewBtn.addEventListener('click', () => this.setView('list'));
|
||||
|
||||
// Sort controls
|
||||
this.sortSelect.addEventListener('change', (e) => this.setSortBy(e.target.value));
|
||||
this.sortDirectionBtn.addEventListener('click', () => this.toggleSortDirection());
|
||||
|
||||
// Search
|
||||
this.searchInput.addEventListener('input', (e) => this.setSearchQuery(e.target.value));
|
||||
this.refreshBtn.addEventListener('click', () => this.refreshLibrary());
|
||||
}
|
||||
|
||||
async loadVideoLibrary() {
|
||||
try {
|
||||
console.log('📁 Loading video library...');
|
||||
|
||||
// Check if desktop file manager is available
|
||||
if (!window.desktopFileManager) {
|
||||
console.error('Desktop file manager not available');
|
||||
this.displayEmptyLibrary('Desktop file manager not available');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get video files from the desktop file manager
|
||||
const videoData = await window.desktopFileManager.getVideoList();
|
||||
|
||||
if (!videoData || !videoData.videos) {
|
||||
console.log('No videos found');
|
||||
this.displayEmptyLibrary('No videos found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Process video data
|
||||
this.videos = videoData.videos.map(video => ({
|
||||
name: video.name,
|
||||
path: video.path,
|
||||
size: video.size || 0,
|
||||
duration: video.duration || 0,
|
||||
thumbnail: video.thumbnail || null,
|
||||
resolution: video.resolution || 'Unknown',
|
||||
format: video.format || 'mp4',
|
||||
bitrate: video.bitrate || 0,
|
||||
dateAdded: video.dateAdded || new Date().toISOString(),
|
||||
qualities: this.detectVideoQualities(video)
|
||||
}));
|
||||
|
||||
console.log(`📁 Loaded ${this.videos.length} videos`);
|
||||
|
||||
// Apply current filters and display
|
||||
this.applyFiltersAndSort();
|
||||
this.displayLibrary();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading video library:', error);
|
||||
this.displayEmptyLibrary('Error loading video library');
|
||||
}
|
||||
}
|
||||
|
||||
detectVideoQualities(video) {
|
||||
// Detect available qualities for a video
|
||||
// This is a placeholder - in a real implementation, you might
|
||||
// check for multiple files with quality indicators in the name
|
||||
const qualities = ['auto'];
|
||||
|
||||
if (video.resolution) {
|
||||
const resolution = video.resolution.toLowerCase();
|
||||
if (resolution.includes('1080') || resolution.includes('1920')) {
|
||||
qualities.push('1080p');
|
||||
}
|
||||
if (resolution.includes('720') || resolution.includes('1280')) {
|
||||
qualities.push('720p');
|
||||
}
|
||||
if (resolution.includes('480') || resolution.includes('854')) {
|
||||
qualities.push('480p');
|
||||
}
|
||||
if (resolution.includes('360') || resolution.includes('640')) {
|
||||
qualities.push('360p');
|
||||
}
|
||||
} else {
|
||||
// Default to common qualities if resolution unknown
|
||||
qualities.push('1080p', '720p', '480p', '360p');
|
||||
}
|
||||
|
||||
return qualities;
|
||||
}
|
||||
|
||||
setView(view) {
|
||||
this.currentView = view;
|
||||
|
||||
// Update button states
|
||||
this.gridViewBtn.classList.toggle('active', view === 'grid');
|
||||
this.listViewBtn.classList.toggle('active', view === 'list');
|
||||
|
||||
// Re-display library
|
||||
this.displayLibrary();
|
||||
}
|
||||
|
||||
setSortBy(sortBy) {
|
||||
this.currentSort = sortBy;
|
||||
this.applyFiltersAndSort();
|
||||
this.displayLibrary();
|
||||
}
|
||||
|
||||
toggleSortDirection() {
|
||||
this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
|
||||
this.sortDirectionBtn.textContent = this.sortDirection === 'asc' ? '↑' : '↓';
|
||||
this.applyFiltersAndSort();
|
||||
this.displayLibrary();
|
||||
}
|
||||
|
||||
setSearchQuery(query) {
|
||||
this.searchQuery = query.toLowerCase();
|
||||
this.applyFiltersAndSort();
|
||||
this.displayLibrary();
|
||||
}
|
||||
|
||||
applyFiltersAndSort() {
|
||||
// Apply search filter
|
||||
this.filteredVideos = this.videos.filter(video =>
|
||||
video.name.toLowerCase().includes(this.searchQuery)
|
||||
);
|
||||
|
||||
// Apply sorting
|
||||
this.filteredVideos.sort((a, b) => {
|
||||
let aValue, bValue;
|
||||
|
||||
switch (this.currentSort) {
|
||||
case 'name':
|
||||
aValue = a.name.toLowerCase();
|
||||
bValue = b.name.toLowerCase();
|
||||
break;
|
||||
case 'date':
|
||||
aValue = new Date(a.dateAdded);
|
||||
bValue = new Date(b.dateAdded);
|
||||
break;
|
||||
case 'duration':
|
||||
aValue = a.duration;
|
||||
bValue = b.duration;
|
||||
break;
|
||||
case 'size':
|
||||
aValue = a.size;
|
||||
bValue = b.size;
|
||||
break;
|
||||
default:
|
||||
aValue = a.name.toLowerCase();
|
||||
bValue = b.name.toLowerCase();
|
||||
}
|
||||
|
||||
if (aValue < bValue) return this.sortDirection === 'asc' ? -1 : 1;
|
||||
if (aValue > bValue) return this.sortDirection === 'asc' ? 1 : -1;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
displayLibrary() {
|
||||
if (this.filteredVideos.length === 0) {
|
||||
this.displayEmptyLibrary('No videos match your search');
|
||||
return;
|
||||
}
|
||||
|
||||
const containerClass = this.currentView === 'grid' ? 'library-grid' : 'library-list';
|
||||
|
||||
this.libraryContent.innerHTML = `
|
||||
<div class="${containerClass}">
|
||||
${this.filteredVideos.map(video => this.createVideoElement(video)).join('')}
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Attach click events to video elements
|
||||
this.attachVideoEvents();
|
||||
}
|
||||
|
||||
createVideoElement(video) {
|
||||
const duration = this.formatDuration(video.duration);
|
||||
const fileSize = this.formatFileSize(video.size);
|
||||
|
||||
if (this.currentView === 'grid') {
|
||||
return `
|
||||
<div class="video-card" data-video-path="${video.path}">
|
||||
<div class="video-thumbnail">
|
||||
${video.thumbnail ?
|
||||
`<img src="${video.thumbnail}" alt="${video.name}">` :
|
||||
'🎬'
|
||||
}
|
||||
<div class="video-duration">${duration}</div>
|
||||
</div>
|
||||
<div class="video-details">
|
||||
<div class="video-name" title="${video.name}">${video.name}</div>
|
||||
<div class="video-meta">
|
||||
<span>${video.resolution}</span>
|
||||
<span>${fileSize}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="video-actions">
|
||||
<button class="btn btn-mini play-video" title="Play">▶</button>
|
||||
<button class="btn btn-mini add-to-playlist" title="Add to Playlist">➕</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
return `
|
||||
<div class="video-list-item" data-video-path="${video.path}">
|
||||
<div class="list-thumbnail">
|
||||
${video.thumbnail ?
|
||||
`<img src="${video.thumbnail}" alt="${video.name}" class="list-thumbnail">` :
|
||||
`<div class="list-thumbnail" style="display: flex; align-items: center; justify-content: center; font-size: 1.5rem;">🎬</div>`
|
||||
}
|
||||
</div>
|
||||
<div class="list-details">
|
||||
<div class="list-name">${video.name}</div>
|
||||
<div class="list-meta">${video.resolution}</div>
|
||||
<div class="list-meta">${duration}</div>
|
||||
<div class="list-meta">${fileSize}</div>
|
||||
</div>
|
||||
<div class="video-actions">
|
||||
<button class="btn btn-mini play-video" title="Play">▶</button>
|
||||
<button class="btn btn-mini add-to-playlist" title="Add to Playlist">➕</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
attachVideoEvents() {
|
||||
// Video card/item click events
|
||||
const videoElements = this.libraryContent.querySelectorAll('.video-card, .video-list-item');
|
||||
videoElements.forEach(element => {
|
||||
element.addEventListener('click', (e) => {
|
||||
if (e.target.closest('.video-actions')) return; // Don't trigger on action buttons
|
||||
|
||||
const videoPath = element.dataset.videoPath;
|
||||
this.selectVideo(videoPath);
|
||||
});
|
||||
});
|
||||
|
||||
// Play button events
|
||||
const playButtons = this.libraryContent.querySelectorAll('.play-video');
|
||||
playButtons.forEach(button => {
|
||||
button.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const videoPath = button.closest('[data-video-path]').dataset.videoPath;
|
||||
this.playVideo(videoPath);
|
||||
});
|
||||
});
|
||||
|
||||
// Add to playlist button events
|
||||
const playlistButtons = this.libraryContent.querySelectorAll('.add-to-playlist');
|
||||
playlistButtons.forEach(button => {
|
||||
button.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const videoPath = button.closest('[data-video-path]').dataset.videoPath;
|
||||
this.addToPlaylist(videoPath);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
selectVideo(videoPath) {
|
||||
// Remove previous selection
|
||||
const previousSelected = this.libraryContent.querySelector('.selected');
|
||||
if (previousSelected) {
|
||||
previousSelected.classList.remove('selected');
|
||||
}
|
||||
|
||||
// Add selection to clicked video
|
||||
const videoElement = this.libraryContent.querySelector(`[data-video-path="${videoPath}"]`);
|
||||
if (videoElement) {
|
||||
videoElement.classList.add('selected');
|
||||
}
|
||||
|
||||
// Update selected video
|
||||
this.selectedVideo = this.videos.find(video => video.path === videoPath);
|
||||
|
||||
// Notify porn cinema
|
||||
if (this.pornCinema) {
|
||||
this.pornCinema.selectVideo(this.selectedVideo);
|
||||
}
|
||||
}
|
||||
|
||||
playVideo(videoPath) {
|
||||
const video = this.videos.find(video => video.path === videoPath);
|
||||
if (video && this.pornCinema) {
|
||||
this.pornCinema.playVideo(video);
|
||||
}
|
||||
}
|
||||
|
||||
addToPlaylist(videoPath) {
|
||||
const video = this.videos.find(video => video.path === videoPath);
|
||||
if (video && this.pornCinema) {
|
||||
this.pornCinema.addToPlaylist(video);
|
||||
}
|
||||
}
|
||||
|
||||
refreshLibrary() {
|
||||
console.log('🔄 Refreshing video library...');
|
||||
this.videos = [];
|
||||
this.filteredVideos = [];
|
||||
this.selectedVideo = null;
|
||||
|
||||
// Show loading state
|
||||
this.libraryContent.innerHTML = `
|
||||
<div class="library-loading">
|
||||
<div class="loading-spinner"></div>
|
||||
<p>Refreshing video library...</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Reload library
|
||||
setTimeout(() => {
|
||||
this.loadVideoLibrary();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
displayEmptyLibrary(message) {
|
||||
this.libraryContent.innerHTML = `
|
||||
<div class="library-loading">
|
||||
<p>${message}</p>
|
||||
<button class="btn btn-secondary" onclick="window.location.href='index.html'">
|
||||
📂 Manage Videos
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// Utility functions
|
||||
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')}`;
|
||||
}
|
||||
}
|
||||
|
||||
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]}`;
|
||||
}
|
||||
|
||||
// Public methods for external access
|
||||
getSelectedVideo() {
|
||||
return this.selectedVideo;
|
||||
}
|
||||
|
||||
getVideoByPath(path) {
|
||||
return this.videos.find(video => video.path === path);
|
||||
}
|
||||
|
||||
getAllVideos() {
|
||||
return this.videos;
|
||||
}
|
||||
|
||||
getFilteredVideos() {
|
||||
return this.filteredVideos;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,794 @@
|
|||
/* Porn Cinema Styles */
|
||||
|
||||
/* ===== CINEMA LAYOUT ===== */
|
||||
.cinema-mode {
|
||||
background: #000;
|
||||
color: #fff;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.cinema-loading {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: rgba(0, 0, 0, 0.95);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
.cinema-header {
|
||||
background: linear-gradient(135deg, #1a1a1a, #2d2d2d);
|
||||
border-bottom: 2px solid #333;
|
||||
padding: 15px 20px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.cinema-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.cinema-nav h1 {
|
||||
margin: 0;
|
||||
font-size: 2rem;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.7);
|
||||
background: linear-gradient(45deg, #ff6b9d, #c471ed, #12c2e9);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.cinema-controls {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* ===== KEYBOARD SHORTCUTS HELP ===== */
|
||||
.shortcuts-help {
|
||||
position: fixed;
|
||||
top: 80px;
|
||||
right: 20px;
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
border: 2px solid #444;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
max-width: 350px;
|
||||
z-index: 2000;
|
||||
display: none;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.shortcuts-help h3 {
|
||||
margin: 0 0 15px 0;
|
||||
color: #ff6b9d;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.shortcuts-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.shortcut-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.shortcut-item kbd {
|
||||
background: #333;
|
||||
border: 1px solid #555;
|
||||
border-radius: 4px;
|
||||
padding: 2px 6px;
|
||||
font-size: 0.8rem;
|
||||
min-width: 24px;
|
||||
text-align: center;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
/* ===== MAIN CINEMA LAYOUT ===== */
|
||||
.cinema-main {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
display: grid;
|
||||
grid-template-rows: auto auto 1fr;
|
||||
gap: 20px;
|
||||
min-height: calc(100vh - 120px);
|
||||
}
|
||||
|
||||
/* ===== VIDEO PLAYER SECTION ===== */
|
||||
.video-player-section {
|
||||
background: #111;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.video-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 60vh;
|
||||
min-height: 400px;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.main-video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.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;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.video-overlay.hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.video-title {
|
||||
font-size: 1.8rem;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
|
||||
margin-bottom: 10px;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.video-info {
|
||||
font-size: 1rem;
|
||||
color: #ccc;
|
||||
text-align: center;
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.play-overlay {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.play-button-large {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border: none;
|
||||
font-size: 2rem;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.play-button-large:hover {
|
||||
background: rgba(255, 255, 255, 1);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.video-loading {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* ===== VIDEO CONTROLS ===== */
|
||||
.video-controls {
|
||||
background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.8));
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 20px;
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.video-container:hover .video-controls,
|
||||
.video-controls:hover,
|
||||
.video-controls.visible {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
position: relative;
|
||||
height: 6px;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.progress-filled {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #ff6b9d, #c471ed);
|
||||
border-radius: 3px;
|
||||
width: 0%;
|
||||
transition: width 0.1s ease;
|
||||
}
|
||||
|
||||
.progress-thumb {
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background: #fff;
|
||||
border-radius: 50%;
|
||||
transform: translateX(-50%);
|
||||
left: 0%;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.progress-bar:hover .progress-thumb {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.time-display {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 0.9rem;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.controls-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.controls-left,
|
||||
.controls-center,
|
||||
.controls-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
color: #fff;
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.control-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.volume-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.volume-slider {
|
||||
width: 80px;
|
||||
height: 4px;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 2px;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.volume-slider::-webkit-slider-thumb {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background: #fff;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.volume-percentage {
|
||||
color: #ccc;
|
||||
font-size: 0.9rem;
|
||||
min-width: 35px;
|
||||
}
|
||||
|
||||
.quality-dropdown,
|
||||
.speed-dropdown {
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
color: #fff;
|
||||
padding: 5px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* ===== PLAYLIST SECTION ===== */
|
||||
.playlist-section {
|
||||
background: #1a1a1a;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.playlist-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 15px;
|
||||
border-bottom: 2px solid #333;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.playlist-header h3 {
|
||||
margin: 0;
|
||||
color: #ff6b9d;
|
||||
}
|
||||
|
||||
.playlist-controls {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.playlist-content {
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.playlist-empty {
|
||||
text-align: center;
|
||||
color: #666;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.playlist-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.playlist-item:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.playlist-item.current {
|
||||
background: rgba(255, 107, 157, 0.2);
|
||||
border: 1px solid #ff6b9d;
|
||||
}
|
||||
|
||||
.playlist-thumbnail {
|
||||
width: 60px;
|
||||
height: 40px;
|
||||
background: #333;
|
||||
border-radius: 4px;
|
||||
margin-right: 15px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.playlist-details {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.playlist-title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.playlist-duration {
|
||||
color: #999;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.playlist-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* ===== VIDEO LIBRARY SECTION ===== */
|
||||
.video-library-section {
|
||||
background: #1a1a1a;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.library-header {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 15px;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 2px solid #333;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.library-header h3 {
|
||||
margin: 0;
|
||||
color: #ff6b9d;
|
||||
}
|
||||
|
||||
.view-toggle {
|
||||
display: flex;
|
||||
border: 1px solid #444;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.view-btn {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: none;
|
||||
color: #ccc;
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.view-btn.active,
|
||||
.view-btn:hover {
|
||||
background: #ff6b9d;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.sort-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.sort-dropdown {
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
border: 1px solid #444;
|
||||
color: #fff;
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.search-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
border: 1px solid #444;
|
||||
color: #fff;
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.search-input::placeholder {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.library-content {
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.library-loading {
|
||||
text-align: center;
|
||||
padding: 50px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* ===== LIBRARY GRID VIEW ===== */
|
||||
.library-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.video-card {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.video-card:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.video-card.selected {
|
||||
border-color: #ff6b9d;
|
||||
background: rgba(255, 107, 157, 0.1);
|
||||
}
|
||||
|
||||
.video-thumbnail {
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
background: #333;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #666;
|
||||
font-size: 2rem;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.video-thumbnail img,
|
||||
.video-thumbnail video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.video-duration {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
right: 5px;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: #fff;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.video-details {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.video-name {
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.video-meta {
|
||||
color: #999;
|
||||
font-size: 0.85rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.video-actions {
|
||||
padding: 0 12px 12px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.video-card:hover .video-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* ===== LIBRARY LIST VIEW ===== */
|
||||
.library-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.video-list-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.video-list-item:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.video-list-item.selected {
|
||||
border-color: #ff6b9d;
|
||||
background: rgba(255, 107, 157, 0.1);
|
||||
}
|
||||
|
||||
.list-thumbnail {
|
||||
width: 80px;
|
||||
height: 50px;
|
||||
background: #333;
|
||||
border-radius: 6px;
|
||||
margin-right: 15px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.list-details {
|
||||
flex: 1;
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr 1fr 1fr;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.list-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.list-meta {
|
||||
color: #999;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* ===== THEATER MODE ===== */
|
||||
.theater-mode .cinema-header {
|
||||
opacity: 0.3;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.theater-mode .cinema-header:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.theater-mode .playlist-section,
|
||||
.theater-mode .video-library-section {
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.theater-mode .playlist-section:hover,
|
||||
.theater-mode .video-library-section:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* ===== FULLSCREEN STYLES ===== */
|
||||
.video-container:-webkit-full-screen {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.video-container:-moz-full-screen {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.video-container:fullscreen {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
/* ===== RESPONSIVE DESIGN ===== */
|
||||
@media (max-width: 1024px) {
|
||||
.cinema-main {
|
||||
padding: 15px;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.video-container {
|
||||
height: 50vh;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.library-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.shortcuts-help {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
max-width: 90vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.cinema-nav {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.controls-row {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.library-header {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.list-details {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.shortcuts-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== UTILITY CLASSES ===== */
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid rgba(255, 255, 255, 0.1);
|
||||
border-left: 4px solid #ff6b9d;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* ===== SCROLLBAR STYLING ===== */
|
||||
.playlist-section::-webkit-scrollbar,
|
||||
.library-content::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.playlist-section::-webkit-scrollbar-track,
|
||||
.library-content::-webkit-scrollbar-track {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.playlist-section::-webkit-scrollbar-thumb,
|
||||
.library-content::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 107, 157, 0.7);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.playlist-section::-webkit-scrollbar-thumb:hover,
|
||||
.library-content::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(255, 107, 157, 1);
|
||||
}
|
||||
Loading…
Reference in New Issue