79 lines
2.5 KiB
TypeScript
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();
|