Stream video through backend with Range request support

Previously videos were served via direct RustFS URLs, which meant:
- No HTTP Range support (browsers had to download the entire file)
- Large videos couldn't play at all
- Video player rendered broken

Now videos stream through GET /api/videos/:id/stream which:
- Proxies video data from RustFS to the browser
- Supports Range requests (HTTP 206 Partial Content) for seeking
- Sets proper headers (Accept-Ranges, Content-Range, Content-Type)
- Caches for 24 hours

Frontend changes:
- VideoPlayer: added playsInline, preload=metadata, object-contain, error state
- VideoDetail: removed duplicate inline video, now uses VideoPlayer component
- index.css: style WebKit video controls (dark panel, no border-radius)
This commit is contained in:
CallMeVerity
2026-06-03 03:56:39 +01:00
parent dc021e4856
commit 6da2539c03
5 changed files with 181 additions and 62 deletions
+41 -4
View File
@@ -4,20 +4,22 @@ import { formatRunTime } from "../api/client";
export default function VideoPlayer({ video }: { video: VideoDetail }) {
const [loaded, setLoaded] = useState(false);
const [error, setError] = useState(false);
const videoRef = useRef<HTMLVideoElement>(null);
return (
<div className="rounded-lg border border-white/5 overflow-hidden">
<div className="aspect-video bg-black relative">
{!loaded && (
{}
{!loaded && !error && (
<div className="absolute inset-0 flex items-center justify-center z-10">
{video.thumbnailUrl ? (
{video.thumbnailUrl && (
<img
src={video.thumbnailUrl}
alt=""
className="absolute inset-0 w-full h-full object-cover blur-sm opacity-50"
/>
) : null}
)}
<div className="relative z-20 flex flex-col items-center gap-2">
<div className="w-10 h-10 rounded-full border-2 border-white/20 border-t-white/80 animate-spin" />
<span className="text-xs text-white/40">
@@ -26,13 +28,48 @@ export default function VideoPlayer({ video }: { video: VideoDetail }) {
</div>
</div>
)}
{}
{error && (
<div className="absolute inset-0 flex items-center justify-center z-10">
{video.thumbnailUrl && (
<img
src={video.thumbnailUrl}
alt=""
className="absolute inset-0 w-full h-full object-cover opacity-30"
/>
)}
<div className="relative z-20 flex flex-col items-center gap-2">
<svg
className="w-12 h-12 text-red-400/60"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1.5}
d="M12 9v2m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<span className="text-xs text-white/40">
Failed to load video
</span>
</div>
</div>
)}
<video
ref={videoRef}
src={video.videoUrl}
controls
className="w-full h-full"
playsInline
preload="metadata"
className="w-full h-full object-contain"
poster={video.thumbnailUrl}
onCanPlay={() => setLoaded(true)}
onError={() => setError(true)}
>
Your browser does not support video playback.
</video>