75 lines
2.4 KiB
TypeScript
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();
|