From eb3cee1b66951dd6bd9dc741cacd2573d11f6919 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 13 Jul 2025 21:17:54 -0400 Subject: [PATCH] fix video stream --- app/main/page.tsx | 53 +++++++++++++++++++++++++++++++++++++++++++---- lib/api.ts | 28 +++++++++++++++---------- 2 files changed, 66 insertions(+), 15 deletions(-) diff --git a/app/main/page.tsx b/app/main/page.tsx index 7f625a9..0a2b1f7 100644 --- a/app/main/page.tsx +++ b/app/main/page.tsx @@ -1,24 +1,66 @@ 'use client'; -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { useAuth } from '@/lib/auth'; -import { apiRequest, API_URL } from '@/lib/api'; +import { apiRequest } from '@/lib/api'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Textarea } from '@/components/ui/textarea'; const VotingPage = () => { const [video, setVideo] = useState(null); + const [videoUrl, setVideoUrl] = useState(null); // State for the blob URL const [reason, setReason] = useState(''); const [message, setMessage] = useState(''); const [error, setError] = useState(''); const auth = useAuth(); + // This effect runs when the `video` metadata is fetched + useEffect(() => { + // If there's no video metadata, do nothing. + if (!video) { + return; + } + + let objectUrl: string; // To keep track of the URL for cleanup + + const fetchVideoBlob = async () => { + try { + // Fetch the video stream as a raw response, not JSON + const response = await apiRequest(`/videos/${video.id}/stream`, { + token: auth.token, + wantsJson: false // We expect a blob, not JSON + }); + + const blob = await response.blob(); + objectUrl = URL.createObjectURL(blob); + setVideoUrl(objectUrl); + + } catch (err: any) { + setError(`Failed to load video: ${err.message}`); + } + }; + + fetchVideoBlob(); + + // Cleanup function: revoke the object URL to free up memory + return () => { + if (objectUrl) { + URL.revokeObjectURL(objectUrl); + } + }; + }, [video, auth.token]); + + const fetchNextVideo = async () => { + // Reset all states for the new video setError(''); setMessage(''); setVideo(null); + setVideoUrl(null); + setReason(''); try { + // This just gets the video metadata (id, person, game) const data = await apiRequest('/videos/vote-next', { token: auth.token }); setVideo(data); } catch (err: any) { @@ -38,7 +80,9 @@ const VotingPage = () => { token: auth.token }); setMessage(`Vote '${decision}' submitted successfully!`); + // Reset state to prepare for the next video setVideo(null); + setVideoUrl(null); setReason(''); } catch (err: any) { setError(err.message); @@ -61,14 +105,15 @@ const VotingPage = () => { {error &&

{error}

} {message &&

{message}

} - {video && ( + {/* The video player now uses the local blob URL */} + {video && videoUrl && (
diff --git a/lib/api.ts b/lib/api.ts index 3f34706..41321ce 100644 --- a/lib/api.ts +++ b/lib/api.ts @@ -1,20 +1,26 @@ -// We still export this for use in components where the raw URL is needed, -// like the