diff --git a/src/modules/processing/match.js b/src/modules/processing/match.js index 6f715ef7..d46c30d9 100644 --- a/src/modules/processing/match.js +++ b/src/modules/processing/match.js @@ -151,6 +151,7 @@ export default async function(host, patternMatch, url, lang, obj) { case "rutube": r = await rutube({ id: patternMatch.id, + yappyId: patternMatch.yappyId, quality: obj.vQuality, isAudioOnly: isAudioOnly }); diff --git a/src/modules/processing/services/rutube.js b/src/modules/processing/services/rutube.js index af99a31d..672d9f59 100644 --- a/src/modules/processing/services/rutube.js +++ b/src/modules/processing/services/rutube.js @@ -1,18 +1,46 @@ import HLS from 'hls-parser'; + import { maxVideoDuration } from "../../config.js"; import { cleanString } from '../../sub/utils.js'; +async function requestJSON(url) { + return await fetch(url) + .then((r) => { return r.json() }) + .catch(() => {}); +} + export default async function(obj) { + if (obj.yappyId) { + let yappy = await requestJSON( + `https://rutube.ru/pangolin/api/web/yappy/yappypage/?client=wdp&videoId=${obj.yappyId}&page=1&page_size=15` + ) + let yappyURL = yappy?.results?.find(r => r.id === obj.yappyId)?.link; + if (!yappyURL) return { error: 'ErrorEmptyDownload' }; + + return { + urls: yappyURL, + filename: `rutube_yappy_${obj.yappyId}.mp4`, + audioFilename: `rutube_yappy_${obj.yappyId}_audio` + } + } + let quality = obj.quality === "max" ? "9000" : obj.quality; - let play = await fetch(`https://rutube.ru/api/play/options/${obj.id}/?no_404=true&referer&pver=v2`).then((r) => { return r.json() }).catch(() => { return false }); + + let play = await requestJSON( + `https://rutube.ru/api/play/options/${obj.id}/?no_404=true&referer&pver=v2` + ) if (!play) return { error: 'ErrorCouldntFetch' }; - if ("hls" in play.live_streams) return { error: 'ErrorLiveVideo' }; - if (!play.video_balancer || play.detail) return { error: 'ErrorEmptyDownload' }; + if (play.detail || !play.video_balancer) return { error: 'ErrorEmptyDownload' }; + if (play.live_streams?.hls) return { error: 'ErrorLiveVideo' }; + + if (play.duration > maxVideoDuration) + return { error: ['ErrorLengthLimit', maxVideoDuration / 60000] }; + + let m3u8 = await fetch(play.video_balancer.m3u8) + .then((r) => { return r.text() }) + .catch(() => {}); - if (play.duration > maxVideoDuration) return { error: ['ErrorLengthLimit', maxVideoDuration / 60000] }; - - let m3u8 = await fetch(play.video_balancer.m3u8).then((r) => { return r.text() }).catch(() => { return false }); if (!m3u8) return { error: 'ErrorCouldntFetch' }; m3u8 = HLS.parse(m3u8).variants.sort((a, b) => Number(b.bandwidth) - Number(a.bandwidth)); @@ -21,6 +49,7 @@ export default async function(obj) { if (Number(quality) < bestQuality.resolution.height) { bestQuality = m3u8.find((i) => (Number(quality) === i["resolution"].height)); } + let fileMetadata = { title: cleanString(play.title.trim()), artist: cleanString(play.author.name.trim()), @@ -31,7 +60,7 @@ export default async function(obj) { isM3U8: true, filenameAttributes: { service: "rutube", - id: play.id, + id: obj.id, title: fileMetadata.title, author: fileMetadata.artist, resolution: `${bestQuality.resolution.width}x${bestQuality.resolution.height}`, diff --git a/src/modules/processing/servicesConfig.json b/src/modules/processing/servicesConfig.json index 1a51d17a..8972ac46 100644 --- a/src/modules/processing/servicesConfig.json +++ b/src/modules/processing/servicesConfig.json @@ -110,7 +110,7 @@ "rutube": { "alias": "rutube videos", "tld": "ru", - "patterns": ["video/:id", "play/embed/:id"], + "patterns": ["video/:id", "play/embed/:id", "shorts/:id", "yappy/:yappyId"], "enabled": true }, "dailymotion": { diff --git a/src/modules/processing/servicesPatternTesters.js b/src/modules/processing/servicesPatternTesters.js index f4dee15b..bdd691d5 100644 --- a/src/modules/processing/servicesPatternTesters.js +++ b/src/modules/processing/servicesPatternTesters.js @@ -19,7 +19,7 @@ export const testers = { patternMatch.sub?.length <= 22 && patternMatch.id?.length <= 10, "rutube": (patternMatch) => - patternMatch.id?.length === 32, + patternMatch.id?.length === 32 || patternMatch.yappyId?.length === 32, "soundcloud": (patternMatch) => (patternMatch.author?.length <= 255 && patternMatch.song?.length <= 255)