Skip to content

Instantly share code, notes, and snippets.

@librz
Created March 14, 2025 07:36
Show Gist options
  • Save librz/3df7287e38776782aea564f399b4cd10 to your computer and use it in GitHub Desktop.
Save librz/3df7287e38776782aea564f399b4cd10 to your computer and use it in GitHub Desktop.
Get video size(in bytes) and duraiton(in seconds) on the frontend
import { useQuery } from "@tanstack/react-query";
import { useRef, useState } from "react";
async function fetchVideoContentLength(videoUrl: string) {
const controller = new AbortController();
const signal = controller.signal;
try {
const response = await fetch(videoUrl, {
// the idiomatic way of requesting only the response headers(without the content) is using a HEAD request, but server may not support the HEAD method
method: "GET",
headers: {
// do not use something like "bytes=0-1", in chrome, it may throw the `net::ERR_CONTENT_LENGTH_MISMATCH)` error and prevent reading reponse.headers
// instead, try request the whole video, but abort request immediately after reading response headers
Range: "bytes=0-",
},
signal: signal,
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const contentRange = response.headers.get("Content-Range");
if (contentRange) {
// e.g. Content-Range: bytes 0-100/210981431
const totalSizeStr = contentRange.split("/")[1] as string;
controller.abort();
return parseInt(totalSizeStr);
} else {
// if content-range is not present, try use content-length
const contentLength = response.headers.get("Content-Length");
controller.abort();
return contentLength ? parseInt(contentLength) : null;
}
} catch (error) {
console.error("Error getting video content length:", error);
return null;
}
}
export function VideoInfo(props: { url: string }) {
const { isLoading, data: bytes } = useQuery({
queryKey: [props.url],
queryFn: () => fetchVideoContentLength(props.url),
});
const videoRef = useRef<HTMLVideoElement>(null);
const [duration, setDuration] = useState(0);
return (
<div>
<video
hidden //hide since <video /> is used only to load meta data
ref={videoRef}
src={props.url}
preload={"metadata"}
onLoadedMetadata={() => {
setDuration(videoRef.current?.duration ?? 0);
}}
/>
{isLoading && "Loading..."}
{bytes && duration && <div>{`bytes: ${bytes}; durtaion: ${duration}s`}</div>}
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment