api: add support for bluesky videos & clean up service patterns
This commit is contained in:
parent
4478a963c5
commit
6c9601690b
4 changed files with 121 additions and 53 deletions
|
@ -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"
|
||||||
|
|
|
@ -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"],
|
||||||
},
|
},
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
52
api/src/processing/services/bluesky.js
Normal file
52
api/src/processing/services/bluesky.js
Normal 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" };
|
||||||
|
}
|
Loading…
Reference in a new issue