api: add support for bluesky videos & clean up service patterns

This commit is contained in:
wukko 2024-09-01 14:34:44 +06:00
parent 4478a963c5
commit 6c9601690b
No known key found for this signature in database
GPG key ID: 3E30B3F26C7B4AA2
4 changed files with 121 additions and 53 deletions

View file

@ -26,6 +26,7 @@ import dailymotion from "./services/dailymotion.js";
import snapchat from "./services/snapchat.js"; import snapchat from "./services/snapchat.js";
import loom from "./services/loom.js"; import loom from "./services/loom.js";
import facebook from "./services/facebook.js"; import facebook from "./services/facebook.js";
import bluesky from "./services/bluesky.js";
let freebind; let freebind;
@ -234,6 +235,12 @@ export default async function(host, patternMatch, obj) {
}); });
break; break;
case "bsky":
r = await bluesky({
...patternMatch
});
break;
default: default:
return createResponse("error", { return createResponse("error", {
code: "error.api.service.unsupported" code: "error.api.service.unsupported"

View file

@ -1,7 +1,7 @@
import UrlPattern from "url-pattern"; import UrlPattern from "url-pattern";
export const audioIgnore = ["vk", "ok", "loom"]; export const audioIgnore = ["vk", "ok", "loom"];
export const hlsExceptions = ["dailymotion", "vimeo", "rutube"]; export const hlsExceptions = ["dailymotion", "vimeo", "rutube", "bsky"];
export const services = { export const services = {
bilibili: { bilibili: {
@ -13,6 +13,12 @@ export const services = {
], ],
subdomains: ["m"], subdomains: ["m"],
}, },
bsky: {
patterns: [
"profile/:user/post/:post"
],
tld: "app",
},
dailymotion: { dailymotion: {
patterns: ["video/:id"], patterns: ["video/:id"],
}, },

View file

@ -1,73 +1,76 @@
export const testers = { export const testers = {
"bilibili": (patternMatch) => "bilibili": pattern =>
patternMatch.comId?.length <= 12 || patternMatch.comShortLink?.length <= 16 pattern.comId?.length <= 12 || pattern.comShortLink?.length <= 16
|| patternMatch.tvId?.length <= 24, || pattern.tvId?.length <= 24,
"dailymotion": (patternMatch) => patternMatch.id?.length <= 32, "dailymotion": pattern => pattern.id?.length <= 32,
"instagram": (patternMatch) => "instagram": pattern =>
patternMatch.postId?.length <= 12 pattern.postId?.length <= 12
|| (patternMatch.username?.length <= 30 && patternMatch.storyId?.length <= 24), || (pattern.username?.length <= 30 && pattern.storyId?.length <= 24),
"loom": (patternMatch) => "loom": pattern =>
patternMatch.id?.length <= 32, pattern.id?.length <= 32,
"ok": (patternMatch) => "ok": pattern =>
patternMatch.id?.length <= 16, pattern.id?.length <= 16,
"pinterest": (patternMatch) => "pinterest": pattern =>
patternMatch.id?.length <= 128 || patternMatch.shortLink?.length <= 32, pattern.id?.length <= 128 || pattern.shortLink?.length <= 32,
"reddit": (patternMatch) => "reddit": pattern =>
(patternMatch.sub?.length <= 22 && patternMatch.id?.length <= 10) (pattern.sub?.length <= 22 && pattern.id?.length <= 10)
|| (patternMatch.user?.length <= 22 && patternMatch.id?.length <= 10), || (pattern.user?.length <= 22 && pattern.id?.length <= 10),
"rutube": (patternMatch) => "rutube": pattern =>
(patternMatch.id?.length === 32 && patternMatch.key?.length <= 32) || (pattern.id?.length === 32 && pattern.key?.length <= 32) ||
patternMatch.id?.length === 32 || patternMatch.yappyId?.length === 32, pattern.id?.length === 32 || pattern.yappyId?.length === 32,
"soundcloud": (patternMatch) => "soundcloud": pattern =>
(patternMatch.author?.length <= 255 && patternMatch.song?.length <= 255) (pattern.author?.length <= 255 && pattern.song?.length <= 255)
|| patternMatch.shortLink?.length <= 32, || pattern.shortLink?.length <= 32,
"snapchat": (patternMatch) => "snapchat": pattern =>
(patternMatch.username?.length <= 32 && (!patternMatch.storyId || patternMatch.storyId?.length <= 255)) (pattern.username?.length <= 32 && (!pattern.storyId || pattern.storyId?.length <= 255))
|| patternMatch.spotlightId?.length <= 255 || pattern.spotlightId?.length <= 255
|| patternMatch.shortLink?.length <= 16, || pattern.shortLink?.length <= 16,
"streamable": (patternMatch) => "streamable": pattern =>
patternMatch.id?.length === 6, pattern.id?.length === 6,
"tiktok": (patternMatch) => "tiktok": pattern =>
patternMatch.postId?.length <= 21 || patternMatch.id?.length <= 13, pattern.postId?.length <= 21 || pattern.id?.length <= 13,
"tumblr": (patternMatch) => "tumblr": pattern =>
patternMatch.id?.length < 21 pattern.id?.length < 21
|| (patternMatch.id?.length < 21 && patternMatch.user?.length <= 32), || (pattern.id?.length < 21 && pattern.user?.length <= 32),
"twitch": (patternMatch) => "twitch": pattern =>
patternMatch.channel && patternMatch.clip?.length <= 100, pattern.channel && pattern.clip?.length <= 100,
"twitter": (patternMatch) => "twitter": pattern =>
patternMatch.id?.length < 20, pattern.id?.length < 20,
"vimeo": (patternMatch) => "vimeo": pattern =>
patternMatch.id?.length <= 11 pattern.id?.length <= 11
&& (!patternMatch.password || patternMatch.password.length < 16), && (!pattern.password || pattern.password.length < 16),
"vine": (patternMatch) => "vine": pattern =>
patternMatch.id?.length <= 12, pattern.id?.length <= 12,
"vk": (patternMatch) => "vk": pattern =>
patternMatch.userId?.length <= 10 && patternMatch.videoId?.length <= 10, pattern.userId?.length <= 10 && pattern.videoId?.length <= 10,
"youtube": (patternMatch) => "youtube": pattern =>
patternMatch.id?.length <= 11, pattern.id?.length <= 11,
"facebook": (patternMatch) => "facebook": pattern =>
patternMatch.shortLink?.length <= 11 pattern.shortLink?.length <= 11
|| patternMatch.username?.length <= 30 || pattern.username?.length <= 30
|| patternMatch.caption?.length <= 255 || pattern.caption?.length <= 255
|| patternMatch.id?.length <= 20 && !patternMatch.shareType || pattern.id?.length <= 20 && !pattern.shareType
|| patternMatch.id?.length <= 20 && patternMatch.shareType?.length === 1, || pattern.id?.length <= 20 && pattern.shareType?.length === 1,
"bsky": pattern =>
pattern.user?.length <= 128 && pattern.post?.length <= 128,
} }

View file

@ -0,0 +1,52 @@
import HLS from "hls-parser";
import { cobaltUserAgent } from "../../config.js";
const extractVideo = async ({ getPost, filename }) => {
const urlMasterHLS = getPost?.thread?.post?.embed?.playlist;
if (!urlMasterHLS) return { error: "fetch.empty" };
const masterHLS = await fetch(urlMasterHLS)
.then(r => r.text())
.catch();
if (!masterHLS) return { error: "fetch.fail" };
const video = HLS.parse(masterHLS)
?.variants
?.reduce((a, b) => a?.bandwidth > b?.bandwidth ? a : b);
const videoURL = new URL(video.uri, urlMasterHLS).toString();
return {
urls: videoURL,
filename: `${filename}.mp4`,
audioFilename: `${filename}_audio`,
isM3U8: true,
}
}
export default async function ({ user, post }) {
const apiEndpoint = new URL("https://public.api.bsky.app/xrpc/app.bsky.feed.getPostThread?depth=0&parentHeight=0");
apiEndpoint.searchParams.set(
"uri",
`at://${user}/app.bsky.feed.post/${post}`
);
const getPost = await fetch(apiEndpoint, {
headers: {
"user-agent": cobaltUserAgent
}
})
.then(r => r.json())
.catch();
if (!getPost || getPost?.error) return { error: "fetch.empty" };
const embedType = getPost?.thread?.post?.embed?.$type;
const filename = `bluesky_${user}_${post}`;
if (embedType === "app.bsky.embed.video#view") {
return await extractVideo({ getPost, filename });
}
return { error: "fetch.empty" };
}