From 0852ade1be4c84a188e35f8ee2b962fde35292e7 Mon Sep 17 00:00:00 2001 From: dumbmoron Date: Thu, 15 Feb 2024 18:11:18 +0000 Subject: [PATCH] bilibili: add support for bilibili.tv links closes #319 --- src/modules/processing/services/bilibili.js | 101 ++++++++++++++---- src/modules/processing/servicesConfig.json | 7 +- .../processing/servicesPatternTesters.js | 5 +- src/modules/processing/url.js | 5 + src/test/tests.json | 9 ++ 5 files changed, 104 insertions(+), 23 deletions(-) diff --git a/src/modules/processing/services/bilibili.js b/src/modules/processing/services/bilibili.js index 5664e8cb..6da110bf 100644 --- a/src/modules/processing/services/bilibili.js +++ b/src/modules/processing/services/bilibili.js @@ -1,42 +1,105 @@ import { genericUserAgent, maxVideoDuration } from "../../config.js"; -// TO-DO: quality picking, bilibili.tv support, and higher quality downloads (currently requires an account) -export default async function({ id, shortLink }) { - if (shortLink) { - id = await fetch(`https://b23.tv/${shortLink}`, { redirect: 'manual' }) +// TO-DO: higher quality downloads (currently requires an account) + +function com_resolveShortlink(shortId) { + return fetch(`https://b23.tv/${shortId}`, { redirect: 'manual' }) .then(r => r.status > 300 && r.status < 400 && r.headers.get('location')) .then(url => { + if (!url) return; const path = new URL(url).pathname; if (path.startsWith('/video/')) return path.split('/')[2]; }) .catch(() => {}) - } +} - if (!id) { - return { error: 'ErrorCouldntFetch' }; - } +function getBest(content) { + return content?.filter(v => v.baseUrl || v.url) + .map(v => (v.baseUrl = v.baseUrl || v.url, v)) + .reduce((a, b) => a?.bandwidth > b?.bandwidth ? a : b); +} +function extractBestQuality(dashData) { + const bestVideo = getBest(dashData.video), + bestAudio = getBest(dashData.audio); + + if (!bestVideo || !bestAudio) return []; + return [ bestVideo, bestAudio ]; +} + +async function com_download(id) { let html = await fetch(`https://bilibili.com/video/${id}`, { headers: { "user-agent": genericUserAgent } }).then((r) => { return r.text() }).catch(() => { return false }); if (!html) return { error: 'ErrorCouldntFetch' }; - if (!(html.includes('')[0]); - if (streamData.data.timelength > maxVideoDuration) return { error: ['ErrorLengthLimit', maxVideoDuration / 60000] }; + if (streamData.data.timelength > maxVideoDuration) { + return { error: ['ErrorLengthLimit', maxVideoDuration / 60000] }; + } - let video = streamData["data"]["dash"]["video"].filter(v => - !v["baseUrl"].includes("https://upos-sz-mirrorcosov.bilivideo.com/") - ).sort((a, b) => Number(b.bandwidth) - Number(a.bandwidth)); - - let audio = streamData["data"]["dash"]["audio"].filter(a => - !a["baseUrl"].includes("https://upos-sz-mirrorcosov.bilivideo.com/") - ).sort((a, b) => Number(b.bandwidth) - Number(a.bandwidth)); + const [ video, audio ] = extractBestQuality(streamData.data.dash); + if (!video || !audio) { + return { error: 'ErrorEmptyDownload' }; + } return { - urls: [video[0]["baseUrl"], audio[0]["baseUrl"]], + urls: [video.baseUrl, audio.baseUrl], audioFilename: `bilibili_${id}_audio`, - filename: `bilibili_${id}_${video[0]["width"]}x${video[0]["height"]}.mp4` + filename: `bilibili_${id}_${video.width}x${video.height}.mp4` }; } + +async function tv_download(id) { + const url = new URL( + 'https://api.bilibili.tv/intl/gateway/web/playurl' + + '?s_locale=en_US&platform=web&qn=64&type=0&device=wap' + + '&tf=0&spm_id=bstar-web.ugc-video-detail.0.0&from_spm_id=' + ); + + url.searchParams.set('aid', id); + + const { data } = await fetch(url).then(a => a.json()); + if (!data?.playurl?.video) { + return { error: 'ErrorEmptyDownload' }; + } + + const [ video, audio ] = extractBestQuality({ + video: data.playurl.video.map(s => s.video_resource) + .filter(s => s.codecs.includes('avc1')), + audio: data.playurl.audio_resource + }); + + if (!video || !audio) { + return { error: 'ErrorEmptyDownload' }; + } + + if (video.duration > maxVideoDuration) { + return { error: ['ErrorLengthLimit', maxVideoDuration / 60000] }; + } + + return { + urls: [video.url, audio.url], + audioFilename: `bilibili_tv_${id}_audio`, + filename: `bilibili_tv_${id}.mp4` + }; +} + +export default async function({ comId, tvId, comShortLink }) { + if (comShortLink) { + comId = await com_resolveShortlink(comShortLink); + } + + if (comId) { + return com_download(comId); + } else if (tvId) { + return tv_download(tvId); + } + + return { error: 'ErrorCouldntFetch' }; +} diff --git a/src/modules/processing/servicesConfig.json b/src/modules/processing/servicesConfig.json index bec1dab2..d6e137e9 100644 --- a/src/modules/processing/servicesConfig.json +++ b/src/modules/processing/servicesConfig.json @@ -2,8 +2,11 @@ "audioIgnore": ["vk", "ok"], "config": { "bilibili": { - "alias": "bilibili.com videos", - "patterns": ["video/:id", "_shortLink/:shortLink"], + "alias": "bilibili.com and bilibili.tv videos", + "patterns": [ + "video/:comId", "_shortLink/:comShortLink", + "_tv/:lang/video/:tvId", "_tv/video/:tvId" + ], "enabled": true }, "reddit": { diff --git a/src/modules/processing/servicesPatternTesters.js b/src/modules/processing/servicesPatternTesters.js index f3418445..30892f62 100644 --- a/src/modules/processing/servicesPatternTesters.js +++ b/src/modules/processing/servicesPatternTesters.js @@ -1,6 +1,7 @@ export const testers = { - "bilibili": (patternMatch) => - patternMatch.id?.length <= 12 || patternMatch.shortLink?.length <= 16, + "bilibili": (patternMatch) => + patternMatch.comId?.length <= 12 || patternMatch.comShortLink?.length <= 16 + || patternMatch.tvId?.length <= 24, "instagram": (patternMatch) => patternMatch.postId?.length <= 12 diff --git a/src/modules/processing/url.js b/src/modules/processing/url.js index 753e9d5d..5e6bd15a 100644 --- a/src/modules/processing/url.js +++ b/src/modules/processing/url.js @@ -49,6 +49,11 @@ export function aliasURL(url) { } break; + case "bilibili": + if (host.tld === 'tv') { + url = new URL(`https://bilibili.com/_tv${url.pathname}`); + } + break; case "b23": if (url.hostname === 'b23.tv' && parts.length === 2) { url = new URL(`https://bilibili.com/_shortLink/${parts[1]}`) diff --git a/src/test/tests.json b/src/test/tests.json index 3161b00d..42f75f40 100644 --- a/src/test/tests.json +++ b/src/test/tests.json @@ -754,6 +754,15 @@ "code": 200, "status": "stream" } + }, + { + "name": "bilibili.tv link", + "url": "https://www.bilibili.tv/en/video/4789599404426256", + "params": {}, + "expected": { + "code": 200, + "status": "stream" + } }], "tumblr": [{ "name": "at.tumblr link",