Skip to content

Commit ff7eb26

Browse files
committed
api/loom: add support for non-transcoded links, add more tests
1 parent 8a7b3e9 commit ff7eb26

File tree

2 files changed

+76
-22
lines changed

2 files changed

+76
-22
lines changed
Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,66 @@
11
import { genericUserAgent } from "../../config.js";
22

3-
export default async function({ id }) {
3+
const craftHeaders = id => ({
4+
"user-agent": genericUserAgent,
5+
"content-type": "application/json",
6+
origin: "https://www.loom.com",
7+
referer: `https://www.loom.com/share/${id}`,
8+
cookie: `loom_referral_video=${id};`,
9+
"x-loom-request-source": "loom_web_be851af",
10+
});
11+
12+
async function fromTranscodedURL(id) {
413
const gql = await fetch(`https://www.loom.com/api/campaigns/sessions/${id}/transcoded-url`, {
514
method: "POST",
6-
headers: {
7-
"user-agent": genericUserAgent,
8-
origin: "https://www.loom.com",
9-
referer: `https://www.loom.com/share/${id}`,
10-
cookie: `loom_referral_video=${id};`,
11-
12-
"apollographql-client-name": "web",
13-
"apollographql-client-version": "14c0b42",
14-
"x-loom-request-source": "loom_web_14c0b42",
15-
},
15+
headers: craftHeaders(id),
1616
body: JSON.stringify({
1717
force_original: false,
1818
password: null,
1919
anonID: null,
2020
deviceID: null
2121
})
2222
})
23-
.then(r => r.status === 200 ? r.json() : false)
23+
.then(r => r.status === 200 && r.json())
2424
.catch(() => {});
2525

26-
if (!gql) return { error: "fetch.empty" };
26+
if (gql?.url?.includes('.mp4?')) {
27+
return gql.url;
28+
}
29+
}
2730

28-
const videoUrl = gql?.url;
31+
async function fromRawURL(id) {
32+
const gql = await fetch(`https://www.loom.com/api/campaigns/sessions/${id}/raw-url`, {
33+
method: "POST",
34+
headers: craftHeaders(id),
35+
body: JSON.stringify({
36+
anonID: crypto.randomUUID(),
37+
client_name: "web",
38+
client_version: "be851af",
39+
deviceID: null,
40+
force_original: false,
41+
password: null,
42+
supported_mime_types: ["video/mp4"],
43+
})
44+
})
45+
.then(r => r.status === 200 && r.json())
46+
.catch(() => {});
47+
48+
if (gql?.url?.includes('.mp4?')) {
49+
return gql.url;
50+
}
51+
}
52+
53+
export default async function({ id }) {
54+
let url = await fromTranscodedURL(id);
55+
url ??= await fromRawURL(id);
2956

30-
if (videoUrl?.includes('.mp4?')) {
31-
return {
32-
urls: videoUrl,
33-
filename: `loom_${id}.mp4`,
34-
audioFilename: `loom_${id}_audio`
35-
}
57+
if (!url) {
58+
return { error: "fetch.empty" }
3659
}
3760

38-
return { error: "fetch.empty" }
61+
return {
62+
urls: url,
63+
filename: `loom_${id}.mp4`,
64+
audioFilename: `loom_${id}_audio`
65+
}
3966
}

api/src/util/tests/loom.json

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,32 @@
2929
"code": 400,
3030
"status": "error"
3131
}
32+
},
33+
{
34+
"name": "video with no transcodedUrl",
35+
"url": "https://www.loom.com/share/aa3d8b08bee74d05af5b42989e9f33e9",
36+
"params": {},
37+
"expected": {
38+
"code": 200,
39+
"status": "redirect"
40+
}
41+
},
42+
{
43+
"name": "video with title in url",
44+
"url": "https://www.loom.com/share/Meet-AI-workflows-aa3d8b08bee74d05af5b42989e9f33e9",
45+
"params": {},
46+
"expected": {
47+
"code": 200,
48+
"status": "redirect"
49+
}
50+
},
51+
{
52+
"name": "video with title in url (2)",
53+
"url": "https://www.loom.com/share/Unlocking-Incredible-Organizational-Velocity-with-Async-Video-4a2a8baf124c4390954dcbb46a58cfd7",
54+
"params": {},
55+
"expected": {
56+
"code": 200,
57+
"status": "redirect"
58+
}
3259
}
33-
]
60+
]

0 commit comments

Comments
 (0)