koishi-plugin-fimtale-api 1.0.1 → 1.0.3
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.js +37 -15
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -85,15 +85,18 @@ function apply(ctx, config) {
|
|
|
85
85
|
processed = processed.replace(/<script\b[^>]*>([\s\S]*?)<\/script>/gmi, "");
|
|
86
86
|
processed = processed.replace(/<style\b[^>]*>([\s\S]*?)<\/style>/gmi, "");
|
|
87
87
|
processed = processed.replace(/<iframe[^>]*>.*?<\/iframe>/gmi, '<p class="align-center" style="color:#999;font-size:0.8em;">[多媒体内容]</p>');
|
|
88
|
+
processed = processed.replace(
|
|
89
|
+
/<div class="material-placeholder">([\s\S]*?)<\/div>/gi,
|
|
90
|
+
(match, content) => {
|
|
91
|
+
const cleanImg = content.replace(/loading="lazy"/gi, "");
|
|
92
|
+
return `<figure class="img-box">${cleanImg}</figure>`;
|
|
93
|
+
}
|
|
94
|
+
);
|
|
88
95
|
processed = processed.replace(/<p[^>]*style="[^"]*text-align:\s*right[^"]*"[^>]*>/gi, '<p class="align-right">');
|
|
89
96
|
processed = processed.replace(/<p[^>]*style="[^"]*text-align:\s*center[^"]*"[^>]*>/gi, '<p class="align-center">');
|
|
90
97
|
processed = processed.replace(/<p[^>]*style="[^"]*text-indent:\s*0[^"]*"[^>]*>/gi, '<p class="no-indent">');
|
|
91
|
-
processed = processed.replace(
|
|
98
|
+
processed = processed.replace(/<\/?div[^>]*>/gi, "");
|
|
92
99
|
processed = processed.replace(/<blockquote[^>]*>/gi, "<blockquote>");
|
|
93
|
-
processed = processed.replace(/<div(?![^>]*class=["']img-box["'])[^>]*>/gi, "<p>");
|
|
94
|
-
processed = processed.replace(/<\/div>/gi, (match, offset, string) => {
|
|
95
|
-
return "</div>";
|
|
96
|
-
});
|
|
97
100
|
processed = processed.replace(/class=(["'])(?!(align-center|align-right|no-indent|img-box)\1)[^"']*\1/gi, "");
|
|
98
101
|
processed = processed.replace(/style\s*=\s*['"][^'"]*['"]/gi, "");
|
|
99
102
|
processed = processed.replace(/<\/?(span|font|o:p)[^>]*>/gi, "");
|
|
@@ -190,9 +193,10 @@ function apply(ctx, config) {
|
|
|
190
193
|
const renderCard = /* @__PURE__ */ __name(async (info, parent) => {
|
|
191
194
|
const isChapter = info.IsChapter || !!parent && parent.ID !== info.ID;
|
|
192
195
|
const displayTitle = isChapter && parent ? parent.Title : info.Title;
|
|
193
|
-
let displayCover =
|
|
194
|
-
if (isChapter && parent)
|
|
195
|
-
|
|
196
|
+
let displayCover = info.Background || extractImage(info.Content);
|
|
197
|
+
if (!displayCover && isChapter && parent) {
|
|
198
|
+
displayCover = parent.Background || extractImage(parent.Content);
|
|
199
|
+
}
|
|
196
200
|
const displayTagsObj = isChapter && parent ? parent.Tags : info.Tags;
|
|
197
201
|
const subTitle = isChapter ? info.Title : null;
|
|
198
202
|
const bgStyle = displayCover ? `background-image: url('${displayCover}');` : `background: ${generateGradient(displayTitle)};`;
|
|
@@ -204,6 +208,7 @@ function apply(ctx, config) {
|
|
|
204
208
|
if (displayTagsObj?.Type) tagsArr.push(displayTagsObj.Type);
|
|
205
209
|
if (displayTagsObj?.Rating && displayTagsObj.Rating !== "E") tagsArr.push(displayTagsObj.Rating);
|
|
206
210
|
if (displayTagsObj?.OtherTags) tagsArr.push(...displayTagsObj.OtherTags);
|
|
211
|
+
const likes = isChapter && parent ? parent.Upvotes || 0 : info.Upvotes || 0;
|
|
207
212
|
const html = `<!DOCTYPE html><html><head><style>
|
|
208
213
|
body { margin: 0; padding: 0; font-family: ${fontStack}; background: transparent; }
|
|
209
214
|
.card { width: 620px; height: 360px; background: #fff; border-radius: 16px; box-shadow: 0 10px 30px rgba(0,0,0,0.15); display: flex; overflow: hidden; }
|
|
@@ -227,7 +232,7 @@ function apply(ctx, config) {
|
|
|
227
232
|
<div class="summary-box"><div class="summary">${summary}</div></div>
|
|
228
233
|
<div class="footer">
|
|
229
234
|
<span class="stat"><b style="color:#009688">热度</b>${info.Views || 0}</span><span class="stat"><b style="color:#673ab7">评论</b>${info.Comments || 0}</span>
|
|
230
|
-
<span class="stat"><b style="color:#4caf50">赞</b>${
|
|
235
|
+
<span class="stat"><b style="color:#4caf50">赞</b>${likes}</span><span class="stat"><b style="color:#795548">字数</b>${info.WordCount || 0}</span>
|
|
231
236
|
</div></div></div></body></html>`;
|
|
232
237
|
const page = await ctx.puppeteer.page();
|
|
233
238
|
await injectCookies(page);
|
|
@@ -294,15 +299,12 @@ function apply(ctx, config) {
|
|
|
294
299
|
#viewport { position: absolute; top: ${marginTop}px; left: ${paddingX}px; width: ${contentWidth}px; height: ${optimalContentHeight}px; overflow: hidden; }
|
|
295
300
|
#content-scroller { height: 100%; width: 100%; column-width: ${contentWidth}px; column-gap: ${columnGap}px; column-fill: auto; padding: ${paddingY}px 0; box-sizing: border-box; font-size: ${config.fontSize}px; line-height: ${lineHeightRatio}; text-align: left; transform: translateX(0); transition: none; }
|
|
296
301
|
|
|
297
|
-
/* 基础文本 */
|
|
298
302
|
p, div { margin: 0 0 0.2em 0; text-indent: 2em; word-wrap: break-word; overflow-wrap: break-word; }
|
|
299
303
|
|
|
300
|
-
/* 辅助类 */
|
|
301
304
|
.align-center { text-align: center !important; text-align-last: center !important; text-indent: 0 !important; margin: 0.8em 0; font-weight: bold; color: #5d4037; }
|
|
302
305
|
.align-right { text-align: right !important; text-indent: 0 !important; margin-top: 0.5em; color: #666; font-style: italic; }
|
|
303
306
|
.no-indent { text-indent: 0 !important; }
|
|
304
307
|
|
|
305
|
-
/* 富文本支持 */
|
|
306
308
|
blockquote { margin: 1em 0.5em; padding-left: 1em; border-left: 4px solid #d7ccc8; color: #666; }
|
|
307
309
|
blockquote p { text-indent: 0; margin: 0.3em 0; }
|
|
308
310
|
|
|
@@ -326,11 +328,9 @@ function apply(ctx, config) {
|
|
|
326
328
|
|
|
327
329
|
a { color: #0277bd; text-decoration: none; }
|
|
328
330
|
|
|
329
|
-
|
|
330
|
-
.img-box { display: flex; justify-content: center; align-items: center; margin: 0.5em 0; width: 100%; }
|
|
331
|
+
figure.img-box { display: flex; justify-content: center; align-items: center; margin: 0.5em 0; width: 100%; }
|
|
331
332
|
img { max-width: 100%; height: auto; display: block; border-radius: 6px; box-shadow: 0 2px 6px rgba(0,0,0,0.1); }
|
|
332
333
|
|
|
333
|
-
/* 标题 */
|
|
334
334
|
h1, h2, h3 { font-size: 1.1em; margin: 0.8em 0; color: #5d4037; text-indent: 0; font-weight: bold; text-align: center; text-align-last: center; break-after: avoid; }
|
|
335
335
|
|
|
336
336
|
strong, b { font-weight: 900; color: #3e2723; }
|
|
@@ -346,6 +346,26 @@ function apply(ctx, config) {
|
|
|
346
346
|
await injectCookies(page);
|
|
347
347
|
await page.setContent(html);
|
|
348
348
|
await page.setViewport({ width: config.deviceWidth, height: config.deviceHeight, deviceScaleFactor: 2 });
|
|
349
|
+
await page.evaluate(async () => {
|
|
350
|
+
await document.fonts.ready;
|
|
351
|
+
await new Promise((resolve) => {
|
|
352
|
+
const imgs2 = Array.from(document.images);
|
|
353
|
+
if (!imgs2.length) return resolve(true);
|
|
354
|
+
let loaded = 0;
|
|
355
|
+
imgs2.forEach((img) => {
|
|
356
|
+
if (img.complete) {
|
|
357
|
+
loaded++;
|
|
358
|
+
if (loaded === imgs2.length) resolve(true);
|
|
359
|
+
} else {
|
|
360
|
+
img.onload = img.onerror = () => {
|
|
361
|
+
loaded++;
|
|
362
|
+
if (loaded === imgs2.length) resolve(true);
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
setTimeout(resolve, 3e3);
|
|
367
|
+
});
|
|
368
|
+
});
|
|
349
369
|
const scrollWidth = await page.$eval("#content-scroller", (el) => el.scrollWidth);
|
|
350
370
|
const step = contentWidth + columnGap;
|
|
351
371
|
const totalPages = Math.floor((scrollWidth + columnGap - 10) / step) + 1;
|
|
@@ -381,6 +401,8 @@ function apply(ctx, config) {
|
|
|
381
401
|
const pages = await renderReadPages(res.data);
|
|
382
402
|
const nodes = pages.map((buf) => (0, import_koishi.h)("message", import_koishi.h.image(buf, "image/jpeg")));
|
|
383
403
|
const navs = [];
|
|
404
|
+
const mainId = res.parent ? res.parent.ID : res.data.ID;
|
|
405
|
+
navs.push(`[目录] /ft.info ${mainId}`);
|
|
384
406
|
if (res.menu?.length) {
|
|
385
407
|
const idx = res.menu.findIndex((m) => m.ID.toString() === threadId);
|
|
386
408
|
if (idx > 0) navs.push(`[上一章] /ft.read ${res.menu[idx - 1].ID}`);
|