From e4d42fa86a7a2092af69e6dacd308a150421cea6 Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 29 May 2024 13:12:52 +0600 Subject: [PATCH] processing: add loom support (#530) --- README.md | 5 ++- src/modules/processing/match.js | 6 +++ src/modules/processing/matchActionDecider.js | 1 + src/modules/processing/services/loom.js | 39 +++++++++++++++++++ src/modules/processing/servicesConfig.json | 7 +++- .../processing/servicesPatternTesters.js | 7 +++- src/test/tests.json | 29 ++++++++++++++ 7 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 src/modules/processing/services/loom.js diff --git a/README.md b/README.md index c67631be..f4c9a701 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,8 @@ this list is not final and keeps expanding over time. if support for a service y | bilibili.com & bilibili.tv | ✅ | ✅ | ✅ | ➖ | ➖ | | dailymotion | ✅ | ✅ | ✅ | ✅ | ✅ | | instagram posts & reels | ✅ | ✅ | ✅ | ➖ | ➖ | -| ok video | ✅ | ❌ | ❌ | ✅ | ✅ | +| loom | ✅ | ❌ | ✅ | ✅ | ➖ | +| ok video | ✅ | ❌ | ✅ | ✅ | ✅ | | pinterest | ✅ | ✅ | ✅ | ➖ | ➖ | | reddit | ✅ | ✅ | ✅ | ❌ | ❌ | | rutube | ✅ | ✅ | ✅ | ✅ | ✅ | @@ -31,7 +32,7 @@ this list is not final and keeps expanding over time. if support for a service y | twitter/x | ✅ | ✅ | ✅ | ➖ | ➖ | | vimeo | ✅ | ✅ | ✅ | ✅ | ✅ | | vine archive | ✅ | ✅ | ✅ | ➖ | ➖ | -| vk videos & clips | ✅ | ❌ | ❌ | ✅ | ✅ | +| vk videos & clips | ✅ | ❌ | ✅ | ✅ | ✅ | | youtube videos, shorts & music | ✅ | ✅ | ✅ | ✅ | ✅ | | emoji | meaning | diff --git a/src/modules/processing/match.js b/src/modules/processing/match.js index c3091709..3e38c4db 100644 --- a/src/modules/processing/match.js +++ b/src/modules/processing/match.js @@ -24,6 +24,7 @@ import streamable from "./services/streamable.js"; import twitch from "./services/twitch.js"; import rutube from "./services/rutube.js"; import dailymotion from "./services/dailymotion.js"; +import loom from "./services/loom.js"; let freebind; @@ -187,6 +188,11 @@ export default async function(host, patternMatch, lang, obj) { case "dailymotion": r = await dailymotion(patternMatch); break; + case "loom": + r = await loom({ + id: patternMatch.id + }); + break; default: return createResponse("error", { t: loc(lang, 'ErrorUnsupported') diff --git a/src/modules/processing/matchActionDecider.js b/src/modules/processing/matchActionDecider.js index 42a35f1a..f7ed3da9 100644 --- a/src/modules/processing/matchActionDecider.js +++ b/src/modules/processing/matchActionDecider.js @@ -129,6 +129,7 @@ export default function(r, host, userFormat, isAudioOnly, lang, isAudioMuted, di case "tumblr": case "pinterest": case "streamable": + case "loom": responseType = "redirect"; break; } diff --git a/src/modules/processing/services/loom.js b/src/modules/processing/services/loom.js new file mode 100644 index 00000000..ecb6c534 --- /dev/null +++ b/src/modules/processing/services/loom.js @@ -0,0 +1,39 @@ +import { genericUserAgent } from "../../config.js"; + +export default async function({ 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", + }, + body: JSON.stringify({ + force_original: false, + password: null, + anonID: null, + deviceID: null + }) + }) + .then(r => r.status === 200 ? r.json() : false) + .catch(() => {}); + + if (!gql) return { error: 'ErrorEmptyDownload' }; + + const videoUrl = gql?.url; + + if (videoUrl?.includes('.mp4?')) { + return { + urls: videoUrl, + filename: `loom_${id}.mp4`, + audioFilename: `loom_${id}_audio` + } + } + + return { error: 'ErrorEmptyDownload' } +} diff --git a/src/modules/processing/servicesConfig.json b/src/modules/processing/servicesConfig.json index c0a11817..ae4cd9f0 100644 --- a/src/modules/processing/servicesConfig.json +++ b/src/modules/processing/servicesConfig.json @@ -1,5 +1,5 @@ { - "audioIgnore": ["vk", "ok"], + "audioIgnore": ["vk", "ok", "loom"], "hlsExceptions": ["dailymotion", "vimeo", "rutube"], "config": { "bilibili": { @@ -112,6 +112,11 @@ "alias": "dailymotion videos", "patterns": ["video/:id"], "enabled": true + }, + "loom": { + "alias": "loom videos", + "patterns": ["share/:id"], + "enabled": true } } } diff --git a/src/modules/processing/servicesPatternTesters.js b/src/modules/processing/servicesPatternTesters.js index a4770c17..ddeea31f 100644 --- a/src/modules/processing/servicesPatternTesters.js +++ b/src/modules/processing/servicesPatternTesters.js @@ -8,7 +8,10 @@ export const testers = { "instagram": (patternMatch) => patternMatch.postId?.length <= 12 || (patternMatch.username?.length <= 30 && patternMatch.storyId?.length <= 24), - + + "loom": (patternMatch) => + patternMatch.id?.length <= 32, + "ok": (patternMatch) => patternMatch.id?.length <= 16, @@ -29,7 +32,7 @@ export const testers = { "streamable": (patternMatch) => patternMatch.id?.length === 6, - + "tiktok": (patternMatch) => patternMatch.postId?.length <= 21 || patternMatch.id?.length <= 13, diff --git a/src/test/tests.json b/src/test/tests.json index 36044cde..501ac2c0 100644 --- a/src/test/tests.json +++ b/src/test/tests.json @@ -1131,5 +1131,34 @@ "code": 200, "status": "stream" } + }], + "loom": [{ + "name": "1080p video", + "url": "https://www.loom.com/share/313bf71d20ca47b2a35b6634cefdb761", + "params": {}, + "expected": { + "code": 200, + "status": "redirect" + } + }, { + "name": "1080p video (muted)", + "url": "https://www.loom.com/share/313bf71d20ca47b2a35b6634cefdb761", + "params": { + "isAudioMuted": true + }, + "expected": { + "code": 200, + "status": "stream" + } + }, { + "name": "1080p video (audio only)", + "url": "https://www.loom.com/share/313bf71d20ca47b2a35b6634cefdb761", + "params": { + "isAudioOnly": true + }, + "expected": { + "code": 200, + "status": "stream" + } }] }