diff --git a/api/src/processing/service-config.js b/api/src/processing/service-config.js index 55946026f..0a35838bd 100644 --- a/api/src/processing/service-config.js +++ b/api/src/processing/service-config.js @@ -7,6 +7,7 @@ export const services = { bilibili: { patterns: [ "video/:comId", + "video/:comId?p=:partId", "_shortLink/:comShortLink", "_tv/:lang/video/:tvId", "_tv/video/:tvId" diff --git a/api/src/processing/service-patterns.js b/api/src/processing/service-patterns.js index 4ae138c25..6dc3ccbd0 100644 --- a/api/src/processing/service-patterns.js +++ b/api/src/processing/service-patterns.js @@ -1,53 +1,72 @@ export const testers = { "bilibili": pattern => - pattern.comId?.length <= 12 || pattern.comShortLink?.length <= 16 - || pattern.tvId?.length <= 24, + (pattern.comId?.length <= 12 && pattern.partId?.length <= 3) || + (pattern.comId?.length <= 12 && !pattern.partId) || + pattern.comShortLink?.length <= 16 || + pattern.tvId?.length <= 24, + + "bsky": pattern => + pattern.user?.length <= 128 && pattern.post?.length <= 128, "dailymotion": pattern => pattern.id?.length <= 32, + "facebook": pattern => + pattern.shortLink?.length <= 11 || + pattern.username?.length <= 30 || + pattern.caption?.length <= 255 || + pattern.id?.length <= 20 && !pattern.shareType || + pattern.id?.length <= 20 && pattern.shareType?.length === 1, + "instagram": pattern => - pattern.postId?.length <= 48 - || pattern.shareId?.length <= 16 - || (pattern.username?.length <= 30 && pattern.storyId?.length <= 24), + pattern.postId?.length <= 48 || + pattern.shareId?.length <= 16 || + (pattern.username?.length <= 30 && pattern.storyId?.length <= 24), "loom": pattern => pattern.id?.length <= 32, + "newgrounds": pattern => + pattern.id?.length <= 12 || + pattern.audioId?.length <= 12, + "ok": pattern => pattern.id?.length <= 16, "pinterest": pattern => - pattern.id?.length <= 128 || pattern.shortLink?.length <= 32, + pattern.id?.length <= 128 || + pattern.shortLink?.length <= 32, "reddit": pattern => - pattern.id?.length <= 16 && !pattern.sub && !pattern.user - || (pattern.sub?.length <= 22 && pattern.id?.length <= 16) - || (pattern.user?.length <= 22 && pattern.id?.length <= 16) - || (pattern.sub?.length <= 22 && pattern.shareId?.length <= 16) - || (pattern.shortId?.length <= 16), + pattern.id?.length <= 16 && !pattern.sub && !pattern.user || + (pattern.sub?.length <= 22 && pattern.id?.length <= 16) || + (pattern.user?.length <= 22 && pattern.id?.length <= 16) || + (pattern.sub?.length <= 22 && pattern.shareId?.length <= 16) || + (pattern.shortId?.length <= 16), "rutube": pattern => (pattern.id?.length === 32 && pattern.key?.length <= 32) || - pattern.id?.length === 32 || pattern.yappyId?.length === 32, - - "soundcloud": pattern => - (pattern.author?.length <= 255 && pattern.song?.length <= 255) - || pattern.shortLink?.length <= 32, + pattern.id?.length === 32 || + pattern.yappyId?.length === 32, "snapchat": pattern => - (pattern.username?.length <= 32 && (!pattern.storyId || pattern.storyId?.length <= 255)) - || pattern.spotlightId?.length <= 255 - || pattern.shortLink?.length <= 16, + (pattern.username?.length <= 32 && (!pattern.storyId || pattern.storyId?.length <= 255)) || + pattern.spotlightId?.length <= 255 || + pattern.shortLink?.length <= 16, + + "soundcloud": pattern => + (pattern.author?.length <= 255 && pattern.song?.length <= 255) || + pattern.shortLink?.length <= 32, "streamable": pattern => pattern.id?.length <= 6, "tiktok": pattern => - pattern.postId?.length <= 21 || pattern.shortLink?.length <= 21, + pattern.postId?.length <= 21 || + pattern.shortLink?.length <= 21, "tumblr": pattern => - pattern.id?.length < 21 - || (pattern.id?.length < 21 && pattern.user?.length <= 32), + pattern.id?.length < 21 || + (pattern.id?.length < 21 && pattern.user?.length <= 32), "twitch": pattern => pattern.channel && pattern.clip?.length <= 100, @@ -56,30 +75,16 @@ export const testers = { pattern.id?.length < 20, "vimeo": pattern => - pattern.id?.length <= 11 - && (!pattern.password || pattern.password.length < 16), + pattern.id?.length <= 11 && (!pattern.password || pattern.password.length < 16), "vk": pattern => (pattern.ownerId?.length <= 10 && pattern.videoId?.length <= 10) || (pattern.ownerId?.length <= 10 && pattern.videoId?.length <= 10 && pattern.videoId?.accessKey <= 18), - "youtube": pattern => - pattern.id?.length <= 11, - - "facebook": pattern => - pattern.shortLink?.length <= 11 - || pattern.username?.length <= 30 - || pattern.caption?.length <= 255 - || pattern.id?.length <= 20 && !pattern.shareType - || pattern.id?.length <= 20 && pattern.shareType?.length === 1, - - "bsky": pattern => - pattern.user?.length <= 128 && pattern.post?.length <= 128, - "xiaohongshu": pattern => - pattern.id?.length <= 24 && pattern.token?.length <= 64 - || pattern.shareId?.length <= 24 && pattern.shareType?.length === 1, + pattern.id?.length <= 24 && pattern.token?.length <= 64 || + pattern.shareId?.length <= 24 && pattern.shareType?.length === 1, - "newgrounds": pattern => - pattern.id?.length <= 12 || pattern.audioId?.length <= 12, + "youtube": pattern => + pattern.id?.length <= 11, } diff --git a/api/src/processing/services/bilibili.js b/api/src/processing/services/bilibili.js index a77ce10c8..297c5f9f2 100644 --- a/api/src/processing/services/bilibili.js +++ b/api/src/processing/services/bilibili.js @@ -17,8 +17,14 @@ function extractBestQuality(dashData) { return [ bestVideo, bestAudio ]; } -async function com_download(id) { - const html = await fetch(`https://bilibili.com/video/${id}`, { +async function com_download(id, partId) { + const url = new URL(`https://bilibili.com/video/${id}`); + + if (partId) { + url.searchParams.set('p', partId); + } + + const html = await fetch(url, { headers: { "user-agent": genericUserAgent } @@ -47,10 +53,15 @@ async function com_download(id) { return { error: "fetch.empty" }; } + let filenameBase = `bilibili_${id}`; + if (partId) { + filenameBase += `_${partId}`; + } + return { urls: [video.baseUrl, audio.baseUrl], - audioFilename: `bilibili_${id}_audio`, - filename: `bilibili_${id}_${video.width}x${video.height}.mp4`, + audioFilename: `${filenameBase}_audio`, + filename: `${filenameBase}_${video.width}x${video.height}.mp4`, }; } @@ -89,14 +100,14 @@ async function tv_download(id) { }; } -export default async function({ comId, tvId, comShortLink }) { +export default async function({ comId, tvId, comShortLink, partId }) { if (comShortLink) { const patternMatch = await resolveRedirectingURL(`https://b23.tv/${comShortLink}`); comId = patternMatch?.comId; } if (comId) { - return com_download(comId); + return com_download(comId, partId); } else if (tvId) { return tv_download(tvId); } diff --git a/api/src/processing/url.js b/api/src/processing/url.js index 5f93f1d08..9ac8a3ee9 100644 --- a/api/src/processing/url.js +++ b/api/src/processing/url.js @@ -147,6 +147,7 @@ function cleanURL(url) { limitQuery('v'); } break; + case "bilibili": case "rutube": if (url.searchParams.get('p')) { limitQuery('p'); diff --git a/api/src/util/tests/bilibili.json b/api/src/util/tests/bilibili.json index c67202952..88b42cdf5 100644 --- a/api/src/util/tests/bilibili.json +++ b/api/src/util/tests/bilibili.json @@ -56,5 +56,14 @@ "code": 200, "status": "tunnel" } + }, + { + "name": "bilibili.com link with part id", + "url": "https://www.bilibili.com/video/BV1uo4y1K72s?spm_id_from=333.788.videopod.episodes&p=6", + "params": {}, + "expected": { + "code": 200, + "status": "tunnel" + } } ]