mirror of
https://github.com/imputnet/cobalt.git
synced 2025-07-18 11:18:28 +00:00
wip: tested implementation but broken stream
TODO @synzr ffmpeg doesn't work for no reason
This commit is contained in:
parent
c06fac08c0
commit
449ee1dac3
@ -85,6 +85,7 @@ export default function(r, host, userFormat, isAudioOnly, lang, isAudioMuted, di
|
|||||||
case "video":
|
case "video":
|
||||||
switch (host) {
|
switch (host) {
|
||||||
case "bilibili":
|
case "bilibili":
|
||||||
|
case "nicovideo":
|
||||||
params = { type: "render" };
|
params = { type: "render" };
|
||||||
break;
|
break;
|
||||||
case "youtube":
|
case "youtube":
|
||||||
|
@ -2,9 +2,15 @@ import { genericUserAgent } from "../../config.js";
|
|||||||
import HLS from "hls-parser";
|
import HLS from "hls-parser";
|
||||||
import util from "node:util";
|
import util from "node:util";
|
||||||
|
|
||||||
|
const NICOVIDEO_EMBED_FRONTEND_HEADERS = {
|
||||||
|
"x-frontend-id": "70",
|
||||||
|
"x-frontend-version": "0",
|
||||||
|
"x-niconico-langauge": "ja-jp",
|
||||||
|
"x-request-with": "https://embed.nicovideo.jp",
|
||||||
|
};
|
||||||
|
|
||||||
const NICOVIDEO_EMBED_URL = "https://embed.nicovideo.jp/watch/%s";
|
const NICOVIDEO_EMBED_URL = "https://embed.nicovideo.jp/watch/%s";
|
||||||
const NICOVIDEO_GUEST_API_URL =
|
const NICOVIDEO_GUEST_API_URL =
|
||||||
// frontend is embed player
|
|
||||||
"https://www.nicovideo.jp/api/watch/v3_guest/%s?_frontendId=70&_frontendVersion=0&actionTrackId=%s";
|
"https://www.nicovideo.jp/api/watch/v3_guest/%s?_frontendId=70&_frontendVersion=0&actionTrackId=%s";
|
||||||
const NICOVIDEO_HLS_API_URL =
|
const NICOVIDEO_HLS_API_URL =
|
||||||
"https://nvapi.nicovideo.jp/v1/watch/%s/access-rights/hls?actionTrackId=%s";
|
"https://nvapi.nicovideo.jp/v1/watch/%s/access-rights/hls?actionTrackId=%s";
|
||||||
@ -12,7 +18,6 @@ const NICOVIDEO_HLS_API_URL =
|
|||||||
const ACTION_TRACK_ID_REGEXP =
|
const ACTION_TRACK_ID_REGEXP =
|
||||||
/"actionTrackId":"[A-Za-z0-9]+_[0-9]+"/;
|
/"actionTrackId":"[A-Za-z0-9]+_[0-9]+"/;
|
||||||
|
|
||||||
// working
|
|
||||||
async function getActionTrackId(id) {
|
async function getActionTrackId(id) {
|
||||||
const page = await fetch(util.format(NICOVIDEO_EMBED_URL, id), {
|
const page = await fetch(util.format(NICOVIDEO_EMBED_URL, id), {
|
||||||
headers: { "user-agent": genericUserAgent },
|
headers: { "user-agent": genericUserAgent },
|
||||||
@ -35,7 +40,6 @@ async function getActionTrackId(id) {
|
|||||||
return actionTrackId;
|
return actionTrackId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// not tested
|
|
||||||
async function fetchGuestData(id, actionTrackId) {
|
async function fetchGuestData(id, actionTrackId) {
|
||||||
const data = await fetch(
|
const data = await fetch(
|
||||||
util.format(NICOVIDEO_GUEST_API_URL, id, actionTrackId),
|
util.format(NICOVIDEO_GUEST_API_URL, id, actionTrackId),
|
||||||
@ -45,7 +49,6 @@ async function fetchGuestData(id, actionTrackId) {
|
|||||||
).then((response) => response.json());
|
).then((response) => response.json());
|
||||||
|
|
||||||
if (data?.meta?.status !== 200) {
|
if (data?.meta?.status !== 200) {
|
||||||
console.debug("fetchGuestData():", data)
|
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +68,6 @@ async function fetchGuestData(id, actionTrackId) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// not tested
|
|
||||||
async function fetchContentURL(id, actionTrackId, accessRightKey, outputs) {
|
async function fetchContentURL(id, actionTrackId, accessRightKey, outputs) {
|
||||||
const data = await fetch(
|
const data = await fetch(
|
||||||
util.format(NICOVIDEO_HLS_API_URL, id, actionTrackId),
|
util.format(NICOVIDEO_HLS_API_URL, id, actionTrackId),
|
||||||
@ -75,6 +77,7 @@ async function fetchContentURL(id, actionTrackId, accessRightKey, outputs) {
|
|||||||
"user-agent": genericUserAgent,
|
"user-agent": genericUserAgent,
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"x-access-right-key": accessRightKey,
|
"x-access-right-key": accessRightKey,
|
||||||
|
...NICOVIDEO_EMBED_FRONTEND_HEADERS,
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ outputs }),
|
body: JSON.stringify({ outputs }),
|
||||||
}
|
}
|
||||||
@ -84,43 +87,37 @@ async function fetchContentURL(id, actionTrackId, accessRightKey, outputs) {
|
|||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
return data.data.contentURL;
|
return data.data.contentUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// not tested
|
|
||||||
async function getHighestQualityHLS(contentURL) {
|
async function getHighestQualityHLS(contentURL) {
|
||||||
const hls = await fetch(contentURL)
|
const hls = await fetch(contentURL)
|
||||||
.then((response) => response.text())
|
.then((response) => response.text())
|
||||||
.then((response) => HLS.parse(response));
|
.then((response) => HLS.parse(response));
|
||||||
|
|
||||||
const highestQualityHLS = hls.variants
|
const highestQualityHLS = hls.variants
|
||||||
.sort((firstVariant, secondVariant) => {
|
.sort(
|
||||||
const firstVariantPixels =
|
(firstVariant, secondVariant) =>
|
||||||
firstVariant.resolution.width * firstVariant.resolution.height;
|
firstVariant.bandwidth - secondVariant.bandwidth
|
||||||
const secondVariantPixels =
|
)
|
||||||
secondVariant.resolution.width * secondVariant.resolution.height;
|
|
||||||
|
|
||||||
return firstVariantPixels - secondVariantPixels;
|
|
||||||
})
|
|
||||||
.pop();
|
.pop();
|
||||||
|
|
||||||
return highestQualityHLS;
|
return [highestQualityHLS.uri, highestQualityHLS.audio.pop().uri];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function nicovideo({ id }) {
|
export default async function nicovideo({ id }) {
|
||||||
try {
|
try {
|
||||||
const actionTrackId = await getActionTrackId(id);
|
const actionTrackId = await getActionTrackId(id);
|
||||||
const highestQualityHLS = await fetchGuestData(actionTrackId)
|
const [video, audio] = await fetchGuestData(id, actionTrackId)
|
||||||
.then(({ accessRightKey, outputs }) =>
|
.then(({ accessRightKey, outputs }) =>
|
||||||
fetchContentURL(id, actionTrackId, accessRightKey, outputs)
|
fetchContentURL(id, actionTrackId, accessRightKey, outputs)
|
||||||
)
|
)
|
||||||
.then((contentURL) => getHighestQualityHLS(contentURL));
|
.then((contentURL) => getHighestQualityHLS(contentURL));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
urls: highestQualityHLS,
|
urls: [video, audio],
|
||||||
isM3U8: true,
|
|
||||||
// TODO @synzr get video information from embed page props
|
// TODO @synzr get video information from embed page props
|
||||||
filenameAttributes: { service: "niconico", id },
|
filenameAttributes: { service: "nicovideo", id, extension: "mp4" },
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { error: "ErrorEmptyDownload" };
|
return { error: "ErrorEmptyDownload" };
|
||||||
|
@ -9,7 +9,7 @@ import { strict as assert } from "assert";
|
|||||||
// optional dependency
|
// optional dependency
|
||||||
const freebind = env.freebindCIDR && await import('freebind').catch(() => {});
|
const freebind = env.freebindCIDR && await import('freebind').catch(() => {});
|
||||||
|
|
||||||
const M3U_SERVICES = ['dailymotion', 'vimeo', 'rutube'];
|
const M3U_SERVICES = ['dailymotion', 'vimeo', 'rutube', 'nicovideo'];
|
||||||
|
|
||||||
const streamCache = new NodeCache({
|
const streamCache = new NodeCache({
|
||||||
stdTTL: env.streamLifespan,
|
stdTTL: env.streamLifespan,
|
||||||
|
Loading…
Reference in New Issue
Block a user