File size: 3,158 Bytes
de4b571 11fcc5a de4b571 11fcc5a de4b571 11fcc5a de4b571 11fcc5a de4b571 11fcc5a de4b571 11fcc5a de4b571 11fcc5a de4b571 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
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`
}
}
|