From c5f8daeb2cf08a76c3bfb4951af1b295f45990c1 Mon Sep 17 00:00:00 2001 From: hymbz Date: Thu, 14 Dec 2023 20:02:58 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20:bug:=20=E4=BF=AE=E5=A4=8D=E5=9C=A8=20eh?= =?UTF-8?q?entai=20=E4=B8=8A=E8=B6=85=E8=BF=87=E4=B8=80=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E5=90=8E=E5=B0=B1=E6=97=A0=E6=B3=95=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E7=9A=84=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/helper/index.ts | 2 +- src/helper/useInit.tsx | 9 +++-- src/site/ehentai.tsx | 91 +++++++++++++++++++++++++++++++++--------- 3 files changed, 80 insertions(+), 22 deletions(-) diff --git a/src/helper/index.ts b/src/helper/index.ts index 7d01051d..eccc31f2 100644 --- a/src/helper/index.ts +++ b/src/helper/index.ts @@ -171,7 +171,7 @@ export const singleThreaded = ( * @returns 所有 Promise 的返回值 */ export const plimit = async ( - fnList: Array<() => Promise>, + fnList: Array<() => Promise | T>, callBack = undefined as | ((doneNum: number, totalNum: number, resList: T[], i: number) => void) | undefined, diff --git a/src/helper/useInit.tsx b/src/helper/useInit.tsx index 43b8e72a..a591f0f0 100644 --- a/src/helper/useInit.tsx +++ b/src/helper/useInit.tsx @@ -177,10 +177,13 @@ export const useInit = async >( totalImgNum: number, ) => { const updateImgList = async () => { - setManga({ onLoading: undefined }); - _setManga('imgList', Array(totalImgNum).fill('')); + const _onLoading = mangaProps.onLoading; + setManga({ + onLoading: undefined, + imgList: Array(totalImgNum).fill(''), + }); await work((i, imgUrl) => _setManga('imgList', i, imgUrl)); - setManga({ onLoading }); + setManga({ onLoading: _onLoading }); }; return async () => { diff --git a/src/site/ehentai.tsx b/src/site/ehentai.tsx index df87be2b..c8a99346 100644 --- a/src/site/ehentai.tsx +++ b/src/site/ehentai.tsx @@ -13,21 +13,29 @@ import { querySelectorAll, wait, log, + testImgUrl, + singleThreaded, + store, } from 'main'; declare const selected_tagname: string; (async () => { - const { options, setFab, setManga, init, dynamicUpdate } = await useInit( - 'ehentai', - { - /** 关联 nhentai */ - associate_nhentai: true, - /** 快捷键翻页 */ - hotkeys_page_turn: true, - autoShow: false, - }, - ); + const { + options, + init, + setFab, + setManga, + _setManga, + dynamicUpdate, + onLoading, + } = await useInit('ehentai', { + /** 关联 nhentai */ + associate_nhentai: true, + /** 快捷键翻页 */ + hotkeys_page_turn: true, + autoShow: false, + }); if (Reflect.has(unsafeWindow, 'mpvkey')) { const imgEleList = querySelectorAll('.mi0[id]'); @@ -81,13 +89,6 @@ declare const selected_tagname: string; return; } - setManga({ - onExit: (isEnd) => { - if (isEnd) scrollIntoView('#cdiv'); - setManga({ show: false }); - }, - }); - // 虽然有 Fab 了不需要这个按钮,但都点习惯了没有还挺别扭的( insertNode( document.getElementById('gd5')!, @@ -95,6 +96,8 @@ declare const selected_tagname: string; ); const comicReadModeDom = document.getElementById('comicReadMode')!; + const getImgFromImgPageRe = /id="img" src="(.+?)"/; + /** 从图片页获取图片地址 */ const getImgFromImgPage = async (url: string): Promise => { const res = await request(url, { @@ -102,7 +105,7 @@ declare const selected_tagname: string; }); try { - return res.responseText.split('id="img" src="')[1].split('"')[0]; + return res.responseText.match(getImgFromImgPageRe)![1]; } catch (error) { throw new Error(t('site.ehentai.fetch_img_url_failed')); } @@ -154,6 +157,7 @@ declare const selected_tagname: string; const totalImgNum = await getImgNum(); const ehImgList: string[] = []; + const ehImgPageList: string[] = []; const { loadImgList } = init( dynamicUpdate(async (setImg) => { @@ -169,6 +173,7 @@ declare const selected_tagname: string; const imgUrl = await getImgFromImgPage(imgPageUrl); const index = startIndex + i; ehImgList[index] = imgUrl; + ehImgPageList[index] = imgPageUrl; setImg(index, imgUrl); }), (_doneNum) => { @@ -187,6 +192,56 @@ declare const selected_tagname: string; }, totalImgNum), ); + /** 获取新的图片页地址 */ + const getNewImgPageUrl = async (url: string) => { + const res = await request(url, { + errorText: t('site.ehentai.fetch_img_page_source_failed'), + }); + + const nl = res.responseText.match(/nl\('(.+?)'\)/)?.[1]; + if (!nl) throw new Error(t('site.ehentai.fetch_img_url_failed')); + const newUrl = new URL(url); + newUrl.searchParams.set('nl', nl); + return newUrl.href; + }; + + /** 刷新指定图片 */ + const reloadImg = async (i: number) => { + const pageUrl = await getNewImgPageUrl(ehImgPageList[i]); + let imgUrl = ''; + while (!imgUrl || !(await testImgUrl(imgUrl))) + imgUrl = await getImgFromImgPage(pageUrl); + ehImgList[i] = imgUrl; + ehImgPageList[i] = pageUrl; + _setManga('imgList', i, imgUrl); + }; + + /** 判断当前显示的是否是 eh 源 */ + const isShowEh = () => store.imgList[0]?.src === ehImgList[0]; + + /** 刷新所有错误图片 */ + const reloadErrorImg = singleThreaded(() => + plimit( + store.imgList.map(({ loadType }, i) => () => { + if (loadType !== 'error' || !isShowEh()) return; + return reloadImg(i); + }), + ), + ); + + setManga({ + onExit: (isEnd) => { + if (isEnd) scrollIntoView('#cdiv'); + setManga({ show: false }); + }, + // 在图片加载出错时刷新图片 + onLoading: async (imgList, img) => { + onLoading(imgList); + if (img?.loadType !== 'error' || (await testImgUrl(img.src))) return; + return reloadErrorImg(); + }, + }); + setFab({ initialShow: options.autoShow }); comicReadModeDom.addEventListener('click', () => loadImgList(ehImgList.length ? ehImgList : undefined, true),