api/loom: add support for non-transcoded links, add more tests

This commit is contained in:
jj
2025-05-29 15:29:53 +00:00
parent 8a7b3e9386
commit ff7eb2639d
2 changed files with 76 additions and 22 deletions

View File

@ -1,18 +1,18 @@
import { genericUserAgent } from "../../config.js"; import { genericUserAgent } from "../../config.js";
export default async function({ id }) { const craftHeaders = id => ({
"user-agent": genericUserAgent,
"content-type": "application/json",
origin: "https://www.loom.com",
referer: `https://www.loom.com/share/${id}`,
cookie: `loom_referral_video=${id};`,
"x-loom-request-source": "loom_web_be851af",
});
async function fromTranscodedURL(id) {
const gql = await fetch(`https://www.loom.com/api/campaigns/sessions/${id}/transcoded-url`, { const gql = await fetch(`https://www.loom.com/api/campaigns/sessions/${id}/transcoded-url`, {
method: "POST", method: "POST",
headers: { headers: craftHeaders(id),
"user-agent": genericUserAgent,
origin: "https://www.loom.com",
referer: `https://www.loom.com/share/${id}`,
cookie: `loom_referral_video=${id};`,
"apollographql-client-name": "web",
"apollographql-client-version": "14c0b42",
"x-loom-request-source": "loom_web_14c0b42",
},
body: JSON.stringify({ body: JSON.stringify({
force_original: false, force_original: false,
password: null, password: null,
@ -20,20 +20,47 @@ export default async function({ id }) {
deviceID: null deviceID: null
}) })
}) })
.then(r => r.status === 200 ? r.json() : false) .then(r => r.status === 200 && r.json())
.catch(() => {}); .catch(() => {});
if (!gql) return { error: "fetch.empty" }; if (gql?.url?.includes('.mp4?')) {
return gql.url;
}
}
const videoUrl = gql?.url; async function fromRawURL(id) {
const gql = await fetch(`https://www.loom.com/api/campaigns/sessions/${id}/raw-url`, {
method: "POST",
headers: craftHeaders(id),
body: JSON.stringify({
anonID: crypto.randomUUID(),
client_name: "web",
client_version: "be851af",
deviceID: null,
force_original: false,
password: null,
supported_mime_types: ["video/mp4"],
})
})
.then(r => r.status === 200 && r.json())
.catch(() => {});
if (videoUrl?.includes('.mp4?')) { if (gql?.url?.includes('.mp4?')) {
return { return gql.url;
urls: videoUrl, }
filename: `loom_${id}.mp4`, }
audioFilename: `loom_${id}_audio`
} export default async function({ id }) {
let url = await fromTranscodedURL(id);
url ??= await fromRawURL(id);
if (!url) {
return { error: "fetch.empty" }
} }
return { error: "fetch.empty" } return {
urls: url,
filename: `loom_${id}.mp4`,
audioFilename: `loom_${id}_audio`
}
} }

View File

@ -29,5 +29,32 @@
"code": 400, "code": 400,
"status": "error" "status": "error"
} }
},
{
"name": "video with no transcodedUrl",
"url": "https://www.loom.com/share/aa3d8b08bee74d05af5b42989e9f33e9",
"params": {},
"expected": {
"code": 200,
"status": "redirect"
}
},
{
"name": "video with title in url",
"url": "https://www.loom.com/share/Meet-AI-workflows-aa3d8b08bee74d05af5b42989e9f33e9",
"params": {},
"expected": {
"code": 200,
"status": "redirect"
}
},
{
"name": "video with title in url (2)",
"url": "https://www.loom.com/share/Unlocking-Incredible-Organizational-Velocity-with-Async-Video-4a2a8baf124c4390954dcbb46a58cfd7",
"params": {},
"expected": {
"code": 200,
"status": "redirect"
}
} }
] ]