mirror of
https://github.com/wukko/cobalt.git
synced 2025-06-12 21:27:39 +02:00
Merge branch 'current' into instagram-stories
This commit is contained in:
30
src/modules/processing/services/rutube.js
Normal file
30
src/modules/processing/services/rutube.js
Normal file
@ -0,0 +1,30 @@
|
||||
import HLS from 'hls-parser';
|
||||
import { maxVideoDuration } from "../../config.js";
|
||||
|
||||
export default async function(obj) {
|
||||
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 });
|
||||
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.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));
|
||||
|
||||
let bestQuality = m3u8[0];
|
||||
if (Number(quality) < bestQuality.resolution.height) {
|
||||
bestQuality = m3u8.find((i) => (Number(quality) === i["resolution"].height));
|
||||
}
|
||||
|
||||
return {
|
||||
urls: bestQuality.uri,
|
||||
isM3U8: true,
|
||||
audioFilename: `rutube_${play.id}_audio`,
|
||||
filename: `rutube_${play.id}_${bestQuality.resolution.width}x${bestQuality.resolution.height}.mp4`
|
||||
}
|
||||
}
|
76
src/modules/processing/services/twitch.js
Normal file
76
src/modules/processing/services/twitch.js
Normal file
@ -0,0 +1,76 @@
|
||||
import { maxVideoDuration } from "../../config.js";
|
||||
|
||||
const gqlURL = "https://gql.twitch.tv/gql";
|
||||
const clientIdHead = { "client-id": "kimne78kx3ncx6brgo4mv6wki5h1ko" };
|
||||
|
||||
export default async function (obj) {
|
||||
let req_metadata = await fetch(gqlURL, {
|
||||
method: "POST",
|
||||
headers: clientIdHead,
|
||||
body: JSON.stringify({
|
||||
query: `{
|
||||
clip(slug: "${obj.clipId}") {
|
||||
broadcaster {
|
||||
login
|
||||
}
|
||||
createdAt
|
||||
curator {
|
||||
login
|
||||
}
|
||||
durationSeconds
|
||||
id
|
||||
medium: thumbnailURL(width: 480, height: 272)
|
||||
title
|
||||
videoQualities {
|
||||
quality
|
||||
sourceURL
|
||||
}
|
||||
}
|
||||
}`
|
||||
})
|
||||
}).then((r) => { return r.status === 200 ? r.json() : false; }).catch(() => { return false });
|
||||
if (!req_metadata) return { error: 'ErrorCouldntFetch' };
|
||||
|
||||
let clipMetadata = req_metadata.data.clip;
|
||||
|
||||
if (clipMetadata.durationSeconds > maxVideoDuration / 1000) return { error: ['ErrorLengthLimit', maxVideoDuration / 60000] };
|
||||
if (!clipMetadata.videoQualities || !clipMetadata.broadcaster) return { error: 'ErrorEmptyDownload' };
|
||||
|
||||
let req_token = await fetch(gqlURL, {
|
||||
method: "POST",
|
||||
headers: clientIdHead,
|
||||
body: JSON.stringify([
|
||||
{
|
||||
"operationName": "VideoAccessToken_Clip",
|
||||
"variables": {
|
||||
"slug": obj.clipId
|
||||
},
|
||||
"extensions": {
|
||||
"persistedQuery": {
|
||||
"version": 1,
|
||||
"sha256Hash": "36b89d2507fce29e5ca551df756d27c1cfe079e2609642b4390aa4c35796eb11"
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
}).then((r) => { return r.status === 200 ? r.json() : false; }).catch(() => { return false });
|
||||
|
||||
if (!req_token) return { error: 'ErrorCouldntFetch' };
|
||||
|
||||
let formats = clipMetadata.videoQualities;
|
||||
let format = formats.find(f => f.quality === obj.quality) || formats[0];
|
||||
|
||||
return {
|
||||
type: "bridge",
|
||||
urls: `${format.sourceURL}?${new URLSearchParams({
|
||||
sig: req_token[0].data.clip.playbackAccessToken.signature,
|
||||
token: req_token[0].data.clip.playbackAccessToken.value
|
||||
})}`,
|
||||
fileMetadata: {
|
||||
title: clipMetadata.title,
|
||||
artist: `Twitch Clip by @${clipMetadata.broadcaster.login}, clipped by @${clipMetadata.curator.login}`,
|
||||
},
|
||||
filename: `twitchclip_${clipMetadata.id}_${format.quality}p.mp4`,
|
||||
audioFilename: `twitchclip_${clipMetadata.id}_audio`
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user