From 2c56d2cf0520d944a8530a25d3573c85c6f14f89 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Tue, 14 Oct 2025 23:19:06 +0800 Subject: [PATCH 1/7] don't resolve video src until upload finish --- apps/web/app/api/desktop/[...route]/video.ts | 7 +- .../[videoId]/_components/CapVideoPlayer.tsx | 134 +++++++++--------- apps/web/instrumentation.node.ts | 2 + 3 files changed, 67 insertions(+), 76 deletions(-) diff --git a/apps/web/app/api/desktop/[...route]/video.ts b/apps/web/app/api/desktop/[...route]/video.ts index 90837ef8d9..e168607f07 100644 --- a/apps/web/app/api/desktop/[...route]/video.ts +++ b/apps/web/app/api/desktop/[...route]/video.ts @@ -209,12 +209,7 @@ app.get( .from(videos) .where(eq(videos.ownerId, user.id)); - if ( - videoCount && - videoCount[0] && - videoCount[0].count === 1 && - user.email - ) { + if (videoCount?.[0] && videoCount[0].count === 1 && user.email) { console.log( "[SendFirstShareableLinkEmail] Sending first shareable link email with 5-minute delay", ); diff --git a/apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx b/apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx index 84c70eb083..90cfefb831 100644 --- a/apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx +++ b/apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx @@ -4,6 +4,7 @@ import { LogoSpinner } from "@cap/ui"; import type { Video } from "@cap/web-domain"; import { faPlay } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { useQuery } from "@tanstack/react-query"; import clsx from "clsx"; import { AnimatePresence, motion } from "framer-motion"; import { AlertTriangleIcon } from "lucide-react"; @@ -77,9 +78,6 @@ export function CapVideoPlayer({ const [videoLoaded, setVideoLoaded] = useState(false); const [hasPlayedOnce, setHasPlayedOnce] = useState(false); const [isMobile, setIsMobile] = useState(false); - const [resolvedVideoSrc, setResolvedVideoSrc] = useState(videoSrc); - const [useCrossOrigin, setUseCrossOrigin] = useState(enableCrossOrigin); - const [urlResolved, setUrlResolved] = useState(false); const retryCount = useRef(0); const retryTimeout = useRef(null); const startTime = useRef(Date.now()); @@ -100,50 +98,58 @@ export function CapVideoPlayer({ return () => window.removeEventListener("resize", checkMobile); }, []); - const fetchNewUrl = useCallback(async () => { - try { - const timestamp = new Date().getTime(); - const urlWithTimestamp = videoSrc.includes("?") - ? `${videoSrc}&_t=${timestamp}` - : `${videoSrc}?_t=${timestamp}`; - - const response = await fetch(urlWithTimestamp, { - method: "GET", - headers: { range: "bytes=0-0" }, - }); - const finalUrl = response.redirected ? response.url : urlWithTimestamp; - - // Check if the resolved URL is from a CORS-incompatible service - const isCloudflareR2 = finalUrl.includes(".r2.cloudflarestorage.com"); - const isS3 = - finalUrl.includes(".s3.") || finalUrl.includes("amazonaws.com"); - const isCorsIncompatible = isCloudflareR2 || isS3; - - // Set CORS based on URL compatibility BEFORE video element is created - if (isCorsIncompatible) { - console.log( - "CapVideoPlayer: Detected CORS-incompatible URL, disabling crossOrigin:", - finalUrl, - ); - setUseCrossOrigin(false); - } else { - setUseCrossOrigin(enableCrossOrigin); - } + const uploadProgressRaw = useUploadProgress( + videoId, + hasActiveUpload || false, + ); + // if the video comes back from S3, just ignore the upload progress. + const uploadProgress = videoLoaded ? null : uploadProgressRaw; + const isUploading = uploadProgress?.status === "uploading"; - setResolvedVideoSrc(finalUrl); - setUrlResolved(true); - return finalUrl; - } catch (error) { - console.error("CapVideoPlayer: Error fetching new video URL:", error); - const timestamp = new Date().getTime(); - const fallbackUrl = videoSrc.includes("?") - ? `${videoSrc}&_t=${timestamp}` - : `${videoSrc}?_t=${timestamp}`; - setResolvedVideoSrc(fallbackUrl); - setUrlResolved(true); - return fallbackUrl; - } - }, [videoSrc, enableCrossOrigin]); + const resolvedSrc = useQuery({ + queryKey: ["resolvedSrc", videoSrc], + queryFn: async () => { + try { + const timestamp = Date.now(); + const urlWithTimestamp = videoSrc.includes("?") + ? `${videoSrc}&_t=${timestamp}` + : `${videoSrc}?_t=${timestamp}`; + + const response = await fetch(urlWithTimestamp, { + method: "GET", + headers: { range: "bytes=0-0" }, + }); + const finalUrl = response.redirected ? response.url : urlWithTimestamp; + + // Check if the resolved URL is from a CORS-incompatible service + const isCloudflareR2 = finalUrl.includes(".r2.cloudflarestorage.com"); + const isS3 = + finalUrl.includes(".s3.") || finalUrl.includes("amazonaws.com"); + const isCorsIncompatible = isCloudflareR2 || isS3; + + let supportsCrossOrigin = enableCrossOrigin; + + // Set CORS based on URL compatibility BEFORE video element is created + if (isCorsIncompatible) { + console.log( + "CapVideoPlayer: Detected CORS-incompatible URL, disabling crossOrigin:", + finalUrl, + ); + supportsCrossOrigin = false; + } + + return { url: finalUrl, supportsCrossOrigin }; + } catch (error) { + console.error("CapVideoPlayer: Error fetching new video URL:", error); + const timestamp = Date.now(); + const fallbackUrl = videoSrc.includes("?") + ? `${videoSrc}&_t=${timestamp}` + : `${videoSrc}?_t=${timestamp}`; + return { fallbackUrl, supportsCrossOrigin: enableCrossOrigin }; + } + }, + enabled: !isUploading, + }); const reloadVideo = useCallback(async () => { const video = videoRef.current; @@ -172,7 +178,7 @@ export function CapVideoPlayer({ } retryCount.current += 1; - }, [fetchNewUrl, maxRetries]); + }, [maxRetries]); const setupRetry = useCallback(() => { if (retryTimeout.current) { @@ -216,26 +222,19 @@ export function CapVideoPlayer({ // Reset state when video source changes useEffect(() => { - setResolvedVideoSrc(videoSrc); + resolvedSrc.refetch(); setVideoLoaded(false); setHasError(false); isRetryingRef.current = false; setIsRetrying(false); retryCount.current = 0; startTime.current = Date.now(); - setUrlResolved(false); - setUseCrossOrigin(enableCrossOrigin); if (retryTimeout.current) { clearTimeout(retryTimeout.current); retryTimeout.current = null; } - }, [videoSrc, enableCrossOrigin]); - - // Resolve video URL on mount and when videoSrc changes - useEffect(() => { - fetchNewUrl(); - }, [fetchNewUrl]); + }, [resolvedSrc.refetch, videoSrc, enableCrossOrigin]); // Track video duration for comment markers useEffect(() => { @@ -251,7 +250,7 @@ export function CapVideoPlayer({ return () => { video.removeEventListener("loadedmetadata", handleLoadedMetadata); }; - }, [urlResolved]); + }, [resolvedSrc.isLoading]); // Track when all data is ready for comment markers const [markersReady, setMarkersReady] = useState(false); @@ -275,7 +274,7 @@ export function CapVideoPlayer({ useEffect(() => { const video = videoRef.current; - if (!video || !urlResolved) return; + if (!video || !resolvedSrc.isLoading) return; const handleLoadedData = () => { setVideoLoaded(true); @@ -445,7 +444,7 @@ export function CapVideoPlayer({ captionTrack.removeEventListener("cuechange", handleCueChange); } }; - }, [hasPlayedOnce, videoSrc, urlResolved]); + }, [hasPlayedOnce, videoSrc, resolvedSrc.isLoading]); const generateVideoFrameThumbnail = useCallback((time: number): string => { const video = videoRef.current; @@ -470,13 +469,6 @@ export function CapVideoPlayer({ return `https://placeholder.pics/svg/224x128/dc2626/ffffff/Error`; }, []); - const uploadProgressRaw = useUploadProgress( - videoId, - hasActiveUpload || false, - ); - // if the video comes back from S3, just ignore the upload progress. - const uploadProgress = videoLoaded ? null : uploadProgressRaw; - const isUploading = uploadProgress?.status === "uploading"; const isUploadFailed = uploadProgress?.status === "failed"; const prevUploadProgress = useRef(uploadProgress); @@ -529,9 +521,9 @@ export function CapVideoPlayer({ )} - {urlResolved && ( + {resolvedSrc.isSuccess && ( { setVideoLoaded(true); @@ -540,7 +532,9 @@ export function CapVideoPlayer({ setShowPlayButton(false); setHasPlayedOnce(true); }} - crossOrigin={useCrossOrigin ? "anonymous" : undefined} + crossOrigin={ + resolvedSrc.data.supportsCrossOrigin ? "anonymous" : undefined + } playsInline autoPlay={autoplay} > @@ -654,7 +648,7 @@ export function CapVideoPlayer({ { From 17c494059a960a4df76dc0a5910a25593fe07152 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Wed, 15 Oct 2025 00:02:48 +0800 Subject: [PATCH 2/7] don't fetch playlist url while fetching upload progress --- .../[videoId]/_components/CapVideoPlayer.tsx | 2 +- .../[videoId]/_components/ProgressCircle.tsx | 43 +++++++++++-------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx b/apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx index 90cfefb831..ca65cf188d 100644 --- a/apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx +++ b/apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx @@ -148,7 +148,7 @@ export function CapVideoPlayer({ return { fallbackUrl, supportsCrossOrigin: enableCrossOrigin }; } }, - enabled: !isUploading, + enabled: !isUploading && uploadProgressRaw?.status !== "fetching", }); const reloadVideo = useCallback(async () => { diff --git a/apps/web/app/s/[videoId]/_components/ProgressCircle.tsx b/apps/web/app/s/[videoId]/_components/ProgressCircle.tsx index 4b13178838..156565c153 100644 --- a/apps/web/app/s/[videoId]/_components/ProgressCircle.tsx +++ b/apps/web/app/s/[videoId]/_components/ProgressCircle.tsx @@ -3,11 +3,11 @@ import type { Video } from "@cap/web-domain"; import clsx from "clsx"; import { Effect, Option } from "effect"; -import { useFeatureFlag } from "@/app/Layout/features"; import { useEffectQuery } from "@/lib/EffectRuntime"; import { withRpc } from "@/lib/Rpcs"; type UploadProgress = + | { status: "fetching" } | { status: "uploading"; lastUpdated: Date; @@ -23,7 +23,10 @@ const MINUTE = 60 * SECOND; const HOUR = 60 * 60 * SECOND; const DAY = 24 * HOUR; -export function useUploadProgress(videoId: Video.VideoId, enabled: boolean) { +export function useUploadProgress( + videoId: Video.VideoId, + enabled: boolean, +): UploadProgress | null { const query = useEffectQuery({ queryKey: ["getUploadProgress", videoId], queryFn: () => @@ -41,25 +44,27 @@ export function useUploadProgress(videoId: Video.VideoId, enabled: boolean) { else return SECOND; }, }); - if (!enabled || !query.data) return null; + + if (!enabled) return null; + if (query.isPending) return { status: "fetching" }; + if (!query.data) return null; + const lastUpdated = new Date(query.data.updatedAt); - return ( - Date.now() - lastUpdated.getTime() > 5 * MINUTE - ? { - status: "failed", - lastUpdated, - } - : { - status: "uploading", - lastUpdated, - progress: - // `0/0` for progress is `NaN` - query.data.total === 0 - ? 0 - : (query.data.uploaded / query.data.total) * 100, - } - ) satisfies UploadProgress; + return Date.now() - lastUpdated.getTime() > 5 * MINUTE + ? { + status: "failed", + lastUpdated, + } + : { + status: "uploading", + lastUpdated, + progress: + // `0/0` for progress is `NaN` + query.data.total === 0 + ? 0 + : (query.data.uploaded / query.data.total) * 100, + }; } const ProgressCircle = ({ From e2deb4e07b94415998de57e3c756a553d840f295 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Wed, 15 Oct 2025 00:31:29 +0800 Subject: [PATCH 3/7] use skipToken --- .../[videoId]/_components/CapVideoPlayer.tsx | 92 ++++++++++--------- 1 file changed, 51 insertions(+), 41 deletions(-) diff --git a/apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx b/apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx index ca65cf188d..5bd39f6134 100644 --- a/apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx +++ b/apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx @@ -4,7 +4,7 @@ import { LogoSpinner } from "@cap/ui"; import type { Video } from "@cap/web-domain"; import { faPlay } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { useQuery } from "@tanstack/react-query"; +import { skipToken, useQuery } from "@tanstack/react-query"; import clsx from "clsx"; import { AnimatePresence, motion } from "framer-motion"; import { AlertTriangleIcon } from "lucide-react"; @@ -105,50 +105,60 @@ export function CapVideoPlayer({ // if the video comes back from S3, just ignore the upload progress. const uploadProgress = videoLoaded ? null : uploadProgressRaw; const isUploading = uploadProgress?.status === "uploading"; + const isUploadProgressPending = uploadProgress?.status === "fetching"; const resolvedSrc = useQuery({ queryKey: ["resolvedSrc", videoSrc], - queryFn: async () => { - try { - const timestamp = Date.now(); - const urlWithTimestamp = videoSrc.includes("?") - ? `${videoSrc}&_t=${timestamp}` - : `${videoSrc}?_t=${timestamp}`; - - const response = await fetch(urlWithTimestamp, { - method: "GET", - headers: { range: "bytes=0-0" }, - }); - const finalUrl = response.redirected ? response.url : urlWithTimestamp; - - // Check if the resolved URL is from a CORS-incompatible service - const isCloudflareR2 = finalUrl.includes(".r2.cloudflarestorage.com"); - const isS3 = - finalUrl.includes(".s3.") || finalUrl.includes("amazonaws.com"); - const isCorsIncompatible = isCloudflareR2 || isS3; - - let supportsCrossOrigin = enableCrossOrigin; - - // Set CORS based on URL compatibility BEFORE video element is created - if (isCorsIncompatible) { - console.log( - "CapVideoPlayer: Detected CORS-incompatible URL, disabling crossOrigin:", - finalUrl, - ); - supportsCrossOrigin = false; - } + queryFn: + isUploadProgressPending || isUploading + ? skipToken + : async () => { + try { + const timestamp = Date.now(); + const urlWithTimestamp = videoSrc.includes("?") + ? `${videoSrc}&_t=${timestamp}` + : `${videoSrc}?_t=${timestamp}`; + + const response = await fetch(urlWithTimestamp, { + method: "GET", + headers: { range: "bytes=0-0" }, + }); + const finalUrl = response.redirected + ? response.url + : urlWithTimestamp; + + // Check if the resolved URL is from a CORS-incompatible service + const isCloudflareR2 = finalUrl.includes( + ".r2.cloudflarestorage.com", + ); + const isS3 = + finalUrl.includes(".s3.") || finalUrl.includes("amazonaws.com"); + const isCorsIncompatible = isCloudflareR2 || isS3; + + let supportsCrossOrigin = enableCrossOrigin; + + // Set CORS based on URL compatibility BEFORE video element is created + if (isCorsIncompatible) { + console.log( + "CapVideoPlayer: Detected CORS-incompatible URL, disabling crossOrigin:", + finalUrl, + ); + supportsCrossOrigin = false; + } - return { url: finalUrl, supportsCrossOrigin }; - } catch (error) { - console.error("CapVideoPlayer: Error fetching new video URL:", error); - const timestamp = Date.now(); - const fallbackUrl = videoSrc.includes("?") - ? `${videoSrc}&_t=${timestamp}` - : `${videoSrc}?_t=${timestamp}`; - return { fallbackUrl, supportsCrossOrigin: enableCrossOrigin }; - } - }, - enabled: !isUploading && uploadProgressRaw?.status !== "fetching", + return { url: finalUrl, supportsCrossOrigin }; + } catch (error) { + console.error( + "CapVideoPlayer: Error fetching new video URL:", + error, + ); + const timestamp = Date.now(); + const fallbackUrl = videoSrc.includes("?") + ? `${videoSrc}&_t=${timestamp}` + : `${videoSrc}?_t=${timestamp}`; + return { fallbackUrl, supportsCrossOrigin: enableCrossOrigin }; + } + }, }); const reloadVideo = useCallback(async () => { From b3393686fff8ae5bd019c41d90a67027a8db4c5c Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Wed, 15 Oct 2025 01:39:13 +0800 Subject: [PATCH 4/7] don't delete upload progress in progress update endpoint --- apps/desktop/src-tauri/src/api.rs | 8 +++++--- apps/desktop/src-tauri/src/upload.rs | 2 +- apps/web/app/api/desktop/[...route]/video.ts | 8 ++++---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/desktop/src-tauri/src/api.rs b/apps/desktop/src-tauri/src/api.rs index d60dbd9492..bf31aee214 100644 --- a/apps/desktop/src-tauri/src/api.rs +++ b/apps/desktop/src-tauri/src/api.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use serde_json::json; use tauri::AppHandle; -use tracing::instrument; +use tracing::{instrument, trace}; use crate::web_api::{AuthedApiError, ManagerExt}; @@ -46,7 +46,7 @@ pub async fn upload_multipart_initiate( .map(|data| data.upload_id) } -#[instrument] +#[instrument(skip(upload_id))] pub async fn upload_multipart_presign_part( app: &AppHandle, video_id: &str, @@ -110,7 +110,7 @@ pub struct S3VideoMeta { pub fps: Option, } -#[instrument] +#[instrument(skip(upload_id))] pub async fn upload_multipart_complete( app: &AppHandle, video_id: &str, @@ -133,6 +133,8 @@ pub async fn upload_multipart_complete( location: Option, } + trace!("Completing multipart upload"); + let resp = app .authed_api_request("/api/upload/multipart/complete", |c, url| { c.post(url) diff --git a/apps/desktop/src-tauri/src/upload.rs b/apps/desktop/src-tauri/src/upload.rs index f741806ebc..06e37f0822 100644 --- a/apps/desktop/src-tauri/src/upload.rs +++ b/apps/desktop/src-tauri/src/upload.rs @@ -616,7 +616,7 @@ fn retryable_client(host: String) -> reqwest::ClientBuilder { /// Takes an incoming stream of bytes and individually uploads them to S3. /// /// Note: It's on the caller to ensure the chunks are sized correctly within S3 limits. -#[instrument(skip(app, stream))] +#[instrument(skip(app, stream, upload_id))] fn multipart_uploader( app: AppHandle, video_id: String, diff --git a/apps/web/app/api/desktop/[...route]/video.ts b/apps/web/app/api/desktop/[...route]/video.ts index e168607f07..b4f48db48c 100644 --- a/apps/web/app/api/desktop/[...route]/video.ts +++ b/apps/web/app/api/desktop/[...route]/video.ts @@ -365,10 +365,10 @@ app.post( updatedAt, }); - if (uploaded === total) - await db() - .delete(videoUploads) - .where(eq(videoUploads.videoId, videoId)); + // if (uploaded === total) + // await db() + // .delete(videoUploads) + // .where(eq(videoUploads.videoId, videoId)); return c.json(true); } catch (error) { From cf0db3dd473b83bd0c0f3a644c211826594a6b34 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Wed, 15 Oct 2025 01:53:09 +0800 Subject: [PATCH 5/7] improve start recording button --- apps/desktop/src-tauri/src/api.rs | 2 +- .../src/routes/target-select-overlay.tsx | 47 +++++++++++++------ 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/apps/desktop/src-tauri/src/api.rs b/apps/desktop/src-tauri/src/api.rs index bf31aee214..96a7f2bb16 100644 --- a/apps/desktop/src-tauri/src/api.rs +++ b/apps/desktop/src-tauri/src/api.rs @@ -110,7 +110,7 @@ pub struct S3VideoMeta { pub fps: Option, } -#[instrument(skip(upload_id))] +#[instrument(skip_all)] pub async fn upload_multipart_complete( app: &AppHandle, video_id: &str, diff --git a/apps/desktop/src/routes/target-select-overlay.tsx b/apps/desktop/src/routes/target-select-overlay.tsx index e0c44bf295..9a5bb990f4 100644 --- a/apps/desktop/src/routes/target-select-overlay.tsx +++ b/apps/desktop/src/routes/target-select-overlay.tsx @@ -4,7 +4,7 @@ import { createEventListenerMap, } from "@solid-primitives/event-listener"; import { useSearchParams } from "@solidjs/router"; -import { createQuery } from "@tanstack/solid-query"; +import { createQuery, useMutation } from "@tanstack/solid-query"; import { emit } from "@tauri-apps/api/event"; import { CheckMenuItem, Menu, Submenu } from "@tauri-apps/api/menu"; import * as dialog from "@tauri-apps/plugin-dialog"; @@ -795,6 +795,18 @@ function RecordingControls(props: { return await Menu.new({ items: [await countdownMenu()] }); }; + const startRecording = useMutation(() => ({ + mutationFn: () => + handleRecordingResult( + commands.startRecording({ + capture_target: props.target, + mode: rawOptions.mode, + capture_system_audio: rawOptions.captureSystemAudio, + }), + setOptions, + ), + })); + return ( <>
@@ -805,43 +817,48 @@ function RecordingControls(props: {
{ if (rawOptions.mode === "instant" && !auth.data) { emit("start-sign-in"); return; } + if (startRecording.isPending) return; - handleRecordingResult( - commands.startRecording({ - capture_target: props.target, - mode: rawOptions.mode, - capture_system_audio: rawOptions.captureSystemAudio, - }), - setOptions, - ); + startRecording.mutate(); }} > -
+
{rawOptions.mode === "studio" ? ( ) : ( )}
- + {rawOptions.mode === "instant" && !auth.data ? "Sign In To Use" : "Start Recording"} - + {`${capitalize(rawOptions.mode)} Mode`}
{ e.stopPropagation(); menuModes().then((menu) => menu.popup()); From 92445f6157d374f9ff14939a1d579801f0da1ff4 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Wed, 15 Oct 2025 02:04:55 +0800 Subject: [PATCH 6/7] typesc --- .../app/(org)/dashboard/caps/components/CapCard/CapCard.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx b/apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx index 94b8c22511..a6fa4d6e6f 100644 --- a/apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx +++ b/apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx @@ -549,7 +549,9 @@ export const CapCard = ({ }} href={`/s/${cap.id}`} > - {imageStatus !== "success" && uploadProgress ? ( + {imageStatus !== "success" && + uploadProgress && + uploadProgress?.status !== "fetching" ? (
From 1e76204b744fa85b7f022e36498cf46347a5dada Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Wed, 15 Oct 2025 02:11:29 +0800 Subject: [PATCH 7/7] address coderabbit feedback --- apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx b/apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx index 5bd39f6134..ccff91ed48 100644 --- a/apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx +++ b/apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx @@ -107,7 +107,7 @@ export function CapVideoPlayer({ const isUploading = uploadProgress?.status === "uploading"; const isUploadProgressPending = uploadProgress?.status === "fetching"; - const resolvedSrc = useQuery({ + const resolvedSrc = useQuery<{ url: string; supportsCrossOrigin: boolean }>({ queryKey: ["resolvedSrc", videoSrc], queryFn: isUploadProgressPending || isUploading @@ -156,7 +156,10 @@ export function CapVideoPlayer({ const fallbackUrl = videoSrc.includes("?") ? `${videoSrc}&_t=${timestamp}` : `${videoSrc}?_t=${timestamp}`; - return { fallbackUrl, supportsCrossOrigin: enableCrossOrigin }; + return { + url: fallbackUrl, + supportsCrossOrigin: enableCrossOrigin, + }; } }, }); @@ -284,7 +287,7 @@ export function CapVideoPlayer({ useEffect(() => { const video = videoRef.current; - if (!video || !resolvedSrc.isLoading) return; + if (!video || resolvedSrc.isLoading) return; const handleLoadedData = () => { setVideoLoaded(true);