Spaces:
Paused
Paused
| import { strict as assert } from "node:assert"; | |
| import { env } from "../config.js"; | |
| import { createResponse } from "../processing/request.js"; | |
| import { testers } from "./service-patterns.js"; | |
| import matchAction from "./match-action.js"; | |
| import { friendlyServiceName } from "./service-alias.js"; | |
| import bilibili from "./services/bilibili.js"; | |
| import reddit from "./services/reddit.js"; | |
| import twitter from "./services/twitter.js"; | |
| import youtube from "./services/youtube.js"; | |
| import vk from "./services/vk.js"; | |
| import ok from "./services/ok.js"; | |
| import tiktok from "./services/tiktok.js"; | |
| import tumblr from "./services/tumblr.js"; | |
| import vimeo from "./services/vimeo.js"; | |
| import soundcloud from "./services/soundcloud.js"; | |
| import instagram from "./services/instagram.js"; | |
| import vine from "./services/vine.js"; | |
| import pinterest from "./services/pinterest.js"; | |
| 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 snapchat from "./services/snapchat.js"; | |
| import loom from "./services/loom.js"; | |
| import facebook from "./services/facebook.js"; | |
| import bluesky from "./services/bluesky.js"; | |
| let freebind; | |
| export default async function({ host, patternMatch, params }) { | |
| const { url } = params; | |
| assert(url instanceof URL); | |
| let dispatcher, requestIP; | |
| if (env.freebindCIDR) { | |
| if (!freebind) { | |
| freebind = await import('freebind'); | |
| } | |
| requestIP = freebind.ip.random(env.freebindCIDR); | |
| dispatcher = freebind.dispatcherFromIP(requestIP, { strict: false }); | |
| } | |
| try { | |
| let r, | |
| isAudioOnly = params.downloadMode === "audio", | |
| isAudioMuted = params.downloadMode === "mute"; | |
| if (!testers[host]) { | |
| return createResponse("error", { | |
| code: "error.api.service.unsupported" | |
| }); | |
| } | |
| if (!(testers[host](patternMatch))) { | |
| return createResponse("error", { | |
| code: "error.api.link.unsupported", | |
| context: { | |
| service: friendlyServiceName(host), | |
| } | |
| }); | |
| } | |
| switch (host) { | |
| case "twitter": | |
| r = await twitter({ | |
| id: patternMatch.id, | |
| index: patternMatch.index - 1, | |
| toGif: !!params.twitterGif, | |
| alwaysProxy: params.alwaysProxy, | |
| dispatcher | |
| }); | |
| break; | |
| case "vk": | |
| r = await vk({ | |
| userId: patternMatch.userId, | |
| videoId: patternMatch.videoId, | |
| quality: params.videoQuality | |
| }); | |
| break; | |
| case "ok": | |
| r = await ok({ | |
| id: patternMatch.id, | |
| quality: params.videoQuality | |
| }); | |
| break; | |
| case "bilibili": | |
| r = await bilibili(patternMatch); | |
| break; | |
| case "youtube": | |
| let fetchInfo = { | |
| id: patternMatch.id.slice(0, 11), | |
| quality: params.videoQuality, | |
| format: params.youtubeVideoCodec, | |
| isAudioOnly, | |
| isAudioMuted, | |
| dubLang: params.youtubeDubLang, | |
| dispatcher | |
| } | |
| if (url.hostname === "music.youtube.com" || isAudioOnly) { | |
| fetchInfo.quality = "max"; | |
| fetchInfo.format = "vp9"; | |
| fetchInfo.isAudioOnly = true; | |
| fetchInfo.isAudioMuted = false; | |
| } | |
| r = await youtube(fetchInfo); | |
| break; | |
| case "reddit": | |
| r = await reddit({ | |
| sub: patternMatch.sub, | |
| id: patternMatch.id, | |
| user: patternMatch.user | |
| }); | |
| break; | |
| case "tiktok": | |
| r = await tiktok({ | |
| postId: patternMatch.postId, | |
| id: patternMatch.id, | |
| fullAudio: params.tiktokFullAudio, | |
| isAudioOnly, | |
| h265: params.tiktokH265, | |
| alwaysProxy: params.alwaysProxy, | |
| }); | |
| break; | |
| case "tumblr": | |
| r = await tumblr({ | |
| id: patternMatch.id, | |
| user: patternMatch.user, | |
| url | |
| }); | |
| break; | |
| case "vimeo": | |
| r = await vimeo({ | |
| id: patternMatch.id.slice(0, 11), | |
| password: patternMatch.password, | |
| quality: params.videoQuality, | |
| isAudioOnly, | |
| }); | |
| break; | |
| case "soundcloud": | |
| isAudioOnly = true; | |
| isAudioMuted = false; | |
| r = await soundcloud({ | |
| url, | |
| author: patternMatch.author, | |
| song: patternMatch.song, | |
| format: params.audioFormat, | |
| shortLink: patternMatch.shortLink || false, | |
| accessKey: patternMatch.accessKey || false | |
| }); | |
| break; | |
| case "instagram": | |
| r = await instagram({ | |
| ...patternMatch, | |
| quality: params.videoQuality, | |
| alwaysProxy: params.alwaysProxy, | |
| dispatcher | |
| }) | |
| break; | |
| case "vine": | |
| r = await vine({ | |
| id: patternMatch.id | |
| }); | |
| break; | |
| case "pinterest": | |
| r = await pinterest({ | |
| id: patternMatch.id, | |
| shortLink: patternMatch.shortLink || false | |
| }); | |
| break; | |
| case "streamable": | |
| r = await streamable({ | |
| id: patternMatch.id, | |
| quality: params.videoQuality, | |
| isAudioOnly, | |
| }); | |
| break; | |
| case "twitch": | |
| r = await twitch({ | |
| clipId: patternMatch.clip || false, | |
| quality: params.videoQuality, | |
| isAudioOnly, | |
| }); | |
| break; | |
| case "rutube": | |
| r = await rutube({ | |
| id: patternMatch.id, | |
| yappyId: patternMatch.yappyId, | |
| key: patternMatch.key, | |
| quality: params.videoQuality, | |
| isAudioOnly, | |
| }); | |
| break; | |
| case "dailymotion": | |
| r = await dailymotion(patternMatch); | |
| break; | |
| case "snapchat": | |
| r = await snapchat({ | |
| ...patternMatch, | |
| alwaysProxy: params.alwaysProxy, | |
| }); | |
| break; | |
| case "loom": | |
| r = await loom({ | |
| id: patternMatch.id | |
| }); | |
| break; | |
| case "facebook": | |
| r = await facebook({ | |
| ...patternMatch | |
| }); | |
| break; | |
| case "bsky": | |
| r = await bluesky({ | |
| ...patternMatch, | |
| alwaysProxy: params.alwaysProxy | |
| }); | |
| break; | |
| default: | |
| return createResponse("error", { | |
| code: "error.api.service.unsupported" | |
| }); | |
| } | |
| if (r.isAudioOnly) { | |
| isAudioOnly = true; | |
| isAudioMuted = false; | |
| } | |
| if (r.error && r.critical) { | |
| return createResponse("critical", { | |
| code: `error.api.${r.error}`, | |
| }) | |
| } | |
| if (r.error) { | |
| let context; | |
| switch(r.error) { | |
| case "content.too_long": | |
| context = { | |
| limit: env.durationLimit / 60, | |
| } | |
| break; | |
| case "fetch.fail": | |
| case "fetch.rate": | |
| case "fetch.critical": | |
| case "link.unsupported": | |
| case "content.video.unavailable": | |
| context = { | |
| service: friendlyServiceName(host), | |
| } | |
| break; | |
| } | |
| return createResponse("error", { | |
| code: `error.api.${r.error}`, | |
| context, | |
| }) | |
| } | |
| return matchAction({ | |
| r, | |
| host, | |
| audioFormat: params.audioFormat, | |
| isAudioOnly, | |
| isAudioMuted, | |
| disableMetadata: params.disableMetadata, | |
| filenameStyle: params.filenameStyle, | |
| twitterGif: params.twitterGif, | |
| requestIP, | |
| audioBitrate: params.audioBitrate, | |
| alwaysProxy: params.alwaysProxy, | |
| }) | |
| } catch { | |
| return createResponse("error", { | |
| code: "error.api.fetch.critical", | |
| context: { | |
| service: friendlyServiceName(host), | |
| } | |
| }) | |
| } | |
| } | |