koishi-plugin-anime-schedule 1.0.8 → 1.1.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/lib/config.d.ts +6 -5
- package/lib/config.js +37 -23
- package/lib/constants.d.ts +77 -22
- package/lib/constants.js +85 -32
- package/lib/data/anilist-match.d.ts +7 -16
- package/lib/data/anilist-match.js +7 -16
- package/lib/data/bangumi-data-loader.d.ts +2 -1
- package/lib/data/bangumi-data-loader.js +4 -8
- package/lib/data/bangumi-data-source.d.ts +6 -20
- package/lib/data/bangumi-data-source.js +6 -19
- package/lib/data/bangumi-data.d.ts +26 -48
- package/lib/data/bangumi-data.js +20 -29
- package/lib/handlers.d.ts +17 -21
- package/lib/handlers.js +126 -71
- package/lib/index.d.ts +1 -1
- package/lib/index.js +26 -22
- package/lib/render/canvas.d.ts +12 -17
- package/lib/render/canvas.js +25 -14
- package/lib/render/covers.d.ts +4 -9
- package/lib/render/covers.js +40 -23
- package/lib/render/fonts.d.ts +5 -11
- package/lib/render/fonts.js +44 -12
- package/lib/render/image.d.ts +53 -76
- package/lib/render/image.js +224 -209
- package/lib/render/info.d.ts +2 -28
- package/lib/render/info.js +179 -167
- package/lib/render/text.d.ts +9 -56
- package/lib/render/text.js +21 -68
- package/lib/services/airing.d.ts +20 -47
- package/lib/services/airing.js +23 -45
- package/lib/services/digest.d.ts +87 -231
- package/lib/services/digest.js +196 -410
- package/lib/services/dispatch.d.ts +7 -15
- package/lib/services/dispatch.js +7 -15
- package/lib/services/info.d.ts +52 -38
- package/lib/services/info.js +158 -80
- package/lib/services/localizer.d.ts +50 -45
- package/lib/services/localizer.js +61 -105
- package/lib/services/network.d.ts +33 -20
- package/lib/services/network.js +57 -14
- package/lib/services/scheduler.d.ts +11 -22
- package/lib/services/scheduler.js +11 -22
- package/lib/types.d.ts +41 -74
- package/lib/types.js +1 -6
- package/lib/utils/index.d.ts +14 -1
- package/lib/utils/index.js +53 -8
- package/lib/utils/time.d.ts +1 -1
- package/lib/utils/time.js +1 -1
- package/package.json +3 -3
- package/readme.md +21 -21
package/lib/config.d.ts
CHANGED
|
@@ -28,6 +28,8 @@ export interface Config {
|
|
|
28
28
|
format: DigestFormat;
|
|
29
29
|
layout: ImageLayout;
|
|
30
30
|
coverLimit: number;
|
|
31
|
+
/** `anime.info` 按关键词查询时最多出几部,`0` 表示不限制 */
|
|
32
|
+
infoKeywordLimit: number;
|
|
31
33
|
showCover: boolean;
|
|
32
34
|
showScore: boolean;
|
|
33
35
|
useForward: boolean;
|
|
@@ -46,8 +48,10 @@ export interface Config {
|
|
|
46
48
|
fontFamily: string;
|
|
47
49
|
coverConcurrency: number;
|
|
48
50
|
coverCacheDays: number;
|
|
49
|
-
/** JPEG 编码质量(
|
|
51
|
+
/** JPEG 编码质量(1-100) */
|
|
50
52
|
jpegQuality: number;
|
|
53
|
+
/** 超采样倍数:画布按「逻辑尺寸 × 该值」建立后再等比缩放,小字笔画更锐利,体积与耗时随之上升 */
|
|
54
|
+
supersample: number;
|
|
51
55
|
};
|
|
52
56
|
subscription: {
|
|
53
57
|
/** 单个群最多订阅的番剧数量,0 表示不限制 */
|
|
@@ -60,8 +64,5 @@ export interface Config {
|
|
|
60
64
|
};
|
|
61
65
|
}
|
|
62
66
|
export declare const Config: Schema<Config>;
|
|
63
|
-
/**
|
|
64
|
-
* 把配置夹到安全区间。
|
|
65
|
-
* 只保证「类型正确」与「不小于最小值」,不设人为上限。
|
|
66
|
-
*/
|
|
67
|
+
/** 把配置夹到安全区间:只保证类型正确且不小于最小值,不设人为上限 */
|
|
67
68
|
export declare function normalizeConfig(config: Config): Config;
|
package/lib/config.js
CHANGED
|
@@ -24,7 +24,7 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
24
24
|
koishi_1.Schema.const('Asia/Taipei').description('Asia/Taipei (UTC+8)'),
|
|
25
25
|
koishi_1.Schema.const('UTC').description('UTC'),
|
|
26
26
|
])
|
|
27
|
-
.description('
|
|
27
|
+
.description('时刻与日期换算时区')
|
|
28
28
|
.default('Asia/Shanghai'),
|
|
29
29
|
locale: koishi_1.Schema.union([
|
|
30
30
|
koishi_1.Schema.const('zh-CN').description('简体中文'),
|
|
@@ -39,10 +39,10 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
39
39
|
dailyEnabled: koishi_1.Schema.boolean().description('每日更新表').default(true),
|
|
40
40
|
dailyTime: koishi_1.Schema.string().pattern(TIME_PATTERN).description('推送时刻').default('08:00'),
|
|
41
41
|
dailyOffsetDays: koishi_1.Schema.number()
|
|
42
|
-
.min(
|
|
43
|
-
.description('
|
|
42
|
+
.min(constants_1.DAILY_OFFSET_MIN).max(constants_1.DAILY_OFFSET_MAX).step(1)
|
|
43
|
+
.description('日期偏移,0 当天、-1 前一天、1 后一天')
|
|
44
44
|
.default(0),
|
|
45
|
-
nightEnabled: koishi_1.Schema.boolean().description('
|
|
45
|
+
nightEnabled: koishi_1.Schema.boolean().description('深夜档,当晚 21:00 至次日 06:00').default(true),
|
|
46
46
|
nightTime: koishi_1.Schema.string().pattern(TIME_PATTERN).description('推送时刻').default('21:00'),
|
|
47
47
|
weeklyEnabled: koishi_1.Schema.boolean().description('本周更新表').default(true),
|
|
48
48
|
weeklyWeekday: koishi_1.Schema.union(WEEKDAYS.map((item) => koishi_1.Schema.const(item.value).description(item.label)))
|
|
@@ -66,11 +66,11 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
66
66
|
content: koishi_1.Schema.object({
|
|
67
67
|
maxItems: koishi_1.Schema.number()
|
|
68
68
|
.min(0).step(10)
|
|
69
|
-
.description('单次推送最多显示多少部,0
|
|
69
|
+
.description('单次推送最多显示多少部,0 不限制')
|
|
70
70
|
.default(0),
|
|
71
71
|
longRunningThreshold: koishi_1.Schema.number()
|
|
72
72
|
.min(0).step(1)
|
|
73
|
-
.description('剔除长期连载番的话数阈值,0
|
|
73
|
+
.description('剔除长期连载番的话数阈值,0 不过滤')
|
|
74
74
|
.default(0),
|
|
75
75
|
}),
|
|
76
76
|
}).description('推送内容'),
|
|
@@ -80,7 +80,7 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
80
80
|
koishi_1.Schema.const('image').description('风格化图片'),
|
|
81
81
|
koishi_1.Schema.const('text').description('纯文本'),
|
|
82
82
|
])
|
|
83
|
-
.description('
|
|
83
|
+
.description('推送形态,图片渲染失败时自动改用文本')
|
|
84
84
|
.default('image'),
|
|
85
85
|
layout: koishi_1.Schema.union([
|
|
86
86
|
koishi_1.Schema.const('table').description('时间表'),
|
|
@@ -90,12 +90,16 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
90
90
|
.default('table'),
|
|
91
91
|
coverLimit: koishi_1.Schema.number()
|
|
92
92
|
.min(0)
|
|
93
|
-
.description('单张图片最多加载多少张封面,0
|
|
93
|
+
.description('单张图片最多加载多少张封面,0 不限制')
|
|
94
94
|
.default(0),
|
|
95
|
+
infoKeywordLimit: koishi_1.Schema.number()
|
|
96
|
+
.min(0)
|
|
97
|
+
.description('anime.info 按关键词查询番剧时显示数量上限,0 不限制')
|
|
98
|
+
.default(20),
|
|
95
99
|
showCover: koishi_1.Schema.boolean().description('显示封面').default(true),
|
|
96
|
-
showScore: koishi_1.Schema.boolean().description('显示评分,评价不足 10
|
|
100
|
+
showScore: koishi_1.Schema.boolean().description('显示评分,评价不足 10 条时隐藏').default(true),
|
|
97
101
|
useForward: koishi_1.Schema.boolean()
|
|
98
|
-
.description('
|
|
102
|
+
.description('纯文本模式用合并转发,避免刷屏')
|
|
99
103
|
.default(true),
|
|
100
104
|
}),
|
|
101
105
|
}).description('输出设置'),
|
|
@@ -121,14 +125,14 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
121
125
|
.default(30),
|
|
122
126
|
airingTtlHours: koishi_1.Schema.number()
|
|
123
127
|
.min(1).step(1)
|
|
124
|
-
.description('
|
|
128
|
+
.description('是否已播完的缓存小时数')
|
|
125
129
|
.default(6),
|
|
126
130
|
}),
|
|
127
131
|
}).description('本地化'),
|
|
128
132
|
koishi_1.Schema.object({
|
|
129
133
|
render: koishi_1.Schema.object({
|
|
130
134
|
fontFamily: koishi_1.Schema.string()
|
|
131
|
-
.description('
|
|
135
|
+
.description('字体族,留空则自动寻找系统中文字体')
|
|
132
136
|
.default(constants_1.DEFAULT_FONT_FAMILY),
|
|
133
137
|
coverConcurrency: koishi_1.Schema.number()
|
|
134
138
|
.min(1)
|
|
@@ -136,19 +140,23 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
136
140
|
.default(constants_1.COVER_CONCURRENCY),
|
|
137
141
|
coverCacheDays: koishi_1.Schema.number()
|
|
138
142
|
.min(0)
|
|
139
|
-
.description('封面缓存天数,0
|
|
143
|
+
.description('封面缓存天数,0 不写磁盘')
|
|
140
144
|
.default(21),
|
|
141
145
|
jpegQuality: koishi_1.Schema.number()
|
|
142
146
|
.min(1).max(100).step(1)
|
|
143
|
-
.description('
|
|
147
|
+
.description('编码质量')
|
|
144
148
|
.default(constants_1.DEFAULT_JPEG_QUALITY),
|
|
149
|
+
supersample: koishi_1.Schema.number()
|
|
150
|
+
.min(1).max(constants_1.MAX_SUPERSAMPLE).step(1)
|
|
151
|
+
.description('超采样倍数')
|
|
152
|
+
.default(constants_1.DEFAULT_SUPERSAMPLE),
|
|
145
153
|
}),
|
|
146
154
|
}).description('图片'),
|
|
147
155
|
koishi_1.Schema.object({
|
|
148
156
|
subscription: koishi_1.Schema.object({
|
|
149
157
|
maxPerChannel: koishi_1.Schema.number()
|
|
150
158
|
.min(0).step(1)
|
|
151
|
-
.description('单个群最多订阅多少部,0
|
|
159
|
+
.description('单个群最多订阅多少部,0 不限制')
|
|
152
160
|
.default(0),
|
|
153
161
|
}),
|
|
154
162
|
}).description('订阅'),
|
|
@@ -157,7 +165,7 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
157
165
|
requestTimeout: koishi_1.Schema.number()
|
|
158
166
|
.min(1)
|
|
159
167
|
.description('单次请求超时(秒)')
|
|
160
|
-
.default(
|
|
168
|
+
.default(constants_1.LOCALIZE_TIMEOUT_SECONDS),
|
|
161
169
|
retries: koishi_1.Schema.number()
|
|
162
170
|
.min(0)
|
|
163
171
|
.description('请求失败重试次数')
|
|
@@ -168,18 +176,22 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
168
176
|
}),
|
|
169
177
|
}).description('其他'),
|
|
170
178
|
]);
|
|
171
|
-
/**
|
|
172
|
-
* 把配置夹到安全区间。
|
|
173
|
-
* 只保证「类型正确」与「不小于最小值」,不设人为上限。
|
|
174
|
-
*/
|
|
179
|
+
/** 把配置夹到安全区间:只保证类型正确且不小于最小值,不设人为上限 */
|
|
175
180
|
function normalizeConfig(config) {
|
|
181
|
+
/** 取整并夹到不小于 `min`(默认 0)。允许负值的字段不能走这个默认值 */
|
|
176
182
|
const int = (value, fallback, min = 0) => {
|
|
177
183
|
if (typeof value !== 'number' || !Number.isFinite(value))
|
|
178
184
|
return fallback;
|
|
179
185
|
return Math.max(min, Math.round(value));
|
|
180
186
|
};
|
|
187
|
+
/** 取整并夹到 `[min, max]`;用于允许负值的字段,如 `dailyOffsetDays` */
|
|
188
|
+
const intBetween = (value, fallback, min, max) => {
|
|
189
|
+
if (typeof value !== 'number' || !Number.isFinite(value))
|
|
190
|
+
return fallback;
|
|
191
|
+
return Math.min(max, Math.max(min, Math.round(value)));
|
|
192
|
+
};
|
|
181
193
|
const time = (value, fallback) => (typeof value === 'string' && TIME_PATTERN.test(value) ? value : fallback);
|
|
182
|
-
//
|
|
194
|
+
// 旧版本的 `both` 形态已移除:非 `text` 一律按图片处理
|
|
183
195
|
const format = config.output?.format === 'text' ? 'text' : 'image';
|
|
184
196
|
return {
|
|
185
197
|
...config,
|
|
@@ -187,7 +199,7 @@ function normalizeConfig(config) {
|
|
|
187
199
|
push: {
|
|
188
200
|
...config.push,
|
|
189
201
|
dailyTime: time(config.push?.dailyTime, '08:00'),
|
|
190
|
-
dailyOffsetDays:
|
|
202
|
+
dailyOffsetDays: intBetween(config.push?.dailyOffsetDays, 0, constants_1.DAILY_OFFSET_MIN, constants_1.DAILY_OFFSET_MAX),
|
|
191
203
|
weeklyWeekday: Math.max(0, Math.min(6, int(config.push?.weeklyWeekday, 1))),
|
|
192
204
|
nightTime: time(config.push?.nightTime, '21:00'),
|
|
193
205
|
weeklyTime: time(config.push?.weeklyTime, '08:00'),
|
|
@@ -204,6 +216,7 @@ function normalizeConfig(config) {
|
|
|
204
216
|
format,
|
|
205
217
|
layout: config.output?.layout === 'list' ? 'list' : 'table',
|
|
206
218
|
coverLimit: int(config.output?.coverLimit, 0),
|
|
219
|
+
infoKeywordLimit: int(config.output?.infoKeywordLimit, 20),
|
|
207
220
|
},
|
|
208
221
|
localize: {
|
|
209
222
|
...config.localize,
|
|
@@ -217,6 +230,7 @@ function normalizeConfig(config) {
|
|
|
217
230
|
coverConcurrency: int(config.render?.coverConcurrency, constants_1.COVER_CONCURRENCY, 1),
|
|
218
231
|
coverCacheDays: int(config.render?.coverCacheDays, 21),
|
|
219
232
|
jpegQuality: Math.min(100, int(config.render?.jpegQuality, constants_1.DEFAULT_JPEG_QUALITY, 1)),
|
|
233
|
+
supersample: Math.min(constants_1.MAX_SUPERSAMPLE, int(config.render?.supersample, constants_1.DEFAULT_SUPERSAMPLE, 1)),
|
|
220
234
|
},
|
|
221
235
|
subscription: {
|
|
222
236
|
...config.subscription,
|
|
@@ -224,7 +238,7 @@ function normalizeConfig(config) {
|
|
|
224
238
|
},
|
|
225
239
|
advanced: {
|
|
226
240
|
...config.advanced,
|
|
227
|
-
requestTimeout: int(config.advanced?.requestTimeout,
|
|
241
|
+
requestTimeout: int(config.advanced?.requestTimeout, constants_1.LOCALIZE_TIMEOUT_SECONDS, 1),
|
|
228
242
|
retries: int(config.advanced?.retries, 2),
|
|
229
243
|
},
|
|
230
244
|
};
|
package/lib/constants.d.ts
CHANGED
|
@@ -19,6 +19,12 @@ export declare const ANILIST_BATCH_SIZE = 50;
|
|
|
19
19
|
* 相当于最多 50 次/分钟,既不会触发突发限流,也远低于上限。
|
|
20
20
|
*/
|
|
21
21
|
export declare const ANILIST_MIN_INTERVAL_MS = 1200;
|
|
22
|
+
/**
|
|
23
|
+
* 收到 429 / 403 后的冷却时长。
|
|
24
|
+
*
|
|
25
|
+
* 429 是超限封禁、403 是 API 被临时停用,两种情况都沿用已有数据一段时间再试。
|
|
26
|
+
*/
|
|
27
|
+
export declare const ANILIST_COOLDOWN_MS: number;
|
|
22
28
|
/**
|
|
23
29
|
* bangumi-data 索引。
|
|
24
30
|
*
|
|
@@ -74,9 +80,8 @@ export declare const DEFAULT_SCORE_TTL_HOURS = 24;
|
|
|
74
80
|
/**
|
|
75
81
|
* 显示评分所需的最少评价人数。
|
|
76
82
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* 会误导人的数字。
|
|
83
|
+
* 低于此数不显示评分:新番开播初期样本极少,评分会随样本增加剧烈变化
|
|
84
|
+
* (实测至高之力 1 票时 7.0,156 票时 6.1)。留空优于给出误导性的数字。
|
|
80
85
|
*/
|
|
81
86
|
export declare const MIN_SCORE_VOTES = 10;
|
|
82
87
|
export declare const LOCALIZE_CACHE_VERSION = 6;
|
|
@@ -86,10 +91,18 @@ export declare const LOCALIZE_CACHE_MAX_ENTRIES = 5000;
|
|
|
86
91
|
export declare const EPISODE_QUERY_LIMIT = 100;
|
|
87
92
|
/**
|
|
88
93
|
* 并发检索 Bangumi 的条数上限。
|
|
89
|
-
*
|
|
90
|
-
*
|
|
94
|
+
*
|
|
95
|
+
* Bangumi 没有公开的严格限流,但一次本季表可能有上百部作品。取 6 路:实测 4 路时周表
|
|
96
|
+
* 取详情要 50 秒上下,6 路约省三分之一,仍远低于会让站点限流的密度。
|
|
97
|
+
*/
|
|
98
|
+
export declare const LOCALIZE_CONCURRENCY = 6;
|
|
99
|
+
/**
|
|
100
|
+
* 单次元数据请求的超时(秒),`advanced.requestTimeout` 的默认值。
|
|
101
|
+
*
|
|
102
|
+
* Bangumi 的条目接口是小体积 JSON,正常几百毫秒返回。等满 20 秒基本等价于连接已死,
|
|
103
|
+
* 而一条超时会把整次构建拖住这么久,所以默认压到 10 秒;嫌紧可以自行调大。
|
|
91
104
|
*/
|
|
92
|
-
export declare const
|
|
105
|
+
export declare const LOCALIZE_TIMEOUT_SECONDS = 10;
|
|
93
106
|
/** 并发拉取封面时的并发上限 */
|
|
94
107
|
export declare const COVER_CONCURRENCY = 8;
|
|
95
108
|
/** 封面下载超时与体积上限,避免个别大图拖垮整次渲染 */
|
|
@@ -106,11 +119,30 @@ export declare const BOLD_FONT_CANDIDATES: string[];
|
|
|
106
119
|
/**
|
|
107
120
|
* JPEG 编码质量。
|
|
108
121
|
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
* 不值得为省这点流量牺牲清晰度。想压体积可以调低。
|
|
122
|
+
* 图片以文字与细线条为主,块状伪影比在照片上明显。取 100 为无损编码,文字边缘最锐利;
|
|
123
|
+
* 体积比 92 约多三成,可调低换取更小体积。
|
|
112
124
|
*/
|
|
113
125
|
export declare const DEFAULT_JPEG_QUALITY = 100;
|
|
126
|
+
/**
|
|
127
|
+
* 超采样倍数。
|
|
128
|
+
*
|
|
129
|
+
* 画布按「逻辑尺寸 × 该值」建立后在上下文上做等比 `scale`,缩回目标尺寸时由更多采样
|
|
130
|
+
* 得出边缘像素。清晰度主要取决于此:各版面最小字号仅 10~11px,1× 下一个字不足十几个
|
|
131
|
+
* 像素,JPEG 质量再高也无法还原。
|
|
132
|
+
*
|
|
133
|
+
* 3 在密集的封面与小字上收益仍可见;5 是收益递减的上限。
|
|
134
|
+
*/
|
|
135
|
+
export declare const DEFAULT_SUPERSAMPLE = 3;
|
|
136
|
+
/** 超采样倍数的可选上限,与配置项的 `.max()` 保持一致 */
|
|
137
|
+
export declare const MAX_SUPERSAMPLE = 5;
|
|
138
|
+
/**
|
|
139
|
+
* 每日更新表的日期偏移范围(天)。
|
|
140
|
+
*
|
|
141
|
+
* 配置项的 `.min()/.max()` 与 `normalizeConfig` 的钳制共用这两个值——
|
|
142
|
+
* 两处分开写时极易只改一处,导致「配置页能填、实际被悄悄夹回去」。
|
|
143
|
+
*/
|
|
144
|
+
export declare const DAILY_OFFSET_MIN = -7;
|
|
145
|
+
export declare const DAILY_OFFSET_MAX = 7;
|
|
114
146
|
/** 注册到 canvas 的字体族名,与 config.render.fontFamily 的默认值保持一致 */
|
|
115
147
|
export declare const DEFAULT_FONT_FAMILY = "Anime Schedule Sans";
|
|
116
148
|
/**
|
|
@@ -131,14 +163,21 @@ export declare const IMAGE_LAYOUT: {
|
|
|
131
163
|
footerHeight: number;
|
|
132
164
|
/** 周历表:七天并排的画布宽度 */
|
|
133
165
|
weekWidth: number;
|
|
134
|
-
/**
|
|
166
|
+
/**
|
|
167
|
+
* 单日时间轴(每日 / 深夜档)画布宽度。
|
|
168
|
+
*
|
|
169
|
+
* 等于「左边距 + 时刻栏 + 轨道 + 时刻↔卡片间距 + 卡片宽 + 右边距」,不留余量,
|
|
170
|
+
* 使「现在」虚线的终点与卡片右缘重合。
|
|
171
|
+
*/
|
|
135
172
|
timelineWidth: number;
|
|
136
173
|
/** 卡片列表(list 排版)的画布宽度 */
|
|
137
174
|
listWidth: number;
|
|
138
175
|
/** 本季封面网格的画布宽度,要放得下 10 列 */
|
|
139
176
|
seasonWidth: number;
|
|
140
|
-
/**
|
|
177
|
+
/** 行卡片高度:容纳固定的两行标题 + 一行元信息 */
|
|
141
178
|
rowHeight: number;
|
|
179
|
+
/** 行卡片宽度,窄于画布,避免文字仅占左半、右侧留空 */
|
|
180
|
+
rowCardWidth: number;
|
|
142
181
|
/** 行卡片之间的间距 */
|
|
143
182
|
rowGap: number;
|
|
144
183
|
/** 行卡片圆角与内边距 */
|
|
@@ -148,12 +187,15 @@ export declare const IMAGE_LAYOUT: {
|
|
|
148
187
|
mediaHeight: number;
|
|
149
188
|
/** 封面与文字之间的间距 */
|
|
150
189
|
mediaGap: number;
|
|
151
|
-
/**
|
|
190
|
+
/** 标题行高与固定行数:短标题也占两行,使元信息行在所有卡片上对齐 */
|
|
191
|
+
cardTitleLineHeight: number;
|
|
192
|
+
cardTitleLines: number;
|
|
193
|
+
/** 标题块与元信息行之间的间距 */
|
|
152
194
|
cardLineGap: number;
|
|
153
195
|
/** 封面圆角 */
|
|
154
196
|
mediaRadius: number;
|
|
155
|
-
/**
|
|
156
|
-
|
|
197
|
+
/** 左侧时刻栏宽度(内容左缘到轨道)。需容纳 `现在 18:30` 这类最长时刻文本,否则被画布左缘裁掉 */
|
|
198
|
+
timelineLabelWidth: number;
|
|
157
199
|
/** 轨道圆点半径 */
|
|
158
200
|
timelineDot: number;
|
|
159
201
|
/** 时刻文字与轨道之间的间距 */
|
|
@@ -176,8 +218,7 @@ export declare const IMAGE_LAYOUT: {
|
|
|
176
218
|
weekTitleLineHeight: number;
|
|
177
219
|
/** 周历表条目内的封面高度(与三行文字齐高,保持 1 : 1.4 比例不再被压扁) */
|
|
178
220
|
weekMediaHeight: number;
|
|
179
|
-
/**
|
|
180
|
-
cardHeight: number;
|
|
221
|
+
/** 列表卡片沿用行卡片高度,两种排版保持一致 */
|
|
181
222
|
cardGap: number;
|
|
182
223
|
/** 两栏之间的间距 */
|
|
183
224
|
cardColumnGap: number;
|
|
@@ -205,17 +246,35 @@ export declare const IMAGE_THEME: {
|
|
|
205
246
|
surface: string;
|
|
206
247
|
cardBorder: string;
|
|
207
248
|
divider: string;
|
|
208
|
-
/**
|
|
249
|
+
/**
|
|
250
|
+
* 强调色:时刻、日期、「今天」高亮,以及详情卡的已播集数与峰值柱。
|
|
251
|
+
*
|
|
252
|
+
* 取珊瑚红 `#e26d6d`——比纯红柔和,整张表看下来不刺眼;详情卡的实心块共用它,
|
|
253
|
+
* 于是「已播集数」与列表里的「已订阅」气泡是同一套视觉语言。
|
|
254
|
+
*/
|
|
209
255
|
accent: string;
|
|
210
256
|
/** 封面的底色(等比完整显示时留白的那一圈) */
|
|
211
257
|
badge: string;
|
|
258
|
+
/** 强调色的浅色底:软胶囊用 */
|
|
259
|
+
accentSoft: string;
|
|
260
|
+
/** 未播集数 / 禁用态的浅灰底 */
|
|
261
|
+
inert: string;
|
|
262
|
+
/** 未播集数的文字色,比 muted 更浅,明确表示「还没到」 */
|
|
263
|
+
inertText: string;
|
|
264
|
+
/** 未播集数的描边:仅靠底色时与白色卡片难以区分,需勾出轮廓 */
|
|
265
|
+
inertBorder: string;
|
|
266
|
+
/** 评分柱的非峰值色 */
|
|
267
|
+
scoreBar: string;
|
|
212
268
|
title: string;
|
|
213
269
|
body: string;
|
|
214
270
|
muted: string;
|
|
215
271
|
onAccent: string;
|
|
272
|
+
/** 评分:橙金色,全插件统一 */
|
|
216
273
|
score: string;
|
|
217
274
|
/** 卡片投影:略深一点,白色卡片才从浅灰背景上立起来 */
|
|
218
275
|
shadow: string;
|
|
276
|
+
/** 封面投影,比卡片投影更轻 */
|
|
277
|
+
coverShadow: string;
|
|
219
278
|
};
|
|
220
279
|
/**
|
|
221
280
|
* 面向用户的文本。默认中文,`ja-JP` / `en-US` 覆盖展示层最常读到的句子,
|
|
@@ -254,12 +313,8 @@ export interface Messages {
|
|
|
254
313
|
footer: string;
|
|
255
314
|
/** 周历表表头的日期区间,例如 `9月21日 - 9月27日`;两端已由 formatDayLabel 本地化 */
|
|
256
315
|
rangeLabel: (start: string, end: string) => string;
|
|
257
|
-
/** 今天的日期列角标 */
|
|
258
|
-
todayBadge: string;
|
|
259
316
|
/** 已在订阅列表里的作品,条目上的气泡 */
|
|
260
317
|
subscribedBadge: string;
|
|
261
|
-
/** 日期列超出上限时的提示 */
|
|
262
|
-
moreItems: (count: number) => string;
|
|
263
318
|
/** 详情卡页脚的数据更新时间 */
|
|
264
319
|
infoUpdated: (moment: string) => string;
|
|
265
320
|
/** 连载状态 */
|
|
@@ -301,7 +356,7 @@ export declare const RESPONSES: {
|
|
|
301
356
|
readonly databaseFailed: "数据库操作失败,请检查后台日志。";
|
|
302
357
|
readonly renderFailed: "图片渲染失败,已改用文本发送。";
|
|
303
358
|
readonly emptySearch: (keyword: string) => string;
|
|
304
|
-
readonly notFound: "
|
|
359
|
+
readonly notFound: "未找到该作品,可能不在 bangumi-data 索引中。";
|
|
305
360
|
};
|
|
306
361
|
/** 日志里出现的固定短语 */
|
|
307
362
|
export declare const LOG_MESSAGES: {
|
package/lib/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LOG_MESSAGES = exports.RESPONSES = exports.MESSAGES = exports.IMAGE_THEME = exports.IMAGE_LAYOUT = exports.DEFAULT_FONT_FAMILY = exports.DEFAULT_JPEG_QUALITY = exports.BOLD_FONT_CANDIDATES = exports.FONT_CANDIDATES = exports.COVER_CACHE_MAX_FILES = exports.COVER_CACHE_TTL_MS = exports.COVER_CACHE_DIR = exports.COVER_MAX_BYTES = exports.COVER_TIMEOUT_MS = exports.COVER_CONCURRENCY = exports.LOCALIZE_CONCURRENCY = exports.EPISODE_QUERY_LIMIT = exports.LOCALIZE_CACHE_MAX_ENTRIES = exports.LOCALIZE_CACHE_VERSION = exports.MIN_SCORE_VOTES = exports.DEFAULT_SCORE_TTL_HOURS = exports.LOCALIZE_CACHE_FILE = exports.LATE_NIGHT_END_HOUR = exports.IMAGE_LIST_TWO_COLUMN_AT = exports.IMAGE_MAX_CARDS = exports.DIGEST_LABELS = exports.PLUGIN_HOMEPAGE = exports.PLUGIN_VERSION = exports.DATA_DIR = exports.BANGUMI_DATA_FILE = exports.BANGUMI_DATA_FALLBACK_URLS = exports.ANILIST_MIN_INTERVAL_MS = exports.ANILIST_BATCH_SIZE = exports.ANILIST_API_URL = exports.BANGUMI_API_HOST = exports.PLUGIN_NAME = void 0;
|
|
3
|
+
exports.LOG_MESSAGES = exports.RESPONSES = exports.MESSAGES = exports.IMAGE_THEME = exports.IMAGE_LAYOUT = exports.DEFAULT_FONT_FAMILY = exports.DAILY_OFFSET_MAX = exports.DAILY_OFFSET_MIN = exports.MAX_SUPERSAMPLE = exports.DEFAULT_SUPERSAMPLE = exports.DEFAULT_JPEG_QUALITY = exports.BOLD_FONT_CANDIDATES = exports.FONT_CANDIDATES = exports.COVER_CACHE_MAX_FILES = exports.COVER_CACHE_TTL_MS = exports.COVER_CACHE_DIR = exports.COVER_MAX_BYTES = exports.COVER_TIMEOUT_MS = exports.COVER_CONCURRENCY = exports.LOCALIZE_TIMEOUT_SECONDS = exports.LOCALIZE_CONCURRENCY = exports.EPISODE_QUERY_LIMIT = exports.LOCALIZE_CACHE_MAX_ENTRIES = exports.LOCALIZE_CACHE_VERSION = exports.MIN_SCORE_VOTES = exports.DEFAULT_SCORE_TTL_HOURS = exports.LOCALIZE_CACHE_FILE = exports.LATE_NIGHT_END_HOUR = exports.IMAGE_LIST_TWO_COLUMN_AT = exports.IMAGE_MAX_CARDS = exports.DIGEST_LABELS = exports.PLUGIN_HOMEPAGE = exports.PLUGIN_VERSION = exports.DATA_DIR = exports.BANGUMI_DATA_FILE = exports.BANGUMI_DATA_FALLBACK_URLS = exports.ANILIST_COOLDOWN_MS = exports.ANILIST_MIN_INTERVAL_MS = exports.ANILIST_BATCH_SIZE = exports.ANILIST_API_URL = exports.BANGUMI_API_HOST = exports.PLUGIN_NAME = void 0;
|
|
4
4
|
exports.buildUserAgent = buildUserAgent;
|
|
5
5
|
exports.getMessages = getMessages;
|
|
6
6
|
/** 插件名,同时作为日志前缀与 User-Agent 主体 */
|
|
@@ -23,6 +23,12 @@ exports.ANILIST_BATCH_SIZE = 50;
|
|
|
23
23
|
* 相当于最多 50 次/分钟,既不会触发突发限流,也远低于上限。
|
|
24
24
|
*/
|
|
25
25
|
exports.ANILIST_MIN_INTERVAL_MS = 1200;
|
|
26
|
+
/**
|
|
27
|
+
* 收到 429 / 403 后的冷却时长。
|
|
28
|
+
*
|
|
29
|
+
* 429 是超限封禁、403 是 API 被临时停用,两种情况都沿用已有数据一段时间再试。
|
|
30
|
+
*/
|
|
31
|
+
exports.ANILIST_COOLDOWN_MS = 5 * 60 * 1000;
|
|
26
32
|
/**
|
|
27
33
|
* bangumi-data 索引。
|
|
28
34
|
*
|
|
@@ -90,9 +96,8 @@ exports.DEFAULT_SCORE_TTL_HOURS = 24;
|
|
|
90
96
|
/**
|
|
91
97
|
* 显示评分所需的最少评价人数。
|
|
92
98
|
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
* 会误导人的数字。
|
|
99
|
+
* 低于此数不显示评分:新番开播初期样本极少,评分会随样本增加剧烈变化
|
|
100
|
+
* (实测至高之力 1 票时 7.0,156 票时 6.1)。留空优于给出误导性的数字。
|
|
96
101
|
*/
|
|
97
102
|
exports.MIN_SCORE_VOTES = 10;
|
|
98
103
|
exports.LOCALIZE_CACHE_VERSION = 6;
|
|
@@ -102,10 +107,18 @@ exports.LOCALIZE_CACHE_MAX_ENTRIES = 5000;
|
|
|
102
107
|
exports.EPISODE_QUERY_LIMIT = 100;
|
|
103
108
|
/**
|
|
104
109
|
* 并发检索 Bangumi 的条数上限。
|
|
105
|
-
*
|
|
106
|
-
*
|
|
110
|
+
*
|
|
111
|
+
* Bangumi 没有公开的严格限流,但一次本季表可能有上百部作品。取 6 路:实测 4 路时周表
|
|
112
|
+
* 取详情要 50 秒上下,6 路约省三分之一,仍远低于会让站点限流的密度。
|
|
113
|
+
*/
|
|
114
|
+
exports.LOCALIZE_CONCURRENCY = 6;
|
|
115
|
+
/**
|
|
116
|
+
* 单次元数据请求的超时(秒),`advanced.requestTimeout` 的默认值。
|
|
117
|
+
*
|
|
118
|
+
* Bangumi 的条目接口是小体积 JSON,正常几百毫秒返回。等满 20 秒基本等价于连接已死,
|
|
119
|
+
* 而一条超时会把整次构建拖住这么久,所以默认压到 10 秒;嫌紧可以自行调大。
|
|
107
120
|
*/
|
|
108
|
-
exports.
|
|
121
|
+
exports.LOCALIZE_TIMEOUT_SECONDS = 10;
|
|
109
122
|
/** 并发拉取封面时的并发上限 */
|
|
110
123
|
exports.COVER_CONCURRENCY = 8;
|
|
111
124
|
/** 封面下载超时与体积上限,避免个别大图拖垮整次渲染 */
|
|
@@ -143,11 +156,30 @@ exports.BOLD_FONT_CANDIDATES = [
|
|
|
143
156
|
/**
|
|
144
157
|
* JPEG 编码质量。
|
|
145
158
|
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
* 不值得为省这点流量牺牲清晰度。想压体积可以调低。
|
|
159
|
+
* 图片以文字与细线条为主,块状伪影比在照片上明显。取 100 为无损编码,文字边缘最锐利;
|
|
160
|
+
* 体积比 92 约多三成,可调低换取更小体积。
|
|
149
161
|
*/
|
|
150
162
|
exports.DEFAULT_JPEG_QUALITY = 100;
|
|
163
|
+
/**
|
|
164
|
+
* 超采样倍数。
|
|
165
|
+
*
|
|
166
|
+
* 画布按「逻辑尺寸 × 该值」建立后在上下文上做等比 `scale`,缩回目标尺寸时由更多采样
|
|
167
|
+
* 得出边缘像素。清晰度主要取决于此:各版面最小字号仅 10~11px,1× 下一个字不足十几个
|
|
168
|
+
* 像素,JPEG 质量再高也无法还原。
|
|
169
|
+
*
|
|
170
|
+
* 3 在密集的封面与小字上收益仍可见;5 是收益递减的上限。
|
|
171
|
+
*/
|
|
172
|
+
exports.DEFAULT_SUPERSAMPLE = 3;
|
|
173
|
+
/** 超采样倍数的可选上限,与配置项的 `.max()` 保持一致 */
|
|
174
|
+
exports.MAX_SUPERSAMPLE = 5;
|
|
175
|
+
/**
|
|
176
|
+
* 每日更新表的日期偏移范围(天)。
|
|
177
|
+
*
|
|
178
|
+
* 配置项的 `.min()/.max()` 与 `normalizeConfig` 的钳制共用这两个值——
|
|
179
|
+
* 两处分开写时极易只改一处,导致「配置页能填、实际被悄悄夹回去」。
|
|
180
|
+
*/
|
|
181
|
+
exports.DAILY_OFFSET_MIN = -7;
|
|
182
|
+
exports.DAILY_OFFSET_MAX = 7;
|
|
151
183
|
/** 注册到 canvas 的字体族名,与 config.render.fontFamily 的默认值保持一致 */
|
|
152
184
|
exports.DEFAULT_FONT_FAMILY = 'Anime Schedule Sans';
|
|
153
185
|
/**
|
|
@@ -162,37 +194,47 @@ exports.DEFAULT_FONT_FAMILY = 'Anime Schedule Sans';
|
|
|
162
194
|
*/
|
|
163
195
|
exports.IMAGE_LAYOUT = {
|
|
164
196
|
/** 统一外边距 */
|
|
165
|
-
padding:
|
|
197
|
+
padding: 28,
|
|
166
198
|
/** 页眉 / 页脚固定高度 */
|
|
167
199
|
headerHeight: 120,
|
|
168
200
|
footerHeight: 52,
|
|
169
201
|
/** 周历表:七天并排的画布宽度 */
|
|
170
202
|
weekWidth: 1680,
|
|
171
|
-
/**
|
|
172
|
-
|
|
203
|
+
/**
|
|
204
|
+
* 单日时间轴(每日 / 深夜档)画布宽度。
|
|
205
|
+
*
|
|
206
|
+
* 等于「左边距 + 时刻栏 + 轨道 + 时刻↔卡片间距 + 卡片宽 + 右边距」,不留余量,
|
|
207
|
+
* 使「现在」虚线的终点与卡片右缘重合。
|
|
208
|
+
*/
|
|
209
|
+
timelineWidth: 798,
|
|
173
210
|
/** 卡片列表(list 排版)的画布宽度 */
|
|
174
211
|
listWidth: 900,
|
|
175
212
|
/** 本季封面网格的画布宽度,要放得下 10 列 */
|
|
176
213
|
seasonWidth: 1680,
|
|
177
214
|
// ---- 通用行卡片 ----
|
|
178
|
-
/**
|
|
179
|
-
rowHeight:
|
|
215
|
+
/** 行卡片高度:容纳固定的两行标题 + 一行元信息 */
|
|
216
|
+
rowHeight: 104,
|
|
217
|
+
/** 行卡片宽度,窄于画布,避免文字仅占左半、右侧留空 */
|
|
218
|
+
rowCardWidth: 640,
|
|
180
219
|
/** 行卡片之间的间距 */
|
|
181
220
|
rowGap: 8,
|
|
182
221
|
/** 行卡片圆角与内边距 */
|
|
183
222
|
rowRadius: 12,
|
|
184
|
-
rowPadding:
|
|
223
|
+
rowPadding: 10,
|
|
185
224
|
/** 行卡片封面高度,宽度按 1 : 1.4 推出 */
|
|
186
|
-
mediaHeight:
|
|
225
|
+
mediaHeight: 92,
|
|
187
226
|
/** 封面与文字之间的间距 */
|
|
188
227
|
mediaGap: 14,
|
|
189
|
-
/**
|
|
228
|
+
/** 标题行高与固定行数:短标题也占两行,使元信息行在所有卡片上对齐 */
|
|
229
|
+
cardTitleLineHeight: 22,
|
|
230
|
+
cardTitleLines: 2,
|
|
231
|
+
/** 标题块与元信息行之间的间距 */
|
|
190
232
|
cardLineGap: 6,
|
|
191
233
|
/** 封面圆角 */
|
|
192
234
|
mediaRadius: 6,
|
|
193
235
|
// ---- 单日时间轴 ----
|
|
194
|
-
/**
|
|
195
|
-
|
|
236
|
+
/** 左侧时刻栏宽度(内容左缘到轨道)。需容纳 `现在 18:30` 这类最长时刻文本,否则被画布左缘裁掉 */
|
|
237
|
+
timelineLabelWidth: 70,
|
|
196
238
|
/** 轨道圆点半径 */
|
|
197
239
|
timelineDot: 4,
|
|
198
240
|
/** 时刻文字与轨道之间的间距 */
|
|
@@ -217,8 +259,7 @@ exports.IMAGE_LAYOUT = {
|
|
|
217
259
|
/** 周历表条目内的封面高度(与三行文字齐高,保持 1 : 1.4 比例不再被压扁) */
|
|
218
260
|
weekMediaHeight: 82,
|
|
219
261
|
// ---- 卡片列表 ----
|
|
220
|
-
/**
|
|
221
|
-
cardHeight: 112,
|
|
262
|
+
/** 列表卡片沿用行卡片高度,两种排版保持一致 */
|
|
222
263
|
cardGap: 12,
|
|
223
264
|
/** 两栏之间的间距 */
|
|
224
265
|
cardColumnGap: 14,
|
|
@@ -247,17 +288,35 @@ exports.IMAGE_THEME = {
|
|
|
247
288
|
surface: '#ffffff',
|
|
248
289
|
cardBorder: '#e6e8ef',
|
|
249
290
|
divider: '#eef0f5',
|
|
250
|
-
/**
|
|
251
|
-
|
|
291
|
+
/**
|
|
292
|
+
* 强调色:时刻、日期、「今天」高亮,以及详情卡的已播集数与峰值柱。
|
|
293
|
+
*
|
|
294
|
+
* 取珊瑚红 `#e26d6d`——比纯红柔和,整张表看下来不刺眼;详情卡的实心块共用它,
|
|
295
|
+
* 于是「已播集数」与列表里的「已订阅」气泡是同一套视觉语言。
|
|
296
|
+
*/
|
|
297
|
+
accent: '#e26d6d',
|
|
252
298
|
/** 封面的底色(等比完整显示时留白的那一圈) */
|
|
253
299
|
badge: '#eef1f7',
|
|
300
|
+
/** 强调色的浅色底:软胶囊用 */
|
|
301
|
+
accentSoft: '#fdeceb',
|
|
302
|
+
/** 未播集数 / 禁用态的浅灰底 */
|
|
303
|
+
inert: '#f3f4f6',
|
|
304
|
+
/** 未播集数的文字色,比 muted 更浅,明确表示「还没到」 */
|
|
305
|
+
inertText: '#9ca3af',
|
|
306
|
+
/** 未播集数的描边:仅靠底色时与白色卡片难以区分,需勾出轮廓 */
|
|
307
|
+
inertBorder: '#e5e7eb',
|
|
308
|
+
/** 评分柱的非峰值色 */
|
|
309
|
+
scoreBar: '#fecaca',
|
|
254
310
|
title: '#1f2330',
|
|
255
311
|
body: '#5a6172',
|
|
256
312
|
muted: '#9aa1b1',
|
|
257
313
|
onAccent: '#ffffff',
|
|
258
|
-
|
|
314
|
+
/** 评分:橙金色,全插件统一 */
|
|
315
|
+
score: '#e69a3a',
|
|
259
316
|
/** 卡片投影:略深一点,白色卡片才从浅灰背景上立起来 */
|
|
260
317
|
shadow: 'rgba(31, 35, 48, 0.10)',
|
|
318
|
+
/** 封面投影,比卡片投影更轻 */
|
|
319
|
+
coverShadow: 'rgba(0, 0, 0, 0.06)',
|
|
261
320
|
};
|
|
262
321
|
const ZH_CN = {
|
|
263
322
|
weekdayNames: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
|
|
@@ -283,9 +342,7 @@ const ZH_CN = {
|
|
|
283
342
|
truncated: (shown, total) => `仅显示前 ${shown} 部,其余 ${total - shown} 部请使用指令按需查询。`,
|
|
284
343
|
footer: '数据来源: bangumi-data / Bangumi',
|
|
285
344
|
rangeLabel: (start, end) => `${start} - ${end}`,
|
|
286
|
-
todayBadge: '今天',
|
|
287
345
|
subscribedBadge: '已订阅',
|
|
288
|
-
moreItems: (count) => `还有 ${count} 部`,
|
|
289
346
|
infoUpdated: (moment) => `数据更新于 ${moment}`,
|
|
290
347
|
infoAiring: '连载中',
|
|
291
348
|
infoFinished: '已完结',
|
|
@@ -328,9 +385,7 @@ const JA_JP = {
|
|
|
328
385
|
truncated: (shown, total) => `先頭 ${shown} 作品のみ表示しています (残り ${total - shown} 作品)。`,
|
|
329
386
|
footer: 'データ提供: bangumi-data / Bangumi',
|
|
330
387
|
rangeLabel: (start, end) => `${start} 〜 ${end}`,
|
|
331
|
-
todayBadge: '今日',
|
|
332
388
|
subscribedBadge: '登録済み',
|
|
333
|
-
moreItems: (count) => `他 ${count} 作品`,
|
|
334
389
|
infoUpdated: (moment) => `データ更新 ${moment}`,
|
|
335
390
|
infoAiring: '放送中',
|
|
336
391
|
infoFinished: '完結',
|
|
@@ -373,9 +428,7 @@ const EN_US = {
|
|
|
373
428
|
truncated: (shown, total) => `Showing first ${shown} of ${total} titles.`,
|
|
374
429
|
footer: 'Data: bangumi-data / Bangumi',
|
|
375
430
|
rangeLabel: (start, end) => `${start} - ${end}`,
|
|
376
|
-
todayBadge: 'Today',
|
|
377
431
|
subscribedBadge: 'Subscribed',
|
|
378
|
-
moreItems: (count) => `+${count} more`,
|
|
379
432
|
infoUpdated: (moment) => `Updated ${moment}`,
|
|
380
433
|
infoAiring: 'Airing',
|
|
381
434
|
infoFinished: 'Finished',
|
|
@@ -415,7 +468,7 @@ exports.RESPONSES = {
|
|
|
415
468
|
databaseFailed: '数据库操作失败,请检查后台日志。',
|
|
416
469
|
renderFailed: '图片渲染失败,已改用文本发送。',
|
|
417
470
|
emptySearch: (keyword) => `未找到与 "${keyword}" 相关的作品,请尝试使用日文原名或更简短的关键词。`,
|
|
418
|
-
notFound: '
|
|
471
|
+
notFound: '未找到该作品,可能不在 bangumi-data 索引中。',
|
|
419
472
|
};
|
|
420
473
|
/** 日志里出现的固定短语 */
|
|
421
474
|
exports.LOG_MESSAGES = {
|