bililive-cli 3.8.2 → 3.9.1
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.
|
@@ -11533,9 +11533,37 @@ const APP_DEFAULT_CONFIG = {
|
|
|
11533
11533
|
},
|
|
11534
11534
|
llmPresets: [],
|
|
11535
11535
|
ai: {
|
|
11536
|
-
vendors: [
|
|
11536
|
+
vendors: [
|
|
11537
|
+
{
|
|
11538
|
+
id: "3d09badd-5402-4b80-9113-48c0739d51b9",
|
|
11539
|
+
name: "阿里云",
|
|
11540
|
+
provider: "aliyun",
|
|
11541
|
+
apiKey: "",
|
|
11542
|
+
},
|
|
11543
|
+
],
|
|
11544
|
+
models: [
|
|
11545
|
+
{
|
|
11546
|
+
vendorId: "3d09badd-5402-4b80-9113-48c0739d51b9",
|
|
11547
|
+
modelId: "116497be-e650-4b21-8769-536859cb16dc",
|
|
11548
|
+
modelName: "fun-asr",
|
|
11549
|
+
remark: "语音识别",
|
|
11550
|
+
tags: ["asr"],
|
|
11551
|
+
config: {},
|
|
11552
|
+
},
|
|
11553
|
+
{
|
|
11554
|
+
vendorId: "3d09badd-5402-4b80-9113-48c0739d51b9",
|
|
11555
|
+
modelId: "ca277547-fabd-462b-99d2-cf76f56002e6",
|
|
11556
|
+
modelName: "qwen-plus",
|
|
11557
|
+
remark: "通用大模型",
|
|
11558
|
+
tags: ["llm"],
|
|
11559
|
+
config: {},
|
|
11560
|
+
},
|
|
11561
|
+
],
|
|
11562
|
+
songRecognizeAsr: {
|
|
11563
|
+
modelId: "",
|
|
11564
|
+
},
|
|
11537
11565
|
songRecognizeLlm: {
|
|
11538
|
-
|
|
11566
|
+
modelId: "",
|
|
11539
11567
|
prompt: `
|
|
11540
11568
|
你是一个极度专业的音乐识别专家,擅长从存在误差的 ASR(语音识别)文本中提取核心语义,并精准锁定歌曲信息。
|
|
11541
11569
|
从搜索结果中精确获取歌名以及需确保返回的【歌词】版本为官方标准发行版,不要遗漏,请提供【歌词】
|
|
@@ -11545,14 +11573,13 @@ const APP_DEFAULT_CONFIG = {
|
|
|
11545
11573
|
"lyrics": "[查询到的完整标准歌词]",
|
|
11546
11574
|
"name": "[准确的歌曲名称]"
|
|
11547
11575
|
}`,
|
|
11548
|
-
model: "qwen-plus",
|
|
11549
11576
|
enableSearch: true,
|
|
11550
11577
|
maxInputLength: 300,
|
|
11551
11578
|
enableStructuredOutput: true,
|
|
11552
11579
|
lyricOptimize: true,
|
|
11553
11580
|
},
|
|
11554
11581
|
songLyricOptimize: {
|
|
11555
|
-
|
|
11582
|
+
modelId: "",
|
|
11556
11583
|
prompt: `
|
|
11557
11584
|
# Role
|
|
11558
11585
|
你是一个极度严谨的音频字幕对齐专家,擅长将破碎的 ASR 识别结果(ASR_Data)完美映射到标准文本(Standard_Lyrics)上。
|
|
@@ -11581,9 +11608,11 @@ const APP_DEFAULT_CONFIG = {
|
|
|
11581
11608
|
## Output Format
|
|
11582
11609
|
仅输出 JSON 对象,格式如下: {"data": [{"st": 123, "et": 456, "t": "标准歌词内容"}]}
|
|
11583
11610
|
`,
|
|
11584
|
-
model: "",
|
|
11585
11611
|
enableStructuredOutput: true,
|
|
11586
11612
|
},
|
|
11613
|
+
subtitleRecognize: {
|
|
11614
|
+
modelId: "",
|
|
11615
|
+
},
|
|
11587
11616
|
},
|
|
11588
11617
|
biliUpload: {
|
|
11589
11618
|
line: "auto",
|
|
@@ -11617,7 +11646,7 @@ const APP_DEFAULT_CONFIG = {
|
|
|
11617
11646
|
qualityRetry: 0,
|
|
11618
11647
|
videoFormat: "auto",
|
|
11619
11648
|
recorderType: "bililive",
|
|
11620
|
-
useServerTimestamp:
|
|
11649
|
+
useServerTimestamp: false,
|
|
11621
11650
|
recordRetryImmediately: true,
|
|
11622
11651
|
bilibili: {
|
|
11623
11652
|
uid: undefined,
|
|
@@ -11773,6 +11802,16 @@ const amfPresets = [
|
|
|
11773
11802
|
label: "quality",
|
|
11774
11803
|
},
|
|
11775
11804
|
];
|
|
11805
|
+
const videoToolBoxPresets = [
|
|
11806
|
+
{
|
|
11807
|
+
value: "1",
|
|
11808
|
+
label: "realtime",
|
|
11809
|
+
},
|
|
11810
|
+
{
|
|
11811
|
+
value: "0",
|
|
11812
|
+
label: "not realtime",
|
|
11813
|
+
},
|
|
11814
|
+
];
|
|
11776
11815
|
const amfAv1Presets = [
|
|
11777
11816
|
...amfPresets,
|
|
11778
11817
|
{
|
|
@@ -11847,6 +11886,17 @@ const videoEncoders = [
|
|
|
11847
11886
|
],
|
|
11848
11887
|
presets: amfPresets,
|
|
11849
11888
|
},
|
|
11889
|
+
{
|
|
11890
|
+
value: "h264_videotoolbox",
|
|
11891
|
+
label: "H.264(Apple)",
|
|
11892
|
+
birateControls: [
|
|
11893
|
+
{
|
|
11894
|
+
value: "VBR",
|
|
11895
|
+
label: "平均比特率",
|
|
11896
|
+
},
|
|
11897
|
+
],
|
|
11898
|
+
presets: videoToolBoxPresets,
|
|
11899
|
+
},
|
|
11850
11900
|
{
|
|
11851
11901
|
value: "libx265",
|
|
11852
11902
|
label: "H.265(x265)",
|
|
@@ -11903,9 +11953,20 @@ const videoEncoders = [
|
|
|
11903
11953
|
],
|
|
11904
11954
|
presets: amfPresets,
|
|
11905
11955
|
},
|
|
11956
|
+
{
|
|
11957
|
+
value: "hevc_videotoolbox",
|
|
11958
|
+
label: "H.265(Apple)",
|
|
11959
|
+
birateControls: [
|
|
11960
|
+
{
|
|
11961
|
+
value: "VBR",
|
|
11962
|
+
label: "平均比特率",
|
|
11963
|
+
},
|
|
11964
|
+
],
|
|
11965
|
+
presets: videoToolBoxPresets,
|
|
11966
|
+
},
|
|
11906
11967
|
{
|
|
11907
11968
|
value: "libsvtav1",
|
|
11908
|
-
label: "AV1
|
|
11969
|
+
label: "AV1(libsvtav1)",
|
|
11909
11970
|
birateControls: [
|
|
11910
11971
|
{
|
|
11911
11972
|
value: "CRF",
|
|
@@ -11977,7 +12038,7 @@ const videoEncoders = [
|
|
|
11977
12038
|
},
|
|
11978
12039
|
{
|
|
11979
12040
|
value: "av1_qsv",
|
|
11980
|
-
label: "AV1
|
|
12041
|
+
label: "AV1(Intel QSV)",
|
|
11981
12042
|
birateControls: [
|
|
11982
12043
|
{
|
|
11983
12044
|
value: "ICQ",
|
|
@@ -11992,7 +12053,7 @@ const videoEncoders = [
|
|
|
11992
12053
|
},
|
|
11993
12054
|
{
|
|
11994
12055
|
value: "av1_nvenc",
|
|
11995
|
-
label: "AV1
|
|
12056
|
+
label: "AV1(NVIDIA NVEnc)",
|
|
11996
12057
|
birateControls: [
|
|
11997
12058
|
{
|
|
11998
12059
|
value: "CQ",
|
|
@@ -12007,7 +12068,7 @@ const videoEncoders = [
|
|
|
12007
12068
|
},
|
|
12008
12069
|
{
|
|
12009
12070
|
value: "av1_amf",
|
|
12010
|
-
label: "AV1
|
|
12071
|
+
label: "AV1(AMD AMF)",
|
|
12011
12072
|
birateControls: [
|
|
12012
12073
|
{
|
|
12013
12074
|
value: "VBR",
|
|
@@ -12016,6 +12077,17 @@ const videoEncoders = [
|
|
|
12016
12077
|
],
|
|
12017
12078
|
presets: amfAv1Presets,
|
|
12018
12079
|
},
|
|
12080
|
+
{
|
|
12081
|
+
value: "av1_videotoolbox",
|
|
12082
|
+
label: "AV1(Apple)",
|
|
12083
|
+
birateControls: [
|
|
12084
|
+
{
|
|
12085
|
+
value: "VBR",
|
|
12086
|
+
label: "平均比特率",
|
|
12087
|
+
},
|
|
12088
|
+
],
|
|
12089
|
+
presets: videoToolBoxPresets,
|
|
12090
|
+
},
|
|
12019
12091
|
];
|
|
12020
12092
|
const defaultRecordConfig = {
|
|
12021
12093
|
providerId: "DouYu",
|
|
@@ -12045,7 +12117,7 @@ const defaultRecordConfig = {
|
|
|
12045
12117
|
recorderType: "ffmpeg",
|
|
12046
12118
|
cookie: "",
|
|
12047
12119
|
doubleScreen: true,
|
|
12048
|
-
useServerTimestamp:
|
|
12120
|
+
useServerTimestamp: false,
|
|
12049
12121
|
handleTime: [null, null],
|
|
12050
12122
|
debugLevel: "none",
|
|
12051
12123
|
api: "web",
|
|
@@ -12146,6 +12218,18 @@ const baseFfmpegPresets = [
|
|
|
12146
12218
|
preset: "balanced",
|
|
12147
12219
|
},
|
|
12148
12220
|
},
|
|
12221
|
+
{
|
|
12222
|
+
id: "b_videotoolbox_h264",
|
|
12223
|
+
name: "H.264(Apple)",
|
|
12224
|
+
config: {
|
|
12225
|
+
...commonPresetParams,
|
|
12226
|
+
encoder: "h264_videotoolbox",
|
|
12227
|
+
bitrateControl: "VBR",
|
|
12228
|
+
bitrate: 8000,
|
|
12229
|
+
bit10: false,
|
|
12230
|
+
preset: "1",
|
|
12231
|
+
},
|
|
12232
|
+
},
|
|
12149
12233
|
{
|
|
12150
12234
|
id: "b_libx265",
|
|
12151
12235
|
name: "H.265(x265)",
|
|
@@ -12198,9 +12282,21 @@ const baseFfmpegPresets = [
|
|
|
12198
12282
|
preset: "balanced",
|
|
12199
12283
|
},
|
|
12200
12284
|
},
|
|
12285
|
+
{
|
|
12286
|
+
id: "b_videotoolbox_h265",
|
|
12287
|
+
name: "H.265(Apple)",
|
|
12288
|
+
config: {
|
|
12289
|
+
...commonPresetParams,
|
|
12290
|
+
encoder: "hevc_videotoolbox",
|
|
12291
|
+
bitrateControl: "VBR",
|
|
12292
|
+
bitrate: 8000,
|
|
12293
|
+
bit10: false,
|
|
12294
|
+
preset: "1",
|
|
12295
|
+
},
|
|
12296
|
+
},
|
|
12201
12297
|
{
|
|
12202
12298
|
id: "b_svt_av1",
|
|
12203
|
-
name: "AV1
|
|
12299
|
+
name: "AV1(libsvtav1)",
|
|
12204
12300
|
config: {
|
|
12205
12301
|
...commonPresetParams,
|
|
12206
12302
|
encoder: "libsvtav1",
|
|
@@ -12214,7 +12310,7 @@ const baseFfmpegPresets = [
|
|
|
12214
12310
|
},
|
|
12215
12311
|
{
|
|
12216
12312
|
id: "b_qsv_av1",
|
|
12217
|
-
name: "AV1
|
|
12313
|
+
name: "AV1(Intel QSV)",
|
|
12218
12314
|
config: {
|
|
12219
12315
|
...commonPresetParams,
|
|
12220
12316
|
encoder: "av1_qsv",
|
|
@@ -12227,7 +12323,7 @@ const baseFfmpegPresets = [
|
|
|
12227
12323
|
},
|
|
12228
12324
|
{
|
|
12229
12325
|
id: "b_nvenc_av1",
|
|
12230
|
-
name: "AV1
|
|
12326
|
+
name: "AV1(NVIDIA NVEnc)",
|
|
12231
12327
|
config: {
|
|
12232
12328
|
...commonPresetParams,
|
|
12233
12329
|
encoder: "av1_nvenc",
|
|
@@ -12241,7 +12337,7 @@ const baseFfmpegPresets = [
|
|
|
12241
12337
|
},
|
|
12242
12338
|
{
|
|
12243
12339
|
id: "b_amf_av1",
|
|
12244
|
-
name: "AV1
|
|
12340
|
+
name: "AV1(AMD AMF)",
|
|
12245
12341
|
config: {
|
|
12246
12342
|
...commonPresetParams,
|
|
12247
12343
|
encoder: "av1_amf",
|
|
@@ -12251,6 +12347,18 @@ const baseFfmpegPresets = [
|
|
|
12251
12347
|
preset: "balanced",
|
|
12252
12348
|
},
|
|
12253
12349
|
},
|
|
12350
|
+
{
|
|
12351
|
+
id: "b_videotoolbox_av1",
|
|
12352
|
+
name: "AV1(Apple)",
|
|
12353
|
+
config: {
|
|
12354
|
+
...commonPresetParams,
|
|
12355
|
+
encoder: "av1_videotoolbox",
|
|
12356
|
+
bitrateControl: "VBR",
|
|
12357
|
+
bitrate: 8000,
|
|
12358
|
+
bit10: false,
|
|
12359
|
+
preset: "1",
|
|
12360
|
+
},
|
|
12361
|
+
},
|
|
12254
12362
|
];
|
|
12255
12363
|
class FFmpegPreset extends CommonPreset {
|
|
12256
12364
|
constructor({ globalConfig }) {
|
|
@@ -12348,7 +12456,7 @@ const DEFAULT_BILIUP_CONFIG = {
|
|
|
12348
12456
|
dynamic: "",
|
|
12349
12457
|
cover: "",
|
|
12350
12458
|
noReprint: 0,
|
|
12351
|
-
watermark:
|
|
12459
|
+
watermark: 0,
|
|
12352
12460
|
openElec: 0,
|
|
12353
12461
|
closeDanmu: 0,
|
|
12354
12462
|
closeReply: 0,
|
|
@@ -44431,7 +44539,7 @@ async function trash(paths, options) {
|
|
|
44431
44539
|
} else if (process$2.platform === 'win32') {
|
|
44432
44540
|
module = await Promise.resolve().then(function () { return require('./windows-CJCw0QtL.cjs'); });
|
|
44433
44541
|
} else {
|
|
44434
|
-
module = await Promise.resolve().then(function () { return require('./linux-
|
|
44542
|
+
module = await Promise.resolve().then(function () { return require('./linux-CNe7K9Gx.cjs'); });
|
|
44435
44543
|
}
|
|
44436
44544
|
|
|
44437
44545
|
return module.default(paths);
|
|
@@ -53122,11 +53230,15 @@ const getHardwareAcceleration = (encoder) => {
|
|
|
53122
53230
|
else if (["libx264", "libx265", "libsvtav1"].includes(encoder)) {
|
|
53123
53231
|
return "cpu";
|
|
53124
53232
|
}
|
|
53233
|
+
else if (["h264_videotoolbox", "hevc_videotoolbox", "av1_videotoolbox"].includes(encoder)) {
|
|
53234
|
+
return "videotoolbox";
|
|
53235
|
+
}
|
|
53125
53236
|
else {
|
|
53126
53237
|
throw new Error(`未知的编码器: ${encoder}`);
|
|
53127
53238
|
}
|
|
53128
53239
|
};
|
|
53129
53240
|
const genFfmpegParams = (options) => {
|
|
53241
|
+
const hardware = getHardwareAcceleration(options.encoder);
|
|
53130
53242
|
const result = [];
|
|
53131
53243
|
if (options.encoder) {
|
|
53132
53244
|
result.push(`-c:v ${options.encoder}`);
|
|
@@ -53158,7 +53270,12 @@ const genFfmpegParams = (options) => {
|
|
|
53158
53270
|
if (options.preset) {
|
|
53159
53271
|
const encoder = videoEncoders.find((item) => item.value === options.encoder);
|
|
53160
53272
|
if ((encoder?.presets ?? []).findIndex((item) => item.value === options.preset) !== -1) {
|
|
53161
|
-
|
|
53273
|
+
if (hardware === "videotoolbox") {
|
|
53274
|
+
result.push(`-realtime ${options.preset}`);
|
|
53275
|
+
}
|
|
53276
|
+
else {
|
|
53277
|
+
result.push(`-preset ${options.preset}`);
|
|
53278
|
+
}
|
|
53162
53279
|
}
|
|
53163
53280
|
}
|
|
53164
53281
|
if (["libsvtav1"].includes(options.encoder)) {
|
|
@@ -57500,6 +57617,9 @@ class Platform extends BaseRequest {
|
|
|
57500
57617
|
...archiveData,
|
|
57501
57618
|
csrf: csrf,
|
|
57502
57619
|
...options,
|
|
57620
|
+
watermark: {
|
|
57621
|
+
state: options.watermark?.state ?? archive.watermark.state,
|
|
57622
|
+
},
|
|
57503
57623
|
};
|
|
57504
57624
|
this.checkOptions(data);
|
|
57505
57625
|
data.aid = Number(data.aid);
|
|
@@ -178148,6 +178268,10 @@ class RecordHistoryModel extends BaseModel {
|
|
|
178148
178268
|
name: "idx_record_history_live_video",
|
|
178149
178269
|
sql: `CREATE INDEX IF NOT EXISTS idx_record_history_live_video ON record_history(live_id, video_file)`,
|
|
178150
178270
|
},
|
|
178271
|
+
{
|
|
178272
|
+
name: "idx_record_history_record_start_time",
|
|
178273
|
+
sql: `CREATE INDEX IF NOT EXISTS idx_record_history_record_start_time ON record_history(record_start_time)`,
|
|
178274
|
+
},
|
|
178151
178275
|
];
|
|
178152
178276
|
for (const index of indexes) {
|
|
178153
178277
|
if (!this.checkIndexExists(index.name)) {
|
|
@@ -178283,6 +178407,41 @@ class RecordHistoryModel extends BaseModel {
|
|
|
178283
178407
|
const data = dataStmt.all(...params, pageSize, offset);
|
|
178284
178408
|
return { data, total };
|
|
178285
178409
|
}
|
|
178410
|
+
/**
|
|
178411
|
+
* 查询总视频时长
|
|
178412
|
+
* @param options 查询参数
|
|
178413
|
+
* @returns 总时长(秒)
|
|
178414
|
+
*/
|
|
178415
|
+
getTotalDuration(options) {
|
|
178416
|
+
const { streamerId, startTime, endTime } = options || {};
|
|
178417
|
+
// 构建WHERE条件
|
|
178418
|
+
const whereConditions = [];
|
|
178419
|
+
const params = [];
|
|
178420
|
+
// 过滤掉 video_duration 为 null 或 0 的记录
|
|
178421
|
+
whereConditions.push("video_duration IS NOT NULL");
|
|
178422
|
+
whereConditions.push("video_duration > 0");
|
|
178423
|
+
if (streamerId) {
|
|
178424
|
+
whereConditions.push("streamer_id = ?");
|
|
178425
|
+
params.push(streamerId);
|
|
178426
|
+
}
|
|
178427
|
+
if (startTime) {
|
|
178428
|
+
whereConditions.push("record_start_time >= ?");
|
|
178429
|
+
params.push(startTime);
|
|
178430
|
+
}
|
|
178431
|
+
if (endTime) {
|
|
178432
|
+
whereConditions.push("record_start_time <= ?");
|
|
178433
|
+
params.push(endTime);
|
|
178434
|
+
}
|
|
178435
|
+
const whereClause = `WHERE ${whereConditions.join(" AND ")}`;
|
|
178436
|
+
const sql = `
|
|
178437
|
+
SELECT COALESCE(SUM(video_duration), 0) as total_duration
|
|
178438
|
+
FROM ${this.tableName}
|
|
178439
|
+
${whereClause}
|
|
178440
|
+
`;
|
|
178441
|
+
const stmt = this.db.prepare(sql);
|
|
178442
|
+
const result = stmt.get(...params);
|
|
178443
|
+
return result.total_duration || 0;
|
|
178444
|
+
}
|
|
178286
178445
|
}
|
|
178287
178446
|
|
|
178288
178447
|
const BaseUploadPart = z.object({
|
|
@@ -178590,6 +178749,21 @@ class RecordHistoryService {
|
|
|
178590
178749
|
getLastRecordTimes(streamerIds) {
|
|
178591
178750
|
return this.recordHistoryModel.getLastRecordTimes(streamerIds);
|
|
178592
178751
|
}
|
|
178752
|
+
/**
|
|
178753
|
+
* 查询总视频时长,默认查询最近一个月
|
|
178754
|
+
* @param options 查询参数
|
|
178755
|
+
* @returns 总时长(秒)
|
|
178756
|
+
*/
|
|
178757
|
+
getTotalDuration(options) {
|
|
178758
|
+
const now = Math.floor(Date.now() / 1000);
|
|
178759
|
+
const oneMonthAgo = now - 30 * 24 * 60 * 60; // 30天前的时间戳
|
|
178760
|
+
const queryOptions = {
|
|
178761
|
+
streamerId: options?.streamerId,
|
|
178762
|
+
startTime: options?.startTime ?? oneMonthAgo,
|
|
178763
|
+
endTime: options?.endTime ?? now,
|
|
178764
|
+
};
|
|
178765
|
+
return this.recordHistoryModel.getTotalDuration(queryOptions);
|
|
178766
|
+
}
|
|
178593
178767
|
}
|
|
178594
178768
|
|
|
178595
178769
|
class UploadPartService {
|
|
@@ -178905,7 +179079,7 @@ let virtualRecordService;
|
|
|
178905
179079
|
let videoSubDataService;
|
|
178906
179080
|
let streamerService;
|
|
178907
179081
|
let videoSubService;
|
|
178908
|
-
|
|
179082
|
+
exports.recordHistoryService = void 0;
|
|
178909
179083
|
let uploadPartService;
|
|
178910
179084
|
let danmuService;
|
|
178911
179085
|
const initDB = (dbRootPath) => {
|
|
@@ -178937,7 +179111,7 @@ const setExportServices = (dbContainer) => {
|
|
|
178937
179111
|
videoSubDataService = dbContainer.resolve("videoSubDataService");
|
|
178938
179112
|
streamerService = dbContainer.resolve("streamerService");
|
|
178939
179113
|
videoSubService = dbContainer.resolve("videoSubService");
|
|
178940
|
-
recordHistoryService = dbContainer.resolve("recordHistoryService");
|
|
179114
|
+
exports.recordHistoryService = dbContainer.resolve("recordHistoryService");
|
|
178941
179115
|
uploadPartService = dbContainer.resolve("uploadPartService");
|
|
178942
179116
|
danmuService = dbContainer.resolve("danmuService");
|
|
178943
179117
|
};
|
|
@@ -179885,6 +180059,52 @@ class Client {
|
|
|
179885
180059
|
}
|
|
179886
180060
|
}
|
|
179887
180061
|
|
|
180062
|
+
/**
|
|
180063
|
+
* 目录ID缓存,存储已成功创建的目录路径到fileID的映射
|
|
180064
|
+
* key: remotePath, value: fileID
|
|
180065
|
+
*/
|
|
180066
|
+
const dirIdCache = new Map();
|
|
180067
|
+
/**
|
|
180068
|
+
* 正在进行中的目录创建请求Promise缓存,防止并发重复请求
|
|
180069
|
+
* key: remotePath, value: Promise<fileID>
|
|
180070
|
+
*/
|
|
180071
|
+
const pendingDirRequests = new Map();
|
|
180072
|
+
/**
|
|
180073
|
+
* 获取或创建目录,带Promise去重缓存
|
|
180074
|
+
* @param client 123网盘客户端实例
|
|
180075
|
+
* @param remotePath 远程目录路径
|
|
180076
|
+
* @returns Promise<number> 目录的fileID
|
|
180077
|
+
*/
|
|
180078
|
+
async function getCachedOrCreateDir(client, remotePath) {
|
|
180079
|
+
// 1. 检查已完成的缓存
|
|
180080
|
+
const cachedId = dirIdCache.get(remotePath);
|
|
180081
|
+
if (cachedId !== undefined) {
|
|
180082
|
+
return cachedId;
|
|
180083
|
+
}
|
|
180084
|
+
// 2. 检查是否有进行中的请求
|
|
180085
|
+
const pendingRequest = pendingDirRequests.get(remotePath);
|
|
180086
|
+
if (pendingRequest) {
|
|
180087
|
+
return pendingRequest;
|
|
180088
|
+
}
|
|
180089
|
+
// 3. 创建新的请求
|
|
180090
|
+
const requestPromise = client
|
|
180091
|
+
.mkdirRecursive(remotePath)
|
|
180092
|
+
.then((fileId) => {
|
|
180093
|
+
// 成功后缓存结果
|
|
180094
|
+
dirIdCache.set(remotePath, fileId);
|
|
180095
|
+
// 从进行中的请求中移除
|
|
180096
|
+
pendingDirRequests.delete(remotePath);
|
|
180097
|
+
return fileId;
|
|
180098
|
+
})
|
|
180099
|
+
.catch((error) => {
|
|
180100
|
+
// 失败时从进行中的请求中移除,不缓存失败结果
|
|
180101
|
+
pendingDirRequests.delete(remotePath);
|
|
180102
|
+
throw error;
|
|
180103
|
+
});
|
|
180104
|
+
// 将Promise存入进行中的请求缓存
|
|
180105
|
+
pendingDirRequests.set(remotePath, requestPromise);
|
|
180106
|
+
return requestPromise;
|
|
180107
|
+
}
|
|
179888
180108
|
/**
|
|
179889
180109
|
* 123网盘上传类
|
|
179890
180110
|
* 使用123pan-uploader进行文件上传
|
|
@@ -179906,6 +180126,20 @@ class Pan123 extends TypedEmitter {
|
|
|
179906
180126
|
this.client = new Client(this.accessToken);
|
|
179907
180127
|
this.speedCalculator = new SpeedCalculator(3000); // 3秒时间窗口
|
|
179908
180128
|
}
|
|
180129
|
+
/**
|
|
180130
|
+
* 清理目录缓存
|
|
180131
|
+
* @param path 可选,指定要清理的路径。不传则清空所有缓存
|
|
180132
|
+
*/
|
|
180133
|
+
static clearDirCache(path) {
|
|
180134
|
+
if (path) {
|
|
180135
|
+
dirIdCache.delete(path);
|
|
180136
|
+
pendingDirRequests.delete(path);
|
|
180137
|
+
}
|
|
180138
|
+
else {
|
|
180139
|
+
dirIdCache.clear();
|
|
180140
|
+
pendingDirRequests.clear();
|
|
180141
|
+
}
|
|
180142
|
+
}
|
|
179909
180143
|
/**
|
|
179910
180144
|
* 检查是否已获取访问令牌
|
|
179911
180145
|
* @returns boolean 是否已获取访问令牌
|
|
@@ -179977,78 +180211,94 @@ class Pan123 extends TypedEmitter {
|
|
|
179977
180211
|
this.emit("error", error);
|
|
179978
180212
|
throw error;
|
|
179979
180213
|
}
|
|
179980
|
-
|
|
179981
|
-
|
|
179982
|
-
|
|
179983
|
-
|
|
179984
|
-
|
|
179985
|
-
|
|
179986
|
-
|
|
179987
|
-
|
|
179988
|
-
|
|
179989
|
-
|
|
179990
|
-
|
|
179991
|
-
|
|
179992
|
-
|
|
179993
|
-
|
|
179994
|
-
|
|
179995
|
-
|
|
179996
|
-
|
|
179997
|
-
|
|
179998
|
-
|
|
179999
|
-
|
|
180000
|
-
|
|
180001
|
-
|
|
180002
|
-
// 计算上传速度(MB/s)
|
|
180003
|
-
const speed = this.speedCalculator.calculateSpeed(data.data.loaded, currentTime);
|
|
180004
|
-
this.emit("progress", {
|
|
180005
|
-
uploaded: this.formatSize(data.data.loaded),
|
|
180006
|
-
total: this.formatSize(data.data.total),
|
|
180007
|
-
percentage,
|
|
180008
|
-
speed,
|
|
180214
|
+
let retryCount = 0;
|
|
180215
|
+
const maxRetries = 1;
|
|
180216
|
+
while (retryCount <= maxRetries) {
|
|
180217
|
+
try {
|
|
180218
|
+
// 如果是重试,清除缓存
|
|
180219
|
+
if (retryCount > 0) {
|
|
180220
|
+
this.logger.info(`检测到parentFileID不存在,清除缓存并重试 (${retryCount}/${maxRetries})`);
|
|
180221
|
+
Pan123.clearDirCache(this.remotePath);
|
|
180222
|
+
}
|
|
180223
|
+
// 需要获取目标目录的ID(使用缓存避免并发重复请求)
|
|
180224
|
+
let parentFileID = await getCachedOrCreateDir(this.client, this.remotePath);
|
|
180225
|
+
// const fileName = path.basename(localFilePath);
|
|
180226
|
+
this.logger.debug(`123网盘开始上传: ${localFilePath} 到 ${this.remotePath}`);
|
|
180227
|
+
const concurrency = options?.concurrency || 3;
|
|
180228
|
+
const limitRate = this.limitRate / concurrency;
|
|
180229
|
+
// 创建上传实例
|
|
180230
|
+
this.currentUploader = new Uploader(localFilePath, this.accessToken, String(parentFileID), {
|
|
180231
|
+
concurrency: concurrency,
|
|
180232
|
+
retryTimes: options?.retry || 7,
|
|
180233
|
+
retryDelay: 5000,
|
|
180234
|
+
duplicate: options?.policy === "skip" ? 2 : 1, // 1: 覆盖, 2: 跳过
|
|
180235
|
+
limitRate,
|
|
180009
180236
|
});
|
|
180010
|
-
|
|
180011
|
-
|
|
180012
|
-
|
|
180013
|
-
|
|
180014
|
-
this.
|
|
180015
|
-
|
|
180016
|
-
|
|
180017
|
-
|
|
180018
|
-
|
|
180019
|
-
|
|
180020
|
-
|
|
180021
|
-
|
|
180022
|
-
|
|
180023
|
-
|
|
180024
|
-
|
|
180025
|
-
|
|
180026
|
-
|
|
180027
|
-
|
|
180028
|
-
|
|
180029
|
-
|
|
180030
|
-
|
|
180031
|
-
|
|
180032
|
-
|
|
180033
|
-
|
|
180034
|
-
|
|
180237
|
+
// 初始化上传计时
|
|
180238
|
+
const uploadStartTime = Date.now();
|
|
180239
|
+
this.speedCalculator.init(uploadStartTime);
|
|
180240
|
+
// 监听上传进度
|
|
180241
|
+
this.currentUploader.on("progress", (data) => {
|
|
180242
|
+
const percentage = Math.round(data.progress * 10000) / 100;
|
|
180243
|
+
const currentTime = Date.now();
|
|
180244
|
+
// 计算上传速度(MB/s)
|
|
180245
|
+
const speed = this.speedCalculator.calculateSpeed(data.data.loaded, currentTime);
|
|
180246
|
+
this.emit("progress", {
|
|
180247
|
+
uploaded: this.formatSize(data.data.loaded),
|
|
180248
|
+
total: this.formatSize(data.data.total),
|
|
180249
|
+
percentage,
|
|
180250
|
+
speed,
|
|
180251
|
+
});
|
|
180252
|
+
});
|
|
180253
|
+
// 监听上传完成
|
|
180254
|
+
this.currentUploader.on("completed", () => {
|
|
180255
|
+
const successMsg = `上传成功: ${localFilePath}`;
|
|
180256
|
+
this.emit("success", successMsg);
|
|
180257
|
+
});
|
|
180258
|
+
// 监听上传错误
|
|
180259
|
+
this.currentUploader.on("error", (error) => {
|
|
180260
|
+
this.logger.error(`上传文件出错: ${error.message}`);
|
|
180261
|
+
this.emit("error", error);
|
|
180262
|
+
});
|
|
180263
|
+
// 监听上传取消
|
|
180264
|
+
this.currentUploader.on("cancel", () => {
|
|
180265
|
+
this.logger.info("上传已取消");
|
|
180266
|
+
this.emit("canceled", "上传已取消");
|
|
180267
|
+
});
|
|
180268
|
+
// 开始上传
|
|
180269
|
+
const result = await this.currentUploader.upload();
|
|
180270
|
+
if (result) {
|
|
180271
|
+
const successMsg = `上传成功: ${localFilePath}`;
|
|
180272
|
+
this.logger.debug(successMsg);
|
|
180273
|
+
this.emit("success", successMsg);
|
|
180274
|
+
return; // 成功后直接返回
|
|
180275
|
+
}
|
|
180276
|
+
else {
|
|
180277
|
+
throw new Error("上传失败");
|
|
180278
|
+
}
|
|
180035
180279
|
}
|
|
180036
|
-
|
|
180037
|
-
|
|
180038
|
-
|
|
180039
|
-
|
|
180040
|
-
|
|
180280
|
+
catch (error) {
|
|
180281
|
+
if (error.message?.includes("cancel")) {
|
|
180282
|
+
this.logger.info("上传已取消");
|
|
180283
|
+
this.emit("canceled", "上传已取消");
|
|
180284
|
+
throw error;
|
|
180285
|
+
}
|
|
180286
|
+
else if (error.message?.includes("parentFileID不存在") && retryCount < maxRetries) {
|
|
180287
|
+
// 如果错误消息包含"parentFileID不存在"且还没达到最大重试次数,则重试
|
|
180288
|
+
retryCount++;
|
|
180289
|
+
continue;
|
|
180290
|
+
}
|
|
180291
|
+
else {
|
|
180292
|
+
this.logger.error(`上传文件出错: ${error.message}`);
|
|
180293
|
+
this.emit("error", error);
|
|
180294
|
+
throw error;
|
|
180295
|
+
}
|
|
180041
180296
|
}
|
|
180042
|
-
|
|
180043
|
-
this.
|
|
180044
|
-
|
|
180297
|
+
finally {
|
|
180298
|
+
this.currentUploader = null;
|
|
180299
|
+
// 重置进度追踪
|
|
180300
|
+
this.speedCalculator.reset();
|
|
180045
180301
|
}
|
|
180046
|
-
throw error;
|
|
180047
|
-
}
|
|
180048
|
-
finally {
|
|
180049
|
-
this.currentUploader = null;
|
|
180050
|
-
// 重置进度追踪
|
|
180051
|
-
this.speedCalculator.reset();
|
|
180052
180302
|
}
|
|
180053
180303
|
}
|
|
180054
180304
|
/**
|
|
@@ -245990,7 +246240,7 @@ function addWithStreamer(data) {
|
|
|
245990
246240
|
});
|
|
245991
246241
|
if (!streamer)
|
|
245992
246242
|
return null;
|
|
245993
|
-
const live = recordHistoryService.add({
|
|
246243
|
+
const live = exports.recordHistoryService.add({
|
|
245994
246244
|
title: data.title,
|
|
245995
246245
|
streamer_id: streamer.id,
|
|
245996
246246
|
live_start_time: data.live_start_time,
|
|
@@ -246001,9 +246251,9 @@ function addWithStreamer(data) {
|
|
|
246001
246251
|
return live;
|
|
246002
246252
|
}
|
|
246003
246253
|
function upadteLive(query, params) {
|
|
246004
|
-
const live = recordHistoryService.query({ video_file: query.video_file, live_id: query.live_id });
|
|
246254
|
+
const live = exports.recordHistoryService.query({ video_file: query.video_file, live_id: query.live_id });
|
|
246005
246255
|
if (live) {
|
|
246006
|
-
recordHistoryService.update({
|
|
246256
|
+
exports.recordHistoryService.update({
|
|
246007
246257
|
id: live.id,
|
|
246008
246258
|
...params,
|
|
246009
246259
|
});
|
|
@@ -246029,7 +246279,7 @@ function queryRecordsByRoomAndPlatform(options) {
|
|
|
246029
246279
|
};
|
|
246030
246280
|
}
|
|
246031
246281
|
// 使用数据库分页而不是内存分页
|
|
246032
|
-
const result = recordHistoryService.paginate({
|
|
246282
|
+
const result = exports.recordHistoryService.paginate({
|
|
246033
246283
|
where: { streamer_id: streamer.id },
|
|
246034
246284
|
page,
|
|
246035
246285
|
pageSize,
|
|
@@ -246055,17 +246305,17 @@ async function removeRecords(channelId, providerId) {
|
|
|
246055
246305
|
});
|
|
246056
246306
|
if (!streamer)
|
|
246057
246307
|
throw new Error("没有找到stream");
|
|
246058
|
-
recordHistoryService.removeRecordsByStreamerId(streamer.id);
|
|
246308
|
+
exports.recordHistoryService.removeRecordsByStreamerId(streamer.id);
|
|
246059
246309
|
return true;
|
|
246060
246310
|
}
|
|
246061
246311
|
function getRecord(data) {
|
|
246062
|
-
return recordHistoryService.query({ video_file: data.file, live_id: data.live_id });
|
|
246312
|
+
return exports.recordHistoryService.query({ video_file: data.file, live_id: data.live_id });
|
|
246063
246313
|
}
|
|
246064
246314
|
function getRecordById(id) {
|
|
246065
|
-
return recordHistoryService.query({ id });
|
|
246315
|
+
return exports.recordHistoryService.query({ id });
|
|
246066
246316
|
}
|
|
246067
246317
|
function removeRecord(id) {
|
|
246068
|
-
const deletedCount = recordHistoryService.removeRecord(id);
|
|
246318
|
+
const deletedCount = exports.recordHistoryService.removeRecord(id);
|
|
246069
246319
|
return deletedCount > 0;
|
|
246070
246320
|
}
|
|
246071
246321
|
/**
|
|
@@ -246091,7 +246341,7 @@ function getLastRecordTimesByChannels(channels) {
|
|
|
246091
246341
|
}
|
|
246092
246342
|
// 批量查询最后录制时间(一次数据库查询)
|
|
246093
246343
|
const streamerIds = streamers.map((s) => s.id);
|
|
246094
|
-
const lastRecordTimesMap = recordHistoryService.getLastRecordTimes(streamerIds);
|
|
246344
|
+
const lastRecordTimesMap = exports.recordHistoryService.getLastRecordTimes(streamerIds);
|
|
246095
246345
|
// 构建 streamer 映射表
|
|
246096
246346
|
const streamerMap = new Map(streamers.map((s) => [`${s.platform}_${s.room_id}`, s.id]));
|
|
246097
246347
|
return channels.map(({ channelId, providerId }) => {
|
|
@@ -246852,6 +247102,7 @@ exports.biliApi = biliApi;
|
|
|
246852
247102
|
exports.braces_1 = braces_1;
|
|
246853
247103
|
exports.burn = burn;
|
|
246854
247104
|
exports.calculateFileQuickHash = calculateFileQuickHash;
|
|
247105
|
+
exports.checkDiskSpace = checkDiskSpace;
|
|
246855
247106
|
exports.checkMergeVideos = checkMergeVideos;
|
|
246856
247107
|
exports.cloneDeep = cloneDeep;
|
|
246857
247108
|
exports.closeDB = closeDB;
|
|
@@ -246862,6 +247113,7 @@ exports.cut = cut;
|
|
|
246862
247113
|
exports.defaultRecordConfig = defaultRecordConfig;
|
|
246863
247114
|
exports.douyu = douyu;
|
|
246864
247115
|
exports.ejs = ejs;
|
|
247116
|
+
exports.escaped = escaped;
|
|
246865
247117
|
exports.executeVirtualRecordConfig = executeVirtualRecordConfig;
|
|
246866
247118
|
exports.fs = fs$k;
|
|
246867
247119
|
exports.genTimeData = genTimeData;
|