video.cerulea.blue/client/viewer.tsx

79 lines
2.5 KiB
TypeScript

import type { Infer } from "@char/lexicon.ts";
import type { VideoLexiconUniverse } from "../common/lexicons.ts";
import { resolveDid, resolveHandle } from "../common/identity.ts";
import { VIDEO_PATTERN } from "../common/routes.ts";
import type { ATProtoUniverse } from "@char/lexicon.ts/atproto";
import { XRPC } from "@char/lexicon.ts/rpc";
type VideoRecord = Infer<VideoLexiconUniverse, "blue.cerulea.video.video">;
const fetchVideoRecord = async (did: string, rkey: string): Promise<VideoRecord> => {
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 xrpc = new XRPC<ATProtoUniverse>(pds);
const record = await xrpc.get("com.atproto.repo.getRecord", {
params: {
collection: "blue.cerulea.video.video",
repo: did,
rkey,
},
});
// TODO: replace this cast with a lexicon.ts validation check
return record.value as VideoRecord;
};
const resolveVideoURL = async (repo: `did:${string}`, blob: string): Promise<string> => {
try {
const xrpc = new XRPC<VideoLexiconUniverse>(globalThis.location.href);
const res = await xrpc.call("blue.cerulea.video.fetchVideo", {
input: { repo, blob },
});
return `/user-content/${res.filename}`;
} catch {
throw new Error("got error fetching URL of video at CDN");
}
};
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 isDid = (s: string): s is `did:${string}` => s.startsWith("did:");
const did = isDid(repo) ? repo : await resolveHandle(repo);
const record = await fetchVideoRecord(did, rkey);
const videoURL = await resolveVideoURL(
did,
"ref" in record.video ? record.video.ref.$link : record.video.cid,
);
const video = (
<video crossOrigin="anonymous" controls>
<source src={videoURL} />
</video>
) as HTMLVideoElement;
video.volume = 0.8;
player.append(video);
if (record.title) {
player.append(<h1>{record.title}</h1>);
document.title = `${record.title} | video.cerulea.blue`;
}
if (record.description) player.append(<p className="description">{record.description}</p>);
};
main();