import { genericUserAgent } from "../../config.js"; 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: craftHeaders(id), body: JSON.stringify({ force_original: false, password: null, anonID: null, deviceID: null }) }) .then(r => r.status === 200 && r.json()) .catch(() => {}); if (gql?.url?.includes('.mp4?')) { return 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 (gql?.url?.includes('.mp4?')) { return gql.url; } } async function getTranscript(id) { const gql = await fetch(`https://www.loom.com/graphql`, { method: "POST", headers: craftHeaders(id), body: JSON.stringify({ operationName: "FetchVideoTranscriptForFetchTranscript", variables: { videoId: id, password: null, }, query: ` query FetchVideoTranscriptForFetchTranscript($videoId: ID!, $password: String) { fetchVideoTranscript(videoId: $videoId, password: $password) { ... on VideoTranscriptDetails { captions_source_url language __typename } ... on GenericError { message __typename } __typename } }`, }) }) .then(r => r.status === 200 && r.json()) .catch(() => {}); if (gql?.data?.fetchVideoTranscript?.captions_source_url?.includes('.vtt?')) { return gql.data.fetchVideoTranscript.captions_source_url; } } export default async function({ id, subtitleLang }) { let url = await fromTranscodedURL(id); url ??= await fromRawURL(id); if (!url) { return { error: "fetch.empty" } } let subtitles; if (subtitleLang) { const transcript = await getTranscript(id); if (transcript) subtitles = transcript; } return { urls: url, subtitles, filename: `loom_${id}.mp4`, audioFilename: `loom_${id}_audio` } }