mirror of
https://github.com/wukko/cobalt.git
synced 2025-01-12 11:52:12 +01:00
rutube: add support for shorts and yappy (#471)
* rutube: add support for shorts and yappy * tests: add rutube yappy and shorts tests Closes #465 Closes #466
This commit is contained in:
parent
8f27c86a43
commit
f20f87bd1d
5 changed files with 56 additions and 9 deletions
|
@ -151,6 +151,7 @@ export default async function(host, patternMatch, url, lang, obj) {
|
||||||
case "rutube":
|
case "rutube":
|
||||||
r = await rutube({
|
r = await rutube({
|
||||||
id: patternMatch.id,
|
id: patternMatch.id,
|
||||||
|
yappyId: patternMatch.yappyId,
|
||||||
quality: obj.vQuality,
|
quality: obj.vQuality,
|
||||||
isAudioOnly: isAudioOnly
|
isAudioOnly: isAudioOnly
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,18 +1,47 @@
|
||||||
import HLS from 'hls-parser';
|
import HLS from 'hls-parser';
|
||||||
|
|
||||||
import { maxVideoDuration } from "../../config.js";
|
import { maxVideoDuration } from "../../config.js";
|
||||||
import { cleanString } from '../../sub/utils.js';
|
import { cleanString } from '../../sub/utils.js';
|
||||||
|
|
||||||
|
async function requestJSON(url) {
|
||||||
|
try {
|
||||||
|
const r = await fetch(url);
|
||||||
|
return await r.json();
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
export default async function(obj) {
|
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 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 (!play) return { error: 'ErrorCouldntFetch' };
|
||||||
|
|
||||||
if ("hls" in play.live_streams) return { error: 'ErrorLiveVideo' };
|
if (play.detail || !play.video_balancer) return { error: 'ErrorEmptyDownload' };
|
||||||
if (!play.video_balancer || play.detail) return { error: 'ErrorEmptyDownload' };
|
if (play.live_streams?.hls) return { error: 'ErrorLiveVideo' };
|
||||||
|
|
||||||
if (play.duration > maxVideoDuration) return { error: ['ErrorLengthLimit', maxVideoDuration / 60000] };
|
if (play.duration > maxVideoDuration)
|
||||||
|
return { error: ['ErrorLengthLimit', maxVideoDuration / 60000] };
|
||||||
|
|
||||||
|
let m3u8 = await fetch(play.video_balancer.m3u8)
|
||||||
|
.then(r => r.text())
|
||||||
|
.catch(() => {});
|
||||||
|
|
||||||
let m3u8 = await fetch(play.video_balancer.m3u8).then((r) => { return r.text() }).catch(() => { return false });
|
|
||||||
if (!m3u8) return { error: 'ErrorCouldntFetch' };
|
if (!m3u8) return { error: 'ErrorCouldntFetch' };
|
||||||
|
|
||||||
m3u8 = HLS.parse(m3u8).variants.sort((a, b) => Number(b.bandwidth) - Number(a.bandwidth));
|
m3u8 = HLS.parse(m3u8).variants.sort((a, b) => Number(b.bandwidth) - Number(a.bandwidth));
|
||||||
|
@ -21,6 +50,7 @@ export default async function(obj) {
|
||||||
if (Number(quality) < bestQuality.resolution.height) {
|
if (Number(quality) < bestQuality.resolution.height) {
|
||||||
bestQuality = m3u8.find((i) => (Number(quality) === i["resolution"].height));
|
bestQuality = m3u8.find((i) => (Number(quality) === i["resolution"].height));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fileMetadata = {
|
let fileMetadata = {
|
||||||
title: cleanString(play.title.trim()),
|
title: cleanString(play.title.trim()),
|
||||||
artist: cleanString(play.author.name.trim()),
|
artist: cleanString(play.author.name.trim()),
|
||||||
|
@ -31,7 +61,7 @@ export default async function(obj) {
|
||||||
isM3U8: true,
|
isM3U8: true,
|
||||||
filenameAttributes: {
|
filenameAttributes: {
|
||||||
service: "rutube",
|
service: "rutube",
|
||||||
id: play.id,
|
id: obj.id,
|
||||||
title: fileMetadata.title,
|
title: fileMetadata.title,
|
||||||
author: fileMetadata.artist,
|
author: fileMetadata.artist,
|
||||||
resolution: `${bestQuality.resolution.width}x${bestQuality.resolution.height}`,
|
resolution: `${bestQuality.resolution.width}x${bestQuality.resolution.height}`,
|
||||||
|
|
|
@ -110,7 +110,7 @@
|
||||||
"rutube": {
|
"rutube": {
|
||||||
"alias": "rutube videos",
|
"alias": "rutube videos",
|
||||||
"tld": "ru",
|
"tld": "ru",
|
||||||
"patterns": ["video/:id", "play/embed/:id"],
|
"patterns": ["video/:id", "play/embed/:id", "shorts/:id", "yappy/:yappyId"],
|
||||||
"enabled": true
|
"enabled": true
|
||||||
},
|
},
|
||||||
"dailymotion": {
|
"dailymotion": {
|
||||||
|
|
|
@ -19,7 +19,7 @@ export const testers = {
|
||||||
patternMatch.sub?.length <= 22 && patternMatch.id?.length <= 10,
|
patternMatch.sub?.length <= 22 && patternMatch.id?.length <= 10,
|
||||||
|
|
||||||
"rutube": (patternMatch) =>
|
"rutube": (patternMatch) =>
|
||||||
patternMatch.id?.length === 32,
|
patternMatch.id?.length === 32 || patternMatch.yappyId?.length === 32,
|
||||||
|
|
||||||
"soundcloud": (patternMatch) =>
|
"soundcloud": (patternMatch) =>
|
||||||
(patternMatch.author?.length <= 255 && patternMatch.song?.length <= 255)
|
(patternMatch.author?.length <= 255 && patternMatch.song?.length <= 255)
|
||||||
|
|
|
@ -1061,6 +1061,22 @@
|
||||||
"code": 200,
|
"code": 200,
|
||||||
"status": "stream"
|
"status": "stream"
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
"name": "yappy",
|
||||||
|
"url": "https://rutube.ru/yappy/f1771e86294748eea8ecb2ac88e55740/",
|
||||||
|
"params": {},
|
||||||
|
"expected": {
|
||||||
|
"code": 200,
|
||||||
|
"status": "stream"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"name": "shorts",
|
||||||
|
"url": "https://rutube.ru/shorts/935c1afafd0e7d52836d671967d53dac/",
|
||||||
|
"params": {},
|
||||||
|
"expected": {
|
||||||
|
"code": 200,
|
||||||
|
"status": "stream"
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
"name": "vertical video (isAudioOnly)",
|
"name": "vertical video (isAudioOnly)",
|
||||||
"url": "https://rutube.ru/video/18a281399b96f9184c647455a86f6724/",
|
"url": "https://rutube.ru/video/18a281399b96f9184c647455a86f6724/",
|
||||||
|
|
Loading…
Reference in a new issue