mirror of
https://github.com/imputnet/cobalt.git
synced 2025-07-10 07:18:30 +00:00
api: add initial draft support for nicovideo
This commit is contained in:
parent
40da8a46d6
commit
a7f6ea5d6f
@ -29,6 +29,7 @@ import loom from "./services/loom.js";
|
||||
import facebook from "./services/facebook.js";
|
||||
import bluesky from "./services/bluesky.js";
|
||||
import xiaohongshu from "./services/xiaohongshu.js";
|
||||
import nicovideo from "./services/nicovideo.js";
|
||||
|
||||
let freebind;
|
||||
|
||||
@ -268,6 +269,14 @@ export default async function({ host, patternMatch, params, authType }) {
|
||||
});
|
||||
break;
|
||||
|
||||
case "nicovideo":
|
||||
r = await nicovideo({
|
||||
...patternMatch,
|
||||
dispatcher,
|
||||
quality: params.videoQuality,
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
return createResponse("error", {
|
||||
code: "error.api.service.unsupported"
|
||||
|
@ -60,6 +60,11 @@ export const services = {
|
||||
loom: {
|
||||
patterns: ["share/:id", "embed/:id"],
|
||||
},
|
||||
nicovideo: {
|
||||
patterns: ["watch/:id"],
|
||||
tld: "jp",
|
||||
subdomains: ["sp"],
|
||||
},
|
||||
ok: {
|
||||
patterns: [
|
||||
"video/:id",
|
||||
|
@ -79,4 +79,6 @@ export const testers = {
|
||||
"xiaohongshu": pattern =>
|
||||
pattern.id?.length <= 24 && pattern.token?.length <= 64
|
||||
|| pattern.shareId?.length <= 24,
|
||||
|
||||
"nicovideo": pattern => pattern.id?.length <= 12,
|
||||
}
|
||||
|
120
api/src/processing/services/nicovideo.js
Normal file
120
api/src/processing/services/nicovideo.js
Normal file
@ -0,0 +1,120 @@
|
||||
import { genericUserAgent } from "../../config.js";
|
||||
|
||||
const genericHeaders = {
|
||||
"user-agent": genericUserAgent,
|
||||
"accept-language": "en-US,en;q=0.9",
|
||||
}
|
||||
|
||||
const getVideoInfo = async (id, dispatcher, quality) => {
|
||||
const html = await fetch(`https://www.nicovideo.jp/watch/${id}`, {
|
||||
dispatcher,
|
||||
headers: genericHeaders
|
||||
}).then(r => r.text()).catch(() => {});
|
||||
|
||||
if (!(html.includes("accessRightKey")
|
||||
|| !(html.includes('<meta name="server-response" content="')))) {
|
||||
return { error: "fetch.fail" };
|
||||
}
|
||||
|
||||
const rawContent =
|
||||
html.split('<meta name="server-response" content="')[1]
|
||||
.split('" />')[0]
|
||||
.replaceAll(""", '"');
|
||||
|
||||
const data = JSON.parse(rawContent)?.data?.response;
|
||||
|
||||
if (!data) {
|
||||
return { error: "fetch.fail" };
|
||||
}
|
||||
|
||||
const audio = data.media?.domand?.audios.find(audio => audio.isAvailable);
|
||||
const bestVideo = data.media?.domand?.videos.find(video => video.isAvailable);
|
||||
|
||||
const preferredVideo = data.media?.domand?.videos.find(video =>
|
||||
video.isAvailable && video.label.split('p')[0] === quality
|
||||
);
|
||||
|
||||
const video = preferredVideo || bestVideo;
|
||||
|
||||
return {
|
||||
watchTrackId: data.client?.watchTrackId,
|
||||
accessRightKey: data.media?.domand?.accessRightKey,
|
||||
|
||||
video,
|
||||
|
||||
outputs: [[video.id, audio.id]],
|
||||
|
||||
author: data.owner?.nickname,
|
||||
title: data.video?.title,
|
||||
}
|
||||
}
|
||||
|
||||
const getHls = async (dispatcher, id, trackId, accessRightKey, outputs) => {
|
||||
const response = await fetch(
|
||||
`https://nvapi.nicovideo.jp/v1/watch/${id}/access-rights/hls?actionTrackId=${trackId}`,
|
||||
{
|
||||
method: "POST",
|
||||
dispatcher,
|
||||
headers: {
|
||||
...genericHeaders,
|
||||
"content-type": "application/json",
|
||||
"x-access-right-key": accessRightKey,
|
||||
"x-frontend-id": "6",
|
||||
"x-frontend-version": "0",
|
||||
"x-request-with": "nicovideo",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
outputs,
|
||||
})
|
||||
}
|
||||
).then(r => r.json()).catch(() => {});
|
||||
|
||||
if (!response?.data?.contentUrl) return;
|
||||
return response.data.contentUrl;
|
||||
}
|
||||
|
||||
export default async function ({ id, dispatcher, quality }) {
|
||||
const {
|
||||
watchTrackId,
|
||||
accessRightKey,
|
||||
video,
|
||||
outputs,
|
||||
author,
|
||||
title,
|
||||
error
|
||||
} = await getVideoInfo(id, dispatcher, quality);
|
||||
|
||||
if (error) {
|
||||
return { error };
|
||||
}
|
||||
|
||||
if (!watchTrackId || !accessRightKey || !outputs) {
|
||||
return { error: "fetch.empty" };
|
||||
}
|
||||
|
||||
const hlsUrl = await getHls(
|
||||
dispatcher,
|
||||
id,
|
||||
watchTrackId,
|
||||
accessRightKey,
|
||||
outputs
|
||||
);
|
||||
|
||||
if (!hlsUrl) {
|
||||
return { error: "fetch.empty" };
|
||||
}
|
||||
|
||||
return {
|
||||
urls: hlsUrl,
|
||||
isHLS: true,
|
||||
filenameAttributes: {
|
||||
service: "nicovideo",
|
||||
id,
|
||||
title,
|
||||
author,
|
||||
resolution: `${video.width}x${video.height}`,
|
||||
qualityLabel: `${video.label}`,
|
||||
extension: "mp4"
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user