From ff7eb2639d15b7b2d3ee247c46914d5c6ee1515a Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 29 May 2025 15:29:53 +0000 Subject: [PATCH] api/loom: add support for non-transcoded links, add more tests --- api/src/processing/services/loom.js | 69 ++++++++++++++++++++--------- api/src/util/tests/loom.json | 29 +++++++++++- 2 files changed, 76 insertions(+), 22 deletions(-) diff --git a/api/src/processing/services/loom.js b/api/src/processing/services/loom.js index 01f71e51..749f6e8f 100644 --- a/api/src/processing/services/loom.js +++ b/api/src/processing/services/loom.js @@ -1,18 +1,18 @@ 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`, { method: "POST", - headers: { - "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", - }, + headers: craftHeaders(id), body: JSON.stringify({ force_original: false, password: null, @@ -20,20 +20,47 @@ export default async function({ id }) { deviceID: null }) }) - .then(r => r.status === 200 ? r.json() : false) + .then(r => r.status === 200 && r.json()) .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?')) { - return { - urls: videoUrl, - filename: `loom_${id}.mp4`, - audioFilename: `loom_${id}_audio` - } + if (gql?.url?.includes('.mp4?')) { + return gql.url; + } +} + +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` + } } diff --git a/api/src/util/tests/loom.json b/api/src/util/tests/loom.json index cc4273d3..0ebb4580 100644 --- a/api/src/util/tests/loom.json +++ b/api/src/util/tests/loom.json @@ -29,5 +29,32 @@ "code": 400, "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" + } } -] \ No newline at end of file +]