diff --git a/CHANGELOG.md b/CHANGELOG.md index f262f58..d2c6c56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ -### 🆕 New Features - * `翻译器`允许通过`参数面板`传入任意`语言代码` - * 传入的`语言代码`如不受`翻译API`支持,则可能导致翻译失败,请先查阅各`翻译API`的`支持语言列表`进行确认 - * 例如:当使用`微软翻译`时,在`参数面板`的`Language[1]`填入`lzh`,可以将翻译语言设置为`中文(文言文)` +### 🛠️ Bug Fixes + * 修复 `$argument` 和 `$persistentStore` 载入顺序颠倒的问题 + * 正确顺序为先读取 `$argument` 再读取 `$persistentStore (BoxJs)` + * 即,有相同键名时,`$persistentStore (BoxJs)` 的值会覆盖 `$argument` 的值 + +### 🔣 Dependencies + * 升级了 `@nsnanocat/util` + * `util` 由 `submodule` 更改为 `package` + * `$platform` 改为 `$app` + * 使用了全新的 `Console` polyfill diff --git a/src/function/database.mjs b/src/function/database.mjs index 9e8c338..22a16e4 100644 --- a/src/function/database.mjs +++ b/src/function/database.mjs @@ -1,7 +1,6 @@ export default { Default: { Settings: { - Switch: true, Type: "Translate", Types: ["Official", "Translate"], Languages: ["EN", "ZH"], @@ -20,7 +19,6 @@ export default { }, Spotify: { Settings: { - Switch: true, CountryCode: "US", Types: ["Translate", "External"], Languages: ["AUTO", "ZH"], diff --git a/src/function/setCache.mjs b/src/function/setCache.mjs index 6e46d09..0db4888 100644 --- a/src/function/setCache.mjs +++ b/src/function/setCache.mjs @@ -1,3 +1,5 @@ +import { Console } from "@nsnanocat/util"; + /** * Set Cache * @author VirgilClyne @@ -6,9 +8,9 @@ * @return {Boolean} isSaved */ export default function setCache(cache, cacheSize = 100) { - console.log(`☑️ Set Cache, cacheSize: ${cacheSize}`, ""); + Console.log("☑️ Set Cache", `cacheSize: ${cacheSize}`); cache = Array.from(cache || []); // Map转Array cache = cache.slice(-cacheSize); // 限制缓存大小 - console.log("✅ Set Cache", ""); + Console.log("✅ Set Cache"); return cache; }; diff --git a/src/function/setENV.mjs b/src/function/setENV.mjs index ba1477f..ea507a3 100644 --- a/src/function/setENV.mjs +++ b/src/function/setENV.mjs @@ -1,4 +1,4 @@ -import { Lodash as _, getStorage, log } from "@nsnanocat/util"; +import { Console, getStorage, Lodash as _ } from "@nsnanocat/util"; /** * Set Environment Variables @@ -9,13 +9,13 @@ import { Lodash as _, getStorage, log } from "@nsnanocat/util"; * @return {Object} { Settings, Caches, Configs } */ export default function setENV(name, platforms, database) { - log("☑️ Set Environment Variables", ""); + Console.log("☑️ Set Environment Variables"); const { Settings, Caches, Configs } = getStorage(name, platforms, database); /***************** Settings *****************/ if (!Array.isArray(Settings?.Types)) Settings.Types = (Settings.Types) ? [Settings.Types] : []; // 只有一个选项时,无逗号分隔 - log(`✅ Set Environment Variables, Settings: ${typeof Settings}, Settings内容: ${JSON.stringify(Settings)}`, ""); + Console.debug(`typeof Settings: ${typeof Settings}`, `Settings: ${JSON.stringify(Settings)}`); /***************** Caches *****************/ - //console.log(`✅ Set Environment Variables, Caches: ${typeof Caches}, Caches内容: ${JSON.stringify(Caches)}`, ""); + //Console.debug(`typeof Caches: ${typeof Caches}`, `Caches: ${JSON.stringify(Caches)}`); if (typeof Caches?.Playlists !== "object" || Array.isArray(Caches?.Playlists)) Caches.Playlists = {}; // 创建Playlists缓存 Caches.Playlists.Master = new Map(JSON.parse(Caches?.Playlists?.Master || "[]")); // Strings转Array转Map Caches.Playlists.Subtitle = new Map(JSON.parse(Caches?.Playlists?.Subtitle || "[]")); // Strings转Array转Map @@ -23,5 +23,6 @@ export default function setENV(name, platforms, database) { if (typeof Caches?.Metadatas !== "object" || Array.isArray(Caches?.Metadatas)) Caches.Metadatas = {}; // 创建Playlists缓存 if (typeof Caches?.Metadatas?.Tracks !== "object") Caches.Metadatas.Tracks = new Map(JSON.parse(Caches?.Metadatas?.Tracks || "[]")); // Strings转Array转Map /***************** Configs *****************/ + Console.log("✅ Set Environment Variables"); return { Settings, Caches, Configs }; }; diff --git a/src/request.dev.js b/src/request.dev.js index bf56a80..7edfdcc 100644 --- a/src/request.dev.js +++ b/src/request.dev.js @@ -1,4 +1,4 @@ -import { $platform, Lodash as _, Storage, fetch, notification, log, logError, wait, done, getScript, runScript } from "@nsnanocat/util"; +import { $app, Console, done, fetch, Lodash as _, notification, Storage, wait } from "@nsnanocat/util"; import database from "./function/database.mjs"; import setENV from "./function/setENV.mjs"; import setCache from "./function/setCache.mjs"; @@ -7,203 +7,193 @@ let $response = undefined; /***************** Processing *****************/ // 解构URL const url = new URL($request.url); -log(`⚠ url: ${url.toJSON()}`, ""); +Console.info(`url: ${url.toJSON()}`, ""); // 获取连接参数 -const METHOD = $request.method; -const HOST = url.hostname; -const PATH = url.pathname; const PATHs = url.pathname.split("/").filter(Boolean); -log(`⚠ METHOD: ${METHOD}, HOST: ${HOST}, PATH: ${PATH}`, ""); +Console.info(`PATHs: ${PATHs}`, ""); // 解析格式 const FORMAT = ($request.headers?.["Content-Type"] ?? $request.headers?.["content-type"])?.split(";")?.[0]; -log(`⚠ FORMAT: ${FORMAT}`, ""); +Console.info(`FORMAT: ${FORMAT}`, ""); !(async () => { /** * 设置 * @type {{Settings: import('./types').Settings}} */ const { Settings, Caches, Configs } = setENV("DualSubs", "Spotify", database); - log(`⚠ Settings.Switch: ${Settings?.Switch}`, ""); - switch (Settings.Switch) { - case false: - break; - default: { - // 获取字幕类型与语言 - const Type = url.searchParams.get("subtype") ?? Settings.Type; - const Languages = [url.searchParams.get("lang")?.toUpperCase?.() ?? Settings.Languages[0], (url.searchParams.get("tlang") ?? Caches?.tlang)?.toUpperCase?.() ?? Settings.Languages[1]]; - log(`⚠ Type: ${Type}, Languages: ${Languages}`, ""); - // 创建空数据 - let body = {}; - // 方法判断 - switch (METHOD) { - case "POST": - case "PUT": - case "PATCH": - case "DELETE": - // 格式判断 + // 获取字幕类型与语言 + const Type = url.searchParams.get("subtype") ?? Settings.Type; + const Languages = [url.searchParams.get("lang")?.toUpperCase?.() ?? Settings.Languages[0], (url.searchParams.get("tlang") ?? Caches?.tlang)?.toUpperCase?.() ?? Settings.Languages[1]]; + Console.info(`Type: ${Type}`, `Languages: ${Languages}`); + // 创建空数据 + let 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); + //Console.debug(`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); + //Console.debug(`body: ${JSON.stringify(body)}`, ""); + //$request.body = XML.stringify(body); + break; + case "text/vtt": + case "application/vtt": + //body = VTT.parse($request.body); + //Console.debug(`body: ${JSON.stringify(body)}`, ""); + //$request.body = VTT.stringify(body); + break; + case "text/json": + case "application/json": + //body = JSON.parse($request.body ?? "{}"); + //Console.debug(`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/octet-stream": { + //Console.debug(`$request: ${JSON.stringify($request, null, 2)}`, ""); + let rawBody = $app === "Quantumult X" ? new Uint8Array($request.bodyBytes ?? []) : ($request.body ?? new Uint8Array()); + //Console.debug(`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": - case "application/grpc": - case "application/grpc+proto": - case "application/octet-stream": { - //log(`🚧 调试信息`, `$request: ${JSON.stringify($request, null, 2)}`, ""); - 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": - switch (PATH) { - case "/bootstrap/v1/bootstrap": - case "/user-customization-service/v1/customize": - delete $request.headers?.["If-None-Match"]; - delete $request.headers?.["if-none-match"]; - break; - case "/extended-metadata/v0/extended-metadata": - break; - } + switch (url.pathname) { + case "/bootstrap/v1/bootstrap": + case "/user-customization-service/v1/customize": + delete $request.headers?.["If-None-Match"]; + delete $request.headers?.["if-none-match"]; break; - case "application/grpc": - case "application/grpc+proto": + case "/extended-metadata/v0/extended-metadata": break; } - // 写入二进制数据 - $request.body = rawBody; break; - } + case "application/grpc": + case "application/grpc+proto": + break; } - //break; // 不中断,继续处理URL - case "GET": - if (PATH.startsWith("/color-lyrics/v2/track/")) { - const trackId = PATHs?.[3]; - log("🚧 调试信息", `trackId: ${trackId}`, ""); - const _request = JSON.parse(JSON.stringify($request)); - _request.url = `https://api.spotify.com/v1/tracks?ids=${trackId}`; - if (_request?.headers?.Accept) _request.headers.Accept = "application/json"; - if (_request?.headers?.accept) _request.headers.accept = "application/json"; - //log(`🚧 调试信息`, `_request: ${JSON.stringify(_request)}`, ""); - const detectStutus = fetch($request); - const detectTrack = fetch(_request); - await Promise.allSettled([detectStutus, detectTrack]).then(results => { - /* + // 写入二进制数据 + $request.body = rawBody; + break; + } + } + //break; // 不中断,继续处理URL + // biome-ignore lint/suspicious/noFallthroughSwitchClause: + case "GET": + if (url.pathname.startsWith("/color-lyrics/v2/track/")) { + const trackId = PATHs?.[3]; + Console.debug(`trackId: ${trackId}`, ""); + const _request = JSON.parse(JSON.stringify($request)); + _request.url = `https://api.spotify.com/v1/tracks?ids=${trackId}`; + if (_request?.headers?.Accept) _request.headers.Accept = "application/json"; + if (_request?.headers?.accept) _request.headers.accept = "application/json"; + //Console.debug(`_request: ${JSON.stringify(_request)}`, ""); + const detectStutus = fetch($request); + const detectTrack = fetch(_request); + await Promise.allSettled([detectStutus, detectTrack]).then(results => { + /* results.forEach((result, i) => { - log(`🚧 调试信息`, `result[${i}]: ${JSON.stringify(result)}`, ""); + Console.debug(`result[${i}]: ${JSON.stringify(result)}`, ""); }); */ - switch (results[0].status) { - case "fulfilled": { - const response = results[0].value; - switch (response?.statusCode ?? response?.status) { - case 200: - if (Settings.Types.includes("Translate")) url.searchParams.set("subtype", "Translate"); - else if (Settings.Types.includes("External")) url.searchParams.set("subtype", "External"); - break; - case 401: - default: - break; - case 404: - if (Settings.Types.includes("External")) url.searchParams.set("subtype", "External"); - break; - } + switch (results[0].status) { + case "fulfilled": { + const response = results[0].value; + switch (response?.statusCode ?? response?.status) { + case 200: + if (Settings.Types.includes("Translate")) url.searchParams.set("subtype", "Translate"); + else if (Settings.Types.includes("External")) url.searchParams.set("subtype", "External"); break; - } - case "rejected": - log("🚧 调试信息", `detectStutus.reason: ${JSON.stringify(results[0].reason)}`, ""); - if (Settings.Types.includes("External")) url.searchParams.set("subtype", "External"); - break; - } - switch (results[1].status) { - case "fulfilled": { - const response = results[1].value; - body = JSON.parse(response.body); - body?.tracks?.forEach?.(track => { - //log(`🚧 调试信息`, `track: ${JSON.stringify(track)}`, ""); - const trackId = track?.id; - const trackInfo = { - id: track?.id, - track: track?.name, - album: track?.album?.name, - artist: track?.artists?.[0]?.name, - }; - // 写入数据 - Caches.Metadatas.Tracks.set(trackId, trackInfo); - }); - // 格式化缓存 - log(`🚧 Caches.Metadatas.Tracks: ${JSON.stringify([...Caches.Metadatas.Tracks.entries()])}`, ""); - Caches.Metadatas.Tracks = setCache(Caches.Metadatas.Tracks, Settings.CacheSize); - // 写入持久化储存 - Storage.setItem(`@DualSubs.${"Spotify"}.Caches.Metadatas.Tracks`, Caches.Metadatas.Tracks); + case 401: + default: break; - } - case "rejected": - log("🚧 调试信息", `detectTrack.reason: ${JSON.stringify(results[1].reason)}`, ""); + case 404: + if (Settings.Types.includes("External")) url.searchParams.set("subtype", "External"); break; } - }); + break; + } + case "rejected": + Console.debug(`detectStutus.reason: ${JSON.stringify(results[0].reason)}`, ""); + if (Settings.Types.includes("External")) url.searchParams.set("subtype", "External"); + break; } - case "HEAD": - case "OPTIONS": - break; - case "CONNECT": - case "TRACE": - break; + switch (results[1].status) { + case "fulfilled": { + const response = results[1].value; + body = JSON.parse(response.body); + body?.tracks?.forEach?.(track => { + //Console.debug(`track: ${JSON.stringify(track)}`, ""); + const trackId = track?.id; + const trackInfo = { + id: track?.id, + track: track?.name, + album: track?.album?.name, + artist: track?.artists?.[0]?.name, + }; + // 写入数据 + Caches.Metadatas.Tracks.set(trackId, trackInfo); + }); + // 格式化缓存 + Console.debug(`Caches.Metadatas.Tracks: ${JSON.stringify([...Caches.Metadatas.Tracks.entries()])}`, ""); + Caches.Metadatas.Tracks = setCache(Caches.Metadatas.Tracks, Settings.CacheSize); + // 写入持久化储存 + Storage.setItem(`@DualSubs.${"Spotify"}.Caches.Metadatas.Tracks`, Caches.Metadatas.Tracks); + break; + } + case "rejected": + Console.debug(`detectTrack.reason: ${JSON.stringify(results[1].reason)}`, ""); + break; + } + }); } - $request.url = url.toString(); - log("🚧 调试信息", `$request.url: ${$request.url}`, ""); + case "HEAD": + case "OPTIONS": + break; + case "CONNECT": + case "TRACE": break; - } } + $request.url = url.toString(); + Console.debug(`$request.url: ${$request.url}`, ""); })() - .catch(e => logError(e)) + .catch(e => Console.error(e)) .finally(() => { - switch ($response) { - case undefined: // 无构造回复数据,发送修改的请求数据 - //log(`🚧 finally`, `$request: ${JSON.stringify($request, null, 2)}`, ""); - done($request); - break; - default: // 有构造回复数据,返回构造的回复数据 - //log(`🚧 finally`, `echo $response: ${JSON.stringify($response, null, 2)}`, ""); + switch (typeof $response) { + case "object": // 有构造回复数据,返回构造的回复数据 + //Console.debug("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) { + switch ($app) { + default: + done({ response: $response }); + break; case "Quantumult X": if (!$response.status) $response.status = "HTTP/1.1 200 OK"; delete $response.headers?.["Content-Length"]; @@ -211,10 +201,14 @@ log(`⚠ FORMAT: ${FORMAT}`, ""); delete $response.headers?.["Transfer-Encoding"]; done($response); break; - default: - done({ response: $response }); - break; } break; + case "undefined": // 无构造回复数据,发送修改的请求数据 + //Console.debug("finally", `$request: ${JSON.stringify($request, null, 2)}`); + done($request); + break; + default: + Console.error(`不合法的 $response 类型: ${typeof $response}`); + break; } }); diff --git a/src/request.js b/src/request.js index 6a3c63c..8993cb2 100644 --- a/src/request.js +++ b/src/request.js @@ -1,185 +1,178 @@ -import { $platform, Lodash as _, Storage, fetch, notification, log, logError, wait, done, getScript, runScript } from "@nsnanocat/util"; +import { $app, Console, done, fetch, Lodash as _, notification, Storage, wait } from "@nsnanocat/util"; import database from "./function/database.mjs"; import setENV from "./function/setENV.mjs"; import setCache from "./function/setCache.mjs"; // 构造回复数据 let $response = undefined; +Console.debug = () => {}; /***************** Processing *****************/ // 解构URL const url = new URL($request.url); -log(`⚠ url: ${url.toJSON()}`, ""); +Console.info(`url: ${url.toJSON()}`, ""); // 获取连接参数 -const METHOD = $request.method; -const HOST = url.hostname; -const PATH = url.pathname; const PATHs = url.pathname.split("/").filter(Boolean); -log(`⚠ METHOD: ${METHOD}, HOST: ${HOST}, PATH: ${PATH}`, ""); +Console.info(`PATHs: ${PATHs}`, ""); // 解析格式 const FORMAT = ($request.headers?.["Content-Type"] ?? $request.headers?.["content-type"])?.split(";")?.[0]; -log(`⚠ FORMAT: ${FORMAT}`, ""); +Console.info(`FORMAT: ${FORMAT}`, ""); !(async () => { /** * 设置 * @type {{Settings: import('./types').Settings}} */ const { Settings, Caches, Configs } = setENV("DualSubs", "Spotify", database); - log(`⚠ Settings.Switch: ${Settings?.Switch}`, ""); - switch (Settings.Switch) { - case false: - break; - default: { - // 获取字幕类型与语言 - const Type = url.searchParams.get("subtype") ?? Settings.Type; - const Languages = [url.searchParams.get("lang")?.toUpperCase?.() ?? Settings.Languages[0], (url.searchParams.get("tlang") ?? Caches?.tlang)?.toUpperCase?.() ?? Settings.Languages[1]]; - log(`⚠ Type: ${Type}, Languages: ${Languages}`, ""); - // 创建空数据 - let body = {}; - // 方法判断 - switch (METHOD) { - case "POST": - case "PUT": - case "PATCH": - case "DELETE": - // 格式判断 + // 获取字幕类型与语言 + const Type = url.searchParams.get("subtype") ?? Settings.Type; + const Languages = [url.searchParams.get("lang")?.toUpperCase?.() ?? Settings.Languages[0], (url.searchParams.get("tlang") ?? Caches?.tlang)?.toUpperCase?.() ?? Settings.Languages[1]]; + Console.info(`Type: ${Type}`, `Languages: ${Languages}`); + // 创建空数据 + let 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": + break; + case "text/xml": + case "text/html": + case "text/plist": + case "application/xml": + case "application/plist": + case "application/x-plist": + break; + case "text/vtt": + case "application/vtt": + break; + case "text/json": + case "application/json": + break; + case "application/protobuf": + case "application/x-protobuf": + case "application/vnd.google.protobuf": + case "application/grpc": + case "application/grpc+proto": + case "application/octet-stream": { + let rawBody = $app === "Quantumult X" ? new Uint8Array($request.bodyBytes ?? []) : ($request.body ?? new Uint8Array()); 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": - break; - case "text/xml": - case "text/html": - case "text/plist": - case "application/xml": - case "application/plist": - case "application/x-plist": - break; - case "text/vtt": - case "application/vtt": - break; - case "text/json": - case "application/json": - break; case "application/protobuf": case "application/x-protobuf": case "application/vnd.google.protobuf": - case "application/grpc": - case "application/grpc+proto": - case "application/octet-stream": { - let rawBody = $platform === "Quantumult X" ? new Uint8Array($request.bodyBytes ?? []) : ($request.body ?? new Uint8Array()); - switch (FORMAT) { - case "application/protobuf": - case "application/x-protobuf": - case "application/vnd.google.protobuf": - switch (PATH) { - case "/bootstrap/v1/bootstrap": - case "/user-customization-service/v1/customize": - delete $request.headers?.["If-None-Match"]; - delete $request.headers?.["if-none-match"]; - break; - case "/extended-metadata/v0/extended-metadata": - break; - } + switch (url.pathname) { + case "/bootstrap/v1/bootstrap": + case "/user-customization-service/v1/customize": + delete $request.headers?.["If-None-Match"]; + delete $request.headers?.["if-none-match"]; break; - case "application/grpc": - case "application/grpc+proto": + case "/extended-metadata/v0/extended-metadata": break; } - // 写入二进制数据 - $request.body = rawBody; break; - } + case "application/grpc": + case "application/grpc+proto": + break; } - //break; // 不中断,继续处理URL - case "GET": - if (PATH.startsWith("/color-lyrics/v2/track/")) { - const trackId = PATHs?.[3]; - log("🚧 调试信息", `trackId: ${trackId}`, ""); - const _request = JSON.parse(JSON.stringify($request)); - _request.url = `https://api.spotify.com/v1/tracks?ids=${trackId}`; - if (_request?.headers?.Accept) _request.headers.Accept = "application/json"; - if (_request?.headers?.accept) _request.headers.accept = "application/json"; - //log(`🚧 调试信息`, `_request: ${JSON.stringify(_request)}`, ""); - const detectStutus = fetch($request); - const detectTrack = fetch(_request); - await Promise.allSettled([detectStutus, detectTrack]).then(results => { - switch (results[0].status) { - case "fulfilled": { - const response = results[0].value; - switch (response?.statusCode ?? response?.status) { - case 200: - if (Settings.Types.includes("Translate")) url.searchParams.set("subtype", "Translate"); - else if (Settings.Types.includes("External")) url.searchParams.set("subtype", "External"); - break; - case 401: - default: - break; - case 404: - if (Settings.Types.includes("External")) url.searchParams.set("subtype", "External"); - break; - } - break; - } - case "rejected": - if (Settings.Types.includes("External")) url.searchParams.set("subtype", "External"); + // 写入二进制数据 + $request.body = rawBody; + break; + } + } + //break; // 不中断,继续处理URL + // biome-ignore lint/suspicious/noFallthroughSwitchClause: + case "GET": + if (url.pathname.startsWith("/color-lyrics/v2/track/")) { + const trackId = PATHs?.[3]; + Console.debug(`trackId: ${trackId}`, ""); + const _request = JSON.parse(JSON.stringify($request)); + _request.url = `https://api.spotify.com/v1/tracks?ids=${trackId}`; + if (_request?.headers?.Accept) _request.headers.Accept = "application/json"; + if (_request?.headers?.accept) _request.headers.accept = "application/json"; + //Console.debug(`_request: ${JSON.stringify(_request)}`, ""); + const detectStutus = fetch($request); + const detectTrack = fetch(_request); + await Promise.allSettled([detectStutus, detectTrack]).then(results => { + switch (results[0].status) { + case "fulfilled": { + const response = results[0].value; + switch (response?.statusCode ?? response?.status) { + case 200: + if (Settings.Types.includes("Translate")) url.searchParams.set("subtype", "Translate"); + else if (Settings.Types.includes("External")) url.searchParams.set("subtype", "External"); break; - } - switch (results[1].status) { - case "fulfilled": { - const response = results[1].value; - body = JSON.parse(response.body); - body?.tracks?.forEach?.(track => { - const trackId = track?.id; - const trackInfo = { - id: track?.id, - track: track?.name, - album: track?.album?.name, - artist: track?.artists?.[0]?.name, - }; - // 写入数据 - Caches.Metadatas.Tracks.set(trackId, trackInfo); - }); - // 格式化缓存 - Caches.Metadatas.Tracks = setCache(Caches.Metadatas.Tracks, Settings.CacheSize); - // 写入持久化储存 - Storage.setItem(`@DualSubs.${"Spotify"}.Caches.Metadatas.Tracks`, Caches.Metadatas.Tracks); + case 401: + default: break; - } - case "rejected": - log("🚧 调试信息", `detectTrack.reason: ${JSON.stringify(results[1].reason)}`, ""); + case 404: + if (Settings.Types.includes("External")) url.searchParams.set("subtype", "External"); break; } - }); + break; + } + case "rejected": + if (Settings.Types.includes("External")) url.searchParams.set("subtype", "External"); + break; } - case "HEAD": - case "OPTIONS": - break; - case "CONNECT": - case "TRACE": - break; + switch (results[1].status) { + case "fulfilled": { + const response = results[1].value; + body = JSON.parse(response.body); + body?.tracks?.forEach?.(track => { + const trackId = track?.id; + const trackInfo = { + id: track?.id, + track: track?.name, + album: track?.album?.name, + artist: track?.artists?.[0]?.name, + }; + // 写入数据 + Caches.Metadatas.Tracks.set(trackId, trackInfo); + }); + // 格式化缓存 + Caches.Metadatas.Tracks = setCache(Caches.Metadatas.Tracks, Settings.CacheSize); + // 写入持久化储存 + Storage.setItem(`@DualSubs.${"Spotify"}.Caches.Metadatas.Tracks`, Caches.Metadatas.Tracks); + break; + } + case "rejected": + Console.debug(`detectTrack.reason: ${JSON.stringify(results[1].reason)}`, ""); + break; + } + }); } - $request.url = url.toString(); - log("🚧 调试信息", `$request.url: ${$request.url}`, ""); + case "HEAD": + case "OPTIONS": + break; + case "CONNECT": + case "TRACE": break; - } } + $request.url = url.toString(); + Console.debug(`$request.url: ${$request.url}`, ""); })() - .catch(e => logError(e)) + .catch(e => Console.error(e)) .finally(() => { - switch ($response) { - case undefined: // 无构造回复数据,发送修改的请求数据 - done($request); - break; - default: // 有构造回复数据,返回构造的回复数据 + switch (typeof $response) { + case "object": // 有构造回复数据,返回构造的回复数据 + //Console.debug("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) { + switch ($app) { + default: + done({ response: $response }); + break; case "Quantumult X": if (!$response.status) $response.status = "HTTP/1.1 200 OK"; delete $response.headers?.["Content-Length"]; @@ -187,10 +180,14 @@ log(`⚠ FORMAT: ${FORMAT}`, ""); delete $response.headers?.["Transfer-Encoding"]; done($response); break; - default: - done({ response: $response }); - break; } break; + case "undefined": // 无构造回复数据,发送修改的请求数据 + //Console.debug("finally", `$request: ${JSON.stringify($request, null, 2)}`); + done($request); + break; + default: + Console.error(`不合法的 $response 类型: ${typeof $response}`); + break; } }); diff --git a/src/response.dev.js b/src/response.dev.js index 7f6d906..f04d776 100644 --- a/src/response.dev.js +++ b/src/response.dev.js @@ -1,4 +1,4 @@ -import { $platform, Lodash as _, Storage, fetch, notification, log, logError, wait, done, getScript, runScript } from "@nsnanocat/util"; +import { $app, Console, done, fetch, Lodash as _, notification, Storage, wait } from "@nsnanocat/util"; import database from "./function/database.mjs"; import setENV from "./function/setENV.mjs"; import setCache from "./function/setCache.mjs"; @@ -10,184 +10,173 @@ import { BatchedExtensionResponse } from "./protobuf/spotify/ExtendedMetadata.js /***************** Processing *****************/ // 解构URL const url = new URL($request.url); -log(`⚠ url: ${url.toJSON()}`, ""); +Console.info(`url: ${url.toJSON()}`, ""); // 获取连接参数 -const METHOD = $request.method; -const HOST = url.hostname; const PATH = url.pathname; -log(`⚠ METHOD: ${METHOD}, HOST: ${HOST}, PATH: ${PATH}`, ""); +Console.info(`PATH: ${PATH}`, ""); // 解析格式 const FORMAT = ($response.headers?.["Content-Type"] ?? $response.headers?.["content-type"])?.split(";")?.[0]; -log(`⚠ FORMAT: ${FORMAT}`, ""); +Console.info(`FORMAT: ${FORMAT}`, ""); !(async () => { /** * 设置 * @type {{Settings: import('./types').Settings}} */ const { Settings, Caches, Configs } = setENV("DualSubs", "Spotify", database); - log(`⚠ Settings.Switch: ${Settings?.Switch}`, ""); - switch (Settings.Switch) { - case false: + // 获取字幕类型与语言 + const Type = url.searchParams.get("subtype") ?? Settings.Type; + const Languages = [url.searchParams.get("lang")?.toUpperCase?.() ?? Settings.Languages[0], (url.searchParams.get("tlang") ?? Caches?.tlang)?.toUpperCase?.() ?? Settings.Languages[1]]; + Console.info(`Type: ${Type}`, `Languages: ${Languages}`); + // 创建空数据 + let body = {}; + // 格式判断 + switch (FORMAT) { + case undefined: // 视为无body break; - default: { - // 获取字幕类型与语言 - const Type = url.searchParams.get("subtype") ?? Settings.Type; - const Languages = [url.searchParams.get("lang")?.toUpperCase?.() ?? Settings.Languages[0], (url.searchParams.get("tlang") ?? Caches?.tlang)?.toUpperCase?.() ?? Settings.Languages[1]]; - log(`⚠ Type: ${Type}, Languages: ${Languages}`, ""); - // 创建空数据 - let body = {}; - // 格式判断 - 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); + 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); + //Console.debug(`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); + //Console.debug(`body: ${JSON.stringify(body)}`, ""); + //$response.body = XML.stringify(body); + break; + case "text/vtt": + case "application/vtt": + //body = VTT.parse($response.body); + //Console.debug(`body: ${JSON.stringify(body)}`, ""); + //$response.body = VTT.stringify(body); + break; + case "text/json": + case "application/json": + body = JSON.parse($response.body ?? "{}"); + Console.debug(`body: ${JSON.stringify(body)}`, ""); + switch (PATH) { + case "/melody/v1/product_state": + //body.product = "premium"; + body.country = Settings.Country; + //body.ads = "0"; + //body["on-demand"] = "1"; + body["selected-language"] = Settings.Languages[1].toLowerCase(); + //body["multiuserplan-current-size"] + //body["preferred-locale"] + //body["multiuserplan-member-type"] + //body["is-standalone-audiobooks"] + //body.catalogue = "premium"; break; - case "text/json": - case "application/json": - body = JSON.parse($response.body ?? "{}"); - log(`🚧 body: ${JSON.stringify(body)}`, ""); - switch (PATH) { - case "/melody/v1/product_state": - //body.product = "premium"; - body.country = Settings.Country; - //body.ads = "0"; - //body["on-demand"] = "1"; - body["selected-language"] = Settings.Languages[1].toLowerCase(); - //body["multiuserplan-current-size"] - //body["preferred-locale"] - //body["multiuserplan-member-type"] - //body["is-standalone-audiobooks"] - //body.catalogue = "premium"; - break; - case "/v1/tracks": - // biome-ignore lint/complexity/noForEach: - body?.tracks?.forEach?.(track => { - log(`🚧 track: ${JSON.stringify(track)}`, ""); - const trackId = track?.id; - const trackInfo = { - track: track?.name, - album: track?.album?.name, - artist: track?.artists?.[0]?.name, - }; - // 写入数据 - Caches.Metadatas.Tracks.set(trackId, trackInfo); - }); - // 格式化缓存 - log("🚧 调试信息", `Caches.Metadatas.Tracks: ${JSON.stringify([...Caches.Metadatas.Tracks.entries()])}`, ""); - Caches.Metadatas.Tracks = setCache(Caches.Metadatas.Tracks, Settings.CacheSize); - // 写入持久化储存 - Storage.setItem(`@DualSubs.${"Spotify"}.Caches.Metadatas.Tracks`, Caches.Metadatas.Tracks); - break; - } - $response.body = JSON.stringify(body); + case "/v1/tracks": + body?.tracks?.forEach?.(track => { + Console.debug(`track: ${JSON.stringify(track)}`, ""); + const trackId = track?.id; + const trackInfo = { + track: track?.name, + album: track?.album?.name, + artist: track?.artists?.[0]?.name, + }; + // 写入数据 + Caches.Metadatas.Tracks.set(trackId, trackInfo); + }); + // 格式化缓存 + Console.debug(`Caches.Metadatas.Tracks: ${JSON.stringify([...Caches.Metadatas.Tracks.entries()])}`, ""); + Caches.Metadatas.Tracks = setCache(Caches.Metadatas.Tracks, Settings.CacheSize); + // 写入持久化储存 + Storage.setItem(`@DualSubs.${"Spotify"}.Caches.Metadatas.Tracks`, Caches.Metadatas.Tracks); 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/octet-stream": { + //Console.debug(`$response: ${JSON.stringify($response, null, 2)}`, ""); + let rawBody = $app === "Quantumult X" ? new Uint8Array($response.bodyBytes ?? []) : ($response.body ?? new Uint8Array()); + //Console.debug(`isBuffer? ${ArrayBuffer.isView(rawBody)}: ${JSON.stringify(rawBody)}`, ""); + switch (FORMAT) { case "application/protobuf": case "application/x-protobuf": case "application/vnd.google.protobuf": - case "application/grpc": - case "application/grpc+proto": - case "application/octet-stream": { - //log(`🚧 $response: ${JSON.stringify($response, null, 2)}`, ""); - 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": + switch (PATH) { + case "/bootstrap/v1/bootstrap": + case "/user-customization-service/v1/customize": switch (PATH) { - case "/bootstrap/v1/bootstrap": - case "/user-customization-service/v1/customize": - switch (PATH) { - case "/bootstrap/v1/bootstrap": { - body = BootstrapResponse.fromBinary(rawBody); - log("🚧 调试信息", `body: ${JSON.stringify(body)}`, ""); - let assignedValues = body?.ucsResponseV0?.result?.success?.customization?.result?.success?.resolveResult?.resolveSuccess?.configuration?.assignedValues; - if (assignedValues) { - assignedValues = modifiedAssignedValues(assignedValues); - } - let accountAttributes = body?.ucsResponseV0?.result?.success?.customization?.result?.success?.accountAttributesResult?.accountAttributesSuccess?.accountAttributes; - if (accountAttributes) { - accountAttributes.country_code = { - value: { - oneofKind: "stringValue", - stringValue: Settings.CountryCode, - }, - }; - accountAttributes = modifiedAccountAttributes(accountAttributes); - } - //log(`🚧 调试信息`, `body: ${JSON.stringify(body)}`, ""); - rawBody = BootstrapResponse.toBinary(body); - break; - } - case "/user-customization-service/v1/customize": { - body = UcsResponseWrapper.fromBinary(rawBody); - log("🚧 调试信息", `body: ${JSON.stringify(body)}`, ""); - let assignedValues = body?.result?.success?.resolveResult?.resolveSuccess?.configuration?.assignedValues; - if (assignedValues) { - assignedValues = modifiedAssignedValues(assignedValues); - } - let accountAttributes = body?.result?.success?.accountAttributesResult?.accountAttributesSuccess?.accountAttributes; - if (accountAttributes) { - accountAttributes.country_code = { - value: { - oneofKind: "stringValue", - stringValue: Settings.CountryCode, - }, - }; - accountAttributes = modifiedAccountAttributes(accountAttributes); - } - log("🚧 调试信息", `body: ${JSON.stringify(body)}`, ""); - rawBody = UcsResponseWrapper.toBinary(body); - break; - } + case "/bootstrap/v1/bootstrap": { + body = BootstrapResponse.fromBinary(rawBody); + Console.debug(`body: ${JSON.stringify(body)}`, ""); + let assignedValues = body?.ucsResponseV0?.result?.success?.customization?.result?.success?.resolveResult?.resolveSuccess?.configuration?.assignedValues; + if (assignedValues) { + assignedValues = modifiedAssignedValues(assignedValues); + } + let accountAttributes = body?.ucsResponseV0?.result?.success?.customization?.result?.success?.accountAttributesResult?.accountAttributesSuccess?.accountAttributes; + if (accountAttributes) { + accountAttributes.country_code = { + value: { + oneofKind: "stringValue", + stringValue: Settings.CountryCode, + }, + }; + accountAttributes = modifiedAccountAttributes(accountAttributes); } + //Console.debug(`body: ${JSON.stringify(body)}`, ""); + rawBody = BootstrapResponse.toBinary(body); break; - case "/extended-metadata/v0/extended-metadata": { - body = BatchedExtensionResponse.fromBinary(rawBody); - log("🚧 调试信息", `body: ${JSON.stringify(body)}`, ""); - rawBody = BatchedExtensionResponse.toBinary(body); + } + case "/user-customization-service/v1/customize": { + body = UcsResponseWrapper.fromBinary(rawBody); + Console.debug(`body: ${JSON.stringify(body)}`, ""); + let assignedValues = body?.result?.success?.resolveResult?.resolveSuccess?.configuration?.assignedValues; + if (assignedValues) { + assignedValues = modifiedAssignedValues(assignedValues); + } + let accountAttributes = body?.result?.success?.accountAttributesResult?.accountAttributesSuccess?.accountAttributes; + if (accountAttributes) { + accountAttributes.country_code = { + value: { + oneofKind: "stringValue", + stringValue: Settings.CountryCode, + }, + }; + accountAttributes = modifiedAccountAttributes(accountAttributes); + } + Console.debug(`body: ${JSON.stringify(body)}`, ""); + rawBody = UcsResponseWrapper.toBinary(body); break; } } break; - case "application/grpc": - case "application/grpc+proto": + case "/extended-metadata/v0/extended-metadata": { + body = BatchedExtensionResponse.fromBinary(rawBody); + Console.debug(`body: ${JSON.stringify(body)}`, ""); + rawBody = BatchedExtensionResponse.toBinary(body); break; + } } - // 写入二进制数据 - $response.body = rawBody; break; - } + case "application/grpc": + case "application/grpc+proto": + break; } + // 写入二进制数据 + $response.body = rawBody; break; } } })() - .catch(e => logError(e)) + .catch(e => Console.error(e)) .finally(() => done($response)); diff --git a/src/response.js b/src/response.js index c23ffc3..f73c37f 100644 --- a/src/response.js +++ b/src/response.js @@ -1,4 +1,4 @@ -import { $platform, Lodash as _, Storage, fetch, notification, log, logError, wait, done, getScript, runScript } from "@nsnanocat/util"; +import { $app, Console, done, fetch, Lodash as _, notification, Storage, wait } from "@nsnanocat/util"; import database from "./function/database.mjs"; import setENV from "./function/setENV.mjs"; import setCache from "./function/setCache.mjs"; @@ -7,162 +7,152 @@ import modifiedAccountAttributes from "./function/modifiedAccountAttributes.mjs" import { BootstrapResponse } from "./protobuf/spotify/remoteConfig/Bootstrap.js"; import { UcsResponseWrapper } from "./protobuf/spotify/remoteConfig/Ucs.js"; import { BatchedExtensionResponse } from "./protobuf/spotify/ExtendedMetadata.js"; +Console.debug = () => {}; /***************** Processing *****************/ // 解构URL const url = new URL($request.url); -log(`⚠ url: ${url.toJSON()}`, ""); +Console.info(`url: ${url.toJSON()}`, ""); // 获取连接参数 -const METHOD = $request.method; -const HOST = url.hostname; const PATH = url.pathname; -log(`⚠ METHOD: ${METHOD}, HOST: ${HOST}, PATH: ${PATH}`, ""); +Console.info(`PATH: ${PATH}`, ""); // 解析格式 const FORMAT = ($response.headers?.["Content-Type"] ?? $response.headers?.["content-type"])?.split(";")?.[0]; -log(`⚠ FORMAT: ${FORMAT}`, ""); +Console.info(`FORMAT: ${FORMAT}`, ""); !(async () => { /** * 设置 * @type {{Settings: import('./types').Settings}} */ const { Settings, Caches, Configs } = setENV("DualSubs", "Spotify", database); - log(`⚠ Settings.Switch: ${Settings?.Switch}`, ""); - switch (Settings.Switch) { - case false: + // 获取字幕类型与语言 + const Type = url.searchParams.get("subtype") ?? Settings.Type; + const Languages = [url.searchParams.get("lang")?.toUpperCase?.() ?? Settings.Languages[0], (url.searchParams.get("tlang") ?? Caches?.tlang)?.toUpperCase?.() ?? Settings.Languages[1]]; + Console.info(`Type: ${Type}`, `Languages: ${Languages}`); + // 创建空数据 + let body = {}; + // 格式判断 + switch (FORMAT) { + case undefined: // 视为无body break; - default: { - // 获取字幕类型与语言 - const Type = url.searchParams.get("subtype") ?? Settings.Type; - const Languages = [url.searchParams.get("lang")?.toUpperCase?.() ?? Settings.Languages[0], (url.searchParams.get("tlang") ?? Caches?.tlang)?.toUpperCase?.() ?? Settings.Languages[1]]; - log(`⚠ Type: ${Type}, Languages: ${Languages}`, ""); - // 创建空数据 - let body = {}; - // 格式判断 - 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": - break; - case "text/xml": - case "text/html": - case "text/plist": - case "application/xml": - case "application/plist": - case "application/x-plist": - break; - case "text/vtt": - case "application/vtt": + 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": + break; + case "text/xml": + case "text/html": + case "text/plist": + case "application/xml": + case "application/plist": + case "application/x-plist": + break; + case "text/vtt": + case "application/vtt": + break; + case "text/json": + case "application/json": + body = JSON.parse($response.body ?? "{}"); + Console.debug(`body: ${JSON.stringify(body)}`, ""); + switch (PATH) { + case "/melody/v1/product_state": + body.country = Settings.Country; + body["selected-language"] = Settings.Languages[1].toLowerCase(); break; - case "text/json": - case "application/json": - body = JSON.parse($response.body ?? "{}"); - log(`🚧 body: ${JSON.stringify(body)}`, ""); - switch (PATH) { - case "/melody/v1/product_state": - body.country = Settings.Country; - body["selected-language"] = Settings.Languages[1].toLowerCase(); - break; - case "/v1/tracks": - // biome-ignore lint/complexity/noForEach: - body?.tracks?.forEach?.(track => { - log(`🚧 track: ${JSON.stringify(track)}`, ""); - const trackId = track?.id; - const trackInfo = { - track: track?.name, - album: track?.album?.name, - artist: track?.artists?.[0]?.name, - }; - // 写入数据 - Caches.Metadatas.Tracks.set(trackId, trackInfo); - }); - // 格式化缓存 - Caches.Metadatas.Tracks = setCache(Caches.Metadatas.Tracks, Settings.CacheSize); - // 写入持久化储存 - Storage.setItem(`@DualSubs.${"Spotify"}.Caches.Metadatas.Tracks`, Caches.Metadatas.Tracks); - break; - } - $response.body = JSON.stringify(body); + case "/v1/tracks": + body?.tracks?.forEach?.(track => { + Console.debug(`track: ${JSON.stringify(track)}`, ""); + const trackId = track?.id; + const trackInfo = { + track: track?.name, + album: track?.album?.name, + artist: track?.artists?.[0]?.name, + }; + // 写入数据 + Caches.Metadatas.Tracks.set(trackId, trackInfo); + }); + // 格式化缓存 + Caches.Metadatas.Tracks = setCache(Caches.Metadatas.Tracks, Settings.CacheSize); + // 写入持久化储存 + Storage.setItem(`@DualSubs.${"Spotify"}.Caches.Metadatas.Tracks`, Caches.Metadatas.Tracks); 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/octet-stream": { + let rawBody = $app === "Quantumult X" ? new Uint8Array($response.bodyBytes ?? []) : ($response.body ?? new Uint8Array()); + switch (FORMAT) { case "application/protobuf": case "application/x-protobuf": case "application/vnd.google.protobuf": - case "application/grpc": - case "application/grpc+proto": - case "application/octet-stream": { - let rawBody = $platform === "Quantumult X" ? new Uint8Array($response.bodyBytes ?? []) : ($response.body ?? new Uint8Array()); - switch (FORMAT) { - case "application/protobuf": - case "application/x-protobuf": - case "application/vnd.google.protobuf": + switch (PATH) { + case "/bootstrap/v1/bootstrap": + case "/user-customization-service/v1/customize": switch (PATH) { - case "/bootstrap/v1/bootstrap": - case "/user-customization-service/v1/customize": - switch (PATH) { - case "/bootstrap/v1/bootstrap": { - body = BootstrapResponse.fromBinary(rawBody); - let assignedValues = body?.ucsResponseV0?.result?.success?.customization?.result?.success?.resolveResult?.resolveSuccess?.configuration?.assignedValues; - if (assignedValues) { - assignedValues = modifiedAssignedValues(assignedValues); - } - let accountAttributes = body?.ucsResponseV0?.result?.success?.customization?.result?.success?.accountAttributesResult?.accountAttributesSuccess?.accountAttributes; - if (accountAttributes) { - accountAttributes.country_code = { - value: { - oneofKind: "stringValue", - stringValue: Settings.CountryCode, - }, - }; - accountAttributes = modifiedAccountAttributes(accountAttributes); - } - rawBody = BootstrapResponse.toBinary(body); - break; - } - case "/user-customization-service/v1/customize": { - body = UcsResponseWrapper.fromBinary(rawBody); - let assignedValues = body?.result?.success?.resolveResult?.resolveSuccess?.configuration?.assignedValues; - if (assignedValues) { - assignedValues = modifiedAssignedValues(assignedValues); - } - let accountAttributes = body?.result?.success?.accountAttributesResult?.accountAttributesSuccess?.accountAttributes; - if (accountAttributes) { - accountAttributes.country_code = { - value: { - oneofKind: "stringValue", - stringValue: Settings.CountryCode, - }, - }; - accountAttributes = modifiedAccountAttributes(accountAttributes); - } - rawBody = UcsResponseWrapper.toBinary(body); - break; - } + case "/bootstrap/v1/bootstrap": { + body = BootstrapResponse.fromBinary(rawBody); + let assignedValues = body?.ucsResponseV0?.result?.success?.customization?.result?.success?.resolveResult?.resolveSuccess?.configuration?.assignedValues; + if (assignedValues) { + assignedValues = modifiedAssignedValues(assignedValues); + } + let accountAttributes = body?.ucsResponseV0?.result?.success?.customization?.result?.success?.accountAttributesResult?.accountAttributesSuccess?.accountAttributes; + if (accountAttributes) { + accountAttributes.country_code = { + value: { + oneofKind: "stringValue", + stringValue: Settings.CountryCode, + }, + }; + accountAttributes = modifiedAccountAttributes(accountAttributes); } + rawBody = BootstrapResponse.toBinary(body); break; - case "/extended-metadata/v0/extended-metadata": { - body = BatchedExtensionResponse.fromBinary(rawBody); - rawBody = BatchedExtensionResponse.toBinary(body); + } + case "/user-customization-service/v1/customize": { + body = UcsResponseWrapper.fromBinary(rawBody); + let assignedValues = body?.result?.success?.resolveResult?.resolveSuccess?.configuration?.assignedValues; + if (assignedValues) { + assignedValues = modifiedAssignedValues(assignedValues); + } + let accountAttributes = body?.result?.success?.accountAttributesResult?.accountAttributesSuccess?.accountAttributes; + if (accountAttributes) { + accountAttributes.country_code = { + value: { + oneofKind: "stringValue", + stringValue: Settings.CountryCode, + }, + }; + accountAttributes = modifiedAccountAttributes(accountAttributes); + } + rawBody = UcsResponseWrapper.toBinary(body); break; } } break; - case "application/grpc": - case "application/grpc+proto": + case "/extended-metadata/v0/extended-metadata": { + body = BatchedExtensionResponse.fromBinary(rawBody); + rawBody = BatchedExtensionResponse.toBinary(body); break; + } } - // 写入二进制数据 - $response.body = rawBody; break; - } + case "application/grpc": + case "application/grpc+proto": + break; } + // 写入二进制数据 + $response.body = rawBody; break; } } })() - .catch(e => logError(e)) + .catch(e => Console.error(e)) .finally(() => done($response)); diff --git a/template/boxjs.settings.json b/template/boxjs.settings.json index 827b4a8..105144c 100644 --- a/template/boxjs.settings.json +++ b/template/boxjs.settings.json @@ -1 +1 @@ -[{"id":"@DualSubs.Spotify.Settings.Switch","name":"总功能开关","type":"boolean","val":true,"desc":"是否启用此APP修改"},{"id":"@DualSubs.Spotify.Settings.Types","name":"[歌词] 启用类型(多选)","type":"checkboxes","val":["Translate","External"],"items":[{"key":"Translate","label":"翻译歌词(翻译器)"},{"key":"External","label":"外部歌词(外部源)"}],"desc":"请选择要添加的歌词选项,如果为多选,则会自动决定提供的歌词类型。"},{"id":"@DualSubs.Spotify.Settings.Languages[0]","name":"[翻译器] 主语言(源语言)","type":"selects","val":"AUTO","items":[{"key":"AUTO","label":"自动 - Automatic"},{"key":"ZH","label":"中文(自动)"},{"key":"ZH-HANS","label":"中文(简体)"},{"key":"ZH-HK","label":"中文(香港)"},{"key":"ZH-HANT","label":"中文(繁体)"},{"key":"EN","label":"English - 英语(自动)"},{"key":"ES","label":"Español - 西班牙语(自动)"},{"key":"JA","label":"日本語 - 日语"},{"key":"KO","label":"한국어 - 韩语"},{"key":"DE","label":"Deutsch - 德语"},{"key":"FR","label":"Français - 法语"},{"key":"TR","label":"Türkçe - 土耳其语"},{"key":"KM","label":"ភាសាខ្មែរ - 高棉语"}],"desc":"仅当源语言识别不准确时更改此选项。"},{"id":"@DualSubs.Spotify.Settings.Languages[1]","name":"[翻译器] 副语言(目标语言)","type":"selects","val":"ZH","items":[{"key":"ZH","label":"中文(自动)"},{"key":"ZH-HANS","label":"中文(简体)"},{"key":"ZH-HK","label":"中文(香港)"},{"key":"ZH-HANT","label":"中文(繁体)"},{"key":"EN","label":"English - 英语(自动)"},{"key":"EN-US","label":"英语(美国)"},{"key":"ES","label":"Español - 西班牙语(自动)"},{"key":"ES-ES","label":"Español - 西班牙语"},{"key":"ES-419","label":"西班牙语(拉丁美洲)"},{"key":"JA","label":"日本語 - 日语"},{"key":"KO","label":"한국어 - 韩语"},{"key":"DE","label":"Deutsch - 德语"},{"key":"FR","label":"Français - 法语"},{"key":"TR","label":"Türkçe - 土耳其语"},{"key":"KM","label":"ភាសាខ្មែរ - 高棉语"}],"desc":"请指定翻译歌词的目标语言。"},{"id":"@DualSubs.Spotify.Settings.Vendor","name":"[翻译器] 服务商API","type":"selects","val":"Google","items":[{"key":"Google","label":"Google Translate"},{"key":"Microsoft","label":"Microsoft Translator(需填写API)"}],"desc":"请选择翻译器所使用的服务商API,更多翻译选项请使用BoxJs。"},{"id":"@DualSubs.Spotify.Settings.LrcVendor","name":"[歌词] 服务商API","type":"selects","val":"NeteaseMusic","items":[{"key":"NeteaseMusic","label":"网易云音乐(官方)"},{"key":"QQMusic","label":"QQ音乐(官方)"},{"key":"NeteaseMusicNodeJS","label":"网易云音乐 NodeJS API"}],"desc":"请选择外部源所使用的服务商API。"}] \ No newline at end of file +[{"id":"@DualSubs.Spotify.Settings.Types","name":"[歌词] 启用类型(多选)","type":"checkboxes","val":["Translate","External"],"items":[{"key":"Translate","label":"翻译歌词(翻译器)"},{"key":"External","label":"外部歌词(外部源)"}],"desc":"请选择要添加的歌词选项,如果为多选,则会自动决定提供的歌词类型。"},{"id":"@DualSubs.Spotify.Settings.Languages[0]","name":"[翻译器] 主语言(源语言)","type":"selects","val":"AUTO","items":[{"key":"AUTO","label":"自动 - Automatic"},{"key":"ZH","label":"中文(自动)"},{"key":"ZH-HANS","label":"中文(简体)"},{"key":"ZH-HK","label":"中文(香港)"},{"key":"ZH-HANT","label":"中文(繁体)"},{"key":"EN","label":"English - 英语(自动)"},{"key":"ES","label":"Español - 西班牙语(自动)"},{"key":"JA","label":"日本語 - 日语"},{"key":"KO","label":"한국어 - 韩语"},{"key":"DE","label":"Deutsch - 德语"},{"key":"FR","label":"Français - 法语"},{"key":"TR","label":"Türkçe - 土耳其语"},{"key":"KM","label":"ភាសាខ្មែរ - 高棉语"}],"desc":"仅当源语言识别不准确时更改此选项。"},{"id":"@DualSubs.Spotify.Settings.Languages[1]","name":"[翻译器] 副语言(目标语言)","type":"selects","val":"ZH","items":[{"key":"ZH","label":"中文(自动)"},{"key":"ZH-HANS","label":"中文(简体)"},{"key":"ZH-HK","label":"中文(香港)"},{"key":"ZH-HANT","label":"中文(繁体)"},{"key":"EN","label":"English - 英语(自动)"},{"key":"EN-US","label":"英语(美国)"},{"key":"ES","label":"Español - 西班牙语(自动)"},{"key":"ES-ES","label":"Español - 西班牙语"},{"key":"ES-419","label":"西班牙语(拉丁美洲)"},{"key":"JA","label":"日本語 - 日语"},{"key":"KO","label":"한국어 - 韩语"},{"key":"DE","label":"Deutsch - 德语"},{"key":"FR","label":"Français - 法语"},{"key":"TR","label":"Türkçe - 土耳其语"},{"key":"KM","label":"ភាសាខ្មែរ - 高棉语"}],"desc":"请指定翻译歌词的目标语言。"},{"id":"@DualSubs.Spotify.Settings.Vendor","name":"[翻译器] 服务商API","type":"selects","val":"Google","items":[{"key":"Google","label":"Google Translate"},{"key":"Microsoft","label":"Microsoft Translator(需填写API)"}],"desc":"请选择翻译器所使用的服务商API,更多翻译选项请使用BoxJs。"},{"id":"@DualSubs.Spotify.Settings.LrcVendor","name":"[歌词] 服务商API","type":"selects","val":"NeteaseMusic","items":[{"key":"NeteaseMusic","label":"网易云音乐(官方)"},{"key":"QQMusic","label":"QQ音乐(官方)"},{"key":"NeteaseMusicNodeJS","label":"网易云音乐 NodeJS API"}],"desc":"请选择外部源所使用的服务商API。"}] \ No newline at end of file diff --git a/template/egern.handlebars b/template/egern.handlebars deleted file mode 100644 index e54c322..0000000 --- a/template/egern.handlebars +++ /dev/null @@ -1,59 +0,0 @@ -name: "{{@package 'displayName'}}" -description: |- - {{#each (split (@package 'description') "\n")}} - {{{this}}} - {{/each}} -open_url: "{{@package 'openUrl'}}" -author: |- - {{#each (@package 'contributors')}} - {{this}} - {{/each}} -homepage: "{{@package 'homepage'}}" -icon: "{{@package 'icon'}}" -category: "{{@package 'organizationName'}}" -date: "{{now "yyyy-MM-dd HH:mm:ss"}}" -version: "{{@package 'version'}}" - -scriptings: -- http_response: - name: '🍿️ DualSubs.Spotify.Tracks.response.json' - match: ^https?:\/\/api\.spotify\.com\/v1\/tracks\? - script_url: https://github.com/DualSubs/Spotify/releases/download/v{{@package 'version'}}/response.bundle.js - body_required: true -- http_request: - name: '🍿️ DualSubs.Spotify.Lyrics.request.json' - match: ^https?:\/\/spclient\.wg\.spotify\.com\/color-lyrics\/v2\/track\/(.+)\?(.*)format=json - script_url: https://github.com/DualSubs/Spotify/releases/download/v{{@package 'version'}}/request.bundle.js - body_required: true -- http_request: - name: '🍿️ DualSubs.Spotify.Lyrics.request.proto' - match: ^https?:\/\/spclient\.wg\.spotify\.com\/color-lyrics\/v2\/track\/\w+\?(.*) - script_url: https://github.com/DualSubs/Spotify/releases/download/v{{@package 'version'}}/request.bundle.js - body_required: true - binary_body: true -- http_response: - name: '🍿️ DualSubs.Spotify.Translate.Lyrics.response.json' - match: ^https?:\/\/spclient\.wg\.spotify\.com\/color-lyrics\/v2\/track\/(.+)\?(.*)format=json(.*)subtype=Translate - script_url: https://github.com/DualSubs/Universal/releases/latest/download/Translate.response.bundle.js - body_required: true -- http_response: - name: '🍿️ DualSubs.Spotify.Translate.Lyrics.response.proto' - match: ^https?:\/\/spclient\.wg\.spotify\.com\/color-lyrics\/v2\/track\/\w+\?(.*)subtype=Translate - script_url: https://github.com/DualSubs/Universal/releases/latest/download/Translate.response.bundle.js - body_required: true - binary_body: true -- http_response: - name: '🍿️ DualSubs.Spotify.External.Lyrics.response.json' - match: ^https?:\/\/spclient\.wg\.spotify\.com\/color-lyrics\/v2\/track\/(.+)\?(.*)format=json(.*)subtype=External - script_url: https://github.com/DualSubs/Universal/releases/latest/download/External.Lyrics.response.bundle.js - body_required: true -- http_response: - name: '🍿️ DualSubs.Spotify.External.Lyrics.response.proto' - match: ^https?:\/\/spclient\.wg\.spotify\.com\/color-lyrics\/v2\/track\/\w+\?(.*)subtype=External - script_url: https://github.com/DualSubs/Universal/releases/latest/download/External.Lyrics.response.bundle.js - body_required: true - binary_body: true -mitm: - hostnames: - - api.spotify.com - - spclient.wg.spotify.com