UNPKG

@ikenxuan/amagi

Version:

抖音、B站的 web 端相关数据接口基于 Node.js 的实现

1,538 lines (1,523 loc) 134 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var chalk = require('chalk'); var log4js = require('log4js'); var path = require('path'); var url = require('url'); var fs = require('fs'); var axios = require('axios'); var crypto = require('crypto'); var zod = require('zod'); var express = require('express'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var log4js__default = /*#__PURE__*/_interopDefault(log4js); var path__default = /*#__PURE__*/_interopDefault(path); var fs__default = /*#__PURE__*/_interopDefault(fs); var axios__default = /*#__PURE__*/_interopDefault(axios); var crypto__default = /*#__PURE__*/_interopDefault(crypto); var express__default = /*#__PURE__*/_interopDefault(express); /*! * @ikenxuan/amagi * Copyright(c) 2023 ikenxuan * GPL-3.0 Licensed */ // node_modules/.pnpm/tsup@8.5.0_@swc+core@1.13.3_tsx@4.20.3_typescript@5.9.2/node_modules/tsup/assets/cjs_shims.js var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href; var importMetaUrl = /* @__PURE__ */ getImportMetaUrl(); var getPackageLogsPath = () => { const currentFileUrl = importMetaUrl; const currentFilePath = url.fileURLToPath(currentFileUrl); const currentDir = path__default.default.dirname(currentFilePath); let packageRoot = currentDir; while (packageRoot !== path__default.default.dirname(packageRoot)) { if (fs__default.default.existsSync(path__default.default.join(packageRoot, "package.json"))) { break; } packageRoot = path__default.default.dirname(packageRoot); } return path__default.default.join(packageRoot, "logs"); }; var logsPath = getPackageLogsPath(); log4js__default.default.configure({ appenders: { console: { type: "stdout", layout: { type: "pattern", pattern: "%[[amagi][%d{hh:mm:ss.SSS}][%4.4p]%] %m" } }, command: { type: "dateFile", filename: path__default.default.join(logsPath, "application", "command"), pattern: "yyyy-MM-dd.log", numBackups: 15, alwaysIncludePattern: true, layout: { type: "pattern", pattern: "[%d{hh:mm:ss.SSS}][%4.4p] %m" } }, httpConsole: { type: "stdout", layout: { type: "pattern", pattern: "%[[amagi][%d{hh:mm:ss.SSS}][HTTP]%] %m" } }, httpRequest: { type: "dateFile", filename: path__default.default.join(logsPath, "http", "requests"), pattern: "yyyy-MM-dd.log", numBackups: 30, alwaysIncludePattern: true, layout: { type: "pattern", pattern: "[%d{hh:mm:ss.SSS}][%4.4p] %m" } } }, categories: { default: { appenders: ["console", "command"], level: "info" }, http: { appenders: ["httpConsole", "httpRequest"], level: "debug" } }, pm2: true }); var CustomLogger = class { logger; chalk; red; green; yellow; blue; magenta; cyan; white; gray; constructor(name) { this.logger = log4js__default.default.getLogger(name); this.chalk = new chalk.Chalk(); this.red = this.chalk.red; this.green = this.chalk.green; this.yellow = this.chalk.yellow; this.blue = this.chalk.blue; this.magenta = this.chalk.magenta; this.cyan = this.chalk.cyan; this.white = this.chalk.white; this.gray = this.chalk.gray; } // 代理 log4js.Logger 的方法 info(message, ...args) { this.logger.info(message, ...args); } warn(message, ...args) { this.logger.warn(message, ...args); } error(message, ...args) { this.logger.error(message, ...args); } mark(message, ...args) { this.logger.mark(message, ...args); } debug(message, ...args) { this.logger.debug(message, ...args); } }; var logger = new CustomLogger("default"); var httpLogger = new CustomLogger("http"); var logMiddleware = (pathsToLog) => { return (req, res, next) => { if (!pathsToLog || pathsToLog.some((path2) => req.url.startsWith(path2))) { const startTime = Date.now(); const url = req.url; const method = req.method; const clientIP = req.headers["x-forwarded-for"] || req.socket.remoteAddress; const referer = req.headers["referer"] || req.headers["referrer"] || "-"; const contentType = req.headers["content-type"] || "-"; const requestSize = req.headers["content-length"] || "0"; const protocol = req.protocol; const httpVersion = req.httpVersion; res.on("finish", () => { const responseTime = Date.now() - startTime; const statusCode = res.statusCode; const responseSize = res.get("content-length") || "0"; const logData = { method, url, statusCode, responseTime: `${responseTime}ms`, clientIP, referer, contentType, requestSize: `${requestSize}B`, responseSize: `${responseSize}B`, protocol, httpVersion, timestamp: (/* @__PURE__ */ new Date()).toISOString() }; httpLogger.debug(JSON.stringify(logData)); }); } next(); }; }; var cleanUserAgent = (userAgent) => { return userAgent.replace(/\s+Edg\/[\d\.]+/g, ""); }; var fetchData = async (config) => { try { const cleanedConfig = { ...config }; if (cleanedConfig.headers && cleanedConfig.headers["User-Agent"]) { cleanedConfig.headers["User-Agent"] = cleanUserAgent(cleanedConfig.headers["User-Agent"]); } const response = await axios__default.default(cleanedConfig); if (response.status === 429) { logger.error("HTTP \u54CD\u5E94\u72B6\u6001\u7801: 429"); throw new Error("ratelimit triggered, \u89E6\u53D1\u901F\u7387\u9650\u5236\uFF01"); } return response.data; } catch (error) { if (error instanceof axios.AxiosError) { logger.error("\u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25:", error.message); throw error; } throw error; } }; var fetchResponse = async (config) => { try { const cleanedConfig = { ...config }; if (cleanedConfig.headers && cleanedConfig.headers["User-Agent"]) { cleanedConfig.headers["User-Agent"] = cleanUserAgent(cleanedConfig.headers["User-Agent"]); } return await axios__default.default(cleanedConfig); } catch (error) { if (error instanceof axios.AxiosError) { throw error; } throw new Error("\u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25"); } }; var getHeadersAndData = async (config) => { try { const response = await fetchResponse(config); return { headers: response.headers, data: response.data }; } catch (error) { logger.error("\u83B7\u53D6\u54CD\u5E94\u5934\u548C\u6570\u636E\u5931\u8D25:", error); return { headers: {}, data: {} }; } }; // src/platform/bilibili/qtparam.ts var qtparam = async (BASEURL, cookie) => { if (cookie === "") return { QUERY: "&platform=html5", STATUS: "!isLogin" }; const logininfo = await fetchData({ url: bilibiliApiUrls.\u767B\u5F55\u57FA\u672C\u4FE1\u606F(), headers: { Cookie: cookie } }); const sign = await wbi_sign(BASEURL, cookie); const qn = [6, 16, 32, 64, 74, 80, 112, 116, 120, 125, 126, 127]; let isvip; logininfo.data.vipStatus === 1 ? isvip = true : isvip = false; if (isvip) { return { QUERY: `&fnval=16&fourk=1&${sign}`, STATUS: "isLogin", isvip }; } else return { QUERY: `&qn=${qn[3]}&fnval=16`, STATUS: "isLogin", isvip }; }; // src/platform/bilibili/sign/bv2av.ts var XOR_CODE = 23442827791579n; var MASK_CODE = 2251799813685247n; var MAX_AID = 1n << 51n; var BASE = 58n; var data = "FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf"; var av2bv = (aid) => { const bytes = ["B", "V", "1", "0", "0", "0", "0", "0", "0", "0", "0", "0"]; let bvIndex = bytes.length - 1; let tmp = (MAX_AID | BigInt(aid)) ^ XOR_CODE; while (tmp > 0) { bytes[bvIndex] = data[Number(tmp % BigInt(BASE))]; tmp = tmp / BASE; bvIndex -= 1; } [bytes[3], bytes[9]] = [bytes[9], bytes[3]]; [bytes[4], bytes[7]] = [bytes[7], bytes[4]]; return bytes.join(""); }; var bv2av = (bvid) => { const bvidArr = Array.from(bvid); [bvidArr[3], bvidArr[9]] = [bvidArr[9], bvidArr[3]]; [bvidArr[4], bvidArr[7]] = [bvidArr[7], bvidArr[4]]; bvidArr.splice(0, 3); const tmp = bvidArr.reduce((pre, bvidChar) => pre * BASE + BigInt(data.indexOf(bvidChar)), 0n); return Number(tmp & MASK_CODE ^ XOR_CODE); }; // src/platform/bilibili/API.ts var BiLiBiLiAPI = class { \u767B\u5F55\u57FA\u672C\u4FE1\u606F() { return "https://api.bilibili.com/x/web-interface/nav"; } \u89C6\u9891\u8BE6\u7EC6\u4FE1\u606F(data2) { return `https://api.bilibili.com/x/web-interface/view?bvid=${data2.bvid}`; } \u89C6\u9891\u6D41\u4FE1\u606F(data2) { return `https://api.bilibili.com/x/player/playurl?avid=${data2.avid}&cid=${data2.cid}`; } /** 评论区类型,type参数详见 [评论区类型代码](https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/comment/readme.md#评论区类型代码) */ \u8BC4\u8BBA\u533A\u660E\u7EC6(data2) { const params = new URLSearchParams({ oid: data2.oid.toString(), type: data2.type.toString(), mode: (data2.mode ?? 3).toString(), plat: "1", seek_rpid: "", web_location: "1315875" }); if (data2.pagination_str) { params.append("pagination_str", JSON.stringify({ offset: data2.pagination_str })); } else { params.append("pagination_str", JSON.stringify({ offset: "" })); } return `https://api.bilibili.com/x/v2/reply/wbi/main?${params.toString()}`; } \u8BC4\u8BBA\u533A\u72B6\u6001(data2) { return `https://api.bilibili.com/x/v2/reply/subject/description?type=${data2.type}&oid=${data2.oid}`; } \u8868\u60C5\u5217\u8868() { return "https://api.bilibili.com/x/emote/user/panel/web?business=reply&web_location=0.0"; } \u756A\u5267\u660E\u7EC6(data2) { if (data2.ep_id) { return `https://api.bilibili.com/pgc/view/web/season?ep_id=${data2.ep_id}`; } else if (data2.season_id) { return `https://api.bilibili.com/pgc/view/web/season?season_id=${data2.season_id}`; } else { throw new Error("\u62DF\u9020\u63A5\u53E3\u5730\u5740\u51FA\u9519\uFF0C\u7F3A\u5C11 ep_id \u6216 season_id \u53C2\u6570"); } } \u756A\u5267\u89C6\u9891\u6D41\u4FE1\u606F(data2) { return `https://api.bilibili.com/pgc/player/web/playurl?cid=${data2.cid}&ep_id=${data2.ep_id}`; } \u7528\u6237\u7A7A\u95F4\u52A8\u6001(data2) { return `https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?host_mid=${data2.host_mid}`; } \u52A8\u6001\u8BE6\u60C5(data2) { return `https://api.bilibili.com/x/polymer/web-dynamic/v1/detail?id=${data2.dynamic_id}`; } \u52A8\u6001\u5361\u7247\u4FE1\u606F(data2) { return `https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/get_dynamic_detail?dynamic_id=${data2.dynamic_id}`; } \u7528\u6237\u540D\u7247\u4FE1\u606F(data2) { return `https://api.bilibili.com/x/web-interface/card?mid=${data2.host_mid}&photo=true`; } \u76F4\u64AD\u95F4\u4FE1\u606F(data2) { return `https://api.live.bilibili.com/room/v1/Room/get_info?room_id=${data2.room_id}`; } \u76F4\u64AD\u95F4\u521D\u59CB\u5316\u4FE1\u606F(data2) { return `https://api.live.bilibili.com/room/v1/Room/room_init?id=${data2.room_id}`; } \u7533\u8BF7\u4E8C\u7EF4\u7801() { return "https://passport.bilibili.com/x/passport-login/web/qrcode/generate"; } \u4E8C\u7EF4\u7801\u72B6\u6001(data2) { return `https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key=${data2.qrcode_key}`; } \u83B7\u53D6UP\u4E3B\u603B\u64AD\u653E\u91CF(data2) { return `https://api.bilibili.com/x/space/upstat?mid=${data2.host_mid}`; } }; var bilibiliApiUrls = new BiLiBiLiAPI(); // src/types/NetworksConfigType.ts var kuaishouAPIErrorCode = /* @__PURE__ */ ((kuaishouAPIErrorCode2) => { kuaishouAPIErrorCode2["COOKIE"] = "INVALID_COOKIE"; kuaishouAPIErrorCode2["UNKNOWN"] = "UNKNOWN_ERROR" /* UNKNOWN */; return kuaishouAPIErrorCode2; })(kuaishouAPIErrorCode || {}); var mixinKeyEncTab = [ 46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49, 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40, 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11, 36, 20, 34, 44, 52 ]; var getMixinKey = (orig) => mixinKeyEncTab.map((n) => orig[n]).join("").slice(0, 32); var encWbi = (params, img_key, sub_key) => { const mixin_key = getMixinKey(img_key + sub_key); const curr_time = Math.round(Date.now() / 1e3); const chr_filter = /[!'()*]/g; Object.assign(params, { wts: curr_time }); const query = Object.keys(params).sort().map((key) => { const value = params[key].toString().replace(chr_filter, ""); return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`; }).join("&"); const wbi_sign2 = crypto__default.default.createHash("md5").update(query + mixin_key).digest("hex"); return `&wts=${curr_time}&w_rid=${wbi_sign2}`; }; var getWbiKeys = async (cookie) => { const res = await axios__default.default("https://api.bilibili.com/x/web-interface/nav", { headers: { Cookie: cookie } }); const response = res.data; const { data: { wbi_img: { img_url, sub_url } } } = response; return { img_key: img_url.slice(img_url.lastIndexOf("/") + 1, img_url.lastIndexOf(".")), sub_key: sub_url.slice(sub_url.lastIndexOf("/") + 1, sub_url.lastIndexOf(".")) }; }; var wbi_sign = async (BASEURL, cookie) => { const web_keys = await getWbiKeys(cookie); const url = new URL(BASEURL); const params = {}; for (const [key, value] of url.searchParams.entries()) { params[key] = value; } const query = encWbi(params, web_keys.img_key, web_keys.sub_key); return query; }; // src/platform/defaultConfigs.ts var generateSecChUa = (userAgent) => { const chromeMatch = userAgent.match(/Chrome\/(\d+)/); const chromeVersion = chromeMatch ? chromeMatch[1] : "125"; return `"Not)A;Brand";v="8", "Chromium";v="${chromeVersion}", "Google Chrome";v="${chromeVersion}"`; }; var getDouyinDefaultConfig = (cookie, requestConfig) => { var _a; let finalUserAgent = ((_a = void 0 ) == null ? void 0 : _a["User-Agent"]) || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"; finalUserAgent = finalUserAgent.replace(/\s+Edg\/[\d\.]+/g, ""); const defHeaders = { Accept: "application/json, text/plain, */*", "Accept-Encoding": "gzip, deflate, br, zstd", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6", Cookie: cookie ? cookie.replace(/\s+/g, "") : "", Priority: "u=1, i", Referer: "https://www.douyin.com/", "Sec-Ch-Ua": generateSecChUa(finalUserAgent), "Sec-Ch-Ua-Mobile": "?0", "Sec-Ch-Ua-Platform": '"Windows"', "Sec-Fetch-Dest": "empty", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Site": "same-origin", "User-Agent": finalUserAgent }; return { method: "GET", timeout: 1e4, ...requestConfig, headers: { ...defHeaders, ...{} } }; }; var getBilibiliDefaultConfig = (cookie, requestConfig) => { const defHeaders = { Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6", "Cache-Control": "max-age=0", Priority: "u=0, i", "Sec-Ch-Ua": '"Microsoft Edge";v="131", "Chromium";v="131", "Not_A Brand";v="24"', "Sec-Ch-Ua-Mobile": "?0", "Sec-Ch-Ua-Platform": '"Windows"', "Sec-Fetch-Dest": "document", "Sec-Fetch-Mode": "navigate", "Sec-Fetch-Site": "none", "Sec-Fetch-User": "?1", "Upgrade-Insecure-Requests": "1", Referer: "https://www.bilibili.com/", Cookie: cookie ? cookie.replace(/\s+/g, "") : "" }; return { method: "GET", timeout: 1e4, ...requestConfig, headers: { ...defHeaders, ...{} } }; }; var getKuaishouDefaultConfig = (cookie, requestConfig) => { const defHeaders = { Referer: "https://www.kuaishou.com/new-reco", Origin: "https://www.kuaishou.com", Accept: "application/json, text/plain, */*", "Accept-Encoding": "gzip, deflate, br, zstd", "Content-Type": "application/json", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6", Priority: "u=0, i", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0", Cookie: cookie ? cookie.replace(/\s+/g, "") : "" }; return { method: "POST", timeout: 1e4, ...requestConfig, headers: { ...defHeaders, ...{} } }; }; // src/platform/bilibili/getdata.ts var fetchBilibili = async (data2, cookie, requestConfig) => { var _a, _b, _c, _d, _e, _f; const defHeaders = getBilibiliDefaultConfig(cookie)["headers"]; const baseRequestConfig = { method: "GET", timeout: 1e4, ...requestConfig, headers: { ...defHeaders, ...{} } }; switch (data2.methodType) { case "\u5355\u4E2A\u89C6\u9891\u4F5C\u54C1\u6570\u636E": { const INFODATA = await GlobalGetData(data2.methodType, { ...baseRequestConfig, url: bilibiliApiUrls.\u89C6\u9891\u8BE6\u7EC6\u4FE1\u606F({ bvid: data2.bvid }) }); return INFODATA; } case "\u5355\u4E2A\u89C6\u9891\u4E0B\u8F7D\u4FE1\u606F\u6570\u636E": { const BASEURL = bilibiliApiUrls.\u89C6\u9891\u6D41\u4FE1\u606F({ avid: data2.avid, cid: data2.cid }); const SIGN = await qtparam(BASEURL, (_a = baseRequestConfig.headers) == null ? void 0 : _a.cookie); const DATA = await GlobalGetData(data2.methodType, { ...baseRequestConfig, url: bilibiliApiUrls.\u89C6\u9891\u6D41\u4FE1\u606F({ avid: data2.avid, cid: data2.cid }) + SIGN.QUERY }); return DATA; } case "\u8BC4\u8BBA\u6570\u636E": { let { oid, number, type, mode, pagination_str, plat, seek_rpid, web_location } = data2; let fetchedComments = []; const maxRequestCount = 100; let requestCount = 0; let tmpresp; let nextPaginationStr = pagination_str; let isEnd = false; const checkStatusUrl = bilibiliApiUrls.\u8BC4\u8BBA\u533A\u72B6\u6001({ oid, type }); const checkStatusRes = await GlobalGetData(data2.methodType, { ...baseRequestConfig, url: checkStatusUrl }); if (checkStatusRes.data === null) { logger.error("\u8BC4\u8BBA\u533A\u672A\u5F00\u653E"); return { code: 404, message: "\u8BC4\u8BBA\u533A\u672A\u5F00\u653E", data: null }; } while (fetchedComments.length < Number(number ?? 20) && requestCount < maxRequestCount && !isEnd) { const baseUrl = bilibiliApiUrls.\u8BC4\u8BBA\u533A\u660E\u7EC6({ type, oid, mode: mode ?? 3, pagination_str: nextPaginationStr, plat: plat ?? 1, seek_rpid, web_location: web_location ?? "1315875" }); const wbiSignQuery = await wbi_sign(baseUrl, (_b = baseRequestConfig.headers) == null ? void 0 : _b.cookie); const finalUrl = baseUrl + wbiSignQuery; const response = await GlobalGetData(data2.methodType, { ...baseRequestConfig, url: finalUrl }); tmpresp = response; const currentComments = ((_c = response.data) == null ? void 0 : _c.replies) || []; fetchedComments.push(...currentComments); if ((_d = response.data) == null ? void 0 : _d.cursor) { nextPaginationStr = (_e = response.data.cursor.pagination_reply) == null ? void 0 : _e.next_offset; isEnd = response.data.cursor.is_end; } else { isEnd = true; } requestCount++; if (isEnd || currentComments.length === 0 || !nextPaginationStr) { logger.info("\u5DF2\u5230\u8FBE\u8BC4\u8BBA\u672B\u5C3E\u6216\u65E0\u66F4\u591A\u8BC4\u8BBA"); break; } } const finalResponse = { ...tmpresp, data: { ...tmpresp.data, // 去重并限制数量 replies: Array.from(new Map(fetchedComments.map((item) => [item.rpid, item])).values()).slice(0, Number(data2.number || 20)) } }; return finalResponse; } case "Emoji\u6570\u636E": { return await GlobalGetData(data2.methodType, { ...baseRequestConfig, url: bilibiliApiUrls.\u8868\u60C5\u5217\u8868() }); } case "\u756A\u5267\u57FA\u672C\u4FE1\u606F\u6570\u636E": { let id = data2.ep_id ? data2.ep_id : data2.season_id; if (!id) { return false; } const idType = id ? id.startsWith("ep") ? "ep_id" : "season_id" : "ep_id"; const newId = idType === "ep_id" ? id.replace("ep", "") : id.replace("ss", ""); const INFO = await GlobalGetData(data2.methodType, { ...baseRequestConfig, url: bilibiliApiUrls.\u756A\u5267\u660E\u7EC6({ [idType]: newId }) }); return INFO; } case "\u756A\u5267\u4E0B\u8F7D\u4FE1\u606F\u6570\u636E": { const BASEURL = bilibiliApiUrls.\u756A\u5267\u89C6\u9891\u6D41\u4FE1\u606F({ cid: data2.cid, ep_id: data2.ep_id.replace("ep", "") }); const SIGN = await qtparam(BASEURL, (_f = baseRequestConfig.headers) == null ? void 0 : _f.cookie); const DATA = await GlobalGetData(data2.methodType, { ...baseRequestConfig, url: bilibiliApiUrls.\u756A\u5267\u89C6\u9891\u6D41\u4FE1\u606F({ cid: data2.cid, ep_id: data2.ep_id.replace("ep", "") }) + SIGN.QUERY }); return DATA; } case "\u7528\u6237\u4E3B\u9875\u52A8\u6001\u5217\u8868\u6570\u636E": { const customConfig = { ...baseRequestConfig, headers: { ...baseRequestConfig.headers, // 只有在外部配置没有referer时才删除内部的referer ...{ referer: void 0 } } }; const { host_mid } = data2; const result = await GlobalGetData(data2.methodType, { ...customConfig, url: bilibiliApiUrls.\u7528\u6237\u7A7A\u95F4\u52A8\u6001({ host_mid }) }); return result; } case "\u52A8\u6001\u8BE6\u60C5\u6570\u636E": { const customConfig = { ...baseRequestConfig, headers: { ...baseRequestConfig.headers, // 只有在外部配置没有referer时才删除内部的referer ...{ referer: void 0 } } }; const dynamicINFO = await GlobalGetData(data2.methodType, { ...customConfig, url: bilibiliApiUrls.\u52A8\u6001\u8BE6\u60C5({ dynamic_id: data2.dynamic_id }) }); return dynamicINFO; } case "\u52A8\u6001\u5361\u7247\u6570\u636E": { const customConfig = { ...baseRequestConfig, headers: { ...baseRequestConfig.headers, // 只有在外部配置没有referer时才删除内部的referer ...{ referer: void 0 } } }; const { dynamic_id } = data2; const dynamicINFO_CARD = await GlobalGetData(data2.methodType, { ...customConfig, url: bilibiliApiUrls.\u52A8\u6001\u5361\u7247\u4FE1\u606F({ dynamic_id }) }); return dynamicINFO_CARD; } case "\u7528\u6237\u4E3B\u9875\u6570\u636E": { const { host_mid } = data2; const result = await GlobalGetData(data2.methodType, { ...baseRequestConfig, url: bilibiliApiUrls.\u7528\u6237\u540D\u7247\u4FE1\u606F({ host_mid }) }); return result; } case "\u76F4\u64AD\u95F4\u4FE1\u606F": { const result = await GlobalGetData(data2.methodType, { ...baseRequestConfig, url: bilibiliApiUrls.\u76F4\u64AD\u95F4\u4FE1\u606F({ room_id: data2.room_id }) }); return result; } case "\u76F4\u64AD\u95F4\u521D\u59CB\u5316\u4FE1\u606F": { const result = await GlobalGetData(data2.methodType, { ...baseRequestConfig, url: bilibiliApiUrls.\u76F4\u64AD\u95F4\u521D\u59CB\u5316\u4FE1\u606F({ room_id: data2.room_id }) }); return result; } case "\u7533\u8BF7\u4E8C\u7EF4\u7801": { const result = await GlobalGetData(data2.methodType, { ...baseRequestConfig, url: bilibiliApiUrls.\u7533\u8BF7\u4E8C\u7EF4\u7801() }); return result; } case "\u4E8C\u7EF4\u7801\u72B6\u6001": { try { const result = await getHeadersAndData({ ...baseRequestConfig, url: bilibiliApiUrls.\u4E8C\u7EF4\u7801\u72B6\u6001({ qrcode_key: data2.qrcode_key }) }); if (result.data.code !== 0) { const errorMessage = bilibiliErrorCodeMap[String(result.data.code)] || result.data.message || "\u672A\u77E5\u9519\u8BEF"; const Err = { errorDescription: `\u83B7\u53D6\u54CD\u5E94\u6570\u636E\u5931\u8D25\uFF01\u539F\u56E0\uFF1A${errorMessage}\uFF01`, requestType: data2.methodType, requestUrl: bilibiliApiUrls.\u4E8C\u7EF4\u7801\u72B6\u6001({ qrcode_key: data2.qrcode_key }) }; return { code: result.data.code, data: result.data, amagiError: Err }; } return { code: 0, data: { data: result.data.data, headers: result.headers }, message: "0" }; } catch (error) { if (error && typeof error === "object") { return error; } return { code: "UNKNOWN_ERROR" /* UNKNOWN */, data: error.data, amagiError: { errorDescription: "\u672A\u77E5\u9519\u8BEF", requestType: data2.methodType, requestUrl: bilibiliApiUrls.\u4E8C\u7EF4\u7801\u72B6\u6001({ qrcode_key: data2.qrcode_key }) } }; } } case "\u767B\u5F55\u57FA\u672C\u4FE1\u606F": { const result = await GlobalGetData(data2.methodType, { ...baseRequestConfig, url: bilibiliApiUrls.\u767B\u5F55\u57FA\u672C\u4FE1\u606F() }); return result; } case "\u83B7\u53D6UP\u4E3B\u603B\u64AD\u653E\u91CF": { const result = await GlobalGetData(data2.methodType, { ...baseRequestConfig, url: bilibiliApiUrls.\u83B7\u53D6UP\u4E3B\u603B\u64AD\u653E\u91CF({ host_mid: data2.host_mid }) }); return result; } case "AV\u8F6CBV": { const result = av2bv(Number(data2.avid.toString().replace(/^av/i, ""))); return { code: 0, message: "success", data: { bvid: result } }; } case "BV\u8F6CAV": { const result = "av" + bv2av(data2.bvid); return { code: 0, message: "success", data: { aid: result } }; } default: logger.warn(`\u672A\u77E5\u7684B\u7AD9\u6570\u636E\u63A5\u53E3\uFF1A\u300C${logger.red(data2.methodType)}\u300D`); return null; } }; var GlobalGetData = async (type, options) => { let warningMessage = ""; try { const result = await fetchData(options); if (!result || result === "") { const Err = { errorDescription: "\u83B7\u53D6\u54CD\u5E94\u6570\u636E\u5931\u8D25\uFF01\u63A5\u53E3\u8FD4\u56DE\u5185\u5BB9\u4E3A\u7A7A\uFF0C\u4F60\u7684B\u7AD9ck\u53EF\u80FD\u5DF2\u7ECF\u5931\u6548\uFF01", requestType: type ?? "\u672A\u77E5\u8BF7\u6C42\u7C7B\u578B", requestUrl: options.url }; warningMessage = ` \u83B7\u53D6\u54CD\u5E94\u6570\u636E\u5931\u8D25\uFF01\u539F\u56E0\uFF1A${logger.yellow("\u63A5\u53E3\u8FD4\u56DE\u5185\u5BB9\u4E3A\u7A7A\uFF0C\u4F60\u7684B\u7AD9ck\u53EF\u80FD\u5DF2\u7ECF\u5931\u6548\uFF01")} \u8BF7\u6C42\u7C7B\u578B\uFF1A\u300C${type}\u300D \u8BF7\u6C42URL\uFF1A${options.url} `; logger.warn(warningMessage); throw { code: "-352" /* RISK_CONTROL_FAILED */, data: result, amagiError: Err }; } if (result.code !== 0) { const errorMessage = bilibiliErrorCodeMap[result.code] || result.message || "\u672A\u77E5\u9519\u8BEF"; const Err = { errorDescription: `\u83B7\u53D6\u54CD\u5E94\u6570\u636E\u5931\u8D25\uFF01\u539F\u56E0\uFF1A${errorMessage}\uFF01`, requestType: type ?? "\u672A\u77E5\u8BF7\u6C42\u7C7B\u578B", requestUrl: options.url }; warningMessage = ` \u83B7\u53D6\u54CD\u5E94\u6570\u636E\u5931\u8D25\uFF01\u539F\u56E0\uFF1A${logger.yellow(errorMessage)} \u9519\u8BEF\u4EE3\u7801\uFF1A${result.code} \u8BF7\u6C42\u7C7B\u578B\uFF1A\u300C${type}\u300D \u8BF7\u6C42URL\uFF1A${options.url} `; logger.warn(warningMessage); throw { code: result.code, data: result, amagiError: Err }; } return result; } catch (error) { if (error && typeof error === "object") { const err = error; return { ...err, amagiMessage: warningMessage }; } return { code: "UNKNOWN_ERROR" /* UNKNOWN */, data: error.data, amagiError: { errorDescription: "\u672A\u77E5\u9519\u8BEF", requestType: type, requestUrl: options.url }, amagiMessage: warningMessage }; } }; var bilibiliErrorCodeMap = { "-1": "\u5E94\u7528\u7A0B\u5E8F\u4E0D\u5B58\u5728\u6216\u5DF2\u88AB\u5C01\u7981", "-2": "Access Key \u9519\u8BEF", "-3": "API \u6821\u9A8C\u5BC6\u5319\u9519\u8BEF", "-4": "\u8C03\u7528\u65B9\u5BF9\u8BE5 Method \u6CA1\u6709\u6743\u9650", "-101": "\u8D26\u53F7\u672A\u767B\u5F55", "-102": "\u8D26\u53F7\u88AB\u5C01\u505C", "-103": "\u79EF\u5206\u4E0D\u8DB3", "-104": "\u786C\u5E01\u4E0D\u8DB3", "-105": "\u9A8C\u8BC1\u7801\u9519\u8BEF", "-106": "\u8D26\u53F7\u975E\u6B63\u5F0F\u4F1A\u5458\u6216\u5728\u9002\u5E94\u671F", "-107": "\u5E94\u7528\u4E0D\u5B58\u5728\u6216\u8005\u88AB\u5C01\u7981", "-108": "\u672A\u7ED1\u5B9A\u624B\u673A", "-110": "\u672A\u7ED1\u5B9A\u624B\u673A", "-111": "csrf \u6821\u9A8C\u5931\u8D25", "-112": "\u7CFB\u7EDF\u5347\u7EA7\u4E2D", "-113": "\u8D26\u53F7\u5C1A\u672A\u5B9E\u540D\u8BA4\u8BC1", "-114": "\u8BF7\u5148\u7ED1\u5B9A\u624B\u673A", "-115": "\u8BF7\u5148\u5B8C\u6210\u5B9E\u540D\u8BA4\u8BC1", "-304": "\u6728\u6709\u6539\u52A8", "-307": "\u649E\u8F66\u8DF3\u8F6C", "-352": "\u98CE\u63A7\u6821\u9A8C\u5931\u8D25 (UA \u6216 wbi \u53C2\u6570\u4E0D\u5408\u6CD5)", "-400": "\u8BF7\u6C42\u9519\u8BEF", "-401": "\u672A\u8BA4\u8BC1 (\u6216\u975E\u6CD5\u8BF7\u6C42)", "-403": "\u8BBF\u95EE\u6743\u9650\u4E0D\u8DB3", "-404": "\u5565\u90FD\u6728\u6709", "-405": "\u4E0D\u652F\u6301\u8BE5\u65B9\u6CD5", "-409": "\u51B2\u7A81", "-412": "\u8BF7\u6C42\u88AB\u62E6\u622A (\u5BA2\u6237\u7AEF ip \u88AB\u670D\u52A1\u7AEF\u98CE\u63A7)", "-500": "\u670D\u52A1\u5668\u9519\u8BEF", "-503": "\u8FC7\u8F7D\u4FDD\u62A4,\u670D\u52A1\u6682\u4E0D\u53EF\u7528", "-504": "\u670D\u52A1\u8C03\u7528\u8D85\u65F6", "-509": "\u8D85\u51FA\u9650\u5236", "-616": "\u4E0A\u4F20\u6587\u4EF6\u4E0D\u5B58\u5728", "-617": "\u4E0A\u4F20\u6587\u4EF6\u592A\u5927", "-625": "\u767B\u5F55\u5931\u8D25\u6B21\u6570\u592A\u591A", "-626": "\u7528\u6237\u4E0D\u5B58\u5728", "-628": "\u5BC6\u7801\u592A\u5F31", "-629": "\u7528\u6237\u540D\u6216\u5BC6\u7801\u9519\u8BEF", "-632": "\u64CD\u4F5C\u5BF9\u8C61\u6570\u91CF\u9650\u5236", "-643": "\u88AB\u9501\u5B9A", "-650": "\u7528\u6237\u7B49\u7EA7\u592A\u4F4E", "-652": "\u91CD\u590D\u7684\u7528\u6237", "-658": "Token \u8FC7\u671F", "-662": "\u5BC6\u7801\u65F6\u95F4\u6233\u8FC7\u671F", "-688": "\u5730\u7406\u533A\u57DF\u9650\u5236", "-689": "\u7248\u6743\u9650\u5236", "-701": "\u6263\u8282\u64CD\u5931\u8D25", "-799": "\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5", "-8888": "\u5BF9\u4E0D\u8D77\uFF0C\u670D\u52A1\u5668\u5F00\u5C0F\u5DEE\u4E86~ (\u0CA5\uFE4F\u0CA5)" }; // src/platform/douyin/sign/a_bogus.ts var SM3 = class { reg; chunk; size; constructor() { this.reg = []; this.chunk = []; this.size = 0; this.reset(); } reset() { this.reg[0] = 1937774191; this.reg[1] = 1226093241; this.reg[2] = 388252375; this.reg[3] = 3666478592; this.reg[4] = 2842636476; this.reg[5] = 372324522; this.reg[6] = 3817729613; this.reg[7] = 2969243214; this.chunk = []; this.size = 0; } write(e) { const a = typeof e === "string" ? this.stringToBytes(e) : e; this.size += a.length; let f = 64 - this.chunk.length; if (a.length < f) { this.chunk = this.chunk.concat(a); } else { this.chunk = this.chunk.concat(a.slice(0, f)); while (this.chunk.length >= 64) { this._compress(this.chunk); f < a.length ? this.chunk = a.slice(f, Math.min(f + 64, a.length)) : this.chunk = []; f += 64; } } } sum(e, t) { if (e) { this.reset(); this.write(e); } this._fill(); for (let f = 0; f < this.chunk.length; f += 64) { this._compress(this.chunk.slice(f, f + 64)); } let i = null; if (t === "hex") { i = ""; for (let f = 0; f < 8; f++) { i += this.padHex(this.reg[f].toString(16), 8); } } else { i = new Array(32); for (let f = 0; f < 8; f++) { let c = this.reg[f]; i[4 * f + 3] = (255 & c) >>> 0; c >>>= 8; i[4 * f + 2] = (255 & c) >>> 0; c >>>= 8; i[4 * f + 1] = (255 & c) >>> 0; c >>>= 8; i[4 * f] = (255 & c) >>> 0; } } this.reset(); return i; } _compress(t) { if (t.length < 64) { console.error("compress error: not enough data"); } else { for (var f = ((e) => { for (var r = new Array(132), t2 = 0; t2 < 16; t2++) { r[t2] = e[4 * t2] << 24, r[t2] |= e[4 * t2 + 1] << 16, r[t2] |= e[4 * t2 + 2] << 8, r[t2] |= e[4 * t2 + 3], r[t2] >>>= 0; } for (var n = 16; n < 68; n++) { let a = r[n - 16] ^ r[n - 9] ^ this.le(r[n - 3], 15); a = a ^ this.le(a, 15) ^ this.le(a, 23), r[n] = (a ^ this.le(r[n - 13], 7) ^ r[n - 6]) >>> 0; } for (n = 0; n < 64; n++) r[n + 68] = (r[n] ^ r[n + 4]) >>> 0; return r; })(t), i = this.reg.slice(0), c = 0; c < 64; c++) { let o = this.le(i[0], 12) + i[4] + this.le(this.de(c), c); const s = ((o = this.le(o = (4294967295 & o) >>> 0, 7)) ^ this.le(i[0], 12)) >>> 0; let u = this.pe(c, i[0], i[1], i[2]); u = (4294967295 & (u = u + i[3] + s + f[c + 68])) >>> 0; let b = this.he(c, i[4], i[5], i[6]); b = (4294967295 & (b = b + i[7] + o + f[c])) >>> 0, i[3] = i[2], i[2] = this.le(i[1], 9), i[1] = i[0], i[0] = u, i[7] = i[6], i[6] = this.le(i[5], 19), i[5] = i[4], i[4] = (b ^ this.le(b, 9) ^ this.le(b, 17)) >>> 0; } for (let l = 0; l < 8; l++) this.reg[l] = (this.reg[l] ^ i[l]) >>> 0; } } _fill() { let a = 8 * this.size; let f = this.chunk.push(128) % 64; while (64 - f < 8) { f -= 64; } while (f < 56) { this.chunk.push(0); f++; } for (let i = 0; i < 4; i++) { const c = Math.floor(a / 4294967296); this.chunk.push(c >>> 8 * (3 - i) & 255); } for (let i = 0; i < 4; i++) { this.chunk.push(a >>> 8 * (3 - i) & 255); } } de(e) { return e >= 0 && e < 16 ? 2043430169 : e >= 16 && e < 64 ? 2055708042 : (console.error("invalid j for constant Tj"), 0); } pe(e, r, t, n) { return e >= 0 && e < 16 ? (r ^ t ^ n) >>> 0 : e >= 16 && e < 64 ? (r & t | r & n | t & n) >>> 0 : (console.error("invalid j for bool function FF"), 0); } he(e, r, t, n) { return e >= 0 && e < 16 ? (r ^ t ^ n) >>> 0 : e >= 16 && e < 64 ? (r & t | ~r & n) >>> 0 : (console.error("invalid j for bool function GG"), 0); } le(e, r) { return (e << (r %= 32) | e >>> 32 - r) >>> 0; } stringToBytes(str) { const n = encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (_, r) => String.fromCharCode(parseInt(r, 16))); const a = new Array(n.length); for (let i = 0; i < n.length; i++) { a[i] = n.charCodeAt(i); } return a; } padHex(num, size) { return num.padStart(size, "0"); } }; function rc4_encrypt(plaintext, key) { const s = []; for (var i = 0; i < 256; i++) { s[i] = i; } var j = 0; for (var i = 0; i < 256; i++) { j = (j + s[i] + key.charCodeAt(i % key.length)) % 256; var temp = s[i]; s[i] = s[j]; s[j] = temp; } var i = 0; var j = 0; const cipher = []; for (let k = 0; k < plaintext.length; k++) { i = (i + 1) % 256; j = (j + s[i]) % 256; var temp = s[i]; s[i] = s[j]; s[j] = temp; const t = (s[i] + s[j]) % 256; cipher.push(String.fromCharCode(s[t] ^ plaintext.charCodeAt(k))); } return cipher.join(""); } function result_encrypt(long_str, num) { const s_obj = { s0: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", s1: "Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe=", s2: "Dkdpgh4ZKsQB80/Mfvw36XI1R25-WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe=", s3: "ckdp1h4ZKsUB80/Mfvw36XIgR25+WQAlEi7NLboqYTOPuzmFjJnryx9HVGDaStCe", s4: "Dkdpgh2ZmsQB80/MfvV36XI1R45-WUAlEixNLwoqYTOPuzKFjJnry79HbGcaStCe" }; const constant = { 0: 16515072, 1: 258048, 2: 4032, str: s_obj[num] }; let result = ""; let lound = 0; let long_int = get_long_int(lound, long_str); for (let i = 0; i < long_str.length / 3 * 4; i++) { if (Math.floor(i / 4) !== lound) { lound += 1; long_int = get_long_int(lound, long_str); } let key = i % 4; let temp_int; switch (key) { case 0: temp_int = (long_int & constant["0"]) >> 18; result += constant["str"].charAt(temp_int); break; case 1: temp_int = (long_int & constant["1"]) >> 12; result += constant["str"].charAt(temp_int); break; case 2: temp_int = (long_int & constant["2"]) >> 6; result += constant["str"].charAt(temp_int); break; case 3: temp_int = long_int & 63; result += constant["str"].charAt(temp_int); break; } } return result; } function get_long_int(round, long_str) { round = round * 3; return long_str.charCodeAt(round) << 16 | long_str.charCodeAt(round + 1) << 8 | long_str.charCodeAt(round + 2); } function gener_random(random, option) { return [ random & 255 & 170 | option[0] & 85, // 163 random & 255 & 85 | option[0] & 170, // 87 random >> 8 & 255 & 170 | option[1] & 85, // 37 random >> 8 & 255 & 85 | option[1] & 170 // 41 ]; } function generate_rc4_bb_str(url_search_params, user_agent, window_env_str, suffix = "cus", Arguments = [0, 1, 14]) { let sm3 = new SM3(); let start_time = Date.now(); const url_search_params_list = sm3.sum(sm3.sum(url_search_params + suffix)); const cus = sm3.sum(sm3.sum(suffix)); const ua = sm3.sum(result_encrypt(rc4_encrypt(user_agent, String.fromCharCode.apply(null, [390625e-8, 1, 14])), "s3")); const end_time = Date.now(); let b = { 8: 3, // 固定 10: end_time, // 3次加密结束时间 15: { aid: 6383, pageId: 6241}, 16: start_time, // 3次加密开始时间 18: 44}; b[20] = b[16] >> 24 & 255; b[21] = b[16] >> 16 & 255; b[22] = b[16] >> 8 & 255; b[23] = b[16] & 255; b[24] = b[16] / 256 / 256 / 256 / 256 >> 0; b[25] = b[16] / 256 / 256 / 256 / 256 / 256 >> 0; b[26] = Arguments[0] >> 24 & 255; b[27] = Arguments[0] >> 16 & 255; b[28] = Arguments[0] >> 8 & 255; b[29] = Arguments[0] & 255; b[30] = Arguments[1] / 256 & 255; b[31] = Arguments[1] % 256 & 255; b[32] = Arguments[1] >> 24 & 255; b[33] = Arguments[1] >> 16 & 255; b[34] = Arguments[2] >> 24 & 255; b[35] = Arguments[2] >> 16 & 255; b[36] = Arguments[2] >> 8 & 255; b[37] = Arguments[2] & 255; b[38] = url_search_params_list[21]; b[39] = url_search_params_list[22]; b[40] = cus[21]; b[41] = cus[22]; b[42] = ua[23]; b[43] = ua[24]; b[44] = b[10] >> 24 & 255; b[45] = b[10] >> 16 & 255; b[46] = b[10] >> 8 & 255; b[47] = b[10] & 255; b[48] = b[8]; b[49] = b[10] / 256 / 256 / 256 / 256 >> 0; b[50] = b[10] / 256 / 256 / 256 / 256 / 256 >> 0; b[51] = b[15].pageId; b[52] = b[15].pageId >> 24 & 255; b[53] = b[15].pageId >> 16 & 255; b[54] = b[15].pageId >> 8 & 255; b[55] = b[15].pageId & 255; b[56] = b[15].aid; b[57] = b[15].aid & 255; b[58] = b[15].aid >> 8 & 255; b[59] = b[15].aid >> 16 & 255; b[60] = b[15].aid >> 24 & 255; const window_env_list = []; for (let index = 0; index < window_env_str.length; index++) { window_env_list.push(window_env_str.charCodeAt(index)); } b[64] = window_env_list.length; b[65] = b[64] & 255; b[66] = b[64] >> 8 & 255; b[69] = [].length; b[70] = b[69] & 255; b[71] = b[69] >> 8 & 255; b[72] = b[18] ^ b[20] ^ b[26] ^ b[30] ^ b[38] ^ b[40] ^ b[42] ^ b[21] ^ b[27] ^ b[31] ^ b[35] ^ b[39] ^ b[41] ^ b[43] ^ b[22] ^ b[28] ^ b[32] ^ b[36] ^ b[23] ^ b[29] ^ b[33] ^ b[37] ^ b[44] ^ b[45] ^ b[46] ^ b[47] ^ b[48] ^ b[49] ^ b[50] ^ b[24] ^ b[25] ^ b[52] ^ b[53] ^ b[54] ^ b[55] ^ b[57] ^ b[58] ^ b[59] ^ b[60] ^ b[65] ^ b[66] ^ b[70] ^ b[71]; let bb = [ b[18], b[20], b[52], b[26], b[30], b[34], b[58], b[38], b[40], b[53], b[42], b[21], b[27], b[54], b[55], b[31], b[35], b[57], b[39], b[41], b[43], b[22], b[28], b[32], b[60], b[36], b[23], b[29], b[33], b[37], b[44], b[45], b[59], b[46], b[47], b[48], b[49], b[50], b[24], b[25], b[65], b[66], b[70], b[71] ]; bb = bb.concat(window_env_list).concat(b[72]); return rc4_encrypt(String.fromCharCode.apply(null, bb), String.fromCharCode.apply(null, [121])); } function generate_random_str() { let random_str_list = []; random_str_list = random_str_list.concat(gener_random(Math.random() * 1e4, [3, 45])); random_str_list = random_str_list.concat(gener_random(Math.random() * 1e4, [1, 0])); random_str_list = random_str_list.concat(gener_random(Math.random() * 1e4, [1, 5])); return String.fromCharCode.apply(null, random_str_list); } var cleanUserAgentForSigning = (userAgent) => { return userAgent.replace(/\s+Edg\/[\d\.]+/g, ""); }; var a_bogus_default = (url, user_agent) => { const cleanedUserAgent = cleanUserAgentForSigning(user_agent); let result_str = generate_random_str() + generate_rc4_bb_str(new URLSearchParams(new URL(url).search).toString(), cleanedUserAgent, "1536|747|1536|834|0|30|0|0|1536|834|1536|864|1525|747|24|24|Win32"); return result_encrypt(result_str, "s4") + "="; }; // src/platform/douyin/sign/index.ts var defaultUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"; var douyinSign = class { /** * 生成一个指定长度的随机字符串 * @param length 字符串长度,默认为116 * @returns */ static Mstoken(length) { const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const randomBytes = crypto__default.default.randomBytes(length ?? 116); return Array.from(randomBytes, (byte) => characters[byte % characters.length]).join(""); } /** * a_bogus 签名算法 * @param url 需要签名的地址 * @returns 对此地址签名后的URL查询参数 */ static AB(url, userAgent) { return a_bogus_default(url, userAgent || defaultUserAgent); } /** 生成一个唯一的验证字符串 */ static VerifyFpManager() { const e = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""); const t = e.length; const n = (/* @__PURE__ */ new Date()).getTime().toString(36); const r = []; r[8] = "_"; r[13] = "_"; r[18] = "_"; r[23] = "_"; r[14] = "4"; for (let o, i = 0; i < 36; i++) { if (!r[i]) { o = 0 | Math.random() * t; r[i] = e[i === 19 ? 3 & o | 8 : o]; } } return "verify_" + n + "_" + r.join(""); } }; // src/platform/douyin/API.ts var extractBrowserVersion = (userAgent) => { if (!userAgent) return "125.0.0.0"; const chromeMatch = userAgent.match(/Chrome\/(\d+\.\d+\.\d+\.\d+)/); if (chromeMatch) { return chromeMatch[1]; } const edgeMatch = userAgent.match(/Edg\/(\d+\.\d+\.\d+\.\d+)/); if (edgeMatch) { return edgeMatch[1]; } return "125.0.0.0"; }; var buildQueryString = (params) => { return Object.entries(params).filter(([_, value]) => value !== void 0 && value !== null).map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join("&"); }; var fp = douyinSign.VerifyFpManager(); var DouyinAPI = class { browserVersion; /** * 构造函数 * @param userAgent - 用户代理字符串,用于提取浏览器版本信息 */ constructor(userAgent) { this.browserVersion = extractBrowserVersion(userAgent); } /** * 获取通用的基础参数 * @returns 通用基础参数对象 */ getBaseParams() { return { device_platform: "webapp", aid: "6383", channel: "channel_pc_web", pc_client_type: "1", cookie_enabled: "true", browser_language: "zh-CN", browser_platform: "Win32", browser_name: "Chrome", browser_version: this.browserVersion, browser_online: "true", engine_name: "Blink", engine_version: this.browserVersion, os_name: "Windows", os_version: "10", cpu_core_num: "16", device_memory: "8", platform: "PC", downlink: "10", effective_type: "4g", msToken: douyinSign.Mstoken(116), verifyFp: fp, fp }; } /** * 获取视频或图集数据的接口地址 * @param data - 请求参数,包含aweme_id * @returns 完整的接口URL */ \u89C6\u9891\u6216\u56FE\u96C6(data2) { const baseUrl = "https://www.douyin.com/aweme/v1/web/aweme/detail/"; const params = { ...this.getBaseParams(), aweme_id: data2.aweme_id, update_version_code: "170400", version_code: "190500", version_name: "19.5.0", screen_width: "2328", screen_height: "1310", round_trip_time: "150", webid: "7351848354471872041" }; return `${baseUrl}?${buildQueryString(params)}`; } /** * 获取评论数据的接口地址 * @param data - 请求参数,包含aweme_id、cursor、number等 * @returns 完整的接口URL */ \u8BC4\u8BBA(data2) { const baseUrl = "https://www.douyin.com/aweme/v1/web/comment/list/"; const params = { ...this.getBaseParams(), aweme_id: data2.aweme_id, cursor: data2.cursor ?? 0, count: data2.number ?? 50, item_type: "0", insert_ids: "", whale_cut_token: "", cut_version: "1", rcFT: "", version_code: "170400", version_name: "17.4.0", screen_width: "1552", screen_height: "970", round_trip_time: "50" }; return `${baseUrl}?${buildQueryString(params)}`; } /** * 获取二级评论数据的接口地址 * @param data - 请求参数,包含aweme_id、comment_id等 * @returns 完整的接口URL */ \u4E8C\u7EA7\u8BC4\u8BBA(data2) { const baseUrl = "https://www.douyin.com/aweme/v1/web/comment/list/reply/"; const params = { device_platform: "webapp", aid: "6383", channel: "channel_pc_web", item_id: data2.aweme_id, comment_id: data2.comment_id, cut_version: "1", cursor: data2.cursor, count: data2.number, item_type: "0", update_version_code: "170400", pc_client_type: "1", pc_libra_divert: "Windows", support_h265: "1", support_dash: "1", version_code: "170400", version_name: "17.4.0", cookie_enabled: "true", screen_width: "1552", screen_height: "970", browser_language: "zh-CN", browser_platform: "Win32", browser_name: "Edge", browser_version: "132.0.0.0", browser_online: "true", engine_name: "Blink", engine_version: "132.0.0.0", os_name: "Windows", os_version: "10", cpu_core_num: "16", device_memory: "8", platform: "PC", downlink: "10", effective_type: "4g", round_trip_time: "50", webid: "7386217876267796006", verifyFp: fp, fp }; return `${baseUrl}?${buildQueryString(params)}`; } /** * 获取动图数据的接口地址 * @param data - 请求参数,包含aweme_id * @returns 完整的接口URL */ \u52A8\u56FE(data2) { const baseUrl = "https://www.iesdouyin.com/web/api/v2/aweme/slidesinfo/"; const params = { reflow_source: "reflow_page", web_id: "7326472315356857893", device_id: "7326472315356857893", aweme_ids: `[${data2.aweme_id}]`, request_source: "200", msToken: douyinSign.Mstoken(116), verifyFp: fp, fp }; return `${baseUrl}?${buildQueryString(params)}`; } /** * 获取表情数据的接口地址 * @returns 完整的接口URL */ \u8868\u60C5() { return "https://www.douyin.com/aweme/v1/web/emoji/list"; } /** * 获取用户主页视频数据的接口地址 * @param data - 请求参数,包含sec_uid * @returns 完整的接口URL */ \u7528\u6237\u4E3B\u9875\u89C6\u9891(data2) { const baseUrl = "https://www.douyin.com/aweme/v1/web/aweme/post/"; const params = { ...this.getBaseParams(), sec_user_id: data2.sec_uid, max_cursor: "0", locate_query: "false", show_live_replay_strategy: "1", need_time_list: "1", time_list_query: "0", whale_cut_token: "", cut_version: "1", count: "18", publish_video_strategy_type: "2", version_code: "170400", version_name: "17.4.0", screen_width: "1552", screen_height: "970", round_trip_time: "50", webid: "7338423850134226495" }; return `${baseUrl}?${buildQueryString(params)}`; } /** * 获取用户主页信息的接口地址 * @param data - 请求参数,包含sec_uid * @returns 完整的接口URL */ \u7528\u6237\u4E3B\u