first draft videos

This commit is contained in:
overcuriousity
2025-08-12 14:53:11 +02:00
parent d6760d0f84
commit f159f904f0
9 changed files with 1789 additions and 1 deletions

185
src/components/Video.astro Normal file
View File

@@ -0,0 +1,185 @@
---
// src/components/Video.astro
import { videoProcessor, type VideoMetadata } from '../utils/videoUtils.js';
export interface Props {
src: string;
title?: string;
description?: string;
controls?: boolean;
autoplay?: boolean;
muted?: boolean;
loop?: boolean;
preload?: 'none' | 'metadata' | 'auto';
aspectRatio?: '16:9' | '4:3' | '1:1';
showMetadata?: boolean;
poster?: string;
width?: string;
height?: string;
fallback?: string;
}
const {
src,
title,
description,
controls = true,
autoplay = false,
muted = false,
loop = false,
preload = 'metadata',
aspectRatio = '16:9',
showMetadata = true,
poster,
width,
height,
fallback
} = Astro.props;
// Process the video URL and generate optimized sources
const metadata: Partial<VideoMetadata> = {
title,
description,
poster
};
const options = {
controls,
autoplay,
muted,
loop,
preload,
aspectRatio,
showMetadata,
width,
height
};
let processedVideo;
let videoHTML = '';
let errorMessage = '';
try {
processedVideo = await videoProcessor.processVideoUrl(src, metadata);
videoHTML = videoProcessor.generateVideoHTML(processedVideo, options);
} catch (error) {
console.error('[VIDEO COMPONENT] Processing failed:', error);
errorMessage = error.message;
videoHTML = `
<div class="video-container aspect-${aspectRatio}">
<div class="video-error">
<div class="error-icon">⚠️</div>
<div>${fallback || `Video could not be loaded: ${errorMessage}`}</div>
</div>
</div>
`;
}
---
<Fragment set:html={videoHTML} />
<script>
// Client-side video enhancement
document.addEventListener('DOMContentLoaded', () => {
const videos = document.querySelectorAll('.video-container video') as NodeListOf<HTMLVideoElement>;
videos.forEach((video: HTMLVideoElement) => {
const container = video.closest('.video-container') as HTMLElement;
if (!container) return;
// Add loading state
video.addEventListener('loadstart', () => {
container.classList.add('loading');
});
video.addEventListener('loadeddata', () => {
container.classList.remove('loading');
container.classList.add('loaded');
});
video.addEventListener('error', (e) => {
console.error('[VIDEO] Load error:', e);
container.classList.remove('loading');
container.classList.add('error');
// Show error message
const errorDiv = document.createElement('div');
errorDiv.className = 'video-error';
errorDiv.innerHTML = `
<div class="error-icon">⚠️</div>
<div>Video could not be loaded</div>
`;
(video as HTMLElement).style.display = 'none';
container.appendChild(errorDiv);
});
// Handle fullscreen
video.addEventListener('dblclick', () => {
const videoElement = video as any;
if (video.requestFullscreen) {
video.requestFullscreen();
} else if (videoElement.webkitRequestFullscreen) {
videoElement.webkitRequestFullscreen();
} else if (videoElement.msRequestFullscreen) {
videoElement.msRequestFullscreen();
}
});
// Add keyboard shortcuts
video.addEventListener('keydown', (e: KeyboardEvent) => {
switch(e.key) {
case ' ':
e.preventDefault();
if (video.paused) {
video.play();
} else {
video.pause();
}
break;
case 'f':
e.preventDefault();
if (video.requestFullscreen) {
video.requestFullscreen();
}
break;
case 'm':
e.preventDefault();
video.muted = !video.muted;
break;
}
});
});
});
</script>
<style>
/* Component-specific enhancements */
.video-container.loading video {
opacity: 0.5;
}
.video-container.loading::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 2rem;
height: 2rem;
border: 3px solid var(--color-border);
border-top: 3px solid var(--color-primary);
border-radius: 50%;
animation: spin 1s linear infinite;
z-index: 10;
}
.video-container.error video {
display: none;
}
@keyframes spin {
0% { transform: translate(-50%, -50%) rotate(0deg); }
100% { transform: translate(-50%, -50%) rotate(360deg); }
}
</style>