diff --git a/api/src/processing/match.js b/api/src/processing/match.js index 1265297c..ab2587a8 100644 --- a/api/src/processing/match.js +++ b/api/src/processing/match.js @@ -209,11 +209,19 @@ export default async function({ host, patternMatch, params, authType }) { break; case "twitch": - r = await twitch({ - clipId: patternMatch.clip || false, - quality: params.videoQuality, - isAudioOnly, - }); + if (url.pathname.includes('/videos/')) { + r = await twitch({ + clipId: false, + type: 'vod' + }); + } + if (url.pathname.includes('/clip/')) { + r = await twitch({ + clipId: patternMatch.clip || false, + quality: params.videoQuality, + isAudioOnly, + }); + } break; case "rutube": diff --git a/api/src/processing/service-config.js b/api/src/processing/service-config.js index 55946026..c91859da 100644 --- a/api/src/processing/service-config.js +++ b/api/src/processing/service-config.js @@ -163,7 +163,10 @@ export const services = { subdomains: "*", }, twitch: { - patterns: [":channel/clip/:clip"], + patterns: [ + ":channel/clip/:clip", + ":type/:videoId" + ], tld: "tv", subdomains: ["clips", "www", "m"], }, diff --git a/api/src/processing/service-patterns.js b/api/src/processing/service-patterns.js index 4ae138c2..51526330 100644 --- a/api/src/processing/service-patterns.js +++ b/api/src/processing/service-patterns.js @@ -50,7 +50,8 @@ export const testers = { || (pattern.id?.length < 21 && pattern.user?.length <= 32), "twitch": pattern => - pattern.channel && pattern.clip?.length <= 100, + pattern.channel && pattern.clip?.length <= 100 + || pattern.type === 'videos' && pattern.videoId?.length >= 1, "twitter": pattern => pattern.id?.length < 20, diff --git a/api/src/processing/services/twitch.js b/api/src/processing/services/twitch.js index 4b9d4551..a9da44de 100644 --- a/api/src/processing/services/twitch.js +++ b/api/src/processing/services/twitch.js @@ -4,6 +4,8 @@ const gqlURL = "https://gql.twitch.tv/gql"; const clientIdHead = { "client-id": "kimne78kx3ncx6brgo4mv6wki5h1ko" }; export default async function (obj) { + if (obj.type === 'vod') return { error: "twitch.vod_not_supported" }; + const req_metadata = await fetch(gqlURL, { method: "POST", headers: clientIdHead, diff --git a/api/src/processing/url.js b/api/src/processing/url.js index 5f93f1d0..1abb17f8 100644 --- a/api/src/processing/url.js +++ b/api/src/processing/url.js @@ -51,6 +51,9 @@ function aliasURL(url) { if (url.hostname === 'clips.twitch.tv' && parts.length >= 2) { url = new URL(`https://twitch.tv/_/clip/${parts[1]}`); } + if ((url.hostname === 'www.twitch.tv' && url.pathname.includes('/videos/')) && parts.length === 3) { + url = new URL(`https://twitch.tv/videos/${parts[2]}`); + } break; case "bilibili": diff --git a/web/i18n/en/error/api.json b/web/i18n/en/error/api.json index 9e922745..b1bb98c7 100644 --- a/web/i18n/en/error/api.json +++ b/web/i18n/en/error/api.json @@ -59,5 +59,7 @@ "youtube.api_error": "youtube updated something about its api and i couldn't get any info about this video. try again in a few seconds, but if this issue sticks, please report it!", "youtube.temporary_disabled": "youtube downloading is temporarily disabled due to restrictions from youtube's side. we're already looking for ways to go around them.\n\nwe apologize for the inconvenience and are doing our best to restore this functionality. check cobalt's socials or github for timely updates!", "youtube.drm": "this youtube video is protected by widevine DRM, so i can't download it. try a different link!", - "youtube.no_session_tokens": "couldn't get required session tokens for youtube. this may be caused by a restriction on youtube's side. try again in a few seconds, but if this issue sticks, please report it!" + "youtube.no_session_tokens": "couldn't get required session tokens for youtube. this may be caused by a restriction on youtube's side. try again in a few seconds, but if this issue sticks, please report it!", + + "twitch.vod_not_supported": "the link you provided is a twitch vod, which is not supported! only twitch clips are supported." }