koishi-plugin-video-parser-all 1.3.5 → 1.3.7
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/lib/index.d.ts +57 -34
- package/lib/index.js +194 -86
- package/package.json +7 -5
- package/readme.md +76 -68
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { Context, Schema } from 'koishi';
|
|
2
|
+
declare module 'koishi' {
|
|
3
|
+
interface Context {
|
|
4
|
+
downloads?: {
|
|
5
|
+
download(url: string, dest: string, options?: Record<string, unknown>): Promise<string>;
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
}
|
|
2
9
|
export declare const name = "video-parser-all";
|
|
3
10
|
export declare const Config: Schema<{
|
|
4
11
|
enable?: boolean | null | undefined;
|
|
@@ -23,9 +30,13 @@ export declare const Config: Schema<{
|
|
|
23
30
|
twitter?: boolean | null | undefined;
|
|
24
31
|
instagram?: boolean | null | undefined;
|
|
25
32
|
doubao?: boolean | null | undefined;
|
|
26
|
-
doubao_chat?: boolean | null | undefined;
|
|
27
33
|
oasis?: boolean | null | undefined;
|
|
28
34
|
wechat_channel?: boolean | null | undefined;
|
|
35
|
+
lishi?: boolean | null | undefined;
|
|
36
|
+
quanmin?: boolean | null | undefined;
|
|
37
|
+
pipigx?: boolean | null | undefined;
|
|
38
|
+
pipixia?: boolean | null | undefined;
|
|
39
|
+
zuiyou?: boolean | null | undefined;
|
|
29
40
|
} & import("cosmokit").Dict) | null | undefined;
|
|
30
41
|
} & import("cosmokit").Dict & {
|
|
31
42
|
unifiedMessageFormat?: string | null | undefined;
|
|
@@ -34,24 +45,24 @@ export declare const Config: Schema<{
|
|
|
34
45
|
showCoverImage?: boolean | null | undefined;
|
|
35
46
|
showMusicCover?: boolean | null | undefined;
|
|
36
47
|
showImageFile?: boolean | null | undefined;
|
|
37
|
-
forceDownloadImage?: boolean | null | undefined;
|
|
38
|
-
imageDownloadTimeout?: number | null | undefined;
|
|
39
|
-
imageTempDir?: string | null | undefined;
|
|
40
|
-
maxImageSize?: number | null | undefined;
|
|
41
48
|
showVideoFile?: boolean | null | undefined;
|
|
49
|
+
forceDownloadImage?: boolean | null | undefined;
|
|
42
50
|
forceDownloadVideo?: boolean | null | undefined;
|
|
43
|
-
|
|
44
|
-
tempDir?: string | null | undefined;
|
|
45
|
-
maxVideoSize?: number | null | undefined;
|
|
46
|
-
maxDescLength?: number | null | undefined;
|
|
47
|
-
maxConcurrent?: number | null | undefined;
|
|
48
|
-
downloadConcurrency?: number | null | undefined;
|
|
51
|
+
} & {
|
|
49
52
|
showMusicVoice?: boolean | null | undefined;
|
|
50
53
|
showMusicVoiceFile?: boolean | null | undefined;
|
|
51
54
|
forceDownloadMusicVoice?: boolean | null | undefined;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
} & {
|
|
56
|
+
maxDescLength?: number | null | undefined;
|
|
57
|
+
maxConcurrent?: number | null | undefined;
|
|
58
|
+
downloadConcurrency?: number | null | undefined;
|
|
59
|
+
mediaDownloadTimeout?: number | null | undefined;
|
|
60
|
+
maxMediaSize?: number | null | undefined;
|
|
61
|
+
downloadEngine?: "internal" | "aria2" | null | undefined;
|
|
62
|
+
aria2Host?: string | null | undefined;
|
|
63
|
+
aria2Port?: number | null | undefined;
|
|
64
|
+
aria2Secret?: string | null | undefined;
|
|
65
|
+
resumeDownload?: boolean | null | undefined;
|
|
55
66
|
} & {
|
|
56
67
|
timeout?: number | null | undefined;
|
|
57
68
|
videoSendTimeout?: number | null | undefined;
|
|
@@ -74,11 +85,11 @@ export declare const Config: Schema<{
|
|
|
74
85
|
ignoreSendError?: boolean | null | undefined;
|
|
75
86
|
retryTimes?: number | null | undefined;
|
|
76
87
|
retryInterval?: number | null | undefined;
|
|
77
|
-
} & {
|
|
78
88
|
enableForward?: boolean | null | undefined;
|
|
79
89
|
} & {
|
|
80
90
|
deduplicationInterval?: number | null | undefined;
|
|
81
91
|
cacheTTL?: number | null | undefined;
|
|
92
|
+
cacheDir?: string | null | undefined;
|
|
82
93
|
} & {
|
|
83
94
|
primaryApiUrl?: string | null | undefined;
|
|
84
95
|
backupApiUrl?: string | null | undefined;
|
|
@@ -100,12 +111,16 @@ export declare const Config: Schema<{
|
|
|
100
111
|
twitter?: boolean | null | undefined;
|
|
101
112
|
instagram?: boolean | null | undefined;
|
|
102
113
|
doubao?: boolean | null | undefined;
|
|
103
|
-
doubao_chat?: boolean | null | undefined;
|
|
104
114
|
oasis?: boolean | null | undefined;
|
|
105
115
|
wechat_channel?: boolean | null | undefined;
|
|
116
|
+
lishi?: boolean | null | undefined;
|
|
117
|
+
quanmin?: boolean | null | undefined;
|
|
118
|
+
pipigx?: boolean | null | undefined;
|
|
119
|
+
pipixia?: boolean | null | undefined;
|
|
120
|
+
zuiyou?: boolean | null | undefined;
|
|
106
121
|
} & import("cosmokit").Dict) | null | undefined;
|
|
107
122
|
customApis?: ({
|
|
108
|
-
platform?: "bilibili" | "douyin" | "kuaishou" | "xiaohongshu" | "weibo" | "xigua" | "youtube" | "tiktok" | "acfun" | "zhihu" | "weishi" | "huya" | "haokan" | "meipai" | "twitter" | "instagram" | "doubao" | "
|
|
123
|
+
platform?: "bilibili" | "douyin" | "kuaishou" | "xiaohongshu" | "weibo" | "xigua" | "youtube" | "tiktok" | "acfun" | "zhihu" | "weishi" | "huya" | "haokan" | "meipai" | "twitter" | "instagram" | "doubao" | "oasis" | "wechat_channel" | null | undefined;
|
|
109
124
|
apiUrl?: string | null | undefined;
|
|
110
125
|
apiKey?: string | null | undefined;
|
|
111
126
|
authHeaderType?: "Bearer" | "X-API-Key" | "Custom" | null | undefined;
|
|
@@ -162,9 +177,13 @@ export declare const Config: Schema<{
|
|
|
162
177
|
twitter: Schema<boolean, boolean>;
|
|
163
178
|
instagram: Schema<boolean, boolean>;
|
|
164
179
|
doubao: Schema<boolean, boolean>;
|
|
165
|
-
doubao_chat: Schema<boolean, boolean>;
|
|
166
180
|
oasis: Schema<boolean, boolean>;
|
|
167
181
|
wechat_channel: Schema<boolean, boolean>;
|
|
182
|
+
lishi: Schema<boolean, boolean>;
|
|
183
|
+
quanmin: Schema<boolean, boolean>;
|
|
184
|
+
pipigx: Schema<boolean, boolean>;
|
|
185
|
+
pipixia: Schema<boolean, boolean>;
|
|
186
|
+
zuiyou: Schema<boolean, boolean>;
|
|
168
187
|
}>;
|
|
169
188
|
} & import("cosmokit").Dict & {
|
|
170
189
|
unifiedMessageFormat: string;
|
|
@@ -173,24 +192,24 @@ export declare const Config: Schema<{
|
|
|
173
192
|
showCoverImage: boolean;
|
|
174
193
|
showMusicCover: boolean;
|
|
175
194
|
showImageFile: boolean;
|
|
176
|
-
forceDownloadImage: boolean;
|
|
177
|
-
imageDownloadTimeout: number;
|
|
178
|
-
imageTempDir: string;
|
|
179
|
-
maxImageSize: number;
|
|
180
195
|
showVideoFile: boolean;
|
|
196
|
+
forceDownloadImage: boolean;
|
|
181
197
|
forceDownloadVideo: boolean;
|
|
182
|
-
|
|
183
|
-
tempDir: string;
|
|
184
|
-
maxVideoSize: number;
|
|
185
|
-
maxDescLength: number;
|
|
186
|
-
maxConcurrent: number;
|
|
187
|
-
downloadConcurrency: number;
|
|
198
|
+
} & {
|
|
188
199
|
showMusicVoice: boolean;
|
|
189
200
|
showMusicVoiceFile: boolean;
|
|
190
201
|
forceDownloadMusicVoice: boolean;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
202
|
+
} & {
|
|
203
|
+
maxDescLength: number;
|
|
204
|
+
maxConcurrent: number;
|
|
205
|
+
downloadConcurrency: number;
|
|
206
|
+
mediaDownloadTimeout: number;
|
|
207
|
+
maxMediaSize: number;
|
|
208
|
+
downloadEngine: "internal" | "aria2";
|
|
209
|
+
aria2Host: string;
|
|
210
|
+
aria2Port: number;
|
|
211
|
+
aria2Secret: string;
|
|
212
|
+
resumeDownload: boolean;
|
|
194
213
|
} & {
|
|
195
214
|
timeout: number;
|
|
196
215
|
videoSendTimeout: number;
|
|
@@ -216,11 +235,11 @@ export declare const Config: Schema<{
|
|
|
216
235
|
ignoreSendError: boolean;
|
|
217
236
|
retryTimes: number;
|
|
218
237
|
retryInterval: number;
|
|
219
|
-
} & {
|
|
220
238
|
enableForward: boolean;
|
|
221
239
|
} & {
|
|
222
240
|
deduplicationInterval: number;
|
|
223
241
|
cacheTTL: number;
|
|
242
|
+
cacheDir: string;
|
|
224
243
|
} & {
|
|
225
244
|
primaryApiUrl: string;
|
|
226
245
|
backupApiUrl: string;
|
|
@@ -242,12 +261,16 @@ export declare const Config: Schema<{
|
|
|
242
261
|
twitter: Schema<boolean, boolean>;
|
|
243
262
|
instagram: Schema<boolean, boolean>;
|
|
244
263
|
doubao: Schema<boolean, boolean>;
|
|
245
|
-
doubao_chat: Schema<boolean, boolean>;
|
|
246
264
|
oasis: Schema<boolean, boolean>;
|
|
247
265
|
wechat_channel: Schema<boolean, boolean>;
|
|
266
|
+
lishi: Schema<boolean, boolean>;
|
|
267
|
+
quanmin: Schema<boolean, boolean>;
|
|
268
|
+
pipigx: Schema<boolean, boolean>;
|
|
269
|
+
pipixia: Schema<boolean, boolean>;
|
|
270
|
+
zuiyou: Schema<boolean, boolean>;
|
|
248
271
|
}>;
|
|
249
272
|
customApis: Schemastery.ObjectT<{
|
|
250
|
-
platform: Schema<"bilibili" | "douyin" | "kuaishou" | "xiaohongshu" | "weibo" | "xigua" | "youtube" | "tiktok" | "acfun" | "zhihu" | "weishi" | "huya" | "haokan" | "meipai" | "twitter" | "instagram" | "doubao" | "
|
|
273
|
+
platform: Schema<"bilibili" | "douyin" | "kuaishou" | "xiaohongshu" | "weibo" | "xigua" | "youtube" | "tiktok" | "acfun" | "zhihu" | "weishi" | "huya" | "haokan" | "meipai" | "twitter" | "instagram" | "doubao" | "oasis" | "wechat_channel", "bilibili" | "douyin" | "kuaishou" | "xiaohongshu" | "weibo" | "xigua" | "youtube" | "tiktok" | "acfun" | "zhihu" | "weishi" | "huya" | "haokan" | "meipai" | "twitter" | "instagram" | "doubao" | "oasis" | "wechat_channel">;
|
|
251
274
|
apiUrl: Schema<string, string>;
|
|
252
275
|
apiKey: Schema<string, string>;
|
|
253
276
|
authHeaderType: Schema<"Bearer" | "X-API-Key" | "Custom", "Bearer" | "X-API-Key" | "Custom">;
|
package/lib/index.js
CHANGED
|
@@ -71,9 +71,9 @@ exports.name = 'video-parser-all';
|
|
|
71
71
|
exports.Config = koishi_1.Schema.intersect([
|
|
72
72
|
koishi_1.Schema.object({
|
|
73
73
|
enable: koishi_1.Schema.boolean().default(true).description('是否启用视频解析插件'),
|
|
74
|
-
botName: koishi_1.Schema.string().default('视频解析机器人').description('
|
|
74
|
+
botName: koishi_1.Schema.string().default('视频解析机器人').description('合并转发中显示的昵称'),
|
|
75
75
|
showWaitingTip: koishi_1.Schema.boolean().default(true).description('解析时显示等待提示'),
|
|
76
|
-
debug: koishi_1.Schema.boolean().default(false).description('
|
|
76
|
+
debug: koishi_1.Schema.boolean().default(false).description('开启调试日志'),
|
|
77
77
|
platformEnabled: koishi_1.Schema.object({
|
|
78
78
|
bilibili: koishi_1.Schema.boolean().default(true).description('哔哩哔哩'),
|
|
79
79
|
douyin: koishi_1.Schema.boolean().default(true).description('抖音'),
|
|
@@ -92,41 +92,50 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
92
92
|
twitter: koishi_1.Schema.boolean().default(true).description('Twitter/X'),
|
|
93
93
|
instagram: koishi_1.Schema.boolean().default(true).description('Instagram'),
|
|
94
94
|
doubao: koishi_1.Schema.boolean().default(true).description('豆包'),
|
|
95
|
-
doubao_chat: koishi_1.Schema.boolean().default(true).description('豆包对话'),
|
|
96
95
|
oasis: koishi_1.Schema.boolean().default(true).description('绿洲'),
|
|
97
96
|
wechat_channel: koishi_1.Schema.boolean().default(true).description('视频号'),
|
|
97
|
+
lishi: koishi_1.Schema.boolean().default(true).description('梨视频'),
|
|
98
|
+
quanmin: koishi_1.Schema.boolean().default(true).description('全民直播'),
|
|
99
|
+
pipigx: koishi_1.Schema.boolean().default(true).description('皮皮搞笑'),
|
|
100
|
+
pipixia: koishi_1.Schema.boolean().default(true).description('皮皮虾'),
|
|
101
|
+
zuiyou: koishi_1.Schema.boolean().default(true).description('最右'),
|
|
98
102
|
}).description('各平台解析开关'),
|
|
99
|
-
}).description('
|
|
103
|
+
}).description('基本设置'),
|
|
100
104
|
koishi_1.Schema.object({
|
|
101
|
-
unifiedMessageFormat: koishi_1.Schema.string().role('textarea').default('标题:${标题}\n作者:${作者}\n简介:${简介}\n音乐标题:${音乐标题}\n音乐作者:${音乐作者}\n点赞:${点赞数}\n收藏:${收藏数}\n转发:${转发数}\n播放:${播放数}\n评论:${评论数}\n图片数量:${图片数量}').description('
|
|
102
|
-
}).description('
|
|
105
|
+
unifiedMessageFormat: koishi_1.Schema.string().role('textarea').default('标题:${标题}\n作者:${作者}\n简介:${简介}\n音乐标题:${音乐标题}\n音乐作者:${音乐作者}\n点赞:${点赞数}\n收藏:${收藏数}\n转发:${转发数}\n播放:${播放数}\n评论:${评论数}\n图片数量:${图片数量}').description('文字格式,支持变量,空行自动隐藏'),
|
|
106
|
+
}).description('消息格式'),
|
|
103
107
|
koishi_1.Schema.object({
|
|
104
|
-
showImageText: koishi_1.Schema.boolean().default(true).description('
|
|
105
|
-
showCoverImage: koishi_1.Schema.boolean().default(true).description('
|
|
106
|
-
showMusicCover: koishi_1.Schema.boolean().default(true).description('
|
|
107
|
-
showImageFile: koishi_1.Schema.boolean().default(true).description('
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
108
|
+
showImageText: koishi_1.Schema.boolean().default(true).description('发送文字内容'),
|
|
109
|
+
showCoverImage: koishi_1.Schema.boolean().default(true).description('发送封面图片'),
|
|
110
|
+
showMusicCover: koishi_1.Schema.boolean().default(true).description('发送音乐封面图片'),
|
|
111
|
+
showImageFile: koishi_1.Schema.boolean().default(true).description('封面/图片是否以图片形式发送(关闭则只发送链接)'),
|
|
112
|
+
showVideoFile: koishi_1.Schema.boolean().default(true).description('视频是否以视频形式发送(关闭则只发送链接)'),
|
|
113
|
+
forceDownloadImage: koishi_1.Schema.boolean().default(false).description('强制下载封面/图片'),
|
|
114
|
+
forceDownloadVideo: koishi_1.Schema.boolean().default(false).description('强制下载视频'),
|
|
115
|
+
}).description('媒体发送'),
|
|
116
|
+
koishi_1.Schema.object({
|
|
117
|
+
showMusicVoice: koishi_1.Schema.boolean().default(false).description('音乐链接以语音形式发送'),
|
|
118
|
+
showMusicVoiceFile: koishi_1.Schema.boolean().default(true).description('音乐语音是否以文件形式发送(关闭则只发送链接)'),
|
|
119
|
+
forceDownloadMusicVoice: koishi_1.Schema.boolean().default(false).description('强制下载音乐语音'),
|
|
120
|
+
}).description('音乐语音(需 silk 和 ffmpeg)'),
|
|
121
|
+
koishi_1.Schema.object({
|
|
122
|
+
maxDescLength: koishi_1.Schema.number().min(0).step(1).default(200).description('简介长度上限'),
|
|
118
123
|
maxConcurrent: koishi_1.Schema.number().min(1).step(1).default(3).description('解析最大并发数'),
|
|
119
124
|
downloadConcurrency: koishi_1.Schema.number().min(1).step(1).default(3).description('下载线程数'),
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
125
|
+
mediaDownloadTimeout: koishi_1.Schema.number().min(0).step(1).default(120000).description('统一下载超时 (ms)'),
|
|
126
|
+
maxMediaSize: koishi_1.Schema.number().min(0).step(1).default(0).description('最大下载文件大小 (MB),0 为不限制'),
|
|
127
|
+
downloadEngine: koishi_1.Schema.union([
|
|
128
|
+
koishi_1.Schema.const('internal').description('内置下载'),
|
|
129
|
+
koishi_1.Schema.const('aria2').description('aria2 下载'),
|
|
130
|
+
]).default('internal').description('下载引擎'),
|
|
131
|
+
aria2Host: koishi_1.Schema.string().default('127.0.0.1').description('aria2 RPC 地址'),
|
|
132
|
+
aria2Port: koishi_1.Schema.number().default(6800).description('aria2 RPC 端口'),
|
|
133
|
+
aria2Secret: koishi_1.Schema.string().default('').description('aria2 RPC 密钥'),
|
|
134
|
+
resumeDownload: koishi_1.Schema.boolean().default(true).description('启用断点续传(仅 aria2 模式)'),
|
|
135
|
+
}).description('性能与限制'),
|
|
127
136
|
koishi_1.Schema.object({
|
|
128
|
-
timeout: koishi_1.Schema.number().min(0).step(1).default(180000).description('API
|
|
129
|
-
videoSendTimeout: koishi_1.Schema.number().min(0).step(1).default(
|
|
137
|
+
timeout: koishi_1.Schema.number().min(0).step(1).default(180000).description('API 请求超时 (ms)'),
|
|
138
|
+
videoSendTimeout: koishi_1.Schema.number().min(0).step(1).default(180000).description('消息发送超时 (ms)'),
|
|
130
139
|
userAgent: koishi_1.Schema.string().default('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36').description('User-Agent'),
|
|
131
140
|
proxy: koishi_1.Schema.object({
|
|
132
141
|
enabled: koishi_1.Schema.boolean().default(false).description('启用代理'),
|
|
@@ -145,19 +154,18 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
145
154
|
name: koishi_1.Schema.string().required().description('头名称'),
|
|
146
155
|
value: koishi_1.Schema.string().required().description('头值'),
|
|
147
156
|
})).default([]).description('自定义请求头'),
|
|
148
|
-
}).description('
|
|
157
|
+
}).description('网络与请求'),
|
|
149
158
|
koishi_1.Schema.object({
|
|
150
159
|
ignoreSendError: koishi_1.Schema.boolean().default(true).description('忽略发送失败'),
|
|
151
160
|
retryTimes: koishi_1.Schema.number().min(0).step(1).default(3).description('重试次数'),
|
|
152
|
-
retryInterval: koishi_1.Schema.number().min(0).step(1).default(1000).description('
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
enableForward: koishi_1.Schema.boolean().default(false).description('启用合并转发(支持 OneBot、Satori 平台)'),
|
|
156
|
-
}).description('发送方式'),
|
|
161
|
+
retryInterval: koishi_1.Schema.number().min(0).step(1).default(1000).description('重试间隔 (ms)'),
|
|
162
|
+
enableForward: koishi_1.Schema.boolean().default(false).description('合并转发(OneBot/Satori)'),
|
|
163
|
+
}).description('发送与重试'),
|
|
157
164
|
koishi_1.Schema.object({
|
|
158
|
-
deduplicationInterval: koishi_1.Schema.number().min(0).step(1).default(180).description('
|
|
159
|
-
cacheTTL: koishi_1.Schema.number().min(0).step(1).default(600).description('
|
|
160
|
-
|
|
165
|
+
deduplicationInterval: koishi_1.Schema.number().min(0).step(1).default(180).description('去重间隔 (s)'),
|
|
166
|
+
cacheTTL: koishi_1.Schema.number().min(0).step(1).default(600).description('缓存时间 (s)'),
|
|
167
|
+
cacheDir: koishi_1.Schema.string().default('./temp_cache').description('统一临时目录'),
|
|
168
|
+
}).description('缓存与临时文件'),
|
|
161
169
|
koishi_1.Schema.object({
|
|
162
170
|
primaryApiUrl: koishi_1.Schema.string().default('https://api.bugpk.com/api/short_videos').hidden(),
|
|
163
171
|
backupApiUrl: koishi_1.Schema.string().default('https://api.bugpk.com/api/svparse').hidden(),
|
|
@@ -179,9 +187,13 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
179
187
|
twitter: koishi_1.Schema.boolean().default(false).description('Twitter/X'),
|
|
180
188
|
instagram: koishi_1.Schema.boolean().default(false).description('Instagram'),
|
|
181
189
|
doubao: koishi_1.Schema.boolean().default(false).description('豆包'),
|
|
182
|
-
doubao_chat: koishi_1.Schema.boolean().default(false).description('豆包对话'),
|
|
183
190
|
oasis: koishi_1.Schema.boolean().default(false).description('绿洲'),
|
|
184
191
|
wechat_channel: koishi_1.Schema.boolean().default(false).description('视频号'),
|
|
192
|
+
lishi: koishi_1.Schema.boolean().default(false).description('梨视频'),
|
|
193
|
+
quanmin: koishi_1.Schema.boolean().default(false).description('全民直播'),
|
|
194
|
+
pipigx: koishi_1.Schema.boolean().default(false).description('皮皮搞笑'),
|
|
195
|
+
pipixia: koishi_1.Schema.boolean().default(false).description('皮皮虾'),
|
|
196
|
+
zuiyou: koishi_1.Schema.boolean().default(false).description('最右'),
|
|
185
197
|
}).description('优先使用专属 API'),
|
|
186
198
|
customApis: koishi_1.Schema.array(koishi_1.Schema.object({
|
|
187
199
|
platform: koishi_1.Schema.union([
|
|
@@ -202,7 +214,6 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
202
214
|
koishi_1.Schema.const('twitter').description('Twitter/X'),
|
|
203
215
|
koishi_1.Schema.const('instagram').description('Instagram'),
|
|
204
216
|
koishi_1.Schema.const('doubao').description('豆包'),
|
|
205
|
-
koishi_1.Schema.const('doubao_chat').description('豆包对话'),
|
|
206
217
|
koishi_1.Schema.const('oasis').description('绿洲'),
|
|
207
218
|
koishi_1.Schema.const('wechat_channel').description('视频号'),
|
|
208
219
|
]).description('平台'),
|
|
@@ -215,12 +226,12 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
215
226
|
]).default('Bearer').description('认证头类型'),
|
|
216
227
|
customHeaderName: koishi_1.Schema.string().default('X-API-Key').description('自定义头名称'),
|
|
217
228
|
fieldMapping: koishi_1.Schema.string().role('textarea').default('{}').description('字段映射 JSON'),
|
|
218
|
-
})).default([]).description('
|
|
229
|
+
})).default([]).description('覆盖内置平台 API'),
|
|
219
230
|
customPlatforms: koishi_1.Schema.array(koishi_1.Schema.object({
|
|
220
231
|
name: koishi_1.Schema.string().required().description('平台名称'),
|
|
221
|
-
exampleUrl: koishi_1.Schema.string().description('
|
|
222
|
-
keywords: koishi_1.Schema.string().required().description('
|
|
223
|
-
apiUrl: koishi_1.Schema.string().required().description('解析 API
|
|
232
|
+
exampleUrl: koishi_1.Schema.string().description('示例链接'),
|
|
233
|
+
keywords: koishi_1.Schema.string().required().description('关键词(逗号分隔)'),
|
|
234
|
+
apiUrl: koishi_1.Schema.string().required().description('解析 API'),
|
|
224
235
|
apiKey: koishi_1.Schema.string().default('').description('API Key'),
|
|
225
236
|
authHeaderType: koishi_1.Schema.union([
|
|
226
237
|
koishi_1.Schema.const('Bearer').description('Bearer'),
|
|
@@ -266,14 +277,14 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
266
277
|
' "music_cover": "data.music.cover",\n' +
|
|
267
278
|
' "music_url": "data.music.url"\n' +
|
|
268
279
|
'}').description('全局字段映射 JSON'),
|
|
269
|
-
}).description('API
|
|
280
|
+
}).description('API 与平台'),
|
|
270
281
|
koishi_1.Schema.object({
|
|
271
282
|
waitingTipText: koishi_1.Schema.string().default('正在解析视频,请稍候...').description('等待提示'),
|
|
272
283
|
unsupportedPlatformText: koishi_1.Schema.string().default('不支持该平台链接').description('不支持提示'),
|
|
273
284
|
invalidLinkText: koishi_1.Schema.string().default('无效的视频链接').description('无效链接提示'),
|
|
274
285
|
parseErrorPrefix: koishi_1.Schema.string().default('❌ 解析失败:').description('错误前缀'),
|
|
275
|
-
parseErrorItemFormat: koishi_1.Schema.string().default('【${url}】: ${msg}').description('
|
|
276
|
-
}).description('
|
|
286
|
+
parseErrorItemFormat: koishi_1.Schema.string().default('【${url}】: ${msg}').description('错误格式'),
|
|
287
|
+
}).description('界面文本'),
|
|
277
288
|
]);
|
|
278
289
|
const logger = new koishi_1.Logger(exports.name);
|
|
279
290
|
let debugEnabled = false;
|
|
@@ -309,10 +320,19 @@ const BUILTIN_LINK_RULES = [
|
|
|
309
320
|
{ pattern: /https?:\/\/x\.com\/\w+\/status\/\d{10,}/gi, type: 'twitter' },
|
|
310
321
|
{ pattern: /https?:\/\/(?:www\.)?instagram\.com\/p\/[0-9a-zA-Z_-]{10,}/gi, type: 'instagram' },
|
|
311
322
|
{ pattern: /https?:\/\/(?:www\.)?doubao\.com\/video\/\d{10,}/gi, type: 'doubao' },
|
|
312
|
-
{ pattern: /https?:\/\/(?:www\.)?doubao\.com\/thread\/[0-9a-zA-Z_-]+/gi, type: 'doubao_chat' },
|
|
313
323
|
{ pattern: /https?:\/\/(?:www\.)?oasis\.weibo\.com\/v\/[0-9a-zA-Z_-]+/gi, type: 'oasis' },
|
|
314
324
|
{ pattern: /https?:\/\/channels\.weixin\.qq\.com\/[0-9a-zA-Z_-]+/gi, type: 'wechat_channel' },
|
|
315
325
|
{ pattern: /https?:\/\/weixin\.qq\.com\/sph\/[0-9a-zA-Z_-]+/gi, type: 'wechat_channel' },
|
|
326
|
+
{ pattern: /https?:\/\/(?:www\.)?pearvideo\.com\/video_\d+/gi, type: 'lishi' },
|
|
327
|
+
{ pattern: /https?:\/\/video\.li\/[0-9a-zA-Z_-]{3,}/gi, type: 'lishi' },
|
|
328
|
+
{ pattern: /https?:\/\/(?:www\.)?quanmin\.tv\/\w+/gi, type: 'quanmin' },
|
|
329
|
+
{ pattern: /https?:\/\/(?:www\.)?quanmintv\.cn\/\w+/gi, type: 'quanmin' },
|
|
330
|
+
{ pattern: /https?:\/\/h5\.pipigx\.com\/pp\/post\/\d+/gi, type: 'pipigx' },
|
|
331
|
+
{ pattern: /https?:\/\/(?:www\.)?ippzone\.com\/\w+/gi, type: 'pipigx' },
|
|
332
|
+
{ pattern: /https?:\/\/(?:h5|www)\.pipix\.com\/\w+/gi, type: 'pipixia' },
|
|
333
|
+
{ pattern: /https?:\/\/(?:www\.)?pipixia\.com\/\w+/gi, type: 'pipixia' },
|
|
334
|
+
{ pattern: /https?:\/\/share\.xiaochuankeji\.cn\/hybrid\/share\/post\?pid=\d+/gi, type: 'zuiyou' },
|
|
335
|
+
{ pattern: /https?:\/\/(?:h5|www)\.izuiyou\.com\/\w+/gi, type: 'zuiyou' },
|
|
316
336
|
];
|
|
317
337
|
function buildCustomLinkRules(customPlatforms) {
|
|
318
338
|
if (!Array.isArray(customPlatforms) || customPlatforms.length === 0)
|
|
@@ -604,6 +624,10 @@ function generateFormattedText(p, format) {
|
|
|
604
624
|
'音乐作者': p.music.author || '',
|
|
605
625
|
'音乐封面': p.music.cover || '',
|
|
606
626
|
};
|
|
627
|
+
const varReplacements = Object.entries(vars).map(([key, val]) => ({
|
|
628
|
+
regex: new RegExp(`\\$\\{${key}\\}`, 'g'),
|
|
629
|
+
value: val,
|
|
630
|
+
}));
|
|
607
631
|
const lines = format.split('\n');
|
|
608
632
|
const resultLines = [];
|
|
609
633
|
for (const line of lines) {
|
|
@@ -622,8 +646,8 @@ function generateFormattedText(p, format) {
|
|
|
622
646
|
continue;
|
|
623
647
|
}
|
|
624
648
|
let newLine = line;
|
|
625
|
-
for (const
|
|
626
|
-
newLine = newLine.replace(
|
|
649
|
+
for (const { regex, value } of varReplacements) {
|
|
650
|
+
newLine = newLine.replace(regex, value);
|
|
627
651
|
}
|
|
628
652
|
resultLines.push(newLine);
|
|
629
653
|
}
|
|
@@ -674,6 +698,7 @@ function apply(ctx, config) {
|
|
|
674
698
|
parseErrorItemFormat: config.parseErrorItemFormat || '【${url}】: ${msg}',
|
|
675
699
|
};
|
|
676
700
|
const proxyConfig = config.proxy || {};
|
|
701
|
+
const cacheDir = config.cacheDir || './temp_cache';
|
|
677
702
|
const customPlatforms = (config.customPlatforms || []).map((p) => ({
|
|
678
703
|
name: p.name,
|
|
679
704
|
apiUrl: p.apiUrl,
|
|
@@ -684,6 +709,27 @@ function apply(ctx, config) {
|
|
|
684
709
|
proxy: p.proxy || null
|
|
685
710
|
}));
|
|
686
711
|
const downloadLimiter = new ConcurrencyLimiter(config.downloadConcurrency || 3);
|
|
712
|
+
const mediaDownloadTimeout = config.mediaDownloadTimeout ?? 120000;
|
|
713
|
+
const maxMediaSize = config.maxMediaSize ?? 0;
|
|
714
|
+
const downloadEngine = config.downloadEngine || 'internal';
|
|
715
|
+
let aria2 = null;
|
|
716
|
+
if (downloadEngine === 'aria2') {
|
|
717
|
+
try {
|
|
718
|
+
const Aria2 = require('aria2');
|
|
719
|
+
aria2 = new Aria2({
|
|
720
|
+
host: config.aria2Host || '127.0.0.1',
|
|
721
|
+
port: config.aria2Port || 6800,
|
|
722
|
+
secure: false,
|
|
723
|
+
secret: config.aria2Secret || '',
|
|
724
|
+
path: '/jsonrpc'
|
|
725
|
+
});
|
|
726
|
+
aria2.open();
|
|
727
|
+
logger.info('aria2 连接成功');
|
|
728
|
+
}
|
|
729
|
+
catch (e) {
|
|
730
|
+
logger.warn('aria2 连接失败,回退到内置下载');
|
|
731
|
+
}
|
|
732
|
+
}
|
|
687
733
|
function getPlatformConfig(type) {
|
|
688
734
|
if (type.startsWith('custom_')) {
|
|
689
735
|
const name = type.slice(7);
|
|
@@ -692,7 +738,7 @@ function apply(ctx, config) {
|
|
|
692
738
|
return {
|
|
693
739
|
apiUrl: custom.apiUrl,
|
|
694
740
|
dedicatedFirst: true,
|
|
695
|
-
apiKey: custom.apiKey,
|
|
741
|
+
apiKey: custom.apiKey || '',
|
|
696
742
|
authHeaderType: custom.authHeaderType,
|
|
697
743
|
customHeaderName: custom.customHeaderName,
|
|
698
744
|
fieldMapping: custom.fieldMapping,
|
|
@@ -706,7 +752,6 @@ function apply(ctx, config) {
|
|
|
706
752
|
bilibili: 'https://api.bugpk.com/api/bilibili',
|
|
707
753
|
douyin: 'https://api.bugpk.com/api/douyin',
|
|
708
754
|
doubao: 'https://api.bugpk.com/api/dbvideos',
|
|
709
|
-
doubao_chat: 'https://api.bugpk.com/api/dbduihua',
|
|
710
755
|
kuaishou: 'https://api.bugpk.com/api/kuaishou',
|
|
711
756
|
xiaohongshu: 'https://api.bugpk.com/api/xhs',
|
|
712
757
|
jimeng: 'https://api.bugpk.com/api/jimengai',
|
|
@@ -730,6 +775,9 @@ function apply(ctx, config) {
|
|
|
730
775
|
customHeaderName = custom.customHeaderName || 'X-API-Key';
|
|
731
776
|
fieldMapping = parseFieldMapping(custom.fieldMapping);
|
|
732
777
|
}
|
|
778
|
+
else {
|
|
779
|
+
apiKey = '';
|
|
780
|
+
}
|
|
733
781
|
const dedicatedFirst = config.platformDedicatedFirst?.[type] ?? false;
|
|
734
782
|
if (!fieldMapping) {
|
|
735
783
|
fieldMapping = parseFieldMapping(config.globalFieldMapping);
|
|
@@ -762,13 +810,77 @@ function apply(ctx, config) {
|
|
|
762
810
|
return cleanUrl(url);
|
|
763
811
|
}
|
|
764
812
|
}
|
|
765
|
-
async function downloadFile(url, timeout, maxSize,
|
|
813
|
+
async function downloadFile(url, timeout, maxSize, filePrefix, fileExts) {
|
|
766
814
|
if (!url)
|
|
767
815
|
throw new Error('链接为空');
|
|
768
|
-
await promises_1.default.mkdir(
|
|
769
|
-
const
|
|
816
|
+
await promises_1.default.mkdir(cacheDir, { recursive: true });
|
|
817
|
+
const extRegexCache = {};
|
|
818
|
+
const ext = fileExts.find(e => {
|
|
819
|
+
const r = extRegexCache[e] || (extRegexCache[e] = new RegExp('\\.' + e + '(\\?|$)', 'i'));
|
|
820
|
+
return r.test(url);
|
|
821
|
+
}) || fileExts[0];
|
|
770
822
|
const fileName = `${filePrefix}_${Date.now()}_${(0, crypto_1.randomBytes)(4).toString('hex')}.${ext}`;
|
|
771
|
-
const filePath = path_1.default.resolve(
|
|
823
|
+
const filePath = path_1.default.resolve(cacheDir, fileName);
|
|
824
|
+
if (ctx.downloads) {
|
|
825
|
+
try {
|
|
826
|
+
const dest = await ctx.downloads.download(url, path_1.default.join(cacheDir, fileName), {
|
|
827
|
+
headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' },
|
|
828
|
+
timeout
|
|
829
|
+
});
|
|
830
|
+
const stat = await promises_1.default.stat(dest);
|
|
831
|
+
if (maxSize > 0 && stat.size > maxSize * 1024 * 1024) {
|
|
832
|
+
await promises_1.default.unlink(dest).catch(() => { });
|
|
833
|
+
throw new Error(`文件过大(${Math.round(stat.size / 1024 / 1024)}MB),超过限制(${maxSize}MB)`);
|
|
834
|
+
}
|
|
835
|
+
return dest;
|
|
836
|
+
}
|
|
837
|
+
catch (e) {
|
|
838
|
+
debugLog('ERROR', `downloads 服务下载失败,回退: ${getErrorMessage(e)}`);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
if (aria2 && config.resumeDownload) {
|
|
842
|
+
try {
|
|
843
|
+
const gid = await aria2.call('aria2.addUri', [url], {
|
|
844
|
+
dir: cacheDir,
|
|
845
|
+
out: fileName,
|
|
846
|
+
split: 4,
|
|
847
|
+
continue: true,
|
|
848
|
+
maxConnectionPerServer: 5,
|
|
849
|
+
timeout: timeout / 1000,
|
|
850
|
+
maxFileNotFound: 5,
|
|
851
|
+
maxTries: 5,
|
|
852
|
+
retryWait: 2,
|
|
853
|
+
header: [`User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36`, `Referer: https://www.baidu.com/`]
|
|
854
|
+
});
|
|
855
|
+
let completed = false;
|
|
856
|
+
const ariaStartTime = Date.now();
|
|
857
|
+
while (!completed) {
|
|
858
|
+
if (Date.now() - ariaStartTime > timeout) {
|
|
859
|
+
await aria2.call('aria2.remove', gid).catch(() => { });
|
|
860
|
+
throw new Error('aria2下载超时');
|
|
861
|
+
}
|
|
862
|
+
const status = await aria2.call('aria2.tellStatus', gid);
|
|
863
|
+
if (status.status === 'complete') {
|
|
864
|
+
completed = true;
|
|
865
|
+
}
|
|
866
|
+
else if (status.status === 'error' || status.status === 'removed') {
|
|
867
|
+
throw new Error('aria2下载失败');
|
|
868
|
+
}
|
|
869
|
+
else {
|
|
870
|
+
await delay(1000);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
const stat = await promises_1.default.stat(filePath);
|
|
874
|
+
if (maxSize > 0 && stat.size > maxSize * 1024 * 1024) {
|
|
875
|
+
await promises_1.default.unlink(filePath).catch(() => { });
|
|
876
|
+
throw new Error(`文件过大(${Math.round(stat.size / 1024 / 1024)}MB),超过限制(${maxSize}MB)`);
|
|
877
|
+
}
|
|
878
|
+
return filePath;
|
|
879
|
+
}
|
|
880
|
+
catch (e) {
|
|
881
|
+
debugLog('ERROR', `aria2下载失败,回退内置下载: ${getErrorMessage(e)}`);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
772
884
|
const writer = (0, fs_1.createWriteStream)(filePath);
|
|
773
885
|
let response;
|
|
774
886
|
try {
|
|
@@ -803,7 +915,7 @@ function apply(ctx, config) {
|
|
|
803
915
|
throw new Error(`写入文件失败: ${getErrorMessage(e)}`);
|
|
804
916
|
}
|
|
805
917
|
}
|
|
806
|
-
async function sendMedia(session, url, type, forceDownload, showFile
|
|
918
|
+
async function sendMedia(session, url, type, forceDownload, showFile) {
|
|
807
919
|
if (!url)
|
|
808
920
|
return;
|
|
809
921
|
await downloadLimiter.acquire();
|
|
@@ -818,7 +930,7 @@ function apply(ctx, config) {
|
|
|
818
930
|
const sendFunc = type === 'audio' ? koishi_1.h.audio : type === 'video' ? koishi_1.h.video : koishi_1.h.image;
|
|
819
931
|
if (forceDownload) {
|
|
820
932
|
try {
|
|
821
|
-
const localPath = await downloadFile(url,
|
|
933
|
+
const localPath = await downloadFile(url, mediaDownloadTimeout, maxMediaSize, prefixMap[type], extMap[type]);
|
|
822
934
|
try {
|
|
823
935
|
await sendWithTimeout(session, sendFunc(`file://${localPath}`));
|
|
824
936
|
}
|
|
@@ -847,7 +959,7 @@ function apply(ctx, config) {
|
|
|
847
959
|
}
|
|
848
960
|
catch {
|
|
849
961
|
try {
|
|
850
|
-
const localPath = await downloadFile(url,
|
|
962
|
+
const localPath = await downloadFile(url, mediaDownloadTimeout, maxMediaSize, prefixMap[type], extMap[type]);
|
|
851
963
|
try {
|
|
852
964
|
await sendWithTimeout(session, sendFunc(`file://${localPath}`));
|
|
853
965
|
}
|
|
@@ -957,26 +1069,26 @@ function apply(ctx, config) {
|
|
|
957
1069
|
await delay(300);
|
|
958
1070
|
}
|
|
959
1071
|
if (p.cover && config.showCoverImage && p.type !== 'live_photo' && !(p.type === 'live' && (p.live_photo?.length || p.images?.length))) {
|
|
960
|
-
await sendMedia(session, p.cover, 'image', config.forceDownloadImage, config.showImageFile
|
|
1072
|
+
await sendMedia(session, p.cover, 'image', config.forceDownloadImage, config.showImageFile).catch(() => { });
|
|
961
1073
|
await delay(300);
|
|
962
1074
|
}
|
|
963
1075
|
if (config.showMusicCover && p.music.cover) {
|
|
964
|
-
await sendMedia(session, p.music.cover, 'image', config.forceDownloadImage, config.showImageFile
|
|
1076
|
+
await sendMedia(session, p.music.cover, 'image', config.forceDownloadImage, config.showImageFile).catch(() => { });
|
|
965
1077
|
await delay(300);
|
|
966
1078
|
}
|
|
967
1079
|
if (p.video && (p.type === 'video' || (p.type === 'live' && !p.live_photo?.length && !p.images?.length))) {
|
|
968
|
-
await sendMedia(session, p.video, 'video', config.forceDownloadVideo, config.showVideoFile
|
|
1080
|
+
await sendMedia(session, p.video, 'video', config.forceDownloadVideo, config.showVideoFile).catch(() => { });
|
|
969
1081
|
await delay(500);
|
|
970
1082
|
}
|
|
971
1083
|
if (p.type === 'image' || p.type === 'live_photo' || (p.type === 'live' && (p.live_photo?.length || p.images?.length))) {
|
|
972
1084
|
const imageUrls = p.images?.length ? p.images : (p.live_photo?.map(lp => lp.image) ?? []);
|
|
973
1085
|
for (const imgUrl of imageUrls) {
|
|
974
|
-
await sendMedia(session, imgUrl, 'image', config.forceDownloadImage, config.showImageFile
|
|
1086
|
+
await sendMedia(session, imgUrl, 'image', config.forceDownloadImage, config.showImageFile).catch(() => { });
|
|
975
1087
|
await delay(200);
|
|
976
1088
|
}
|
|
977
1089
|
}
|
|
978
1090
|
if (config.showMusicVoice && p.music.url) {
|
|
979
|
-
await sendMedia(session, p.music.url, 'audio', config.forceDownloadMusicVoice, config.showMusicVoiceFile
|
|
1091
|
+
await sendMedia(session, p.music.url, 'audio', config.forceDownloadMusicVoice, config.showMusicVoiceFile).catch(() => { });
|
|
980
1092
|
await delay(300);
|
|
981
1093
|
}
|
|
982
1094
|
}
|
|
@@ -1170,19 +1282,16 @@ function apply(ctx, config) {
|
|
|
1170
1282
|
});
|
|
1171
1283
|
const tempCleanupInterval = setInterval(async () => {
|
|
1172
1284
|
try {
|
|
1173
|
-
const
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
if (now - stats.mtimeMs > 3600000) {
|
|
1184
|
-
await promises_1.default.unlink(filePath).catch(() => { });
|
|
1185
|
-
}
|
|
1285
|
+
const files = await promises_1.default.readdir(cacheDir);
|
|
1286
|
+
const now = Date.now();
|
|
1287
|
+
for (const file of files) {
|
|
1288
|
+
if ((file.startsWith('video_') && file.endsWith('.mp4')) ||
|
|
1289
|
+
(file.startsWith('img_') && file.match(/\.(png|jpg|jpeg|gif|webp)$/i)) ||
|
|
1290
|
+
(file.startsWith('music_') && file.match(/\.(mp3|wav|ogg|flac|aac|m4a)$/i))) {
|
|
1291
|
+
const filePath = path_1.default.join(cacheDir, file);
|
|
1292
|
+
const stats = await promises_1.default.stat(filePath);
|
|
1293
|
+
if (now - stats.mtimeMs > 3600000) {
|
|
1294
|
+
await promises_1.default.unlink(filePath).catch(() => { });
|
|
1186
1295
|
}
|
|
1187
1296
|
}
|
|
1188
1297
|
}
|
|
@@ -1193,21 +1302,20 @@ function apply(ctx, config) {
|
|
|
1193
1302
|
}, 3600000);
|
|
1194
1303
|
ctx.on('dispose', () => {
|
|
1195
1304
|
clearInterval(tempCleanupInterval);
|
|
1305
|
+
if (aria2)
|
|
1306
|
+
aria2.close();
|
|
1196
1307
|
urlCacheLocal.clear();
|
|
1197
1308
|
dedupCache.clear();
|
|
1198
1309
|
debugLog('INFO', '插件已卸载');
|
|
1199
1310
|
});
|
|
1200
1311
|
process.on('beforeExit', async () => {
|
|
1201
1312
|
try {
|
|
1202
|
-
const
|
|
1203
|
-
for (const
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
(file.startsWith('music_') && file.match(/\.(mp3|wav|ogg|flac|aac|m4a)$/i))) {
|
|
1209
|
-
await promises_1.default.unlink(path_1.default.join(dir, file)).catch(() => { });
|
|
1210
|
-
}
|
|
1313
|
+
const files = await promises_1.default.readdir(cacheDir);
|
|
1314
|
+
for (const file of files) {
|
|
1315
|
+
if ((file.startsWith('video_') && file.endsWith('.mp4')) ||
|
|
1316
|
+
(file.startsWith('img_') && file.match(/\.(png|jpg|jpeg|gif|webp)$/i)) ||
|
|
1317
|
+
(file.startsWith('music_') && file.match(/\.(mp3|wav|ogg|flac|aac|m4a)$/i))) {
|
|
1318
|
+
await promises_1.default.unlink(path_1.default.join(cacheDir, file)).catch(() => { });
|
|
1211
1319
|
}
|
|
1212
1320
|
}
|
|
1213
1321
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-video-parser-all",
|
|
3
3
|
"description": "Koishi 全平台视频/图集解析插件,支持抖音/快手/B站/微博/小红书/剪映/YouTube/TikTok等20+平台",
|
|
4
|
-
"version": "1.3.
|
|
4
|
+
"version": "1.3.7",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -45,7 +45,6 @@
|
|
|
45
45
|
"twitter",
|
|
46
46
|
"instagram",
|
|
47
47
|
"doubao",
|
|
48
|
-
"doubao_chat",
|
|
49
48
|
"jimeng",
|
|
50
49
|
"oasis",
|
|
51
50
|
"wechat_channel",
|
|
@@ -94,14 +93,14 @@
|
|
|
94
93
|
"typescript": "^5.3.3"
|
|
95
94
|
},
|
|
96
95
|
"dependencies": {
|
|
97
|
-
"axios": "^1.16.1"
|
|
98
|
-
"fast-xml-parser": "^4.5.6"
|
|
96
|
+
"axios": "^1.16.1"
|
|
99
97
|
},
|
|
100
98
|
"peerDependencies": {
|
|
101
99
|
"@koishijs/plugin-console": "^5.30.4",
|
|
102
100
|
"koishi": "^4.18.7",
|
|
103
101
|
"koishi-plugin-silk": "^1.0.0",
|
|
104
|
-
"koishi-plugin-ffmpeg": "^1.0.0"
|
|
102
|
+
"koishi-plugin-ffmpeg": "^1.0.0",
|
|
103
|
+
"aria2": "^4.1.2"
|
|
105
104
|
},
|
|
106
105
|
"peerDependenciesMeta": {
|
|
107
106
|
"koishi-plugin-silk": {
|
|
@@ -109,6 +108,9 @@
|
|
|
109
108
|
},
|
|
110
109
|
"koishi-plugin-ffmpeg": {
|
|
111
110
|
"optional": true
|
|
111
|
+
},
|
|
112
|
+
"aria2": {
|
|
113
|
+
"optional": true
|
|
112
114
|
}
|
|
113
115
|
},
|
|
114
116
|
"repository": {
|
package/readme.md
CHANGED
|
@@ -20,89 +20,94 @@ This is a **multi-platform video/image parsing plugin** developed for the Koishi
|
|
|
20
20
|
|
|
21
21
|
## 配置项说明 (Configuration)
|
|
22
22
|
|
|
23
|
-
###
|
|
23
|
+
### 基本设置
|
|
24
24
|
| 配置项 | 类型 | 默认值 | 说明 |
|
|
25
25
|
|--------|------|--------|------|
|
|
26
|
-
| `enable` | boolean | true |
|
|
27
|
-
| `botName` | string | 视频解析机器人 |
|
|
28
|
-
| `showWaitingTip` | boolean | true |
|
|
29
|
-
| `debug` | boolean | false |
|
|
30
|
-
| `platformEnabled` | object |
|
|
26
|
+
| `enable` | boolean | true | 启用插件 |
|
|
27
|
+
| `botName` | string | 视频解析机器人 | 合并转发中的昵称 |
|
|
28
|
+
| `showWaitingTip` | boolean | true | 显示等待提示 |
|
|
29
|
+
| `debug` | boolean | false | Debug 日志 |
|
|
30
|
+
| `platformEnabled` | object | 全开 | 各平台开关 |
|
|
31
31
|
|
|
32
|
-
###
|
|
32
|
+
### 消息格式
|
|
33
33
|
| 配置项 | 类型 | 默认值 | 说明 |
|
|
34
34
|
|--------|------|--------|------|
|
|
35
|
-
| `unifiedMessageFormat` | string |
|
|
35
|
+
| `unifiedMessageFormat` | string | 见预设 | 文字格式,支持变量,空行自动隐藏 |
|
|
36
36
|
|
|
37
|
-
###
|
|
37
|
+
### 媒体发送
|
|
38
38
|
| 配置项 | 类型 | 默认值 | 说明 |
|
|
39
39
|
|--------|------|--------|------|
|
|
40
|
-
| `showImageText` | boolean | true |
|
|
41
|
-
| `showCoverImage` | boolean | true |
|
|
42
|
-
| `showMusicCover` | boolean | true |
|
|
43
|
-
| `showImageFile` | boolean | true |
|
|
44
|
-
| `
|
|
45
|
-
| `
|
|
46
|
-
| `
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
| `forceDownloadVideo` | boolean | false | 强制下载视频后发送 |
|
|
50
|
-
| `videoDownloadTimeout` | number | 120000 | 视频下载超时(毫秒) |
|
|
51
|
-
| `tempDir` | string | `./temp_videos` | 临时视频存储目录 |
|
|
52
|
-
| `maxVideoSize` | number | 0 | 最大下载视频大小(MB),0 不限制 |
|
|
53
|
-
| `maxDescLength` | number | 200 | 简介最大长度(字符) |
|
|
54
|
-
| `maxConcurrent` | number | 3 | 解析最大并发数 |
|
|
55
|
-
| `downloadConcurrency` | number | 3 | 下载线程数(≥1 整数) |
|
|
56
|
-
| `showMusicVoice` | boolean | false | 是否发送音乐(转语音)。**需要依赖 `koishi-plugin-silk` 和 `koishi-plugin-ffmpeg`** |
|
|
57
|
-
| `showMusicVoiceFile` | boolean | true | 音乐语音是否以文件形式发送(关闭则只发链接) |
|
|
58
|
-
| `forceDownloadMusicVoice` | boolean | false | 强制下载音乐语音后发送 |
|
|
59
|
-
| `musicDownloadTimeout` | number | 120000 | 音乐下载超时(毫秒) |
|
|
60
|
-
| `musicTempDir` | string | `./temp_music` | 临时音乐存储目录 |
|
|
61
|
-
| `maxMusicSize` | number | 0 | 最大下载音乐大小(MB),0 不限制 |
|
|
62
|
-
|
|
63
|
-
### 网络与 API 设置 (Network & API Settings)
|
|
40
|
+
| `showImageText` | boolean | true | 发送文字内容 |
|
|
41
|
+
| `showCoverImage` | boolean | true | 发送封面图片 |
|
|
42
|
+
| `showMusicCover` | boolean | true | 发送音乐封面 |
|
|
43
|
+
| `showImageFile` | boolean | true | 封面/图片是否以图片形式发送(关闭则只发送链接) |
|
|
44
|
+
| `showVideoFile` | boolean | true | 视频是否以视频形式发送(关闭则只发送链接) |
|
|
45
|
+
| `forceDownloadImage` | boolean | false | 强制下载封面/图片 |
|
|
46
|
+
| `forceDownloadVideo` | boolean | false | 强制下载视频 |
|
|
47
|
+
|
|
48
|
+
### 音乐语音(需 silk 和 ffmpeg)
|
|
64
49
|
| 配置项 | 类型 | 默认值 | 说明 |
|
|
65
50
|
|--------|------|--------|------|
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
69
|
-
| `proxy` | object | `{ enabled: false, protocol: "http", host: "127.0.0.1", port: 7890, auth: { username: "", password: "" } }` | HTTP/HTTPS 代理。`enabled` 开关(默认关闭),`protocol` 下拉选择 `http` 或 `https` |
|
|
70
|
-
| `customHeaders` | array | [] | 自定义请求头,每项含 `name` 和 `value` |
|
|
51
|
+
| `showMusicVoice` | boolean | false | 音乐链接以语音发送 |
|
|
52
|
+
| `showMusicVoiceFile` | boolean | true | 音乐语音是否以文件形式发送(关闭则只发送链接) |
|
|
53
|
+
| `forceDownloadMusicVoice` | boolean | false | 强制下载音乐语音 |
|
|
71
54
|
|
|
72
|
-
###
|
|
55
|
+
### 性能与限制
|
|
73
56
|
| 配置项 | 类型 | 默认值 | 说明 |
|
|
74
57
|
|--------|------|--------|------|
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
58
|
+
| `maxDescLength` | number | 200 | 简介长度上限 |
|
|
59
|
+
| `maxConcurrent` | number | 3 | 解析最大并发数 |
|
|
60
|
+
| `downloadConcurrency` | number | 3 | 下载线程数 |
|
|
61
|
+
| `mediaDownloadTimeout` | number | 120000 | 统一下载超时 (ms) |
|
|
62
|
+
| `maxMediaSize` | number | 0 | 最大下载文件大小 (MB),0 为不限制 |
|
|
63
|
+
| `downloadEngine` | string | internal | 下载引擎(internal / aria2) |
|
|
64
|
+
| `aria2Host` | string | 127.0.0.1 | aria2 RPC 地址 |
|
|
65
|
+
| `aria2Port` | number | 6800 | aria2 RPC 端口 |
|
|
66
|
+
| `aria2Secret` | string | | aria2 RPC 密钥 |
|
|
67
|
+
| `resumeDownload` | boolean | true | 启用断点续传(仅 aria2) |
|
|
68
|
+
|
|
69
|
+
### 网络与请求
|
|
70
|
+
| 配置项 | 类型 | 默认值 | 说明 |
|
|
71
|
+
|--------|------|--------|------|
|
|
72
|
+
| `timeout` | number | 180000 | API 超时 (ms) |
|
|
73
|
+
| `videoSendTimeout` | number | 180000 | 发送超时 (ms) |
|
|
74
|
+
| `userAgent` | string | 见预设 | User-Agent |
|
|
75
|
+
| `proxy` | object | ... | HTTP/HTTPS 代理 |
|
|
76
|
+
| `customHeaders` | array | [] | 自定义请求头 |
|
|
79
77
|
|
|
80
|
-
###
|
|
78
|
+
### 发送与重试
|
|
81
79
|
| 配置项 | 类型 | 默认值 | 说明 |
|
|
82
80
|
|--------|------|--------|------|
|
|
83
81
|
| `ignoreSendError` | boolean | true | 忽略发送失败 |
|
|
84
82
|
| `retryTimes` | number | 3 | 重试次数 |
|
|
85
|
-
| `retryInterval` | number | 1000 |
|
|
83
|
+
| `retryInterval` | number | 1000 | 重试间隔 (ms) |
|
|
84
|
+
| `enableForward` | boolean | false | 合并转发(OneBot/Satori) |
|
|
86
85
|
|
|
87
|
-
###
|
|
86
|
+
### 缓存与临时文件
|
|
88
87
|
| 配置项 | 类型 | 默认值 | 说明 |
|
|
89
88
|
|--------|------|--------|------|
|
|
90
|
-
| `
|
|
89
|
+
| `deduplicationInterval` | number | 180 | 去重间隔 (s) |
|
|
90
|
+
| `cacheTTL` | number | 600 | 缓存时间 (s) |
|
|
91
|
+
| `cacheDir` | string | ./temp_cache | 统一临时目录 |
|
|
91
92
|
|
|
92
|
-
###
|
|
93
|
+
### API 与平台
|
|
93
94
|
| 配置项 | 类型 | 默认值 | 说明 |
|
|
94
95
|
|--------|------|--------|------|
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
| `apiKeys` | array | [] | 多 API 密钥列表 |
|
|
97
|
+
| `rotationMode` | string | sequential | 密钥轮换模式(sequential / load_balance) |
|
|
98
|
+
| `platformDedicatedFirst` | object | 全关 | 优先专属 API |
|
|
99
|
+
| `customApis` | array | [] | 覆盖内置平台 API |
|
|
100
|
+
| `customPlatforms` | array | [] | 自定义新平台 |
|
|
101
|
+
| `globalFieldMapping` | string | 预设 | 全局字段映射 JSON |
|
|
102
|
+
|
|
103
|
+
### 界面文本
|
|
99
104
|
| 配置项 | 类型 | 默认值 | 说明 |
|
|
100
105
|
|--------|------|--------|------|
|
|
101
|
-
| `waitingTipText` | string |
|
|
102
|
-
| `unsupportedPlatformText` | string |
|
|
103
|
-
| `invalidLinkText` | string |
|
|
106
|
+
| `waitingTipText` | string | 正在解析... | 等待提示 |
|
|
107
|
+
| `unsupportedPlatformText` | string | 不支持该平台 | 不支持提示 |
|
|
108
|
+
| `invalidLinkText` | string | 无效链接 | 无效链接提示 |
|
|
104
109
|
| `parseErrorPrefix` | string | ❌ 解析失败: | 错误前缀 |
|
|
105
|
-
| `parseErrorItemFormat` | string |
|
|
110
|
+
| `parseErrorItemFormat` | string | ... | 错误格式 |
|
|
106
111
|
|
|
107
112
|
## 支持的变量 (Supported Variables)
|
|
108
113
|
在 `unifiedMessageFormat` 中可使用以下变量,空行自动隐藏:
|
|
@@ -125,13 +130,19 @@ This is a **multi-platform video/image parsing plugin** developed for the Koishi
|
|
|
125
130
|
| `${音乐标题}` | 音乐标题 |
|
|
126
131
|
| `${音乐作者}` | 音乐作者 |
|
|
127
132
|
|
|
128
|
-
##
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
- `koishi-plugin-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
## 依赖说明 (Dependencies)
|
|
134
|
+
### 音乐语音(可选)
|
|
135
|
+
若启用 `showMusicVoice`,请安装:
|
|
136
|
+
- `koishi-plugin-silk`:silk 编解码
|
|
137
|
+
- `koishi-plugin-ffmpeg`:音频重采样
|
|
138
|
+
### aria2 下载引擎(可选)
|
|
139
|
+
若启用 `downloadEngine: 'aria2'`,请安装并启动 aria2 服务,并安装 npm 包 `aria2`:
|
|
140
|
+
- 安装 aria2 服务端:https://github.com/aria2/aria2
|
|
141
|
+
- 安装 npm 客户端:`npm install aria2`
|
|
142
|
+
- 启动 RPC:`aria2c --enable-rpc --rpc-listen-all=true --rpc-allow-origin-all`
|
|
143
|
+
未满足条件时自动降级为内置下载,不影响正常使用。
|
|
144
|
+
### downloads 服务(可选)
|
|
145
|
+
插件会自动检测 Koishi 内置 `downloads` 服务,优先使用其下载文件,失败时回退到内置/aria2 下载。
|
|
135
146
|
|
|
136
147
|
## 支持的平台 (Supported Platforms)
|
|
137
148
|
| 平台名称 | 关键词识别 | 解析能力 |
|
|
@@ -150,22 +161,19 @@ This is a **multi-platform video/image parsing plugin** developed for the Koishi
|
|
|
150
161
|
| YouTube(油管) | youtube, youtu.be | 视频 |
|
|
151
162
|
| TikTok(国际版抖音) | tiktok, tiktok.com | 短视频 |
|
|
152
163
|
| 好看视频 | haokan, haokan.baidu.com | 短视频 |
|
|
153
|
-
| 梨视频 | video.li | 短视频 |
|
|
154
164
|
| 美拍 | meipai, meipai.com | 短视频 |
|
|
155
|
-
| 全民直播 | quanmin (quanmin.tv) | 直播 |
|
|
156
165
|
| Twitter / X | twitter, x.com | 视频、图文 |
|
|
157
166
|
| Instagram | instagram, instagram.com | 图文、Reels |
|
|
158
167
|
| 豆包 | doubao (doubao.com/video) | 视频 |
|
|
159
|
-
| 豆包对话 | doubao (doubao.com/thread) | 对话分享 |
|
|
160
168
|
| 皮皮搞笑 | pipigx, h5.pipigx.com | 短视频 |
|
|
161
169
|
| 皮皮虾 | pipixia, h5.pipix.com | 短视频 |
|
|
162
170
|
| 最右 | zuiyou, xiaochuankeji.cn | 短视频 |
|
|
171
|
+
| 梨视频 | video.li, pearvideo.com | 短视频 |
|
|
172
|
+
| 全民直播 | quanmin (quanmin.tv) | 直播 |
|
|
163
173
|
| 绿洲 (Oasis) | oasis.weibo.com | 视频、图文 |
|
|
164
174
|
| 视频号 (WeChat Channels) | channels.weixin.qq.com, weixin.qq.com/sph/ | 短视频 |
|
|
165
175
|
| 🔧 自定义平台 | 通过 `customPlatforms` 添加 | 取决于 API |
|
|
166
176
|
|
|
167
|
-
> 注:部分平台解析能力可能因API限制有所差异。可通过 `platformEnabled` 单独关闭。
|
|
168
|
-
|
|
169
177
|
## 项目贡献者 (Contributors)
|
|
170
178
|
|
|
171
179
|
| 贡献者 (Contributor) | 贡献内容 (Contribution) |
|