From 4524d178fb1fab7ca52035f3bcb65f73dc34d686 Mon Sep 17 00:00:00 2001 From: Virgil Clyne Date: Mon, 4 Nov 2024 14:16:55 +0800 Subject: [PATCH] refactor: js --- .gitmodules | 3 + src/protobuf | 1 + src/request.dev.js | 343 +++++++++++---------- src/request.js | 343 +++++++++++---------- src/response.dev.js | 707 +++++++++++++++++++------------------------- 5 files changed, 647 insertions(+), 750 deletions(-) create mode 100644 .gitmodules create mode 160000 src/protobuf diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..4f00f40 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/protobuf"] + path = src/protobuf + url = https://github.com/BiliUniverse/protobuf.git diff --git a/src/protobuf b/src/protobuf new file mode 160000 index 0000000..52ccf84 --- /dev/null +++ b/src/protobuf @@ -0,0 +1 @@ +Subproject commit 52ccf843547339028604c773f7b9cf038162f272 diff --git a/src/request.dev.js b/src/request.dev.js index 247ef40..1ba13fc 100644 --- a/src/request.dev.js +++ b/src/request.dev.js @@ -1,5 +1,4 @@ -import { $platform, URL, Lodash as _, Storage, fetch, notification, log, logError, wait, done, getScript, runScript } from "@nsnanocat/util"; -import { gRPC } from "@nsnanocat/util"; +import { $platform, Lodash as _, Storage, fetch, notification, log, logError, wait, done, gRPC } from "@nsnanocat/util"; import database from "./function/database.mjs"; import setENV from "./function/setENV.mjs"; // 构造回复数据 @@ -9,8 +8,8 @@ let $response = undefined; const url = new URL($request.url); log(`⚠ url: ${url.toJSON()}`, ""); // 获取连接参数 -const METHOD = $request.method, HOST = url.hostname, PATH = url.pathname, PATHs = url.pathname.split("/").filter(Boolean); -log(`⚠ METHOD: ${METHOD}, HOST: ${HOST}, PATH: ${PATH}` , ""); +const PATHs = url.pathname.split("/").filter(Boolean); +log(`⚠ PATHs: ${PATHs}`, ""); // 解析格式 const FORMAT = ($request.headers?.["Content-Type"] ?? $request.headers?.["content-type"])?.split(";")?.[0]; log(`⚠ FORMAT: ${FORMAT}`, ""); @@ -20,190 +19,186 @@ log(`⚠ FORMAT: ${FORMAT}`, ""); * @type {{Settings: import('./types').Settings}} */ const { Settings, Caches, Configs } = setENV("BiliBili", "Redirect", database); - log(`⚠ Settings.Switch: ${Settings?.Switch}`, ""); - switch (Settings.Switch) { - case true: - default: { - // 创建空数据 - const body = {}; - // 方法判断 - switch (METHOD) { - case "POST": - case "PUT": - case "PATCH": - // biome-ignore lint/suspicious/noFallthroughSwitchClause: - case "DELETE": - // 格式判断 + // 创建空数据 + const body = {}; + // 方法判断 + switch ($request.method) { + case "POST": + case "PUT": + case "PATCH": + // biome-ignore lint/suspicious/noFallthroughSwitchClause: + case "DELETE": + // 格式判断 + switch (FORMAT) { + case undefined: // 视为无body + break; + case "application/x-www-form-urlencoded": + case "text/plain": + default: + break; + case "application/x-mpegURL": + case "application/x-mpegurl": + case "application/vnd.apple.mpegurl": + case "audio/mpegurl": + //body = M3U8.parse($request.body); + //log(`🚧 body: ${JSON.stringify(body)}`, ""); + //$request.body = M3U8.stringify(body); + break; + case "text/xml": + case "text/html": + case "text/plist": + case "application/xml": + case "application/plist": + case "application/x-plist": + //body = XML.parse($request.body); + //log(`🚧 body: ${JSON.stringify(body)}`, ""); + //$request.body = XML.stringify(body); + break; + case "text/vtt": + case "application/vtt": + //body = VTT.parse($request.body); + //log(`🚧 body: ${JSON.stringify(body)}`, ""); + //$request.body = VTT.stringify(body); + break; + case "text/json": + case "application/json": + //body = JSON.parse($request.body ?? "{}"); + //log(`🚧 body: ${JSON.stringify(body)}`, ""); + //$request.body = JSON.stringify(body); + break; + case "application/protobuf": + case "application/x-protobuf": + case "application/vnd.google.protobuf": + case "application/grpc": + case "application/grpc+proto": + case "application/vnd.apple.flatbuffer": + case "application/octet-stream": { + //log(`🚧 $request.body: ${JSON.stringify($request.body)}`, ""); + let rawBody = $platform === "Quantumult X" ? new Uint8Array($request.bodyBytes ?? []) : ($request.body ?? new Uint8Array()); + //log(`🚧 isBuffer? ${ArrayBuffer.isView(rawBody)}: ${JSON.stringify(rawBody)}`, ""); switch (FORMAT) { - case undefined: // 视为无body - break; - case "application/x-www-form-urlencoded": - case "text/plain": - default: - break; - case "application/x-mpegURL": - case "application/x-mpegurl": - case "application/vnd.apple.mpegurl": - case "audio/mpegurl": - //body = M3U8.parse($request.body); - //log(`🚧 body: ${JSON.stringify(body)}`, ""); - //$request.body = M3U8.stringify(body); - break; - case "text/xml": - case "text/html": - case "text/plist": - case "application/xml": - case "application/plist": - case "application/x-plist": - //body = XML.parse($request.body); - //log(`🚧 body: ${JSON.stringify(body)}`, ""); - //$request.body = XML.stringify(body); - break; - case "text/vtt": - case "application/vtt": - //body = VTT.parse($request.body); - //log(`🚧 body: ${JSON.stringify(body)}`, ""); - //$request.body = VTT.stringify(body); - break; - case "text/json": - case "application/json": - //body = JSON.parse($request.body ?? "{}"); - //log(`🚧 body: ${JSON.stringify(body)}`, ""); - //$request.body = JSON.stringify(body); - break; case "application/protobuf": case "application/x-protobuf": case "application/vnd.google.protobuf": + break; case "application/grpc": case "application/grpc+proto": - case "application/octet-stream": { - //log(`🚧 $request.body: ${JSON.stringify($request.body)}`, ""); - let rawBody = ($platform === "Quantumult X") ? new Uint8Array($request.bodyBytes ?? []) : $request.body ?? new Uint8Array(); - //log(`🚧 isBuffer? ${ArrayBuffer.isView(rawBody)}: ${JSON.stringify(rawBody)}`, ""); - switch (FORMAT) { - case "application/protobuf": - case "application/x-protobuf": - case "application/vnd.google.protobuf": - break; - case "application/grpc": - case "application/grpc+proto": - rawBody = gRPC.decode(rawBody); - rawBody = gRPC.encode(rawBody); - break; - }; - // 写入二进制数据 - $request.body = rawBody; + rawBody = gRPC.decode(rawBody); + rawBody = gRPC.encode(rawBody); break; - } - }; - //break; // 不中断,继续处理URL - case "GET": - case "HEAD": - case "OPTIONS": - case undefined: // QX牛逼,script-echo-response不返回method + } + // 写入二进制数据 + $request.body = rawBody; + break; + } + } + //break; // 不中断,继续处理URL + case "GET": + case "HEAD": + case "OPTIONS": + default: + // 主机判断 + switch (url.hostname) { + case "upos-sz-mirrorali.bilivideo.com": // 阿里云 CDN + case "upos-sz-mirroralib.bilivideo.com": // 阿里云 CDN + case "upos-sz-mirroralio1.bilivideo.com": // 阿里云 CDN + case "upos-sz-mirrorcos.bilivideo.com": // 腾讯云 CDN + case "upos-sz-mirrorcosb.bilivideo.com": // 腾讯云 CDN,VOD 加速类型 + case "upos-sz-mirrorcoso1.bilivideo.com": // 腾讯云 CDN + case "upos-sz-mirrorhw.bilivideo.com": // 华为云 CDN,融合 CDN + case "upos-sz-mirrorhwb.bilivideo.com": // 华为云 CDN,融合 CDN + case "upos-sz-mirrorhwo1.bilivideo.com": // 华为云 CDN,融合 CDN + case "upos-sz-mirror08c.bilivideo.com": // 华为云 CDN,融合 CDN + case "upos-sz-mirror08h.bilivideo.com": // 华为云 CDN,融合 CDN + case "upos-sz-mirror08ct.bilivideo.com": // 华为云 CDN,融合 CDN + case "upos-sz-mirroraliov.bilivideo.com": // 阿里云 CDN,海外 + case "upos-sz-mirrorcosov.bilivideo.com": // 腾讯云 CDN,海外 + case "upos-sz-mirrorhwov.bilivideo.com": // 华为云 CDN,海外 + break; + case "upos-hz-mirrorakam.akamaized.net": // Akamai CDN,海外,有参数校验,其他类型的 CDN 不能直接替换为此 Host。但反过来可以。 + url.hostname = Settings.Host.Akamaized; + break; + case "upos-sz-mirroralibstar1.bilivideo.com": // 阿里云 CDN,海外(东南亚),其他类型的 CDN 应该不能替换为此 Host,但反过来可以。 + case "upos-sz-mirrorcosbstar1.bilivideo.com": // 腾讯云 CDN,海外(东南亚),其他类型的 CDN 应该不能替换为此 Host,但反过来可以。 + case "upos-sz-mirrorhwbstar1.bilivideo.com": // 华为云 CDN,海外(东南亚),其他类型的 CDN 应该不能替换为此 Host,但反过来可以。 + case "upos-bstar1-mirrorakam.akamaized.net": // Akamai CDN,海外(东南亚),有参数校验,其他类型的 CDN 不能直接替换为此 Host。但反过来可以。 + url.hostname = Settings.Host.BStar; + break; default: - // 主机判断 - switch (HOST) { - case "upos-sz-mirrorali.bilivideo.com": // 阿里云 CDN - case "upos-sz-mirroralib.bilivideo.com": // 阿里云 CDN - case "upos-sz-mirroralio1.bilivideo.com": // 阿里云 CDN - case "upos-sz-mirrorcos.bilivideo.com": // 腾讯云 CDN - case "upos-sz-mirrorcosb.bilivideo.com": // 腾讯云 CDN,VOD 加速类型 - case "upos-sz-mirrorcoso1.bilivideo.com": // 腾讯云 CDN - case "upos-sz-mirrorhw.bilivideo.com": // 华为云 CDN,融合 CDN - case "upos-sz-mirrorhwb.bilivideo.com": // 华为云 CDN,融合 CDN - case "upos-sz-mirrorhwo1.bilivideo.com": // 华为云 CDN,融合 CDN - case "upos-sz-mirror08c.bilivideo.com": // 华为云 CDN,融合 CDN - case "upos-sz-mirror08h.bilivideo.com": // 华为云 CDN,融合 CDN - case "upos-sz-mirror08ct.bilivideo.com": // 华为云 CDN,融合 CDN - case "upos-sz-mirroraliov.bilivideo.com": // 阿里云 CDN,海外 - case "upos-sz-mirrorcosov.bilivideo.com": // 腾讯云 CDN,海外 - case "upos-sz-mirrorhwov.bilivideo.com": // 华为云 CDN,海外 + switch (url.port) { + case "486": { + // MCDN + const cdn = url.searchParams.get("cdn"); + const sid = url.searchParams.get("sid"); + if (cdn) { + url.hostname = `d1--${cdn}.bilivideo.com`; + url.port = ""; + } else if (sid) { + url.hostname = `${sid}.bilivideo.com`; + url.port = ""; + } break; - case "upos-hz-mirrorakam.akamaized.net": // Akamai CDN,海外,有参数校验,其他类型的 CDN 不能直接替换为此 Host。但反过来可以。 - url.hostname = Settings.Host.Akamaized; + } + case "4480": // PCDN + url.protocol = "http"; + url.hostname = url.searchParams.get("xy_usource") || Settings.Host.PCDN; + url.port = ""; break; - case "upos-sz-mirroralibstar1.bilivideo.com": // 阿里云 CDN,海外(东南亚),其他类型的 CDN 应该不能替换为此 Host,但反过来可以。 - case "upos-sz-mirrorcosbstar1.bilivideo.com": // 腾讯云 CDN,海外(东南亚),其他类型的 CDN 应该不能替换为此 Host,但反过来可以。 - case "upos-sz-mirrorhwbstar1.bilivideo.com": // 华为云 CDN,海外(东南亚),其他类型的 CDN 应该不能替换为此 Host,但反过来可以。 - case "upos-bstar1-mirrorakam.akamaized.net": // Akamai CDN,海外(东南亚),有参数校验,其他类型的 CDN 不能直接替换为此 Host。但反过来可以。 - url.hostname = Settings.Host.BStar; + case "4483": // MCDN + case "8000": // MCDN + case "8082": // MCDN + case "9102": // MCDN + url.protocol = "https"; + url.hostname = Settings.Host.MCDN; + url.port = ""; + url.pathname = ""; + for (const key of url.searchParams.keys()) url.searchParams.delete(key); + url.searchParams.set("url", $request.url); break; - default: - switch (url.port) { - case "486": { // MCDN - const cdn = url.searchParams.get("cdn"); - const sid = url.searchParams.get("sid"); - if (cdn) { - url.hostname = `d1--${cdn}.bilivideo.com`; - url.port = ""; - } else if (sid) { - url.hostname = `${sid}.bilivideo.com`; - url.port = ""; - }; - break; - } - case "4480": // PCDN - url.protocol = "http"; - url.hostname = url.searchParams.get("xy_usource") || Settings.Host.PCDN; - url.port = ""; - break; - case "4483": // MCDN - case "8000": // MCDN - case "8082": // MCDN - case "9102": // MCDN - url.protocol = "https"; - url.hostname = Settings.Host.MCDN; - url.port = ""; - url.pathname = ""; - url.searchParams.set("url", $request.url); - break; - case "9305": // PCDN - url.protocol = "http"; - url.hostname = url.PATHs.shift(); - url.port = ""; - url.pathname = url.PATHs.join("/"); - break; - }; + case "9305": // PCDN + url.protocol = "http"; + url.hostname = url.PATHs.shift(); + url.port = ""; + url.pathname = url.PATHs.join("/"); break; - }; - break; - case "CONNECT": - case "TRACE": - break; - }; - if ($request.headers?.Host) $request.headers.Host = url.hostname; - $request.url = url.toString(); - log("🚧 调试信息", `$request.url: ${$request.url}`, ""); - break; - } - case false: - break; - }; -})() -.catch(e => logError(e)) -.finally(() => { - switch ($response) { - default: // 有构造回复数据,返回构造的回复数据 - //log(`🚧 finally`, `echo $response: ${JSON.stringify($response, null, 2)}`, ""); - if ($response.headers?.["Content-Encoding"]) $response.headers["Content-Encoding"] = "identity"; - if ($response.headers?.["content-encoding"]) $response.headers["content-encoding"] = "identity"; - switch ($platform) { - default: - done({ response: $response }); - break; - case "Quantumult X": - if (!$response.status) $response.status = "HTTP/1.1 200 OK"; - delete $response.headers?.["Content-Length"]; - delete $response.headers?.["content-length"]; - delete $response.headers?.["Transfer-Encoding"]; - done($response); + } break; } break; - case undefined: // 无构造回复数据,发送修改的请求数据 - //log(`🚧 finally`, `$request: ${JSON.stringify($request, null, 2)}`, ""); - done($request); + case "CONNECT": + case "TRACE": break; } -}); + if ($request.headers?.Host) $request.headers.Host = url.hostname; + $request.url = url.toString(); + log("🚧 调试信息", `$request.url: ${$request.url}`, ""); +})() + .catch(e => logError(e)) + .finally(() => { + switch (typeof $response) { + case "object": // 有构造回复数据,返回构造的回复数据 + //log("🚧 finally", `echo $response: ${JSON.stringify($response, null, 2)}`, ""); + if ($response.headers?.["Content-Encoding"]) $response.headers["Content-Encoding"] = "identity"; + if ($response.headers?.["content-encoding"]) $response.headers["content-encoding"] = "identity"; + switch ($platform) { + default: + done({ response: $response }); + break; + case "Quantumult X": + if (!$response.status) $response.status = "HTTP/1.1 200 OK"; + delete $response.headers?.["Content-Length"]; + delete $response.headers?.["content-length"]; + delete $response.headers?.["Transfer-Encoding"]; + done($response); + break; + } + break; + case "undefined": // 无构造回复数据,发送修改的请求数据 + //log("🚧 finally", `$request: ${JSON.stringify($request, null, 2)}`, ""); + done($request); + break; + default: + logError(`不合法的 $response 类型: ${typeof $response}`, ""); + break; + } + }); diff --git a/src/request.js b/src/request.js index f46a12f..0651f64 100644 --- a/src/request.js +++ b/src/request.js @@ -1,5 +1,4 @@ -import { $platform, URL, Lodash as _, Storage, fetch, notification, log, logError, wait, done, getScript, runScript } from "@nsnanocat/util"; -import { gRPC } from "@nsnanocat/util"; +import { $platform, Lodash as _, Storage, fetch, notification, log, logError, wait, done, gRPC } from "@nsnanocat/util"; import database from "./function/database.mjs"; import setENV from "./function/setENV.mjs"; // 构造回复数据 @@ -9,8 +8,8 @@ let $response = undefined; const url = new URL($request.url); //log(`⚠ url: ${url.toJSON()}`, ""); // 获取连接参数 -const METHOD = $request.method, HOST = url.hostname, PATH = url.pathname, PATHs = url.pathname.split("/").filter(Boolean); -log(`⚠ METHOD: ${METHOD}, HOST: ${HOST}, PATH: ${PATH}` , ""); +const PATHs = url.pathname.split("/").filter(Boolean); +log(`⚠ PATHs: ${PATHs}`, ""); // 解析格式 const FORMAT = ($request.headers?.["Content-Type"] ?? $request.headers?.["content-type"])?.split(";")?.[0]; //log(`⚠ FORMAT: ${FORMAT}`, ""); @@ -20,190 +19,186 @@ const FORMAT = ($request.headers?.["Content-Type"] ?? $request.headers?.["conten * @type {{Settings: import('./types').Settings}} */ const { Settings, Caches, Configs } = setENV("BiliBili", "Redirect", database); - //log(`⚠ Settings.Switch: ${Settings?.Switch}`, ""); - switch (Settings.Switch) { - case true: - default: { - // 创建空数据 - const body = {}; - // 方法判断 - switch (METHOD) { - case "POST": - case "PUT": - case "PATCH": - // biome-ignore lint/suspicious/noFallthroughSwitchClause: - case "DELETE": - // 格式判断 + // 创建空数据 + const body = {}; + // 方法判断 + switch ($request.method) { + case "POST": + case "PUT": + case "PATCH": + // biome-ignore lint/suspicious/noFallthroughSwitchClause: + case "DELETE": + // 格式判断 + switch (FORMAT) { + case undefined: // 视为无body + break; + case "application/x-www-form-urlencoded": + case "text/plain": + default: + break; + case "application/x-mpegURL": + case "application/x-mpegurl": + case "application/vnd.apple.mpegurl": + case "audio/mpegurl": + //body = M3U8.parse($request.body); + //log(`🚧 body: ${JSON.stringify(body)}`, ""); + //$request.body = M3U8.stringify(body); + break; + case "text/xml": + case "text/html": + case "text/plist": + case "application/xml": + case "application/plist": + case "application/x-plist": + //body = XML.parse($request.body); + //log(`🚧 body: ${JSON.stringify(body)}`, ""); + //$request.body = XML.stringify(body); + break; + case "text/vtt": + case "application/vtt": + //body = VTT.parse($request.body); + //log(`🚧 body: ${JSON.stringify(body)}`, ""); + //$request.body = VTT.stringify(body); + break; + case "text/json": + case "application/json": + //body = JSON.parse($request.body ?? "{}"); + //log(`🚧 body: ${JSON.stringify(body)}`, ""); + //$request.body = JSON.stringify(body); + break; + case "application/protobuf": + case "application/x-protobuf": + case "application/vnd.google.protobuf": + case "application/grpc": + case "application/grpc+proto": + case "application/vnd.apple.flatbuffer": + case "application/octet-stream": { + //log(`🚧 $request.body: ${JSON.stringify($request.body)}`, ""); + let rawBody = $platform === "Quantumult X" ? new Uint8Array($request.bodyBytes ?? []) : ($request.body ?? new Uint8Array()); + //log(`🚧 isBuffer? ${ArrayBuffer.isView(rawBody)}: ${JSON.stringify(rawBody)}`, ""); switch (FORMAT) { - case undefined: // 视为无body - break; - case "application/x-www-form-urlencoded": - case "text/plain": - default: - break; - case "application/x-mpegURL": - case "application/x-mpegurl": - case "application/vnd.apple.mpegurl": - case "audio/mpegurl": - //body = M3U8.parse($request.body); - //log(`🚧 body: ${JSON.stringify(body)}`, ""); - //$request.body = M3U8.stringify(body); - break; - case "text/xml": - case "text/html": - case "text/plist": - case "application/xml": - case "application/plist": - case "application/x-plist": - //body = XML.parse($request.body); - //log(`🚧 body: ${JSON.stringify(body)}`, ""); - //$request.body = XML.stringify(body); - break; - case "text/vtt": - case "application/vtt": - //body = VTT.parse($request.body); - //log(`🚧 body: ${JSON.stringify(body)}`, ""); - //$request.body = VTT.stringify(body); - break; - case "text/json": - case "application/json": - //body = JSON.parse($request.body ?? "{}"); - //log(`🚧 body: ${JSON.stringify(body)}`, ""); - //$request.body = JSON.stringify(body); - break; case "application/protobuf": case "application/x-protobuf": case "application/vnd.google.protobuf": + break; case "application/grpc": case "application/grpc+proto": - case "application/octet-stream": { - //log(`🚧 $request.body: ${JSON.stringify($request.body)}`, ""); - let rawBody = ($platform === "Quantumult X") ? new Uint8Array($request.bodyBytes ?? []) : $request.body ?? new Uint8Array(); - //log(`🚧 isBuffer? ${ArrayBuffer.isView(rawBody)}: ${JSON.stringify(rawBody)}`, ""); - switch (FORMAT) { - case "application/protobuf": - case "application/x-protobuf": - case "application/vnd.google.protobuf": - break; - case "application/grpc": - case "application/grpc+proto": - rawBody = gRPC.decode(rawBody); - rawBody = gRPC.encode(rawBody); - break; - }; - // 写入二进制数据 - $request.body = rawBody; + rawBody = gRPC.decode(rawBody); + rawBody = gRPC.encode(rawBody); break; - } - }; - //break; // 不中断,继续处理URL - case "GET": - case "HEAD": - case "OPTIONS": - case undefined: // QX牛逼,script-echo-response不返回method + } + // 写入二进制数据 + $request.body = rawBody; + break; + } + } + //break; // 不中断,继续处理URL + case "GET": + case "HEAD": + case "OPTIONS": + default: + // 主机判断 + switch (url.hostname) { + case "upos-sz-mirrorali.bilivideo.com": // 阿里云 CDN + case "upos-sz-mirroralib.bilivideo.com": // 阿里云 CDN + case "upos-sz-mirroralio1.bilivideo.com": // 阿里云 CDN + case "upos-sz-mirrorcos.bilivideo.com": // 腾讯云 CDN + case "upos-sz-mirrorcosb.bilivideo.com": // 腾讯云 CDN,VOD 加速类型 + case "upos-sz-mirrorcoso1.bilivideo.com": // 腾讯云 CDN + case "upos-sz-mirrorhw.bilivideo.com": // 华为云 CDN,融合 CDN + case "upos-sz-mirrorhwb.bilivideo.com": // 华为云 CDN,融合 CDN + case "upos-sz-mirrorhwo1.bilivideo.com": // 华为云 CDN,融合 CDN + case "upos-sz-mirror08c.bilivideo.com": // 华为云 CDN,融合 CDN + case "upos-sz-mirror08h.bilivideo.com": // 华为云 CDN,融合 CDN + case "upos-sz-mirror08ct.bilivideo.com": // 华为云 CDN,融合 CDN + case "upos-sz-mirroraliov.bilivideo.com": // 阿里云 CDN,海外 + case "upos-sz-mirrorcosov.bilivideo.com": // 腾讯云 CDN,海外 + case "upos-sz-mirrorhwov.bilivideo.com": // 华为云 CDN,海外 + break; + case "upos-hz-mirrorakam.akamaized.net": // Akamai CDN,海外,有参数校验,其他类型的 CDN 不能直接替换为此 Host。但反过来可以。 + url.hostname = Settings.Host.Akamaized; + break; + case "upos-sz-mirroralibstar1.bilivideo.com": // 阿里云 CDN,海外(东南亚),其他类型的 CDN 应该不能替换为此 Host,但反过来可以。 + case "upos-sz-mirrorcosbstar1.bilivideo.com": // 腾讯云 CDN,海外(东南亚),其他类型的 CDN 应该不能替换为此 Host,但反过来可以。 + case "upos-sz-mirrorhwbstar1.bilivideo.com": // 华为云 CDN,海外(东南亚),其他类型的 CDN 应该不能替换为此 Host,但反过来可以。 + case "upos-bstar1-mirrorakam.akamaized.net": // Akamai CDN,海外(东南亚),有参数校验,其他类型的 CDN 不能直接替换为此 Host。但反过来可以。 + url.hostname = Settings.Host.BStar; + break; default: - // 主机判断 - switch (HOST) { - case "upos-sz-mirrorali.bilivideo.com": // 阿里云 CDN - case "upos-sz-mirroralib.bilivideo.com": // 阿里云 CDN - case "upos-sz-mirroralio1.bilivideo.com": // 阿里云 CDN - case "upos-sz-mirrorcos.bilivideo.com": // 腾讯云 CDN - case "upos-sz-mirrorcosb.bilivideo.com": // 腾讯云 CDN,VOD 加速类型 - case "upos-sz-mirrorcoso1.bilivideo.com": // 腾讯云 CDN - case "upos-sz-mirrorhw.bilivideo.com": // 华为云 CDN,融合 CDN - case "upos-sz-mirrorhwb.bilivideo.com": // 华为云 CDN,融合 CDN - case "upos-sz-mirrorhwo1.bilivideo.com": // 华为云 CDN,融合 CDN - case "upos-sz-mirror08c.bilivideo.com": // 华为云 CDN,融合 CDN - case "upos-sz-mirror08h.bilivideo.com": // 华为云 CDN,融合 CDN - case "upos-sz-mirror08ct.bilivideo.com": // 华为云 CDN,融合 CDN - case "upos-sz-mirroraliov.bilivideo.com": // 阿里云 CDN,海外 - case "upos-sz-mirrorcosov.bilivideo.com": // 腾讯云 CDN,海外 - case "upos-sz-mirrorhwov.bilivideo.com": // 华为云 CDN,海外 + switch (url.port) { + case "486": { + // MCDN + const cdn = url.searchParams.get("cdn"); + const sid = url.searchParams.get("sid"); + if (cdn) { + url.hostname = `d1--${cdn}.bilivideo.com`; + url.port = ""; + } else if (sid) { + url.hostname = `${sid}.bilivideo.com`; + url.port = ""; + } break; - case "upos-hz-mirrorakam.akamaized.net": // Akamai CDN,海外,有参数校验,其他类型的 CDN 不能直接替换为此 Host。但反过来可以。 - url.hostname = Settings.Host.Akamaized; + } + case "4480": // PCDN + url.protocol = "http"; + url.hostname = url.searchParams.get("xy_usource") || Settings.Host.PCDN; + url.port = ""; break; - case "upos-sz-mirroralibstar1.bilivideo.com": // 阿里云 CDN,海外(东南亚),其他类型的 CDN 应该不能替换为此 Host,但反过来可以。 - case "upos-sz-mirrorcosbstar1.bilivideo.com": // 腾讯云 CDN,海外(东南亚),其他类型的 CDN 应该不能替换为此 Host,但反过来可以。 - case "upos-sz-mirrorhwbstar1.bilivideo.com": // 华为云 CDN,海外(东南亚),其他类型的 CDN 应该不能替换为此 Host,但反过来可以。 - case "upos-bstar1-mirrorakam.akamaized.net": // Akamai CDN,海外(东南亚),有参数校验,其他类型的 CDN 不能直接替换为此 Host。但反过来可以。 - url.hostname = Settings.Host.BStar; + case "4483": // MCDN + case "8000": // MCDN + case "8082": // MCDN + case "9102": // MCDN + url.protocol = "https"; + url.hostname = Settings.Host.MCDN; + url.port = ""; + url.pathname = ""; + for (const key of url.searchParams.keys()) url.searchParams.delete(key); + url.searchParams.set("url", $request.url); break; - default: - switch (url.port) { - case "486": { // MCDN - const cdn = url.searchParams.get("cdn"); - const sid = url.searchParams.get("sid"); - if (cdn) { - url.hostname = `d1--${cdn}.bilivideo.com`; - url.port = ""; - } else if (sid) { - url.hostname = `${sid}.bilivideo.com`; - url.port = ""; - }; - break; - } - case "4480": // PCDN - url.protocol = "http"; - url.hostname = url.searchParams.get("xy_usource") || Settings.Host.PCDN; - url.port = ""; - break; - case "4483": // MCDN - case "8000": // MCDN - case "8082": // MCDN - case "9102": // MCDN - url.protocol = "https"; - url.hostname = Settings.Host.MCDN; - url.port = ""; - url.pathname = ""; - url.searchParams.set("url", $request.url); - break; - case "9305": // PCDN - url.protocol = "http"; - url.hostname = url.PATHs.shift(); - url.port = ""; - url.pathname = url.PATHs.join("/"); - break; - }; + case "9305": // PCDN + url.protocol = "http"; + url.hostname = url.PATHs.shift(); + url.port = ""; + url.pathname = url.PATHs.join("/"); break; - }; - break; - case "CONNECT": - case "TRACE": - break; - }; - if ($request.headers?.Host) $request.headers.Host = url.hostname; - $request.url = url.toString(); - //log("🚧 调试信息", `$request.url: ${$request.url}`, ""); - break; - } - case false: - break; - }; -})() -.catch(e => logError(e)) -.finally(() => { - switch ($response) { - default: // 有构造回复数据,返回构造的回复数据 - //log(`🚧 finally`, `echo $response: ${JSON.stringify($response, null, 2)}`, ""); - if ($response.headers?.["Content-Encoding"]) $response.headers["Content-Encoding"] = "identity"; - if ($response.headers?.["content-encoding"]) $response.headers["content-encoding"] = "identity"; - switch ($platform) { - default: - done({ response: $response }); - break; - case "Quantumult X": - if (!$response.status) $response.status = "HTTP/1.1 200 OK"; - delete $response.headers?.["Content-Length"]; - delete $response.headers?.["content-length"]; - delete $response.headers?.["Transfer-Encoding"]; - done($response); + } break; } break; - case undefined: // 无构造回复数据,发送修改的请求数据 - //log(`🚧 finally`, `$request: ${JSON.stringify($request, null, 2)}`, ""); - done($request); + case "CONNECT": + case "TRACE": break; } -}); + if ($request.headers?.Host) $request.headers.Host = url.hostname; + $request.url = url.toString(); + //log("🚧 调试信息", `$request.url: ${$request.url}`, ""); +})() + .catch(e => logError(e)) + .finally(() => { + switch (typeof $response) { + case "object": // 有构造回复数据,返回构造的回复数据 + //log("🚧 finally", `echo $response: ${JSON.stringify($response, null, 2)}`, ""); + if ($response.headers?.["Content-Encoding"]) $response.headers["Content-Encoding"] = "identity"; + if ($response.headers?.["content-encoding"]) $response.headers["content-encoding"] = "identity"; + switch ($platform) { + default: + done({ response: $response }); + break; + case "Quantumult X": + if (!$response.status) $response.status = "HTTP/1.1 200 OK"; + delete $response.headers?.["Content-Length"]; + delete $response.headers?.["content-length"]; + delete $response.headers?.["Transfer-Encoding"]; + done($response); + break; + } + break; + case "undefined": // 无构造回复数据,发送修改的请求数据 + //log("🚧 finally", `$request: ${JSON.stringify($request, null, 2)}`, ""); + done($request); + break; + default: + logError(`不合法的 $response 类型: ${typeof $response}`, ""); + break; + } + }); diff --git a/src/response.dev.js b/src/response.dev.js index 7c2da2c..342425c 100644 --- a/src/response.dev.js +++ b/src/response.dev.js @@ -1,16 +1,17 @@ -import { $platform, URL, Lodash as _, Storage, fetch, notification, log, logError, wait, done, getScript, runScript } from "@nsnanocat/util"; -import { gRPC } from "@nsnanocat/util"; +import { $platform, URL, Lodash as _, Storage, fetch, notification, log, logError, wait, done, gRPC } from "@nsnanocat/util"; import database from "./function/database.mjs"; import setENV from "./function/setENV.mjs"; import { WireType, UnknownFieldHandler, reflectionMergePartial, MESSAGE_TYPE, MessageType, BinaryReader, isJsonObject, typeofJsonValue, jsonWriteOptions } from "@protobuf-ts/runtime/build/es2015/index.js"; -// import { Any } from "./protobuf/google/protobuf/any.js"; +import { Any } from "./protobuf/google/protobuf/any.js"; /***************** Processing *****************/ // 解构URL const url = new URL($request.url); log(`⚠ url: ${url.toJSON()}`, ""); // 获取连接参数 -const METHOD = $request.method, HOST = url.hostname, PATH = url.pathname, PATHs = url.pathname.split("/").filter(Boolean); -log(`⚠ METHOD: ${METHOD}, HOST: ${HOST}, PATH: ${PATH}`, ""); +const HOST = url.hostname, + PATH = url.pathname, + PATHs = url.pathname.split("/").filter(Boolean); +log(`⚠ HOST: ${HOST}, PATH: ${PATH}`, ""); // 解析格式 const FORMAT = ($response.headers?.["Content-Type"] ?? $response.headers?.["content-type"])?.split(";")?.[0]; log(`⚠ FORMAT: ${FORMAT}`, ""); @@ -20,460 +21,362 @@ log(`⚠ FORMAT: ${FORMAT}`, ""); * @type {{Settings: import('./types').Settings}} */ const { Settings, Caches, Configs } = setENV("BiliBili", "Redirect", database); - log(`⚠ Settings.Switch: ${Settings?.Switch}`, ""); - switch (Settings.Switch) { - case true: - default: { - // 创建空数据 - let body = { code: 0, message: "0", data: {} }; - // 信息组 - const infoGroup = { - seasonTitle: url.searchParams.get("season_title"), - seasonId: Number.parseInt(url.searchParams.get("season_id"), 10) || undefined, - epId: Number.parseInt(url.searchParams.get("ep_id"), 10) || undefined, - mId: Number.parseInt(url.searchParams.get("mid") || url.searchParams.get("vmid"), 10) || undefined, - evaluate: undefined, - keyword: url.searchParams.get("keyword"), - locale: url.searchParams.get("locale"), - locales: [], - type: "UGC", - }; - // 格式判断 - switch (FORMAT) { - case undefined: // 视为无body - break; - case "application/x-www-form-urlencoded": - case "text/plain": - default: - break; - case "application/x-mpegURL": - case "application/x-mpegurl": - case "application/vnd.apple.mpegurl": - case "audio/mpegurl": - //body = M3U8.parse($response.body); - //log(`🚧 body: ${JSON.stringify(body)}`, ""); - //$response.body = M3U8.stringify(body); - break; - case "text/xml": - case "text/html": - case "text/plist": - case "application/xml": - case "application/plist": - case "application/x-plist": - //body = XML.parse($response.body); - //log(`🚧 body: ${JSON.stringify(body)}`, ""); - //$response.body = XML.stringify(body); + // 创建空数据 + let body = { code: 0, message: "0", data: {} }; + // 信息组 + const infoGroup = { + seasonTitle: url.searchParams.get("season_title"), + seasonId: Number.parseInt(url.searchParams.get("season_id"), 10) || undefined, + epId: Number.parseInt(url.searchParams.get("ep_id"), 10) || undefined, + mId: Number.parseInt(url.searchParams.get("mid") || url.searchParams.get("vmid"), 10) || undefined, + evaluate: undefined, + keyword: url.searchParams.get("keyword"), + locale: url.searchParams.get("locale"), + locales: [], + type: "UGC", + }; + // 格式判断 + switch (FORMAT) { + case undefined: // 视为无body + break; + case "application/x-www-form-urlencoded": + case "text/plain": + default: + break; + case "application/x-mpegURL": + case "application/x-mpegurl": + case "application/vnd.apple.mpegurl": + case "audio/mpegurl": + //body = M3U8.parse($response.body); + //log(`🚧 body: ${JSON.stringify(body)}`, ""); + //$response.body = M3U8.stringify(body); + break; + case "text/xml": + case "text/html": + case "text/plist": + case "application/xml": + case "application/plist": + case "application/x-plist": + //body = XML.parse($response.body); + //log(`🚧 body: ${JSON.stringify(body)}`, ""); + //$response.body = XML.stringify(body); + break; + case "text/vtt": + case "application/vtt": + //body = VTT.parse($response.body); + //log(`🚧 body: ${JSON.stringify(body)}`, ""); + //$response.body = VTT.stringify(body); + break; + case "text/json": + case "application/json": + body = JSON.parse($response.body ?? "{}"); + // 解析链接 + switch (url.hostname) { + case "www.bilibili.com": break; - case "text/vtt": - case "application/vtt": - //body = VTT.parse($response.body); - //log(`🚧 body: ${JSON.stringify(body)}`, ""); - //$response.body = VTT.stringify(body); + case "app.bilibili.com": + case "app.biliapi.net": break; - case "text/json": - case "application/json": - body = JSON.parse($response.body ?? "{}"); - // 解析链接 - switch (HOST) { - case "www.bilibili.com": + case "api.bilibili.com": + case "api.biliapi.net": + switch (PATH) { + case "/pgc/player/api/playurl": // 番剧-播放地址-api + case "/pgc/player/web/playurl": // 番剧-播放地址-web + case "/pgc/player/web/playurl/html5": // 番剧-播放地址-web-HTML5 + infoGroup.type = "PGC"; break; - case "app.bilibili.com": - case "app.biliapi.net": + case "/pgc/page/bangumi": // 追番页 + case "/pgc/page/cinema/tab": // 观影页 + infoGroup.type = "PGC"; break; - case "api.bilibili.com": - case "api.biliapi.net": - switch (PATH) { - case "/pgc/player/api/playurl": // 番剧-播放地址-api - case "/pgc/player/web/playurl": // 番剧-播放地址-web - case "/pgc/player/web/playurl/html5": // 番剧-播放地址-web-HTML5 - infoGroup.type = "PGC"; - break; - case "/pgc/page/bangumi": // 追番页 - case "/pgc/page/cinema/tab": // 观影页 - infoGroup.type = "PGC"; - break; - case "/x/player/wbi/playurl": // UGC-用户生产内容-播放地址 - break; - case "/x/space/acc/info": // 用户空间-账号信息-pc - case "/x/space/wbi/acc/info": // 用户空间-账号信息-wbi - switch (infoGroup.mId) { - case 11783021: // 哔哩哔哩番剧出差 - case 1988098633: // b站_戲劇咖 - case 2042149112: // b站_綜藝咖 - break; - default: - break; - } - break; - case "/pgc/view/v2/app/season": // 番剧页面-内容-app + case "/x/player/wbi/playurl": // UGC-用户生产内容-播放地址 + break; + case "/x/space/acc/info": // 用户空间-账号信息-pc + case "/x/space/wbi/acc/info": // 用户空间-账号信息-wbi + switch (infoGroup.mId) { + case 11783021: // 哔哩哔哩番剧出差 + case 1988098633: // b站_戲劇咖 + case 2042149112: // b站_綜藝咖 break; - case "/pgc/view/web/season": // 番剧-内容-web - case "/pgc/view/pc/season": // 番剧-内容-pc + default: break; } break; + case "/pgc/view/v2/app/season": // 番剧页面-内容-app + break; + case "/pgc/view/web/season": // 番剧-内容-web + case "/pgc/view/pc/season": // 番剧-内容-pc + break; } - $response.body = JSON.stringify(body); break; + } + $response.body = JSON.stringify(body); + break; + case "application/protobuf": + case "application/x-protobuf": + case "application/vnd.google.protobuf": + case "application/grpc": + case "application/grpc+proto": + case "application/vnd.apple.flatbuffer": + case "application/octet-stream": { + //log(`🚧 $response.body: ${JSON.stringify($response.body)}`, ""); + let rawBody = $platform === "Quantumult X" ? new Uint8Array($response.bodyBytes ?? []) : ($response.body ?? new Uint8Array()); + //log(`🚧 isBuffer? ${ArrayBuffer.isView(rawBody)}: ${JSON.stringify(rawBody)}`, ""); + switch (FORMAT) { case "application/protobuf": case "application/x-protobuf": case "application/vnd.google.protobuf": + break; case "application/grpc": case "application/grpc+proto": - case "application/octet-stream": { - //log(`🚧 $response.body: ${JSON.stringify($response.body)}`, ""); - let rawBody = $platform === "Quantumult X" ? new Uint8Array($response.bodyBytes ?? []) : ($response.body ?? new Uint8Array()); - //log(`🚧 isBuffer? ${ArrayBuffer.isView(rawBody)}: ${JSON.stringify(rawBody)}`, ""); - switch (FORMAT) { - case "application/protobuf": - case "application/x-protobuf": - case "application/vnd.google.protobuf": - break; - case "application/grpc": - case "application/grpc+proto": - rawBody = gRPC.decode(rawBody); - // 解析链接并处理protobuf数据 - // 主机判断 - switch (HOST) { - case "grpc.biliapi.net": // HTTP/2 - case "app.bilibili.com": { - // HTTP/1.1 + rawBody = gRPC.decode(rawBody); + // 解析链接并处理protobuf数据 + // 主机判断 + switch (url.hostname) { + case "grpc.biliapi.net": // HTTP/2 + case "app.biliapi.net": // HTTP/1.1 + case "app.bilibili.com": // HTTP/1.1 + switch (PATHs?.[0]) { + case "bilibili.app.viewunite.v1.View": /****************** initialization start *******************/ - // protobuf/google/protobuf/any.proto - class Any$Type extends MessageType { + /****************** initialization finish *******************/ + switch (PATHs?.[1]) { + case "View": // 播放页 + break; + } + break; + case "bilibili.app.playerunite.v1.Player": { + /****************** initialization start *******************/ + class Stream$Type extends MessageType { constructor() { - super("google.protobuf.Any", [ - { no: 1, name: "type_url", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "value", kind: "scalar", T: 12 /*ScalarType.BYTES*/ }, + super("bilibili.playershared.Stream", [ + { no: 1, name: "stream_info", kind: "message", T: () => StreamInfo }, + { no: 2, name: "dash_video", kind: "message", oneof: "content", T: () => DashVideo }, + { no: 3, name: "segment_video", kind: "message", oneof: "content", T: () => SegmentVideo }, ]); } - /** - * Pack the message into a new `Any`. - * - * Uses 'type.googleapis.com/full.type.name' as the type URL. - */ - pack(message, type) { - return { - typeUrl: this.typeNameToUrl(type.typeName), - value: type.toBinary(message), - }; + } + const Stream = new Stream$Type(); + class StreamInfo$Type extends MessageType { + constructor() { + super("bilibili.playershared.StreamInfo", [ + { no: 1, name: "quality", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }, + { no: 2, name: "format", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: "description", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 4, name: "err_code", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }, + //{ no: 5, name: "limit", kind: "message", T: () => StreamLimit }, + { no: 6, name: "need_vip", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, + { no: 7, name: "need_login", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, + { no: 8, name: "intact", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, + { no: 9, name: "no_rexcode", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, + { no: 10, name: "attribute", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 2 /*LongType.NUMBER*/ }, + { no: 11, name: "new_description", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 12, name: "display_desc", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 13, name: "superscript", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 14, name: "vip_free", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, + { no: 15, name: "subtitle", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + //{ no: 16, name: "scheme", kind: "message", T: () => Scheme }, + { no: 17, name: "support_drm", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, + ]); } - /** - * Unpack the message from the `Any`. - */ - unpack(any, type, options) { - if (!this.contains(any, type)) throw new Error("Cannot unpack google.protobuf.Any with typeUrl '" + any.typeUrl + "' as " + type.typeName + "."); - return type.fromBinary(any.value, options); + } + const StreamInfo = new StreamInfo$Type(); + class DashVideo$Type extends MessageType { + constructor() { + super("bilibili.playershared.DashVideo", [ + { no: 1, name: "base_url", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "backup_url", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: "bandwidth", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }, + { no: 4, name: "codecid", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }, + { no: 5, name: "md5", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 6, name: "size", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 2 /*LongType.NUMBER*/ }, + { no: 7, name: "audio_id", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }, + { no: 8, name: "no_rexcode", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, + { no: 9, name: "frame_rate", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 10, name: "width", kind: "scalar", T: 5 /*ScalarType.INT32*/ }, + { no: 11, name: "height", kind: "scalar", T: 5 /*ScalarType.INT32*/ }, + { no: 12, name: "widevine_pssh", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + ]); } - /** - * Does the given `Any` contain a packed message of the given type? - */ - contains(any, type) { - if (!any.typeUrl.length) return false; - let wants = typeof type == "string" ? type : type.typeName; - let has = this.typeUrlToName(any.typeUrl); - return wants === has; + } + const DashVideo = new DashVideo$Type(); + class FragmentVideo$Type extends MessageType { + constructor() { + super("bilibili.playershared.FragmentVideo", [{ no: 1, name: "videos", kind: "message", repeat: 1 /*RepeatType.PACKED*/, T: () => FragmentVideoInfo }]); } - /** - * Convert the message to canonical JSON value. - * - * You have to provide the `typeRegistry` option so that the - * packed message can be converted to JSON. - * - * The `typeRegistry` option is also required to read - * `google.protobuf.Any` from JSON format. - */ - internalJsonWrite(any, options) { - if (any.typeUrl === "") return {}; - let typeName = this.typeUrlToName(any.typeUrl); - let opt = jsonWriteOptions(options); - let type = opt.typeRegistry?.find(t => t.typeName === typeName); - if (!type) throw new globalThis.Error("Unable to convert google.protobuf.Any with typeUrl '" + any.typeUrl + "' to JSON. The specified type " + typeName + " is not available in the type registry."); - let value = type.fromBinary(any.value, { readUnknownField: false }); - let json = type.internalJsonWrite(value, opt); - if (typeName.startsWith("google.protobuf.") || !isJsonObject(json)) json = { value: json }; - json["@type"] = any.typeUrl; - return json; + } + const FragmentVideo = new FragmentVideo$Type(); + class FragmentVideoInfo$Type extends MessageType { + constructor() { + super("bilibili.playershared.FragmentVideoInfo", [ + //{ no: 1, name: "fragment_info", kind: "message", T: () => FragmentInfo }, + { no: 2, name: "vod_info", kind: "message", T: () => VodInfo }, + //{ no: 3, name: "play_arc_conf", kind: "message", T: () => PlayArcConf }, + //{ no: 4, name: "dimension", kind: "message", T: () => Dimension }, + { no: 5, name: "timelength", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 2 /*LongType.NUMBER*/ }, + //{ no: 6, name: "video_type", kind: "enum", T: () => ["bilibili.playershared.BizType", BizType, "BIZ_TYPE_"] }, + { no: 7, name: "playable_status", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, + ]); } - internalJsonRead(json, options, target) { - if (!isJsonObject(json)) throw new globalThis.Error("Unable to parse google.protobuf.Any from JSON " + typeofJsonValue(json) + "."); - if (typeof json["@type"] != "string" || json["@type"] == "") return this.create(); - let typeName = this.typeUrlToName(json["@type"]); - let type = options?.typeRegistry?.find(t => t.typeName == typeName); - if (!type) throw new globalThis.Error("Unable to parse google.protobuf.Any from JSON. The specified type " + typeName + " is not available in the type registry."); - let value; - if (typeName.startsWith("google.protobuf.") && json.hasOwnProperty("value")) value = type.fromJson(json["value"], options); - else { - let copy = Object.assign({}, json); - delete copy["@type"]; - value = type.fromJson(copy, options); - } - if (target === undefined) target = this.create(); - target.typeUrl = json["@type"]; - target.value = type.toBinary(value); - return target; + } + const FragmentVideoInfo = new FragmentVideoInfo$Type(); + class ResponseUrl$Type extends MessageType { + constructor() { + super("bilibili.playershared.ResponseUrl", [ + { no: 1, name: "order", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }, + { no: 2, name: "length", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 2 /*LongType.NUMBER*/ }, + { no: 3, name: "size", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 2 /*LongType.NUMBER*/ }, + { no: 4, name: "url", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 5, name: "backup_url", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ }, + { no: 6, name: "md5", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + ]); } - typeNameToUrl(name) { - if (!name.length) throw new Error("invalid type name: " + name); - return "type.googleapis.com/" + name; + } + const ResponseUrl = new ResponseUrl$Type(); + class SegmentVideo$Type extends MessageType { + constructor() { + super("bilibili.playershared.SegmentVideo", [{ no: 1, name: "segment", kind: "message", repeat: 1 /*RepeatType.PACKED*/, T: () => ResponseUrl }]); } - typeUrlToName(url) { - if (!url.length) throw new Error("invalid type url: " + url); - let slash = url.lastIndexOf("/"); - let name = slash > 0 ? url.substring(slash + 1) : url; - if (!name.length) throw new Error("invalid type url: " + url); - return name; + } + const SegmentVideo = new SegmentVideo$Type(); + class VodInfo$Type extends MessageType { + constructor() { + super("bilibili.playershared.VodInfo", [ + { no: 1, name: "quality", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }, + { no: 2, name: "format", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: "timelength", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 2 /*LongType.NUMBER*/ }, + { no: 4, name: "video_codecid", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }, + { no: 5, name: "stream_list", kind: "message", repeat: 1 /*RepeatType.PACKED*/, T: () => Stream }, + //{ no: 6, name: "dash_audio", kind: "message", repeat: 1 /*RepeatType.PACKED*/, T: () => DashItem }, + //{ no: 7, name: "dolby", kind: "message", T: () => DolbyItem }, + //{ no: 8, name: "volume", kind: "message", T: () => VolumeInfo }, + //{ no: 9, name: "loss_less_item", kind: "message", T: () => LossLessItem }, + //{ no: 10, name: "support_project", kind: "scalar", T: 8 /*ScalarType.BOOL*/ } + ]); } } - const Any = new Any$Type(); + const VodInfo = new VodInfo$Type(); /****************** initialization finish *******************/ - switch (PATHs?.[0]) { - case "bilibili.app.viewunite.v1.View": - /****************** initialization start *******************/ - /****************** initialization finish *******************/ - switch (PATHs?.[1]) { - case "View": // 播放页 - break; - } - break; - case "bilibili.app.playerunite.v1.Player": { + switch (PATHs?.[1]) { + case "PlayViewUnite": { + // 播放页 /****************** initialization start *******************/ - class Stream$Type extends MessageType { - constructor() { - super("bilibili.playershared.Stream", [ - { no: 1, name: "stream_info", kind: "message", T: () => StreamInfo }, - { no: 2, name: "dash_video", kind: "message", oneof: "content", T: () => DashVideo }, - { no: 3, name: "segment_video", kind: "message", oneof: "content", T: () => SegmentVideo }, - ]); - } - } - const Stream = new Stream$Type(); - class StreamInfo$Type extends MessageType { + class PlayViewUniteReply$Type extends MessageType { constructor() { - super("bilibili.playershared.StreamInfo", [ - { no: 1, name: "quality", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }, - { no: 2, name: "format", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 3, name: "description", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 4, name: "err_code", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }, - //{ no: 5, name: "limit", kind: "message", T: () => StreamLimit }, - { no: 6, name: "need_vip", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, - { no: 7, name: "need_login", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, - { no: 8, name: "intact", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, - { no: 9, name: "no_rexcode", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, - { no: 10, name: "attribute", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 2 /*LongType.NUMBER*/ }, - { no: 11, name: "new_description", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 12, name: "display_desc", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 13, name: "superscript", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 14, name: "vip_free", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, - { no: 15, name: "subtitle", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - //{ no: 16, name: "scheme", kind: "message", T: () => Scheme }, - { no: 17, name: "support_drm", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, + super("bilibili.app.playerunite.v1.PlayViewUniteReply", [ + { no: 1, name: "vod_info", kind: "message", T: () => VodInfo }, + //{ no: 2, name: "play_arc_conf", kind: "message", T: () => PlayArcConf }, + //{ no: 3, name: "play_device_conf", kind: "message", T: () => PlayDeviceConf }, + //{ no: 4, name: "event", kind: "message", T: () => Event }, + //{ no: 5, name: "supplement", kind: "message", T: () => Any }, + //{ no: 6, name: "play_arc", kind: "message", T: () => PlayArc }, + //{ no: 7, name: "qn_trial_info", kind: "message", T: () => QnTrialInfo }, + //{ no: 8, name: "history", kind: "message", T: () => History }, + //{ no: 9, name: "view_info", kind: "message", T: () => ViewInfo }, + { no: 10, name: "fragment_video", kind: "message", T: () => FragmentVideo }, ]); } } - const StreamInfo = new StreamInfo$Type(); - class DashVideo$Type extends MessageType { - constructor() { - super("bilibili.playershared.DashVideo", [ - { no: 1, name: "base_url", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "backup_url", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ }, - { no: 3, name: "bandwidth", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }, - { no: 4, name: "codecid", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }, - { no: 5, name: "md5", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 6, name: "size", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 2 /*LongType.NUMBER*/ }, - { no: 7, name: "audio_id", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }, - { no: 8, name: "no_rexcode", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, - { no: 9, name: "frame_rate", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 10, name: "width", kind: "scalar", T: 5 /*ScalarType.INT32*/ }, - { no: 11, name: "height", kind: "scalar", T: 5 /*ScalarType.INT32*/ }, - { no: 12, name: "widevine_pssh", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - ]); - } - } - const DashVideo = new DashVideo$Type(); - class FragmentVideo$Type extends MessageType { - constructor() { - super("bilibili.playershared.FragmentVideo", [{ no: 1, name: "videos", kind: "message", repeat: 1 /*RepeatType.PACKED*/, T: () => FragmentVideoInfo }]); - } - } - const FragmentVideo = new FragmentVideo$Type(); - class FragmentVideoInfo$Type extends MessageType { - constructor() { - super("bilibili.playershared.FragmentVideoInfo", [ - //{ no: 1, name: "fragment_info", kind: "message", T: () => FragmentInfo }, - { no: 2, name: "vod_info", kind: "message", T: () => VodInfo }, - //{ no: 3, name: "play_arc_conf", kind: "message", T: () => PlayArcConf }, - //{ no: 4, name: "dimension", kind: "message", T: () => Dimension }, - { no: 5, name: "timelength", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 2 /*LongType.NUMBER*/ }, - //{ no: 6, name: "video_type", kind: "enum", T: () => ["bilibili.playershared.BizType", BizType, "BIZ_TYPE_"] }, - { no: 7, name: "playable_status", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, - ]); - } - } - const FragmentVideoInfo = new FragmentVideoInfo$Type(); - class ResponseUrl$Type extends MessageType { - constructor() { - super("bilibili.playershared.ResponseUrl", [ - { no: 1, name: "order", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }, - { no: 2, name: "length", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 2 /*LongType.NUMBER*/ }, - { no: 3, name: "size", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 2 /*LongType.NUMBER*/ }, - { no: 4, name: "url", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 5, name: "backup_url", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ }, - { no: 6, name: "md5", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - ]); - } - } - const ResponseUrl = new ResponseUrl$Type(); - class SegmentVideo$Type extends MessageType { - constructor() { - super("bilibili.playershared.SegmentVideo", [{ no: 1, name: "segment", kind: "message", repeat: 1 /*RepeatType.PACKED*/, T: () => ResponseUrl }]); - } - } - const SegmentVideo = new SegmentVideo$Type(); - class VodInfo$Type extends MessageType { - constructor() { - super("bilibili.playershared.VodInfo", [ - { no: 1, name: "quality", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }, - { no: 2, name: "format", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 3, name: "timelength", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 2 /*LongType.NUMBER*/ }, - { no: 4, name: "video_codecid", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }, - { no: 5, name: "stream_list", kind: "message", repeat: 1 /*RepeatType.PACKED*/, T: () => Stream }, - //{ no: 6, name: "dash_audio", kind: "message", repeat: 1 /*RepeatType.PACKED*/, T: () => DashItem }, - //{ no: 7, name: "dolby", kind: "message", T: () => DolbyItem }, - //{ no: 8, name: "volume", kind: "message", T: () => VolumeInfo }, - //{ no: 9, name: "loss_less_item", kind: "message", T: () => LossLessItem }, - //{ no: 10, name: "support_project", kind: "scalar", T: 8 /*ScalarType.BOOL*/ } - ]); - } - } - const VodInfo = new VodInfo$Type(); + const PlayViewUniteReply = new PlayViewUniteReply$Type(); /****************** initialization finish *******************/ - switch (PATHs?.[1]) { - case "PlayViewUnite": { - // 播放页 - /****************** initialization start *******************/ - class PlayViewUniteReply$Type extends MessageType { - constructor() { - super("bilibili.app.playerunite.v1.PlayViewUniteReply", [ - { no: 1, name: "vod_info", kind: "message", T: () => VodInfo }, - //{ no: 2, name: "play_arc_conf", kind: "message", T: () => PlayArcConf }, - //{ no: 3, name: "play_device_conf", kind: "message", T: () => PlayDeviceConf }, - //{ no: 4, name: "event", kind: "message", T: () => Event }, - //{ no: 5, name: "supplement", kind: "message", T: () => Any }, - //{ no: 6, name: "play_arc", kind: "message", T: () => PlayArc }, - //{ no: 7, name: "qn_trial_info", kind: "message", T: () => QnTrialInfo }, - //{ no: 8, name: "history", kind: "message", T: () => History }, - //{ no: 9, name: "view_info", kind: "message", T: () => ViewInfo }, - { no: 10, name: "fragment_video", kind: "message", T: () => FragmentVideo }, - ]); - } - } - const PlayViewUniteReply = new PlayViewUniteReply$Type(); - /****************** initialization finish *******************/ - let data = PlayViewUniteReply.fromBinary(body); - log(`🚧 data: ${JSON.stringify(data)}`, ""); - let UF = UnknownFieldHandler.list(data); - //log(`🚧 UF: ${JSON.stringify(UF)}`, ""); - if (UF) { - UF = UF.map(uf => { - //uf.no; // 22 - //uf.wireType; // WireType.Varint - // use the binary reader to decode the raw data: - let reader = new BinaryReader(uf.data); - let addedNumber = reader.int32(); // 7777 - log(`🚧 no: ${uf.no}, wireType: ${uf.wireType}, addedNumber: ${addedNumber}`, ""); + let data = PlayViewUniteReply.fromBinary(body); + log(`🚧 data: ${JSON.stringify(data)}`, ""); + let UF = UnknownFieldHandler.list(data); + //log(`🚧 UF: ${JSON.stringify(UF)}`, ""); + if (UF) { + UF = UF.map(uf => { + //uf.no; // 22 + //uf.wireType; // WireType.Varint + // use the binary reader to decode the raw data: + let reader = new BinaryReader(uf.data); + let addedNumber = reader.int32(); // 7777 + log(`🚧 no: ${uf.no}, wireType: ${uf.wireType}, addedNumber: ${addedNumber}`, ""); + }); + } + data.vodInfo.streamList = data.vodInfo.streamList.map(stream => { + switch (stream?.content?.oneofKind) { + case "dashVideo": + stream.content.dashVideo.baseUrl = stream.content.dashVideo.backupUrl.at(-1); + break; + case "SegmentVideo": + stream.content.segmentVideo.segment = stream.content.segmentVideo.segment.map(segment => { + segment.url = segment.backupUrl.at(-1); + return segment; }); - } - data.vodInfo.streamList = data.vodInfo.streamList.map(stream => { - switch (stream?.content?.oneofKind) { - case "dashVideo": - stream.content.dashVideo.baseUrl = stream.content.dashVideo.backupUrl.at(-1); - break; - case "SegmentVideo": - stream.content.segmentVideo.segment = stream.content.segmentVideo.segment.map(segment => { - segment.url = segment.backupUrl.at(-1); - return segment; - }); - break; - } - return stream; - }); - log(`🚧 data: ${JSON.stringify(data)}`, ""); - body = PlayViewUniteReply.toBinary(data); - break; + break; } - } + return stream; + }); + log(`🚧 data: ${JSON.stringify(data)}`, ""); + body = PlayViewUniteReply.toBinary(data); break; } - case "bilibili.app.playurl.v1.PlayURL": // 普通视频 - /****************** initialization start *******************/ - /****************** initialization finish *******************/ - switch (PATHs?.[1]) { - case "PlayView": // 播放地址 - break; - case "PlayConf": // 播放配置 - break; - } + } + break; + } + case "bilibili.app.playurl.v1.PlayURL": // 普通视频 + /****************** initialization start *******************/ + /****************** initialization finish *******************/ + switch (PATHs?.[1]) { + case "PlayView": // 播放地址 + break; + case "PlayConf": // 播放配置 break; - case "bilibili.pgc.gateway.player.v2.PlayURL": // 番剧 + } + break; + case "bilibili.pgc.gateway.player.v2.PlayURL": // 番剧 + /****************** initialization start *******************/ + /****************** initialization finish *******************/ + infoGroup.type = "PGC"; + switch (PATHs?.[1]) { + case "PlayView": // 播放地址 /****************** initialization start *******************/ /****************** initialization finish *******************/ - infoGroup.type = "PGC"; - switch (PATHs?.[1]) { - case "PlayView": // 播放地址 - /****************** initialization start *******************/ - /****************** initialization finish *******************/ - break; - case "PlayConf": // 播放配置 - break; - } break; - case "bilibili.app.nativeact.v1.NativeAct": // 活动-节目、动画、韩综(港澳台) - switch (PATHs?.[1]) { - case "Index": // 首页 - break; - } + case "PlayConf": // 播放配置 break; - case "bilibili.app.interface.v1.Search": // 搜索框 - switch (PATHs?.[1]) { - case "Suggest3": // 搜索建议 - break; - } + } + break; + case "bilibili.app.nativeact.v1.NativeAct": // 活动-节目、动画、韩综(港澳台) + switch (PATHs?.[1]) { + case "Index": // 首页 break; - case "bilibili.polymer.app.search.v1.Search": // 搜索结果 + } + break; + case "bilibili.app.interface.v1.Search": // 搜索框 + switch (PATHs?.[1]) { + case "Suggest3": // 搜索建议 + break; + } + break; + case "bilibili.polymer.app.search.v1.Search": // 搜索结果 + /****************** initialization start *******************/ + /****************** initialization finish *******************/ + switch (PATHs?.[1]) { + case "SearchAll": { + // 全部结果(综合) /****************** initialization start *******************/ /****************** initialization finish *******************/ - switch (PATHs?.[1]) { - case "SearchAll": { - // 全部结果(综合) - /****************** initialization start *******************/ - /****************** initialization finish *******************/ - break; - } - case "SearchByType": { - // 分类结果(番剧、用户、影视、专栏) - break; - } - } break; + } + case "SearchByType": { + // 分类结果(番剧、用户、影视、专栏) + break; + } } break; - } } - rawBody = gRPC.encode(rawBody); break; } - // 写入二进制数据 - $response.body = rawBody; + rawBody = gRPC.encode(rawBody); break; - } } - log(`🚧 信息组, infoGroup: ${JSON.stringify(infoGroup)}`, ""); + // 写入二进制数据 + $response.body = rawBody; break; } - case false: - break; } + log(`🚧 信息组, infoGroup: ${JSON.stringify(infoGroup)}`, ""); })() .catch(e => logError(e)) .finally(() => done($response));