import HLS from "hls-parser"; import { env } from "../../config.js"; async function requestJSON(url) { try { const r = await fetch(url); return await r.json(); } catch {} } const delta = (a, b) => Math.abs(a - b); export default async function(obj) { if (obj.yappyId) { const yappy = await requestJSON( `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: "fetch.empty" }; return { urls: yappyURL, filename: `rutube_yappy_${obj.yappyId}.mp4`, audioFilename: `rutube_yappy_${obj.yappyId}_audio` } } const quality = Number(obj.quality) || 9000; const requestURL = new URL(`https://rutube.ru/api/play/options/${obj.id}/?no_404=true&referer&pver=v2`); if (obj.key) requestURL.searchParams.set('p', obj.key); const play = await requestJSON(requestURL); if (!play) return { error: "fetch.fail" }; if (play.detail?.type === "blocking_rule") { return { error: "content.video.region" }; } 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: "content.too_long" }; let m3u8 = await fetch(play.video_balancer.m3u8) .then(r => r.text()) .catch(() => {}); if (!m3u8) return { error: "fetch.fail" }; m3u8 = HLS.parse(m3u8).variants; const matchingQuality = m3u8.reduce((prev, next) => { const diff = { prev: delta(quality, prev.resolution.height), next: delta(quality, next.resolution.height) }; return diff.prev < diff.next ? prev : next; }); const fileMetadata = { title: play.title.trim(), artist: play.author.name.trim(), } return { urls: matchingQuality.uri, isHLS: true, filenameAttributes: { service: "rutube", id: obj.id, title: fileMetadata.title, author: fileMetadata.artist, resolution: `${matchingQuality.resolution.width}x${matchingQuality.resolution.height}`, qualityLabel: `${matchingQuality.resolution.height}p`, extension: "mp4" }, fileMetadata: fileMetadata } }