@ikenxuan/amagi
Version:
抖音、B站的 web 端相关数据接口基于 Node.js 的实现
1,633 lines • 337 kB
JavaScript
Object.defineProperties(exports, {
__esModule: { value: true },
[Symbol.toStringTag]: { value: "Module" }
});
const require_chunk = require("../chunk-CKQMccvm.cjs");
let node_url = require("node:url");
node_url = require_chunk.__toESM(node_url, 1);
let node_events = require("node:events");
let zod = require("zod");
zod = require_chunk.__toESM(zod, 1);
let _ikenxuan_xhshow_ts = require("@ikenxuan/xhshow-ts");
let node_crypto = require("node:crypto");
node_crypto = require_chunk.__toESM(node_crypto, 1);
let axios = require("axios");
axios = require_chunk.__toESM(axios, 1);
let chalk = require("chalk");
let protobufjs = require("protobufjs");
protobufjs = require_chunk.__toESM(protobufjs, 1);
let express = require("express");
express = require_chunk.__toESM(express, 1);
//#region src/utils/deprecation.ts
/**
* 废弃 API 注册表
* 存储所有已注册的废弃 API 配置
*/
const deprecatedApis = /* @__PURE__ */ new Map();
/**
* 注册一个废弃的 API
*
* 将 API 添加到废弃注册表中,后续可通过 checkDeprecation 检查
*
* @param config - 废弃配置对象
*
* @example
* ```typescript
* registerDeprecatedApi({
* name: 'getDouyinData',
* deprecatedIn: '6.0.0',
* removedIn: '7.0.0',
* replacement: 'douyinFetcher',
* throwError: true
* })
* ```
*/
function registerDeprecatedApi(config) {
deprecatedApis.set(config.name, config);
}
/**
* 检查 API 是否已废弃并进行相应处理
*
* 如果 API 已注册为废弃,根据配置决定是打印警告还是抛出错误
*
* @param apiName - 要检查的 API 名称
* @throws {DeprecatedApiError} 如果 API 已废弃且配置为抛出错误
*
* @example
* ```typescript
* // 在函数开头调用检查
* function getDouyinData(options) {
* checkDeprecation('getDouyinData')
* // ...
* }
* ```
*/
function checkDeprecation(apiName) {
const config = deprecatedApis.get(apiName);
if (!config) return;
const message = buildDeprecationMessage(config);
if (config.throwError) throw new DeprecatedApiError(message, config);
else console.warn(message);
}
/**
* 根据配置构建废弃提示消息
*
* @param config - 废弃配置
* @returns 格式化的废弃提示消息字符串
*/
function buildDeprecationMessage(config) {
const lines = [`[DEPRECATED] "${config.name}" 已在 v${config.deprecatedIn} 版本废弃。`];
if (config.replacement) lines.push(`请使用 "${config.replacement}" 替代。`);
else lines.push("此接口已被上游删除,无法继续使用,无可用替代方案。");
if (config.removedIn) lines.push(`此 API 将在 v${config.removedIn} 版本移除。`);
if (config.migrationGuide) lines.push(`迁移指南: ${config.migrationGuide}`);
return lines.join("\n");
}
/**
* 废弃 API 调用错误类
*
* 当调用已废弃且配置为抛出错误的 API 时抛出此错误
* 包含完整的废弃配置信息,便于调试和迁移
*/
var DeprecatedApiError = class DeprecatedApiError extends Error {
/** 废弃配置信息,包含替代方案等详细信息 */
config;
/**
* 创建废弃 API 错误实例
*
* @param message - 错误消息
* @param config - 废弃配置对象
*/
constructor(message, config) {
super(message);
this.name = "DeprecatedApiError";
this.config = config;
Error.captureStackTrace?.(this, DeprecatedApiError);
}
};
registerDeprecatedApi({
name: "getDouyinData",
deprecatedIn: "6.0.0",
removedIn: "7.0.0",
replacement: "douyinFetcher 或 client.douyin.fetcher",
migrationGuide: "https://github.com/ikenxuan/amagi/blob/main/packages/core/MIGRATION-v6.md",
throwError: true
});
registerDeprecatedApi({
name: "getBilibiliData",
deprecatedIn: "6.0.0",
removedIn: "7.0.0",
replacement: "bilibiliFetcher 或 client.bilibili.fetcher",
migrationGuide: "https://github.com/ikenxuan/amagi/blob/main/packages/core/MIGRATION-v6.md",
throwError: true
});
registerDeprecatedApi({
name: "getKuaishouData",
deprecatedIn: "6.0.0",
removedIn: "7.0.0",
replacement: "kuaishouFetcher 或 client.kuaishou.fetcher",
migrationGuide: "https://github.com/ikenxuan/amagi/blob/main/packages/core/MIGRATION-v6.md",
throwError: true
});
registerDeprecatedApi({
name: "getXiaohongshuData",
deprecatedIn: "6.0.0",
removedIn: "7.0.0",
replacement: "xiaohongshuFetcher 或 client.xiaohongshu.fetcher",
migrationGuide: "https://github.com/ikenxuan/amagi/blob/main/packages/core/MIGRATION-v6.md",
throwError: true
});
[
{
name: "单个视频作品数据",
replacement: "fetchVideoInfo"
},
{
name: "单个视频下载信息数据",
replacement: "fetchVideoStreamUrl"
},
{
name: "评论数据",
replacement: "fetchComments"
},
{
name: "指定评论的回复",
replacement: "fetchCommentReplies"
},
{
name: "用户主页数据",
replacement: "fetchUserCard"
},
{
name: "用户主页动态列表数据",
replacement: "fetchUserDynamicList"
},
{
name: "用户空间详细信息",
replacement: "fetchUserSpaceInfo"
},
{
name: "获取UP主总播放量",
replacement: "fetchUploaderTotalViews"
},
{
name: "Emoji数据",
replacement: "fetchEmojiList"
},
{
name: "番剧基本信息数据",
replacement: "fetchBangumiInfo"
},
{
name: "番剧下载信息数据",
replacement: "fetchBangumiStreamUrl"
},
{
name: "动态详情数据",
replacement: "fetchDynamicDetail"
},
{
name: "直播间信息",
replacement: "fetchLiveRoomInfo"
},
{
name: "直播间初始化信息",
replacement: "fetchLiveRoomInitInfo"
},
{
name: "登录基本信息",
replacement: "fetchLoginStatus"
},
{
name: "申请二维码",
replacement: "requestLoginQrcode"
},
{
name: "二维码状态",
replacement: "checkQrcodeStatus"
},
{
name: "AV转BV",
replacement: "convertAvToBv"
},
{
name: "BV转AV",
replacement: "convertBvToAv"
},
{
name: "专栏正文内容",
replacement: "fetchArticleContent"
},
{
name: "专栏显示卡片信息",
replacement: "fetchArticleCards"
},
{
name: "专栏文章基本信息",
replacement: "fetchArticleInfo"
},
{
name: "文集基本信息",
replacement: "fetchArticleListInfo"
},
{
name: "实时弹幕",
replacement: "fetchVideoDanmaku"
},
{
name: "从_v_voucher_申请_captcha",
replacement: "requestCaptchaFromVoucher"
},
{
name: "验证验证码结果",
replacement: "validateCaptchaResult"
},
{
name: "视频作品数据",
replacement: "fetchVideoWork"
},
{
name: "图集作品数据",
replacement: "fetchImageAlbumWork"
},
{
name: "合辑作品数据",
replacement: "fetchSlidesWork"
},
{
name: "文字作品数据",
replacement: "fetchTextWork"
},
{
name: "聚合解析",
replacement: "parseWork"
},
{
name: "指定评论回复数据",
replacement: "fetchCommentReplies"
},
{
name: "用户主页视频列表数据",
replacement: "fetchUserVideoList"
},
{
name: "热点词数据",
replacement: "fetchSuggestWords"
},
{
name: "搜索数据",
replacement: "searchContent"
},
{
name: "音乐数据",
replacement: "fetchMusicInfo"
},
{
name: "直播间信息数据",
replacement: "fetchLiveRoomInfo"
},
{
name: "申请二维码数据",
replacement: "requestLoginQrcode"
},
{
name: "动态表情数据",
replacement: "fetchDynamicEmojiList"
},
{
name: "弹幕数据",
replacement: "fetchDanmakuList"
},
{
name: "单个视频作品数据",
replacement: "fetchVideoWork"
},
{
name: "首页推荐数据",
replacement: "fetchHomeFeed"
},
{
name: "单个笔记数据",
replacement: "fetchNoteDetail"
},
{
name: "用户数据",
replacement: "fetchUserProfile"
},
{
name: "用户笔记数据",
replacement: "fetchUserNoteList"
},
{
name: "表情列表",
replacement: "fetchEmojiList"
},
{
name: "搜索笔记",
replacement: "searchNotes"
}
].forEach(({ name, replacement }) => {
registerDeprecatedApi({
name: `methodType: '${name}'`,
deprecatedIn: "6.0.0",
removedIn: "7.0.0",
replacement: `fetcher.${replacement}()`,
throwError: true
});
});
registerDeprecatedApi({
name: `methodType: '动态卡片数据'`,
deprecatedIn: "6.0.0",
removedIn: "7.0.0",
migrationGuide: "https://amagi-docs.vercel.app/docs/changelog/6.1.3",
throwError: false
});
registerDeprecatedApi({
name: "fetchDynamicCard",
deprecatedIn: "6.1.3",
removedIn: "7.0.0",
migrationGuide: "https://amagi-docs.vercel.app/docs/changelog/6.1.3",
throwError: false
});
//#endregion
//#region src/model/DataFetchers.ts
/**
* 数据获取器模块 (已废弃)
*
* 此模块中的 getXXXData 函数已在 v6 版本废弃并移除
* 请使用新的 fetcher API 替代
*
* @module model/DataFetchers
* @deprecated v6 已废弃,请使用 fetcher API 替代
*/
/**
* 获取抖音数据
*
* @deprecated v6 已废弃,请使用 douyinFetcher 或 client.douyin.fetcher 替代
* @throws {DeprecatedApiError} 调用时抛出废弃错误
*
* @example
* ```typescript
* // 旧用法 (已废弃,会抛出错误)
* const data = await getDouyinData('videoWork', { aweme_id: '123' }, cookie)
*
* // 新用法
* import { douyinFetcher } from '@ikenxuan/amagi'
* const data = await douyinFetcher.fetchVideoWork({ aweme_id: '123' }, cookie)
*
* // 或使用客户端实例
* const client = createAmagiClient({ cookies: { douyin: cookie } })
* const data = await client.douyin.fetcher.fetchVideoWork({ aweme_id: '123' })
* ```
*/
function getDouyinData(..._args) {
checkDeprecation("getDouyinData");
throw new Error("getDouyinData 已废弃");
}
/**
* 获取B站数据
*
* @deprecated v6 已废弃,请使用 bilibiliFetcher 或 client.bilibili.fetcher 替代
* @throws {DeprecatedApiError} 调用时抛出废弃错误
*
* @example
* ```typescript
* // 旧用法 (已废弃,会抛出错误)
* const data = await getBilibiliData('videoInfo', { bvid: 'BV123' }, cookie)
*
* // 新用法
* import { bilibiliFetcher } from '@ikenxuan/amagi'
* const data = await bilibiliFetcher.fetchVideoInfo({ bvid: 'BV123' }, cookie)
*
* // 或使用客户端实例
* const client = createAmagiClient({ cookies: { bilibili: cookie } })
* const data = await client.bilibili.fetcher.fetchVideoInfo({ bvid: 'BV123' })
* ```
*/
function getBilibiliData(..._args) {
checkDeprecation("getBilibiliData");
throw new Error("getBilibiliData 已废弃");
}
/**
* 获取快手数据
*
* @deprecated v6 已废弃,请使用 kuaishouFetcher 或 client.kuaishou.fetcher 替代
* @throws {DeprecatedApiError} 调用时抛出废弃错误
*
* @example
* ```typescript
* // 旧用法 (已废弃,会抛出错误)
* const data = await getKuaishouData('videoWork', { photoId: '123' }, cookie)
*
* // 新用法
* import { kuaishouFetcher } from '@ikenxuan/amagi'
* const data = await kuaishouFetcher.fetchVideoWork({ photoId: '123' }, cookie)
*
* // 或使用客户端实例
* const client = createAmagiClient({ cookies: { kuaishou: cookie } })
* const data = await client.kuaishou.fetcher.fetchVideoWork({ photoId: '123' })
* ```
*/
function getKuaishouData(..._args) {
checkDeprecation("getKuaishouData");
throw new Error("getKuaishouData 已废弃");
}
/**
* 获取小红书数据
*
* @deprecated v6 已废弃,请使用 xiaohongshuFetcher 或 client.xiaohongshu.fetcher 替代
* @throws {DeprecatedApiError} 调用时抛出废弃错误
*
* @example
* ```typescript
* // 旧用法 (已废弃,会抛出错误)
* const data = await getXiaohongshuData('noteDetail', { note_id: '123' }, cookie)
*
* // 新用法
* import { xiaohongshuFetcher } from '@ikenxuan/amagi'
* const data = await xiaohongshuFetcher.fetchNoteDetail({ note_id: '123' }, cookie)
*
* // 或使用客户端实例
* const client = createAmagiClient({ cookies: { xiaohongshu: cookie } })
* const data = await client.xiaohongshu.fetcher.fetchNoteDetail({ note_id: '123' })
* ```
*/
function getXiaohongshuData(..._args) {
checkDeprecation("getXiaohongshuData");
throw new Error("getXiaohongshuData 已废弃");
}
//#endregion
//#region src/platform/bilibili/API.ts
/**
* B站 API URL 构建类
*
* 提供所有 B站 API 的 URL 构建方法
*/
var BilibiliAPI = class {
/** 获取登录基本信息 */
getLoginStatus() {
return "https://api.bilibili.com/x/web-interface/nav";
}
/** 获取视频详细信息 */
getVideoInfo(data) {
return `https://api.bilibili.com/x/web-interface/view?bvid=${data.bvid}`;
}
/** 获取视频流信息 */
getVideoStream(data) {
return `https://api.bilibili.com/x/player/playurl?avid=${data.avid}&cid=${data.cid}`;
}
/**
* 获取评论区明细
* @see https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/comment/readme.md#评论区类型代码
*/
getComments(data) {
const params = new URLSearchParams({
oid: data.oid.toString(),
type: data.type.toString(),
mode: (data.mode ?? 3).toString(),
plat: "1",
seek_rpid: "",
web_location: "1315875"
});
if (data.pagination_str) params.append("pagination_str", JSON.stringify({ offset: data.pagination_str }));
else params.append("pagination_str", JSON.stringify({ offset: "" }));
return `https://api.bilibili.com/x/v2/reply/wbi/main?${params.toString()}`;
}
/** 获取评论区状态 */
getCommentStatus(data) {
return `https://api.bilibili.com/x/v2/reply/subject/description?type=${data.type}&oid=${data.oid}`;
}
/** 获取指定评论的回复 */
getCommentReplies(data) {
return `https://api.bilibili.com/x/v2/reply/reply?type=${data.type}&oid=${data.oid}&root=${data.root}&ps=${data.number}`;
}
/** 获取表情列表 */
getEmojiList() {
return "https://api.bilibili.com/x/emote/user/panel/web?business=reply&web_location=0.0";
}
/** 获取番剧明细 */
getBangumiInfo(data) {
if (data.ep_id) return `https://api.bilibili.com/pgc/view/web/season?ep_id=${data.ep_id}`;
else if (data.season_id) return `https://api.bilibili.com/pgc/view/web/season?season_id=${data.season_id}`;
else throw new Error("Missing required parameter: ep_id or season_id");
}
/** 获取番剧视频流信息 */
getBangumiStream(data) {
return `https://api.bilibili.com/pgc/player/web/playurl?cid=${data.cid}&ep_id=${data.ep_id}`;
}
/** 获取用户空间动态 */
getUserDynamicList(data) {
return `https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?${new URLSearchParams({
host_mid: data.host_mid.toString(),
offset: "",
platform: "web",
features: "itemOpusStyle,listOnlyfans,opusBigCover,onlyfansVote,forwardListHidden,decorationCard,commentsNewVersion,onlyfansAssetsV2,ugcDelete,onlyfansQaCard,avatarAutoTheme,sunflowerStyle,eva3CardOpus,eva3CardVideo,eva3CardComment"
}).toString()}`;
}
/** 获取动态详情 */
getDynamicDetail(data) {
return `https://api.bilibili.com/x/polymer/web-dynamic/v1/detail?id=${data.dynamic_id}&features=itemOpusStyle,opusBigCover,onlyfansVote,endFooterHidden,decorationCard,onlyfansAssetsV2,ugcDelete,onlyfansQaCard,editable,opusPrivateVisible,avatarAutoTheme`;
}
/**
* 获取动态卡片信息
*
* @deprecated B站官方已于 `2025-08-09` 删除原 `dynamic_svr` 接口,该接口已停用。
* 调用将返回错误信息,请使用 {@link getDynamicDetail} 替代。
*/
getDynamicCard(data) {
return this.getDynamicDetail(data);
}
/** 获取用户名片信息 */
getUserCard(data) {
return `https://api.bilibili.com/x/web-interface/card?mid=${data.host_mid}&photo=true`;
}
/** 获取直播间信息 */
getLiveRoomInfo(data) {
return `https://api.live.bilibili.com/room/v1/Room/get_info?room_id=${data.room_id}`;
}
/** 获取直播间初始化信息 */
getLiveRoomInit(data) {
return `https://api.live.bilibili.com/room/v1/Room/room_init?id=${data.room_id}`;
}
/** 申请登录二维码 */
getLoginQrcode() {
return "https://passport.bilibili.com/x/passport-login/web/qrcode/generate";
}
/** 查询二维码状态 */
getQrcodeStatus(data) {
return `https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key=${data.qrcode_key}`;
}
/** 获取UP主总播放量 */
getUploaderTotalViews(data) {
return `https://api.bilibili.com/x/space/upstat?mid=${data.host_mid}`;
}
/** 获取专栏正文内容 */
getArticleContent(data) {
return `https://api.bilibili.com/x/article/view?id=${data.id}`;
}
/** 获取专栏显示卡片信息 */
getArticleCards(data) {
return `https://api.bilibili.com/x/article/cards?ids=${Array.isArray(data.ids) ? data.ids.join(",") : data.ids}`;
}
/** 获取专栏文章基本信息 */
getArticleInfo(data) {
return `https://api.bilibili.com/x/article/viewinfo?id=${data.id}`;
}
/** 获取文集基本信息 */
getArticleListInfo(data) {
return `https://api.bilibili.com/x/article/list/web/articles?id=${data.id}`;
}
/** 获取用户空间详细信息 */
getUserSpaceInfo(data) {
return `https://api.bilibili.com/x/space/wbi/acc/info?mid=${data.host_mid}`;
}
/** 从 v_voucher 申请验证码 */
getCaptchaFromVoucher(data) {
return {
Url: "https://api.bilibili.com/x/gaia-vgate/v1/register",
Body: {
...data.csrf !== void 0 && { csrf: data.csrf },
v_voucher: data.v_voucher
}
};
}
/** 验证验证码结果 */
validateCaptcha(data) {
return {
Url: "https://api.bilibili.com/x/gaia-vgate/v1/validate",
Body: {
challenge: data.challenge,
token: data.token,
validate: data.validate,
seccode: data.seccode,
...data.csrf !== void 0 && { csrf: data.csrf }
}
};
}
/**
* 获取实时弹幕(web端 protobuf 接口)
* @see https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/danmaku/danmaku_proto.md
*/
getVideoDanmaku(data) {
return `https://api.bilibili.com/x/v2/dm/web/seg.so?${new URLSearchParams({
type: "1",
oid: data.cid.toString(),
segment_index: (data.segment_index ?? 1).toString()
}).toString()}`;
}
};
/** B站 API URL 构建器实例 */
const bilibiliApiUrls = new BilibiliAPI();
//#endregion
//#region src/platform/bilibili/BilibiliApi.ts
/**
* 创建废弃的 API 存根函数
*/
const createDeprecatedStub$3 = (methodName) => {
return (..._args) => {
checkDeprecation("getBilibiliData");
throw new Error(`bilibili.${methodName} 已废弃,请使用 bilibiliFetcher 替代`);
};
};
/**
* B站相关 API 的命名空间。
*
* @deprecated v6 已废弃,请使用 bilibiliFetcher 或 client.bilibili.fetcher 替代
*/
const bilibili = {
/** @deprecated 请使用 bilibiliFetcher.fetchVideoInfo 替代 */
getVideoInfo: createDeprecatedStub$3("getVideoInfo"),
/** @deprecated 请使用 bilibiliFetcher.fetchVideoStreamUrl 替代 */
getVideoStream: createDeprecatedStub$3("getVideoStream"),
/** @deprecated 请使用 bilibiliFetcher.fetchComments 替代 */
getComments: createDeprecatedStub$3("getComments"),
/** @deprecated 请使用 bilibiliFetcher.fetchCommentReplies 替代 */
getCommentReply: createDeprecatedStub$3("getCommentReply"),
/** @deprecated 请使用 bilibiliFetcher.fetchUserCard 替代 */
getUserProfile: createDeprecatedStub$3("getUserProfile"),
/** @deprecated 请使用 bilibiliFetcher.fetchUserDynamicList 替代 */
getUserDynamic: createDeprecatedStub$3("getUserDynamic"),
/** @deprecated 请使用 bilibiliFetcher.fetchEmojiList 替代 */
getEmojiList: createDeprecatedStub$3("getEmojiList"),
/** @deprecated 请使用 bilibiliFetcher.fetchBangumiInfo 替代 */
getBangumiInfo: createDeprecatedStub$3("getBangumiInfo"),
/** @deprecated 请使用 bilibiliFetcher.fetchBangumiStreamUrl 替代 */
getBangumiStream: createDeprecatedStub$3("getBangumiStream"),
/** @deprecated 请使用 bilibiliFetcher.fetchDynamicDetail 替代 */
getDynamicInfo: createDeprecatedStub$3("getDynamicInfo"),
/** @deprecated 请使用 bilibiliFetcher.fetchDynamicCard 替代 */
getDynamicCard: createDeprecatedStub$3("getDynamicCard"),
/** @deprecated 请使用 bilibiliFetcher.fetchLiveRoomInfo 替代 */
getLiveRoomDetail: createDeprecatedStub$3("getLiveRoomDetail"),
/** @deprecated 请使用 bilibiliFetcher.fetchLiveRoomInitInfo 替代 */
getLiveRoomInitInfo: createDeprecatedStub$3("getLiveRoomInitInfo"),
/** @deprecated 请使用 bilibiliFetcher.fetchLoginStatus 替代 */
getLoginBasicInfo: createDeprecatedStub$3("getLoginBasicInfo"),
/** @deprecated 请使用 bilibiliFetcher.requestLoginQrcode 替代 */
getLoginQrcode: createDeprecatedStub$3("getLoginQrcode"),
/** @deprecated 请使用 bilibiliFetcher.checkQrcodeStatus 替代 */
checkQrcodeStatus: createDeprecatedStub$3("checkQrcodeStatus"),
/** @deprecated 请使用 bilibiliFetcher.fetchUploaderTotalViews 替代 */
getUserTotalPlayCount: createDeprecatedStub$3("getUserTotalPlayCount"),
/** @deprecated 请使用 bilibiliFetcher.convertAvToBv 替代 */
convertAvToBv: createDeprecatedStub$3("convertAvToBv"),
/** @deprecated 请使用 bilibiliFetcher.convertBvToAv 替代 */
convertBvToAv: createDeprecatedStub$3("convertBvToAv"),
/** @deprecated 请使用 bilibiliFetcher.fetchArticleContent 替代 */
getArticleContent: createDeprecatedStub$3("getArticleContent"),
/** @deprecated 请使用 bilibiliFetcher.fetchArticleCards 替代 */
getArticleCard: createDeprecatedStub$3("getArticleCard"),
/** @deprecated 请使用 bilibiliFetcher.fetchArticleInfo 替代 */
getArticleInfo: createDeprecatedStub$3("getArticleInfo"),
/** @deprecated 请使用 bilibiliFetcher.fetchArticleListInfo 替代 */
getColumnInfo: createDeprecatedStub$3("getColumnInfo"),
/** @deprecated 请使用 bilibiliFetcher.fetchUserSpaceInfo 替代 */
getUserProfileDetail: createDeprecatedStub$3("getUserProfileDetail"),
/** @deprecated 请使用 bilibiliFetcher.requestCaptchaFromVoucher 替代 */
applyVoucherCaptcha: createDeprecatedStub$3("applyVoucherCaptcha"),
/** @deprecated 请使用 bilibiliFetcher.validateCaptchaResult 替代 */
validateCaptcha: createDeprecatedStub$3("validateCaptcha"),
/** @deprecated 请使用 bilibiliFetcher.fetchVideoDanmaku 替代 */
getDanmaku: createDeprecatedStub$3("getDanmaku")
};
/**
* 创建绑定了cookie的B站API对象
*
* @deprecated v6 已废弃,请使用 createBoundBilibiliFetcher 替代
*/
const createBoundBilibiliApi = (_cookie, _requestConfig) => {
return { ...bilibili };
};
//#endregion
//#region src/model/events.ts
/**
* Amagi 事件系统
* @module model/events
* @description 提供类型安全的事件发射器,用于日志、HTTP、网络和 API 事件的监听与触发
*/
/**
* 类型安全的事件发射器
* @description 继承自 Node.js EventEmitter,提供泛型约束确保事件名称与数据类型匹配
*/
var TypedEventEmitter = class extends node_events.EventEmitter {
/**
* 触发事件
* @param event - 事件名称
* @param data - 事件数据
* @returns 是否有监听器处理了该事件
*/
emit(event, data) {
return super.emit(event, data);
}
/**
* 注册事件监听器
* @param event - 事件名称
* @param listener - 事件处理函数
* @returns this (支持链式调用)
*/
on(event, listener) {
return super.on(event, listener);
}
/**
* 注册一次性事件监听器
* @param event - 事件名称
* @param listener - 事件处理函数 (只触发一次)
* @returns this (支持链式调用)
*/
once(event, listener) {
return super.once(event, listener);
}
/**
* 移除事件监听器
* @param event - 事件名称
* @param listener - 要移除的事件处理函数
* @returns this (支持链式调用)
*/
off(event, listener) {
return super.off(event, listener);
}
};
/**
* Amagi 全局事件发射器实例
* @description 单例模式,所有模块共享同一个事件总线
* @example
* ```typescript
* import { amagiEvents } from 'amagi/model/events'
*
* // 监听 API 成功事件
* amagiEvents.on('api:success', (data) => {
* console.log(`[${data.platform}] ${data.methodType} 耗时 ${data.duration}ms`)
* })
* ```
*/
const amagiEvents = new TypedEventEmitter();
/**
* 发射日志事件
* @param level - 日志级别
* @param message - 日志消息
* @param args - 附加参数
*/
const emitLog = (level, message, ...args) => {
amagiEvents.emit(`log:${level}`, {
level,
message,
args: args.length > 0 ? args : void 0,
timestamp: /* @__PURE__ */ new Date()
});
};
/**
* 发射 HTTP 请求事件
* @param data - 请求数据 (不含 timestamp)
*/
const emitHttpRequest = (data) => {
amagiEvents.emit("http:request", {
...data,
timestamp: /* @__PURE__ */ new Date()
});
};
/**
* 发射 HTTP 响应事件
* @param data - 响应数据 (不含 timestamp)
*/
const emitHttpResponse = (data) => {
amagiEvents.emit("http:response", {
...data,
timestamp: /* @__PURE__ */ new Date()
});
};
/**
* 发射网络重试事件
* @param data - 重试数据 (不含 timestamp)
*/
const emitNetworkRetry = (data) => {
amagiEvents.emit("network:retry", {
...data,
timestamp: /* @__PURE__ */ new Date()
});
};
/**
* 发射网络错误事件
* @param data - 错误数据 (不含 timestamp)
*/
const emitNetworkError = (data) => {
amagiEvents.emit("network:error", {
...data,
timestamp: /* @__PURE__ */ new Date()
});
};
/**
* 发射 API 成功事件
* @param data - 成功数据 (不含 timestamp)
*/
const emitApiSuccess = (data) => {
amagiEvents.emit("api:success", {
...data,
timestamp: /* @__PURE__ */ new Date()
});
};
/**
* 发射 API 错误事件
* @param data - 错误数据 (不含 timestamp)
*/
const emitApiError = (data) => {
amagiEvents.emit("api:error", {
...data,
timestamp: /* @__PURE__ */ new Date()
});
};
/**
* 发射 info 级别日志
* @param message - 日志消息
* @param args - 附加参数
*/
const emitLogInfo = (message, ...args) => {
emitLog("info", message, ...args);
};
/**
* 发射 warn 级别日志
* @param message - 日志消息
* @param args - 附加参数
*/
const emitLogWarn = (message, ...args) => {
emitLog("warn", message, ...args);
};
/**
* 发射 error 级别日志
* @param message - 日志消息
* @param args - 附加参数
*/
const emitLogError = (message, ...args) => {
emitLog("error", message, ...args);
};
/**
* 发射 debug 级别日志
* @param message - 日志消息
* @param args - 附加参数
*/
const emitLogDebug = (message, ...args) => {
emitLog("debug", message, ...args);
};
/**
* 发射 mark 级别日志 (用于重要标记)
* @param message - 日志消息
* @param args - 附加参数
*/
const emitLogMark = (message, ...args) => {
emitLog("mark", message, ...args);
};
//#endregion
//#region src/validation/utils.ts
function smartNumber(errorMessage, minValue = 1, isInteger = false) {
if (isInteger) return zod.default.coerce.number({ error: errorMessage }).int({ error: `${errorMessage.replace("不能为空", "")}必须是整数,不能包含小数` }).min(minValue, { error: `${errorMessage.replace("不能为空", "")}必须大于等于${minValue}` });
else return zod.default.coerce.number({ error: errorMessage }).min(minValue, { error: `${errorMessage.replace("不能为空", "")}必须大于等于${minValue}` });
}
/**
* 智能正整数转换器 - 专门用于正整数类型的转换
* @param errorMessage - 自定义错误信息
* @returns Zod正整数验证器
*/
const smartPositiveInteger = (errorMessage) => {
return smartNumber(errorMessage, 1, true);
};
/**
* 从页面HTML中提取用户信息
* @param html - 包含用户页面HTML的字符串
* @returns 提取到的用户信息对象或null
*/
const extractCreatorInfoFromHtml = (html) => {
const match = html.match(/<script>window\.__INITIAL_STATE__=(.+)<\/script>/m);
if (!match) return null;
try {
const jsonStr = match[1].replace(/:undefined/g, ":null");
return JSON.parse(jsonStr)?.user?.userPageData ?? null;
} catch (error) {
console.error("解析用户信息失败:", error);
return null;
}
};
//#endregion
//#region src/validation/bilibili.ts
/** 视频信息参数验证 */
const BilibiliVideoParamsSchema = zod.default.object({
methodType: zod.default.literal("videoInfo", { error: "方法类型必须是\"videoInfo\"" }),
bvid: zod.default.string({ error: "BVID必须是字符串" }).min(1, { error: "BVID不能为空" })
});
/** 视频流参数验证 */
const BilibiliVideoDownloadParamsSchema = zod.default.object({
methodType: zod.default.literal("videoStream", { error: "方法类型必须是\"videoStream\"" }),
avid: smartNumber("AVID不能为空", 1, true),
cid: smartNumber("CID不能为空", 1, true)
});
/** 评论参数验证 */
const BilibiliCommentParamsSchema = zod.default.object({
methodType: zod.default.literal("comments", { error: "方法类型必须是\"comments\"" }),
oid: zod.default.string({ error: "OID必须是字符串" }).min(1, { error: "OID不能为空" }),
type: smartNumber("评论类型不能为空", 1, true).refine((val) => [
1,
2,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
33
].includes(val), { error: "无效的评论区类型" }),
number: zod.default.coerce.number({ error: "评论数量必须是数字" }).int({ error: "评论数量必须是整数" }).positive({ error: "评论数量必须是正数" }).default(20).optional(),
pn: zod.default.coerce.number({ error: "页码必须是数字" }).int({ error: "页码必须是整数" }).positive({ error: "页码必须是正数" }).default(1).optional()
});
/** 评论回复参数验证 */
const BilibiliCommentReplyParamsSchema = zod.default.object({
methodType: zod.default.literal("commentReplies", { error: "方法类型必须是\"commentReplies\"" }),
oid: zod.default.string({ error: "OID必须是字符串" }).min(1, { error: "OID不能为空" }),
type: smartNumber("评论类型不能为空", 1, true).refine((val) => [
1,
2,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
33
].includes(val), { error: "无效的评论区类型" }),
root: zod.default.string({ error: "根评论ID必须是字符串" }).min(1, { error: "根评论ID不能为空" }),
number: zod.default.coerce.number({ error: "评论数量必须是数字" }).int({ error: "评论数量必须是整数" }).positive({ error: "评论数量必须是正数" }).default(20).optional(),
pn: zod.default.coerce.number({ error: "页码必须是数字" }).int({ error: "页码必须是整数" }).positive({ error: "页码必须是正数" }).default(1).optional()
});
/** 用户参数验证 */
const BilibiliUserParamsSchema = zod.default.object({
methodType: zod.default.enum([
"userCard",
"userDynamicList",
"uploaderTotalViews",
"userSpaceInfo"
], { error: "方法类型必须是指定的枚举值之一" }),
host_mid: smartNumber("UP主UID不能为空", 1, true)
});
/** 表情参数验证 */
const BilibiliEmojiParamsSchema = zod.default.object({ methodType: zod.default.literal("emojiList", { error: "方法类型必须是\"emojiList\"" }) });
/** 番剧信息参数验证 */
const BilibiliBangumiInfoParamsSchema = zod.default.object({
methodType: zod.default.literal("bangumiInfo", { error: "方法类型必须是\"bangumiInfo\"" }),
ep_id: zod.default.string({ error: "番剧EP ID必须是字符串" }).min(1, { error: "番剧EP ID不能为空" }).optional(),
season_id: zod.default.string({ error: "番剧季度ID必须是字符串" }).optional()
}).refine((data) => data.ep_id ?? data.season_id, {
error: "ep_id 和 season_id 至少需要提供一个",
path: ["ep_id"]
});
/** 番剧流参数验证 */
const BilibiliBangumiStreamParamsSchema = zod.default.object({
methodType: zod.default.literal("bangumiStream", { error: "方法类型必须是\"bangumiStream\"" }),
cid: smartNumber("CID不能为空", 1, true),
ep_id: zod.default.string({ error: "番剧EP ID必须是字符串" }).min(1, { error: "番剧EP ID不能为空" })
});
/** 动态参数验证 */
const BilibiliDynamicParamsSchema = zod.default.object({
methodType: zod.default.enum(["dynamicDetail", "dynamicCard"], { error: "方法类型必须是\"dynamicDetail\"或\"dynamicCard\"" }),
dynamic_id: zod.default.string({ error: "动态ID必须是字符串" }).min(1, { error: "动态ID不能为空" })
});
/** 直播间参数验证 */
const BilibiliLiveParamsSchema = zod.default.object({
methodType: zod.default.enum(["liveRoomInfo", "liveRoomInit"], { error: "方法类型必须是\"liveRoomInfo\"或\"liveRoomInit\"" }),
room_id: zod.default.string({ error: "直播间ID必须是字符串" }).min(1, { error: "直播间ID不能为空" })
});
/** 登录状态参数验证 */
const BilibiliLoginParamsSchema = zod.default.object({ methodType: zod.default.literal("loginStatus", { error: "方法类型必须是\"loginStatus\"" }) });
/** 申请二维码参数验证 */
const BilibiliQrcodeParamsSchema = zod.default.object({ methodType: zod.default.literal("loginQrcode", { error: "方法类型必须是\"loginQrcode\"" }) });
/** 二维码状态参数验证 */
const BilibiliQrcodeStatusParamsSchema = zod.default.object({
methodType: zod.default.literal("qrcodeStatus", { error: "方法类型必须是\"qrcodeStatus\"" }),
qrcode_key: zod.default.string({ error: "二维码key必须是字符串" }).min(1, { error: "二维码key不能为空" })
});
/** AV转BV参数验证 */
const BilibiliAv2BvParamsSchema = zod.default.object({
methodType: zod.default.literal("avToBv", { error: "方法类型必须是\"avToBv\"" }),
avid: zod.default.coerce.number({ error: "AVID必须是数字" }).int({ error: "AVID必须是整数" }).positive({ error: "AVID必须是正数" })
});
/** BV转AV参数验证 */
const BilibiliBv2AvParamsSchema = zod.default.object({
methodType: zod.default.literal("bvToAv", { error: "方法类型必须是\"bvToAv\"" }),
bvid: zod.default.string({ error: "BVID必须是字符串" }).min(1, { error: "BVID不能为空" })
});
/** 专栏内容参数验证 */
const BilibiliArticleParamsSchema = zod.default.object({
methodType: zod.default.literal("articleContent", { error: "方法类型必须是\"articleContent\"" }),
id: zod.default.string({ error: "专栏ID必须是字符串" }).min(1, { error: "专栏ID不能为空" })
});
/** 专栏卡片参数验证 */
const BilibiliArticleCardParamsSchema = zod.default.object({
methodType: zod.default.literal("articleCards", { error: "方法类型必须是\"articleCards\"" }),
ids: zod.default.union([zod.default.array(zod.default.string({ error: "被查询的 id 列表必须是字符串数组" })).min(1, { error: "被查询的 id 列表不能为空" }), zod.default.string({ error: "被查询的 id 列表必须是字符串" }).min(1, { error: "被查询的 id 列表不能为空" })])
});
/** 专栏信息参数验证 */
const BilibiliArticleInfoParamsSchema = zod.default.object({
methodType: zod.default.literal("articleInfo", { error: "方法类型必须是\"articleInfo\"" }),
id: zod.default.string({ error: "专栏ID必须是字符串" }).min(1, { error: "专栏ID不能为空" })
});
/** 文集信息参数验证 */
const BilibiliColumnInfoParamsSchema = zod.default.object({
methodType: zod.default.literal("articleListInfo", { error: "方法类型必须是\"articleListInfo\"" }),
id: zod.default.string({ error: "文集ID必须是字符串" }).min(1, { error: "文集ID不能为空" })
});
/** 验证码申请参数验证 */
const BilibiliApplyCaptchaParamsSchema = zod.default.object({
methodType: zod.default.literal("captchaFromVoucher", { error: "方法类型必须是\"captchaFromVoucher\"" }),
csrf: zod.default.string({ error: "CSRF Token必须是字符串" }).optional(),
v_voucher: zod.default.string({ error: "验证码ID必须是字符串" }).min(1, { error: "验证码ID不能为空" })
});
/** 验证码验证参数验证 */
const BilibiliValidateCaptchaParamsSchema = zod.default.object({
methodType: zod.default.literal("validateCaptcha", { error: "方法类型必须是\"validateCaptcha\"" }),
csrf: zod.default.string({ error: "CSRF Token必须是字符串" }).optional(),
challenge: zod.default.string({ error: "验证码challenge必须是字符串" }).min(1, { error: "验证码challenge不能为空" }),
token: zod.default.string({ error: "验证码token必须是字符串" }).min(1, { error: "验证码token不能为空" }),
validate: zod.default.string({ error: "验证码validate必须是字符串" }).min(1, { error: "验证码validate不能为空" }),
seccode: zod.default.string({ error: "验证码seccode必须是字符串" }).min(1, { error: "验证码seccode不能为空" })
});
/** 弹幕参数验证 */
const BilibiliDanmakuParamsSchema = zod.default.object({
methodType: zod.default.literal("videoDanmaku", { error: "方法类型必须是\"videoDanmaku\"" }),
cid: smartNumber("CID不能为空", 1, true),
segment_index: zod.default.coerce.number({ error: "分段序号必须是数字" }).int({ error: "分段序号必须是整数" }).positive({ error: "分段序号必须是正数" }).default(1).optional()
});
/** B站参数验证模式映射 */
const BilibiliValidationSchemas = {
videoInfo: BilibiliVideoParamsSchema,
videoStream: BilibiliVideoDownloadParamsSchema,
comments: BilibiliCommentParamsSchema,
commentReplies: BilibiliCommentReplyParamsSchema,
userCard: BilibiliUserParamsSchema,
userDynamicList: BilibiliUserParamsSchema,
userSpaceInfo: BilibiliUserParamsSchema,
emojiList: BilibiliEmojiParamsSchema,
bangumiInfo: BilibiliBangumiInfoParamsSchema,
bangumiStream: BilibiliBangumiStreamParamsSchema,
dynamicDetail: BilibiliDynamicParamsSchema,
dynamicCard: BilibiliDynamicParamsSchema,
liveRoomInfo: BilibiliLiveParamsSchema,
liveRoomInit: BilibiliLiveParamsSchema,
loginStatus: BilibiliLoginParamsSchema,
loginQrcode: BilibiliQrcodeParamsSchema,
qrcodeStatus: BilibiliQrcodeStatusParamsSchema,
uploaderTotalViews: BilibiliUserParamsSchema,
avToBv: BilibiliAv2BvParamsSchema,
bvToAv: BilibiliBv2AvParamsSchema,
articleContent: BilibiliArticleParamsSchema,
articleCards: BilibiliArticleCardParamsSchema,
articleInfo: BilibiliArticleInfoParamsSchema,
articleListInfo: BilibiliColumnInfoParamsSchema,
captchaFromVoucher: BilibiliApplyCaptchaParamsSchema,
validateCaptcha: BilibiliValidateCaptchaParamsSchema,
videoDanmaku: BilibiliDanmakuParamsSchema
};
/** B站方法路由映射 */
const BilibiliMethodRoutes = {
videoInfo: "/fetch_one_video",
videoStream: "/fetch_video_playurl",
comments: "/fetch_work_comments",
commentReplies: "/fetch_comment_reply",
userCard: "/fetch_user_profile",
userDynamicList: "/fetch_user_dynamic",
userSpaceInfo: "/fetch_user_space_info",
emojiList: "/fetch_emoji_list",
bangumiInfo: "/fetch_bangumi_video_info",
bangumiStream: "/fetch_bangumi_video_playurl",
dynamicDetail: "/fetch_dynamic_info",
dynamicCard: "/fetch_dynamic_card",
liveRoomInfo: "/fetch_live_room_detail",
liveRoomInit: "/fetch_liveroom_def",
loginStatus: "/login_basic_info",
loginQrcode: "/new_login_qrcode",
qrcodeStatus: "/check_qrcode",
uploaderTotalViews: "/fetch_user_full_view",
avToBv: "/av_to_bv",
bvToAv: "/bv_to_av",
articleContent: "/fetch_article_content",
articleCards: "/fetch_article_card",
articleInfo: "/fetch_article_info",
articleListInfo: "/fetch_column_info",
captchaFromVoucher: "/apply_captcha",
validateCaptcha: "/validate_captcha",
videoDanmaku: "/fetch_danmaku"
};
//#endregion
//#region src/validation/douyin.ts
/** 作品参数验证 */
const DouyinWorkParamsSchema = zod.default.object({
methodType: zod.default.enum([
"videoWork",
"imageAlbumWork",
"slidesWork",
"parseWork",
"textWork"
], { error: "方法类型必须是指定的枚举值之一" }),
aweme_id: zod.default.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" })
});
/** 评论参数验证 */
const DouyinCommentParamsSchema = zod.default.object({
methodType: zod.default.literal("comments", { error: "方法类型必须是\"comments\"" }),
aweme_id: zod.default.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" }),
number: smartPositiveInteger("评论数量必须是正整数").optional().default(50),
cursor: zod.default.coerce.number({ error: "游标必须是数字" }).int({ error: "游标必须是整数" }).min(0, { error: "游标不能小于0" }).default(0).optional()
});
/** 热点词参数验证 */
const DouyinHotWordsParamsSchema = zod.default.object({
methodType: zod.default.literal("suggestWords", { error: "方法类型必须是\"suggestWords\"" }),
query: zod.default.string({ error: "搜索词必须是字符串" }).min(1, { error: "搜索词不能为空" })
});
/** 搜索参数验证 */
const DouyinSearchParamsSchema = zod.default.object({
methodType: zod.default.literal("search", { error: "方法类型必须是\"search\"" }),
query: zod.default.string({ error: "搜索词必须是字符串" }).min(1, { error: "搜索词不能为空" }),
type: zod.default.enum([
"general",
"user",
"video"
], { error: "搜索类型必须是\"general\"、\"user\"或\"video\"" }).optional().default("general"),
number: smartPositiveInteger("搜索数量必须是正整数").optional().default(10),
search_id: zod.default.string({ error: "搜索ID必须是字符串" }).optional()
});
/** 评论回复参数验证 */
const DouyinCommentReplyParamsSchema = zod.default.object({
methodType: zod.default.literal("commentReplies", { error: "方法类型必须是\"commentReplies\"" }),
aweme_id: zod.default.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" }),
comment_id: zod.default.string({ error: "评论ID必须是字符串" }).min(1, { error: "评论ID不能为空" }),
number: smartPositiveInteger("评论数量必须是正整数").optional().default(5),
cursor: zod.default.coerce.number({ error: "游标必须是数字" }).int({ error: "游标必须是整数" }).min(0, { error: "游标不能小于0" }).default(0).optional()
});
/** 用户参数验证 */
const DouyinUserParamsSchema = zod.default.object({
methodType: zod.default.literal("userProfile", { error: "方法类型必须是\"userProfile\"" }),
sec_uid: zod.default.string({ error: "用户ID必须是字符串" }).min(1, { error: "用户ID不能为空" })
});
/** 用户列表参数验证(视频列表、喜欢列表、推荐列表) */
const DouyinUserListParamsSchema = zod.default.object({
methodType: zod.default.enum([
"userVideoList",
"userFavoriteList",
"userRecommendList"
], { error: "方法类型必须是指定的枚举值之一" }),
sec_uid: zod.default.string({ error: "用户ID必须是字符串" }).min(1, { error: "用户ID不能为空" }),
number: smartPositiveInteger("获取数量必须是正整数").optional().default(18),
max_cursor: zod.default.string({ error: "游标必须是字符串" }).optional()
});
/** 音乐参数验证 */
const DouyinMusicParamsSchema = zod.default.object({
methodType: zod.default.literal("musicInfo", { error: "方法类型必须是\"musicInfo\"" }),
music_id: zod.default.string({ error: "音乐ID必须是字符串" }).min(1, { error: "音乐ID不能为空" })
});
/** 直播间参数验证 */
const DouyinLiveRoomParamsSchema = zod.default.object({
methodType: zod.default.literal("liveRoomInfo", { error: "方法类型必须是\"liveRoomInfo\"" }),
web_rid: zod.default.string({ error: "直播间ID必须是字符串" }).min(1, { error: "直播间ID不能为空" }),
room_id: zod.default.string({ error: "直播间ID必须是字符串" }).min(1, { error: "直播间ID不能为空" })
});
/** 二维码参数验证 */
const DouyinQrcodeParamsSchema = zod.default.object({
methodType: zod.default.literal("loginQrcode", { error: "方法类型必须是\"loginQrcode\"" }),
verify_fp: zod.default.string({ error: "fp指纹必须是字符串" }).min(1, { error: "fp指纹不能为空" })
});
/** 表情列表参数验证 */
const DouyinEmojiListParamsSchema = zod.default.object({ methodType: zod.default.literal("emojiList", { error: "方法类型必须是\"emojiList\"" }) });
/** 动态表情参数验证 */
const DouyinEmojiProParamsSchema = zod.default.object({ methodType: zod.default.literal("dynamicEmojiList", { error: "方法类型必须是\"dynamicEmojiList\"" }) });
/** 弹幕参数验证 */
const DouyinDanmakuParamsSchema = zod.default.object({
methodType: zod.default.literal("danmakuList", { error: "方法类型必须是\"danmakuList\"" }),
aweme_id: zod.default.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" }),
start_time: zod.default.coerce.number({ error: "开始时间必须是数字" }).int({ error: "开始时间必须是整数" }).min(0, { error: "开始时间不能小于0" }).optional(),
end_time: zod.default.coerce.number({ error: "结束时间必须是数字" }).int({ error: "结束时间必须是整数" }).min(0, { error: "结束时间不能小于0" }).optional(),
duration: zod.default.coerce.number({ error: "视频时长必须是数字" }).int({ error: "视频时长必须是整数" }).min(0, { error: "视频时长不能小于0" })
}).refine((data) => {
if (data.end_time !== void 0) return data.end_time <= data.duration;
return true;
}, {
error: "获取弹幕区间的结束时间不能超过视频总时长",
path: ["end_time"]
}).refine((data) => {
if (data.start_time !== void 0 && data.end_time !== void 0) return data.start_time < data.end_time;
return true;
}, {
error: "获取弹幕区间的开始时间必须小于结束时间",
path: ["start_time"]
});
/** 抖音参数验证模式映射 */
const DouyinValidationSchemas = {
textWork: DouyinWorkParamsSchema,
parseWork: DouyinWorkParamsSchema,
videoWork: DouyinWorkParamsSchema,
imageAlbumWork: DouyinWorkParamsSchema,
slidesWork: DouyinWorkParamsSchema,
comments: DouyinCommentParamsSchema,
userProfile: DouyinUserParamsSchema,
userVideoList: DouyinUserListParamsSchema,
userFavoriteList: DouyinUserListParamsSchema,
userRecommendList: DouyinUserListParamsSchema,
suggestWords: DouyinHotWordsParamsSchema,
search: DouyinSearchParamsSchema,
musicInfo: DouyinMusicParamsSchema,
liveRoomInfo: DouyinLiveRoomParamsSchema,
loginQrcode: DouyinQrcodeParamsSchema,
emojiList: DouyinEmojiListParamsSchema,
dynamicEmojiList: DouyinEmojiProParamsSchema,
commentReplies: DouyinCommentReplyParamsSchema,
danmakuList: DouyinDanmakuParamsSchema
};
/** 抖音方法路由映射 */
const DouyinMethodRoutes = {
parseWork: "/fetch_one_work",
textWork: "/fetch_one_work",
videoWork: "/fetch_one_work",
imageAlbumWork: "/fetch_one_work",
slidesWork: "/fetch_one_work",
comments: "/fetch_work_comments",
commentReplies: "/fetch_video_comment_replies",
userProfile: "/fetch_user_info",
userVideoList: "/fetch_user_post_videos",
userFavoriteList: "/fetch_user_favorite_list",
userRecommendList: "/fetch_user_recommend_list",
search: "/fetch_search_info",
suggestWords: "/fetch_suggest_words",
musicInfo: "/fetch_music_work",
emojiList: "/fetch_emoji_list",
dynamicEmojiList: "/fetch_emoji_pro_list",
liveRoomInfo: "/fetch_user_live_videos",
danmakuList: "/fetch_work_danmaku",
loginQrcode: "/fetch_login_qrcode"
};
//#endregion
//#region src/validation/kuaishou.ts
/**
* 快手视频参数验证模式
*/
const KuaishouVideoParamsSchema = zod.default.object({
methodType: zod.default.literal("videoWork", { error: "methodType must be \"videoWork\"" }),
photoId: zod.default.string({ error: "photoId must be a string" }).min(1, { error: "photoId cannot be empty" })
});
/**
* 快手评论参数验证模式
*/
const KuaishouCommentParamsSchema = zod.default.object({
methodType: zod.default.literal("comments", { error: "methodType must be \"comments\"" }),
photoId: zod.default.string({ error: "photoId must be a string" }).min(1, { error: "photoId cannot be empty" })
});
/**
* 快手用户主页参数验证模式
*/
const KuaishouUserProfileParamsSchema = zod.default.object({
methodType: zod.default.literal("userProfile", { error: "methodType must be \"userProfile\"" }),
principalId: zod.default.string({ error: "principalId must be a string" }).min(1, { error: "principalId cannot be empty" })
});
/**
* 快手用户作品列表参数验证模式
*/
const KuaishouUserWorkListParamsSchema = zod.default.object({
methodType: zod.default.literal("userWorkList", { error: "methodType must be \"userWorkList\"" }),
principalId: zod.default.string({ error: "principalId must be a string" }).min(1, { error: "principalId cannot be empty" }),
pcursor: zod.default.string({ error: "pcursor must be a string" }).optional(),
count: zod.default.number({ error: "count must be a number" }).int({ error: "count must be an integer" }).positive({ error: "count must be positive" }).max(100, { error: "count must be less than or equal to 100" }).optional()
});
/**
* 快手直播间信息参数验证模式
*/
const KuaishouLiveRoomInfoParamsSchema = zod.default.object({
methodType: zod.default.literal("liveRoomInfo", { error: "methodType must be \"liveRoomInfo\"" }),
principalId: zod.default.string({ error: "principalId must be a string" }).min(1, { error: "principalId cannot be empty" })
});
/**
* 快手表情参数验证模式
*/
const KuaishouEmojiParamsSchema = zod.default.object({ methodType: zod.default.literal("emojiList", { error: "methodType must be \"emojiList\"" }) });
/**
* 快手参数验证模式映射
*/
const KuaishouValidationSchemas = {
videoWork: KuaishouVideoParamsSchema,
comments: KuaishouCommentParamsSchema,
userProfile: KuaishouUserProfileParamsSchema,
userWorkList: KuaishouUserWorkListParamsSchema,
liveRoomInfo: KuaishouLiveRoomInfoParamsSchema,
emojiList: KuaishouEmojiParamsSchema
};
/**
* 快手方法路由映射
*/
const KuaishouMethodRoutes = {
videoWork: "/fetch_one_work",
comments: "/fetch_work_comments",
userProfile: "/fetch_user_profile",
userWorkList: "/fetch_user_work_list",
liveRoomInfo: "/fetch_live_room_info",
emojiList: "/fetch_emoji_list"
};
//#endregion
//#region src/platform/xiaohongshu/sign/index.ts
/**
* 小红书签名算法类
*/
var xiaohongshuSign = class {
static client = new _ikenxuan_xhshow_ts.Xhshow();
/**
* 生成GET请求的X-S签名
* @param path - API路径
* @param a1Cookie - a1 cookie值
* @param clientType - 客户端类型,默认为 'xhs-pc-web'
* @param params - 查询参数对象
* @returns X-S签名
*/
static generateXSGet(path, a1Cookie, clientType = "xhs-pc-web", params = {}) {
return this.client.signXsGet(path, a1Cookie, clientType, params);
}
/**
* 生成POST请求的X-S签名
* @param path - API路径
* @param a1Cookie - a1 cookie值
* @param clientType - 客户端类型,默认为 'xhs-pc-web'
* @param body - 请求体对象
* @returns X-S签名
*/
static generateXSPost(path, a1Cookie, clientType = "xhs-pc-web", body = {}) {
return this.client.signXsPost(path, a1Cookie, clientType, body);
}
/**
* 生成X-S-Common参数
* @param cookies - cookie字符串
* @returns Base64编码的随机字符串
*/
static generateXSCommon(cookies) {
return this.client.signXsCommon(cookies);
}
/**
* 生成X-T时间戳
* @returns 当前时间戳字符串
*/
static generateXT() {
return this.client.getXT();
}
/**
* 生成X-B3-Traceid
* @returns 16位随机字符串
*/
static generateXB3Traceid() {
return this.client.getB3TraceId();
}
/**
* 从cookie字符串中提取a1值
* @param cookieString - 完整的cookie字符串
* @returns a1 cookie值
*/
static extractA1FromCookie(cookieString) {
const match = cookieString.match(/a1=([^;]+)/);
return match ? match[1] : "";
}
/**
* 生成搜索ID
* @returns 搜索ID字符串
*/
static getSearchId = () => (BigInt(Date.now()) << 64n) + BigInt(Math.floor(Math.random() * 2147483646)).toString(36);
};
//#endregion
//#region src/platform/xiaohongshu/API.ts
/**
* 搜索排序类型枚举
*/
let SearchSortType = /* @__PURE__ */ function(SearchSortType) {
/**
* 默认排序
*/
SearchSortType["GENERAL"] = "general";
/**
* 最受欢迎(按热度降序)
*/
SearchSortType["MOST_POPULAR"] = "popularity_descending";
/**
* 最新发布(按时间降序)
*/
SearchSortType["LATEST"] = "time_descending";
return SearchSortType;
}({});
/**
* 搜索笔记类型枚举
*/
let SearchNoteType = /* @__PURE__ */ function(SearchNoteType) {
/**
* 默认(全部类型)
*/
SearchNoteType[SearchNoteType["ALL"] = 0] = "ALL";
/**
* 仅视频
*/
SearchNoteType[SearchNoteType["VIDEO"] = 1] = "VIDEO";
/**
* 仅图片
*/
SearchNoteType[SearchNoteType["IMAGE"] = 2] = "IMAGE";
return SearchNoteType;
}({});
/**
* 构建查询字符串
* @param params - 参数对象
* @returns 查询字符串
*/
const buildQueryString$1 = (params) => {
return Object.entries(params).filter(([_, value]) => value !== void 0 && value !== null).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
};
/**
* 小红书API地址配置
*/
const xiaohongshuApiUrls = {
/**
* 获取首页推荐数据的接口地址
* @param data - 请求参数
* @returns 完整的接口URL
*/
homeFeed(data = {}) {
return {
apiPath: "/api/sns/web/v1/homefeed",
Url: "https://edith.xiaohongshu.com/api/sns/web/v1/homefeed",
Body: {
cursor_score: data.cursor_score ?? "1.7599348899670024E9",
num: data.num ?? 33,
refresh_type: data.refresh_type ?? 3,
note_index: data.note_index ?? 33,
category: data.category ?? "homefeed_recommend",
search_key: data.search_key ?? "",
image_formats: [
"jpg",
"webp",
"avif"
]
}
};
},
/**
* 获取单个笔记数据的接口地址
* @param data - 请求参数
* @returns 完整的接口URL
*/
noteDetail(data) {
return {
apiPath: "/api/sns/web/v1/feed",
Url: "https://edith.xiaohongshu.com/api/sns/web/v1/feed",
Body: {
source_note_id: data.note_id,
image_formats: [
"jpg",
"webp",
"avif"
],
extra: { need_body_topic: "1" },
xsec_source: "pc_feed",
xsec_token: data.xsec_token
}
};
},
/**
* 获取评论数据的接口地址
* @param data - 请求参数
* @returns 完整的接口URL
*/
noteComments(data) {
return {
apiPath: "/api/sns/web/v2/comment/page",
Url: `https://edith.xiaohongshu.com/api/sns/web/v2/comment/page?${buildQueryString$1({
note_id: data.note_id,
cursor: data.cursor ?? "",
image_formats: [
"jpg",
"webp",
"avif"
].join(","),
xsec_token: data.xsec_token
})}`
};
},
/**
* 获取用户数据的接口地址
* @param data - 请求参数
* @returns 完整的接口URL
*/
userProfile(data) {
return {
apiPath: "/api/sns/web/v1/user/otherinfo",
Url: `https://www.xiaohongshu.com/user/profile/${data.user_id}`
};
},
/**
* 获取用户笔记数据的接口地址
* @param data - 请求参数
* @returns 完整的接口URL
*/
userNoteList(data) {
return {
apiPath: "/api/sns/web/v1/user_posted",
Url: `https://edith.xiaohongshu.com/api/sns/web/v1/user_posted?${buildQueryString$1({
user_id: data.user_id,
cursor: data.cursor ?? "",
num: data.num ?? 30,
image_formats: [
"jpg",
"webp",
"avif"
].join(","),
xsec_source: "pc_feed"
})}`
};
},
/**
* 获取笔记表情列表的接口地址
* @param data - 请求参数
* @returns 完整的接口URL
*/
emojiList(data) {
return {
apiPath: "/api/im/redmoji/detail",
Url: "https://edith.xiaohongshu.com/api/im/redmoji/detail"
};
},
/**
* 搜索笔记的接口地址
* @param data - 请求参数
* @returns 完整的接口URL
*/
searchNotes(data) {
return {
apiPath: "/api/sns/web/v1/search/notes",
Body: {
keyword: data.keyword,
page: data.page ?? 1,
page_size: data.page_size ?? 20,
sort: "general",
note_type: 0,
search_id: xiaohongshuSign.getSearchId(),
image_formats: [
"jpg",
"webp",
"avif"
]
},
Url: "https://edith.xiaohongshu.com/api/sns/web/v1/search/notes"
};
}
};
/**
* 创建小红书API URLs实例
* @returns 小红书API URLs对象
*/
const createXiaohongshuApiUrls = () => {
return xiaohongshuApiUrls;
};
//#endregion
//#region src/validation/xiaohongshu.ts
const SearchSortTypeValues = Object.values(SearchSortType).filter((v) => typeof v === "string");
const SearchNoteTypeValues = Object.values(SearchNoteType).filter((v) => typeof v === "number");
/**
* 小红书验证模式映射
*/
const XiaohongshuValidationSchemas = {
homeFeed: zod.default.object({
methodType: zod.default.literal("homeFeed", { error: "methodType must be \"homeFeed\"" }),
cursor_score: zod.default.string({ error: "cursor_score must be a string" }).optional(),
num: zod.default.coerce.number({ error: "num must be a number" }).int({ error: "num must be an integer" }).min(1, { error: "num cannot be less than 1" }).max(100, { error: "num cannot be greater than 100" }).optional(),
refresh_type: zod.default.coerce.number({ error: "refresh_type must be a number" }).int({ error: "refresh_type must be an integer" }).optional(),
note_index: zod.default.coerce.number({ error: "note_index must be a number" }).int({ error: "note_index must be an integer" }).optional(),
category: zod.default.string({ error: "category must be a string" }).optional(),
search_key: zod.default.string({ error: "search_key must be a string" }).optional()
}),
noteDetail: zod.default.object({
methodType: zod.default.literal("noteDetail", { error: "methodType must be \"noteDetail\"" }),
note_id: zod.default.string({ error: "note_id must be a string" }),
xsec_token: zod.default.string({ error: "xsec_token must be a string" })
}),
noteComments: zod.default.object({
methodType: zod.default.literal("noteComments", { error: "methodType must be \"noteComments\"" }),
note_id: zod.default.string({ error: "note_id must be a string" }),
cursor: zod.d