video-upload-simple.ts
edited
1// NOTE: this uses Deno
2
3import { AppBskyEmbedVideo, AtpAgent } from "npm:@atproto/api";
4
5const userAgent = new AtpAgent({
6 service: prompt("Service URL (default: https://bsky.social):") ||
7 "https://bsky.social",
8});
9
10await userAgent.login({
11 identifier: prompt("Handle:")!,
12 password: prompt("Password:")!,
13});
14
15console.log(`Logged in as ${userAgent.session?.handle}`);
16
17const videoPath = prompt("Video file (.mp4):")!;
18
19const file = await Deno.open(videoPath);
20const { size } = await file.stat();
21
22// optional: print upload progress
23let bytesUploaded = 0;
24const progressTrackingStream = new TransformStream({
25 transform(chunk, controller) {
26 controller.enqueue(chunk);
27 bytesUploaded += chunk.byteLength;
28 console.log(
29 "upload progress:",
30 Math.trunc(bytesUploaded / size * 100) + "%",
31 );
32 },
33 flush() {
34 console.log("upload complete ✨");
35 },
36});
37
38const { data } = await userAgent.com.atproto.repo.uploadBlob(
39 // @ts-expect-error - expecting an Uint8Array, but a ReadableStream is fine
40 file.readable.pipeThrough(progressTrackingStream),
41);
42
43console.log("video uploaded, posting...");
44
45await userAgent.post({
46 text: "This post should have a video attached",
47 langs: ["en"],
48 embed: {
49 $type: "app.bsky.embed.video",
50 video: data.blob,
51 aspectRatio: await getAspectRatio(videoPath),
52 } satisfies AppBskyEmbedVideo.Main,
53});
54
55console.log("done ✨ (video will take a little bit to process)");
56
57// bonus: get aspect ratio using ffprobe
58// in the browser, you can just put the video uri in a <video> element
59// and measure the dimensions once it loads. in React Native, the image picker
60// will give you the dimensions directly
61
62import { ffprobe } from "https://deno.land/x/fast_forward@0.1.6/ffprobe.ts";
63
64async function getAspectRatio(fileName: string) {
65 const { streams } = await ffprobe(fileName, {});
66 const videoSteam = streams.find((stream) => stream.codec_type === "video");
67 return {
68 width: videoSteam.width,
69 height: videoSteam.height,
70 };
71}