Compare commits

...

3 Commits

Author SHA1 Message Date
Your Name
faf0634924 屏蔽右键 2025-08-18 12:33:18 +08:00
Your Name
6d4c44427c dev可以正常播放音频 2025-08-18 12:20:56 +08:00
Your Name
5795061602 dev可以正常播放音频 2025-08-18 11:33:42 +08:00
2 changed files with 6947 additions and 408 deletions

View File

@@ -3,7 +3,7 @@
<!-- Connection Status --> <!-- Connection Status -->
<div class="absolute top-4 right-4 z-10"> <div class="absolute top-4 right-4 z-10">
<div class="flex items-center gap-2 px-3 py-1 rounded-full text-sm" :class="connectionStatusClass"> <div class="flex items-center gap-2 px-3 py-1 rounded-full text-sm" :class="connectionStatusClass">
<div class="w-2 h-2 rounded-full" :class="connectionDotClass"></div> <div class="w-2 h-2 rounded-full" :class="connectionDotClass" />
{{ connectionStatus }} {{ connectionStatus }}
</div> </div>
</div> </div>
@@ -14,7 +14,7 @@
<video <video
v-if="currentVideo" v-if="currentVideo"
ref="videoElement" ref="videoElement"
class="w-full h-full object-contain" class="w-full h-full object-contain bg-black"
:src="videoSrc" :src="videoSrc"
:volume="volume / 100" :volume="volume / 100"
:loop="isLooping" :loop="isLooping"
@@ -47,20 +47,22 @@
<!-- Loading Animation --> <!-- Loading Animation -->
<div v-if="isConnecting" class="flex items-center space-x-1"> <div v-if="isConnecting" class="flex items-center space-x-1">
<div class="w-2 h-2 bg-gray-500 rounded-full animate-pulse"></div> <div class="w-2 h-2 bg-gray-500 rounded-full animate-pulse" />
<div class="w-2 h-2 bg-gray-500 rounded-full animate-pulse" style="animation-delay: 0.2s"></div> <div class="w-2 h-2 bg-gray-500 rounded-full animate-pulse" style="animation-delay: 0.2s" />
<div class="w-2 h-2 bg-gray-500 rounded-full animate-pulse" style="animation-delay: 0.4s"></div> <div class="w-2 h-2 bg-gray-500 rounded-full animate-pulse" style="animation-delay: 0.4s" />
</div> </div>
</div> </div>
<!-- Overlay Controls (hidden by default, shown on hover if enabled) --> <!-- Overlay Controls (hidden by default, shown on hover if enabled) -->
<div v-if="showControls && currentVideo" <div
class="absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity duration-300"> v-if="showControls && currentVideo"
class="absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity duration-300"
>
<div class="flex items-center space-x-4"> <div class="flex items-center space-x-4">
<button @click="togglePlay" class="p-3 rounded-full bg-white bg-opacity-20 hover:bg-opacity-30 transition-all"> <button class="p-3 rounded-full bg-white bg-opacity-20 hover:bg-opacity-30 transition-all" @click="togglePlay">
<UIcon :name="isPlaying ? 'lucide:pause' : 'lucide:play'" class="w-8 h-8" /> <UIcon :name="isPlaying ? 'lucide:pause' : 'lucide:play'" class="w-8 h-8" />
</button> </button>
<button @click="stop" class="p-3 rounded-full bg-white bg-opacity-20 hover:bg-opacity-30 transition-all"> <button class="p-3 rounded-full bg-white bg-opacity-20 hover:bg-opacity-30 transition-all" @click="stop">
<UIcon name="lucide:square" class="w-8 h-8" /> <UIcon name="lucide:square" class="w-8 h-8" />
</button> </button>
</div> </div>
@@ -70,456 +72,487 @@
<!-- Debug Info (only in dev mode) --> <!-- Debug Info (only in dev mode) -->
<div v-if="isDev" class="absolute bottom-4 left-4 text-xs text-gray-500 space-y-1"> <div v-if="isDev" class="absolute bottom-4 left-4 text-xs text-gray-500 space-y-1">
<div>Volume: {{ volume }}%</div> <div>Volume: {{ volume }}%</div>
<div v-if="currentVideo">Position: {{ Math.floor(position) }}s / {{ Math.floor(duration) }}s</div> <div v-if="currentVideo">
Position: {{ Math.floor(position) }}s / {{ Math.floor(duration) }}s
</div>
<div>Loop: {{ isLooping ? 'On' : 'Off' }}</div> <div>Loop: {{ isLooping ? 'On' : 'Off' }}</div>
<div>Fullscreen: {{ isFullscreen ? 'On' : 'Off' }}</div> <div>Fullscreen: {{ isFullscreen ? 'On' : 'Off' }}</div>
<div v-if="videoElement">
Video: {{ videoElement.videoWidth }}x{{ videoElement.videoHeight }}
</div>
<div v-if="currentVideo">
Src: {{ videoSrc }}
</div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
// remove direct import that breaks vite in dev; we will fallback to file:// URLs // remove direct import that breaks vite in dev; we will fallback to file:// URLs
const appConfig = useAppConfig() const appConfig = useAppConfig();
const isDev = process.env.NODE_ENV === 'development' const isDev = process.env.NODE_ENV === "development";
// Tauri functions will be loaded dynamically // Tauri functions will be loaded dynamically
let invoke = null let invoke = null;
let listen = null let listen = null;
// Player state // Player state
const connectionStatus = ref('Disconnected') const connectionStatus = ref("Disconnected");
const isConnecting = ref(false) const isConnecting = ref(false);
const currentVideo = ref(null) const currentVideo = ref(null);
const isPlaying = ref(false) const isPlaying = ref(false);
const position = ref(0) const position = ref(0);
const duration = ref(0) const duration = ref(0);
const volume = ref(appConfig.player.defaultVolume) const volume = ref(appConfig.player.defaultVolume);
const isLooping = ref(false) const isLooping = ref(false);
const isFullscreen = ref(false) const isFullscreen = ref(false);
const showControls = ref(false) const showControls = ref(false);
const isMuted = ref(true) const isMuted = ref(true);
// Video element ref // Video element ref
const videoElement = ref(null) const videoElement = ref(null);
// Video source URL - reactive ref that gets updated when currentVideo changes // Video source URL - reactive ref that gets updated when currentVideo changes
const videoSrc = ref('') const videoSrc = ref("");
// Convert file path to Tauri-compatible URL // Convert file path to Tauri-compatible URL
async function updateVideoSrc() { async function updateVideoSrc() {
const raw = currentVideo.value?.path || '' const raw = currentVideo.value?.path || "";
if (!raw) { if (!raw) {
console.log('🎥 No video path available') console.log("🎥 No video path available");
videoSrc.value = '' videoSrc.value = "";
return return;
} }
// Handle Windows paths with backslashes and drive letters // Check if we're running in Tauri
const isWindowsPath = /^[a-zA-Z]:[\\/]|^\\/.test(raw) const isTauri = typeof window !== "undefined" && window.__TAURI_INTERNALS__;
if (isWindowsPath) { if (isTauri) {
// Windows: Use file:// protocol for local file access try {
// Convert backslashes to forward slashes and use file:// protocol // Use Tauri's convertFileSrc to get proper URL for local files
let normalizedPath = raw.replace(/\\/g, '/') const { convertFileSrc } = await import("@tauri-apps/api/core");
videoSrc.value = convertFileSrc(raw);
// Ensure proper file:// format with drive letter console.log("🎥 Using Tauri convertFileSrc for video:", videoSrc.value);
if (!normalizedPath.startsWith('/')) { } catch (error) {
normalizedPath = '/' + normalizedPath console.error("❌ Failed to use Tauri convertFileSrc:", error);
} // Fallback to HTTP URL for bundled resources
if (raw.includes("video.mp4") || raw.endsWith("video.mp4")) {
// Remove duplicate slashes videoSrc.value = "/video.mp4";
normalizedPath = normalizedPath.replace(/\/+/g, '/')
videoSrc.value = `file://${normalizedPath}`
} else { } else {
// Unix-like: Use file:// protocol for absolute paths
if (raw.startsWith('/')) {
videoSrc.value = `file://${raw}`
} else {
// Use relative HTTP URL for bundled resources
const filename = raw.split(/[/\\]/).pop() || "video.mp4"; const filename = raw.split(/[/\\]/).pop() || "video.mp4";
if (filename === 'video.mp4') { videoSrc.value = `/${filename}`;
videoSrc.value = '/video.mp4'
} else {
videoSrc.value = `/${filename}`
} }
} }
} else {
// In web browser, use HTTP URLs
const filename = raw.split(/[/\\]/).pop() || "video.mp4";
if (filename === "video.mp4") {
videoSrc.value = "/video.mp4";
} else {
videoSrc.value = `/${filename}`;
}
} }
console.log('🎥 Using URL for video:') console.log("🎥 Using URL for video:");
console.log(' - Original path:', raw) console.log(" - Original path:", raw);
console.log(' - Platform:', isWindowsPath ? 'Windows' : 'Unix') console.log(" - Platform:", isTauri ? "Tauri" : "Web");
console.log(' - Final URL:', videoSrc.value) console.log(" - Final URL:", videoSrc.value);
} }
// Watch for changes in currentVideo and update videoSrc // Watch for changes in currentVideo and update videoSrc
watch(currentVideo, updateVideoSrc, { immediate: true }) watch(currentVideo, updateVideoSrc, { immediate: true });
// Watch for changes in videoSrc to debug // Watch for changes in videoSrc to debug
watch(videoSrc, (newSrc, oldSrc) => { watch(videoSrc, (newSrc, oldSrc) => {
console.log('🎥 videoSrc changed:') console.log("🎥 videoSrc changed:");
console.log(' - Old:', oldSrc) console.log(" - Old:", oldSrc);
console.log(' - New:', newSrc) console.log(" - New:", newSrc);
}) });
// Computed properties // Computed properties
const connectionStatusClass = computed(() => { const connectionStatusClass = computed(() => {
switch (connectionStatus.value) { switch (connectionStatus.value) {
case 'Connected': case "Connected":
return 'bg-green-900 bg-opacity-50 text-green-300' return "bg-green-900 bg-opacity-50 text-green-300";
case 'Connecting': case "Connecting":
return 'bg-yellow-900 bg-opacity-50 text-yellow-300' return "bg-yellow-900 bg-opacity-50 text-yellow-300";
default: default:
return 'bg-red-900 bg-opacity-50 text-red-300' return "bg-red-900 bg-opacity-50 text-red-300";
} }
}) });
const connectionDotClass = computed(() => { const connectionDotClass = computed(() => {
switch (connectionStatus.value) { switch (connectionStatus.value) {
case 'Connected': case "Connected":
return 'bg-green-400' return "bg-green-400";
case 'Connecting': case "Connecting":
return 'bg-yellow-400 animate-pulse' return "bg-yellow-400 animate-pulse";
default: default:
return 'bg-red-400' return "bg-red-400";
} }
}) });
const statusMessage = computed(() => { const statusMessage = computed(() => {
if (isConnecting.value) { if (isConnecting.value) {
return 'Connecting to controller...' return "Connecting to controller...";
} }
if (connectionStatus.value === 'Connected') { if (connectionStatus.value === "Connected") {
return 'Ready to play. Waiting for controller commands...' return "Ready to play. Waiting for controller commands...";
} }
return 'Waiting for controller connection...' return "Waiting for controller connection...";
}) });
// Video control functions // Video control functions
function togglePlay() { function togglePlay() {
if (!videoElement.value) return if (!videoElement.value) return;
if (isPlaying.value) { if (isPlaying.value) {
videoElement.value.pause() videoElement.value.pause();
} else { } else {
videoElement.value.play() videoElement.value.play();
} }
isPlaying.value = !isPlaying.value isPlaying.value = !isPlaying.value;
} }
function stop() { function stop() {
if (!videoElement.value) return if (!videoElement.value) return;
videoElement.value.pause() videoElement.value.pause();
videoElement.value.currentTime = 0 videoElement.value.currentTime = 0;
isPlaying.value = false isPlaying.value = false;
position.value = 0 position.value = 0;
} }
function seek(time) { function seek(time) {
if (!videoElement.value) return if (!videoElement.value) return;
videoElement.value.currentTime = time videoElement.value.currentTime = time;
position.value = time position.value = time;
} }
function setVolume(newVolume) { function setVolume(newVolume) {
volume.value = Math.max(0, Math.min(100, newVolume)) volume.value = Math.max(0, Math.min(100, newVolume));
if (videoElement.value) { if (videoElement.value) {
videoElement.value.volume = volume.value / 100 videoElement.value.volume = volume.value / 100;
} }
} }
// Video event handlers // Video event handlers
function onVideoLoaded() { function onVideoLoaded() {
console.log('🎬 Video loaded event triggered') console.log("🎬 Video loaded event triggered");
if (videoElement.value) { if (videoElement.value) {
duration.value = videoElement.value.duration duration.value = videoElement.value.duration;
videoElement.value.volume = volume.value / 100 videoElement.value.volume = volume.value / 100;
console.log('🎬 Video metadata:', { console.log("🎬 Video metadata:", {
duration: duration.value, duration: duration.value,
volume: volume.value, volume: volume.value,
currentSrc: videoElement.value.currentSrc, currentSrc: videoElement.value.currentSrc,
readyState: videoElement.value.readyState readyState: videoElement.value.readyState
}) });
// 尝试主动播放;若被策略阻止则静音后重试 // 尝试主动播放;若被策略阻止则静音后重试
console.log('🎬 Attempting to play video...') console.log("🎬 Attempting to play video...");
// Always start muted to comply with autoplay policies, then unmute // Always start muted to comply with autoplay policies, then unmute
isMuted.value = true isMuted.value = true;
// Small delay to ensure video is ready // Small delay to ensure video is ready
setTimeout(() => { setTimeout(() => {
if (videoElement.value) { if (videoElement.value) {
const playPromise = videoElement.value.play() const playPromise = videoElement.value.play();
if (playPromise && typeof playPromise.then === 'function') { if (playPromise && typeof playPromise.then === "function") {
playPromise.then(() => { playPromise.then(() => {
console.log('✅ Video started playing successfully (muted)') console.log("✅ Video started playing successfully (muted)");
isPlaying.value = true isPlaying.value = true;
// Try to unmute after successful playback // Try to unmute after successful playback
setTimeout(() => { setTimeout(() => {
if (videoElement.value) { if (videoElement.value) {
videoElement.value.muted = false videoElement.value.muted = false;
isMuted.value = false isMuted.value = false;
console.log('🔊 Video unmuted') console.log("🔊 Video unmuted");
} }
}, 1000) }, 1000);
}).catch((err) => { }).catch((err) => {
console.error('❌ Autoplay failed:', err) console.error("❌ Autoplay failed:", err);
// Show user a play button if autoplay fails // Show user a play button if autoplay fails
showControls.value = true showControls.value = true;
}) });
} else { } else {
// Fallback for browsers without promise-based play // Fallback for browsers without promise-based play
try { try {
videoElement.value.play() videoElement.value.play();
console.log('✅ Video started playing (fallback)') console.log("✅ Video started playing (fallback)");
isPlaying.value = true isPlaying.value = true;
} catch (err) { } catch (err) {
console.error('❌ Autoplay failed (fallback):', err) console.error("❌ Autoplay failed (fallback):", err);
showControls.value = true showControls.value = true;
} }
} }
} }
}, 500) }, 500);
} }
} }
function onTimeUpdate() { function onTimeUpdate() {
if (videoElement.value) { if (videoElement.value) {
position.value = videoElement.value.currentTime position.value = videoElement.value.currentTime;
} }
} }
function onVideoEnded() { function onVideoEnded() {
isPlaying.value = false isPlaying.value = false;
// Send playback finished event to controller // Send playback finished event to controller
// This will be implemented when WebSocket is ready // This will be implemented when WebSocket is ready
} }
function onVideoError(event) { function onVideoError(event) {
console.error('❌ Video playback error:', event) console.error("❌ Video playback error:", event);
console.error('❌ Video error details:', { console.error("❌ Video error details:", {
error: event.target?.error, error: event.target?.error,
networkState: event.target?.networkState, networkState: event.target?.networkState,
readyState: event.target?.readyState, readyState: event.target?.readyState,
currentSrc: event.target?.currentSrc currentSrc: event.target?.currentSrc
}) });
currentVideo.value = null currentVideo.value = null;
isPlaying.value = false isPlaying.value = false;
} }
// Initialize fullscreen if configured // Initialize fullscreen if configured
onMounted(async () => { onMounted(async () => {
console.log('🔧 Component mounted, initializing...') console.log("🔧 Component mounted, initializing...");
console.log('📁 Current directory:', window.location.href) console.log("📁 Current directory:", window.location.href);
if (appConfig.player.autoFullscreen) { if (appConfig.player.autoFullscreen) {
console.log('🔧 Auto fullscreen enabled') console.log("🔧 Auto fullscreen enabled");
// Request fullscreen // Request fullscreen
nextTick(() => { nextTick(() => {
document.documentElement.requestFullscreen?.() || document.documentElement.requestFullscreen?.()
document.documentElement.webkitRequestFullscreen?.() || document.documentElement.webkitRequestFullscreen?.();
}) });
} }
// Setup Tauri event listeners // Setup Tauri event listeners
console.log('🔧 Setting up Tauri listeners...') console.log("🔧 Setting up Tauri listeners...");
await setupTauriListeners() await setupTauriListeners();
// Get initial player state // Get initial player state
console.log('🔧 Getting initial player state...') console.log("🔧 Getting initial player state...");
await updatePlayerState() await updatePlayerState();
console.log('✅ Component initialization complete') console.log("✅ Component initialization complete");
// Additional debug: check if video.mp4 exists in public folder // Additional debug: check if video.mp4 exists in public folder
try { try {
const response = await fetch('/video.mp4', { method: 'HEAD' }) const response = await fetch("/video.mp4", { method: "HEAD" });
if (response.ok) { if (response.ok) {
console.log('📹 Found video.mp4 in public folder via HTTP') console.log("📹 Found video.mp4 in public folder via HTTP");
} else { } else {
console.log('❌ video.mp4 not accessible via HTTP') console.log("❌ video.mp4 not accessible via HTTP");
} }
} catch (e) { } catch (e) {
console.log('🌐 Cannot check video.mp4 via HTTP (expected in Tauri)') console.log("🌐 Cannot check video.mp4 via HTTP (expected in Tauri)");
} }
}) });
// Setup Tauri event listeners // Setup Tauri event listeners
async function setupTauriListeners() { async function setupTauriListeners() {
try { try {
// Load Tauri APIs dynamically // Load Tauri APIs dynamically
const { invoke: tauriInvoke } = await import('@tauri-apps/api/core') const { invoke: tauriInvoke } = await import("@tauri-apps/api/core");
const { listen: tauriListen } = await import('@tauri-apps/api/event') const { listen: tauriListen } = await import("@tauri-apps/api/event");
// Set global references // Set global references
invoke = tauriInvoke invoke = tauriInvoke;
listen = tauriListen listen = tauriListen;
// Listen for connection status changes // Listen for connection status changes
await listen('connection-status', (event) => { await listen("connection-status", (event) => {
connectionStatus.value = event.payload === 'connected' ? 'Connected' : 'Disconnected' connectionStatus.value = event.payload === "connected" ? "Connected" : "Disconnected";
isConnecting.value = false isConnecting.value = false;
}) });
// Listen for player commands from backend // Listen for player commands from backend
await listen('player-command', async (event) => { await listen("player-command", async (event) => {
console.log('📡 Backend player command received:', event.payload) console.log("📡 Backend player command received:", event.payload);
const command = event.payload const command = event.payload;
await handlePlayerCommand(command) await handlePlayerCommand(command);
}) });
console.log('✅ Tauri event listeners setup complete') console.log("✅ Tauri event listeners setup complete");
} catch (error) { } catch (error) {
console.error('❌ Failed to setup Tauri listeners:', error) console.error("❌ Failed to setup Tauri listeners:", error);
// Fallback for development mode // Fallback for development mode
setTimeout(() => { setTimeout(() => {
connectionStatus.value = 'Waiting for WebSocket server...' connectionStatus.value = "Waiting for WebSocket server...";
}, 2000) }, 2000);
} }
} }
// Update player state from backend // Update player state from backend
async function updatePlayerState() { async function updatePlayerState() {
if (!invoke) { if (!invoke) {
console.warn('Tauri invoke function not available yet') console.warn("Tauri invoke function not available yet");
return return;
} }
try { try {
const state = await invoke('get_player_state') const state = await invoke("get_player_state");
currentVideo.value = state.current_video currentVideo.value = state.current_video;
isPlaying.value = state.playback_status === 'playing' isPlaying.value = state.playback_status === "playing";
position.value = state.position position.value = state.position;
duration.value = state.duration duration.value = state.duration;
volume.value = state.volume volume.value = state.volume;
isLooping.value = state.is_looping isLooping.value = state.is_looping;
isFullscreen.value = state.is_fullscreen isFullscreen.value = state.is_fullscreen;
connectionStatus.value = state.connection_status === 'connected' ? 'Connected' : 'Disconnected' connectionStatus.value = state.connection_status === "connected" ? "Connected" : "Disconnected";
console.log('✅ Player state updated:', state) console.log("✅ Player state updated:", state);
} catch (error) { } catch (error) {
console.error('❌ Failed to get player state:', error) console.error("❌ Failed to get player state:", error);
} }
} }
// Handle player commands from WebSocket // Handle player commands from WebSocket
async function handlePlayerCommand(command) { async function handlePlayerCommand(command) {
console.log('🎮 Received player command:', command) console.log("🎮 Received player command:", command);
switch (command.type) { switch (command.type) {
case 'play': case "play":
if (videoElement.value) { if (videoElement.value) {
try { try {
// Ensure video is ready before attempting to play // Ensure video is ready before attempting to play
if (videoElement.value.readyState >= 2) { if (videoElement.value.readyState >= 2) {
await videoElement.value.play() await videoElement.value.play();
isPlaying.value = true isPlaying.value = true;
} else { } else {
// Wait for video to be ready // Wait for video to be ready
videoElement.value.addEventListener('canplay', () => { videoElement.value.addEventListener("canplay", () => {
videoElement.value.play().catch(console.error) videoElement.value.play().catch(console.error);
isPlaying.value = true isPlaying.value = true;
}, { once: true }) }, { once: true });
} }
} catch (error) { } catch (error) {
console.error('Play error:', error) console.error("Play error:", error);
} }
} }
break break;
case 'pause': case "pause":
if (videoElement.value) { if (videoElement.value) {
videoElement.value.pause() videoElement.value.pause();
isPlaying.value = false isPlaying.value = false;
} }
break break;
case 'stop': case "stop":
stop() stop();
break break;
case 'seek': case "seek":
seek(command.position) seek(command.position);
break break;
case 'setVolume': case "setVolume":
setVolume(command.volume) setVolume(command.volume);
break break;
case 'setLoop': case "setLoop":
isLooping.value = command.enabled isLooping.value = command.enabled;
if (videoElement.value) { if (videoElement.value) {
videoElement.value.loop = command.enabled videoElement.value.loop = command.enabled;
} }
break break;
case 'toggleFullscreen': case "toggleFullscreen":
toggleFullscreenMode() toggleFullscreenMode();
break break;
case 'loadVideo': case "loadVideo":
await loadVideoFromPath(command.path) await loadVideoFromPath(command.path);
break break;
} }
// Update state after command // Update state after command
await updatePlayerState() await updatePlayerState();
} }
// Load video from file path // Load video from file path
async function loadVideoFromPath(path) { async function loadVideoFromPath(path) {
console.log('📥 loadVideoFromPath called with:', path) console.log("📥 loadVideoFromPath called with:", path);
try { try {
const rawPath = path.startsWith('file://') ? path.slice(7) : path const rawPath = path.startsWith("file://") ? path.slice(7) : path;
console.log('📥 Processing video path:', { originalPath: path, rawPath }) console.log("📥 Processing video path:", { originalPath: path, rawPath });
const title = rawPath.split('/').pop() || rawPath const title = rawPath.split("/").pop() || rawPath;
currentVideo.value = { currentVideo.value = {
path: rawPath, path: rawPath,
title, title,
duration: null, duration: null,
size: null, size: null,
format: null format: null
} };
console.log('📹 currentVideo.value set to:', currentVideo.value) console.log("📹 currentVideo.value set to:", currentVideo.value);
// The videoSrc will be updated automatically via the watcher // The videoSrc will be updated automatically via the watcher
if (invoke) { if (invoke) {
await invoke('load_video', { path: rawPath }) await invoke("load_video", { path: rawPath });
} }
console.log('📹 Video loaded successfully:', title) console.log("📹 Video loaded successfully:", title);
} catch (error) { } catch (error) {
console.error('❌ Failed to load video:', error) console.error("❌ Failed to load video:", error);
} }
} }
// Toggle fullscreen mode // Toggle fullscreen mode
function toggleFullscreenMode() { function toggleFullscreenMode() {
if (document.fullscreenElement) { if (document.fullscreenElement) {
document.exitFullscreen?.() document.exitFullscreen?.();
isFullscreen.value = false isFullscreen.value = false;
} else { } else {
document.documentElement.requestFullscreen?.() document.documentElement.requestFullscreen?.();
isFullscreen.value = true isFullscreen.value = true;
} }
} }
// Global keyboard shortcuts // Global keyboard shortcuts
function handleKeyPress(event) { function handleKeyPress(event) {
switch (event.key) { switch (event.key) {
case ' ': case " ":
event.preventDefault() event.preventDefault();
togglePlay() togglePlay();
break break;
case 'f': case "f":
case 'F': case "F":
event.preventDefault() event.preventDefault();
// Toggle fullscreen // Toggle fullscreen
break break;
} }
} }
onMounted(() => { onMounted(() => {
document.addEventListener('keydown', handleKeyPress) document.addEventListener("keydown", handleKeyPress);
})
// Disable context menu on video and entire page
const disableContextMenu = (e) => {
e.preventDefault();
return false;
};
// Add contextmenu event listeners
document.addEventListener("contextmenu", disableContextMenu);
// Also disable on video element specifically
if (videoElement.value) {
videoElement.value.addEventListener("contextmenu", disableContextMenu);
}
// Store the handler for cleanup
window._disableContextMenu = disableContextMenu;
});
onUnmounted(() => { onUnmounted(() => {
document.removeEventListener('keydown', handleKeyPress) document.removeEventListener("keydown", handleKeyPress);
})
// Clean up context menu listeners
if (window._disableContextMenu) {
document.removeEventListener("contextmenu", window._disableContextMenu);
if (videoElement.value) {
videoElement.value.removeEventListener("contextmenu", window._disableContextMenu);
}
delete window._disableContextMenu;
}
});
</script> </script>

File diff suppressed because it is too large Load Diff