video.cerulea.blue/client/viewer.tsx

75 lines
2.4 KiB
TypeScript

import { resolveDid, resolveHandle } from "../common/identity.ts";
import { VIDEO_PATTERN } from "../common/routes.ts";
type BlobRef = {
$type: "blob";
ref: { $link: string };
mimeType?: string;
size?: number;
};
type VideoRecord = { video: BlobRef; title?: string; description?: string };
const fetchVideoRecord = async (
did: string,
rkey: string
): Promise<VideoRecord> => {
// TODO: we do lots of casting here that we shouldn't need once we have lexicon.ts validations
const didDoc = await resolveDid(did);
const pds = didDoc.service?.find((it) => it.id === "#atproto_pds")
?.serviceEndpoint as string | undefined;
if (!pds) throw new Error("could not resolve pds for requested repo");
const getRecordURL = new URL("/xrpc/com.atproto.repo.getRecord", pds);
getRecordURL.searchParams.set("collection", "blue.cerulea.video.video");
getRecordURL.searchParams.set("repo", did);
getRecordURL.searchParams.set("rkey", rkey);
const recordResponse = await fetch(getRecordURL);
if (recordResponse.status !== 200)
throw new Error("got error fetching record");
const record = (await recordResponse.json()).value as VideoRecord;
return record;
};
const resolveVideoURL = async (did: string, blob: string): Promise<string> => {
const res = await fetch("/xrpc/blue.cerulea.video.fetchVideo", {
method: "POST",
headers: { "content-encoding": "application/json" },
body: JSON.stringify({ repo: did, blob }),
}).then((r) => r.json());
return `/user-content/${res.filename}`;
};
const main = async () => {
const player = document.querySelector("#player")! as HTMLElement;
const location = VIDEO_PATTERN.exec(globalThis.location.href);
if (!location) throw new Error("video pattern did not match url");
const { repo, rkey } = location.pathname.groups as {
repo: string;
rkey: string;
};
const did = repo.startsWith("did:") ? repo : await resolveHandle(repo);
const video = await fetchVideoRecord(did, rkey);
const videoURL = await resolveVideoURL(did, video.video.ref.$link);
player.append(
<video crossOrigin="anonymous" controls>
<source src={videoURL} />
</video>
);
if (video.title) {
player.append(<h1>{video.title}</h1>);
document.title = `${video.title} | video.cerulea.blue`;
}
if (video.description)
player.append(<p className="description">{video.description}</p>);
};
main();