From 05abf9ad3ec8928e9d639cc5a50a2e6c85bde089 Mon Sep 17 00:00:00 2001 From: wukko Date: Tue, 20 Aug 2024 21:10:37 +0600 Subject: [PATCH] api: update error codes in services, add more error codes where needed --- api/src/processing/match.js | 6 +-- api/src/processing/services/bilibili.js | 37 +++++++++---- api/src/processing/services/dailymotion.js | 19 ++++--- api/src/processing/services/facebook.js | 5 +- api/src/processing/services/instagram.js | 34 +++++++----- api/src/processing/services/loom.js | 4 +- api/src/processing/services/ok.js | 17 +++--- api/src/processing/services/pinterest.js | 6 +-- api/src/processing/services/reddit.js | 15 ++++-- api/src/processing/services/rutube.js | 21 +++++--- api/src/processing/services/snapchat.js | 24 +++++---- api/src/processing/services/soundcloud.js | 23 +++++--- api/src/processing/services/streamable.js | 6 +-- api/src/processing/services/tiktok.js | 11 ++-- api/src/processing/services/tumblr.js | 11 ++-- api/src/processing/services/twitch.js | 30 +++++++---- api/src/processing/services/twitter.js | 16 ++++-- api/src/processing/services/vimeo.js | 23 ++++---- api/src/processing/services/vine.js | 4 +- api/src/processing/services/vk.js | 31 +++++++---- api/src/processing/services/youtube.js | 61 +++++++++++++++------- 21 files changed, 263 insertions(+), 141 deletions(-) diff --git a/api/src/processing/match.js b/api/src/processing/match.js index 0d60754f..50ca4c23 100644 --- a/api/src/processing/match.js +++ b/api/src/processing/match.js @@ -241,14 +241,14 @@ export default async function(host, patternMatch, obj) { if (r.error && r.critical) { return createResponse("critical", { - code: r.error + code: `error.api.${r.error}`, }) } if (r.error) { return createResponse("error", { - code: r.error, - context: response?.context + code: `error.api.${r.error}`, + context: r?.context }) } diff --git a/api/src/processing/services/bilibili.js b/api/src/processing/services/bilibili.js index c562406a..c4762bad 100644 --- a/api/src/processing/services/bilibili.js +++ b/api/src/processing/services/bilibili.js @@ -30,22 +30,34 @@ function extractBestQuality(dashData) { async function com_download(id) { let html = await fetch(`https://bilibili.com/video/${id}`, { - headers: { "user-agent": genericUserAgent } - }).then(r => r.text()).catch(() => {}); - if (!html) return { error: 'ErrorCouldntFetch' }; + headers: { + "user-agent": genericUserAgent + } + }) + .then(r => r.text()) + .catch(() => {}); + + if (!html) { + return { error: "fetch.fail" } + } if (!(html.includes('')[0]); if (streamData.data.timelength > env.durationLimit * 1000) { - return { error: ['ErrorLengthLimit', env.durationLimit / 60] }; + return { + error: "content.too_long", + context: { + limit: env.durationLimit / 60 + } + } } const [ video, audio ] = extractBestQuality(streamData.data.dash); if (!video || !audio) { - return { error: 'ErrorEmptyDownload' }; + return { error: "fetch.empty" }; } return { @@ -66,7 +78,7 @@ async function tv_download(id) { const { data } = await fetch(url).then(a => a.json()); if (!data?.playurl?.video) { - return { error: 'ErrorEmptyDownload' }; + return { error: "fetch.empty" }; } const [ video, audio ] = extractBestQuality({ @@ -76,11 +88,16 @@ async function tv_download(id) { }); if (!video || !audio) { - return { error: 'ErrorEmptyDownload' }; + return { error: "fetch.empty" }; } if (video.duration > env.durationLimit * 1000) { - return { error: ['ErrorLengthLimit', env.durationLimit / 60] }; + return { + error: "content.too_long", + context: { + limit: env.durationLimit / 60 + } + } } return { @@ -101,5 +118,5 @@ export default async function({ comId, tvId, comShortLink }) { return tv_download(tvId); } - return { error: 'ErrorCouldntFetch' }; + return { error: "fetch.fail" }; } diff --git a/api/src/processing/services/dailymotion.js b/api/src/processing/services/dailymotion.js index c1fca95d..67d867cc 100644 --- a/api/src/processing/services/dailymotion.js +++ b/api/src/processing/services/dailymotion.js @@ -1,5 +1,5 @@ -import HLSParser from 'hls-parser'; -import { env } from '../../config.js'; +import HLSParser from "hls-parser"; +import { env } from "../../config.js"; let _token; @@ -31,7 +31,7 @@ const getToken = async () => { export default async function({ id }) { const token = await getToken(); - if (!token) return { error: 'ErrorSomethingWentWrong' }; + if (!token) return { error: "fetch.fail" }; const req = await fetch('https://graphql.api.dailymotion.com/', { @@ -70,20 +70,25 @@ export default async function({ id }) { const media = req?.data?.media; if (media?.__typename !== 'Video' || !media.hlsURL) { - return { error: 'ErrorEmptyDownload' } + return { error: "fetch.empty" } } if (media.duration > env.durationLimit) { - return { error: ['ErrorLengthLimit', env.durationLimit / 60] }; + return { + error: "content.too_long", + context: { + limit: env.durationLimit / 60 + } + } } const manifest = await fetch(media.hlsURL).then(r => r.text()).catch(() => {}); - if (!manifest) return { error: 'ErrorSomethingWentWrong' }; + if (!manifest) return { error: "fetch.fail" }; const bestQuality = HLSParser.parse(manifest).variants .filter(v => v.codecs.includes('avc1')) .reduce((a, b) => a.bandwidth > b.bandwidth ? a : b); - if (!bestQuality) return { error: 'ErrorEmptyDownload' } + if (!bestQuality) return { error: "fetch.empty" } const fileMetadata = { title: media.title, diff --git a/api/src/processing/services/facebook.js b/api/src/processing/services/facebook.js index 17dedab4..7bfd4751 100644 --- a/api/src/processing/services/facebook.js +++ b/api/src/processing/services/facebook.js @@ -33,7 +33,8 @@ export default async function({ id, shareType, shortLink }) { .then(r => r.text()) .catch(() => false); - if (!html) return { error: 'ErrorCouldntFetch' }; + if (!html && shortLink) return { error: "fetch.short_link" } + if (!html) return { error: "fetch.fail" }; const urls = []; const hd = html.match('"browser_native_hd_url":(".*?")'); @@ -43,7 +44,7 @@ export default async function({ id, shareType, shortLink }) { if (sd?.[1]) urls.push(JSON.parse(sd[1])); if (!urls.length) { - return { error: 'ErrorEmptyDownload' }; + return { error: "fetch.empty" }; } const baseFilename = `facebook_${id || shortLink}`; diff --git a/api/src/processing/services/instagram.js b/api/src/processing/services/instagram.js index 6ea05178..9a43a0d9 100644 --- a/api/src/processing/services/instagram.js +++ b/api/src/processing/services/instagram.js @@ -1,5 +1,5 @@ -import { createStream } from "../../stream/manage.js"; import { genericUserAgent } from "../../config.js"; +import { createStream } from "../../stream/manage.js"; import { getCookie, updateCookie } from "../cookie/manager.js"; const commonHeaders = { @@ -206,7 +206,7 @@ export default function(obj) { .map(e => { const type = e.video_versions ? "video" : "photo"; const imageUrl = e.image_versions2.candidates[0].url; - + let url = imageUrl; if (type === 'video') { const video = e.video_versions.reduce((a, b) => a.width * a.height < b.width * b.height ? b : a); @@ -246,7 +246,7 @@ export default function(obj) { let data, result; try { const cookie = getCookie('instagram'); - + const bearer = getCookie('instagram_bearer'); const token = bearer?.values()?.token; @@ -271,7 +271,7 @@ export default function(obj) { if (!data && cookie) data = await requestGQL(id, cookie); } catch {} - if (!data) return { error: 'ErrorCouldntFetch' }; + if (!data) return { error: "fetch.fail" }; if (data?.gql_data) { result = extractOldPost(data, id) @@ -280,7 +280,7 @@ export default function(obj) { } if (result) return result; - return { error: 'ErrorEmptyDownload' } + return { error: "fetch.empty" } } async function usernameToId(username, cookie) { @@ -295,11 +295,16 @@ export default function(obj) { async function getStory(username, id) { const cookie = getCookie('instagram'); - if (!cookie) return { error: 'ErrorUnsupported' }; + if (!cookie) return { + error: "link.unsupported", + context: { + service: "instagram" + } + } const userId = await usernameToId(username, cookie); - if (!userId) return { error: 'ErrorEmptyDownload' }; - + if (!userId) return { error: "fetch.empty" }; + const dtsgId = await findDtsgId(cookie); const url = new URL('https://www.instagram.com/api/graphql/'); @@ -320,8 +325,8 @@ export default function(obj) { } catch {} const item = media.items.find(m => m.pk === id); - if (!item) return { error: 'ErrorEmptyDownload' }; - + if (!item) return { error: "fetch.empty" }; + if (item.video_versions) { const video = item.video_versions.reduce((a, b) => a.width * a.height < b.width * b.height ? b : a) return { @@ -338,12 +343,17 @@ export default function(obj) { } } - return { error: 'ErrorUnsupported' }; + return { + error: "link.unsupported", + context: { + service: "instagram" + } + } } const { postId, storyId, username } = obj; if (postId) return getPost(postId); if (username && storyId) return getStory(username, storyId); - return { error: 'ErrorUnsupported' } + return { error: "fetch.fail" } } diff --git a/api/src/processing/services/loom.js b/api/src/processing/services/loom.js index ecb6c534..01f71e51 100644 --- a/api/src/processing/services/loom.js +++ b/api/src/processing/services/loom.js @@ -23,7 +23,7 @@ export default async function({ id }) { .then(r => r.status === 200 ? r.json() : false) .catch(() => {}); - if (!gql) return { error: 'ErrorEmptyDownload' }; + if (!gql) return { error: "fetch.empty" }; const videoUrl = gql?.url; @@ -35,5 +35,5 @@ export default async function({ id }) { } } - return { error: 'ErrorEmptyDownload' } + return { error: "fetch.empty" } } diff --git a/api/src/processing/services/ok.js b/api/src/processing/services/ok.js index 8f177043..afa3f0e8 100644 --- a/api/src/processing/services/ok.js +++ b/api/src/processing/services/ok.js @@ -19,26 +19,31 @@ export default async function(o) { headers: { "user-agent": genericUserAgent } }).then(r => r.text()).catch(() => {}); - if (!html) return { error: 'ErrorCouldntFetch' }; + if (!html) return { error: "fetch.fail" }; let videoData = html.match(/
/) ?.[1] ?.replaceAll(""", '"'); if (!videoData) { - return { error: 'ErrorEmptyDownload' }; + return { error: "fetch.empty" }; } videoData = JSON.parse(JSON.parse(videoData).flashvars.metadata); if (videoData.provider !== "UPLOADED_ODKL") - return { error: 'ErrorUnsupported' }; + return { error: "link.unsupported" }; if (videoData.movie.is_live) - return { error: 'ErrorLiveVideo' }; + return { error: "content.video.live" }; if (videoData.movie.duration > env.durationLimit) - return { error: ['ErrorLengthLimit', env.durationLimit / 60] }; + return { + error: "content.too_long", + context: { + limit: env.durationLimit / 60 + } + } let videos = videoData.videos.filter(v => !v.disallowed); let bestVideo = videos.find(v => resolutions[v.name] === quality) || videos[videos.length - 1]; @@ -61,5 +66,5 @@ export default async function(o) { } } - return { error: 'ErrorEmptyDownload' } + return { error: "fetch.empty" } } diff --git a/api/src/processing/services/pinterest.js b/api/src/processing/services/pinterest.js index 5a5f9438..efdc8cdb 100644 --- a/api/src/processing/services/pinterest.js +++ b/api/src/processing/services/pinterest.js @@ -12,13 +12,13 @@ export default async function(o) { .catch(() => {}); } if (id.includes("--")) id = id.split("--")[1]; - if (!id) return { error: 'ErrorCouldntFetch' }; + if (!id) return { error: "fetch.fail" }; let html = await fetch(`https://www.pinterest.com/pin/${id}/`, { headers: { "user-agent": genericUserAgent } }).then(r => r.text()).catch(() => {}); - if (!html) return { error: 'ErrorCouldntFetch' }; + if (!html) return { error: "fetch.fail" }; let videoLink = [...html.matchAll(videoRegex)] .map(([, link]) => link) @@ -39,5 +39,5 @@ export default async function(o) { isPhoto: true } - return { error: 'ErrorEmptyDownload' }; + return { error: "fetch.empty" }; } diff --git a/api/src/processing/services/reddit.js b/api/src/processing/services/reddit.js index 41f9efb4..cd712aa6 100644 --- a/api/src/processing/services/reddit.js +++ b/api/src/processing/services/reddit.js @@ -9,7 +9,7 @@ async function getAccessToken() { * you can get these by making a reddit app and * authenticating an account against reddit's oauth2 api * see: https://github.com/reddit-archive/reddit/wiki/OAuth2 - * + * * any additional cookie fields are managed by this code and you * should not touch them unless you know what you're doing. **/ const cookie = await getCookie('reddit'); @@ -67,7 +67,9 @@ export default async function(obj) { } ).then(r => r.json()).catch(() => {}); - if (!data || !Array.isArray(data)) return { error: 'ErrorCouldntFetch' }; + if (!data || !Array.isArray(data)) { + return { error: "fetch.fail" } + } data = data[0]?.data?.children[0]?.data; @@ -77,10 +79,15 @@ export default async function(obj) { } if (!data.secure_media?.reddit_video) - return { error: 'ErrorEmptyDownload' }; + return { error: "fetch.empty" }; if (data.secure_media?.reddit_video?.duration > env.durationLimit) - return { error: ['ErrorLengthLimit', env.durationLimit / 60] }; + return { + error: "content.too_long", + context: { + limit: env.durationLimit / 60 + } + } let audio = false, video = data.secure_media?.reddit_video?.fallback_url?.split('?')[0], diff --git a/api/src/processing/services/rutube.js b/api/src/processing/services/rutube.js index c162eaf5..705c455e 100644 --- a/api/src/processing/services/rutube.js +++ b/api/src/processing/services/rutube.js @@ -1,7 +1,7 @@ -import HLS from 'hls-parser'; +import HLS from "hls-parser"; import { env } from "../../config.js"; -import { cleanString } from '../../misc/utils.js'; +import { cleanString } from "../../misc/utils.js"; async function requestJSON(url) { try { @@ -18,7 +18,7 @@ export default async function(obj) { `https://rutube.ru/pangolin/api/web/yappy/yappypage/?client=wdp&videoId=${obj.yappyId}&page=1&page_size=15` ) const yappyURL = yappy?.results?.find(r => r.id === obj.yappyId)?.link; - if (!yappyURL) return { error: 'ErrorEmptyDownload' }; + if (!yappyURL) return { error: "fetch.empty" }; return { urls: yappyURL, @@ -33,19 +33,24 @@ export default async function(obj) { if (obj.key) requestURL.searchParams.set('p', obj.key); const play = await requestJSON(requestURL); - if (!play) return { error: 'ErrorCouldntFetch' }; + if (!play) return { error: "fetch.fail" }; - if (play.detail || !play.video_balancer) return { error: 'ErrorEmptyDownload' }; - if (play.live_streams?.hls) return { error: 'ErrorLiveVideo' }; + if (play.detail || !play.video_balancer) return { error: "fetch.empty" }; + if (play.live_streams?.hls) return { error: "content.video.live" }; if (play.duration > env.durationLimit * 1000) - return { error: ['ErrorLengthLimit', env.durationLimit / 60] }; + return { + error: "content.too_long", + context: { + limit: env.durationLimit / 60 + } + } let m3u8 = await fetch(play.video_balancer.m3u8) .then(r => r.text()) .catch(() => {}); - if (!m3u8) return { error: 'ErrorCouldntFetch' }; + if (!m3u8) return { error: "fetch.fail" }; m3u8 = HLS.parse(m3u8).variants; diff --git a/api/src/processing/services/snapchat.js b/api/src/processing/services/snapchat.js index d8b00770..95eb368e 100644 --- a/api/src/processing/services/snapchat.js +++ b/api/src/processing/services/snapchat.js @@ -1,16 +1,16 @@ +import { extract, normalizeURL } from "../url.js"; import { genericUserAgent } from "../../config.js"; import { getRedirectingURL } from "../../misc/utils.js"; -import { extract, normalizeURL } from "../url.js"; const SPOTLIGHT_VIDEO_REGEX = //; const NEXT_DATA_REGEX = /