karin-plugin-kkk 2.34.0 → 2.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +869 -1165
- package/README.md +4 -5
- package/config/default_config/app.yaml +1 -1
- package/config/default_config/bilibili.yaml +2 -2
- package/config/default_config/config.json +136 -0
- package/config/default_config/cookies.yaml +2 -2
- package/config/default_config/douyin.yaml +3 -3
- package/config/default_config/kuaishou.yaml +1 -1
- package/config/default_config/pushlist.yaml +6 -6
- package/config/default_config/request.yaml +1 -1
- package/config/default_config/xiaohongshu.yaml +2 -2
- package/lib/build-metadata.json +5 -5
- package/lib/core_chunk/amagi.d.mts +18 -18
- package/lib/core_chunk/amagi.js +1 -1
- package/lib/core_chunk/amagiClient.js +1 -1
- package/lib/core_chunk/main.js +752 -1928
- package/lib/core_chunk/vendor.js +4738 -2417
- package/lib/karin-plugin-kkk.css +198 -236
- package/lib/root.js +1 -1
- package/lib/web/assets/CronEditor-KiEwYMrF.js +1 -0
- package/lib/web/assets/DesktopLayout-Cdla4l6u.js +1 -0
- package/lib/web/assets/MobileLayout-DfXVuyA-.js +1 -0
- package/lib/web/assets/ThemeSwitch-DcAlzu8R.js +1 -0
- package/lib/web/assets/index-3mh4kVq1.css +2 -0
- package/lib/web/assets/index-zS6iiHmV.js +47 -0
- package/lib/web/index.html +3 -3
- package/package.json +28 -28
- package/resources/font/bilifont/font.css +3 -3
- package/resources/font/fansmedal-num/font.css +1 -1
- package/lib/web/assets/AboutPanel-DFjZYu60.js +0 -1
- package/lib/web/assets/DesktopLayout-Cr5AitGP.js +0 -1
- package/lib/web/assets/MobileLayout-BW-vN-VU.js +0 -2
- package/lib/web/assets/index-8KaTMCj2.css +0 -2
- package/lib/web/assets/index-CQhoJUBv.js +0 -28
package/lib/core_chunk/main.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { i as __toESM } from "./rolldown-runtime.js";
|
|
2
|
-
import { $ as SiGooglephotos, $n as
|
|
2
|
+
import { $ as SiGooglephotos, $n as require_jsx_runtime, $t as Puzzle, A as RiHashtag, An as Copy, At as Upload, B as RiThumbUpFill, Bn as CircleAlert, Bt as ShoppingBag, C as AiFillHeart, Cn as Eye, Ct as X, D as Markdown, Dn as Crown, Dt as UsersRound, E as AiOutlineVideoCamera, En as Download, Et as Users, F as RiMessage3Fill, Fn as CircleQuestionMark, Ft as Star, G as RiVideoLine, Gn as Box, Gt as Search, H as RiTrophyFill, Hn as ChartColumn, Ht as ShieldCheck, I as RiPieChart2Fill, In as CircleFadingArrowUp, It as Square, J as SiSamsung, Jn as BellRing, Jt as RotateCcw, K as SiXiaomi, Kn as Bot, Kt as ScanLine, L as RiShareForwardFill, Ln as CircleEllipsis, Lt as SquarePen, M as RiHeart3Line, Mn as Clock, Mt as Trash2, N as RiHeartLine, Nn as Clock3, Nt as Terminal, O as RiArrowRightFill, On as Cpu, Ot as User, P as RiLiveLine, Pn as Clapperboard, Pt as Sun, Q as SiHonor, Qn as clsx, Qt as QrCode, R as RiStarFill, Rn as CircleCheck, Rt as Sparkles, S as BiImage, Sn as FilePlay, St as Zap, T as AiFillStar, Tn as ExternalLink, Tt as Video, U as RiUserFollowLine, Un as Camera, Ut as Share2, V as RiTiktokFill, Vn as Check, Vt as Shield, W as RiVerifiedBadgeFill, Wn as Calendar, Wt as Settings2, X as SiOneplus, Xn as Chip, Xt as Radio, Y as SiOppo, Yn as ArrowDownToLine, Yt as RefreshCw, Z as SiHuawei, Zn as Button, Zt as Quote, _ as MdSchedule, _n as Hash, _t as fromUnixTime, a as VictoryScatter, an as Moon, ar as zod_default, at as r, b as FaTiktok, bn as Gamepad2, bt as differenceInSeconds, c as VictoryChart, cn as Menu, ct as m, d as VictoryTheme, dn as LoaderCircle, dt as e, en as Plus, er as require_server_node, et as SiGithub, f as rehypeHighlight, fn as Link, ft as a$1, g as MdLocationOn, gn as Heart, gt as parse, h as MdLightbulbOutline, hn as Image$1, ht as zhCN, i as require_heic_decode, in as Music, ir as Xhshow, it as o$2, j as RiHeart3Fill, jn as Code, jt as TriangleAlert, k as RiGroupLine, kn as CornerDownLeft, kt as UserPlus, l as VictoryAxis, ln as Maximize, lt as a, m as MdInfoOutline, mn as Info, mt as c, n as require_lib, nn as Pencil, nr as require_protobufjs, nt as SiApple, o as VictoryPie, on as Monitor, ot as o, p as MdFitScreen, pn as LayoutTemplate, pt as o$1, q as SiVivo, qn as Bookmark, qt as Save, r as require_jpeg_js, rn as Palette, rr as Chalk, rt as SiAnthropic, s as VictoryLine, sn as MessageCircle, st as o$3, t as createProxyMiddleware, tn as Play, tr as require_react, tt as SiBilibili, u as VictoryLabel, un as MapPin, ut as n, v as FaCommentDots, vn as GitBranch, vt as formatDistanceToNow, w as AiFillPushpin, wn as EyeOff, wt as WandSparkles, x as FaUserGroup, xn as FileText, xt as twMerge, y as FaMusic, yn as Gift, yt as format, z as RiStarLine, zn as CircleCheckBig, zt as Smartphone } from "./vendor.js";
|
|
3
3
|
import "node:module";
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path, { resolve } from "node:path";
|
|
6
|
-
import URL$
|
|
7
|
-
import karin$1, { BOT_CONNECT, app, authMiddleware, checkPkgUpdate, checkPort, common, config,
|
|
6
|
+
import URL$1, { fileURLToPath } from "node:url";
|
|
7
|
+
import karin$1, { BOT_CONNECT, app, authMiddleware, checkPkgUpdate, checkPort, common, config, createBadRequestResponse, createNotFoundResponse, createServerErrorResponse, createSuccessResponse, db, defineConfig, ffmpeg, ffprobe, hooks, karin, karinPathHtml, karinPathTemp, logger, logger as logger$1, logs, mkdirSync, parseChangelog, range, render, restart, segment, updatePkg, watch } from "node-karin";
|
|
8
8
|
import { EventEmitter } from "node:events";
|
|
9
9
|
import crypto from "node:crypto";
|
|
10
10
|
import axios, { AxiosError } from "node-karin/axios";
|
|
@@ -1597,7 +1597,7 @@ var xiaohongshuApiUrls = {
|
|
|
1597
1597
|
* @param data - 请求参数
|
|
1598
1598
|
* @returns 完整的接口URL
|
|
1599
1599
|
*/
|
|
1600
|
-
emojiList(
|
|
1600
|
+
emojiList(_data) {
|
|
1601
1601
|
return {
|
|
1602
1602
|
apiPath: "/api/im/redmoji/detail",
|
|
1603
1603
|
Url: "https://edith.xiaohongshu.com/api/im/redmoji/detail"
|
|
@@ -2503,7 +2503,7 @@ var generateSecChUa = (userAgent) => {
|
|
|
2503
2503
|
*/
|
|
2504
2504
|
var getDouyinDefaultConfig = (cookie, requestConfig) => {
|
|
2505
2505
|
let finalUserAgent = requestConfig?.headers?.["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";
|
|
2506
|
-
finalUserAgent = finalUserAgent.replace(/\s+Edg\/[\d
|
|
2506
|
+
finalUserAgent = finalUserAgent.replace(/\s+Edg\/[\d.]+/g, "");
|
|
2507
2507
|
const defHeaders = {
|
|
2508
2508
|
Accept: "application/json, text/plain, */*",
|
|
2509
2509
|
"Accept-Encoding": "gzip, deflate, br, zstd",
|
|
@@ -2800,7 +2800,8 @@ var SM3 = class {
|
|
|
2800
2800
|
this.chunk = this.chunk.concat(a.slice(0, f));
|
|
2801
2801
|
while (this.chunk.length >= 64) {
|
|
2802
2802
|
this._compress(this.chunk);
|
|
2803
|
-
f < a.length
|
|
2803
|
+
if (f < a.length) this.chunk = a.slice(f, Math.min(f + 64, a.length));
|
|
2804
|
+
else this.chunk = [];
|
|
2804
2805
|
f += 64;
|
|
2805
2806
|
}
|
|
2806
2807
|
}
|
|
@@ -2836,10 +2837,17 @@ var SM3 = class {
|
|
|
2836
2837
|
if (t.length < 64) console.error("compress error: not enough data");
|
|
2837
2838
|
else {
|
|
2838
2839
|
for (var f = ((e) => {
|
|
2839
|
-
for (var r = new Array(132), t = 0; t < 16; t++)
|
|
2840
|
+
for (var r = new Array(132), t = 0; t < 16; t++) {
|
|
2841
|
+
r[t] = e[4 * t] << 24;
|
|
2842
|
+
r[t] |= e[4 * t + 1] << 16;
|
|
2843
|
+
r[t] |= e[4 * t + 2] << 8;
|
|
2844
|
+
r[t] |= e[4 * t + 3];
|
|
2845
|
+
r[t] >>>= 0;
|
|
2846
|
+
}
|
|
2840
2847
|
for (var n = 16; n < 68; n++) {
|
|
2841
2848
|
let a = r[n - 16] ^ r[n - 9] ^ this.le(r[n - 3], 15);
|
|
2842
|
-
a = a ^ this.le(a, 15) ^ this.le(a, 23)
|
|
2849
|
+
a = a ^ this.le(a, 15) ^ this.le(a, 23);
|
|
2850
|
+
r[n] = (a ^ this.le(r[n - 13], 7) ^ r[n - 6]) >>> 0;
|
|
2843
2851
|
}
|
|
2844
2852
|
for (n = 0; n < 64; n++) r[n + 68] = (r[n] ^ r[n + 4]) >>> 0;
|
|
2845
2853
|
return r;
|
|
@@ -2849,7 +2857,15 @@ var SM3 = class {
|
|
|
2849
2857
|
let u = this.pe(c, i[0], i[1], i[2]);
|
|
2850
2858
|
u = (4294967295 & (u = u + i[3] + s + f[c + 68])) >>> 0;
|
|
2851
2859
|
let b = this.he(c, i[4], i[5], i[6]);
|
|
2852
|
-
b = (4294967295 & (b = b + i[7] + o + f[c])) >>> 0
|
|
2860
|
+
b = (4294967295 & (b = b + i[7] + o + f[c])) >>> 0;
|
|
2861
|
+
i[3] = i[2];
|
|
2862
|
+
i[2] = this.le(i[1], 9);
|
|
2863
|
+
i[1] = i[0];
|
|
2864
|
+
i[0] = u;
|
|
2865
|
+
i[7] = i[6];
|
|
2866
|
+
i[6] = this.le(i[5], 19);
|
|
2867
|
+
i[5] = i[4];
|
|
2868
|
+
i[4] = (b ^ this.le(b, 9) ^ this.le(b, 17)) >>> 0;
|
|
2853
2869
|
}
|
|
2854
2870
|
for (let l = 0; l < 8; l++) this.reg[l] = (this.reg[l] ^ i[l]) >>> 0;
|
|
2855
2871
|
}
|
|
@@ -3160,7 +3176,7 @@ function generate_random_str() {
|
|
|
3160
3176
|
* @returns 清理后的User-Agent字符串
|
|
3161
3177
|
*/
|
|
3162
3178
|
var cleanUserAgentForSigning = (userAgent) => {
|
|
3163
|
-
return userAgent.replace(/\s+Edg\/[\d
|
|
3179
|
+
return userAgent.replace(/\s+Edg\/[\d.]+/g, "");
|
|
3164
3180
|
};
|
|
3165
3181
|
/**
|
|
3166
3182
|
* 抖音a_bogus签名算法
|
|
@@ -3272,7 +3288,7 @@ var XBogus = class {
|
|
|
3272
3288
|
* @returns 包含完整URL、X-Bogus值和使用的User-Agent的元组
|
|
3273
3289
|
*/
|
|
3274
3290
|
getXBogus(url, ua) {
|
|
3275
|
-
const parsedUrl = new URL$
|
|
3291
|
+
const parsedUrl = new URL$1.URL(url);
|
|
3276
3292
|
const urlPath = parsedUrl.pathname + parsedUrl.search;
|
|
3277
3293
|
const currentUa = ua ?? this.defaultUa;
|
|
3278
3294
|
const rc4EncryptedUa = this.rc4Encrypt(this.uaKey, currentUa);
|
|
@@ -4957,7 +4973,7 @@ async function fetchTextWork(options, cookie, requestConfig) {
|
|
|
4957
4973
|
* console.log(result.data) // 自动解析的作品数据
|
|
4958
4974
|
* ```
|
|
4959
4975
|
*/
|
|
4960
|
-
async function parseWork
|
|
4976
|
+
async function parseWork(options, cookie, requestConfig) {
|
|
4961
4977
|
return fetchDouyinInternal("parseWork", options, {
|
|
4962
4978
|
cookie,
|
|
4963
4979
|
requestConfig
|
|
@@ -5003,7 +5019,7 @@ function createBoundDouyinFetcher(cookie, requestConfig) {
|
|
|
5003
5019
|
fetchImageAlbumWork: (options, reqConfig) => fetchImageAlbumWork(options, cookie, reqConfig ?? requestConfig),
|
|
5004
5020
|
fetchSlidesWork: (options, reqConfig) => fetchSlidesWork(options, cookie, reqConfig ?? requestConfig),
|
|
5005
5021
|
fetchTextWork: (options, reqConfig) => fetchTextWork(options, cookie, reqConfig ?? requestConfig),
|
|
5006
|
-
parseWork: (options, reqConfig) => parseWork
|
|
5022
|
+
parseWork: (options, reqConfig) => parseWork(options, cookie, reqConfig ?? requestConfig),
|
|
5007
5023
|
fetchDanmakuList: (options, reqConfig) => fetchDanmakuList(options, cookie, reqConfig ?? requestConfig),
|
|
5008
5024
|
fetchWorkComments: (options, reqConfig) => fetchWorkComments$1(options, cookie, reqConfig ?? requestConfig),
|
|
5009
5025
|
fetchCommentReplies: (options, reqConfig) => fetchCommentReplies(options, cookie, reqConfig ?? requestConfig),
|
|
@@ -5041,7 +5057,7 @@ var douyinFetcher$1 = {
|
|
|
5041
5057
|
fetchImageAlbumWork,
|
|
5042
5058
|
fetchSlidesWork,
|
|
5043
5059
|
fetchTextWork,
|
|
5044
|
-
parseWork
|
|
5060
|
+
parseWork,
|
|
5045
5061
|
fetchDanmakuList,
|
|
5046
5062
|
fetchWorkComments: fetchWorkComments$1,
|
|
5047
5063
|
fetchCommentReplies,
|
|
@@ -7919,7 +7935,7 @@ var createNetworkErrorResult = (error, retries) => {
|
|
|
7919
7935
|
* @returns 清理后的User-Agent字符串
|
|
7920
7936
|
*/
|
|
7921
7937
|
var cleanUserAgent = (userAgent) => {
|
|
7922
|
-
return userAgent.replace(/\s+Edg\/[\d
|
|
7938
|
+
return userAgent.replace(/\s+Edg\/[\d.]+/g, "");
|
|
7923
7939
|
};
|
|
7924
7940
|
/**
|
|
7925
7941
|
* 执行网络请求并返回数据(带自动重试)
|
|
@@ -8171,8 +8187,7 @@ var qtparam = async (BASEURL, cookie) => {
|
|
|
8171
8187
|
126,
|
|
8172
8188
|
127
|
|
8173
8189
|
];
|
|
8174
|
-
|
|
8175
|
-
logininfo.data.vipStatus === 1 ? isvip = true : isvip = false;
|
|
8190
|
+
const isvip = logininfo.data.vipStatus === 1;
|
|
8176
8191
|
if (isvip) return {
|
|
8177
8192
|
QUERY: `&fnval=4048&fourk=1&${sign}`,
|
|
8178
8193
|
STATUS: "isLogin",
|
|
@@ -8313,7 +8328,7 @@ var DmSegMobileReplyType = null;
|
|
|
8313
8328
|
*/
|
|
8314
8329
|
function getProtoType() {
|
|
8315
8330
|
if (DmSegMobileReplyType) return DmSegMobileReplyType;
|
|
8316
|
-
DmSegMobileReplyType = import_protobufjs.
|
|
8331
|
+
DmSegMobileReplyType = import_protobufjs.Root.fromJSON(DANMAKU_PROTO_JSON).lookupType("bilibili.community.service.dm.v1.DmSegMobileReply");
|
|
8317
8332
|
return DmSegMobileReplyType;
|
|
8318
8333
|
}
|
|
8319
8334
|
/**
|
|
@@ -9330,15 +9345,6 @@ var kuaishouUtils = {
|
|
|
9330
9345
|
//#endregion
|
|
9331
9346
|
//#region ../amagi/packages/core/src/platform/xiaohongshu/XiaohongshuApi.ts
|
|
9332
9347
|
/**
|
|
9333
|
-
* 小红书 API 模块 (已废弃)
|
|
9334
|
-
*
|
|
9335
|
-
* 此模块中的 API 已在 v6 版本废弃
|
|
9336
|
-
* 请使用 xiaohongshuFetcher 或 client.xiaohongshu.fetcher 替代
|
|
9337
|
-
*
|
|
9338
|
-
* @module platform/xiaohongshu/XiaohongshuApi
|
|
9339
|
-
* @deprecated v6 已废弃,请使用 fetcher API 替代
|
|
9340
|
-
*/
|
|
9341
|
-
/**
|
|
9342
9348
|
* 创建废弃的 API 存根函数
|
|
9343
9349
|
*/
|
|
9344
9350
|
var createDeprecatedStub = (methodName) => {
|
|
@@ -10200,62 +10206,79 @@ var Cfg = class {
|
|
|
10200
10206
|
dirCfgPath;
|
|
10201
10207
|
/** 默认配置文件路径 */
|
|
10202
10208
|
defCfgPath;
|
|
10209
|
+
/** JSON 配置文件路径 */
|
|
10210
|
+
get jsonConfigPath() {
|
|
10211
|
+
return `${this.dirCfgPath}/config.json`;
|
|
10212
|
+
}
|
|
10213
|
+
get defJsonConfigPath() {
|
|
10214
|
+
return `${this.defCfgPath}/config.json`;
|
|
10215
|
+
}
|
|
10203
10216
|
constructor() {
|
|
10204
10217
|
this.dirCfgPath = `${karinPathBase}/${Root.pluginName}/config`;
|
|
10205
10218
|
this.defCfgPath = `${Root.pluginPath}/config/default_config/`;
|
|
10206
10219
|
}
|
|
10207
10220
|
/** 初始化配置 */
|
|
10208
10221
|
initCfg() {
|
|
10209
|
-
|
|
10210
|
-
const
|
|
10211
|
-
|
|
10212
|
-
|
|
10213
|
-
|
|
10214
|
-
|
|
10215
|
-
|
|
10222
|
+
if (!fs.existsSync(this.dirCfgPath)) fs.mkdirSync(this.dirCfgPath, { recursive: true });
|
|
10223
|
+
const hasJson = fs.existsSync(this.jsonConfigPath);
|
|
10224
|
+
const hasYaml = [
|
|
10225
|
+
"app",
|
|
10226
|
+
"bilibili",
|
|
10227
|
+
"cookies",
|
|
10228
|
+
"douyin",
|
|
10229
|
+
"kuaishou",
|
|
10230
|
+
"pushlist",
|
|
10231
|
+
"request",
|
|
10232
|
+
"upload",
|
|
10233
|
+
"xiaohongshu"
|
|
10234
|
+
].some((name) => fs.existsSync(`${this.dirCfgPath}/${name}.yaml`));
|
|
10235
|
+
if (!hasJson && !hasYaml && fs.existsSync(this.defJsonConfigPath)) fs.copyFileSync(this.defJsonConfigPath, this.jsonConfigPath);
|
|
10236
|
+
if (fs.existsSync(this.jsonConfigPath) && fs.existsSync(this.defJsonConfigPath)) {
|
|
10237
|
+
const userConfig = this.getJson();
|
|
10238
|
+
const defConfig = JSON.parse(fs.readFileSync(this.defJsonConfigPath, "utf8"));
|
|
10239
|
+
const merged = this.mergeJsonConfigs(defConfig, userConfig);
|
|
10240
|
+
if (JSON.stringify(merged) !== JSON.stringify(userConfig)) this.setJson(merged);
|
|
10216
10241
|
}
|
|
10217
|
-
/**
|
|
10218
|
-
* @description 监听配置文件
|
|
10219
|
-
*/
|
|
10220
10242
|
setTimeout(() => {
|
|
10221
|
-
|
|
10222
|
-
|
|
10223
|
-
|
|
10224
|
-
logger.
|
|
10225
|
-
import("./amagiClient.js").then(({ reloadAmagiConfig }) => {
|
|
10226
|
-
reloadAmagiConfig();
|
|
10227
|
-
}).catch((error) => {
|
|
10228
|
-
logger.error(`[Config] 重载 Amagi Client 失败: ${error}`);
|
|
10229
|
-
});
|
|
10243
|
+
if (fs.existsSync(this.jsonConfigPath)) watch(this.jsonConfigPath, (old, now) => {
|
|
10244
|
+
if (old?.amagi !== now?.amagi) {
|
|
10245
|
+
logger.debug("[karin-plugin-kkk][Config] 检测到 amagi 配置变化,正在重载 Amagi Client...");
|
|
10246
|
+
import("./amagiClient.js").then(({ reloadAmagiConfig }) => reloadAmagiConfig()).catch((error) => logger.error(`[karin-plugin-kkk][Config] 重载 Amagi Client 失败: ${error}`));
|
|
10230
10247
|
}
|
|
10231
|
-
})
|
|
10248
|
+
});
|
|
10232
10249
|
}, 2e3);
|
|
10233
10250
|
return this;
|
|
10234
10251
|
}
|
|
10252
|
+
/** 读取 JSON 配置 */
|
|
10253
|
+
getJson() {
|
|
10254
|
+
return JSON.parse(fs.readFileSync(this.jsonConfigPath, "utf8"));
|
|
10255
|
+
}
|
|
10256
|
+
/** 写入 JSON 配置 */
|
|
10257
|
+
setJson(config) {
|
|
10258
|
+
fs.writeFileSync(this.jsonConfigPath, JSON.stringify(config, null, 2), "utf8");
|
|
10259
|
+
}
|
|
10260
|
+
/** 深度合并 JSON 配置(保留用户值,补充默认值) */
|
|
10261
|
+
mergeJsonConfigs(def, user) {
|
|
10262
|
+
if (!def || typeof def !== "object" || Array.isArray(def)) return user ?? def;
|
|
10263
|
+
if (!user || typeof user !== "object" || Array.isArray(user)) return user ?? def;
|
|
10264
|
+
const result = { ...def };
|
|
10265
|
+
for (const key in user) if (typeof user[key] === "object" && !Array.isArray(user[key]) && user[key] !== null) result[key] = this.mergeJsonConfigs(def[key], user[key]);
|
|
10266
|
+
else result[key] = user[key];
|
|
10267
|
+
return result;
|
|
10268
|
+
}
|
|
10235
10269
|
/**
|
|
10236
|
-
*
|
|
10237
|
-
* @param name
|
|
10238
|
-
* @returns 返回合并后的配置
|
|
10270
|
+
* 获取配置
|
|
10271
|
+
* @param name 配置键名
|
|
10239
10272
|
*/
|
|
10240
10273
|
getDefOrConfig(name) {
|
|
10241
|
-
|
|
10242
|
-
const config = this.getYaml("config", name);
|
|
10243
|
-
return {
|
|
10244
|
-
...def,
|
|
10245
|
-
...config
|
|
10246
|
-
};
|
|
10274
|
+
return this.getJson()[name] || {};
|
|
10247
10275
|
}
|
|
10248
10276
|
/** 获取所有配置文件 */
|
|
10249
10277
|
async All() {
|
|
10250
10278
|
const { getDouyinDB, getBilibiliDB } = await import("./db.js");
|
|
10251
10279
|
const douyinDB = await getDouyinDB();
|
|
10252
10280
|
const bilibiliDB = await getBilibiliDB();
|
|
10253
|
-
const allConfig =
|
|
10254
|
-
const files = fs.readdirSync(this.defCfgPath);
|
|
10255
|
-
for (const file of files) {
|
|
10256
|
-
const fileName = path.basename(file, ".yaml");
|
|
10257
|
-
allConfig[fileName] = this.getDefOrConfig(fileName) || {};
|
|
10258
|
-
}
|
|
10281
|
+
const allConfig = this.getJson();
|
|
10259
10282
|
if (allConfig.pushlist) try {
|
|
10260
10283
|
if (allConfig.pushlist.douyin) for (const item of allConfig.pushlist.douyin) {
|
|
10261
10284
|
const filterWords = await douyinDB.getFilterWords(item.sec_uid);
|
|
@@ -10279,71 +10302,62 @@ var Cfg = class {
|
|
|
10279
10302
|
return allConfig;
|
|
10280
10303
|
}
|
|
10281
10304
|
/**
|
|
10282
|
-
*
|
|
10283
|
-
* @param type 配置文件类型
|
|
10284
|
-
* @param name 配置文件名
|
|
10285
|
-
* @returns 返回 YAML 文件内容
|
|
10286
|
-
*/
|
|
10287
|
-
getYaml(type, name) {
|
|
10288
|
-
return requireFileSync(type === "config" ? `${this.dirCfgPath}/${name}.yaml` : `${this.defCfgPath}/${name}.yaml`, { force: true });
|
|
10289
|
-
}
|
|
10290
|
-
/**
|
|
10291
|
-
* 修改配置文件
|
|
10292
|
-
* @param name 文件名
|
|
10293
|
-
* @param key 键
|
|
10294
|
-
* @param value 值
|
|
10295
|
-
* @param type 配置文件类型,默认为用户配置文件 `config`
|
|
10296
|
-
*/
|
|
10297
|
-
Modify(name, key, value, type = "config") {
|
|
10298
|
-
const path = type === "config" ? `${this.dirCfgPath}/${name}.yaml` : `${this.defCfgPath}/${name}.yaml`;
|
|
10299
|
-
const yamlData = YAML.parseDocument(fs.readFileSync(path, "utf8"));
|
|
10300
|
-
const keys = key.split(".");
|
|
10301
|
-
yamlData.setIn(keys, value);
|
|
10302
|
-
fs.writeFileSync(path, yamlData.toString({ lineWidth: -1 }), "utf8");
|
|
10303
|
-
}
|
|
10304
|
-
/**
|
|
10305
|
-
* 修改整个配置文件,保留注释
|
|
10305
|
+
* 修改整个配置文件
|
|
10306
10306
|
* @param name 文件名
|
|
10307
10307
|
* @param config 完整的配置对象
|
|
10308
|
-
* @param type 配置文件类型,默认为用户配置文件 `config`
|
|
10309
10308
|
*/
|
|
10310
10309
|
async ModifyPro(name, config, type = "config") {
|
|
10310
|
+
if (type !== "config") return false;
|
|
10311
10311
|
const { getDouyinDB, getBilibiliDB } = await import("./db.js");
|
|
10312
10312
|
const douyinDB = await getDouyinDB();
|
|
10313
10313
|
const bilibiliDB = await getBilibiliDB();
|
|
10314
|
-
const
|
|
10315
|
-
|
|
10316
|
-
|
|
10317
|
-
|
|
10318
|
-
|
|
10319
|
-
|
|
10320
|
-
const cleanedConfig = { ...config };
|
|
10321
|
-
if ("douyin" in cleanedConfig) cleanedConfig.douyin = cleanedConfig.douyin.map((item) => {
|
|
10322
|
-
const { Keywords, Tags, filterMode, ...rest } = item;
|
|
10323
|
-
return rest;
|
|
10324
|
-
});
|
|
10325
|
-
if ("bilibili" in cleanedConfig) cleanedConfig.bilibili = cleanedConfig.bilibili.map((item) => {
|
|
10326
|
-
const { Keywords, Tags, filterMode, ...rest } = item;
|
|
10327
|
-
return rest;
|
|
10328
|
-
});
|
|
10329
|
-
filterCfg = cleanedConfig;
|
|
10330
|
-
}
|
|
10331
|
-
const newConfigNode = YAML.parseDocument(YAML.stringify(filterCfg)).contents;
|
|
10332
|
-
this.deepMergeYaml(doc.contents, newConfigNode);
|
|
10333
|
-
fs.writeFileSync(filePath, doc.toString({ lineWidth: -1 }), "utf8");
|
|
10334
|
-
if ("douyin" in config) {
|
|
10335
|
-
await this.syncFilterConfigToDb(config.douyin, douyinDB, "sec_uid");
|
|
10336
|
-
logger.debug("已同步抖音过滤配置到数据库");
|
|
10337
|
-
}
|
|
10338
|
-
if ("bilibili" in config) {
|
|
10339
|
-
await this.syncFilterConfigToDb(config.bilibili, bilibiliDB, "host_mid");
|
|
10340
|
-
logger.debug("已同步B站过滤配置到数据库");
|
|
10341
|
-
}
|
|
10342
|
-
return true;
|
|
10343
|
-
} catch (error) {
|
|
10344
|
-
logger.error(`修改配置文件时发生错误:${error}`);
|
|
10345
|
-
return false;
|
|
10314
|
+
const jsonConfig = this.getJson();
|
|
10315
|
+
jsonConfig[name] = config;
|
|
10316
|
+
this.setJson(jsonConfig);
|
|
10317
|
+
if ("douyin" in config) {
|
|
10318
|
+
await this.syncFilterConfigToDb(config.douyin, douyinDB, "sec_uid");
|
|
10319
|
+
logger.debug("[karin-plugin-kkk][Config] 已同步抖音过滤配置到数据库");
|
|
10346
10320
|
}
|
|
10321
|
+
if ("bilibili" in config) {
|
|
10322
|
+
await this.syncFilterConfigToDb(config.bilibili, bilibiliDB, "host_mid");
|
|
10323
|
+
logger.debug("[karin-plugin-kkk][Config] 已同步B站过滤配置到数据库");
|
|
10324
|
+
}
|
|
10325
|
+
return true;
|
|
10326
|
+
}
|
|
10327
|
+
/**
|
|
10328
|
+
* 修改配置字段(支持深层嵌套路径,包括数组索引)
|
|
10329
|
+
* @param moduleName 模块名
|
|
10330
|
+
* @param path 字段路径,如 'push.switch' 或 'cookies.douyin' 或 'list[0].name'
|
|
10331
|
+
* @param value 新值
|
|
10332
|
+
*/
|
|
10333
|
+
async Modify(moduleName, path, value) {
|
|
10334
|
+
const jsonConfig = this.getJson();
|
|
10335
|
+
const pathKeys = this.parsePath(path);
|
|
10336
|
+
let target = jsonConfig[moduleName];
|
|
10337
|
+
for (let i = 0; i < pathKeys.length - 1; i++) {
|
|
10338
|
+
const key = pathKeys[i];
|
|
10339
|
+
if (!(key in target)) target[key] = {};
|
|
10340
|
+
target = target[key];
|
|
10341
|
+
}
|
|
10342
|
+
const lastKey = pathKeys[pathKeys.length - 1];
|
|
10343
|
+
target[lastKey] = value;
|
|
10344
|
+
this.setJson(jsonConfig);
|
|
10345
|
+
if (moduleName === "pushlist") {
|
|
10346
|
+
const { getDouyinDB, getBilibiliDB } = await import("./db.js");
|
|
10347
|
+
const douyinDB = await getDouyinDB();
|
|
10348
|
+
const bilibiliDB = await getBilibiliDB();
|
|
10349
|
+
if ("douyin" in jsonConfig[moduleName]) await this.syncFilterConfigToDb(jsonConfig[moduleName].douyin, douyinDB, "sec_uid");
|
|
10350
|
+
if ("bilibili" in jsonConfig[moduleName]) await this.syncFilterConfigToDb(jsonConfig[moduleName].bilibili, bilibiliDB, "host_mid");
|
|
10351
|
+
}
|
|
10352
|
+
return true;
|
|
10353
|
+
}
|
|
10354
|
+
/**
|
|
10355
|
+
* 解析路径字符串,支持点号和数组索引
|
|
10356
|
+
* 'a.b.c' => ['a', 'b', 'c']
|
|
10357
|
+
* 'a[0].b' => ['a', '0', 'b']
|
|
10358
|
+
*/
|
|
10359
|
+
parsePath(path) {
|
|
10360
|
+
return path.replace(/\[(\d+)\]/g, ".$1").split(".");
|
|
10347
10361
|
}
|
|
10348
10362
|
/**
|
|
10349
10363
|
* 同步过滤配置到数据库
|
|
@@ -10369,86 +10383,115 @@ var Cfg = class {
|
|
|
10369
10383
|
}
|
|
10370
10384
|
}
|
|
10371
10385
|
/**
|
|
10372
|
-
* 深度合并YAML节点(保留目标注释)
|
|
10373
|
-
* @param target 目标节点(保留注释的原始节点)
|
|
10374
|
-
* @param source 源节点(提供新值的节点)
|
|
10375
|
-
*/
|
|
10376
|
-
deepMergeYaml(target, source) {
|
|
10377
|
-
if (YAML.isMap(target) && YAML.isMap(source)) for (const pair of source.items) {
|
|
10378
|
-
const key = pair.key;
|
|
10379
|
-
const sourceVal = pair.value;
|
|
10380
|
-
const targetVal = target.get(key);
|
|
10381
|
-
if (targetVal === void 0) target.set(key, sourceVal);
|
|
10382
|
-
else if (YAML.isMap(targetVal) && YAML.isMap(sourceVal)) this.deepMergeYaml(targetVal, sourceVal);
|
|
10383
|
-
else if (YAML.isSeq(targetVal) && YAML.isSeq(sourceVal)) {
|
|
10384
|
-
targetVal.items = sourceVal.items;
|
|
10385
|
-
targetVal.flow = sourceVal.flow;
|
|
10386
|
-
} else target.set(key, sourceVal);
|
|
10387
|
-
}
|
|
10388
|
-
}
|
|
10389
|
-
mergeObjectsWithPriority(userDoc, defaultDoc) {
|
|
10390
|
-
let differences = false;
|
|
10391
|
-
/** 合并 YAML 对象,确保注释保留 */
|
|
10392
|
-
const mergeYamlNodes = (target, source) => {
|
|
10393
|
-
if (YAML.isMap(target) && YAML.isMap(source)) for (const pair of source.items) {
|
|
10394
|
-
const key = pair.key;
|
|
10395
|
-
const value = pair.value;
|
|
10396
|
-
const existing = target.get(key);
|
|
10397
|
-
if (existing === void 0) {
|
|
10398
|
-
differences = true;
|
|
10399
|
-
target.set(key, value);
|
|
10400
|
-
} else if (YAML.isMap(value) && YAML.isMap(existing)) mergeYamlNodes(existing, value);
|
|
10401
|
-
else if (existing !== value) {
|
|
10402
|
-
differences = true;
|
|
10403
|
-
target.set(key, value);
|
|
10404
|
-
}
|
|
10405
|
-
}
|
|
10406
|
-
};
|
|
10407
|
-
mergeYamlNodes(defaultDoc.contents, userDoc.contents);
|
|
10408
|
-
return {
|
|
10409
|
-
differences,
|
|
10410
|
-
result: defaultDoc
|
|
10411
|
-
};
|
|
10412
|
-
}
|
|
10413
|
-
/**
|
|
10414
10386
|
* 同步配置到数据库
|
|
10415
|
-
* 这个方法应该在所有模块都初始化完成后调用
|
|
10416
10387
|
*/
|
|
10417
10388
|
async syncConfigToDatabase() {
|
|
10418
10389
|
try {
|
|
10419
10390
|
const { getDouyinDB, getBilibiliDB } = await import("./db.js");
|
|
10420
10391
|
const douyinDB = await getDouyinDB();
|
|
10421
10392
|
const bilibiliDB = await getBilibiliDB();
|
|
10422
|
-
const pushCfg = this.
|
|
10423
|
-
if (pushCfg
|
|
10424
|
-
if (pushCfg
|
|
10425
|
-
logger.debug("[BilibiliDB] + [DouyinDB] 配置已同步到数据库");
|
|
10393
|
+
const pushCfg = this.getJson().pushlist;
|
|
10394
|
+
if (pushCfg?.bilibili) await bilibiliDB.syncConfigSubscriptions(pushCfg.bilibili);
|
|
10395
|
+
if (pushCfg?.douyin) await douyinDB.syncConfigSubscriptions(pushCfg.douyin);
|
|
10396
|
+
logger.debug("[karin-plugin-kkk][BilibiliDB] + [DouyinDB] 配置已同步到数据库");
|
|
10426
10397
|
} catch (error) {
|
|
10427
|
-
logger.error("同步配置到数据库失败:", error);
|
|
10398
|
+
logger.error("[karin-plugin-kkk]同步配置到数据库失败:", error);
|
|
10428
10399
|
}
|
|
10429
10400
|
}
|
|
10430
10401
|
};
|
|
10431
|
-
/**
|
|
10432
|
-
* 配置实例缓存
|
|
10433
|
-
*/
|
|
10434
10402
|
var configInstance = null;
|
|
10435
|
-
|
|
10436
|
-
* 获取配置实例(延迟初始化)
|
|
10437
|
-
* @returns 配置实例
|
|
10438
|
-
*/
|
|
10403
|
+
var migrationExecuted = false;
|
|
10439
10404
|
var getConfigInstance = () => {
|
|
10440
|
-
if (!configInstance)
|
|
10441
|
-
if (
|
|
10442
|
-
|
|
10443
|
-
|
|
10405
|
+
if (!configInstance) {
|
|
10406
|
+
if (!migrationExecuted) {
|
|
10407
|
+
migrateConfigFromYaml();
|
|
10408
|
+
migrationExecuted = true;
|
|
10409
|
+
}
|
|
10410
|
+
configInstance = new Proxy(new Cfg().initCfg(), { get(target, prop) {
|
|
10411
|
+
if (prop in target) return target[prop];
|
|
10412
|
+
return target.getDefOrConfig(prop);
|
|
10413
|
+
} });
|
|
10414
|
+
}
|
|
10444
10415
|
return configInstance;
|
|
10445
10416
|
};
|
|
10446
|
-
/**
|
|
10447
|
-
* 配置对象代理
|
|
10448
|
-
*/
|
|
10449
10417
|
var Config = new Proxy({}, { get(target, prop) {
|
|
10450
10418
|
return getConfigInstance()[prop];
|
|
10451
10419
|
} });
|
|
10420
|
+
/**
|
|
10421
|
+
* 从 YAML 迁移到 JSON
|
|
10422
|
+
*/
|
|
10423
|
+
var migrateConfigFromYaml = () => {
|
|
10424
|
+
const dirCfgPath = `${karinPathBase}/${Root.pluginName}/config`;
|
|
10425
|
+
const defCfgPath = `${Root.pluginPath}/config/default_config/`;
|
|
10426
|
+
const jsonConfigPath = `${dirCfgPath}/config.json`;
|
|
10427
|
+
if (fs.existsSync(jsonConfigPath)) return;
|
|
10428
|
+
const yamlFiles = [
|
|
10429
|
+
"app",
|
|
10430
|
+
"bilibili",
|
|
10431
|
+
"cookies",
|
|
10432
|
+
"douyin",
|
|
10433
|
+
"kuaishou",
|
|
10434
|
+
"pushlist",
|
|
10435
|
+
"request",
|
|
10436
|
+
"upload",
|
|
10437
|
+
"xiaohongshu"
|
|
10438
|
+
];
|
|
10439
|
+
if (!yamlFiles.some((name) => fs.existsSync(`${dirCfgPath}/${name}.yaml`))) return;
|
|
10440
|
+
try {
|
|
10441
|
+
const defYaml = {};
|
|
10442
|
+
for (const name of yamlFiles) {
|
|
10443
|
+
const file = `${defCfgPath}/${name}.yaml`;
|
|
10444
|
+
if (fs.existsSync(file)) defYaml[name] = YAML.parse(fs.readFileSync(file, "utf8"));
|
|
10445
|
+
}
|
|
10446
|
+
const userYaml = {};
|
|
10447
|
+
for (const name of yamlFiles) {
|
|
10448
|
+
const file = `${dirCfgPath}/${name}.yaml`;
|
|
10449
|
+
if (fs.existsSync(file)) userYaml[name] = filterKeys(YAML.parse(fs.readFileSync(file, "utf8")), defYaml[name]);
|
|
10450
|
+
}
|
|
10451
|
+
const jsonConfig = {
|
|
10452
|
+
amagi: {
|
|
10453
|
+
timeout: userYaml.request?.timeout ?? defYaml.request?.timeout,
|
|
10454
|
+
"User-Agent": userYaml.request?.["User-Agent"] ?? defYaml.request?.["User-Agent"],
|
|
10455
|
+
proxy: userYaml.request?.proxy ?? defYaml.request?.proxy,
|
|
10456
|
+
cookies: userYaml.cookies || defYaml.cookies || {},
|
|
10457
|
+
APIServer: userYaml.app?.APIServer ?? defYaml.app?.APIServer,
|
|
10458
|
+
APIServerMount: userYaml.app?.APIServerMount ?? defYaml.app?.APIServerMount,
|
|
10459
|
+
APIServerPort: userYaml.app?.APIServerPort ?? defYaml.app?.APIServerPort
|
|
10460
|
+
},
|
|
10461
|
+
app: {
|
|
10462
|
+
...defYaml.app,
|
|
10463
|
+
...defYaml.upload,
|
|
10464
|
+
...userYaml.app,
|
|
10465
|
+
...userYaml.upload
|
|
10466
|
+
},
|
|
10467
|
+
douyin: userYaml.douyin || defYaml.douyin,
|
|
10468
|
+
bilibili: userYaml.bilibili || defYaml.bilibili,
|
|
10469
|
+
kuaishou: userYaml.kuaishou || defYaml.kuaishou,
|
|
10470
|
+
xiaohongshu: userYaml.xiaohongshu || defYaml.xiaohongshu,
|
|
10471
|
+
pushlist: userYaml.pushlist || defYaml.pushlist
|
|
10472
|
+
};
|
|
10473
|
+
fs.writeFileSync(jsonConfigPath, JSON.stringify(jsonConfig, null, 2), "utf8");
|
|
10474
|
+
const backupDir = `${dirCfgPath}/yaml_backup_${Date.now()}`;
|
|
10475
|
+
fs.mkdirSync(backupDir, { recursive: true });
|
|
10476
|
+
for (const name of yamlFiles) {
|
|
10477
|
+
const file = `${dirCfgPath}/${name}.yaml`;
|
|
10478
|
+
if (fs.existsSync(file)) {
|
|
10479
|
+
fs.copyFileSync(file, `${backupDir}/${name}.yaml`);
|
|
10480
|
+
fs.unlinkSync(file);
|
|
10481
|
+
}
|
|
10482
|
+
}
|
|
10483
|
+
logger.info(`[karin-plugin-kkk][Config] YAML 配置已迁移到 config.json,备份保存在 ${backupDir}`);
|
|
10484
|
+
} catch (error) {
|
|
10485
|
+
logger.error(`[karin-plugin-kkk][Config] YAML 迁移失败: ${error}`);
|
|
10486
|
+
}
|
|
10487
|
+
};
|
|
10488
|
+
var filterKeys = (user, template) => {
|
|
10489
|
+
if (!template || typeof template !== "object") return user;
|
|
10490
|
+
if (Array.isArray(template)) return user;
|
|
10491
|
+
const result = {};
|
|
10492
|
+
for (const key in template) if (key in user) result[key] = typeof template[key] === "object" && !Array.isArray(template[key]) ? filterKeys(user[key], template[key]) : user[key];
|
|
10493
|
+
return result;
|
|
10494
|
+
};
|
|
10452
10495
|
//#endregion
|
|
10453
10496
|
//#region src/module/utils/amagiClient.ts
|
|
10454
10497
|
/**
|
|
@@ -10476,48 +10519,24 @@ var AmagiBase = class {
|
|
|
10476
10519
|
}
|
|
10477
10520
|
/** 创建解析库实例 */
|
|
10478
10521
|
createAmagiClient = () => {
|
|
10522
|
+
const amagi = Config.amagi;
|
|
10479
10523
|
return Client({
|
|
10480
|
-
cookies: {
|
|
10481
|
-
douyin: Config.cookies.douyin,
|
|
10482
|
-
bilibili: Config.cookies.bilibili,
|
|
10483
|
-
kuaishou: Config.cookies.kuaishou,
|
|
10484
|
-
xiaohongshu: Config.cookies.xiaohongshu
|
|
10485
|
-
},
|
|
10524
|
+
cookies: amagi.cookies || {},
|
|
10486
10525
|
request: {
|
|
10487
|
-
timeout:
|
|
10488
|
-
headers: { "User-Agent":
|
|
10489
|
-
proxy:
|
|
10526
|
+
timeout: amagi.timeout,
|
|
10527
|
+
headers: { "User-Agent": amagi["User-Agent"] },
|
|
10528
|
+
proxy: amagi.proxy?.switch ? amagi.proxy : false
|
|
10490
10529
|
}
|
|
10491
10530
|
});
|
|
10492
10531
|
};
|
|
10493
10532
|
/**
|
|
10494
10533
|
* 重载配置 - 重新创建 Amagi Client 实例
|
|
10495
|
-
* 当配置文件中的 cookies 或 request 配置更新后,调用此方法使新配置生效
|
|
10496
10534
|
*/
|
|
10497
10535
|
reloadConfig() {
|
|
10498
10536
|
logger.debug("[AmagiClient] 检测到配置变化,正在重载...");
|
|
10499
|
-
const oldCookies = {
|
|
10500
|
-
douyin: Config.cookies.douyin?.substring(0, 20) + "...",
|
|
10501
|
-
bilibili: Config.cookies.bilibili?.substring(0, 20) + "...",
|
|
10502
|
-
kuaishou: Config.cookies.kuaishou?.substring(0, 20) + "...",
|
|
10503
|
-
xiaohongshu: Config.cookies.xiaohongshu?.substring(0, 20) + "..."
|
|
10504
|
-
};
|
|
10505
10537
|
const client = this.createAmagiClient();
|
|
10506
10538
|
this.amagi = this.wrapAmagiClient(client);
|
|
10507
|
-
const newCookies = {
|
|
10508
|
-
douyin: Config.cookies.douyin?.substring(0, 20) + "...",
|
|
10509
|
-
bilibili: Config.cookies.bilibili?.substring(0, 20) + "...",
|
|
10510
|
-
kuaishou: Config.cookies.kuaishou?.substring(0, 20) + "...",
|
|
10511
|
-
xiaohongshu: Config.cookies.xiaohongshu?.substring(0, 20) + "..."
|
|
10512
|
-
};
|
|
10513
10539
|
logger.debug("[AmagiClient] 配置重载完成");
|
|
10514
|
-
logger.debug(`[AmagiClient] Cookie 变化对比:\n${util.inspect({
|
|
10515
|
-
旧配置: oldCookies,
|
|
10516
|
-
新配置: newCookies
|
|
10517
|
-
}, {
|
|
10518
|
-
colors: true,
|
|
10519
|
-
depth: 2
|
|
10520
|
-
})}`);
|
|
10521
10540
|
}
|
|
10522
10541
|
/** 包装解析库实例,递归代理所有嵌套对象的方法 */
|
|
10523
10542
|
wrapAmagiClient = (client) => {
|
|
@@ -10559,20 +10578,14 @@ var AmagiBase = class {
|
|
|
10559
10578
|
};
|
|
10560
10579
|
};
|
|
10561
10580
|
/**
|
|
10562
|
-
*
|
|
10563
|
-
* 在 softFetch 中配置后,对应接口调用不会抛出异常,而是原样返回 Result,
|
|
10564
|
-
* 由业务代码根据 code 决定后续处理逻辑。
|
|
10565
|
-
*
|
|
10581
|
+
* 软错误码常量
|
|
10566
10582
|
* Bilibili:
|
|
10567
10583
|
* 12061 - UP主已关闭评论区
|
|
10568
10584
|
*/
|
|
10569
10585
|
var SOFT_ERROR_CODES = { BILIBILI_COMMENTS_DISABLED: 12061 };
|
|
10570
10586
|
/**
|
|
10571
|
-
* 调用 amagi fetcher 方法,允许特定错误码不抛出异常而是以 Result
|
|
10572
|
-
*
|
|
10573
|
-
* 业务代码收到返回值后,通过判断 result.code 决定继续解析还是返回提示。
|
|
10574
|
-
*
|
|
10575
|
-
* @param fn - 经过代理包装的 amagi 方法调用
|
|
10587
|
+
* 调用 amagi fetcher 方法,允许特定错误码不抛出异常而是以 Result 形式返回
|
|
10588
|
+
* @param fn - 经过代理包装的 amagi 方法调用
|
|
10576
10589
|
* @param allowedCodes - 不应抛出异常的错误码列表
|
|
10577
10590
|
*/
|
|
10578
10591
|
var softFetch = async (fn, allowedCodes) => {
|
|
@@ -10589,37 +10602,13 @@ var softFetch = async (fn, allowedCodes) => {
|
|
|
10589
10602
|
throw err;
|
|
10590
10603
|
}
|
|
10591
10604
|
};
|
|
10592
|
-
/** 获取已初始化的解析库实例(单例) */
|
|
10593
|
-
var createLiveProxy = (getter) => {
|
|
10594
|
-
return new Proxy({}, { get(_target, prop) {
|
|
10595
|
-
const current = getter();
|
|
10596
|
-
const value = Reflect.get(current, prop);
|
|
10597
|
-
if (typeof value === "function") return value.bind(current);
|
|
10598
|
-
if (value && typeof value === "object") return createLiveProxy(() => Reflect.get(getter(), prop));
|
|
10599
|
-
return value;
|
|
10600
|
-
} });
|
|
10601
|
-
};
|
|
10602
10605
|
var amagiClientInstance = new AmagiBase();
|
|
10603
|
-
|
|
10604
|
-
var amagiClient = createLiveProxy(() => amagiClientInstance.amagi);
|
|
10605
|
-
/**
|
|
10606
|
-
* 重载 Amagi 配置
|
|
10607
|
-
* 当 cookies 或 request 配置更新后调用此方法,使新配置立即生效
|
|
10608
|
-
* @example
|
|
10609
|
-
* ```typescript
|
|
10610
|
-
* // 更新配置后
|
|
10611
|
-
* await Config.Modify('cookies', 'douyin', newCookie)
|
|
10612
|
-
* reloadAmagiConfig() // 重载配置
|
|
10613
|
-
* ```
|
|
10614
|
-
*/
|
|
10606
|
+
var amagiClient = amagiClientInstance.amagi;
|
|
10615
10607
|
var reloadAmagiConfig = () => {
|
|
10616
10608
|
amagiClientInstance.reloadConfig();
|
|
10617
10609
|
};
|
|
10618
|
-
/** B站 Fetcher 实例 */
|
|
10619
10610
|
var bilibiliFetcher = amagiClient.bilibili.fetcher;
|
|
10620
|
-
/** 抖音 Fetcher 实例 */
|
|
10621
10611
|
var douyinFetcher = amagiClient.douyin.fetcher;
|
|
10622
|
-
/** 快手 Fetcher 实例 */
|
|
10623
10612
|
var kuaishouFetcher = amagiClient.kuaishou.fetcher;
|
|
10624
10613
|
amagiClient.xiaohongshu.fetcher;
|
|
10625
10614
|
//#endregion
|
|
@@ -10646,7 +10635,7 @@ var Base = class extends AmagiBase {
|
|
|
10646
10635
|
/**
|
|
10647
10636
|
* 统计每个平台使用最多的机器人ID和使用次数
|
|
10648
10637
|
* @param pushList - 推送列表配置
|
|
10649
|
-
* @returns
|
|
10638
|
+
* @returns
|
|
10650
10639
|
*/
|
|
10651
10640
|
var statBotId$1 = (pushList) => {
|
|
10652
10641
|
const platformBotCount = {
|
|
@@ -10719,12 +10708,12 @@ var uploadFile = async (event, file, videoUrl, options) => {
|
|
|
10719
10708
|
selfId = event.selfId;
|
|
10720
10709
|
contact = event.contact;
|
|
10721
10710
|
}
|
|
10722
|
-
if (Config.
|
|
10711
|
+
if (Config.app.compress && file.totalBytes > Config.app.compresstrigger) {
|
|
10723
10712
|
const Duration = await getMediaDuration(file.filepath);
|
|
10724
|
-
logger.warn(logger.yellow(`视频大小 (${file.totalBytes} MB) 触发压缩条件(设定值:${Config.
|
|
10725
|
-
const message = [segment.text(`视频大小 (${file.totalBytes} MB) 触发压缩条件(设定值:${Config.
|
|
10713
|
+
logger.warn(logger.yellow(`视频大小 (${file.totalBytes} MB) 触发压缩条件(设定值:${Config.app.compresstrigger} MB),正在进行压缩至${Config.app.compressvalue} MB...`));
|
|
10714
|
+
const message = [segment.text(`视频大小 (${file.totalBytes} MB) 触发压缩条件(设定值:${Config.app.compresstrigger} MB),正在进行压缩至${Config.app.compressvalue} MB...`), options?.message_id ? segment.reply(options.message_id) : segment.text("")];
|
|
10726
10715
|
const msg1 = await karin$1.sendMsg(selfId, contact, message);
|
|
10727
|
-
const targetBitrate = Common.calculateBitrate(Config.
|
|
10716
|
+
const targetBitrate = Common.calculateBitrate(Config.app.compresstrigger, Duration) * .75;
|
|
10728
10717
|
const startTime = Date.now();
|
|
10729
10718
|
const outputPath = `${Common.tempDri.video}tmp_${Date.now()}.mp4`;
|
|
10730
10719
|
await compressVideo({
|
|
@@ -10739,8 +10728,8 @@ var uploadFile = async (event, file, videoUrl, options) => {
|
|
|
10739
10728
|
const message2 = [segment.text(`压缩后最终视频大小为: ${newFileSize.toFixed(1)} MB,压缩耗时:${((endTime - startTime) / 1e3).toFixed(1)} 秒`), segment.reply(msg1.messageId)];
|
|
10740
10729
|
await karin$1.sendMsg(selfId, contact, message2);
|
|
10741
10730
|
}
|
|
10742
|
-
if (options) options.useGroupFile = Config.
|
|
10743
|
-
if (Config.
|
|
10731
|
+
if (options) options.useGroupFile = Config.app.usegroupfile && newFileSize > Config.app.groupfilevalue;
|
|
10732
|
+
if (Config.app.videoSendMode === "base64" && !options?.useGroupFile) {
|
|
10744
10733
|
File = `base64://${fs.readFileSync(file.filepath).toString("base64")}`;
|
|
10745
10734
|
logger.mark(`已开启视频文件 base64转换 正在进行${logger.yellow("base64转换中")}...`);
|
|
10746
10735
|
} else File = options?.useGroupFile ? file.filepath : `file://${file.filepath}`;
|
|
@@ -10751,14 +10740,14 @@ var uploadFile = async (event, file, videoUrl, options) => {
|
|
|
10751
10740
|
await bot.uploadFile(contact, File, file.originTitle ? `${file.originTitle}.mp4` : `${File.split("/").pop()}`);
|
|
10752
10741
|
} else {
|
|
10753
10742
|
logger.mark(`${logger.blue("主动消息:")} 视频大小: ${newFileSize.toFixed(1)}MB 正在通过${logger.yellow("karin.sendMsg")}回复...`);
|
|
10754
|
-
(await karin$1.sendMsg(selfId, contact, [segment.video(File)])).messageId ?
|
|
10743
|
+
sendStatus = (await karin$1.sendMsg(selfId, contact, [segment.video(File)])).messageId ? true : false;
|
|
10755
10744
|
}
|
|
10756
10745
|
else if (options?.useGroupFile) {
|
|
10757
10746
|
logger.mark(`${logger.blue("被动消息:")} 视频大小: ${newFileSize.toFixed(1)}MB 正在通过${logger.yellow("e.bot.uploadFile")}回复...`);
|
|
10758
10747
|
await event.bot.uploadFile(event.contact, File, file.originTitle ? `${file.originTitle}.mp4` : `${File.split("/").pop()}`);
|
|
10759
10748
|
} else {
|
|
10760
10749
|
logger.mark(`${logger.blue("被动消息:")} 视频大小: ${newFileSize.toFixed(1)}MB 正在通过${logger.yellow("e.reply")}回复...`);
|
|
10761
|
-
(await event.reply(segment.video(File) || videoUrl)).messageId ?
|
|
10750
|
+
sendStatus = (await event.reply(segment.video(File) || videoUrl)).messageId ? true : false;
|
|
10762
10751
|
}
|
|
10763
10752
|
return sendStatus;
|
|
10764
10753
|
} catch (error) {
|
|
@@ -10768,8 +10757,8 @@ var uploadFile = async (event, file, videoUrl, options) => {
|
|
|
10768
10757
|
} finally {
|
|
10769
10758
|
const filePath = file.filepath;
|
|
10770
10759
|
Common.registerVideoPreview(filePath, Config.app.removeCache, 1800 * 1e3);
|
|
10771
|
-
logger.mark(`临时预览地址:http://localhost:${process.env.HTTP_PORT}/
|
|
10772
|
-
Config.app.removeCache
|
|
10760
|
+
logger.mark(`临时预览地址:http://localhost:${process.env.HTTP_PORT}/kkk/ssr/video/${encodeURIComponent(filePath.split("/").pop() ?? "")}`);
|
|
10761
|
+
if (Config.app.removeCache) logger.info(`文件 ${filePath} 将在 30 分钟后删除`);
|
|
10773
10762
|
setTimeout(async () => {
|
|
10774
10763
|
if (await Common.removeFile(filePath)) Common.markVideoPreviewRemoved(filePath);
|
|
10775
10764
|
}, 1800 * 1e3);
|
|
@@ -10789,8 +10778,8 @@ var downloadVideo = async (event, downloadOpt, uploadOpt) => {
|
|
|
10789
10778
|
}).getHeaders());
|
|
10790
10779
|
const fileSizeInMB = (fileSizeContent / (1024 * 1024)).toFixed(2);
|
|
10791
10780
|
const fileSize = parseInt(parseFloat(fileSizeInMB).toFixed(2));
|
|
10792
|
-
if (fileSizeContent > 0 && Config.
|
|
10793
|
-
const message = segment.text(`视频:「${downloadOpt.title.originTitle ?? "Error: 文件名获取失败"}」大小 (${fileSizeInMB} MB) 超出最大限制(设定值:${Config.
|
|
10781
|
+
if (fileSizeContent > 0 && Config.app.usefilelimit && fileSize > Config.app.filelimit) {
|
|
10782
|
+
const message = segment.text(`视频:「${downloadOpt.title.originTitle ?? "Error: 文件名获取失败"}」大小 (${fileSizeInMB} MB) 超出最大限制(设定值:${Config.app.filelimit} MB),已取消上传`);
|
|
10794
10783
|
const selfId = event.selfId || uploadOpt?.activeOption?.uin;
|
|
10795
10784
|
const contact = event.contact || karin$1.contactGroup(uploadOpt?.activeOption?.group_id) || karin$1.contactFriend(selfId);
|
|
10796
10785
|
await karin$1.sendMsg(selfId, contact, message);
|
|
@@ -10816,7 +10805,7 @@ var downloadVideo = async (event, downloadOpt, uploadOpt) => {
|
|
|
10816
10805
|
*/
|
|
10817
10806
|
var downloadFile = async (videoUrl, opt) => {
|
|
10818
10807
|
const startTime = Date.now();
|
|
10819
|
-
const uploadConfig = Config.
|
|
10808
|
+
const uploadConfig = Config.app;
|
|
10820
10809
|
const throttleConfig = {
|
|
10821
10810
|
enabled: uploadConfig.downloadThrottle ?? false,
|
|
10822
10811
|
maxSpeed: (uploadConfig.downloadMaxSpeed ?? 10) * 1024 * 1024,
|
|
@@ -11583,7 +11572,7 @@ var buildGoogleMotionPhoto = async (options) => {
|
|
|
11583
11572
|
};
|
|
11584
11573
|
//#endregion
|
|
11585
11574
|
//#region src/module/utils/FFmpeg.ts
|
|
11586
|
-
/**
|
|
11575
|
+
/**
|
|
11587
11576
|
* 修复 m4s 文件为标准 MP4 格式
|
|
11588
11577
|
* B站的 DASH 流使用 m4s 格式,缺少 moov atom,需要转换
|
|
11589
11578
|
*/
|
|
@@ -11980,7 +11969,7 @@ var extractTotalBytesFromHeaders = (headers) => {
|
|
|
11980
11969
|
* @returns 安全的文件名
|
|
11981
11970
|
*/
|
|
11982
11971
|
var sanitizeFilename = (filename) => {
|
|
11983
|
-
return filename.replace(/[<>:"/\\|?*\
|
|
11972
|
+
return filename.replace(/[<>:"/\\|?*\p{Cc}]/gu, "_").replace(/^\.+/, "").replace(/\.+$/, "").replace(/\s+/g, "_").substring(0, 200);
|
|
11984
11973
|
};
|
|
11985
11974
|
//#endregion
|
|
11986
11975
|
//#region src/module/utils/Network/ThrottleStream.ts
|
|
@@ -12341,7 +12330,7 @@ var ImageDownloader = class {
|
|
|
12341
12330
|
* @returns 处理后的图片路径(HTTP URL / file:// 协议 / base64://)
|
|
12342
12331
|
*/
|
|
12343
12332
|
async processImage(imageUrl, title, index) {
|
|
12344
|
-
switch (Config.
|
|
12333
|
+
switch (Config.app.imageSendMode) {
|
|
12345
12334
|
case "base64": try {
|
|
12346
12335
|
return await this.downloadAndConvertToBase64(imageUrl);
|
|
12347
12336
|
} catch (error) {
|
|
@@ -12766,18 +12755,18 @@ function getImageDownloader() {
|
|
|
12766
12755
|
/**
|
|
12767
12756
|
* 处理图片 URL,根据配置决定是否本地下载
|
|
12768
12757
|
* 这是一个便捷函数,用于快速处理单个图片
|
|
12769
|
-
*
|
|
12758
|
+
*
|
|
12770
12759
|
* @param imageUrl - 图片 URL
|
|
12771
12760
|
* @param title - 作品标题(用于文件命名)
|
|
12772
12761
|
* @param index - 图片索引(用于多图场景)
|
|
12773
12762
|
* @returns 处理后的图片路径(可能是 file:// 协议或原始 HTTP URL)
|
|
12774
|
-
*
|
|
12763
|
+
*
|
|
12775
12764
|
* @example
|
|
12776
12765
|
* ```ts
|
|
12777
12766
|
* // 单张图片
|
|
12778
12767
|
* const imagePath = await processImageUrl('https://example.com/image.jpg', '作品标题')
|
|
12779
12768
|
* await e.reply(segment.image(imagePath))
|
|
12780
|
-
*
|
|
12769
|
+
*
|
|
12781
12770
|
* // 多张图片
|
|
12782
12771
|
* const imagePaths = await Promise.all(
|
|
12783
12772
|
* imageUrls.map((url, i) => processImageUrl(url, '作品标题', i))
|
|
@@ -12791,7 +12780,7 @@ async function processImageUrl(imageUrl, title, index) {
|
|
|
12791
12780
|
//#region ../template/src/dev/preview/utils/time.ts
|
|
12792
12781
|
var import_react = /* @__PURE__ */ __toESM(require_react(), 1);
|
|
12793
12782
|
var import_server_node = require_server_node();
|
|
12794
|
-
var formatDuration$
|
|
12783
|
+
var formatDuration$3 = (ms) => {
|
|
12795
12784
|
const totalSeconds = Math.max(Math.floor(ms / 1e3), 0);
|
|
12796
12785
|
const hours = Math.floor(totalSeconds / 3600);
|
|
12797
12786
|
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
@@ -12815,7 +12804,7 @@ var buildStatus = (state) => {
|
|
|
12815
12804
|
statusText: `文件 ${filePath} 将在未知时间后删除`,
|
|
12816
12805
|
countdownText: "--:--"
|
|
12817
12806
|
};
|
|
12818
|
-
const countdownText = formatDuration$
|
|
12807
|
+
const countdownText = formatDuration$3(state.remainingMs);
|
|
12819
12808
|
return {
|
|
12820
12809
|
statusText: `文件 ${filePath} 将在 ${countdownText} 后删除`,
|
|
12821
12810
|
countdownText
|
|
@@ -17345,7 +17334,7 @@ var OriginalAVContent = ({ content }) => {
|
|
|
17345
17334
|
children: content.duration_text
|
|
17346
17335
|
}),
|
|
17347
17336
|
content.play,
|
|
17348
|
-
"观看
|
|
17337
|
+
"观看 ",
|
|
17349
17338
|
content.danmaku,
|
|
17350
17339
|
"弹幕"
|
|
17351
17340
|
]
|
|
@@ -19043,6 +19032,69 @@ var BilibiliVideoInfo = import_react.memo((props) => {
|
|
|
19043
19032
|
});
|
|
19044
19033
|
BilibiliVideoInfo.displayName = "BilibiliVideoInfo";
|
|
19045
19034
|
//#endregion
|
|
19035
|
+
//#region ../template/src/components/platforms/douyin/Icons.tsx
|
|
19036
|
+
var svgProps = (size, { weight: _weight, ...props }) => ({
|
|
19037
|
+
width: size,
|
|
19038
|
+
height: size,
|
|
19039
|
+
fill: "none",
|
|
19040
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
19041
|
+
...props
|
|
19042
|
+
});
|
|
19043
|
+
/** 抖音官方点赞图标,提取自 packages/template/icon.html。 */
|
|
19044
|
+
var DouyinLikeIcon = ({ size = 36, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
|
|
19045
|
+
viewBox: "0 0 891 816",
|
|
19046
|
+
...svgProps(size, props),
|
|
19047
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
|
|
19048
|
+
fill: "currentColor",
|
|
19049
|
+
d: "M453.036 88.712C493.774 30.664 560.66 0 634.251 0 774.403 0 890.972 121.59 890.972 266.137v.054c.01-.02.019-.04.028-.06 0 4.121-.07 7.241-.122 9.537-.072 3.151-.108 4.747.122 5.247-.531 30.403-5.778 55.522-15.101 88.712-5.289 5.96-10.204 17.184-15.1 29.572-7.724 11.998-10.647 17.81-15.101 29.57a546.67 546.67 0 0 1-14.452 22.974c-37.449 56.8-87.537 113.33-137.579 163.509-78.331 79.025-158.123 144.805-192.835 173.422-9.585 7.901-15.732 12.969-17.464 14.7-12.301 12.303-24.603 12.611-36.905 12.619-.324.004-.649.006-.977.006-25.236 0-37.854-12.619-50.472-25.237-.963-.963-3.413-2.984-7.156-5.979-38.233-28.184-124.273-96.997-205.116-180C121.066 542.02 61.622 470.007 29.092 399.588 16.474 374.351.731 314.264 0 280.922c.269-.267.227-1.873.144-5.078C.083 273.498 0 270.297 0 266.137 0 121.524 116.502 0 256.721 0c73.458 0 140.405 30.664 196.315 88.712Z"
|
|
19050
|
+
})
|
|
19051
|
+
});
|
|
19052
|
+
/** 抖音官方评论图标,提取自 packages/template/icon.html。 */
|
|
19053
|
+
var DouyinCommentIcon = ({ size = 36, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
|
|
19054
|
+
viewBox: "-24.75 -11.25 22.5 22.31",
|
|
19055
|
+
...svgProps(size, props),
|
|
19056
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
|
|
19057
|
+
fill: "currentColor",
|
|
19058
|
+
d: " M-4.644999980926514,4.482999801635742 C-7.25,7.818999767303467 -13.482000350952148,8.300000190734863 -13.482000350952148,8.300000190734863 C-13.482000350952148,8.300000190734863 -14.413999557495117,11.48799991607666 -12.135000228881836,10.821000099182129 C-9.855999946594238,10.154000282287598 -7.25,8.152000427246094 -4.644999980926514,4.482999801635742z M-17.36,-1.04 C-17.36,-0.16 -18.07,0.56 -18.96,0.56 C-18.96,0.56 -18.96,0.56 -18.96,0.56 C-19.85,0.56 -20.57,-0.16 -20.57,-1.04 C-20.57,-1.92 -19.85,-2.64 -18.96,-2.64 C-18.07,-2.64 -17.36,-1.92 -17.36,-1.04z M-11.89,-1.04 C-11.89,-0.16 -12.61,0.56 -13.5,0.56 C-13.5,0.56 -13.5,0.56 -13.5,0.56 C-14.39,0.56 -15.11,-0.16 -15.11,-1.04 C-15.11,-1.92 -14.39,-2.64 -13.5,-2.64 C-12.61,-2.64 -11.89,-1.92 -11.89,-1.04z M-6.43,-1.04 C-6.43,-0.16 -7.15,0.56 -8.04,0.56 C-8.04,0.56 -8.04,0.56 -8.04,0.56 C-8.92,0.56 -9.64,-0.16 -9.64,-1.04 C-9.64,-1.92 -8.92,-2.64 -8.04,-2.64 C-7.15,-2.64 -6.43,-1.92 -6.43,-1.04z M-5.79,5.98 C-3.56,3.75 -2.25,1.42 -2.25,-1.29 C-2.25,-6.79 -7.29,-11.25 -13.5,-11.25 C-19.71,-11.25 -24.75,-6.79 -24.75,-1.29 C-24.75,4.21 -19.55,7.99 -13.34,7.99 C-13.34,7.99 -13.34,11.06 -13.34,11.06 C-13.34,11.06 -8.51,8.72 -5.79,5.98z"
|
|
19059
|
+
})
|
|
19060
|
+
});
|
|
19061
|
+
/** 抖音官方收藏图标,提取自 packages/template/icon.html。 */
|
|
19062
|
+
var DouyinFavoriteIcon = ({ size = 36, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
|
|
19063
|
+
viewBox: "-2.285 -1.439 104.57 99.703",
|
|
19064
|
+
...svgProps(size, props),
|
|
19065
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
|
|
19066
|
+
fill: "currentColor",
|
|
19067
|
+
d: "M43.79199981689453,4.316999912261963 C45.93899917602539,-1.4390000104904175 54.06100082397461,-1.4390000104904175 56.20800018310547,4.316999912261963 C56.20800018310547,4.316999912261963 65.927001953125,30.375 65.927001953125,30.375 C65.927001953125,30.375 93.6510009765625,31.59000015258789 93.6510009765625,31.59000015258789 C99.7750015258789,31.857999801635742 102.28500366210938,39.60200119018555 97.48799896240234,43.428001403808594 C97.48799896240234,43.428001403808594 75.77100372314453,60.74700164794922 75.77100372314453,60.74700164794922 C75.77100372314453,60.74700164794922 83.18599700927734,87.55599975585938 83.18599700927734,87.55599975585938 C84.8239974975586,93.47799682617188 78.25299835205078,98.26399993896484 73.14099884033203,94.87200164794922 C73.14099884033203,94.87200164794922 50,79.51799774169922 50,79.51799774169922 C50,79.51799774169922 26.858999252319336,94.87200164794922 26.858999252319336,94.87200164794922 C21.746999740600586,98.26399993896484 15.175999641418457,93.47799682617188 16.81399917602539,87.55599975585938 C16.81399917602539,87.55599975585938 24.229000091552734,60.74700164794922 24.229000091552734,60.74700164794922 C24.229000091552734,60.74700164794922 2.51200008392334,43.428001403808594 2.51200008392334,43.428001403808594 C-2.2850000858306885,39.60200119018555 0.22499999403953552,31.857999801635742 6.348999977111816,31.59000015258789 C6.348999977111816,31.59000015258789 34.073001861572266,30.375 34.073001861572266,30.375 C34.073001861572266,30.375 43.79199981689453,4.316999912261963 43.79199981689453,4.316999912261963z"
|
|
19068
|
+
})
|
|
19069
|
+
});
|
|
19070
|
+
/** 抖音官方分享图标,提取自 packages/template/icon.html。 */
|
|
19071
|
+
var DouyinShareIcon = ({ size = 36, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
|
|
19072
|
+
viewBox: "-13.517 -10.558 25.734 22.375",
|
|
19073
|
+
...svgProps(size, props),
|
|
19074
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
|
|
19075
|
+
fill: "currentColor",
|
|
19076
|
+
d: "M.815 4.762V-4.93s-6.033-.564-10.06 3.755c-4.027 4.319-4.272 12.992-1.597 8.919C-8.062 3.511.815 4.762.815 4.762Zm9.699-1.767c1.655-1.461 1.703-3.887.107-5.413L3.706-9.032C2.11-10.558.815-10.264.815-8.375v3.421c.009 6.839 0 6.039 0 6.039 0 2.208 0 5.452 0 7.24 0 1.788 1.343 2.053 2.998.591l6.701-5.921Z"
|
|
19077
|
+
})
|
|
19078
|
+
});
|
|
19079
|
+
/** 抖音官方推荐图标。 */
|
|
19080
|
+
var DouyinRecommendIcon = ({ size = 36, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
|
|
19081
|
+
viewBox: "0 0 27 27",
|
|
19082
|
+
...svgProps(size, props),
|
|
19083
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
|
|
19084
|
+
fill: "currentColor",
|
|
19085
|
+
d: "M18.544 22.86H8.42V9.697l5.821-5.821a1.79 1.79 0 0 1 2.531 0c.118.118.215.278.291.48.076.203.114.397.114.583v.354l-1.114 4.404h5.518c.54 0 1.012.203 1.417.608.405.405.608.877.608 1.417v2.025a2.059 2.059 0 0 1-.152.759l-3.037 7.137c-.152.338-.405.625-.76.861a1.978 1.978 0 0 1-1.113.354ZM6.79 9.697V22.86H4.366a1.625 1.625 0 0 1-1.625-1.625v-9.91c0-.898.728-1.626 1.625-1.626H6.79Z"
|
|
19086
|
+
})
|
|
19087
|
+
});
|
|
19088
|
+
/** 抖音官方更多图标,提取自 packages/template/icon.html。 */
|
|
19089
|
+
var DouyinMoreIcon = ({ size = 36, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
|
|
19090
|
+
viewBox: "0 0 36 36",
|
|
19091
|
+
...svgProps(size, props),
|
|
19092
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
|
|
19093
|
+
fill: "currentColor",
|
|
19094
|
+
d: "M13.556 17.778a1.778 1.778 0 1 1-3.556 0 1.778 1.778 0 0 1 3.556 0Zm6.222 0a1.778 1.778 0 1 1-3.556 0 1.778 1.778 0 0 1 3.556 0Zm4.444 1.778a1.778 1.778 0 1 0 0-3.556 1.778 1.778 0 0 0 0 3.556Z"
|
|
19095
|
+
})
|
|
19096
|
+
});
|
|
19097
|
+
//#endregion
|
|
19046
19098
|
//#region ../template/src/components/platforms/douyin/ArticleWork.tsx
|
|
19047
19099
|
/**
|
|
19048
19100
|
* 抖音Logo头部组件
|
|
@@ -19105,7 +19157,6 @@ var ContentSection = ({ markdown, images }) => {
|
|
|
19105
19157
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
19106
19158
|
className: "prose prose-lg max-w-none text-foreground select-text",
|
|
19107
19159
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Markdown, {
|
|
19108
|
-
children: markdown.replace(/!\[([^\]]*)\]\(([^\s)]+)(?:\s+width=\d+)?(?:\s+height=\d+)?\)/g, ""),
|
|
19109
19160
|
components: {
|
|
19110
19161
|
h1: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
|
|
19111
19162
|
className: "text-8xl font-bold mb-8 mt-12 text-foreground",
|
|
@@ -19168,7 +19219,8 @@ var ContentSection = ({ markdown, images }) => {
|
|
|
19168
19219
|
className: "bg-surface px-3 py-1 rounded text-5xl text-accent font-mono",
|
|
19169
19220
|
children
|
|
19170
19221
|
})
|
|
19171
|
-
}
|
|
19222
|
+
},
|
|
19223
|
+
children: markdown.replace(/!\[([^\]]*)\]\(([^\s)]+)(?:\s+width=\d+)?(?:\s+height=\d+)?\)/g, "")
|
|
19172
19224
|
})
|
|
19173
19225
|
})
|
|
19174
19226
|
});
|
|
@@ -19184,22 +19236,22 @@ var InfoSection$2 = (props) => {
|
|
|
19184
19236
|
children: [
|
|
19185
19237
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
19186
19238
|
className: "flex gap-2 items-center",
|
|
19187
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
19239
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 44 }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.dianzan, "点赞"] })]
|
|
19188
19240
|
}),
|
|
19189
19241
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "·" }),
|
|
19190
19242
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
19191
19243
|
className: "flex gap-2 items-center",
|
|
19192
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
19244
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinCommentIcon, { size: 44 }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.pinglun, "评论"] })]
|
|
19193
19245
|
}),
|
|
19194
19246
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "·" }),
|
|
19195
19247
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
19196
19248
|
className: "flex gap-2 items-center",
|
|
19197
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
19249
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinFavoriteIcon, { size: 44 }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.shouchang, "收藏"] })]
|
|
19198
19250
|
}),
|
|
19199
19251
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "·" }),
|
|
19200
19252
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
19201
19253
|
className: "flex gap-2 items-center",
|
|
19202
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
19254
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinShareIcon, { size: 44 }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.share, "分享"] })]
|
|
19203
19255
|
})
|
|
19204
19256
|
]
|
|
19205
19257
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
@@ -19247,7 +19299,7 @@ var UserInfoSection$1 = (props) => {
|
|
|
19247
19299
|
children: [
|
|
19248
19300
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
19249
19301
|
className: "flex gap-1 items-center",
|
|
19250
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
19302
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 28 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
19251
19303
|
className: "text-muted",
|
|
19252
19304
|
children: "获赞"
|
|
19253
19305
|
})]
|
|
@@ -19433,28 +19485,28 @@ var VideoInfoHeader = (props) => {
|
|
|
19433
19485
|
children: [
|
|
19434
19486
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
19435
19487
|
className: "flex items-center gap-4 text-foreground/50",
|
|
19436
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
19488
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 48 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
19437
19489
|
className: "text-5xl font-medium text-foreground/90",
|
|
19438
19490
|
children: formatDouyinCommentDiggCount(props.Statistics.digg_count)
|
|
19439
19491
|
})]
|
|
19440
19492
|
}),
|
|
19441
19493
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
19442
19494
|
className: "flex items-center gap-4 text-foreground/50",
|
|
19443
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
19495
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinCommentIcon, { size: 48 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
19444
19496
|
className: "text-5xl font-medium text-foreground/90",
|
|
19445
19497
|
children: formatDouyinCommentDiggCount(props.Statistics.comment_count)
|
|
19446
19498
|
})]
|
|
19447
19499
|
}),
|
|
19448
19500
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
19449
19501
|
className: "flex items-center gap-4 text-foreground/50",
|
|
19450
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
19502
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinShareIcon, { size: 48 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
19451
19503
|
className: "text-5xl font-medium text-foreground/90",
|
|
19452
19504
|
children: formatDouyinCommentDiggCount(props.Statistics.share_count)
|
|
19453
19505
|
})]
|
|
19454
19506
|
}),
|
|
19455
19507
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
19456
19508
|
className: "flex items-center gap-4 text-foreground/50",
|
|
19457
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
19509
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinFavoriteIcon, { size: 48 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
19458
19510
|
className: "text-5xl font-medium text-foreground/90",
|
|
19459
19511
|
children: formatDouyinCommentDiggCount(props.Statistics.collect_count)
|
|
19460
19512
|
})]
|
|
@@ -19594,7 +19646,7 @@ var ReplyItemComponent = ({ reply, depth = 0, isLast, maxDepth = 6 }) => {
|
|
|
19594
19646
|
className: "flex items-center h-12.5",
|
|
19595
19647
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
19596
19648
|
className: "flex items-center text-muted",
|
|
19597
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
19649
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinMoreIcon, {
|
|
19598
19650
|
size: 45,
|
|
19599
19651
|
className: "mr-5"
|
|
19600
19652
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
@@ -19700,10 +19752,7 @@ var ReplyItemComponent = ({ reply, depth = 0, isLast, maxDepth = 6 }) => {
|
|
|
19700
19752
|
}),
|
|
19701
19753
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
19702
19754
|
className: "flex gap-2 items-center",
|
|
19703
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
19704
|
-
size: 40,
|
|
19705
|
-
className: "text-muted"
|
|
19706
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
19755
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 40 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
19707
19756
|
className: "text-4xl select-text",
|
|
19708
19757
|
children: formatDouyinCommentDiggCount(reply.digg_count)
|
|
19709
19758
|
})]
|
|
@@ -19807,10 +19856,7 @@ var CommentItemComponent$1 = (props) => {
|
|
|
19807
19856
|
}),
|
|
19808
19857
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
19809
19858
|
className: "flex gap-2 items-center transition-colors cursor-pointer",
|
|
19810
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
19811
|
-
size: 44,
|
|
19812
|
-
className: "text-muted"
|
|
19813
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
19859
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 44 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
19814
19860
|
className: "text-4xl select-text",
|
|
19815
19861
|
children: formatDouyinCommentDiggCount(props.digg_count)
|
|
19816
19862
|
})]
|
|
@@ -19952,22 +19998,22 @@ var InfoSection$1 = (props) => {
|
|
|
19952
19998
|
children: [
|
|
19953
19999
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
19954
20000
|
className: "flex gap-2 items-center",
|
|
19955
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
20001
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 44 }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.dianzan, "点赞"] })]
|
|
19956
20002
|
}),
|
|
19957
20003
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "·" }),
|
|
19958
20004
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
19959
20005
|
className: "flex gap-2 items-center",
|
|
19960
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
20006
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinCommentIcon, { size: 44 }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.pinglun, "评论"] })]
|
|
19961
20007
|
}),
|
|
19962
20008
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "·" }),
|
|
19963
20009
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
19964
20010
|
className: "flex gap-2 items-center",
|
|
19965
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
20011
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinFavoriteIcon, { size: 44 }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.shouchang, "收藏"] })]
|
|
19966
20012
|
}),
|
|
19967
20013
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "·" }),
|
|
19968
20014
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
19969
20015
|
className: "flex gap-2 items-center",
|
|
19970
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
20016
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinShareIcon, { size: 44 }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.share, "分享"] })]
|
|
19971
20017
|
})
|
|
19972
20018
|
]
|
|
19973
20019
|
}),
|
|
@@ -20026,10 +20072,7 @@ var UserInfoSection = (props) => {
|
|
|
20026
20072
|
children: [
|
|
20027
20073
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
20028
20074
|
className: "flex gap-1 items-center",
|
|
20029
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
20030
|
-
size: 28,
|
|
20031
|
-
className: "text-like"
|
|
20032
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
20075
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 28 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
20033
20076
|
className: "text-muted",
|
|
20034
20077
|
children: "获赞"
|
|
20035
20078
|
})]
|
|
@@ -20298,10 +20341,7 @@ var DouyinFavoriteList = (props) => {
|
|
|
20298
20341
|
}),
|
|
20299
20342
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
20300
20343
|
className: "absolute -bottom-3 -right-3 bg-rose-500 text-white px-5 py-2 rounded-full border-[5px] border-white font-bold text-xl shadow-xl flex items-center gap-2",
|
|
20301
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
20302
|
-
size: 20,
|
|
20303
|
-
className: "fill-current"
|
|
20304
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "赞了" })]
|
|
20344
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 20 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "赞了" })]
|
|
20305
20345
|
})
|
|
20306
20346
|
]
|
|
20307
20347
|
}),
|
|
@@ -20325,10 +20365,7 @@ var DouyinFavoriteList = (props) => {
|
|
|
20325
20365
|
className: "mt-6 flex flex-col items-center gap-2 text-rose-500",
|
|
20326
20366
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
20327
20367
|
className: "flex items-center gap-3",
|
|
20328
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
20329
|
-
size: 40,
|
|
20330
|
-
className: "fill-current"
|
|
20331
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
20368
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 40 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
20332
20369
|
className: "text-4xl font-bold",
|
|
20333
20370
|
children: "刚刚赞了这个作品"
|
|
20334
20371
|
})]
|
|
@@ -20435,24 +20472,24 @@ var DouyinFavoriteList = (props) => {
|
|
|
20435
20472
|
className: "grid grid-cols-5 gap-6 z-10",
|
|
20436
20473
|
children: [
|
|
20437
20474
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$3, {
|
|
20438
|
-
icon:
|
|
20475
|
+
icon: DouyinLikeIcon,
|
|
20439
20476
|
value: props.data.dianzan,
|
|
20440
|
-
|
|
20477
|
+
iconColor: "#FE2C55"
|
|
20441
20478
|
}),
|
|
20442
20479
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$3, {
|
|
20443
|
-
icon:
|
|
20480
|
+
icon: DouyinRecommendIcon,
|
|
20444
20481
|
value: props.data.tuijian
|
|
20445
20482
|
}),
|
|
20446
20483
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$3, {
|
|
20447
|
-
icon:
|
|
20484
|
+
icon: DouyinCommentIcon,
|
|
20448
20485
|
value: props.data.pinglun
|
|
20449
20486
|
}),
|
|
20450
20487
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$3, {
|
|
20451
|
-
icon:
|
|
20488
|
+
icon: DouyinFavoriteIcon,
|
|
20452
20489
|
value: props.data.shouchang
|
|
20453
20490
|
}),
|
|
20454
20491
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$3, {
|
|
20455
|
-
icon:
|
|
20492
|
+
icon: DouyinShareIcon,
|
|
20456
20493
|
value: props.data.share
|
|
20457
20494
|
})
|
|
20458
20495
|
]
|
|
@@ -20465,11 +20502,12 @@ var DouyinFavoriteList = (props) => {
|
|
|
20465
20502
|
});
|
|
20466
20503
|
};
|
|
20467
20504
|
/** 统计项小组件 */
|
|
20468
|
-
var StatItem$3 = ({ icon: IconComponent, value,
|
|
20505
|
+
var StatItem$3 = ({ icon: IconComponent, value, iconColor }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
20469
20506
|
className: "flex flex-col items-center justify-center gap-2 py-6 rounded-3xl bg-surface",
|
|
20470
20507
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconComponent, {
|
|
20471
20508
|
size: 48,
|
|
20472
|
-
|
|
20509
|
+
color: iconColor,
|
|
20510
|
+
className: "opacity-90"
|
|
20473
20511
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
20474
20512
|
className: "text-2xl font-bold text-foreground/70 mt-1",
|
|
20475
20513
|
children: value
|
|
@@ -20738,22 +20776,22 @@ var DouyinDynamicStatus$1 = (props) => {
|
|
|
20738
20776
|
className: "flex gap-6 items-center text-5xl tracking-normal select-text text-foreground/70",
|
|
20739
20777
|
children: [
|
|
20740
20778
|
{
|
|
20741
|
-
icon:
|
|
20779
|
+
icon: DouyinLikeIcon,
|
|
20742
20780
|
value: dianzan,
|
|
20743
20781
|
label: "点赞"
|
|
20744
20782
|
},
|
|
20745
20783
|
{
|
|
20746
|
-
icon:
|
|
20784
|
+
icon: DouyinCommentIcon,
|
|
20747
20785
|
value: pinglun,
|
|
20748
20786
|
label: "评论"
|
|
20749
20787
|
},
|
|
20750
20788
|
{
|
|
20751
|
-
icon:
|
|
20789
|
+
icon: DouyinFavoriteIcon,
|
|
20752
20790
|
value: shouchang,
|
|
20753
20791
|
label: "收藏"
|
|
20754
20792
|
},
|
|
20755
20793
|
{
|
|
20756
|
-
icon:
|
|
20794
|
+
icon: DouyinShareIcon,
|
|
20757
20795
|
value: share,
|
|
20758
20796
|
label: "分享"
|
|
20759
20797
|
}
|
|
@@ -20922,19 +20960,19 @@ var DouyinDynamicFooter$1 = (props) => {
|
|
|
20922
20960
|
className: "text-3xl flex gap-6 items-center text-foreground/70",
|
|
20923
20961
|
children: [
|
|
20924
20962
|
{
|
|
20925
|
-
icon:
|
|
20963
|
+
icon: DouyinLikeIcon,
|
|
20926
20964
|
iconSize: 36,
|
|
20927
20965
|
label: "获赞",
|
|
20928
20966
|
value: 获赞
|
|
20929
20967
|
},
|
|
20930
20968
|
{
|
|
20931
|
-
icon: o$
|
|
20969
|
+
icon: o$3,
|
|
20932
20970
|
iconSize: 36,
|
|
20933
20971
|
label: "关注",
|
|
20934
20972
|
value: 关注
|
|
20935
20973
|
},
|
|
20936
20974
|
{
|
|
20937
|
-
icon: r
|
|
20975
|
+
icon: r,
|
|
20938
20976
|
iconSize: 36,
|
|
20939
20977
|
label: "粉丝",
|
|
20940
20978
|
value: 粉丝
|
|
@@ -21197,10 +21235,7 @@ var InfoSection = ({ data }) => {
|
|
|
21197
21235
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
21198
21236
|
className: "flex items-center gap-1.5",
|
|
21199
21237
|
children: [
|
|
21200
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
21201
|
-
size: 28,
|
|
21202
|
-
className: "text-foreground/20"
|
|
21203
|
-
}),
|
|
21238
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 28 }),
|
|
21204
21239
|
data.like_count,
|
|
21205
21240
|
"点赞"
|
|
21206
21241
|
]
|
|
@@ -21312,7 +21347,7 @@ var BottomSection = ({ data }) => {
|
|
|
21312
21347
|
className: "flex flex-col gap-2 px-6 py-3 rounded-2xl bg-default/45",
|
|
21313
21348
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
21314
21349
|
className: "flex items-center gap-2",
|
|
21315
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
21350
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 28 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
21316
21351
|
className: "text-foreground/40",
|
|
21317
21352
|
children: "获赞"
|
|
21318
21353
|
})]
|
|
@@ -21474,7 +21509,7 @@ var MusicAuthorInfoSection = ({ avatarUrl, username, userShortId, totalFavorited
|
|
|
21474
21509
|
}),
|
|
21475
21510
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
21476
21511
|
className: "flex gap-2 items-center",
|
|
21477
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
21512
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 32 }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: ["获赞: ", totalFavorited] })]
|
|
21478
21513
|
}),
|
|
21479
21514
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
21480
21515
|
className: "flex gap-2 items-center",
|
|
@@ -21866,10 +21901,7 @@ var DouyinRecommendList = (props) => {
|
|
|
21866
21901
|
}),
|
|
21867
21902
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
21868
21903
|
className: "absolute -bottom-3 -right-3 bg-emerald-500 text-white px-5 py-2 rounded-full border-[5px] border-white font-bold text-xl shadow-xl flex items-center gap-2",
|
|
21869
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
21870
|
-
size: 20,
|
|
21871
|
-
className: "fill-current"
|
|
21872
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "推荐" })]
|
|
21904
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinRecommendIcon, { size: 20 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "推荐" })]
|
|
21873
21905
|
})
|
|
21874
21906
|
]
|
|
21875
21907
|
}),
|
|
@@ -21893,10 +21925,7 @@ var DouyinRecommendList = (props) => {
|
|
|
21893
21925
|
className: "mt-6 flex flex-col items-center gap-2 text-emerald-500",
|
|
21894
21926
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
21895
21927
|
className: "flex items-center gap-3",
|
|
21896
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
21897
|
-
size: 40,
|
|
21898
|
-
className: "fill-current"
|
|
21899
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
21928
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinRecommendIcon, { size: 40 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
21900
21929
|
className: "text-4xl font-bold",
|
|
21901
21930
|
children: "刚刚推荐了这个作品"
|
|
21902
21931
|
})]
|
|
@@ -22003,24 +22032,24 @@ var DouyinRecommendList = (props) => {
|
|
|
22003
22032
|
className: "grid grid-cols-5 gap-6 z-10",
|
|
22004
22033
|
children: [
|
|
22005
22034
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$2, {
|
|
22006
|
-
icon:
|
|
22035
|
+
icon: DouyinLikeIcon,
|
|
22007
22036
|
value: props.data.dianzan
|
|
22008
22037
|
}),
|
|
22009
22038
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$2, {
|
|
22010
|
-
icon:
|
|
22039
|
+
icon: DouyinRecommendIcon,
|
|
22011
22040
|
value: props.data.tuijian,
|
|
22012
|
-
|
|
22041
|
+
iconColor: "#13C15A"
|
|
22013
22042
|
}),
|
|
22014
22043
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$2, {
|
|
22015
|
-
icon:
|
|
22044
|
+
icon: DouyinCommentIcon,
|
|
22016
22045
|
value: props.data.pinglun
|
|
22017
22046
|
}),
|
|
22018
22047
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$2, {
|
|
22019
|
-
icon:
|
|
22048
|
+
icon: DouyinFavoriteIcon,
|
|
22020
22049
|
value: props.data.shouchang
|
|
22021
22050
|
}),
|
|
22022
22051
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$2, {
|
|
22023
|
-
icon:
|
|
22052
|
+
icon: DouyinShareIcon,
|
|
22024
22053
|
value: props.data.share
|
|
22025
22054
|
})
|
|
22026
22055
|
]
|
|
@@ -22033,11 +22062,12 @@ var DouyinRecommendList = (props) => {
|
|
|
22033
22062
|
});
|
|
22034
22063
|
};
|
|
22035
22064
|
/** 统计项小组件 */
|
|
22036
|
-
var StatItem$2 = ({ icon: IconComponent, value,
|
|
22065
|
+
var StatItem$2 = ({ icon: IconComponent, value, iconColor }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
22037
22066
|
className: "flex flex-col items-center justify-center gap-2 py-6 rounded-3xl bg-surface",
|
|
22038
22067
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconComponent, {
|
|
22039
22068
|
size: 48,
|
|
22040
|
-
|
|
22069
|
+
color: iconColor,
|
|
22070
|
+
className: "opacity-90"
|
|
22041
22071
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
22042
22072
|
className: "text-2xl font-bold text-foreground/70 mt-1",
|
|
22043
22073
|
children: value
|
|
@@ -22054,12 +22084,12 @@ var pushTypeConfig = {
|
|
|
22054
22084
|
favorite: {
|
|
22055
22085
|
label: "喜欢列表",
|
|
22056
22086
|
color: "bg-[#d94f50]/5 text-[#b04546] border-[#d94f50]/12",
|
|
22057
|
-
icon:
|
|
22087
|
+
icon: DouyinLikeIcon
|
|
22058
22088
|
},
|
|
22059
22089
|
recommend: {
|
|
22060
22090
|
label: "推荐列表",
|
|
22061
22091
|
color: "bg-[#c9943a]/5 text-[#a07d30] border-[#c9943a]/12",
|
|
22062
|
-
icon:
|
|
22092
|
+
icon: DouyinRecommendIcon
|
|
22063
22093
|
},
|
|
22064
22094
|
live: {
|
|
22065
22095
|
label: "直播状态",
|
|
@@ -22193,7 +22223,7 @@ var DouyinUserItem = (props) => {
|
|
|
22193
22223
|
label: "粉丝"
|
|
22194
22224
|
},
|
|
22195
22225
|
{
|
|
22196
|
-
icon:
|
|
22226
|
+
icon: DouyinLikeIcon,
|
|
22197
22227
|
value: props.total_favorited,
|
|
22198
22228
|
label: "获赞"
|
|
22199
22229
|
},
|
|
@@ -22388,7 +22418,7 @@ var DouyinUserList = (props) => {
|
|
|
22388
22418
|
/**
|
|
22389
22419
|
* 格式化数字显示 (使用中文单位:万、亿)
|
|
22390
22420
|
*/
|
|
22391
|
-
var formatCount
|
|
22421
|
+
var formatCount = (count) => {
|
|
22392
22422
|
if (count >= 1e8) return (count / 1e8).toFixed(1) + "亿";
|
|
22393
22423
|
if (count >= 1e4) return (count / 1e4).toFixed(1) + "万";
|
|
22394
22424
|
return count.toString();
|
|
@@ -22397,7 +22427,7 @@ var formatCount$2 = (count) => {
|
|
|
22397
22427
|
* 格式化视频时长显示 (如: 6:20)
|
|
22398
22428
|
* @param milliseconds 毫秒数
|
|
22399
22429
|
*/
|
|
22400
|
-
var formatDuration$
|
|
22430
|
+
var formatDuration$2 = (milliseconds) => {
|
|
22401
22431
|
const seconds = Math.floor(milliseconds / 1e3);
|
|
22402
22432
|
return `${Math.floor(seconds / 60)}:${(seconds % 60).toString().padStart(2, "0")}`;
|
|
22403
22433
|
};
|
|
@@ -22433,7 +22463,7 @@ var VideoCard = ({ video }) => {
|
|
|
22433
22463
|
}),
|
|
22434
22464
|
video.is_video && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
22435
22465
|
className: "absolute bottom-4 right-4 px-8 py-3 rounded-2xl text-4xl bg-white/50 text-black backdrop-blur-xs shadow-lg",
|
|
22436
|
-
children: formatDuration$
|
|
22466
|
+
children: formatDuration$2(video.duration)
|
|
22437
22467
|
}),
|
|
22438
22468
|
video.music && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
22439
22469
|
className: "absolute bottom-4 left-4 flex items-center gap-2 px-6 py-3 rounded-2xl text-xl bg-white/50 text-black backdrop-blur-xs shadow-lg",
|
|
@@ -22465,19 +22495,19 @@ var VideoCard = ({ video }) => {
|
|
|
22465
22495
|
children: [
|
|
22466
22496
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
22467
22497
|
className: "flex items-center gap-2 text-foreground/70",
|
|
22468
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
22498
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 34 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: formatCount(video.statistics.like_count) })]
|
|
22469
22499
|
}),
|
|
22470
22500
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
22471
22501
|
className: "flex items-center gap-2 text-foreground/70",
|
|
22472
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
22502
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinCommentIcon, { size: 34 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: formatCount(video.statistics.comment_count) })]
|
|
22473
22503
|
}),
|
|
22474
22504
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
22475
22505
|
className: "flex items-center gap-2 text-foreground/70",
|
|
22476
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
22506
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinFavoriteIcon, { size: 34 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: formatCount(video.statistics.collect_count) })]
|
|
22477
22507
|
}),
|
|
22478
22508
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
22479
22509
|
className: "flex items-center gap-2 text-foreground/70",
|
|
22480
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
22510
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinShareIcon, { size: 34 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: formatCount(video.statistics.share_count) })]
|
|
22481
22511
|
})
|
|
22482
22512
|
]
|
|
22483
22513
|
})
|
|
@@ -22559,7 +22589,7 @@ var DouyinUserVideoList = (prpos) => {
|
|
|
22559
22589
|
}),
|
|
22560
22590
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
22561
22591
|
className: "font-medium text-4xl text-foreground",
|
|
22562
|
-
children: [" ", formatCount
|
|
22592
|
+
children: [" ", formatCount(prpos.data.user.following_count)]
|
|
22563
22593
|
})
|
|
22564
22594
|
]
|
|
22565
22595
|
}),
|
|
@@ -22576,24 +22606,21 @@ var DouyinUserVideoList = (prpos) => {
|
|
|
22576
22606
|
}),
|
|
22577
22607
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
22578
22608
|
className: "font-medium text-4xl text-foreground",
|
|
22579
|
-
children: [" ", formatCount
|
|
22609
|
+
children: [" ", formatCount(prpos.data.user.follower_count)]
|
|
22580
22610
|
})
|
|
22581
22611
|
]
|
|
22582
22612
|
}),
|
|
22583
22613
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
22584
22614
|
className: "flex items-center gap-2",
|
|
22585
22615
|
children: [
|
|
22586
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
22587
|
-
size: 28,
|
|
22588
|
-
className: "text-muted"
|
|
22589
|
-
}),
|
|
22616
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 28 }),
|
|
22590
22617
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
22591
22618
|
className: "text-muted",
|
|
22592
22619
|
children: "获赞"
|
|
22593
22620
|
}),
|
|
22594
22621
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
22595
22622
|
className: "font-medium text-4xl text-foreground",
|
|
22596
|
-
children: [" ", formatCount
|
|
22623
|
+
children: [" ", formatCount(prpos.data.user.total_favorited)]
|
|
22597
22624
|
})
|
|
22598
22625
|
]
|
|
22599
22626
|
})
|
|
@@ -22621,7 +22648,8 @@ var DouyinUserVideoList = (prpos) => {
|
|
|
22621
22648
|
className: "text-accent font-bold",
|
|
22622
22649
|
children: ["1~", prpos.data.videos.length]
|
|
22623
22650
|
}),
|
|
22624
|
-
"
|
|
22651
|
+
" ",
|
|
22652
|
+
"之间的数字解析对应作品。例如发送“1”解析第一个作品"
|
|
22625
22653
|
] })]
|
|
22626
22654
|
})
|
|
22627
22655
|
}),
|
|
@@ -22647,12 +22675,12 @@ var formatNumber$1 = (num) => {
|
|
|
22647
22675
|
if (num >= 1e4) return `${(num / 1e4).toFixed(1)}万`;
|
|
22648
22676
|
return num.toLocaleString();
|
|
22649
22677
|
};
|
|
22650
|
-
var formatDuration$
|
|
22678
|
+
var formatDuration$1 = (ms) => {
|
|
22651
22679
|
const seconds = Math.floor(ms / 1e3);
|
|
22652
22680
|
return `${Math.floor(seconds / 60)}:${(seconds % 60).toString().padStart(2, "0")}`;
|
|
22653
22681
|
};
|
|
22654
22682
|
var DouyinVideoInfo = import_react.memo((props) => {
|
|
22655
|
-
const duration = (0, import_react.useMemo)(() => props.data.video ? formatDuration$
|
|
22683
|
+
const duration = (0, import_react.useMemo)(() => props.data.video ? formatDuration$1(props.data.video.duration) : null, [props.data.video]);
|
|
22656
22684
|
const coverMaskStyle = (0, import_react.useMemo)(() => ({
|
|
22657
22685
|
maskImage: `linear-gradient(to bottom,
|
|
22658
22686
|
transparent 0%,
|
|
@@ -22802,19 +22830,19 @@ var DouyinVideoInfo = import_react.memo((props) => {
|
|
|
22802
22830
|
className: "flex items-center gap-14 shrink-0",
|
|
22803
22831
|
children: [
|
|
22804
22832
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$1, {
|
|
22805
|
-
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
22833
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 48 }),
|
|
22806
22834
|
value: props.data.statistics.digg_count
|
|
22807
22835
|
}),
|
|
22808
22836
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$1, {
|
|
22809
|
-
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
22837
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinCommentIcon, { size: 48 }),
|
|
22810
22838
|
value: props.data.statistics.comment_count
|
|
22811
22839
|
}),
|
|
22812
22840
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$1, {
|
|
22813
|
-
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
22841
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinFavoriteIcon, { size: 48 }),
|
|
22814
22842
|
value: props.data.statistics.collect_count
|
|
22815
22843
|
}),
|
|
22816
22844
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$1, {
|
|
22817
|
-
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
22845
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinShareIcon, { size: 48 }),
|
|
22818
22846
|
value: props.data.statistics.share_count
|
|
22819
22847
|
})
|
|
22820
22848
|
]
|
|
@@ -22883,7 +22911,7 @@ var StatItem$1 = ({ icon, value }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx
|
|
|
22883
22911
|
DouyinVideoInfo.displayName = "DouyinVideoInfo";
|
|
22884
22912
|
//#endregion
|
|
22885
22913
|
//#region ../template/src/components/platforms/douyin/VideoWork.tsx
|
|
22886
|
-
function formatDuration
|
|
22914
|
+
function formatDuration(duration) {
|
|
22887
22915
|
if (typeof duration !== "number" || !Number.isFinite(duration) || duration < 0) return void 0;
|
|
22888
22916
|
const totalSeconds = Math.floor(duration / 1e3);
|
|
22889
22917
|
const hours = Math.floor(totalSeconds / 3600);
|
|
@@ -22948,7 +22976,7 @@ var DouyinAvatarUserInfo = (props) => {
|
|
|
22948
22976
|
};
|
|
22949
22977
|
var DouyinVideoCover = (props) => {
|
|
22950
22978
|
const { image_url, music, duration } = props.data;
|
|
22951
|
-
const durationText = formatDuration
|
|
22979
|
+
const durationText = formatDuration(duration);
|
|
22952
22980
|
const musicBadge = music && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
22953
22981
|
className: "absolute left-7 bottom-7 z-20 flex items-center gap-4 max-w-[72%] p-3 rounded-3xl bg-black/45 backdrop-blur-2xl border border-white/20 shadow-large overflow-hidden",
|
|
22954
22982
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
@@ -23070,22 +23098,22 @@ var DouyinDynamicStatus = (props) => {
|
|
|
23070
23098
|
className: "flex gap-6 items-center text-5xl tracking-normal select-text text-foreground/70",
|
|
23071
23099
|
children: [
|
|
23072
23100
|
{
|
|
23073
|
-
icon:
|
|
23101
|
+
icon: DouyinLikeIcon,
|
|
23074
23102
|
value: dianzan,
|
|
23075
23103
|
label: "点赞"
|
|
23076
23104
|
},
|
|
23077
23105
|
{
|
|
23078
|
-
icon:
|
|
23106
|
+
icon: DouyinCommentIcon,
|
|
23079
23107
|
value: pinglun,
|
|
23080
23108
|
label: "评论"
|
|
23081
23109
|
},
|
|
23082
23110
|
{
|
|
23083
|
-
icon:
|
|
23111
|
+
icon: DouyinFavoriteIcon,
|
|
23084
23112
|
value: shouchang,
|
|
23085
23113
|
label: "收藏"
|
|
23086
23114
|
},
|
|
23087
23115
|
{
|
|
23088
|
-
icon:
|
|
23116
|
+
icon: DouyinShareIcon,
|
|
23089
23117
|
value: share,
|
|
23090
23118
|
label: "分享"
|
|
23091
23119
|
}
|
|
@@ -23095,7 +23123,7 @@ var DouyinDynamicStatus = (props) => {
|
|
|
23095
23123
|
className: "flex gap-2 items-end",
|
|
23096
23124
|
children: [
|
|
23097
23125
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, {
|
|
23098
|
-
size:
|
|
23126
|
+
size: 46,
|
|
23099
23127
|
weight: "fill",
|
|
23100
23128
|
className: "mt-2"
|
|
23101
23129
|
}),
|
|
@@ -23255,19 +23283,19 @@ var DouyinDynamicFooter = (props) => {
|
|
|
23255
23283
|
className: "text-3xl flex gap-6 items-center text-foreground/70",
|
|
23256
23284
|
children: [
|
|
23257
23285
|
{
|
|
23258
|
-
icon:
|
|
23286
|
+
icon: DouyinLikeIcon,
|
|
23259
23287
|
iconSize: 36,
|
|
23260
23288
|
label: "获赞",
|
|
23261
23289
|
value: 获赞
|
|
23262
23290
|
},
|
|
23263
23291
|
{
|
|
23264
|
-
icon: o$
|
|
23292
|
+
icon: o$3,
|
|
23265
23293
|
iconSize: 36,
|
|
23266
23294
|
label: "关注",
|
|
23267
23295
|
value: 关注
|
|
23268
23296
|
},
|
|
23269
23297
|
{
|
|
23270
|
-
icon: r
|
|
23298
|
+
icon: r,
|
|
23271
23299
|
iconSize: 36,
|
|
23272
23300
|
label: "粉丝",
|
|
23273
23301
|
value: 粉丝
|
|
@@ -24987,7 +25015,8 @@ var GroupStatistics = (props) => {
|
|
|
24987
25015
|
className: "font-black text-foreground",
|
|
24988
25016
|
children: props.data.globalTotalGroups
|
|
24989
25017
|
}),
|
|
24990
|
-
" 个群组 · 解析
|
|
25018
|
+
" 个群组 · 解析",
|
|
25019
|
+
" ",
|
|
24991
25020
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
24992
25021
|
className: "font-black text-foreground",
|
|
24993
25022
|
children: props.data.globalTotalParses
|
|
@@ -25270,15 +25299,16 @@ var ansi256ToColor = (colorCode) => {
|
|
|
25270
25299
|
return `#${hex}${hex}${hex}`;
|
|
25271
25300
|
};
|
|
25272
25301
|
var convertAnsiToHtml = (text) => {
|
|
25273
|
-
const ansiRegex =
|
|
25302
|
+
const ansiRegex = /* @__PURE__ */ new RegExp("\x1B\\[([0-9;]+)m", "g");
|
|
25274
25303
|
let result = "", lastIndex = 0;
|
|
25275
25304
|
let currentStyles = { classes: [] };
|
|
25276
25305
|
let match;
|
|
25277
25306
|
const escapeHtml = (str) => str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
25307
|
+
const formatLogContent = (content) => escapeHtml(content).replace(/([\u3400-\u9fff\uf900-\ufaff\u3000-\u303f\uff00-\uffef]+)/g, "<span class=\"font-[HarmonyOSHans-Regular]\">$1</span>");
|
|
25278
25308
|
const makeSpan = (content) => {
|
|
25279
25309
|
const hasClass = currentStyles.classes.length > 0, hasInline = currentStyles.inlineColor;
|
|
25280
|
-
if (!hasClass && !hasInline) return
|
|
25281
|
-
return `<span${hasClass ? ` class="${currentStyles.classes.join(" ")}"` : ""}${hasInline ? ` style="color: ${currentStyles.inlineColor}"` : ""}>${
|
|
25310
|
+
if (!hasClass && !hasInline) return formatLogContent(content);
|
|
25311
|
+
return `<span${hasClass ? ` class="${currentStyles.classes.join(" ")}"` : ""}${hasInline ? ` style="color: ${currentStyles.inlineColor}"` : ""}>${formatLogContent(content)}</span>`;
|
|
25282
25312
|
};
|
|
25283
25313
|
while ((match = ansiRegex.exec(text)) !== null) {
|
|
25284
25314
|
if (match.index > lastIndex) result += makeSpan(text.substring(lastIndex, match.index));
|
|
@@ -25318,7 +25348,7 @@ var convertAnsiToHtml = (text) => {
|
|
|
25318
25348
|
};
|
|
25319
25349
|
var getLogLevelTheme = (level, isDark) => {
|
|
25320
25350
|
const themeMap = {
|
|
25321
|
-
|
|
25351
|
+
TRAC: {
|
|
25322
25352
|
bgClass: isDark ? "bg-muted/10" : "bg-muted/5",
|
|
25323
25353
|
borderClass: "border-muted/20",
|
|
25324
25354
|
textClass: "text-muted",
|
|
@@ -25326,7 +25356,7 @@ var getLogLevelTheme = (level, isDark) => {
|
|
|
25326
25356
|
levelClass: isDark ? "text-muted/10" : "text-muted/10",
|
|
25327
25357
|
dotClass: "bg-muted/40"
|
|
25328
25358
|
},
|
|
25329
|
-
|
|
25359
|
+
DEBU: {
|
|
25330
25360
|
bgClass: isDark ? "bg-cyan-400/10" : "bg-cyan-500/5",
|
|
25331
25361
|
borderClass: isDark ? "border-cyan-400/20" : "border-cyan-500/20",
|
|
25332
25362
|
textClass: isDark ? "text-cyan-400" : "text-cyan-600",
|
|
@@ -25334,7 +25364,7 @@ var getLogLevelTheme = (level, isDark) => {
|
|
|
25334
25364
|
levelClass: isDark ? "text-cyan-400/10" : "text-cyan-600/10",
|
|
25335
25365
|
dotClass: isDark ? "bg-cyan-400/40" : "bg-cyan-500/40"
|
|
25336
25366
|
},
|
|
25337
|
-
|
|
25367
|
+
MARK: {
|
|
25338
25368
|
bgClass: isDark ? "bg-muted/10" : "bg-muted/5",
|
|
25339
25369
|
borderClass: "border-muted/20",
|
|
25340
25370
|
textClass: "text-muted",
|
|
@@ -25342,7 +25372,7 @@ var getLogLevelTheme = (level, isDark) => {
|
|
|
25342
25372
|
levelClass: isDark ? "text-muted/10" : "text-muted/10",
|
|
25343
25373
|
dotClass: "bg-muted/40"
|
|
25344
25374
|
},
|
|
25345
|
-
|
|
25375
|
+
INFO: {
|
|
25346
25376
|
bgClass: "bg-success-soft",
|
|
25347
25377
|
borderClass: "border-success/25",
|
|
25348
25378
|
textClass: "text-success",
|
|
@@ -25350,7 +25380,7 @@ var getLogLevelTheme = (level, isDark) => {
|
|
|
25350
25380
|
levelClass: isDark ? "text-success/10" : "text-success/10",
|
|
25351
25381
|
dotClass: "bg-success/40"
|
|
25352
25382
|
},
|
|
25353
|
-
|
|
25383
|
+
WARN: {
|
|
25354
25384
|
bgClass: "bg-warning-soft",
|
|
25355
25385
|
borderClass: "border-warning/25",
|
|
25356
25386
|
textClass: "text-warning",
|
|
@@ -25358,7 +25388,7 @@ var getLogLevelTheme = (level, isDark) => {
|
|
|
25358
25388
|
levelClass: isDark ? "text-warning/10" : "text-warning-soft",
|
|
25359
25389
|
dotClass: "bg-warning/40"
|
|
25360
25390
|
},
|
|
25361
|
-
|
|
25391
|
+
ERRO: {
|
|
25362
25392
|
bgClass: "bg-danger-soft",
|
|
25363
25393
|
borderClass: "border-danger/25",
|
|
25364
25394
|
textClass: "text-danger",
|
|
@@ -25366,7 +25396,7 @@ var getLogLevelTheme = (level, isDark) => {
|
|
|
25366
25396
|
levelClass: isDark ? "text-danger/10" : "text-danger/10",
|
|
25367
25397
|
dotClass: "bg-danger/40"
|
|
25368
25398
|
},
|
|
25369
|
-
|
|
25399
|
+
FATA: {
|
|
25370
25400
|
bgClass: isDark ? "bg-pink-400/10" : "bg-pink-500/5",
|
|
25371
25401
|
borderClass: isDark ? "border-pink-400/25" : "border-pink-500/25",
|
|
25372
25402
|
textClass: isDark ? "text-pink-400" : "text-pink-500",
|
|
@@ -26024,7 +26054,8 @@ var handlerError = (props) => {
|
|
|
26024
26054
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MdSchedule, { size: 24 }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
26025
26055
|
"Built Time: ",
|
|
26026
26056
|
data.buildTime,
|
|
26027
|
-
" 于
|
|
26057
|
+
" 于",
|
|
26058
|
+
" ",
|
|
26028
26059
|
formatDistanceToNow(parse(data.buildTime, "yyyy年MM月dd日 HH:mm", /* @__PURE__ */ new Date()), { locale: zhCN }),
|
|
26029
26060
|
"前"
|
|
26030
26061
|
] })]
|
|
@@ -27904,13 +27935,15 @@ var VersionWarning = (props) => {
|
|
|
27904
27935
|
className: "text-[26px] leading-relaxed",
|
|
27905
27936
|
style: { color: secondaryColor },
|
|
27906
27937
|
children: [
|
|
27907
|
-
"当前插件版本基于
|
|
27938
|
+
"当前插件版本基于",
|
|
27939
|
+
" ",
|
|
27908
27940
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
27909
27941
|
className: "font-bold font-mono",
|
|
27910
27942
|
style: { color: accentColor },
|
|
27911
27943
|
children: ["node-karin v", props.data.requireVersion]
|
|
27912
27944
|
}),
|
|
27913
|
-
"
|
|
27945
|
+
" ",
|
|
27946
|
+
"开发,低版本运行时可能出现功能异常或兼容性问题,请及时更新以获得最佳体验"
|
|
27914
27947
|
]
|
|
27915
27948
|
})]
|
|
27916
27949
|
})]
|
|
@@ -29829,11 +29862,11 @@ var SSRRender = class {
|
|
|
29829
29862
|
};
|
|
29830
29863
|
/**
|
|
29831
29864
|
* SSR 预渲染组件为 HTML 的具体实现
|
|
29832
|
-
*
|
|
29865
|
+
*
|
|
29833
29866
|
* @template K 模板类型键,用于类型推断
|
|
29834
29867
|
* @param options 渲染配置选项
|
|
29835
29868
|
* @returns 渲染结果 Promise
|
|
29836
|
-
*
|
|
29869
|
+
*
|
|
29837
29870
|
* # Example
|
|
29838
29871
|
* ```typescript
|
|
29839
29872
|
* // 基础使用
|
|
@@ -29845,7 +29878,7 @@ var SSRRender = class {
|
|
|
29845
29878
|
* },
|
|
29846
29879
|
* outputDir: './output'
|
|
29847
29880
|
* })
|
|
29848
|
-
*
|
|
29881
|
+
*
|
|
29849
29882
|
* // 使用插件
|
|
29850
29883
|
* const result = await reactServerRender({
|
|
29851
29884
|
* request: renderRequest,
|
|
@@ -30313,7 +30346,7 @@ var embedWatermark = (pngBytes, watermarkText) => {
|
|
|
30313
30346
|
/**
|
|
30314
30347
|
* 渲染函数
|
|
30315
30348
|
* 将指定路径的模板渲染为图片元素数组
|
|
30316
|
-
*
|
|
30349
|
+
*
|
|
30317
30350
|
* @param event 消息事件对象,用于获取机器人账号信息
|
|
30318
30351
|
* @template P 渲染路径,必须是有效的动态路径
|
|
30319
30352
|
* @param path 渲染路径,格式为 "平台/组件ID" 或 "平台/分类/组件ID"
|
|
@@ -32598,7 +32631,7 @@ var webConfig = defineConfig({
|
|
|
32598
32631
|
}]
|
|
32599
32632
|
},
|
|
32600
32633
|
page: {
|
|
32601
|
-
url: process.env.NODE_ENV === "development" ? "http://
|
|
32634
|
+
url: process.env.NODE_ENV === "development" ? "http://localhost:5176/kkk/assets/karin-config" : "/kkk/assets/karin-config",
|
|
32602
32635
|
title: "kkk插件配置管理",
|
|
32603
32636
|
description: "使用 kkk 插件自带的配置管理页面"
|
|
32604
32637
|
}
|
|
@@ -32707,7 +32740,7 @@ var renderErrorImage = async (ctx, opts = {}) => {
|
|
|
32707
32740
|
//#region src/module/utils/bot.ts
|
|
32708
32741
|
/**
|
|
32709
32742
|
* 获取候选机器人
|
|
32710
|
-
* @returns
|
|
32743
|
+
* @returns
|
|
32711
32744
|
*/
|
|
32712
32745
|
var getCandidateBots = () => {
|
|
32713
32746
|
return karin$1.getAllBotList().map((item) => item.bot).filter((bot) => bot.account.name !== "console");
|
|
@@ -32715,7 +32748,7 @@ var getCandidateBots = () => {
|
|
|
32715
32748
|
/**
|
|
32716
32749
|
* 获取非console主机器人ID列表
|
|
32717
32750
|
* @param masters - 主机器人ID列表
|
|
32718
|
-
* @returns
|
|
32751
|
+
* @returns
|
|
32719
32752
|
*/
|
|
32720
32753
|
var getNonConsoleMasters = (masters = config.master()) => {
|
|
32721
32754
|
return masters.filter((id) => id !== "console");
|
|
@@ -32723,7 +32756,7 @@ var getNonConsoleMasters = (masters = config.master()) => {
|
|
|
32723
32756
|
/**
|
|
32724
32757
|
* 获取可访问的主机器人
|
|
32725
32758
|
* @param masters - 主机器人ID列表
|
|
32726
|
-
* @returns
|
|
32759
|
+
* @returns
|
|
32727
32760
|
*/
|
|
32728
32761
|
var getReachableMasterBots = async (masters = config.master()) => {
|
|
32729
32762
|
const owners = getNonConsoleMasters(masters);
|
|
@@ -32754,7 +32787,7 @@ var getReachableMasterBots = async (masters = config.master()) => {
|
|
|
32754
32787
|
/**
|
|
32755
32788
|
* 获取一个最少能用的机器人实例,优先级:1. selfId 参数指定的机器人 2. 可访问主人机器人中的第一个 3. pushlist 中活跃的机器人 4. 任意一个在线机器人
|
|
32756
32789
|
* @param selfId - 机器人ID
|
|
32757
|
-
* @returns
|
|
32790
|
+
* @returns
|
|
32758
32791
|
*/
|
|
32759
32792
|
var resolveUsableBot = async (selfId) => {
|
|
32760
32793
|
if (selfId) {
|
|
@@ -32959,7 +32992,7 @@ var sendErrorToAllMasters = async (ctx, img, customPrefix) => {
|
|
|
32959
32992
|
* @param event - 错误事件上下文
|
|
32960
32993
|
* @param isPush - 是否为推送任务
|
|
32961
32994
|
*
|
|
32962
|
-
* @returns
|
|
32995
|
+
* @returns
|
|
32963
32996
|
*/
|
|
32964
32997
|
var resolveSingleMasterTarget = async (event, isPush) => {
|
|
32965
32998
|
if (isPush) {
|
|
@@ -33987,10 +34020,10 @@ var Bilibili = class extends Base {
|
|
|
33987
34020
|
this.downloadfilename = "";
|
|
33988
34021
|
this.forceBurnDanmaku = options?.forceBurnDanmaku ?? false;
|
|
33989
34022
|
this.headers.Referer = "https://www.bilibili.com/";
|
|
33990
|
-
this.headers.Cookie = Config.cookies.bilibili;
|
|
34023
|
+
this.headers.Cookie = Config.amagi.cookies.bilibili;
|
|
33991
34024
|
}
|
|
33992
34025
|
async BilibiliHandler(iddata) {
|
|
33993
|
-
Config.app.parseTip
|
|
34026
|
+
if (Config.app.parseTip) this.e.reply("检测到B站链接,开始解析");
|
|
33994
34027
|
switch (this.Type) {
|
|
33995
34028
|
case "one_video": {
|
|
33996
34029
|
const infoData = await this.amagi.bilibili.fetcher.fetchVideoInfo({
|
|
@@ -34037,9 +34070,12 @@ var Bilibili = class extends Base {
|
|
|
34037
34070
|
host_mid: infoData.data.data.owner.mid,
|
|
34038
34071
|
typeMode: "strict"
|
|
34039
34072
|
});
|
|
34040
|
-
|
|
34041
|
-
|
|
34042
|
-
|
|
34073
|
+
let hotDanmaku;
|
|
34074
|
+
if (Config.bilibili.showDanmakuInVideoInfo) {
|
|
34075
|
+
const danmakuCid = iddata.p ? infoData.data.data.pages[iddata.p - 1]?.cid ?? infoData.data.data.cid : infoData.data.data.cid;
|
|
34076
|
+
const danmakuDuration = iddata.p ? infoData.data.data.pages[iddata.p - 1]?.duration ?? infoData.data.data.duration : infoData.data.data.duration;
|
|
34077
|
+
hotDanmaku = getHotDanmaku(await this.fetchVideoDanmakuList(danmakuCid, danmakuDuration), 20);
|
|
34078
|
+
}
|
|
34043
34079
|
const img = await Render(this.e, "bilibili/videoInfo", {
|
|
34044
34080
|
share_url: "https://b23.tv/" + infoData.data.data.bvid,
|
|
34045
34081
|
title: infoData.data.data.title,
|
|
@@ -34120,7 +34156,7 @@ var Bilibili = class extends Base {
|
|
|
34120
34156
|
}
|
|
34121
34157
|
}
|
|
34122
34158
|
}
|
|
34123
|
-
if (Config.bilibili.sendContent.some((content) => content === "video")) if (Config.
|
|
34159
|
+
if (Config.bilibili.sendContent.some((content) => content === "video")) if (Config.app.usefilelimit && Number(videoSize) > Number(Config.app.filelimit) && !Config.app.compress) this.e.reply(`设定的最大上传大小为 ${Config.app.filelimit}MB\n当前解析到的视频大小为 ${Number(videoSize)}MB\n视频太大了,还是去B站看吧~`, { reply: true });
|
|
34124
34160
|
else {
|
|
34125
34161
|
if (Config.bilibili.videoQuality !== 0 && Config.bilibili.videoQuality < 64) this.islogin = false;
|
|
34126
34162
|
let danmakuList = [];
|
|
@@ -34284,7 +34320,7 @@ var Bilibili = class extends Base {
|
|
|
34284
34320
|
filepath: filePath,
|
|
34285
34321
|
totalBytes: 0
|
|
34286
34322
|
});
|
|
34287
|
-
const videoPath = Config.
|
|
34323
|
+
const videoPath = Config.app.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
|
|
34288
34324
|
imgArray.push(segment.video(videoPath));
|
|
34289
34325
|
}
|
|
34290
34326
|
}
|
|
@@ -34301,7 +34337,7 @@ var Bilibili = class extends Base {
|
|
|
34301
34337
|
filepath: motionPhotoCoverPath,
|
|
34302
34338
|
totalBytes: 0
|
|
34303
34339
|
});
|
|
34304
|
-
const motionPhotoCover = Config.
|
|
34340
|
+
const motionPhotoCover = Config.app.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
|
|
34305
34341
|
imgArray.push(segment.image(motionPhotoCover));
|
|
34306
34342
|
hasPushedMotionPhotoCover = true;
|
|
34307
34343
|
}
|
|
@@ -34880,7 +34916,7 @@ var Bilibili = class extends Base {
|
|
|
34880
34916
|
await Common.removeFile(bmp3.filepath, true);
|
|
34881
34917
|
const stats = fs.statSync(filePath);
|
|
34882
34918
|
const fileSizeInMB = Number((stats.size / (1024 * 1024)).toFixed(2));
|
|
34883
|
-
if (fileSizeInMB > Config.
|
|
34919
|
+
if (fileSizeInMB > Config.app.groupfilevalue) await uploadFile(this.e, {
|
|
34884
34920
|
filepath: filePath,
|
|
34885
34921
|
totalBytes: fileSizeInMB,
|
|
34886
34922
|
originTitle: this.downloadfilename
|
|
@@ -34922,7 +34958,7 @@ var Bilibili = class extends Base {
|
|
|
34922
34958
|
await Common.removeFile(videoFile.filepath, true);
|
|
34923
34959
|
const stats = fs.statSync(filePath);
|
|
34924
34960
|
const fileSizeInMB = Number((stats.size / (1024 * 1024)).toFixed(2));
|
|
34925
|
-
if (fileSizeInMB > Config.
|
|
34961
|
+
if (fileSizeInMB > Config.app.groupfilevalue) await uploadFile(this.e, {
|
|
34926
34962
|
filepath: filePath,
|
|
34927
34963
|
totalBytes: fileSizeInMB,
|
|
34928
34964
|
originTitle: this.downloadfilename
|
|
@@ -35123,7 +35159,7 @@ var mapping_table = (type) => {
|
|
|
35123
35159
|
* 根据动态类型获取对应的oid(对象ID),用于后续评论接口调用
|
|
35124
35160
|
* @param dynamicType 动态类型
|
|
35125
35161
|
* @param dynamicData 动态数据
|
|
35126
|
-
* @returns
|
|
35162
|
+
* @returns
|
|
35127
35163
|
*/
|
|
35128
35164
|
var oid = (dynamicType, dynamicData) => {
|
|
35129
35165
|
switch (dynamicType) {
|
|
@@ -35204,7 +35240,7 @@ var getvideosize = async (videourl, audiourl, bvid) => {
|
|
|
35204
35240
|
headers: {
|
|
35205
35241
|
...BASE_HEADERS,
|
|
35206
35242
|
Referer: `https://www.bilibili.com/video/${bvid}`,
|
|
35207
|
-
Cookie: Config.cookies.bilibili
|
|
35243
|
+
Cookie: Config.amagi.cookies.bilibili
|
|
35208
35244
|
}
|
|
35209
35245
|
}).getHeaders();
|
|
35210
35246
|
const audioheaders = await new Network({
|
|
@@ -35212,7 +35248,7 @@ var getvideosize = async (videourl, audiourl, bvid) => {
|
|
|
35212
35248
|
headers: {
|
|
35213
35249
|
...BASE_HEADERS,
|
|
35214
35250
|
Referer: `https://www.bilibili.com/video/${bvid}`,
|
|
35215
|
-
Cookie: Config.cookies.bilibili
|
|
35251
|
+
Cookie: Config.amagi.cookies.bilibili
|
|
35216
35252
|
}
|
|
35217
35253
|
}).getHeaders();
|
|
35218
35254
|
const videoSize = extractTotalBytesFromHeaders(videoheaders);
|
|
@@ -35269,8 +35305,8 @@ var getStringDisplayWidth$1 = (str) => {
|
|
|
35269
35305
|
};
|
|
35270
35306
|
/**
|
|
35271
35307
|
* 提取专栏中的所有图片URL
|
|
35272
|
-
* @param content
|
|
35273
|
-
* @returns
|
|
35308
|
+
* @param content
|
|
35309
|
+
* @returns
|
|
35274
35310
|
*/
|
|
35275
35311
|
var extractArticleImages = (content) => {
|
|
35276
35312
|
const images = [];
|
|
@@ -35544,9 +35580,9 @@ var extractFansDetail = (member) => {
|
|
|
35544
35580
|
* @returns
|
|
35545
35581
|
*/
|
|
35546
35582
|
var genParams = async (apiURL) => {
|
|
35547
|
-
if (Config.cookies.bilibili === "" || Config.cookies.bilibili === null) return "&platform=html5";
|
|
35583
|
+
if (Config.amagi.cookies.bilibili === "" || Config.amagi.cookies.bilibili === null) return "&platform=html5";
|
|
35548
35584
|
const loginInfo = await bilibiliFetcher.fetchLoginStatus({ typeMode: "strict" });
|
|
35549
|
-
const genSign = await wbi_sign(apiURL, Config.cookies.bilibili);
|
|
35585
|
+
const genSign = await wbi_sign(apiURL, Config.amagi.cookies.bilibili);
|
|
35550
35586
|
const qn = [
|
|
35551
35587
|
6,
|
|
35552
35588
|
16,
|
|
@@ -35561,19 +35597,15 @@ var genParams = async (apiURL) => {
|
|
|
35561
35597
|
126,
|
|
35562
35598
|
127
|
|
35563
35599
|
];
|
|
35564
|
-
|
|
35565
|
-
loginInfo.data.data.vipStatus === 1 ? isvip = true : isvip = false;
|
|
35566
|
-
if (isvip) return `&fnval=16&fourk=1&${genSign}`;
|
|
35600
|
+
if (loginInfo.data.data.vipStatus === 1) return `&fnval=16&fourk=1&${genSign}`;
|
|
35567
35601
|
else return `&qn=${qn[3]}&fnval=16`;
|
|
35568
35602
|
};
|
|
35569
35603
|
var checkCk = async () => {
|
|
35570
|
-
if (Config.cookies.bilibili === "" || Config.cookies.bilibili === null) return {
|
|
35604
|
+
if (Config.amagi.cookies.bilibili === "" || Config.amagi.cookies.bilibili === null) return {
|
|
35571
35605
|
Status: "!isLogin",
|
|
35572
35606
|
isVIP: false
|
|
35573
35607
|
};
|
|
35574
|
-
const
|
|
35575
|
-
let isVIP;
|
|
35576
|
-
loginInfo.data.data.vipStatus === 1 ? isVIP = true : isVIP = false;
|
|
35608
|
+
const isVIP = (await bilibiliFetcher.fetchLoginStatus({ typeMode: "strict" })).data.data.vipStatus === 1;
|
|
35577
35609
|
if (isVIP) return {
|
|
35578
35610
|
Status: "isLogin",
|
|
35579
35611
|
isVIP
|
|
@@ -35734,8 +35766,8 @@ var bilibiliLogin = async (e) => {
|
|
|
35734
35766
|
let cookieString;
|
|
35735
35767
|
if (Array.isArray(setCookieHeader)) cookieString = setCookieHeader.join("; ");
|
|
35736
35768
|
else cookieString = setCookieHeader || "";
|
|
35737
|
-
Config.Modify("
|
|
35738
|
-
await e.reply("
|
|
35769
|
+
Config.Modify("amagi", "cookies.bilibili", cookieString);
|
|
35770
|
+
await e.reply("登录成功!用户登录凭证已保存至配置", { reply: true });
|
|
35739
35771
|
await recallMessages();
|
|
35740
35772
|
};
|
|
35741
35773
|
/**
|
|
@@ -35810,7 +35842,7 @@ var allBilibiliPushTypes = [
|
|
|
35810
35842
|
var bilibiliBaseHeaders = {
|
|
35811
35843
|
...BASE_HEADERS,
|
|
35812
35844
|
Referer: "https://www.bilibili.com",
|
|
35813
|
-
Cookie: Config.cookies.bilibili
|
|
35845
|
+
Cookie: Config.amagi.cookies.bilibili
|
|
35814
35846
|
};
|
|
35815
35847
|
var Bilibilipush = class extends Base {
|
|
35816
35848
|
force = false;
|
|
@@ -35914,7 +35946,7 @@ var Bilibilipush = class extends Base {
|
|
|
35914
35946
|
${logger.cyan("动态id")}:${logger.yellow(dynamicId)}
|
|
35915
35947
|
${logger.cyan("访问地址")}:${logger.green("https://t.bilibili.com/" + dynamicId)}`);
|
|
35916
35948
|
let skip = await skipDynamic$1(data[dynamicId]);
|
|
35917
|
-
skip
|
|
35949
|
+
if (skip) logger.warn(`动态 https://t.bilibili.com/${dynamicId} 已被处理,跳过`);
|
|
35918
35950
|
let send_video = true;
|
|
35919
35951
|
let img = [];
|
|
35920
35952
|
this.injectBotToEventForRender(data[dynamicId].targets);
|
|
@@ -36320,8 +36352,8 @@ var Bilibilipush = class extends Base {
|
|
|
36320
36352
|
playUrlData.data.data.accept_description = correctList.accept_description;
|
|
36321
36353
|
/** 获取第一个视频流的大小 */
|
|
36322
36354
|
videoSize = await getvideosize(correctList.videoList[0].base_url, playUrlData.data.data.dash.audio[0].base_url, data[dynamicId].Dynamic_Data.modules.module_dynamic.major.archive.bvid);
|
|
36323
|
-
if (Config.
|
|
36324
|
-
await karin$1.sendMsg(botId, Contact, [segment.text(`设定的最大上传大小为 ${Config.
|
|
36355
|
+
if (Config.app.usefilelimit && Number(videoSize) > Number(Config.app.filelimit) && !Config.app.compress) {
|
|
36356
|
+
await karin$1.sendMsg(botId, Contact, [segment.text(`设定的最大上传大小为 ${Config.app.filelimit}MB\n当前解析到的视频大小为 ${Number(videoSize)}MB\n视频太大了,还是去B站看吧~`), segment.reply(status.messageId)]);
|
|
36325
36357
|
break;
|
|
36326
36358
|
}
|
|
36327
36359
|
logger.mark(`当前处于自动推送状态,解析到的视频大小为 ${logger.yellow(Number(videoSize))} MB`);
|
|
@@ -36348,7 +36380,7 @@ var Bilibilipush = class extends Base {
|
|
|
36348
36380
|
await Common.removeFile(mp3File.filepath, true);
|
|
36349
36381
|
const stats = fs.statSync(filePath);
|
|
36350
36382
|
const fileSizeInMB = Number((stats.size / (1024 * 1024)).toFixed(2));
|
|
36351
|
-
if (fileSizeInMB > Config.
|
|
36383
|
+
if (fileSizeInMB > Config.app.groupfilevalue) await uploadFile(this.e, {
|
|
36352
36384
|
filepath: filePath,
|
|
36353
36385
|
totalBytes: fileSizeInMB,
|
|
36354
36386
|
originTitle: `${infoData.data.data.desc.substring(0, 50).replace(/[\\/:\\*\\?"<>\\|\r\n\s]/g, " ")}`
|
|
@@ -36427,7 +36459,7 @@ var Bilibilipush = class extends Base {
|
|
|
36427
36459
|
filepath: filePath,
|
|
36428
36460
|
totalBytes: 0
|
|
36429
36461
|
});
|
|
36430
|
-
const videoPath = Config.
|
|
36462
|
+
const videoPath = Config.app.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
|
|
36431
36463
|
imgArray.push(segment.video(videoPath));
|
|
36432
36464
|
}
|
|
36433
36465
|
}
|
|
@@ -36444,7 +36476,7 @@ var Bilibilipush = class extends Base {
|
|
|
36444
36476
|
filepath: motionPhotoCoverPath,
|
|
36445
36477
|
totalBytes: 0
|
|
36446
36478
|
});
|
|
36447
|
-
const motionPhotoCover = Config.
|
|
36479
|
+
const motionPhotoCover = Config.app.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
|
|
36448
36480
|
imgArray.push(segment.image(motionPhotoCover));
|
|
36449
36481
|
hasPushedMotionPhotoCover = true;
|
|
36450
36482
|
}
|
|
@@ -37505,7 +37537,7 @@ var DouYin = class DouYin extends Base {
|
|
|
37505
37537
|
return true;
|
|
37506
37538
|
}
|
|
37507
37539
|
async DouyinHandler(data) {
|
|
37508
|
-
Config.app.parseTip
|
|
37540
|
+
if (Config.app.parseTip) this.e.reply("检测到抖音链接,开始解析");
|
|
37509
37541
|
switch (this.type) {
|
|
37510
37542
|
case "one_work": {
|
|
37511
37543
|
const VideoData = await this.amagi.douyin.fetcher.parseWork({
|
|
@@ -37615,7 +37647,7 @@ var DouYin = class DouYin extends Base {
|
|
|
37615
37647
|
filepath: filePath,
|
|
37616
37648
|
totalBytes: 0
|
|
37617
37649
|
});
|
|
37618
|
-
const videoPath = Config.
|
|
37650
|
+
const videoPath = Config.app.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
|
|
37619
37651
|
processedImages.push(segment.video(videoPath));
|
|
37620
37652
|
}
|
|
37621
37653
|
}
|
|
@@ -37632,7 +37664,7 @@ var DouYin = class DouYin extends Base {
|
|
|
37632
37664
|
filepath: motionPhotoCoverPath,
|
|
37633
37665
|
totalBytes: 0
|
|
37634
37666
|
});
|
|
37635
|
-
const motionPhotoCover = Config.
|
|
37667
|
+
const motionPhotoCover = Config.app.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
|
|
37636
37668
|
processedImages.push(segment.image(motionPhotoCover));
|
|
37637
37669
|
hasPushedMotionPhotoCover = true;
|
|
37638
37670
|
}
|
|
@@ -37770,7 +37802,7 @@ var DouYin = class DouYin extends Base {
|
|
|
37770
37802
|
filepath: filePath,
|
|
37771
37803
|
totalBytes: 0
|
|
37772
37804
|
});
|
|
37773
|
-
const videoPath = Config.
|
|
37805
|
+
const videoPath = Config.app.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
|
|
37774
37806
|
images.push(segment.video(videoPath));
|
|
37775
37807
|
}
|
|
37776
37808
|
}
|
|
@@ -37787,7 +37819,7 @@ var DouYin = class DouYin extends Base {
|
|
|
37787
37819
|
filepath: motionPhotoCoverPath,
|
|
37788
37820
|
totalBytes: 0
|
|
37789
37821
|
});
|
|
37790
|
-
const motionPhotoCover = Config.
|
|
37822
|
+
const motionPhotoCover = Config.app.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
|
|
37791
37823
|
images.push(segment.image(motionPhotoCover));
|
|
37792
37824
|
hasPushedMotionPhotoCover = true;
|
|
37793
37825
|
}
|
|
@@ -37941,7 +37973,9 @@ var DouYin = class DouYin extends Base {
|
|
|
37941
37973
|
else {
|
|
37942
37974
|
const suggest = [];
|
|
37943
37975
|
if (VideoData.data.aweme_detail?.suggest_words?.suggest_words) {
|
|
37944
|
-
for (const item of VideoData.data.aweme_detail.suggest_words.suggest_words) if (item.words && item.scene === "comment_top_rec")
|
|
37976
|
+
for (const item of VideoData.data.aweme_detail.suggest_words.suggest_words) if (item.words && item.scene === "comment_top_rec") {
|
|
37977
|
+
for (const v of item.words) if (v.word) suggest.push(v.word);
|
|
37978
|
+
}
|
|
37945
37979
|
}
|
|
37946
37980
|
const aweme = VideoData.data.aweme_detail;
|
|
37947
37981
|
const img = await Render(this.e, "douyin/comment", {
|
|
@@ -38024,7 +38058,7 @@ var DouYin = class DouYin extends Base {
|
|
|
38024
38058
|
await Common.removeFile(videoFile.filepath, true);
|
|
38025
38059
|
const stats = fs.statSync(filePath);
|
|
38026
38060
|
const fileSizeInMB = Number((stats.size / (1024 * 1024)).toFixed(2));
|
|
38027
|
-
if (fileSizeInMB > Config.
|
|
38061
|
+
if (fileSizeInMB > Config.app.groupfilevalue) await uploadFile(this.e, {
|
|
38028
38062
|
filepath: filePath,
|
|
38029
38063
|
totalBytes: fileSizeInMB,
|
|
38030
38064
|
originTitle: g_title || ""
|
|
@@ -38258,7 +38292,7 @@ var douyinProcessVideos = (videos, videoQuality, maxAutoVideoSize) => {
|
|
|
38258
38292
|
videos.sort((a, b) => b.play_addr.data_size - a.play_addr.data_size);
|
|
38259
38293
|
});
|
|
38260
38294
|
if (videoQuality === "adapt") {
|
|
38261
|
-
const sizeLimitBytes = (maxAutoVideoSize || Config.
|
|
38295
|
+
const sizeLimitBytes = (maxAutoVideoSize || Config.app.filelimit) * 1024 * 1024;
|
|
38262
38296
|
for (const quality of [
|
|
38263
38297
|
"4k",
|
|
38264
38298
|
"2k",
|
|
@@ -38538,7 +38572,7 @@ var getDouyinID = async (event, url, log = true) => {
|
|
|
38538
38572
|
logger.warn("无法获取作品ID");
|
|
38539
38573
|
break;
|
|
38540
38574
|
}
|
|
38541
|
-
log
|
|
38575
|
+
if (log) console.log(result);
|
|
38542
38576
|
return result;
|
|
38543
38577
|
};
|
|
38544
38578
|
//#endregion
|
|
@@ -39265,7 +39299,7 @@ async function renderLiveImage(options) {
|
|
|
39265
39299
|
var douyinBaseHeaders = {
|
|
39266
39300
|
...BASE_HEADERS,
|
|
39267
39301
|
Referer: "https://www.douyin.com",
|
|
39268
|
-
Cookie: Config.cookies.douyin
|
|
39302
|
+
Cookie: Config.amagi.cookies.douyin
|
|
39269
39303
|
};
|
|
39270
39304
|
var DouYinpush = class extends Base {
|
|
39271
39305
|
force = false;
|
|
@@ -39279,7 +39313,7 @@ var DouYinpush = class extends Base {
|
|
|
39279
39313
|
constructor(e = {}, force = false) {
|
|
39280
39314
|
super(e);
|
|
39281
39315
|
this.headers.Referer = "https://www.douyin.com";
|
|
39282
|
-
this.headers.Cookie = Config.cookies.douyin;
|
|
39316
|
+
this.headers.Cookie = Config.amagi.cookies.douyin;
|
|
39283
39317
|
this.force = force;
|
|
39284
39318
|
}
|
|
39285
39319
|
injectBotToEventForRender(targets) {
|
|
@@ -39299,7 +39333,7 @@ var DouYinpush = class extends Base {
|
|
|
39299
39333
|
const deletedCount = await cleanOldDynamicCache("douyin");
|
|
39300
39334
|
if (deletedCount > 0) logger.info(`已清理 ${deletedCount} 条过期的抖音作品缓存记录`);
|
|
39301
39335
|
if (await this.checkremark()) return true;
|
|
39302
|
-
this.ensureConfigFields(Config.pushlist.douyin);
|
|
39336
|
+
await this.ensureConfigFields(Config.pushlist.douyin);
|
|
39303
39337
|
const registeredBotIds = karin$1.getAllBotID();
|
|
39304
39338
|
const filteredPushList = this.filterPushListByRegisteredBots(Config.pushlist.douyin, registeredBotIds);
|
|
39305
39339
|
if (filteredPushList.length === 0) {
|
|
@@ -39315,10 +39349,30 @@ var DouYinpush = class extends Base {
|
|
|
39315
39349
|
* 检查并补全配置文件中缺失的字段
|
|
39316
39350
|
* @param pushList 推送配置列表
|
|
39317
39351
|
*/
|
|
39318
|
-
ensureConfigFields(pushList) {
|
|
39352
|
+
async ensureConfigFields(pushList) {
|
|
39319
39353
|
if (!pushList || pushList.length === 0) return;
|
|
39320
39354
|
let hasChanges = false;
|
|
39321
39355
|
for (const item of pushList) {
|
|
39356
|
+
if (!item.sec_uid && item.short_id) try {
|
|
39357
|
+
logger.info(`自动获取用户 ${item.remark || item.short_id} 的 sec_uid`);
|
|
39358
|
+
const searchResult = await this.amagi.douyin.fetcher.searchContent({
|
|
39359
|
+
query: item.short_id,
|
|
39360
|
+
type: "user",
|
|
39361
|
+
typeMode: "strict"
|
|
39362
|
+
});
|
|
39363
|
+
let matchedUser = null;
|
|
39364
|
+
for (const userItem of searchResult.data.user_list) if ((userItem.user_info.unique_id || userItem.user_info.short_id) === item.short_id) {
|
|
39365
|
+
matchedUser = userItem.user_info;
|
|
39366
|
+
break;
|
|
39367
|
+
}
|
|
39368
|
+
if (matchedUser?.sec_uid) {
|
|
39369
|
+
item.sec_uid = matchedUser.sec_uid;
|
|
39370
|
+
hasChanges = true;
|
|
39371
|
+
logger.info(`已为 ${item.remark || item.short_id} 补全 sec_uid: ${item.sec_uid}`);
|
|
39372
|
+
} else logger.warn(`无法获取用户 ${item.short_id} 的 sec_uid`);
|
|
39373
|
+
} catch (error) {
|
|
39374
|
+
logger.error(`获取 ${item.short_id} 的 sec_uid 失败: ${error}`);
|
|
39375
|
+
}
|
|
39322
39376
|
if (!item.pushTypes || item.pushTypes.length === 0) {
|
|
39323
39377
|
item.pushTypes = [
|
|
39324
39378
|
"post",
|
|
@@ -39402,7 +39456,7 @@ var DouYinpush = class extends Base {
|
|
|
39402
39456
|
`);
|
|
39403
39457
|
const Detail_Data = pushItem.Detail_Data;
|
|
39404
39458
|
const skip = await skipDynamic(pushItem);
|
|
39405
|
-
skip
|
|
39459
|
+
if (skip) logger.warn(`作品 https://www.douyin.com/video/${actualAwemeId} 已被处理,跳过`);
|
|
39406
39460
|
let img = [];
|
|
39407
39461
|
let iddata = { type: "one_work" };
|
|
39408
39462
|
this.injectBotToEventForRender(pushItem.targets);
|
|
@@ -39583,7 +39637,7 @@ var DouYinpush = class extends Base {
|
|
|
39583
39637
|
filepath: filePath,
|
|
39584
39638
|
totalBytes: 0
|
|
39585
39639
|
});
|
|
39586
|
-
const videoPath = Config.
|
|
39640
|
+
const videoPath = Config.app.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
|
|
39587
39641
|
images.push(segment.video(videoPath));
|
|
39588
39642
|
}
|
|
39589
39643
|
}
|
|
@@ -39600,7 +39654,7 @@ var DouYinpush = class extends Base {
|
|
|
39600
39654
|
filepath: motionPhotoCoverPath,
|
|
39601
39655
|
totalBytes: 0
|
|
39602
39656
|
});
|
|
39603
|
-
const motionPhotoCover = Config.
|
|
39657
|
+
const motionPhotoCover = Config.app.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
|
|
39604
39658
|
images.push(segment.image(motionPhotoCover));
|
|
39605
39659
|
hasPushedMotionPhotoCover = true;
|
|
39606
39660
|
}
|
|
@@ -39706,7 +39760,7 @@ var DouYinpush = class extends Base {
|
|
|
39706
39760
|
filepath: filePath,
|
|
39707
39761
|
totalBytes: 0
|
|
39708
39762
|
});
|
|
39709
|
-
const videoPath = Config.
|
|
39763
|
+
const videoPath = Config.app.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
|
|
39710
39764
|
processedImages.push(segment.video(videoPath));
|
|
39711
39765
|
}
|
|
39712
39766
|
}
|
|
@@ -39723,7 +39777,7 @@ var DouYinpush = class extends Base {
|
|
|
39723
39777
|
filepath: motionPhotoCoverPath,
|
|
39724
39778
|
totalBytes: 0
|
|
39725
39779
|
});
|
|
39726
|
-
const motionPhotoCover = Config.
|
|
39780
|
+
const motionPhotoCover = Config.app.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
|
|
39727
39781
|
processedImages.push(segment.image(motionPhotoCover));
|
|
39728
39782
|
hasPushedMotionPhotoCover = true;
|
|
39729
39783
|
}
|
|
@@ -39799,6 +39853,10 @@ var DouYinpush = class extends Base {
|
|
|
39799
39853
|
const filteredUserList = userList.filter((item) => item.switch !== false);
|
|
39800
39854
|
for (const item of filteredUserList) {
|
|
39801
39855
|
await common.sleep(2e3);
|
|
39856
|
+
if (!item.sec_uid) {
|
|
39857
|
+
logger.warn(`用户 ${item.remark || item.short_id} 缺少 sec_uid,跳过`);
|
|
39858
|
+
continue;
|
|
39859
|
+
}
|
|
39802
39860
|
const sec_uid = item.sec_uid;
|
|
39803
39861
|
const pushTypes = item.pushTypes || ["post"];
|
|
39804
39862
|
logger.debug(`开始获取用户:${item.remark}(${sec_uid})的内容,推送类型:${pushTypes.join(", ")}`);
|
|
@@ -39920,7 +39978,7 @@ var DouYinpush = class extends Base {
|
|
|
39920
39978
|
});
|
|
39921
39979
|
/** 处理抖音号 */
|
|
39922
39980
|
let user_shortid;
|
|
39923
|
-
UserInfoData.data.user.unique_id === "" ?
|
|
39981
|
+
user_shortid = UserInfoData.data.user.unique_id === "" ? UserInfoData.data.user.short_id : UserInfoData.data.user.unique_id;
|
|
39924
39982
|
config.douyin ??= [];
|
|
39925
39983
|
const existingItem = config.douyin.find((item) => item.sec_uid === sec_uid);
|
|
39926
39984
|
const isSubscribed = await douyinDBInstance.isSubscribed(sec_uid, groupId);
|
|
@@ -40210,7 +40268,7 @@ var getKuaishouID = async (url, log = true) => {
|
|
|
40210
40268
|
logger.warn("无法获取作品ID");
|
|
40211
40269
|
break;
|
|
40212
40270
|
}
|
|
40213
|
-
log
|
|
40271
|
+
if (log) console.log(result);
|
|
40214
40272
|
return result;
|
|
40215
40273
|
};
|
|
40216
40274
|
//#endregion
|
|
@@ -40229,7 +40287,7 @@ var Kuaishou = class extends Base {
|
|
|
40229
40287
|
await this.e.reply("不支持解析的视频");
|
|
40230
40288
|
return true;
|
|
40231
40289
|
}
|
|
40232
|
-
Config.app.parseTip
|
|
40290
|
+
if (Config.app.parseTip) this.e.reply("检测到快手链接,开始解析");
|
|
40233
40291
|
const video_url = data.VideoData.data.data.visionVideoDetail.photo.photoUrl;
|
|
40234
40292
|
const transformedData = Object.entries(data.EmojiData.data.data.visionBaseEmoticons.iconUrls).map(([name, path]) => {
|
|
40235
40293
|
return {
|
|
@@ -40346,7 +40404,7 @@ var getXiaohongshuID = async (url, log = true) => {
|
|
|
40346
40404
|
break;
|
|
40347
40405
|
}
|
|
40348
40406
|
if (result.type === "unknown") throw new Error("无法从链接中提取小红书笔记ID");
|
|
40349
|
-
log
|
|
40407
|
+
if (log) console.log(result);
|
|
40350
40408
|
return result;
|
|
40351
40409
|
};
|
|
40352
40410
|
//#endregion
|
|
@@ -40493,8 +40551,8 @@ var Xiaohongshu = class extends Base {
|
|
|
40493
40551
|
this.type = iddata?.type;
|
|
40494
40552
|
}
|
|
40495
40553
|
async XiaohongshuHandler(data) {
|
|
40496
|
-
if (Config.cookies.xiaohongshu === "") throw new Error("我还没有小红书的 Cookies,暂时无法解析呢 ~");
|
|
40497
|
-
Config.app.parseTip
|
|
40554
|
+
if (Config.amagi.cookies.xiaohongshu === "") throw new Error("我还没有小红书的 Cookies,暂时无法解析呢 ~");
|
|
40555
|
+
if (Config.app.parseTip) await this.e.reply("检测到小红书链接,开始解析");
|
|
40498
40556
|
const NoteData = await this.amagi.xiaohongshu.fetcher.fetchNoteDetail({
|
|
40499
40557
|
typeMode: "strict",
|
|
40500
40558
|
note_id: data.note_id,
|
|
@@ -40553,7 +40611,7 @@ var Xiaohongshu = class extends Base {
|
|
|
40553
40611
|
headers: {
|
|
40554
40612
|
...BASE_HEADERS,
|
|
40555
40613
|
Referer: "https://www.xiaohongshu.com",
|
|
40556
|
-
Cookie: Config.cookies.xiaohongshu
|
|
40614
|
+
Cookie: Config.amagi.cookies.xiaohongshu
|
|
40557
40615
|
}
|
|
40558
40616
|
});
|
|
40559
40617
|
let staticImgPath = "";
|
|
@@ -40567,7 +40625,7 @@ var Xiaohongshu = class extends Base {
|
|
|
40567
40625
|
headers: {
|
|
40568
40626
|
...BASE_HEADERS,
|
|
40569
40627
|
Referer: "https://www.xiaohongshu.com",
|
|
40570
|
-
Cookie: Config.cookies.xiaohongshu
|
|
40628
|
+
Cookie: Config.amagi.cookies.xiaohongshu
|
|
40571
40629
|
}
|
|
40572
40630
|
});
|
|
40573
40631
|
if (livePhoto.filepath) {
|
|
@@ -40594,7 +40652,7 @@ var Xiaohongshu = class extends Base {
|
|
|
40594
40652
|
filepath: filePath,
|
|
40595
40653
|
totalBytes: 0
|
|
40596
40654
|
});
|
|
40597
|
-
const videoPath = Config.
|
|
40655
|
+
const videoPath = Config.app.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
|
|
40598
40656
|
processedImages.push(segment.video(videoPath));
|
|
40599
40657
|
}
|
|
40600
40658
|
}
|
|
@@ -40611,7 +40669,7 @@ var Xiaohongshu = class extends Base {
|
|
|
40611
40669
|
filepath: motionPhotoCoverPath,
|
|
40612
40670
|
totalBytes: 0
|
|
40613
40671
|
});
|
|
40614
|
-
const motionPhotoCover = Config.
|
|
40672
|
+
const motionPhotoCover = Config.app.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
|
|
40615
40673
|
processedImages.push(segment.image(motionPhotoCover));
|
|
40616
40674
|
hasPushedMotionPhotoCover = true;
|
|
40617
40675
|
hasGeneratedLivePhoto = true;
|
|
@@ -40667,7 +40725,7 @@ var Xiaohongshu = class extends Base {
|
|
|
40667
40725
|
headers: {
|
|
40668
40726
|
...BASE_HEADERS,
|
|
40669
40727
|
Referer: "https://www.xiaohongshu.com",
|
|
40670
|
-
Cookie: Config.cookies.xiaohongshu
|
|
40728
|
+
Cookie: Config.amagi.cookies.xiaohongshu
|
|
40671
40729
|
}
|
|
40672
40730
|
}, { message_id: this.e.messageId });
|
|
40673
40731
|
else await this.e.reply(segment.video(video.url_default));
|
|
@@ -40741,7 +40799,7 @@ var xiaohongshuProcessVideos = (streamData, videoQuality, maxAutoVideoSize) => {
|
|
|
40741
40799
|
videos.sort((a, b) => b.size - a.size);
|
|
40742
40800
|
});
|
|
40743
40801
|
if (videoQuality === "adapt") {
|
|
40744
|
-
const sizeLimitBytes = (maxAutoVideoSize || Config.
|
|
40802
|
+
const sizeLimitBytes = (maxAutoVideoSize || Config.app.filelimit) * 1024 * 1024;
|
|
40745
40803
|
for (const quality of [
|
|
40746
40804
|
"4k",
|
|
40747
40805
|
"2k",
|
|
@@ -40986,9 +41044,9 @@ var douyinLogin = async (e) => {
|
|
|
40986
41044
|
if (!hasTtwid) logger.warn(" - 缺少 ttwid");
|
|
40987
41045
|
}
|
|
40988
41046
|
logger.debug("开始保存 cookies...");
|
|
40989
|
-
Config.Modify("
|
|
41047
|
+
Config.Modify("amagi", "cookies.douyin", cookieString);
|
|
40990
41048
|
logger.debug("cookies 保存完成");
|
|
40991
|
-
await e.reply("
|
|
41049
|
+
await e.reply("登录成功!用户登录凭证已保存至配置", { reply: true });
|
|
40992
41050
|
await Promise.all(msg_id.map(async (id) => {
|
|
40993
41051
|
await e.bot.recallMsg(e.contact, id);
|
|
40994
41052
|
}));
|
|
@@ -41194,7 +41252,7 @@ var handleCacheCleanup = wrapWithErrorHandler(async () => {
|
|
|
41194
41252
|
const twoHoursAgo = Date.now() - 7200 * 1e3;
|
|
41195
41253
|
const videoDeleted = removeOldFiles(Common.tempDri.video, twoHoursAgo);
|
|
41196
41254
|
logger.debug(`${Common.tempDri.video} 目录下已删除 ${videoDeleted} 个文件`);
|
|
41197
|
-
if (Config.
|
|
41255
|
+
if (Config.app.imageSendMode === "file") {
|
|
41198
41256
|
const imageDeleted = removeOldFiles(Common.tempDri.images, twoHoursAgo);
|
|
41199
41257
|
logger.debug(`${Common.tempDri.images} 目录下已删除 ${imageDeleted} 个文件`);
|
|
41200
41258
|
}
|
|
@@ -41463,7 +41521,7 @@ var handleSetBilibiliPush = wrapWithErrorHandler(async (e) => {
|
|
|
41463
41521
|
logger.info(`B站推送已${enable ? "开启" : "关闭"}`);
|
|
41464
41522
|
return true;
|
|
41465
41523
|
}
|
|
41466
|
-
if (!Config.cookies.bilibili) {
|
|
41524
|
+
if (!Config.amagi.cookies.bilibili) {
|
|
41467
41525
|
await e.reply("\n请先配置B站Cookie", { at: true });
|
|
41468
41526
|
return true;
|
|
41469
41527
|
}
|
|
@@ -42045,7 +42103,7 @@ var handleDouyin = wrapWithErrorHandler(async (e, next) => {
|
|
|
42045
42103
|
var handleBilibili = wrapWithErrorHandler(async (e, next) => {
|
|
42046
42104
|
e.msg = e.msg.replace(/\\/g, "");
|
|
42047
42105
|
const forceBurnDanmaku = /^#?弹幕解析/.test(e.msg);
|
|
42048
|
-
const urlRegex = /(https?:\/\/(?:(?:www\.|m\.|t\.)?bilibili\.com|b23\.tv|bili2233\.cn)\/[a-zA-Z0-9_
|
|
42106
|
+
const urlRegex = /(https?:\/\/(?:(?:www\.|m\.|t\.)?bilibili\.com|b23\.tv|bili2233\.cn)\/[a-zA-Z0-9_\-.~:/?#[\]@!$&'()*+,;=]+)/;
|
|
42049
42107
|
const bvRegex = /^BV[1-9a-zA-Z]{10}$/;
|
|
42050
42108
|
const avRegex = /^av\d+$/i;
|
|
42051
42109
|
let url = null;
|
|
@@ -42407,1091 +42465,40 @@ var update = karin$1.task("kkk-更新检测", "*/3 * * * *", Handler, {
|
|
|
42407
42465
|
log: false
|
|
42408
42466
|
});
|
|
42409
42467
|
//#endregion
|
|
42410
|
-
//#region src/
|
|
42411
|
-
|
|
42412
|
-
|
|
42413
|
-
|
|
42414
|
-
|
|
42415
|
-
/**
|
|
42416
|
-
|
|
42417
|
-
|
|
42418
|
-
|
|
42419
|
-
|
|
42420
|
-
|
|
42421
|
-
|
|
42422
|
-
|
|
42423
|
-
|
|
42424
|
-
|
|
42425
|
-
|
|
42426
|
-
|
|
42427
|
-
|
|
42428
|
-
|
|
42429
|
-
|
|
42430
|
-
|
|
42431
|
-
|
|
42432
|
-
|
|
42433
|
-
|
|
42434
|
-
|
|
42435
|
-
|
|
42436
|
-
|
|
42437
|
-
|
|
42438
|
-
|
|
42439
|
-
|
|
42440
|
-
|
|
42441
|
-
});
|
|
42442
|
-
avatarCache.set(mid, userProfile.data?.data?.card?.face || "");
|
|
42443
|
-
} catch (error) {
|
|
42444
|
-
if (error instanceof AmagiError && (error.code === -352 || error.code === -412)) {
|
|
42445
|
-
logger.warn(`[BilibiliAPI] 获取头像时遇到风控(${error.code})`);
|
|
42446
|
-
hitRiskControl = true;
|
|
42447
|
-
}
|
|
42448
|
-
avatarCache.set(mid, "");
|
|
42449
|
-
}
|
|
42450
|
-
}
|
|
42451
|
-
return createSuccessResponse(res, caches.map((cache) => {
|
|
42452
|
-
const authorName = cache.bilibiliUser?.remark || cache.host_mid.toString();
|
|
42453
|
-
return {
|
|
42454
|
-
id: cache.dynamic_id,
|
|
42455
|
-
platform: "bilibili",
|
|
42456
|
-
title: `B站动态 ${cache.dynamic_id}`,
|
|
42457
|
-
author: authorName,
|
|
42458
|
-
authorId: cache.host_mid.toString(),
|
|
42459
|
-
avatar: avatarCache.get(cache.host_mid) || "",
|
|
42460
|
-
thumbnail: "",
|
|
42461
|
-
type: "dynamic",
|
|
42462
|
-
dynamicType: cache.dynamic_type,
|
|
42463
|
-
createdAt: cache.createdAt.getTime()
|
|
42464
|
-
};
|
|
42465
|
-
}));
|
|
42466
|
-
} catch (error) {
|
|
42467
|
-
logger.error("[BilibiliAPI] 获取内容列表失败:", error);
|
|
42468
|
-
return createServerErrorResponse(res, "获取内容列表失败");
|
|
42469
|
-
}
|
|
42470
|
-
};
|
|
42471
|
-
/**
|
|
42472
|
-
* 添加B站内容
|
|
42473
|
-
* POST /api/v1/platforms/bilibili/contents
|
|
42474
|
-
* Body: { contentId, groupId, authorId }
|
|
42475
|
-
*/
|
|
42476
|
-
var addContent$1 = async (req, res) => {
|
|
42477
|
-
try {
|
|
42478
|
-
const { contentId, groupId, authorId } = req.body;
|
|
42479
|
-
if (!contentId || !groupId || !authorId) return createBadRequestResponse(res, "请提供 contentId、groupId 和 authorId");
|
|
42480
|
-
const bilibiliDB = await getBilibiliDB();
|
|
42481
|
-
const hostMid = parseInt(authorId);
|
|
42482
|
-
if (!await bilibiliDB.getBilibiliUser(hostMid)) return createBadRequestResponse(res, "该UP主未在订阅列表中,请先添加订阅");
|
|
42483
|
-
await bilibiliDB.addDynamicCache(contentId, hostMid, groupId, "manual");
|
|
42484
|
-
return createSuccessResponse(res, { message: "添加成功" });
|
|
42485
|
-
} catch (error) {
|
|
42486
|
-
logger.error("[BilibiliAPI] 添加内容失败:", error);
|
|
42487
|
-
return createServerErrorResponse(res, "添加内容失败");
|
|
42488
|
-
}
|
|
42489
|
-
};
|
|
42490
|
-
/**
|
|
42491
|
-
* 删除B站内容
|
|
42492
|
-
* POST /api/kkk/v1/platforms/bilibili/contents/:id/delete
|
|
42493
|
-
* Body: { groupId: string }
|
|
42494
|
-
*/
|
|
42495
|
-
var deleteContent$1 = async (req, res) => {
|
|
42496
|
-
try {
|
|
42497
|
-
const { id } = req.params;
|
|
42498
|
-
const { groupId } = req.body;
|
|
42499
|
-
if (!id || Array.isArray(id) && !id[0] || !groupId) return createBadRequestResponse(res, "请提供内容ID和群组ID");
|
|
42500
|
-
const result = await (await getBilibiliDB()).dynamicCacheRepository.delete({
|
|
42501
|
-
dynamic_id: Array.isArray(id) ? id[0] : id,
|
|
42502
|
-
groupId
|
|
42503
|
-
});
|
|
42504
|
-
if (result.affected === 0) return createBadRequestResponse(res, "未找到要删除的内容");
|
|
42505
|
-
return createSuccessResponse(res, {
|
|
42506
|
-
message: "删除成功",
|
|
42507
|
-
affected: result.affected
|
|
42508
|
-
});
|
|
42509
|
-
} catch (error) {
|
|
42510
|
-
logger.error("[BilibiliAPI] 删除内容失败:", error);
|
|
42511
|
-
return createServerErrorResponse(res, "删除内容失败");
|
|
42512
|
-
}
|
|
42513
|
-
};
|
|
42514
|
-
//#endregion
|
|
42515
|
-
//#region src/platform/bilibili/api/risk-control.ts
|
|
42516
|
-
/**
|
|
42517
|
-
* B站风控处理 API
|
|
42518
|
-
*/
|
|
42519
|
-
/**
|
|
42520
|
-
* 创建风控错误响应
|
|
42521
|
-
*/
|
|
42522
|
-
var createRiskControlResponse = (res, geetest, token, v_voucher) => {
|
|
42523
|
-
return res.status(452).json({
|
|
42524
|
-
message: "B站风控验证",
|
|
42525
|
-
code: -352,
|
|
42526
|
-
data: {
|
|
42527
|
-
type: "bilibili_risk_control",
|
|
42528
|
-
geetest,
|
|
42529
|
-
token,
|
|
42530
|
-
v_voucher
|
|
42531
|
-
}
|
|
42532
|
-
});
|
|
42533
|
-
};
|
|
42534
|
-
/**
|
|
42535
|
-
* 创建无法验证的风控错误响应
|
|
42536
|
-
*/
|
|
42537
|
-
var createRiskControlNoVoucherResponse = (res, code) => {
|
|
42538
|
-
return res.status(452).json({
|
|
42539
|
-
message: "B站风控",
|
|
42540
|
-
code,
|
|
42541
|
-
data: {
|
|
42542
|
-
type: "bilibili_risk_control_no_voucher",
|
|
42543
|
-
message: code === -352 ? "B站风控校验失败,请稍后重试或更换 Cookie" : "当前IP被B站风控,请稍后重试或更换网络"
|
|
42544
|
-
}
|
|
42545
|
-
});
|
|
42546
|
-
};
|
|
42547
|
-
/**
|
|
42548
|
-
* 处理 B站风控错误
|
|
42549
|
-
* 检测 -352 和 -412 错误并申请验证码
|
|
42550
|
-
*/
|
|
42551
|
-
var handleBilibiliRiskControl = async (error, res) => {
|
|
42552
|
-
if (!(error instanceof AmagiError)) return false;
|
|
42553
|
-
if (error.code !== -352 && error.code !== -412) return false;
|
|
42554
|
-
const v_voucher = error.data?.data?.v_voucher;
|
|
42555
|
-
if (!v_voucher) {
|
|
42556
|
-
logger.info(`[BilibiliAPI] 检测到风控(${error.code}),但无 v_voucher`);
|
|
42557
|
-
createRiskControlNoVoucherResponse(res, error.code);
|
|
42558
|
-
return true;
|
|
42559
|
-
}
|
|
42560
|
-
try {
|
|
42561
|
-
logger.info(`[BilibiliAPI] 检测到风控(${error.code}),申请验证码...`);
|
|
42562
|
-
const verification = await bilibiliFetcher.requestCaptchaFromVoucher({
|
|
42563
|
-
v_voucher,
|
|
42564
|
-
typeMode: "strict"
|
|
42565
|
-
});
|
|
42566
|
-
if (!verification.data?.data?.geetest) {
|
|
42567
|
-
logger.error("[BilibiliAPI] 申请验证码失败");
|
|
42568
|
-
createRiskControlNoVoucherResponse(res, error.code);
|
|
42569
|
-
return true;
|
|
42570
|
-
}
|
|
42571
|
-
const geetest = verification.data.data.geetest;
|
|
42572
|
-
const token = verification.data.data.token;
|
|
42573
|
-
createRiskControlResponse(res, {
|
|
42574
|
-
gt: geetest.gt,
|
|
42575
|
-
challenge: geetest.challenge
|
|
42576
|
-
}, token, v_voucher);
|
|
42577
|
-
return true;
|
|
42578
|
-
} catch (err) {
|
|
42579
|
-
logger.error("[BilibiliAPI] 申请验证码异常:", err);
|
|
42580
|
-
createRiskControlNoVoucherResponse(res, error.code);
|
|
42581
|
-
return true;
|
|
42582
|
-
}
|
|
42583
|
-
};
|
|
42584
|
-
/**
|
|
42585
|
-
* B站风控验证结果提交
|
|
42586
|
-
* POST /api/v1/platforms/bilibili/verify
|
|
42587
|
-
* Body: { challenge, token, validate, seccode }
|
|
42588
|
-
*/
|
|
42589
|
-
var verifyCaptcha = async (req, res) => {
|
|
42590
|
-
try {
|
|
42591
|
-
const { challenge, token, validate, seccode } = req.body;
|
|
42592
|
-
if (!challenge || !token || !validate || !seccode) return createBadRequestResponse(res, "验证参数不完整");
|
|
42593
|
-
logger.info("[BilibiliAPI] 提交风控验证结果...");
|
|
42594
|
-
const verifyResult = await bilibiliFetcher.validateCaptchaResult({
|
|
42595
|
-
challenge,
|
|
42596
|
-
token,
|
|
42597
|
-
validate,
|
|
42598
|
-
seccode,
|
|
42599
|
-
typeMode: "strict"
|
|
42600
|
-
});
|
|
42601
|
-
if (verifyResult.success && verifyResult.data?.data?.grisk_id) {
|
|
42602
|
-
logger.info(`[BilibiliAPI] 验证成功,grisk_id: ${verifyResult.data.data.grisk_id}`);
|
|
42603
|
-
return createSuccessResponse(res, {
|
|
42604
|
-
success: true,
|
|
42605
|
-
message: "验证成功",
|
|
42606
|
-
grisk_id: verifyResult.data.data.grisk_id
|
|
42607
|
-
});
|
|
42608
|
-
}
|
|
42609
|
-
return createSuccessResponse(res, {
|
|
42610
|
-
success: false,
|
|
42611
|
-
message: "验证失败,请重试"
|
|
42612
|
-
});
|
|
42613
|
-
} catch (error) {
|
|
42614
|
-
logger.error("[BilibiliAPI] 验证请求失败:", error);
|
|
42615
|
-
if (error instanceof AmagiError) {
|
|
42616
|
-
if (error.code === -111) return createSuccessResponse(res, {
|
|
42617
|
-
success: false,
|
|
42618
|
-
message: "验证失败,建议重新配置 B站 Cookie"
|
|
42619
|
-
});
|
|
42620
|
-
return createSuccessResponse(res, {
|
|
42621
|
-
success: false,
|
|
42622
|
-
message: error.rawError?.errorDescription || "验证失败"
|
|
42623
|
-
});
|
|
42624
|
-
}
|
|
42625
|
-
return createServerErrorResponse(res, "验证请求失败");
|
|
42626
|
-
}
|
|
42627
|
-
};
|
|
42628
|
-
//#endregion
|
|
42629
|
-
//#region src/platform/bilibili/api/parse.ts
|
|
42630
|
-
/**
|
|
42631
|
-
* 判断是否为图文动态
|
|
42632
|
-
*/
|
|
42633
|
-
function isDynamicTypeDraw(data) {
|
|
42634
|
-
return data.data.item.type === DynamicType.DRAW;
|
|
42635
|
-
}
|
|
42636
|
-
/**
|
|
42637
|
-
* 判断是否为文字动态
|
|
42638
|
-
*/
|
|
42639
|
-
function isDynamicTypeWord(data) {
|
|
42640
|
-
return data.data.item.type === DynamicType.WORD;
|
|
42641
|
-
}
|
|
42642
|
-
/**
|
|
42643
|
-
* 判断是否为视频动态
|
|
42644
|
-
*/
|
|
42645
|
-
function isDynamicTypeAV(data) {
|
|
42646
|
-
return data.data.item.type === DynamicType.AV;
|
|
42647
|
-
}
|
|
42648
|
-
/**
|
|
42649
|
-
* 判断是否为转发动态
|
|
42650
|
-
*/
|
|
42651
|
-
function isDynamicTypeForward(data) {
|
|
42652
|
-
return data.data.item.type === DynamicType.FORWARD;
|
|
42653
|
-
}
|
|
42654
|
-
/**
|
|
42655
|
-
* 判断是否为专栏文章动态
|
|
42656
|
-
*/
|
|
42657
|
-
function isDynamicTypeArticle(data) {
|
|
42658
|
-
return data.data.item.type === DynamicType.ARTICLE;
|
|
42659
|
-
}
|
|
42660
|
-
/**
|
|
42661
|
-
* 判断是否为直播推荐动态
|
|
42662
|
-
*/
|
|
42663
|
-
function isDynamicTypeLiveRcmd(data) {
|
|
42664
|
-
return data.data.item.type === DynamicType.LIVE_RCMD;
|
|
42665
|
-
}
|
|
42666
|
-
/**
|
|
42667
|
-
* 格式化数字
|
|
42668
|
-
*/
|
|
42669
|
-
var formatCount$1 = (count) => {
|
|
42670
|
-
if (count >= 1e8) return `${(count / 1e8).toFixed(1)}亿`;
|
|
42671
|
-
if (count >= 1e4) return `${(count / 1e4).toFixed(1)}万`;
|
|
42672
|
-
return count.toString();
|
|
42673
|
-
};
|
|
42674
|
-
/**
|
|
42675
|
-
* 格式化时长
|
|
42676
|
-
*/
|
|
42677
|
-
var formatDuration$1 = (seconds) => {
|
|
42678
|
-
return `${Math.floor(seconds / 60)}:${Math.floor(seconds % 60).toString().padStart(2, "0")}`;
|
|
42679
|
-
};
|
|
42680
|
-
/**
|
|
42681
|
-
* 格式化时间戳
|
|
42682
|
-
*/
|
|
42683
|
-
var formatTimestamp$1 = (timestamp) => {
|
|
42684
|
-
const date = /* @__PURE__ */ new Date(timestamp * 1e3);
|
|
42685
|
-
const diff = (/* @__PURE__ */ new Date()).getTime() - date.getTime();
|
|
42686
|
-
if (diff < 6e4) return "刚刚";
|
|
42687
|
-
if (diff < 36e5) return `${Math.floor(diff / 6e4)}分钟前`;
|
|
42688
|
-
if (diff < 864e5) return `${Math.floor(diff / 36e5)}小时前`;
|
|
42689
|
-
if (diff < 2592e6) return `${Math.floor(diff / 864e5)}天前`;
|
|
42690
|
-
return `${date.getMonth() + 1}月${date.getDate()}日`;
|
|
42691
|
-
};
|
|
42692
|
-
/**
|
|
42693
|
-
* 处理评论中的表情
|
|
42694
|
-
*/
|
|
42695
|
-
var processCommentEmojis$1 = (text, emojiData) => {
|
|
42696
|
-
if (!text || !emojiData?.data?.packages) return text;
|
|
42697
|
-
const emojiMap = /* @__PURE__ */ new Map();
|
|
42698
|
-
emojiData.data.packages.forEach((pkg) => {
|
|
42699
|
-
pkg.emote.forEach((emote) => {
|
|
42700
|
-
emojiMap.set(emote.text, emote.url);
|
|
42701
|
-
});
|
|
42702
|
-
});
|
|
42703
|
-
let processedText = text;
|
|
42704
|
-
processedText = processedText.replace(/\[([^\]]+)\]/g, (match, emojiName) => {
|
|
42705
|
-
const emojiUrl = emojiMap.get(match) || emojiMap.get(emojiName);
|
|
42706
|
-
if (emojiUrl) return `<img src="${emojiUrl}" alt="${emojiName}" class="emoji" />`;
|
|
42707
|
-
return match;
|
|
42708
|
-
});
|
|
42709
|
-
return processedText.split(/(<img[^>]*>)/).map((part) => {
|
|
42710
|
-
if (part.startsWith("<img")) return part;
|
|
42711
|
-
if (part.trim()) return `<span>${part}</span>`;
|
|
42712
|
-
return part;
|
|
42713
|
-
}).join("");
|
|
42714
|
-
};
|
|
42715
|
-
/**
|
|
42716
|
-
* 解析评论数据
|
|
42717
|
-
*/
|
|
42718
|
-
var parseComments$1 = (commentsData, emojiData) => {
|
|
42719
|
-
if (!commentsData || !Array.isArray(commentsData)) return [];
|
|
42720
|
-
return commentsData.map((comment, index) => {
|
|
42721
|
-
let processedContent = comment.content?.message || "";
|
|
42722
|
-
if (emojiData && processedContent) processedContent = processCommentEmojis$1(processedContent, emojiData);
|
|
42723
|
-
const pictures = comment.content?.pictures;
|
|
42724
|
-
return {
|
|
42725
|
-
id: comment.rpid?.toString() || index.toString(),
|
|
42726
|
-
author: comment.member?.uname || "匿名用户",
|
|
42727
|
-
avatar: comment.member?.avatar || "",
|
|
42728
|
-
content: processedContent,
|
|
42729
|
-
images: pictures?.map((pic) => pic.img_src) || [],
|
|
42730
|
-
likes: comment.like || 0,
|
|
42731
|
-
timestamp: formatTimestamp$1(comment.ctime || 0)
|
|
42732
|
-
};
|
|
42733
|
-
});
|
|
42734
|
-
};
|
|
42735
|
-
/**
|
|
42736
|
-
* 获取动态评论类型
|
|
42737
|
-
* 参考 mapping_table 函数的映射关系
|
|
42738
|
-
*/
|
|
42739
|
-
var getDynamicCommentType = (dynamicType) => {
|
|
42740
|
-
switch (dynamicType) {
|
|
42741
|
-
case DynamicType.AV: return 1;
|
|
42742
|
-
case DynamicType.DRAW: return 11;
|
|
42743
|
-
case DynamicType.ARTICLE: return 12;
|
|
42744
|
-
case DynamicType.LIVE_RCMD:
|
|
42745
|
-
case DynamicType.FORWARD:
|
|
42746
|
-
case DynamicType.WORD:
|
|
42747
|
-
default: return 17;
|
|
42748
|
-
}
|
|
42749
|
-
};
|
|
42750
|
-
/**
|
|
42751
|
-
* 获取动态 OID(用于评论接口)
|
|
42752
|
-
*/
|
|
42753
|
-
var getDynamicOid = (dynamicInfo, dynamicCard) => {
|
|
42754
|
-
const item = dynamicInfo.data.item;
|
|
42755
|
-
if (isDynamicTypeAV(dynamicInfo)) return (dynamicInfo.data.item.modules.module_dynamic.major?.archive)?.aid?.toString() || item.id_str;
|
|
42756
|
-
if (isDynamicTypeWord(dynamicInfo) || isDynamicTypeForward(dynamicInfo)) return item.id_str;
|
|
42757
|
-
return dynamicCard.card?.desc?.rid?.toString() || item.id_str;
|
|
42758
|
-
};
|
|
42759
|
-
/**
|
|
42760
|
-
* 解析图文动态内容
|
|
42761
|
-
*/
|
|
42762
|
-
var parseDrawDynamic = (data) => {
|
|
42763
|
-
const moduleDynamic = data.data.item.modules.module_dynamic;
|
|
42764
|
-
const description = moduleDynamic.major?.opus?.summary?.text || "";
|
|
42765
|
-
const images = (moduleDynamic.major?.opus?.pics || []).map((pic) => pic.url).filter((url) => typeof url === "string");
|
|
42766
|
-
return {
|
|
42767
|
-
title: "图文动态",
|
|
42768
|
-
description,
|
|
42769
|
-
thumbnail: images[0] || "",
|
|
42770
|
-
images,
|
|
42771
|
-
type: "note"
|
|
42772
|
-
};
|
|
42773
|
-
};
|
|
42774
|
-
/**
|
|
42775
|
-
* 解析文字动态内容
|
|
42776
|
-
*/
|
|
42777
|
-
var parseWordDynamic = (data) => {
|
|
42778
|
-
return {
|
|
42779
|
-
title: "文字动态",
|
|
42780
|
-
description: data.data.item.modules.module_dynamic.major?.opus?.summary?.text || "",
|
|
42781
|
-
thumbnail: "",
|
|
42782
|
-
images: [],
|
|
42783
|
-
type: "note"
|
|
42784
|
-
};
|
|
42785
|
-
};
|
|
42786
|
-
/**
|
|
42787
|
-
* 解析视频动态内容
|
|
42788
|
-
*/
|
|
42789
|
-
var parseAVDynamic = (data) => {
|
|
42790
|
-
const archive = data.data.item.modules.module_dynamic.major?.archive;
|
|
42791
|
-
return {
|
|
42792
|
-
title: archive?.title || "视频动态",
|
|
42793
|
-
description: archive?.desc || "",
|
|
42794
|
-
thumbnail: archive?.cover || "",
|
|
42795
|
-
images: [],
|
|
42796
|
-
type: "video"
|
|
42797
|
-
};
|
|
42798
|
-
};
|
|
42799
|
-
/**
|
|
42800
|
-
* 解析转发动态内容
|
|
42801
|
-
*/
|
|
42802
|
-
var parseForwardDynamic = (data) => {
|
|
42803
|
-
return {
|
|
42804
|
-
title: "转发动态",
|
|
42805
|
-
description: data.data.item.modules.module_dynamic.desc?.text || "",
|
|
42806
|
-
thumbnail: "",
|
|
42807
|
-
images: [],
|
|
42808
|
-
type: "forward"
|
|
42809
|
-
};
|
|
42810
|
-
};
|
|
42811
|
-
/**
|
|
42812
|
-
* 解析专栏文章动态内容
|
|
42813
|
-
*/
|
|
42814
|
-
var parseArticleDynamic = (data) => {
|
|
42815
|
-
const opus = data.data.item.modules.module_dynamic.major?.opus;
|
|
42816
|
-
return {
|
|
42817
|
-
title: opus?.title || "专栏文章",
|
|
42818
|
-
description: opus?.summary?.text || "",
|
|
42819
|
-
thumbnail: "",
|
|
42820
|
-
images: [],
|
|
42821
|
-
type: "article"
|
|
42822
|
-
};
|
|
42823
|
-
};
|
|
42824
|
-
/**
|
|
42825
|
-
* 解析直播推荐动态内容
|
|
42826
|
-
*/
|
|
42827
|
-
var parseLiveRcmdDynamic = (data) => {
|
|
42828
|
-
const liveRcmd = data.data.item.modules.module_dynamic.major?.live_rcmd;
|
|
42829
|
-
let title = "直播推荐";
|
|
42830
|
-
let thumbnail = "";
|
|
42831
|
-
if (liveRcmd?.content) try {
|
|
42832
|
-
const liveInfo = JSON.parse(liveRcmd.content);
|
|
42833
|
-
title = liveInfo.live_play_info?.title || "直播推荐";
|
|
42834
|
-
thumbnail = liveInfo.live_play_info?.cover || "";
|
|
42835
|
-
} catch {}
|
|
42836
|
-
return {
|
|
42837
|
-
title,
|
|
42838
|
-
description: "",
|
|
42839
|
-
thumbnail,
|
|
42840
|
-
images: [],
|
|
42841
|
-
type: "live"
|
|
42842
|
-
};
|
|
42843
|
-
};
|
|
42844
|
-
/**
|
|
42845
|
-
* 解析动态内容(主入口)
|
|
42846
|
-
* 使用类型守卫进行类型安全的分发
|
|
42847
|
-
*/
|
|
42848
|
-
var parseDynamicContent = (dynamicInfo) => {
|
|
42849
|
-
if (isDynamicTypeDraw(dynamicInfo)) return parseDrawDynamic(dynamicInfo);
|
|
42850
|
-
if (isDynamicTypeWord(dynamicInfo)) return parseWordDynamic(dynamicInfo);
|
|
42851
|
-
if (isDynamicTypeAV(dynamicInfo)) return parseAVDynamic(dynamicInfo);
|
|
42852
|
-
if (isDynamicTypeForward(dynamicInfo)) return parseForwardDynamic(dynamicInfo);
|
|
42853
|
-
if (isDynamicTypeArticle(dynamicInfo)) return parseArticleDynamic(dynamicInfo);
|
|
42854
|
-
if (isDynamicTypeLiveRcmd(dynamicInfo)) return parseLiveRcmdDynamic(dynamicInfo);
|
|
42855
|
-
return {
|
|
42856
|
-
title: "动态内容",
|
|
42857
|
-
description: "",
|
|
42858
|
-
thumbnail: "",
|
|
42859
|
-
images: [],
|
|
42860
|
-
type: "dynamic"
|
|
42861
|
-
};
|
|
42862
|
-
};
|
|
42863
|
-
/**
|
|
42864
|
-
* 获取动态作者信息
|
|
42865
|
-
*/
|
|
42866
|
-
var getDynamicAuthor = (dynamicInfo) => {
|
|
42867
|
-
const moduleAuthor = dynamicInfo.data.item.modules.module_author;
|
|
42868
|
-
return {
|
|
42869
|
-
name: moduleAuthor.name || "未知用户",
|
|
42870
|
-
avatar: moduleAuthor.face || "",
|
|
42871
|
-
id: moduleAuthor.mid?.toString() || ""
|
|
42872
|
-
};
|
|
42873
|
-
};
|
|
42874
|
-
/**
|
|
42875
|
-
* 获取动态统计信息
|
|
42876
|
-
*/
|
|
42877
|
-
var getDynamicStats = (dynamicInfo) => {
|
|
42878
|
-
const moduleStat = dynamicInfo.data.item.modules.module_stat;
|
|
42879
|
-
return {
|
|
42880
|
-
views: formatCount$1(moduleStat.forward?.count || 0),
|
|
42881
|
-
likes: formatCount$1(moduleStat.like?.count || 0),
|
|
42882
|
-
commentCount: moduleStat.comment?.count || 0
|
|
42883
|
-
};
|
|
42884
|
-
};
|
|
42885
|
-
/**
|
|
42886
|
-
* 解析B站视频
|
|
42887
|
-
* POST /api/kkk/v1/platforms/bilibili/parse/video
|
|
42888
|
-
* Body: { bvid?: string, aid?: string }
|
|
42889
|
-
*/
|
|
42890
|
-
var parseVideo = async (req, res) => {
|
|
42891
|
-
try {
|
|
42892
|
-
const { bvid, aid } = req.body;
|
|
42893
|
-
if (!bvid && !aid) return createBadRequestResponse(res, "请提供 bvid 或 aid");
|
|
42894
|
-
const videoDetail = (await bilibiliFetcher.fetchVideoInfo({
|
|
42895
|
-
bvid: bvid || "",
|
|
42896
|
-
typeMode: "strict"
|
|
42897
|
-
})).data.data;
|
|
42898
|
-
const streamData = (await bilibiliFetcher.fetchVideoStreamUrl({
|
|
42899
|
-
avid: videoDetail.aid,
|
|
42900
|
-
cid: videoDetail.cid,
|
|
42901
|
-
typeMode: "strict"
|
|
42902
|
-
})).data.data;
|
|
42903
|
-
const [commentsResponse, emojiResponse] = await Promise.all([bilibiliFetcher.fetchComments({
|
|
42904
|
-
oid: videoDetail.aid.toString(),
|
|
42905
|
-
type: 1,
|
|
42906
|
-
number: 50,
|
|
42907
|
-
typeMode: "strict"
|
|
42908
|
-
}), bilibiliFetcher.fetchEmojiList({ typeMode: "strict" })]);
|
|
42909
|
-
const commentsData = commentsResponse.data.data;
|
|
42910
|
-
const emojiData = emojiResponse.data;
|
|
42911
|
-
const comments = parseComments$1(commentsData.replies || [], emojiData);
|
|
42912
|
-
return createSuccessResponse(res, {
|
|
42913
|
-
id: videoDetail.bvid,
|
|
42914
|
-
title: videoDetail.title || "无标题",
|
|
42915
|
-
description: videoDetail.desc || "",
|
|
42916
|
-
thumbnail: videoDetail.pic || "",
|
|
42917
|
-
duration: formatDuration$1(videoDetail.duration || 0),
|
|
42918
|
-
views: formatCount$1(videoDetail.stat?.view || 0),
|
|
42919
|
-
likes: formatCount$1(videoDetail.stat?.like || 0),
|
|
42920
|
-
author: {
|
|
42921
|
-
name: videoDetail.owner?.name || "未知用户",
|
|
42922
|
-
avatar: videoDetail.owner?.face || "",
|
|
42923
|
-
id: videoDetail.owner?.mid?.toString() || ""
|
|
42924
|
-
},
|
|
42925
|
-
type: "video",
|
|
42926
|
-
downloadUrl: {
|
|
42927
|
-
video: streamData.dash?.video?.[0]?.baseUrl,
|
|
42928
|
-
audio: streamData.dash?.audio?.[0]?.baseUrl
|
|
42929
|
-
},
|
|
42930
|
-
tags: videoDetail.tname ? [videoDetail.tname] : [],
|
|
42931
|
-
comments,
|
|
42932
|
-
commentCount: videoDetail.stat?.reply || 0
|
|
42933
|
-
});
|
|
42934
|
-
} catch (error) {
|
|
42935
|
-
const err = error;
|
|
42936
|
-
if (await handleBilibiliRiskControl(err, res)) return;
|
|
42937
|
-
logger.error("[BilibiliAPI] 解析视频失败:", err);
|
|
42938
|
-
return createServerErrorResponse(res, `解析失败: ${err.message}`);
|
|
42939
|
-
}
|
|
42940
|
-
};
|
|
42941
|
-
/**
|
|
42942
|
-
* 解析B站动态(返回原始数据)
|
|
42943
|
-
* POST /api/kkk/v1/platforms/bilibili/parse/dynamic/raw
|
|
42944
|
-
* Body: { dynamic_id: string }
|
|
42945
|
-
*
|
|
42946
|
-
* 返回原始 API 数据,前端自行处理不同动态类型
|
|
42947
|
-
* 注意:不获取评论数据,减少请求开销
|
|
42948
|
-
*/
|
|
42949
|
-
var parseDynamicRaw = async (req, res) => {
|
|
42950
|
-
try {
|
|
42951
|
-
const { dynamic_id } = req.body;
|
|
42952
|
-
if (!dynamic_id) return createBadRequestResponse(res, "请提供动态ID (dynamic_id)");
|
|
42953
|
-
const dynamicInfo = (await bilibiliFetcher.fetchDynamicDetail({
|
|
42954
|
-
dynamic_id,
|
|
42955
|
-
typeMode: "strict"
|
|
42956
|
-
})).data;
|
|
42957
|
-
const [dynamicCardResponse, userProfileResponse] = await Promise.all([bilibiliFetcher.fetchDynamicCard({
|
|
42958
|
-
dynamic_id: dynamicInfo.data.item.id_str,
|
|
42959
|
-
typeMode: "strict"
|
|
42960
|
-
}), bilibiliFetcher.fetchUserCard({
|
|
42961
|
-
host_mid: dynamicInfo.data.item.modules.module_author.mid,
|
|
42962
|
-
typeMode: "strict"
|
|
42963
|
-
})]);
|
|
42964
|
-
const dynamicCard = dynamicCardResponse.data.data;
|
|
42965
|
-
return createSuccessResponse(res, {
|
|
42966
|
-
dynamicInfo: dynamicInfo.data,
|
|
42967
|
-
dynamicCard,
|
|
42968
|
-
userProfile: userProfileResponse.data.data,
|
|
42969
|
-
comments: null,
|
|
42970
|
-
emoji: null
|
|
42971
|
-
});
|
|
42972
|
-
} catch (error) {
|
|
42973
|
-
const err = error;
|
|
42974
|
-
if (await handleBilibiliRiskControl(err, res)) return;
|
|
42975
|
-
logger.error("[BilibiliAPI] 解析动态失败:", err);
|
|
42976
|
-
return createServerErrorResponse(res, `解析失败: ${err.message}`);
|
|
42977
|
-
}
|
|
42978
|
-
};
|
|
42979
|
-
/**
|
|
42980
|
-
* 解析B站动态
|
|
42981
|
-
* POST /api/kkk/v1/platforms/bilibili/parse/dynamic
|
|
42982
|
-
* Body: { dynamic_id: string }
|
|
42983
|
-
*/
|
|
42984
|
-
var parseDynamic = async (req, res) => {
|
|
42985
|
-
try {
|
|
42986
|
-
const { dynamic_id } = req.body;
|
|
42987
|
-
if (!dynamic_id) return createBadRequestResponse(res, "请提供动态ID (dynamic_id)");
|
|
42988
|
-
const dynamicInfo = (await bilibiliFetcher.fetchDynamicDetail({
|
|
42989
|
-
dynamic_id,
|
|
42990
|
-
typeMode: "strict"
|
|
42991
|
-
})).data;
|
|
42992
|
-
const dynamicCard = (await bilibiliFetcher.fetchDynamicCard({
|
|
42993
|
-
dynamic_id: dynamicInfo.data.item.id_str,
|
|
42994
|
-
typeMode: "strict"
|
|
42995
|
-
})).data.data;
|
|
42996
|
-
const dynamicContent = parseDynamicContent(dynamicInfo);
|
|
42997
|
-
const author = getDynamicAuthor(dynamicInfo);
|
|
42998
|
-
const stats = getDynamicStats(dynamicInfo);
|
|
42999
|
-
let comments = [];
|
|
43000
|
-
const itemType = dynamicInfo.data.item.type;
|
|
43001
|
-
if (itemType !== DynamicType.LIVE_RCMD) try {
|
|
43002
|
-
const [commentsResponse, emojiResponse] = await Promise.all([bilibiliFetcher.fetchComments({
|
|
43003
|
-
type: getDynamicCommentType(itemType),
|
|
43004
|
-
oid: getDynamicOid(dynamicInfo, dynamicCard),
|
|
43005
|
-
typeMode: "strict"
|
|
43006
|
-
}), bilibiliFetcher.fetchEmojiList({ typeMode: "strict" })]);
|
|
43007
|
-
const commentsData = commentsResponse.data.data;
|
|
43008
|
-
const emojiData = emojiResponse.data;
|
|
43009
|
-
comments = parseComments$1(commentsData.replies || [], emojiData);
|
|
43010
|
-
} catch (error) {
|
|
43011
|
-
logger.warn("[BilibiliAPI] 获取动态评论失败:", error);
|
|
43012
|
-
}
|
|
43013
|
-
return createSuccessResponse(res, {
|
|
43014
|
-
id: dynamicInfo.data.item.id_str,
|
|
43015
|
-
title: dynamicContent.title,
|
|
43016
|
-
description: dynamicContent.description,
|
|
43017
|
-
thumbnail: dynamicContent.thumbnail || author.avatar,
|
|
43018
|
-
duration: "0:00",
|
|
43019
|
-
views: stats.views,
|
|
43020
|
-
likes: stats.likes,
|
|
43021
|
-
author,
|
|
43022
|
-
type: dynamicContent.type,
|
|
43023
|
-
dynamicType: itemType,
|
|
43024
|
-
images: dynamicContent.images,
|
|
43025
|
-
tags: [],
|
|
43026
|
-
comments,
|
|
43027
|
-
commentCount: stats.commentCount
|
|
43028
|
-
});
|
|
43029
|
-
} catch (error) {
|
|
43030
|
-
const err = error;
|
|
43031
|
-
if (await handleBilibiliRiskControl(err, res)) return;
|
|
43032
|
-
logger.error("[BilibiliAPI] 解析动态失败:", err);
|
|
43033
|
-
return createServerErrorResponse(res, `解析失败: ${err.message}`);
|
|
43034
|
-
}
|
|
43035
|
-
};
|
|
43036
|
-
//#endregion
|
|
43037
|
-
//#region src/platform/bilibili/api/video.ts
|
|
43038
|
-
var QUALITY_MAP = {
|
|
43039
|
-
6: "240P 极速",
|
|
43040
|
-
16: "360P 流畅",
|
|
43041
|
-
32: "480P 清晰",
|
|
43042
|
-
64: "720P 高清",
|
|
43043
|
-
74: "720P60 高帧率",
|
|
43044
|
-
80: "1080P 高清",
|
|
43045
|
-
112: "1080P+ 高码率",
|
|
43046
|
-
116: "1080P60 高帧率",
|
|
43047
|
-
120: "4K 超清",
|
|
43048
|
-
125: "HDR 真彩色",
|
|
43049
|
-
126: "杜比视界",
|
|
43050
|
-
127: "8K 超高清"
|
|
43051
|
-
};
|
|
43052
|
-
/**
|
|
43053
|
-
* 获取视频播放地址
|
|
43054
|
-
* GET /api/kkk/v1/platforms/bilibili/video/playurl?bvid=xxx&p=1
|
|
43055
|
-
*
|
|
43056
|
-
* 返回视频播放地址,前端直接使用这些 URL 播放
|
|
43057
|
-
* - DASH 格式:返回视频流和音频流列表,前端使用支持 DASH 的播放器
|
|
43058
|
-
* - DURL 格式:返回合并好的单流地址,可直接播放
|
|
43059
|
-
*/
|
|
43060
|
-
var getVideoPlayUrl = async (req, res) => {
|
|
43061
|
-
try {
|
|
43062
|
-
const { bvid, p } = req.query;
|
|
43063
|
-
if (!bvid) return createBadRequestResponse(res, "请提供视频 BV 号 (bvid)");
|
|
43064
|
-
const pageNum = p ? parseInt(p, 10) : 1;
|
|
43065
|
-
const videoInfo = (await bilibiliFetcher.fetchVideoInfo({
|
|
43066
|
-
bvid,
|
|
43067
|
-
typeMode: "strict"
|
|
43068
|
-
})).data.data;
|
|
43069
|
-
const cid = pageNum > 1 && videoInfo.pages[pageNum - 1] ? videoInfo.pages[pageNum - 1].cid : videoInfo.cid;
|
|
43070
|
-
const playUrlData = (await bilibiliFetcher.fetchVideoStreamUrl({
|
|
43071
|
-
avid: videoInfo.aid,
|
|
43072
|
-
cid,
|
|
43073
|
-
typeMode: "strict"
|
|
43074
|
-
})).data.data;
|
|
43075
|
-
const response = {
|
|
43076
|
-
bvid: videoInfo.bvid,
|
|
43077
|
-
aid: videoInfo.aid,
|
|
43078
|
-
cid,
|
|
43079
|
-
title: videoInfo.title,
|
|
43080
|
-
duration: videoInfo.duration,
|
|
43081
|
-
cover: videoInfo.pic
|
|
43082
|
-
};
|
|
43083
|
-
if (playUrlData.dash) {
|
|
43084
|
-
response.streamType = "dash";
|
|
43085
|
-
const videoMap = /* @__PURE__ */ new Map();
|
|
43086
|
-
for (const video of playUrlData.dash.video) if (!videoMap.has(video.id)) videoMap.set(video.id, {
|
|
43087
|
-
url: video.baseUrl || video.base_url,
|
|
43088
|
-
quality: video.id,
|
|
43089
|
-
qualityDesc: QUALITY_MAP[video.id] || `${video.id}P`,
|
|
43090
|
-
codecs: video.codecs,
|
|
43091
|
-
width: video.width,
|
|
43092
|
-
height: video.height,
|
|
43093
|
-
bandwidth: video.bandwidth
|
|
43094
|
-
});
|
|
43095
|
-
response.videoStreams = Array.from(videoMap.values()).sort((a, b) => b.quality - a.quality);
|
|
43096
|
-
response.audioStreams = playUrlData.dash.audio.map((audio) => ({
|
|
43097
|
-
url: audio.baseUrl || audio.base_url,
|
|
43098
|
-
quality: audio.id,
|
|
43099
|
-
bandwidth: audio.bandwidth
|
|
43100
|
-
})).sort((a, b) => b.bandwidth - a.bandwidth);
|
|
43101
|
-
} else if (playUrlData.durl && playUrlData.durl.length > 0) {
|
|
43102
|
-
response.streamType = "durl";
|
|
43103
|
-
response.durlUrl = playUrlData.durl[0].url;
|
|
43104
|
-
response.durlQuality = playUrlData.accept_description?.[0] || "默认清晰度";
|
|
43105
|
-
} else return createServerErrorResponse(res, "无法获取视频播放地址");
|
|
43106
|
-
return createSuccessResponse(res, response);
|
|
43107
|
-
} catch (error) {
|
|
43108
|
-
const err = error;
|
|
43109
|
-
if (await handleBilibiliRiskControl(err, res)) return;
|
|
43110
|
-
logger.error("[BilibiliAPI] 获取视频播放地址失败:", err);
|
|
43111
|
-
return createServerErrorResponse(res, `获取播放地址失败: ${err.message}`);
|
|
43112
|
-
}
|
|
43113
|
-
};
|
|
43114
|
-
//#endregion
|
|
43115
|
-
//#region src/platform/bilibili/api/index.ts
|
|
43116
|
-
/**
|
|
43117
|
-
* B站平台 API 路由
|
|
43118
|
-
*/
|
|
43119
|
-
var router$1 = express.Router();
|
|
43120
|
-
router$1.get("/contents", getContents$1);
|
|
43121
|
-
router$1.post("/contents", addContent$1);
|
|
43122
|
-
router$1.post("/contents/:id/delete", deleteContent$1);
|
|
43123
|
-
router$1.post("/parse/video", parseVideo);
|
|
43124
|
-
router$1.post("/parse/dynamic", parseDynamic);
|
|
43125
|
-
router$1.post("/parse/dynamic/raw", parseDynamicRaw);
|
|
43126
|
-
router$1.get("/video/playurl", getVideoPlayUrl);
|
|
43127
|
-
router$1.post("/verify", verifyCaptcha);
|
|
43128
|
-
//#endregion
|
|
43129
|
-
//#region src/platform/douyin/api/contents.ts
|
|
43130
|
-
/**
|
|
43131
|
-
* 抖音内容管理 API
|
|
43132
|
-
*/
|
|
43133
|
-
/**
|
|
43134
|
-
* 获取抖音内容列表
|
|
43135
|
-
* GET /api/v1/platforms/douyin/contents?groupId=xxx
|
|
43136
|
-
*/
|
|
43137
|
-
var getContents = async (req, res) => {
|
|
43138
|
-
try {
|
|
43139
|
-
const { groupId } = req.query;
|
|
43140
|
-
if (!groupId || typeof groupId !== "string") return createBadRequestResponse(res, "请提供群组ID");
|
|
43141
|
-
const caches = await (await getDouyinDB()).awemeCacheRepository.find({
|
|
43142
|
-
where: { groupId },
|
|
43143
|
-
relations: ["douyinUser"],
|
|
43144
|
-
order: { createdAt: "DESC" },
|
|
43145
|
-
take: 100
|
|
43146
|
-
});
|
|
43147
|
-
const userAvatarMap = /* @__PURE__ */ new Map();
|
|
43148
|
-
const uniqueSecUids = [...new Set(caches.map((c) => c.sec_uid))];
|
|
43149
|
-
for (let i = 0; i < uniqueSecUids.length; i += 5) {
|
|
43150
|
-
const batch = uniqueSecUids.slice(i, i + 5);
|
|
43151
|
-
await Promise.all(batch.map(async (secUid) => {
|
|
43152
|
-
try {
|
|
43153
|
-
const userProfile = await douyinFetcher.fetchUserProfile({
|
|
43154
|
-
sec_uid: secUid,
|
|
43155
|
-
typeMode: "strict"
|
|
43156
|
-
});
|
|
43157
|
-
userAvatarMap.set(secUid, userProfile.data?.user?.avatar_larger?.url_list[0] || "");
|
|
43158
|
-
} catch {
|
|
43159
|
-
userAvatarMap.set(secUid, "");
|
|
43160
|
-
}
|
|
43161
|
-
}));
|
|
43162
|
-
}
|
|
43163
|
-
return createSuccessResponse(res, caches.map((cache) => {
|
|
43164
|
-
const cacheWithUser = cache;
|
|
43165
|
-
const authorName = cacheWithUser.douyinUser?.remark || cacheWithUser.douyinUser?.short_id || cache.sec_uid;
|
|
43166
|
-
return {
|
|
43167
|
-
id: cache.aweme_id,
|
|
43168
|
-
platform: "douyin",
|
|
43169
|
-
title: `抖音作品 ${cache.aweme_id}`,
|
|
43170
|
-
author: authorName,
|
|
43171
|
-
authorId: cache.sec_uid,
|
|
43172
|
-
avatar: userAvatarMap.get(cache.sec_uid) || "",
|
|
43173
|
-
thumbnail: "",
|
|
43174
|
-
type: "video",
|
|
43175
|
-
createdAt: cache.createdAt.getTime()
|
|
43176
|
-
};
|
|
43177
|
-
}));
|
|
43178
|
-
} catch (error) {
|
|
43179
|
-
logger.error("[DouyinAPI] 获取内容列表失败:", error);
|
|
43180
|
-
return createServerErrorResponse(res, "获取内容列表失败");
|
|
43181
|
-
}
|
|
43182
|
-
};
|
|
43183
|
-
/**
|
|
43184
|
-
* 添加抖音内容
|
|
43185
|
-
* POST /api/v1/platforms/douyin/contents
|
|
43186
|
-
* Body: { contentId, groupId, authorId }
|
|
43187
|
-
*/
|
|
43188
|
-
var addContent = async (req, res) => {
|
|
43189
|
-
try {
|
|
43190
|
-
const { contentId, groupId, authorId } = req.body;
|
|
43191
|
-
if (!contentId || !groupId || !authorId) return createBadRequestResponse(res, "请提供 contentId、groupId 和 authorId");
|
|
43192
|
-
const douyinDB = await getDouyinDB();
|
|
43193
|
-
if (!await douyinDB.getDouyinUser(authorId)) return createBadRequestResponse(res, "该作者未在订阅列表中,请先添加订阅");
|
|
43194
|
-
await douyinDB.addAwemeCache(contentId, authorId, groupId);
|
|
43195
|
-
return createSuccessResponse(res, { message: "添加成功" });
|
|
43196
|
-
} catch (error) {
|
|
43197
|
-
logger.error("[DouyinAPI] 添加内容失败:", error);
|
|
43198
|
-
return createServerErrorResponse(res, "添加内容失败");
|
|
43199
|
-
}
|
|
43200
|
-
};
|
|
43201
|
-
/**
|
|
43202
|
-
* 删除抖音内容
|
|
43203
|
-
* POST /api/kkk/v1/platforms/douyin/contents/:id/delete
|
|
43204
|
-
* Body: { groupId: string }
|
|
43205
|
-
*/
|
|
43206
|
-
var deleteContent = async (req, res) => {
|
|
43207
|
-
try {
|
|
43208
|
-
const { id } = req.params;
|
|
43209
|
-
const { groupId } = req.body;
|
|
43210
|
-
if (!id || Array.isArray(id) && !id[0] || !groupId) return createBadRequestResponse(res, "请提供内容ID和群组ID");
|
|
43211
|
-
const result = await (await getDouyinDB()).awemeCacheRepository.delete({
|
|
43212
|
-
aweme_id: Array.isArray(id) ? id[0] : id,
|
|
43213
|
-
groupId
|
|
43214
|
-
});
|
|
43215
|
-
if (result.affected === 0) return createBadRequestResponse(res, "未找到要删除的内容");
|
|
43216
|
-
return createSuccessResponse(res, {
|
|
43217
|
-
message: "删除成功",
|
|
43218
|
-
affected: result.affected
|
|
43219
|
-
});
|
|
43220
|
-
} catch (error) {
|
|
43221
|
-
logger.error("[DouyinAPI] 删除内容失败:", error);
|
|
43222
|
-
return createServerErrorResponse(res, "删除内容失败");
|
|
43223
|
-
}
|
|
43224
|
-
};
|
|
43225
|
-
//#endregion
|
|
43226
|
-
//#region src/platform/douyin/api/parse.ts
|
|
43227
|
-
/**
|
|
43228
|
-
* 格式化数字
|
|
43229
|
-
*/
|
|
43230
|
-
var formatCount = (count) => {
|
|
43231
|
-
if (count >= 1e8) return `${(count / 1e8).toFixed(1)}亿`;
|
|
43232
|
-
if (count >= 1e4) return `${(count / 1e4).toFixed(1)}万`;
|
|
43233
|
-
return count.toString();
|
|
43234
|
-
};
|
|
43235
|
-
/**
|
|
43236
|
-
* 格式化时长
|
|
43237
|
-
*/
|
|
43238
|
-
var formatDuration = (seconds) => {
|
|
43239
|
-
return `${Math.floor(seconds / 60)}:${Math.floor(seconds % 60).toString().padStart(2, "0")}`;
|
|
43240
|
-
};
|
|
43241
|
-
/**
|
|
43242
|
-
* 格式化时间戳
|
|
43243
|
-
*/
|
|
43244
|
-
var formatTimestamp = (timestamp) => {
|
|
43245
|
-
const date = /* @__PURE__ */ new Date(timestamp * 1e3);
|
|
43246
|
-
const diff = (/* @__PURE__ */ new Date()).getTime() - date.getTime();
|
|
43247
|
-
if (diff < 6e4) return "刚刚";
|
|
43248
|
-
if (diff < 36e5) return `${Math.floor(diff / 6e4)}分钟前`;
|
|
43249
|
-
if (diff < 864e5) return `${Math.floor(diff / 36e5)}小时前`;
|
|
43250
|
-
if (diff < 2592e6) return `${Math.floor(diff / 864e5)}天前`;
|
|
43251
|
-
return `${date.getMonth() + 1}月${date.getDate()}日`;
|
|
43252
|
-
};
|
|
43253
|
-
/**
|
|
43254
|
-
* 从标题中移除标签
|
|
43255
|
-
*/
|
|
43256
|
-
var removeTags = (title, tags) => {
|
|
43257
|
-
if (!title || !tags || tags.length === 0) return title;
|
|
43258
|
-
let cleanTitle = title;
|
|
43259
|
-
tags.forEach((tag) => {
|
|
43260
|
-
if (tag) {
|
|
43261
|
-
const hashtagPattern = new RegExp(`#${tag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:\\s|$)`, "gi");
|
|
43262
|
-
cleanTitle = cleanTitle.replace(hashtagPattern, "");
|
|
43263
|
-
const atPattern = new RegExp(`@${tag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:\\s|$)`, "gi");
|
|
43264
|
-
cleanTitle = cleanTitle.replace(atPattern, "");
|
|
43265
|
-
}
|
|
43266
|
-
});
|
|
43267
|
-
return cleanTitle.replace(/\s+/g, " ").trim();
|
|
43268
|
-
};
|
|
43269
|
-
/**
|
|
43270
|
-
* 处理评论中的表情
|
|
43271
|
-
*/
|
|
43272
|
-
var processCommentEmojis = (text, emojiData) => {
|
|
43273
|
-
if (!text || !emojiData?.emoji_list) return text;
|
|
43274
|
-
let processedText = text;
|
|
43275
|
-
processedText = processedText.replace(/\[([^\]]+)\]/g, (match, emojiName) => {
|
|
43276
|
-
const emojiInfo = emojiData.emoji_list.find((emoji) => emoji.display_name === `[${emojiName}]`);
|
|
43277
|
-
if (emojiInfo && emojiInfo.emoji_url?.url_list?.[0]) return `<img src="${emojiInfo.emoji_url.url_list[0]}" alt="${emojiName}" class="emoji" />`;
|
|
43278
|
-
return match;
|
|
43279
|
-
});
|
|
43280
|
-
return processedText.split(/(<img[^>]*>)/).map((part) => {
|
|
43281
|
-
if (part.startsWith("<img")) return part;
|
|
43282
|
-
if (part.trim()) return `<span>${part}</span>`;
|
|
43283
|
-
return part;
|
|
43284
|
-
}).join("");
|
|
43285
|
-
};
|
|
43286
|
-
/**
|
|
43287
|
-
* 解析评论数据
|
|
43288
|
-
*/
|
|
43289
|
-
var parseComments = (commentsData, emojiData) => {
|
|
43290
|
-
if (!commentsData || !Array.isArray(commentsData)) return [];
|
|
43291
|
-
return commentsData.map((comment, index) => {
|
|
43292
|
-
let processedText = comment.text || "";
|
|
43293
|
-
if (emojiData && comment.text) processedText = processCommentEmojis(comment.text, emojiData);
|
|
43294
|
-
return {
|
|
43295
|
-
id: comment.cid?.toString() || index.toString(),
|
|
43296
|
-
author: comment.user?.nickname || "匿名用户",
|
|
43297
|
-
avatar: comment.user?.avatar_thumb?.url_list?.[0] || "",
|
|
43298
|
-
content: processedText,
|
|
43299
|
-
images: comment.image_list?.map((img) => img.origin_url?.url_list?.[3]).filter(Boolean) || [],
|
|
43300
|
-
likes: comment.digg_count || 0,
|
|
43301
|
-
timestamp: formatTimestamp(comment.create_time || 0)
|
|
43302
|
-
};
|
|
43303
|
-
});
|
|
43304
|
-
};
|
|
43305
|
-
/**
|
|
43306
|
-
* 解析抖音作品
|
|
43307
|
-
* POST /api/v1/platforms/douyin/parse
|
|
43308
|
-
* Body: { aweme_id: string }
|
|
43309
|
-
*/
|
|
43310
|
-
var parseWork = async (req, res) => {
|
|
43311
|
-
try {
|
|
43312
|
-
const { aweme_id } = req.body;
|
|
43313
|
-
if (!aweme_id) return createBadRequestResponse(res, "请提供作品ID (aweme_id)");
|
|
43314
|
-
const [workResponse, commentsResponse, emojiResponse] = await Promise.all([
|
|
43315
|
-
douyinFetcher.parseWork({
|
|
43316
|
-
aweme_id,
|
|
43317
|
-
typeMode: "strict"
|
|
43318
|
-
}),
|
|
43319
|
-
douyinFetcher.fetchWorkComments({
|
|
43320
|
-
aweme_id,
|
|
43321
|
-
typeMode: "strict"
|
|
43322
|
-
}),
|
|
43323
|
-
douyinFetcher.fetchEmojiList({ typeMode: "strict" })
|
|
43324
|
-
]);
|
|
43325
|
-
const awemeDetail = workResponse.data.aweme_detail;
|
|
43326
|
-
const commentsData = commentsResponse.data.comments;
|
|
43327
|
-
const emojiData = emojiResponse.data;
|
|
43328
|
-
const isSlides = awemeDetail.is_slides === true && awemeDetail.images !== null;
|
|
43329
|
-
const isVideo = !awemeDetail.images && !isSlides;
|
|
43330
|
-
const workType = isSlides ? "slides" : isVideo ? "video" : "note";
|
|
43331
|
-
const comments = parseComments(commentsData, emojiData);
|
|
43332
|
-
const tags = Array.isArray(awemeDetail.text_extra) ? awemeDetail.text_extra.map((tag) => typeof tag === "string" ? tag : tag.hashtag_name).filter(Boolean) : [];
|
|
43333
|
-
const cleanTitle = removeTags(awemeDetail.desc || "无标题", tags);
|
|
43334
|
-
let slides;
|
|
43335
|
-
if (isSlides && awemeDetail.images) slides = awemeDetail.images.map((item) => {
|
|
43336
|
-
if (item.clip_type === 2) return {
|
|
43337
|
-
type: "image",
|
|
43338
|
-
url: item.url_list[2],
|
|
43339
|
-
thumbnail: item.url_list[2]
|
|
43340
|
-
};
|
|
43341
|
-
else if (item.clip_type === 3) {
|
|
43342
|
-
const videoUri = item.video?.play_addr_h264?.uri;
|
|
43343
|
-
const videoUrl = videoUri ? `https://aweme.snssdk.com/aweme/v1/play/?video_id=${videoUri}&ratio=1080p&line=0` : item.video?.play_addr_h264?.url_list?.[0];
|
|
43344
|
-
return {
|
|
43345
|
-
type: "livephoto",
|
|
43346
|
-
url: item.url_list[2],
|
|
43347
|
-
videoUrl,
|
|
43348
|
-
thumbnail: item.url_list[2],
|
|
43349
|
-
duration: formatDuration(item.video?.duration / 1e3 || 0)
|
|
43350
|
-
};
|
|
43351
|
-
} else return {
|
|
43352
|
-
type: "video",
|
|
43353
|
-
url: item.video.play_addr_h264.url_list[0],
|
|
43354
|
-
thumbnail: item.url_list[2],
|
|
43355
|
-
duration: formatDuration(item.video?.duration / 1e3 || 0)
|
|
43356
|
-
};
|
|
43357
|
-
});
|
|
43358
|
-
return createSuccessResponse(res, {
|
|
43359
|
-
id: awemeDetail.aweme_id,
|
|
43360
|
-
title: cleanTitle,
|
|
43361
|
-
description: awemeDetail.desc || "",
|
|
43362
|
-
thumbnail: isVideo ? awemeDetail.video?.cover?.url_list?.[0] : awemeDetail.images?.[0]?.url_list?.[0] || "",
|
|
43363
|
-
duration: isVideo ? formatDuration(awemeDetail.video?.duration / 1e3 || 0) : "0:00",
|
|
43364
|
-
views: formatCount(awemeDetail.statistics?.play_count || 0),
|
|
43365
|
-
likes: formatCount(awemeDetail.statistics?.digg_count || 0),
|
|
43366
|
-
author: {
|
|
43367
|
-
name: awemeDetail.author?.nickname || "未知用户",
|
|
43368
|
-
avatar: awemeDetail.author?.avatar_thumb?.url_list?.[0] || "",
|
|
43369
|
-
id: awemeDetail.author?.sec_uid || ""
|
|
43370
|
-
},
|
|
43371
|
-
type: workType,
|
|
43372
|
-
downloadUrl: {
|
|
43373
|
-
video: isVideo ? awemeDetail.video?.play_addr?.url_list?.[0] : isSlides && slides ? slides.find((slide) => slide.type === "video")?.url : void 0,
|
|
43374
|
-
audio: isSlides && awemeDetail.video ? awemeDetail.video?.play_addr?.url_list?.[0] : awemeDetail.music?.play_url?.uri
|
|
43375
|
-
},
|
|
43376
|
-
images: workType === "note" ? awemeDetail.images?.map((img) => img.url_list?.[2]).filter(Boolean) : void 0,
|
|
43377
|
-
slides,
|
|
43378
|
-
tags,
|
|
43379
|
-
comments,
|
|
43380
|
-
commentCount: awemeDetail.statistics?.comment_count || 0
|
|
43381
|
-
});
|
|
43382
|
-
} catch (error) {
|
|
43383
|
-
logger.error("[DouyinAPI] 解析作品失败:", error);
|
|
43384
|
-
return createServerErrorResponse(res, `解析失败: ${error.message}`);
|
|
43385
|
-
}
|
|
43386
|
-
};
|
|
43387
|
-
//#endregion
|
|
43388
|
-
//#region src/platform/douyin/api/index.ts
|
|
43389
|
-
/**
|
|
43390
|
-
* 抖音平台 API 路由
|
|
43391
|
-
*/
|
|
43392
|
-
var router = express.Router();
|
|
43393
|
-
router.get("/contents", getContents);
|
|
43394
|
-
router.post("/contents", addContent);
|
|
43395
|
-
router.post("/contents/:id/delete", deleteContent);
|
|
43396
|
-
router.post("/parse", parseWork);
|
|
43397
|
-
//#endregion
|
|
43398
|
-
//#region src/module/server/auth.ts
|
|
43399
|
-
/**
|
|
43400
|
-
* Base64解码
|
|
43401
|
-
* @param str Base64编码的字符串
|
|
43402
|
-
* @returns 解码后的字符串
|
|
43403
|
-
*/
|
|
43404
|
-
var base64Decode = (str) => {
|
|
43405
|
-
return Buffer.from(str, "base64").toString("utf8");
|
|
43406
|
-
};
|
|
43407
|
-
/**
|
|
43408
|
-
* URL解码
|
|
43409
|
-
* @param str URL编码的字符串
|
|
43410
|
-
* @returns 解码后的字符串
|
|
43411
|
-
*/
|
|
43412
|
-
var urlDecode = (str) => {
|
|
43413
|
-
return decodeURIComponent(str);
|
|
43414
|
-
};
|
|
43415
|
-
/**
|
|
43416
|
-
* 十六进制解码
|
|
43417
|
-
* @param str 十六进制编码的字符串
|
|
43418
|
-
* @returns 解码后的字符串
|
|
43419
|
-
*/
|
|
43420
|
-
var hexDecode = (str) => {
|
|
43421
|
-
return Buffer.from(str, "hex").toString("utf8");
|
|
43422
|
-
};
|
|
43423
|
-
/**
|
|
43424
|
-
* 反转字符串
|
|
43425
|
-
* @param str 待反转字符串
|
|
43426
|
-
* @returns 反转后的字符串
|
|
43427
|
-
*/
|
|
43428
|
-
var reverseString = (str) => {
|
|
43429
|
-
return str.split("").reverse().join("");
|
|
43430
|
-
};
|
|
43431
|
-
/**
|
|
43432
|
-
* 字符偏移解码
|
|
43433
|
-
* @param str 编码的字符串
|
|
43434
|
-
* @param offset 偏移量
|
|
43435
|
-
* @returns 解码后的字符串
|
|
43436
|
-
*/
|
|
43437
|
-
var charOffsetDecode = (str, offset = 5) => {
|
|
43438
|
-
return str.split("").map((char) => {
|
|
43439
|
-
const code = char.charCodeAt(0);
|
|
43440
|
-
return String.fromCharCode(code - offset);
|
|
43441
|
-
}).join("");
|
|
43442
|
-
};
|
|
43443
|
-
/**
|
|
43444
|
-
* 多层解码解密
|
|
43445
|
-
* @param str 多层编码的字符串
|
|
43446
|
-
* @returns 解码后的原始字符串
|
|
43447
|
-
*/
|
|
43448
|
-
var multiLayerDecode = (str) => {
|
|
43449
|
-
try {
|
|
43450
|
-
let decoded = base64Decode(str);
|
|
43451
|
-
decoded = urlDecode(decoded);
|
|
43452
|
-
decoded = base64Decode(decoded);
|
|
43453
|
-
decoded = reverseString(decoded);
|
|
43454
|
-
decoded = hexDecode(decoded);
|
|
43455
|
-
decoded = charOffsetDecode(decoded, 5);
|
|
43456
|
-
return decoded;
|
|
43457
|
-
} catch (error) {
|
|
43458
|
-
throw new Error("多层解码失败:" + error);
|
|
43459
|
-
}
|
|
43460
|
-
};
|
|
43461
|
-
/**
|
|
43462
|
-
* HMAC-SHA256签名验证中间件
|
|
43463
|
-
* @param req 请求对象
|
|
43464
|
-
* @param res 响应对象
|
|
43465
|
-
* @param next 下一个中间件函数
|
|
43466
|
-
*/
|
|
43467
|
-
var signatureVerificationMiddleware = (req, res, next) => {
|
|
43468
|
-
try {
|
|
43469
|
-
const encodedSignature = req.headers["x-signature"];
|
|
43470
|
-
const timestamp = req.headers["x-timestamp"];
|
|
43471
|
-
const nonce = req.headers["x-nonce"];
|
|
43472
|
-
const token = req.headers["authorization"]?.replace("Bearer ", "") || "";
|
|
43473
|
-
if (!encodedSignature || !timestamp || !nonce) return createBadRequestResponse(res, "缺少必要的签名参数");
|
|
43474
|
-
if (Math.abs(Date.now() - parseInt(timestamp)) > 300 * 1e3) return createBadRequestResponse(res, "请求时间戳已过期");
|
|
43475
|
-
let decodedSignature;
|
|
43476
|
-
try {
|
|
43477
|
-
decodedSignature = multiLayerDecode(encodedSignature);
|
|
43478
|
-
} catch (error) {
|
|
43479
|
-
return createBadRequestResponse(res, "签名格式错误:" + error);
|
|
43480
|
-
}
|
|
43481
|
-
const signatureString = `${req.method.toUpperCase()}|${req.headers["x-original-url"] || req.originalUrl}|${req.method === "GET" ? "" : JSON.stringify(req.body || {})}|${timestamp}|${nonce}`;
|
|
43482
|
-
const expectedSignature = crypto.createHmac("sha256", token).update(signatureString).digest("hex");
|
|
43483
|
-
if (decodedSignature !== expectedSignature) {
|
|
43484
|
-
logger.warn(`签名验证失败: 期望=${expectedSignature}, 解码后实际=${decodedSignature}, 签名字符串=${signatureString}`);
|
|
43485
|
-
return createBadRequestResponse(res, "签名验证失败");
|
|
43486
|
-
}
|
|
43487
|
-
next();
|
|
43488
|
-
} catch (error) {
|
|
43489
|
-
logger.error("签名验证中间件错误:", error);
|
|
43490
|
-
return createServerErrorResponse(res, "签名验证失败");
|
|
43491
|
-
}
|
|
42468
|
+
//#region src/module/server/constants/routes.ts
|
|
42469
|
+
/**
|
|
42470
|
+
* 路由常量定义
|
|
42471
|
+
*/
|
|
42472
|
+
var KKK_PREFIX = "/kkk";
|
|
42473
|
+
/** 静态文件 */
|
|
42474
|
+
var ASSETS_PREFIX = "/assets";
|
|
42475
|
+
/** SSR 页面 */
|
|
42476
|
+
var SSR_PREFIX = "/ssr";
|
|
42477
|
+
`${KKK_PREFIX}${ASSETS_PREFIX}`;
|
|
42478
|
+
`${KKK_PREFIX}${SSR_PREFIX}`;
|
|
42479
|
+
`${KKK_PREFIX}`;
|
|
42480
|
+
var ROUTES = {
|
|
42481
|
+
/** 获取所有 Bot */
|
|
42482
|
+
BOTS: "/bots",
|
|
42483
|
+
/** 获取指定 Bot 信息 */
|
|
42484
|
+
BOT_INFO: "/bots/:botId",
|
|
42485
|
+
/** 获取指定 Bot 的群列表 */
|
|
42486
|
+
BOT_GROUPS: "/bots/:botId/groups",
|
|
42487
|
+
/** 获取指定 Bot 群信息 */
|
|
42488
|
+
BOT_GROUP_INFO: "/bots/:botId/groups/:groupId",
|
|
42489
|
+
/** 获取所有群组 */
|
|
42490
|
+
GROUPS_BATCH: "/groups/batch",
|
|
42491
|
+
/** 获取插件所有配置 */
|
|
42492
|
+
CONFIG: "/config",
|
|
42493
|
+
/** 获取视频流 */
|
|
42494
|
+
VIDEO_STREAM: "/stream/:filename",
|
|
42495
|
+
/** 获取视频事件 */
|
|
42496
|
+
VIDEO_EVENTS: "/video/:filename/events",
|
|
42497
|
+
/** 视频播放页面 */
|
|
42498
|
+
VIDEO_PAGE: "/video/:filename"
|
|
43492
42499
|
};
|
|
43493
42500
|
//#endregion
|
|
43494
|
-
//#region src/module/server/
|
|
42501
|
+
//#region src/module/server/controllers/bots.ts
|
|
43495
42502
|
/**
|
|
43496
42503
|
* Bot 管理 API
|
|
43497
42504
|
*/
|
|
@@ -43543,7 +42550,7 @@ var getBots = async (_req, res) => {
|
|
|
43543
42550
|
*/
|
|
43544
42551
|
var getBotInfo = async (req, res) => {
|
|
43545
42552
|
try {
|
|
43546
|
-
const
|
|
42553
|
+
const botId = Array.isArray(req.params.botId) ? req.params.botId[0] : req.params.botId;
|
|
43547
42554
|
if (!botId) return createServerErrorResponse(res, "缺少 botId 参数");
|
|
43548
42555
|
const bot = getOnlineBotById(botId);
|
|
43549
42556
|
if (!bot || bot.account.name === "console") return createServerErrorResponse(res, "Bot 不存在或不在线");
|
|
@@ -43564,7 +42571,7 @@ var getBotInfo = async (req, res) => {
|
|
|
43564
42571
|
*/
|
|
43565
42572
|
var getBotGroups = async (req, res) => {
|
|
43566
42573
|
try {
|
|
43567
|
-
const
|
|
42574
|
+
const botId = Array.isArray(req.params.botId) ? req.params.botId[0] : req.params.botId;
|
|
43568
42575
|
if (!botId) return createServerErrorResponse(res, "缺少 botId 参数");
|
|
43569
42576
|
const botItem = karin.getAllBotList().find((item) => item.bot.account.selfId === botId);
|
|
43570
42577
|
if (!botItem) return createServerErrorResponse(res, "Bot 不存在或不在线");
|
|
@@ -43594,7 +42601,8 @@ var getBotGroups = async (req, res) => {
|
|
|
43594
42601
|
*/
|
|
43595
42602
|
var getBotGroupInfo = async (req, res) => {
|
|
43596
42603
|
try {
|
|
43597
|
-
const
|
|
42604
|
+
const botId = Array.isArray(req.params.botId) ? req.params.botId[0] : req.params.botId;
|
|
42605
|
+
const groupId = Array.isArray(req.params.groupId) ? req.params.groupId[0] : req.params.groupId;
|
|
43598
42606
|
if (!botId || !groupId) return createServerErrorResponse(res, "缺少 botId 或 groupId 参数");
|
|
43599
42607
|
const bot = getOnlineBotById(botId);
|
|
43600
42608
|
if (!bot) return createServerErrorResponse(res, "Bot 不存在或不在线");
|
|
@@ -43679,141 +42687,31 @@ var getGroupsBatch = async (req, res) => {
|
|
|
43679
42687
|
}
|
|
43680
42688
|
};
|
|
43681
42689
|
//#endregion
|
|
43682
|
-
//#region src/module/server/
|
|
42690
|
+
//#region src/module/server/controllers/config.ts
|
|
43683
42691
|
/**
|
|
43684
|
-
*
|
|
43685
|
-
*
|
|
42692
|
+
* 配置管理 API
|
|
42693
|
+
* 提供配置的增删改查接口,供 APP 端使用
|
|
43686
42694
|
*/
|
|
43687
|
-
var getAllConfig = async (_req, res) => {
|
|
43688
|
-
try {
|
|
43689
|
-
const config = await Config.All();
|
|
43690
|
-
res.json({
|
|
43691
|
-
success: true,
|
|
43692
|
-
message: "获取配置成功",
|
|
43693
|
-
data: config
|
|
43694
|
-
});
|
|
43695
|
-
} catch (error) {
|
|
43696
|
-
res.status(500).json({
|
|
43697
|
-
success: false,
|
|
43698
|
-
message: `获取配置失败: ${error.message}`,
|
|
43699
|
-
data: null
|
|
43700
|
-
});
|
|
43701
|
-
}
|
|
43702
|
-
};
|
|
43703
|
-
/**
|
|
43704
|
-
* 获取指定配置模块
|
|
43705
|
-
* GET /api/kkk/v1/config/:module
|
|
43706
|
-
*/
|
|
43707
|
-
var getConfigModule = async (req, res) => {
|
|
43708
|
-
try {
|
|
43709
|
-
const { module } = req.params;
|
|
43710
|
-
const allConfig = await Config.All();
|
|
43711
|
-
if (!(module in allConfig)) return res.status(400).json({
|
|
43712
|
-
success: false,
|
|
43713
|
-
message: `配置模块 "${module}" 不存在`,
|
|
43714
|
-
data: null
|
|
43715
|
-
});
|
|
43716
|
-
res.json({
|
|
43717
|
-
success: true,
|
|
43718
|
-
message: "获取配置成功",
|
|
43719
|
-
data: allConfig[module]
|
|
43720
|
-
});
|
|
43721
|
-
} catch (error) {
|
|
43722
|
-
res.status(500).json({
|
|
43723
|
-
success: false,
|
|
43724
|
-
message: `获取配置失败: ${error.message}`,
|
|
43725
|
-
data: null
|
|
43726
|
-
});
|
|
43727
|
-
}
|
|
43728
|
-
};
|
|
43729
42695
|
/**
|
|
43730
|
-
*
|
|
43731
|
-
*
|
|
43732
|
-
*/
|
|
43733
|
-
var updateConfigModule = async (req, res) => {
|
|
43734
|
-
try {
|
|
43735
|
-
const { module } = req.params;
|
|
43736
|
-
const newConfig = req.body?.config || req.body;
|
|
43737
|
-
if (!newConfig || typeof newConfig !== "object") return res.status(400).json({
|
|
43738
|
-
success: false,
|
|
43739
|
-
message: "请求体必须是有效的配置对象",
|
|
43740
|
-
data: null
|
|
43741
|
-
});
|
|
43742
|
-
if ("_method" in newConfig) delete newConfig._method;
|
|
43743
|
-
if (!(module in await Config.All())) return res.status(400).json({
|
|
43744
|
-
success: false,
|
|
43745
|
-
message: `配置模块 "${module}" 不存在`,
|
|
43746
|
-
data: null
|
|
43747
|
-
});
|
|
43748
|
-
if (await Config.ModifyPro(module, newConfig)) {
|
|
43749
|
-
if (module === "pushlist") await Config.syncConfigToDatabase();
|
|
43750
|
-
if (module === "cookies" || module === "request") reloadAmagiConfig();
|
|
43751
|
-
const updatedConfig = await Config.All();
|
|
43752
|
-
res.json({
|
|
43753
|
-
success: true,
|
|
43754
|
-
message: "配置更新成功",
|
|
43755
|
-
data: updatedConfig[module]
|
|
43756
|
-
});
|
|
43757
|
-
} else res.status(500).json({
|
|
43758
|
-
success: false,
|
|
43759
|
-
message: "配置更新失败",
|
|
43760
|
-
data: null
|
|
43761
|
-
});
|
|
43762
|
-
} catch (error) {
|
|
43763
|
-
res.status(500).json({
|
|
43764
|
-
success: false,
|
|
43765
|
-
message: `配置更新失败: ${error.message}`,
|
|
43766
|
-
data: null
|
|
43767
|
-
});
|
|
43768
|
-
}
|
|
43769
|
-
};
|
|
43770
|
-
/**
|
|
43771
|
-
* 更新单个配置项
|
|
43772
|
-
* PATCH /api/kkk/v1/config/:module
|
|
43773
|
-
* Body: { key: string, value: any }
|
|
42696
|
+
* 获取所有配置
|
|
42697
|
+
* GET /kkk/web/v1/config
|
|
43774
42698
|
*/
|
|
43775
|
-
var
|
|
42699
|
+
var getAllConfig = async (_req, res) => {
|
|
43776
42700
|
try {
|
|
43777
|
-
|
|
43778
|
-
const { key, value } = req.body;
|
|
43779
|
-
if (!key) return res.status(400).json({
|
|
43780
|
-
success: false,
|
|
43781
|
-
message: "缺少配置项 key",
|
|
43782
|
-
data: null
|
|
43783
|
-
});
|
|
43784
|
-
if (!(module in await Config.All())) return res.status(400).json({
|
|
43785
|
-
success: false,
|
|
43786
|
-
message: `配置模块 "${module}" 不存在`,
|
|
43787
|
-
data: null
|
|
43788
|
-
});
|
|
43789
|
-
Config.Modify(module, key, value);
|
|
43790
|
-
const updatedConfig = await Config.All();
|
|
43791
|
-
res.json({
|
|
43792
|
-
success: true,
|
|
43793
|
-
message: "配置项更新成功",
|
|
43794
|
-
data: updatedConfig[module]
|
|
43795
|
-
});
|
|
42701
|
+
return createSuccessResponse(res, await Config.All());
|
|
43796
42702
|
} catch (error) {
|
|
43797
|
-
res.
|
|
43798
|
-
success: false,
|
|
43799
|
-
message: `配置项更新失败: ${error.message}`,
|
|
43800
|
-
data: null
|
|
43801
|
-
});
|
|
42703
|
+
return createServerErrorResponse(res, `获取配置失败: ${error.message}`);
|
|
43802
42704
|
}
|
|
43803
42705
|
};
|
|
43804
42706
|
/**
|
|
43805
42707
|
* 批量更新配置
|
|
43806
|
-
*
|
|
42708
|
+
* POST /kkk/web/v1/config
|
|
43807
42709
|
* Body: Partial<ConfigType>
|
|
43808
42710
|
*/
|
|
43809
42711
|
var updateAllConfig = async (req, res) => {
|
|
43810
42712
|
try {
|
|
43811
42713
|
const newConfig = req.body;
|
|
43812
|
-
if (!newConfig || typeof newConfig !== "object") return res
|
|
43813
|
-
success: false,
|
|
43814
|
-
message: "请求体必须是有效的配置对象",
|
|
43815
|
-
data: null
|
|
43816
|
-
});
|
|
42714
|
+
if (!newConfig || typeof newConfig !== "object") return createBadRequestResponse(res, "请求体必须是有效的配置对象");
|
|
43817
42715
|
const oldConfig = await Config.All();
|
|
43818
42716
|
const results = [];
|
|
43819
42717
|
let needReloadAmagi = false;
|
|
@@ -43832,7 +42730,7 @@ var updateAllConfig = async (req, res) => {
|
|
|
43832
42730
|
module,
|
|
43833
42731
|
success
|
|
43834
42732
|
});
|
|
43835
|
-
if (success &&
|
|
42733
|
+
if (success && moduleName === "amagi") needReloadAmagi = true;
|
|
43836
42734
|
} catch (error) {
|
|
43837
42735
|
results.push({
|
|
43838
42736
|
module,
|
|
@@ -43843,211 +42741,23 @@ var updateAllConfig = async (req, res) => {
|
|
|
43843
42741
|
if ("pushlist" in newConfig) await Config.syncConfigToDatabase();
|
|
43844
42742
|
if (needReloadAmagi) reloadAmagiConfig();
|
|
43845
42743
|
const allSuccess = results.every((r) => r.success);
|
|
43846
|
-
const updatedConfig = await Config.All();
|
|
43847
|
-
res.json({
|
|
43848
|
-
success: allSuccess,
|
|
43849
|
-
message: allSuccess ? "所有配置更新成功" : "部分配置更新失败",
|
|
43850
|
-
data: {
|
|
43851
|
-
config: updatedConfig,
|
|
43852
|
-
results
|
|
43853
|
-
}
|
|
43854
|
-
});
|
|
43855
|
-
} catch (error) {
|
|
43856
|
-
res.status(500).json({
|
|
43857
|
-
success: false,
|
|
43858
|
-
message: `配置更新失败: ${error.message}`,
|
|
43859
|
-
data: null
|
|
43860
|
-
});
|
|
43861
|
-
}
|
|
43862
|
-
};
|
|
43863
|
-
//#endregion
|
|
43864
|
-
//#region src/module/server/api/groups.ts
|
|
43865
|
-
/**
|
|
43866
|
-
* 群组管理 API
|
|
43867
|
-
*/
|
|
43868
|
-
/**
|
|
43869
|
-
* 获取所有已订阅推送功能的群组列表
|
|
43870
|
-
* GET /api/v1/groups
|
|
43871
|
-
*/
|
|
43872
|
-
var getGroups = async (_req, res) => {
|
|
43873
|
-
try {
|
|
43874
|
-
const douyinDB = await getDouyinDB();
|
|
43875
|
-
const bilibiliDB = await getBilibiliDB();
|
|
43876
|
-
const [douyinGroups, bilibiliGroups] = await Promise.all([douyinDB.groupRepository.find(), bilibiliDB.groupRepository.find()]);
|
|
43877
|
-
const allGroupsMap = /* @__PURE__ */ new Map();
|
|
43878
|
-
douyinGroups.forEach((group) => {
|
|
43879
|
-
allGroupsMap.set(group.id, {
|
|
43880
|
-
id: group.id,
|
|
43881
|
-
botId: group.botId
|
|
43882
|
-
});
|
|
43883
|
-
});
|
|
43884
|
-
bilibiliGroups.forEach((group) => {
|
|
43885
|
-
if (!allGroupsMap.has(group.id)) allGroupsMap.set(group.id, {
|
|
43886
|
-
id: group.id,
|
|
43887
|
-
botId: group.botId
|
|
43888
|
-
});
|
|
43889
|
-
});
|
|
43890
|
-
const groupList = [];
|
|
43891
|
-
for (const group of allGroupsMap.values()) {
|
|
43892
|
-
const [douyinSubscriptions, bilibiliSubscriptions] = await Promise.all([douyinDB.getGroupSubscriptions(group.id), bilibiliDB.getGroupSubscriptions(group.id)]);
|
|
43893
|
-
if (douyinSubscriptions.length > 0 || bilibiliSubscriptions.length > 0) {
|
|
43894
|
-
const bot = getBot(group.botId);
|
|
43895
|
-
let groupName = group.id;
|
|
43896
|
-
let groupAvatarUrl = "";
|
|
43897
|
-
let botAvatarUrl = "";
|
|
43898
|
-
let isOnline = true;
|
|
43899
|
-
if (!bot) isOnline = false;
|
|
43900
|
-
else try {
|
|
43901
|
-
const groupInfo = await bot.getGroupInfo(group.id);
|
|
43902
|
-
if (groupInfo) groupName = groupInfo.groupName || groupName;
|
|
43903
|
-
groupAvatarUrl = await bot.getGroupAvatarUrl(group.id) || "";
|
|
43904
|
-
botAvatarUrl = await bot.getAvatarUrl(group.botId) || "";
|
|
43905
|
-
} catch (e) {
|
|
43906
|
-
logger.warn(`[GroupsAPI] 获取群组信息失败 ${group.id}:`, e);
|
|
43907
|
-
}
|
|
43908
|
-
groupList.push({
|
|
43909
|
-
id: group.id,
|
|
43910
|
-
name: groupName,
|
|
43911
|
-
avatar: groupAvatarUrl,
|
|
43912
|
-
botId: group.botId,
|
|
43913
|
-
botAvatar: botAvatarUrl,
|
|
43914
|
-
isOnline,
|
|
43915
|
-
subscriptionCount: {
|
|
43916
|
-
douyin: douyinSubscriptions.length,
|
|
43917
|
-
bilibili: bilibiliSubscriptions.length
|
|
43918
|
-
}
|
|
43919
|
-
});
|
|
43920
|
-
}
|
|
43921
|
-
}
|
|
43922
|
-
return createSuccessResponse(res, groupList);
|
|
43923
|
-
} catch (error) {
|
|
43924
|
-
logger.error("[GroupsAPI] 获取群组列表失败:", error);
|
|
43925
|
-
return createServerErrorResponse(res, "获取群组列表失败");
|
|
43926
|
-
}
|
|
43927
|
-
};
|
|
43928
|
-
//#endregion
|
|
43929
|
-
//#region src/module/server/api/link.ts
|
|
43930
|
-
/**
|
|
43931
|
-
* 链接解析 API
|
|
43932
|
-
*/
|
|
43933
|
-
/**
|
|
43934
|
-
* 识别平台类型
|
|
43935
|
-
*/
|
|
43936
|
-
var detectPlatform = (url) => {
|
|
43937
|
-
if (url.includes("douyin.com") || url.includes("iesdouyin.com") || url.includes("webcast.amemv.com") || url.includes("live.douyin.com")) return "douyin";
|
|
43938
|
-
if (url.includes("bilibili.com") || url.includes("b23.tv")) return "bilibili";
|
|
43939
|
-
return "unknown";
|
|
43940
|
-
};
|
|
43941
|
-
/**
|
|
43942
|
-
* 从 URL 中提取作品 ID
|
|
43943
|
-
*/
|
|
43944
|
-
var extractWorkId = (url, platform) => {
|
|
43945
|
-
if (platform === "douyin") {
|
|
43946
|
-
const videoMatch = /video\/(\d+)/.exec(url);
|
|
43947
|
-
if (videoMatch) return {
|
|
43948
|
-
type: "video",
|
|
43949
|
-
id: videoMatch[1]
|
|
43950
|
-
};
|
|
43951
|
-
const noteMatch = /note\/(\d+)/.exec(url);
|
|
43952
|
-
if (noteMatch) return {
|
|
43953
|
-
type: "note",
|
|
43954
|
-
id: noteMatch[1]
|
|
43955
|
-
};
|
|
43956
|
-
const modalMatch = /modal_id=(\d+)/.exec(url);
|
|
43957
|
-
if (modalMatch) return {
|
|
43958
|
-
type: "video",
|
|
43959
|
-
id: modalMatch[1]
|
|
43960
|
-
};
|
|
43961
|
-
}
|
|
43962
|
-
if (platform === "bilibili") {
|
|
43963
|
-
const bvidMatch = /\/video\/(BV[a-zA-Z0-9]+)/.exec(url);
|
|
43964
|
-
if (bvidMatch) return {
|
|
43965
|
-
type: "video",
|
|
43966
|
-
id: bvidMatch[1]
|
|
43967
|
-
};
|
|
43968
|
-
const aidMatch = /\/video\/av(\d+)/.exec(url);
|
|
43969
|
-
if (aidMatch) return {
|
|
43970
|
-
type: "video",
|
|
43971
|
-
id: aidMatch[1]
|
|
43972
|
-
};
|
|
43973
|
-
const tMatch = /^https:\/\/t\.bilibili\.com\/(\d+)/.exec(url);
|
|
43974
|
-
if (tMatch) return {
|
|
43975
|
-
type: "dynamic",
|
|
43976
|
-
id: tMatch[1]
|
|
43977
|
-
};
|
|
43978
|
-
const opusMatch = /\/opus\/(\d+)/.exec(url);
|
|
43979
|
-
if (opusMatch) return {
|
|
43980
|
-
type: "dynamic",
|
|
43981
|
-
id: opusMatch[1]
|
|
43982
|
-
};
|
|
43983
|
-
}
|
|
43984
|
-
return null;
|
|
43985
|
-
};
|
|
43986
|
-
/**
|
|
43987
|
-
* 解析短链接并获取最终 URL
|
|
43988
|
-
* POST /api/v1/link/resolve
|
|
43989
|
-
* Body: { link: string }
|
|
43990
|
-
*/
|
|
43991
|
-
var resolveLink = async (req, res) => {
|
|
43992
|
-
try {
|
|
43993
|
-
const { link } = req.body;
|
|
43994
|
-
if (!link || typeof link !== "string") return createBadRequestResponse(res, "请提供有效的链接");
|
|
43995
|
-
const finalUrl = (await axios.get(link, {
|
|
43996
|
-
headers: { "User-Agent": "Apifox/1.0.0 (https://apifox.com)" },
|
|
43997
|
-
maxRedirects: 10,
|
|
43998
|
-
validateStatus: () => true
|
|
43999
|
-
})).request.res?.responseUrl || link;
|
|
44000
|
-
if (finalUrl.includes("403 Forbidden")) return createServerErrorResponse(res, "无法获取链接的重定向地址");
|
|
44001
|
-
const platform = detectPlatform(finalUrl);
|
|
44002
|
-
const workInfo = extractWorkId(finalUrl, platform);
|
|
44003
|
-
logger.debug(`[LinkAPI] 链接解析: ${link} -> ${platform} (${workInfo?.type}: ${workInfo?.id})`);
|
|
44004
42744
|
return createSuccessResponse(res, {
|
|
44005
|
-
|
|
44006
|
-
|
|
44007
|
-
|
|
44008
|
-
workType: workInfo?.type || null,
|
|
44009
|
-
workId: workInfo?.id || null
|
|
44010
|
-
});
|
|
42745
|
+
config: await Config.All(),
|
|
42746
|
+
results
|
|
42747
|
+
}, allSuccess ? "所有配置更新成功" : "部分配置更新失败");
|
|
44011
42748
|
} catch (error) {
|
|
44012
|
-
|
|
44013
|
-
return createServerErrorResponse(res, `链接解析失败: ${error.message}`);
|
|
42749
|
+
return createServerErrorResponse(res, `配置更新失败: ${error.message}`);
|
|
44014
42750
|
}
|
|
44015
42751
|
};
|
|
44016
42752
|
//#endregion
|
|
44017
|
-
//#region src/module/server/
|
|
44018
|
-
/**
|
|
44019
|
-
* API 路由聚合
|
|
44020
|
-
*/
|
|
44021
|
-
var apiRouter = express.Router();
|
|
44022
|
-
var authMiddlewares = [authMiddleware, signatureVerificationMiddleware];
|
|
44023
|
-
apiRouter.get("/bots", ...authMiddlewares, getBots);
|
|
44024
|
-
apiRouter.get("/bots/:botId", ...authMiddlewares, getBotInfo);
|
|
44025
|
-
apiRouter.get("/bots/:botId/groups", ...authMiddlewares, getBotGroups);
|
|
44026
|
-
apiRouter.get("/bots/:botId/groups/:groupId", ...authMiddlewares, getBotGroupInfo);
|
|
44027
|
-
apiRouter.get("/groups", ...authMiddlewares, getGroups);
|
|
44028
|
-
apiRouter.post("/groups/batch", ...authMiddlewares, getGroupsBatch);
|
|
44029
|
-
apiRouter.post("/link/resolve", ...authMiddlewares, resolveLink);
|
|
44030
|
-
apiRouter.get("/config", ...authMiddlewares, getAllConfig);
|
|
44031
|
-
apiRouter.put("/config", ...authMiddlewares, updateAllConfig);
|
|
44032
|
-
apiRouter.post("/config", ...authMiddlewares, updateAllConfig);
|
|
44033
|
-
apiRouter.get("/config/:module", ...authMiddlewares, getConfigModule);
|
|
44034
|
-
apiRouter.put("/config/:module", ...authMiddlewares, updateConfigModule);
|
|
44035
|
-
apiRouter.post("/config/:module", ...authMiddlewares, updateConfigModule);
|
|
44036
|
-
apiRouter.patch("/config/:module", ...authMiddlewares, patchConfigItem);
|
|
44037
|
-
apiRouter.use("/platforms/douyin", ...authMiddlewares, router);
|
|
44038
|
-
apiRouter.use("/platforms/bilibili", ...authMiddlewares, router$1);
|
|
44039
|
-
//#endregion
|
|
44040
|
-
//#region src/module/server/router.ts
|
|
42753
|
+
//#region src/module/server/controllers/video.ts
|
|
44041
42754
|
/**
|
|
44042
|
-
*
|
|
42755
|
+
* 视频控制器
|
|
44043
42756
|
*/
|
|
44044
42757
|
/**
|
|
44045
42758
|
* 视频文件流传输
|
|
44046
|
-
* GET /api/kkk/stream/:filename
|
|
44047
|
-
* @param req 请求对象。
|
|
44048
|
-
* @param res 响应对象。
|
|
44049
42759
|
*/
|
|
44050
|
-
var
|
|
42760
|
+
var getVideoStream = (req, res) => {
|
|
44051
42761
|
const filenameParam = req.params.filename;
|
|
44052
42762
|
const filename = Array.isArray(filenameParam) ? filenameParam[0] : filenameParam;
|
|
44053
42763
|
if (!filename) {
|
|
@@ -44064,7 +42774,7 @@ var videoStreamRouter = (req, res) => {
|
|
|
44064
42774
|
const start = parseInt(parts[0], 10);
|
|
44065
42775
|
const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
|
|
44066
42776
|
if (start >= fileSize || end >= fileSize || start > end) {
|
|
44067
|
-
res.status(416).send("
|
|
42777
|
+
res.status(416).send("请求范围不满足");
|
|
44068
42778
|
return;
|
|
44069
42779
|
}
|
|
44070
42780
|
const chunksize = end - start + 1;
|
|
@@ -44096,7 +42806,7 @@ var videoStreamRouter = (req, res) => {
|
|
|
44096
42806
|
file.on("error", (err) => {
|
|
44097
42807
|
logger.error(`读取视频文件流时出错 (Full): ${err.message}`);
|
|
44098
42808
|
if (!res.headersSent) try {
|
|
44099
|
-
createNotFoundResponse(res, "
|
|
42809
|
+
createNotFoundResponse(res, "读取视频文件失败");
|
|
44100
42810
|
} catch (e) {
|
|
44101
42811
|
logger.error("发送读取错误响应失败:", e);
|
|
44102
42812
|
if (!res.writableEnded) res.end();
|
|
@@ -44110,18 +42820,15 @@ var videoStreamRouter = (req, res) => {
|
|
|
44110
42820
|
else if (!res.writableEnded) res.end();
|
|
44111
42821
|
} else {
|
|
44112
42822
|
logger.error(`处理视频数据请求时发生错误: ${error.message}`);
|
|
44113
|
-
if (!res.headersSent) createNotFoundResponse(res, "
|
|
42823
|
+
if (!res.headersSent) createNotFoundResponse(res, "服务器错误");
|
|
44114
42824
|
else if (!res.writableEnded) res.end();
|
|
44115
42825
|
}
|
|
44116
42826
|
}
|
|
44117
42827
|
};
|
|
44118
42828
|
/**
|
|
44119
|
-
*
|
|
44120
|
-
* GET /api/kkk/video/:filename
|
|
44121
|
-
* @param req 请求对象。
|
|
44122
|
-
* @param res 响应对象。
|
|
42829
|
+
* 视频播放页面(SSR)
|
|
44123
42830
|
*/
|
|
44124
|
-
var
|
|
42831
|
+
var getVideoPage = (req, res) => {
|
|
44125
42832
|
const filenameParam = req.params.filename;
|
|
44126
42833
|
const filename = Array.isArray(filenameParam) ? filenameParam[0] : filenameParam;
|
|
44127
42834
|
if (!filename) {
|
|
@@ -44130,7 +42837,7 @@ var getVideoRouter = (req, res) => {
|
|
|
44130
42837
|
}
|
|
44131
42838
|
const videoPath = Common.validateVideoRequest(filename, res);
|
|
44132
42839
|
if (!videoPath) return;
|
|
44133
|
-
const videoDataUrl = `/
|
|
42840
|
+
const videoDataUrl = `/kkk/v1/stream/${encodeURIComponent(filename)}`;
|
|
44134
42841
|
const previewInfo = Common.getVideoPreview(filename);
|
|
44135
42842
|
const removeCache = previewInfo?.removeCache ?? Config.app.removeCache;
|
|
44136
42843
|
const createdAt = previewInfo?.createdAt ?? Date.now();
|
|
@@ -44142,19 +42849,16 @@ var getVideoRouter = (req, res) => {
|
|
|
44142
42849
|
removeCache,
|
|
44143
42850
|
createdAt,
|
|
44144
42851
|
expireAt,
|
|
44145
|
-
eventsUrl: `/
|
|
42852
|
+
eventsUrl: `/kkk/v1/video/${encodeURIComponent(filename)}/events`
|
|
44146
42853
|
});
|
|
44147
42854
|
res.setHeader("Cache-Control", "no-cache");
|
|
44148
42855
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
44149
42856
|
res.send(htmlContent);
|
|
44150
42857
|
};
|
|
44151
42858
|
/**
|
|
44152
|
-
*
|
|
44153
|
-
* 持续向前端推送剩余有效期与删除状态,并在预览失效后自动结束连接。
|
|
44154
|
-
* @param req 请求对象。
|
|
44155
|
-
* @param res 响应对象。
|
|
42859
|
+
* 视频预览状态事件流(SSE)
|
|
44156
42860
|
*/
|
|
44157
|
-
var
|
|
42861
|
+
var getVideoEvents = (req, res) => {
|
|
44158
42862
|
const filenameParam = req.params.filename;
|
|
44159
42863
|
const filename = Array.isArray(filenameParam) ? filenameParam[0] : filenameParam;
|
|
44160
42864
|
if (!filename) {
|
|
@@ -44175,10 +42879,6 @@ var videoPreviewEventsRouter = (req, res) => {
|
|
|
44175
42879
|
res.setHeader("Connection", "keep-alive");
|
|
44176
42880
|
res.flushHeaders?.();
|
|
44177
42881
|
let timer = null;
|
|
44178
|
-
/**
|
|
44179
|
-
* 推送一次最新的预览状态到客户端。
|
|
44180
|
-
* @returns 当前预览是否已被删除或失效。
|
|
44181
|
-
*/
|
|
44182
42882
|
const sendPayload = () => {
|
|
44183
42883
|
const currentInfo = Common.getVideoPreview(filename) ?? previewInfo;
|
|
44184
42884
|
const now = Date.now();
|
|
@@ -44220,12 +42920,130 @@ var videoPreviewEventsRouter = (req, res) => {
|
|
|
44220
42920
|
});
|
|
44221
42921
|
};
|
|
44222
42922
|
//#endregion
|
|
44223
|
-
//#region src/module/server/
|
|
44224
|
-
|
|
44225
|
-
|
|
44226
|
-
|
|
44227
|
-
|
|
42923
|
+
//#region src/module/server/middlewares/auth.ts
|
|
42924
|
+
/**
|
|
42925
|
+
* Base64解码
|
|
42926
|
+
* @param str Base64编码的字符串
|
|
42927
|
+
* @returns 解码后的字符串
|
|
42928
|
+
*/
|
|
42929
|
+
var base64Decode = (str) => {
|
|
42930
|
+
return Buffer.from(str, "base64").toString("utf8");
|
|
44228
42931
|
};
|
|
42932
|
+
/**
|
|
42933
|
+
* URL解码
|
|
42934
|
+
* @param str URL编码的字符串
|
|
42935
|
+
* @returns 解码后的字符串
|
|
42936
|
+
*/
|
|
42937
|
+
var urlDecode = (str) => {
|
|
42938
|
+
return decodeURIComponent(str);
|
|
42939
|
+
};
|
|
42940
|
+
/**
|
|
42941
|
+
* 十六进制解码
|
|
42942
|
+
* @param str 十六进制编码的字符串
|
|
42943
|
+
* @returns 解码后的字符串
|
|
42944
|
+
*/
|
|
42945
|
+
var hexDecode = (str) => {
|
|
42946
|
+
return Buffer.from(str, "hex").toString("utf8");
|
|
42947
|
+
};
|
|
42948
|
+
/**
|
|
42949
|
+
* 反转字符串
|
|
42950
|
+
* @param str 待反转字符串
|
|
42951
|
+
* @returns 反转后的字符串
|
|
42952
|
+
*/
|
|
42953
|
+
var reverseString = (str) => {
|
|
42954
|
+
return str.split("").reverse().join("");
|
|
42955
|
+
};
|
|
42956
|
+
/**
|
|
42957
|
+
* 字符偏移解码
|
|
42958
|
+
* @param str 编码的字符串
|
|
42959
|
+
* @param offset 偏移量
|
|
42960
|
+
* @returns 解码后的字符串
|
|
42961
|
+
*/
|
|
42962
|
+
var charOffsetDecode = (str, offset = 5) => {
|
|
42963
|
+
return str.split("").map((char) => {
|
|
42964
|
+
const code = char.charCodeAt(0);
|
|
42965
|
+
return String.fromCharCode(code - offset);
|
|
42966
|
+
}).join("");
|
|
42967
|
+
};
|
|
42968
|
+
/**
|
|
42969
|
+
* 多层解码解密
|
|
42970
|
+
* @param str 多层编码的字符串
|
|
42971
|
+
* @returns 解码后的原始字符串
|
|
42972
|
+
*/
|
|
42973
|
+
var multiLayerDecode = (str) => {
|
|
42974
|
+
try {
|
|
42975
|
+
let decoded = base64Decode(str);
|
|
42976
|
+
decoded = urlDecode(decoded);
|
|
42977
|
+
decoded = base64Decode(decoded);
|
|
42978
|
+
decoded = reverseString(decoded);
|
|
42979
|
+
decoded = hexDecode(decoded);
|
|
42980
|
+
decoded = charOffsetDecode(decoded, 5);
|
|
42981
|
+
return decoded;
|
|
42982
|
+
} catch (error) {
|
|
42983
|
+
throw new Error("多层解码失败:" + error);
|
|
42984
|
+
}
|
|
42985
|
+
};
|
|
42986
|
+
/**
|
|
42987
|
+
* HMAC-SHA256签名验证中间件
|
|
42988
|
+
* @param req 请求对象
|
|
42989
|
+
* @param res 响应对象
|
|
42990
|
+
* @param next 下一个中间件函数
|
|
42991
|
+
*/
|
|
42992
|
+
var signatureVerificationMiddleware = (req, res, next) => {
|
|
42993
|
+
try {
|
|
42994
|
+
const encodedSignature = req.headers["x-signature"];
|
|
42995
|
+
const timestamp = req.headers["x-timestamp"];
|
|
42996
|
+
const nonce = req.headers["x-nonce"];
|
|
42997
|
+
const token = req.headers["authorization"]?.replace("Bearer ", "") || "";
|
|
42998
|
+
if (!encodedSignature || !timestamp || !nonce) return createBadRequestResponse(res, "缺少必要的签名参数");
|
|
42999
|
+
if (Math.abs(Date.now() - parseInt(timestamp)) > 300 * 1e3) return createBadRequestResponse(res, "请求时间戳已过期");
|
|
43000
|
+
let decodedSignature;
|
|
43001
|
+
try {
|
|
43002
|
+
decodedSignature = multiLayerDecode(encodedSignature);
|
|
43003
|
+
} catch (error) {
|
|
43004
|
+
return createBadRequestResponse(res, "签名格式错误:" + error);
|
|
43005
|
+
}
|
|
43006
|
+
const signatureString = `${req.method.toUpperCase()}|${req.headers["x-original-url"] || req.originalUrl}|${req.method === "GET" ? "" : JSON.stringify(req.body || {})}|${timestamp}|${nonce}`;
|
|
43007
|
+
const expectedSignature = crypto.createHmac("sha256", token).update(signatureString).digest("hex");
|
|
43008
|
+
if (decodedSignature !== expectedSignature) {
|
|
43009
|
+
logger.warn(`签名验证失败: 期望=${expectedSignature}, 解码后实际=${decodedSignature}, 签名字符串=${signatureString}`);
|
|
43010
|
+
return createBadRequestResponse(res, "签名验证失败");
|
|
43011
|
+
}
|
|
43012
|
+
next();
|
|
43013
|
+
} catch (error) {
|
|
43014
|
+
logger.error("签名验证中间件错误:", error);
|
|
43015
|
+
return createServerErrorResponse(res, "签名验证失败");
|
|
43016
|
+
}
|
|
43017
|
+
};
|
|
43018
|
+
//#endregion
|
|
43019
|
+
//#region src/module/server/routes/api.ts
|
|
43020
|
+
var import_lib = /* @__PURE__ */ __toESM(require_lib(), 1);
|
|
43021
|
+
/**
|
|
43022
|
+
* API 路由注册
|
|
43023
|
+
*/
|
|
43024
|
+
var apiRouter = express.Router();
|
|
43025
|
+
var authMiddlewares = [authMiddleware, signatureVerificationMiddleware];
|
|
43026
|
+
apiRouter.get(ROUTES.BOTS, ...authMiddlewares, getBots);
|
|
43027
|
+
apiRouter.get(ROUTES.BOT_INFO, ...authMiddlewares, getBotInfo);
|
|
43028
|
+
apiRouter.get(ROUTES.BOT_GROUPS, ...authMiddlewares, getBotGroups);
|
|
43029
|
+
apiRouter.get(ROUTES.BOT_GROUP_INFO, ...authMiddlewares, getBotGroupInfo);
|
|
43030
|
+
apiRouter.post(ROUTES.GROUPS_BATCH, ...authMiddlewares, getGroupsBatch);
|
|
43031
|
+
apiRouter.get(ROUTES.CONFIG, ...authMiddlewares, getAllConfig);
|
|
43032
|
+
apiRouter.post(ROUTES.CONFIG, ...authMiddlewares, updateAllConfig);
|
|
43033
|
+
apiRouter.get(ROUTES.VIDEO_STREAM, getVideoStream);
|
|
43034
|
+
apiRouter.get(ROUTES.VIDEO_EVENTS, getVideoEvents);
|
|
43035
|
+
//#endregion
|
|
43036
|
+
//#region src/module/server/routes/ssr.ts
|
|
43037
|
+
/**
|
|
43038
|
+
* SSR 路由注册
|
|
43039
|
+
*/
|
|
43040
|
+
var ssrRouter = express.Router();
|
|
43041
|
+
ssrRouter.get(ROUTES.VIDEO_PAGE, getVideoPage);
|
|
43042
|
+
//#endregion
|
|
43043
|
+
//#region src/module/server/routes/static.ts
|
|
43044
|
+
/**
|
|
43045
|
+
* 静态文件路由注册
|
|
43046
|
+
*/
|
|
44229
43047
|
var webDistPath = path.join(Root.pluginPath, "lib", "web");
|
|
44230
43048
|
var webIndexPath = path.join(webDistPath, "index.html");
|
|
44231
43049
|
var sendWebIndex = (res) => {
|
|
@@ -44234,10 +43052,34 @@ var sendWebIndex = (res) => {
|
|
|
44234
43052
|
res.setHeader("Cache-Control", "no-cache");
|
|
44235
43053
|
res.type("html").send(html);
|
|
44236
43054
|
} catch (error) {
|
|
44237
|
-
|
|
44238
|
-
|
|
44239
|
-
|
|
43055
|
+
logger.error("[karin-plugin-kkk] 读取 Web UI 入口文件失败:", error);
|
|
43056
|
+
createServerErrorResponse(res, "加载 Web UI 失败");
|
|
43057
|
+
}
|
|
43058
|
+
};
|
|
43059
|
+
var staticRouter = express.Router();
|
|
43060
|
+
var webStatic = express.static(webDistPath, {
|
|
43061
|
+
redirect: false,
|
|
43062
|
+
setHeaders: (res, filePath) => {
|
|
43063
|
+
if (filePath.endsWith(".html")) {
|
|
43064
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
43065
|
+
return;
|
|
43066
|
+
}
|
|
43067
|
+
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
|
44240
43068
|
}
|
|
43069
|
+
});
|
|
43070
|
+
staticRouter.use(webStatic);
|
|
43071
|
+
staticRouter.use((_req, res) => {
|
|
43072
|
+
sendWebIndex(res);
|
|
43073
|
+
});
|
|
43074
|
+
//#endregion
|
|
43075
|
+
//#region src/module/server/routes/index.ts
|
|
43076
|
+
/**
|
|
43077
|
+
* 主路由注册
|
|
43078
|
+
*/
|
|
43079
|
+
var server = express();
|
|
43080
|
+
var proxyOptions = {
|
|
43081
|
+
target: "https://developer.huawei.com",
|
|
43082
|
+
changeOrigin: true
|
|
44241
43083
|
};
|
|
44242
43084
|
server.use(import_lib.default());
|
|
44243
43085
|
server.use("/", createProxyMiddleware(proxyOptions));
|
|
@@ -44254,39 +43096,21 @@ if (process.env.NODE_ENV !== "test") checkPort(3780).then((isOpen) => {
|
|
|
44254
43096
|
var app$1 = express.Router();
|
|
44255
43097
|
app$1.use(express.json());
|
|
44256
43098
|
app$1.use(express.urlencoded({ extended: true }));
|
|
44257
|
-
if (Config.
|
|
44258
|
-
app$1.use("/amagi/api/bilibili", createBilibiliRoutes(Config.cookies.bilibili));
|
|
44259
|
-
app$1.use("/amagi/api/douyin", createDouyinRoutes(Config.cookies.douyin));
|
|
44260
|
-
app$1.use("/amagi/api/kuaishou", createKuaishouRoutes(Config.cookies.kuaishou));
|
|
44261
|
-
app$1.use("/amagi/api/xiaohongshu", createXiaohongshuRoutes(Config.cookies.xiaohongshu));
|
|
44262
|
-
} else if (Config.
|
|
44263
|
-
bilibili: Config.cookies.bilibili,
|
|
44264
|
-
douyin: Config.cookies.douyin,
|
|
44265
|
-
kuaishou: Config.cookies.kuaishou,
|
|
44266
|
-
xiaohongshu: Config.cookies.xiaohongshu
|
|
44267
|
-
} }).startServer(Config.
|
|
44268
|
-
app$1.get("/stream/:filename", videoStreamRouter);
|
|
44269
|
-
app$1.get("/video/:filename", getVideoRouter);
|
|
44270
|
-
app$1.get("/video/:filename/events", videoPreviewEventsRouter);
|
|
43099
|
+
if (Config.amagi.APIServer && Config.amagi.APIServerMount) {
|
|
43100
|
+
app$1.use("/amagi/api/bilibili", createBilibiliRoutes(Config.amagi.cookies.bilibili));
|
|
43101
|
+
app$1.use("/amagi/api/douyin", createDouyinRoutes(Config.amagi.cookies.douyin));
|
|
43102
|
+
app$1.use("/amagi/api/kuaishou", createKuaishouRoutes(Config.amagi.cookies.kuaishou));
|
|
43103
|
+
app$1.use("/amagi/api/xiaohongshu", createXiaohongshuRoutes(Config.amagi.cookies.xiaohongshu));
|
|
43104
|
+
} else if (Config.amagi.APIServer) new Client({ cookies: {
|
|
43105
|
+
bilibili: Config.amagi.cookies.bilibili,
|
|
43106
|
+
douyin: Config.amagi.cookies.douyin,
|
|
43107
|
+
kuaishou: Config.amagi.cookies.kuaishou,
|
|
43108
|
+
xiaohongshu: Config.amagi.cookies.xiaohongshu
|
|
43109
|
+
} }).startServer(Config.amagi.APIServerPort);
|
|
44271
43110
|
app$1.use("/v1", apiRouter);
|
|
44272
|
-
|
|
44273
|
-
|
|
44274
|
-
|
|
44275
|
-
setHeaders: (res, filePath) => {
|
|
44276
|
-
if (filePath.endsWith(".html")) {
|
|
44277
|
-
res.setHeader("Cache-Control", "no-cache");
|
|
44278
|
-
return;
|
|
44279
|
-
}
|
|
44280
|
-
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
|
44281
|
-
}
|
|
44282
|
-
});
|
|
44283
|
-
staticRouter.use(webStatic);
|
|
44284
|
-
staticRouter.use((_req, res) => {
|
|
44285
|
-
sendWebIndex(res);
|
|
44286
|
-
});
|
|
44287
|
-
/** 将子路由挂载到主路由上 */
|
|
44288
|
-
app.use("/kkk", staticRouter);
|
|
44289
|
-
app.use("/api/kkk", app$1);
|
|
43111
|
+
app$1.use(SSR_PREFIX, ssrRouter);
|
|
43112
|
+
app$1.use(ASSETS_PREFIX, staticRouter);
|
|
43113
|
+
app.use(KKK_PREFIX, app$1);
|
|
44290
43114
|
//#endregion
|
|
44291
43115
|
//#region src/setup.ts
|
|
44292
43116
|
var requireVersion = "1.16.1";
|
|
@@ -44333,4 +43157,4 @@ mkdirSync(`${karinPathBase}/${Root.pluginName}/data`);
|
|
|
44333
43157
|
mkdirSync(Common.tempDri.images);
|
|
44334
43158
|
mkdirSync(Common.tempDri.video);
|
|
44335
43159
|
//#endregion
|
|
44336
|
-
export { createLineBreakNode as $,
|
|
43160
|
+
export { createLineBreakNode as $, xiaohongshuSign as $n, emitLogDebug as $r, CommentType as $t, template_default as A, getHeadersAndData as An, BilibiliBv2AvParamsSchema as Ar, KuaishouApiRoutes as At, DouyinDBBase as B, douyinApiUrls as Bn, BilibiliQrcodeParamsSchema as Br, DouyinInternalMethods as Bt, help as C, qtparam as Cn, BilibiliApplyCaptchaParamsSchema as Cr, CreateApp as Ct, removeOldFiles as D, logger$2 as Dn, BilibiliAv2BvParamsSchema as Dr, BilibiliMethodMapping as Dt, dylogin as E, logMiddleware as En, BilibiliArticleParamsSchema as Er, BilibiliApiRoutes as Et, getBilibiliDB as F, kuaishouFetcher$1 as Fn, BilibiliDynamicParamsSchema as Fr, getEnglishMethodName as Ft, createAtNode as G, createSuccessResponse$1 as Gn, BilibiliVideoDownloadParamsSchema as Gr, MethodMaps as Gt, reactServerRender as H, bilibiliFetcher$1 as Hn, BilibiliUserParamsSchema as Hr, KuaishouFetcherMethods as Ht, getDouyinDB as I, kuaishouSign as In, BilibiliEmojiParamsSchema as Ir, BilibiliFetcherMethods as It, createEmojiNode as J, validateKuaishouParams as Jn, emitApiError as Jr, XiaohongshuMethodToFetcher as Jt, createBlockquoteNode as K, validateBilibiliParams as Kn, BilibiliVideoParamsSchema as Kr, XiaohongshuFetcherMethods as Kt, getStatisticsDB as L, kuaishouApiUrls as Ln, BilibiliLiveParamsSchema as Lr, BilibiliInternalMethods as Lt, bilibiliDBInstance as M, createBoundXiaohongshuFetcher as Mn, BilibiliCommentParamsSchema as Mr, XiaohongshuApiRoutes as Mt, cleanOldDynamicCache as N, xiaohongshuFetcher$1 as Nn, BilibiliCommentReplyParamsSchema as Nr, XiaohongshuMethodMapping as Nt, task as O, fetchData as On, BilibiliBangumiInfoParamsSchema as Or, DouyinApiRoutes as Ot, douyinDBInstance as P, createBoundKuaishouFetcher as Pn, BilibiliDanmakuParamsSchema as Pr, getApiRoute as Pt, createImageNode as Q, xiaohongshuApiUrls as Qn, emitLog as Qr, MajorType as Qt, initAllDatabases as R, douyinFetcher$1 as Rn, BilibiliLoginParamsSchema as Rr, BilibiliMethodToFetcher as Rt, setdyPush as S, bv2av as Sn, DouyinWorkParamsSchema as Sr, softFetch as St, biLogin as T, initLogger as Tn, BilibiliArticleInfoParamsSchema as Tr, amagiClient$1 as Tt, renderVideoPreviewPage as U, createBoundBilibiliFetcher as Un, BilibiliValidateCaptchaParamsSchema as Ur, KuaishouInternalMethods as Ut, BilibiliDBBase as V, douyinSign as Vn, BilibiliQrcodeStatusParamsSchema as Vr, DouyinMethodToFetcher as Vt, renderRichTextToReact as W, createErrorResponse as Wn, BilibiliValidationSchemas as Wr, KuaishouMethodToFetcher as Wt, createHeadingNode as X, XiaohongshuMethodRoutes as Xn, emitHttpRequest as Xr, DynamicType as Xt, createHashtagNode as Y, validateXiaohongshuParams as Yn, emitApiSuccess as Yr, toFetcherMethod as Yt, createHorizontalRuleNode as Z, XiaohongshuValidationSchemas as Zn, emitHttpResponse as Zr, AdditionalType as Zt, douyinPush as _, handleError as _n, DouyinQrcodeParamsSchema as _r, SOFT_ERROR_CODES as _t, bilibiliAPP as a, emitNetworkRetry as ai, kuaishouUtils as an, KuaishouUserWorkListParamsSchema as ar, createParagraphNode as at, globalIgnore as b, parseDmSegMobileReply as bn, DouyinUserParamsSchema as br, kuaishouFetcher as bt, prefix as c, bilibiliApiUrls as ci, kuaishou$1 as cn, DouyinCommentParamsSchema as cr, createTextNode as ct, globalStatistics as d, getKuaishouData as di, createBoundDouyinApi as dn, DouyinEmojiListParamsSchema as dr, createVoteNode as dt, emitLogError as ei, createAmagiClient as en, KuaishouCommentParamsSchema as er, createLinkCardNode as et, groupStatistics as f, Root as fi, douyin$1 as fn, DouyinEmojiProParamsSchema as fr, createWebLinkNode as ft, changeBotID as g, ValidationError as gn, DouyinMusicParamsSchema as gr, AmagiError as gt, bilibiliPushList as h, ApiError as hn, DouyinMethodRoutes as hr, AmagiBase as ht, update as i, emitNetworkError as ii, xiaohongshu$1 as in, KuaishouUserProfileParamsSchema as ir, createMentionNode as it, webConfig as j, isNetworkErrorResult as jn, BilibiliColumnInfoParamsSchema as jr, KuaishouMethodMapping as jt, testWrapWithErrorHandler as k, fetchResponse as kn, BilibiliBangumiStreamParamsSchema as kr, DouyinMethodMapping as kt, xiaohongshuAPP as l, getBilibiliData as li, douyinUtils as ln, DouyinCommentReplyParamsSchema as lr, createTopicNode as lt, bilibiliPush as m, createBilibiliRoutes as mn, DouyinLiveRoomParamsSchema as mr, normalizeRichTextNodes as mt, kkkUpdateCommand as n, emitLogMark as ni, createXiaohongshuRoutes as nn, KuaishouLiveRoomInfoParamsSchema as nr, createListNode as nt, douyinAPP as o, bilibili$1 as oi, createKuaishouRoutes as on, KuaishouValidationSchemas as or, createRichTextDocument as ot, qrLogin as p, bilibiliUtils as pn, DouyinHotWordsParamsSchema as pr, extractRichTextPlainText as pt, createCodeBlockNode as q, validateDouyinParams as qn, amagiEvents as qr, XiaohongshuInternalMethods as qt, kkkUpdateTest as r, emitLogWarn as ri, createBoundXiaohongshuApi as rn, KuaishouMethodRoutes as rr, createLotteryNode as rt, kuaishouAPP as s, createBoundBilibiliApi as si, createBoundKuaishouApi as sn, KuaishouVideoParamsSchema as sr, createSearchKeywordNode as st, kkkUpdate as t, emitLogInfo as ti, xiaohongshuUtils as tn, KuaishouEmojiParamsSchema as tr, createListItemNode as tt, testPush as u, getDouyinData as ui, createDouyinRoutes as un, DouyinDanmakuParamsSchema as ur, createViewPictureNode as ut, douyinPushList as v, bilibiliErrorCodeMap as vn, DouyinSearchParamsSchema as vr, bilibiliFetcher as vt, version as w, httpLogger as wn, BilibiliArticleCardParamsSchema as wr, amagi as wt, setbiliPush as x, av2bv as xn, DouyinValidationSchemas as xr, reloadAmagiConfig as xt, forcePush as y, wbi_sign as yn, DouyinUserListParamsSchema as yr, douyinFetcher as yt, StatisticsDBBase as z, createBoundDouyinFetcher as zn, BilibiliMethodRoutes as zr, DouyinFetcherMethods as zt };
|