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.
Files changed (2) hide show
  1. package/lib/index.js +37 -15
  2. 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(/<div class="material-placeholder">/gi, '<div class="img-box">');
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 = null;
194
- if (isChapter && parent) displayCover = parent.Background || extractImage(parent.Content);
195
- if (!displayCover) displayCover = info.Background || extractImage(info.Content);
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>${info.Upvotes || 0}</span><span class="stat"><b style="color:#795548">字数</b>${info.WordCount || 0}</span>
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}`);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-fimtale-api",
3
3
  "description": "Koishi插件,从fimtale搜索/订阅/随机获取小说/解析链接等",
4
- "version": "1.0.1",
4
+ "version": "1.0.3",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [