11// ==UserScript==
22// @name ComicRead
33// @namespace ComicRead
4- // @version 10.10.2
5- // @description 为漫画站增加双页阅读、翻译等优化体验的增强功能。百合会(记录阅读历史、自动签到等)、百合会新站、动漫之家(解锁隐藏漫画)、E-Hentai(关联 nhentai、快捷收藏、标签染色、识别广告页等)、nhentai(彻底屏蔽漫画、无限滚动)、Yurifans(自动签到)、拷贝漫画(copymanga)(显示最后阅读记录、解锁隐藏漫画)、PonpomuYuri、再漫画、明日方舟泰拉记事社、禁漫天堂、漫画柜(manhuagui)、漫画DB(manhuadb)、动漫屋(dm5)、绅士漫画(wnacg)、mangabz、komiic、MangaDex、無限動漫、新新漫画、熱辣漫畫、hitomi、SchaleNetwork、kemono、nekohouse、welovemanga
4+ // @version 10.11.0
5+ // @description 为漫画站增加双页阅读、翻译等优化体验的增强功能。百合会(记录阅读历史、自动签到等)、百合会新站、动漫之家(解锁隐藏漫画)、E-Hentai(关联 nhentai、快捷收藏、标签染色、识别广告页等)、nhentai(彻底屏蔽漫画、无限滚动)、Yurifans(自动签到)、拷贝漫画(copymanga)(显示最后阅读记录、解锁隐藏漫画)、PonpomuYuri、再漫画、明日方舟泰拉记事社、禁漫天堂、漫画柜(manhuagui)、漫画DB(manhuadb)、动漫屋(dm5)、绅士漫画(wnacg)、mangabz、komiic、MangaDex、NoyAcg、 無限動漫、新新漫画、熱辣漫畫、hitomi、SchaleNetwork、kemono、nekohouse、welovemanga
66// @description:en Add enhanced features to the comic site for optimized experience, including dual-page reading and translation. E-Hentai (Associate nhentai, Quick favorite, Colorize tags, Floating tag list, etc.) | nhentai (Totally block comics, Auto page turning) | hitomi | Anchira | kemono | nekohouse | welovemanga.
77// @description:ru Добавляет расширенные функции для удобства на сайт, такие как двухстраничный режим и перевод.
88// @author hymbz
5959// @match *://mangabz.com/*
6060// @match *://komiic.com/*
6161// @match *://mangadex.org/*
62+ // @match *://noy1.top/*
6263// @match *://8.twobili.com/*
6364// @match *://a.twobili.com/*
6465// @match *://articles.onemoreplace.tw/*
@@ -1177,14 +1178,18 @@ const request = async (url, details, retryNum = 0, errorNum = 0) => {
11771178 };
11781179 }
11791180 if (typeof GM_xmlhttpRequest === 'undefined') throw new Error(helper.t('pwa.alert.userscript_not_installed'));
1181+ let targetUrl = url;
1182+ // https://github.com/hymbz/ComicReadScript/issues/195
1183+ // 在某些情况下 Tampermonkey 无法正确处理相对协议的 url
1184+ // 实际 finalUrl 会变成 \`///xxx.xxx\` 莫名多了一个斜杠
1185+ // 然而在修改代码发出正确的请求后,就再也无法复现了
1186+ // 不过以防万一还是在这里手动处理下
1187+ if (url.startsWith('//')) targetUrl = \`http:\${url}\`;
1188+ // stay 没法处理相对路径,也得转换一下
1189+ else if (url.startsWith('/')) targetUrl = \`\${window.location.origin}\${url}\`;
11801190 const res = await xmlHttpRequest({
11811191 method: 'GET',
1182- // https://github.com/hymbz/ComicReadScript/issues/195
1183- // 在某些情况下 Tampermonkey 无法正确处理相对协议的 url
1184- // 实际 finalUrl 会变成 \`///xxx.xxx\` 莫名多了一个斜杠
1185- // 然而在修改代码发出正确的请求后,就再也无法复现了
1186- // 不过以防万一还是在这里手动处理下
1187- url: url.startsWith('//') ? \`http:\${url}\` : url,
1192+ url: targetUrl,
11881193 headers,
11891194 timeout: 1000 * 10,
11901195 ...details
@@ -1193,6 +1198,13 @@ const request = async (url, details, retryNum = 0, errorNum = 0) => {
11931198 helper.log.error(errorText, res);
11941199 throw new Error(errorText);
11951200 }
1201+
1202+ // stay 好像没有正确处理 json,只能再单独判断处理一下
1203+ if (details?.responseType === 'json' && (typeof res.response !== 'object' || Object.keys(res.response).length === 0)) {
1204+ try {
1205+ Reflect.set(res, 'response', JSON.parse(res.responseText));
1206+ } catch {}
1207+ }
11961208 return res;
11971209 } catch (error) {
11981210 if (errorNum >= retryNum) {
@@ -1944,6 +1956,12 @@ const handleDragAnima$1 = () => {
19441956 animationId$2 = requestAnimationFrame(handleDragAnima$1);
19451957};
19461958
1959+ /** 一段时间没有移动后应该将速率归零 */
1960+ const resetVelocity = helper.debounce(() => {
1961+ velocity.x = 0;
1962+ velocity.y = 0;
1963+ }, 200);
1964+
19471965/** 是否正在双指捏合缩放中 */
19481966let pinchZoom = false;
19491967
@@ -1968,10 +1986,13 @@ const handleZoomDrag = ({
19681986 mouse.x += x - lx;
19691987 mouse.y += y - ly;
19701988 if (animationId$2 === null) animationId$2 = requestAnimationFrame(handleDragAnima$1);
1989+ resetVelocity();
19711990 break;
19721991 }
19731992 case 'up':
19741993 {
1994+ resetVelocity.clear();
1995+
19751996 // 当双指捏合结束,一个手指抬起时,将剩余的指针当作刚点击来处理
19761997 if (pinchZoom) {
19771998 pinchZoom = false;
@@ -3987,6 +4008,7 @@ const ComicImgFlow = () => {
39874008 return (() => {
39884009 var _el$ = web.template(\`<div tabindex=-1><div tabindex=-1>\`)(),
39894010 _el$2 = _el$.firstChild;
4011+ _el$.addEventListener("transitionend", handleTransitionEnd);
39904012 var _ref$ = bindRef('mangaBox');
39914013 typeof _ref$ === "function" && web.use(_ref$, _el$);
39924014 _el$2.addEventListener("transitionend", handleTransitionEnd);
@@ -8666,7 +8688,7 @@ const handleVersionUpdate = async () => {
86668688 _el$.firstChild;
86678689 web.insert(_el$, () => GM.info.script.version, null);
86688690 return _el$;
8669- })(), web.template(\`<h3>修复\`)(), web.template(\`<ul><li>修复部分手机浏览器在禁漫天堂等网站上无法正常显示图片的 bug\`)(), web.createComponent(VersionTip, {
8691+ })(), web.template(\`<h3>新增\`)(), web.template(\`<ul><li>支持 NoyAcg\`)(), web.template(\`<h3> 修复\`)(), web.template(\`<ul><li><p>修复放大后拖拽不跟手的 bug </p></li><li><p>修复拷贝漫画解锁隐藏漫画不支持移动端的 bug\`)(), web.createComponent(VersionTip, {
86708692 v1: version,
86718693 v2: '10.8.0',
86728694 get children() {
@@ -11843,7 +11865,7 @@ const handleLastChapter = comicName => {
1184311865};
1184411866
1184511867// 生成目录
11846- const buildChapters = async (comicName, rootDom ) => {
11868+ const buildChapters = async (comicName, isMobile ) => {
1184711869 // 拷贝有些漫画虽然可以通过 api 获取到数据,但网页上的目录被隐藏了
1184811870 // 举例:https://mangacopy.com/comic/yueguangxiadeyishijiezhilv
1184911871
@@ -11854,7 +11876,8 @@ const buildChapters = async (comicName, rootDom) => {
1185411876 } = await main.request(`/comicdetail/${comicName}/chapters`, {
1185511877 responseType: 'json',
1185611878 errorText: '加載漫畫目錄失敗',
11857- headers
11879+ headers,
11880+ fetch: false
1185811881 });
1185911882 // 解码 api 返回的数据
1186011883 const decryptData = async (cipher, key, iv) => {
@@ -11879,89 +11902,154 @@ const buildChapters = async (comicName, rootDom) => {
1187911902 id
1188011903 }) => [id, []]));
1188111904 for (const chapter of props.chapters) chapters[chapter.type].push(chapter);
11905+ if (isMobile) {
11906+ // 删掉占位置的分隔线
11907+ for (const dom of helper.querySelectorAll('.van-divider')) dom.remove();
11908+ return (() => {
11909+ var _el$ = web.template(`<div class="detailsTextContentTabs van-tabs van-tabs--line">`)();
11910+ web.insert(_el$, web.createComponent(solidJs.For, {
11911+ each: type,
11912+ children: ({
11913+ id,
11914+ name
11915+ }) => web.createComponent(solidJs.Show, {
11916+ get when() {
11917+ return chapters[id].length;
11918+ },
11919+ get children() {
11920+ return [(() => {
11921+ var _el$2 = web.template(`<div class=van-tabs__wrap><div role=tablist class="van-tabs__nav van-tabs__nav--line"><div role=tab class="van-tab van-tab--active"><span class="van-tab__text van-tab__text--ellipsis"><span></span></span></div><div class=van-tabs__line>`)(),
11922+ _el$3 = _el$2.firstChild,
11923+ _el$4 = _el$3.firstChild,
11924+ _el$5 = _el$4.firstChild,
11925+ _el$6 = _el$5.firstChild,
11926+ _el$7 = _el$4.nextSibling;
11927+ _el$3.style.setProperty("background", "transparent");
11928+ web.insert(_el$6, name);
11929+ _el$7.style.setProperty("width", "0.24rem");
11930+ _el$7.style.setProperty("transform", "translateX(187.5px) translateX(-50%)");
11931+ _el$7.style.setProperty("transition-duration", "0.3s");
11932+ return _el$2;
11933+ })(), (() => {
11934+ var _el$8 = web.template(`<div class=van-tab__pane><div class="chapterList van-grid">`)(),
11935+ _el$9 = _el$8.firstChild;
11936+ _el$9.style.setProperty("padding-left", "0.24rem");
11937+ web.insert(_el$9, web.createComponent(solidJs.For, {
11938+ get each() {
11939+ return chapters[id];
11940+ },
11941+ children: chapter => (() => {
11942+ var _el$10 = web.template(`<div class="chapterItem oneLines van-grid-item"><a class="van-grid-item__content van-grid-item__content--center"><span class=van-grid-item__text>`)(),
11943+ _el$11 = _el$10.firstChild,
11944+ _el$12 = _el$11.firstChild;
11945+ _el$10.style.setProperty("flex-basis", "25%");
11946+ _el$10.style.setProperty("padding-right", "0.24rem");
11947+ _el$10.style.setProperty("margin-top", "0.24rem");
11948+ web.insert(_el$12, () => chapter.name);
11949+ web.effect(_p$ => {
11950+ var _v$ = !!(props.last_chapter.uuid === chapter.id),
11951+ _v$2 = `/comic/${comicName}/chapter/${chapter.id}`;
11952+ _v$ !== _p$.e && _el$10.classList.toggle("red", _p$.e = _v$);
11953+ _v$2 !== _p$.t && web.setAttribute(_el$11, "href", _p$.t = _v$2);
11954+ return _p$;
11955+ }, {
11956+ e: undefined,
11957+ t: undefined
11958+ });
11959+ return _el$10;
11960+ })()
11961+ }));
11962+ return _el$8;
11963+ })()];
11964+ }
11965+ })
11966+ }));
11967+ return _el$;
11968+ })();
11969+ }
1188211970 return [(() => {
11883- var _el$ = web.template(`<span>`)();
11884- web.insert(_el$, () => props.name);
11885- return _el$;
11971+ var _el$13 = web.template(`<span>`)();
11972+ web.insert(_el$13 , () => props.name);
11973+ return _el$13 ;
1188611974 })(), (() => {
11887- var _el$2 = web.template(`<div class=table-default><div class=table-default-title><ul class="nav nav-tabs"role=tablist></ul><div class=table-default-right><span>更新內容:</span><a target=_blank></a><span>更新時間:</span><span></span></div></div><div class=table-default-box><div class=tab-content>`)(),
11888- _el$3 = _el$2 .firstChild,
11889- _el$4 = _el$3 .firstChild,
11890- _el$5 = _el$4 .nextSibling,
11891- _el$6 = _el$5 .firstChild,
11892- _el$7 = _el$6 .nextSibling,
11893- _el$8 = _el$7 .nextSibling,
11894- _el$9 = _el$8 .nextSibling,
11895- _el$10 = _el$3 .nextSibling,
11896- _el$11 = _el$10 .firstChild;
11897- web.insert(_el$4 , web.createComponent(solidJs.For, {
11975+ var _el$14 = web.template(`<div class=table-default><div class=table-default-title><ul class="nav nav-tabs"role=tablist></ul><div class=table-default-right><span>更新內容:</span><a target=_blank></a><span>更新時間:</span><span></span></div></div><div class=table-default-box><div class=tab-content>`)(),
11976+ _el$15 = _el$14 .firstChild,
11977+ _el$16 = _el$15 .firstChild,
11978+ _el$17 = _el$16 .nextSibling,
11979+ _el$18 = _el$17 .firstChild,
11980+ _el$19 = _el$18 .nextSibling,
11981+ _el$20 = _el$19 .nextSibling,
11982+ _el$21 = _el$20 .nextSibling,
11983+ _el$22 = _el$15 .nextSibling,
11984+ _el$23 = _el$22 .firstChild;
11985+ web.insert(_el$16 , web.createComponent(solidJs.For, {
1189811986 each: type,
1189911987 children: ({
1190011988 id,
1190111989 name
1190211990 }) => (() => {
11903- var _el$12 = web.template(`<li class=nav-item><a class=nav-link data-toggle=tab role=tab aria-selected=false>`)(),
11904- _el$13 = _el$12 .firstChild;
11905- web.insert(_el$13 , name);
11991+ var _el$24 = web.template(`<li class=nav-item><a class=nav-link data-toggle=tab role=tab aria-selected=false>`)(),
11992+ _el$25 = _el$24 .firstChild;
11993+ web.insert(_el$25 , name);
1190611994 web.effect(_p$ => {
11907- var _v$ = !!(chapters[id].length === 0),
11908- _v$2 = `#${props.path_word}${name}`;
11909- _v$ !== _p$.e && _el$13 .classList.toggle("disabled", _p$.e = _v$);
11910- _v$2 !== _p$.t && web.setAttribute(_el$13 , "href", _p$.t = _v$2 );
11995+ var _v$3 = !!(chapters[id].length === 0),
11996+ _v$4 = `#${props.path_word}${name}`;
11997+ _v$3 !== _p$.e && _el$25 .classList.toggle("disabled", _p$.e = _v$3 );
11998+ _v$4 !== _p$.t && web.setAttribute(_el$25 , "href", _p$.t = _v$4 );
1191111999 return _p$;
1191212000 }, {
1191312001 e: undefined,
1191412002 t: undefined
1191512003 });
11916- return _el$12 ;
12004+ return _el$24 ;
1191712005 })()
1191812006 }));
11919- web.insert(_el$7 , () => props.last_chapter.name);
11920- web.insert(_el$9 , () => props.last_chapter.datetime_created);
11921- web.insert(_el$11 , web.createComponent(solidJs.For, {
12007+ web.insert(_el$19 , () => props.last_chapter.name);
12008+ web.insert(_el$21 , () => props.last_chapter.datetime_created);
12009+ web.insert(_el$23 , web.createComponent(solidJs.For, {
1192212010 each: type,
1192312011 children: ({
1192412012 id,
1192512013 name
1192612014 }) => (() => {
11927- var _el$14 = web.template(`<div role=tabpanel class="tab-pane fade"><ul>`)(),
11928- _el$15 = _el$14 .firstChild;
11929- web.insert(_el$15 , web.createComponent(solidJs.For, {
12015+ var _el$26 = web.template(`<div role=tabpanel class="tab-pane fade"><ul>`)(),
12016+ _el$27 = _el$26 .firstChild;
12017+ web.insert(_el$27 , web.createComponent(solidJs.For, {
1193012018 get each() {
1193112019 return chapters[id];
1193212020 },
1193312021 children: chapter => (() => {
11934- var _el$16 = web.template(`<a target=_blank><li>`)(),
11935- _el$17 = _el$16 .firstChild;
11936- _el$16 .style.setProperty("display", "block");
11937- web.insert(_el$17 , () => chapter.name);
12022+ var _el$28 = web.template(`<a target=_blank><li>`)(),
12023+ _el$29 = _el$28 .firstChild;
12024+ _el$28 .style.setProperty("display", "block");
12025+ web.insert(_el$29 , () => chapter.name);
1193812026 web.effect(_p$ => {
11939- var _v$3 = `/comic/${comicName}/chapter/${chapter.id}`,
11940- _v$4 = chapter.name;
11941- _v$3 !== _p$.e && web.setAttribute(_el$16 , "href", _p$.e = _v$3 );
11942- _v$4 !== _p$.t && web.setAttribute(_el$16 , "title", _p$.t = _v$4 );
12027+ var _v$5 = `/comic/${comicName}/chapter/${chapter.id}`,
12028+ _v$6 = chapter.name;
12029+ _v$5 !== _p$.e && web.setAttribute(_el$28 , "href", _p$.e = _v$5 );
12030+ _v$6 !== _p$.t && web.setAttribute(_el$28 , "title", _p$.t = _v$6 );
1194312031 return _p$;
1194412032 }, {
1194512033 e: undefined,
1194612034 t: undefined
1194712035 });
11948- return _el$16 ;
12036+ return _el$28 ;
1194912037 })()
1195012038 }));
11951- web.effect(() => web.setAttribute(_el$14 , "id", `${props.path_word}${name}`));
11952- return _el$14 ;
12039+ web.effect(() => web.setAttribute(_el$26 , "id", `${props.path_word}${name}`));
12040+ return _el$26 ;
1195312041 })()
1195412042 }));
11955- web.effect(() => web.setAttribute(_el$7 , "href", `/comic/${comicName}/chapter/${props.last_chapter.comic_id}`));
11956- return _el$2 ;
12043+ web.effect(() => web.setAttribute(_el$19 , "href", `/comic/${comicName}/chapter/${props.last_chapter.comic_id}`));
12044+ return _el$14 ;
1195712045 })()];
1195812046 };
1195912047 web.render(() => web.createComponent(solidJs.For, {
1196012048 get each() {
1196112049 return Object.values(groups);
1196212050 },
1196312051 children: Group
11964- }), rootDom );
12052+ }), isMobile ? helper.querySelector('.detailsTextContent') : helper.querySelector('.upLoop') );
1196512053
1196612054 // 点击每个分组下第一个激活的标签
1196712055 for (const group of helper.querySelectorAll('.upLoop .table-default-title')) group.querySelector('.nav-link:not(.disabled)')?.click();
@@ -12037,10 +12125,31 @@ const buildChapters = async (comicName, rootDom) => {
1203712125 if (!id && window.location.href.includes('/comic/')) {
1203812126 comicName = window.location.href.split('/comic/')[1];
1203912127 if (!comicName) return;
12128+ let isHidden = false;
12129+ const isMobile = window.location.href.includes('/h5/');
12130+ if (isMobile) {
12131+ // 等到加载提示框消失
12132+ await helper.wait(() => helper.querySelector('.van-toast__text')?.parentElement?.style.display === 'none');
12133+ // 再等一秒看有没有屏蔽提示
12134+ if (await helper.wait(() => helper.querySelector('.isBan')?.textContent?.includes('不提供閱覽'), 1000)) {
12135+ isHidden = true;
12136+ }
12137+ } else {
12138+ isHidden =
12139+ // 先检查有没有屏蔽提示
12140+ Boolean(helper.querySelector('.wargin')?.textContent?.includes('不提供閱覽')) ||
12141+ // 再等一秒看目录有没有加载出来
12142+ !(await helper.wait(() => helper.querySelector('.upLoop .table-default-title'), 1000));
12143+ }
1204012144
12041- // 稍等看有没有目录加载出来,没有就自己生成
12042- if (!(await helper.wait(() => helper.querySelector('.upLoop .table-default-title'), 1000))) await buildChapters(comicName, helper.querySelector('.upLoop'));
12043- if (token) handleLastChapter(comicName);
12145+ // 如果漫画被隐藏了,就自己生成目录
12146+ if (isHidden) {
12147+ // 给屏蔽提示加个删除线
12148+ const tip = helper.querySelector('.isBan, .wargin');
12149+ if (tip) tip.style.textDecoration = 'line-through';
12150+ await buildChapters(comicName, isMobile);
12151+ }
12152+ if (!isMobile && token) handleLastChapter(comicName);
1204412153 }
1204512154})();
1204612155
@@ -12106,13 +12215,7 @@ const buildChapters = async (comicName, rootDom) => {
1210612215 }
1210712216
1210812217 // #[禁漫天堂](https://18comic.vip)
12109- case 'jmcomic.me':
12110- case 'jmcomic1.me':
12111- case 'jmcomic-zzz.one':
12112- case '18comic-dwo.cc':
12113- case '18comic.org':
12114- case '18comic.vip':
12115- {
12218+ {
1211612219const main = require('main');
1211712220const helper = require('helper');
1211812221
@@ -12482,6 +12585,27 @@ const helper = require('helper');
1248212585 break;
1248312586 }
1248412587
12588+ // #[NoyAcg](https://noy1.top)
12589+ case 'noy1.top':
12590+ {
12591+ options = {
12592+ name: 'NoyAcg',
12593+ async getImgList() {
12594+ const [,, id] = window.location.hash.split('/');
12595+
12596+ // 随便拿一个图片来获取 cdn url
12597+ const img = await helper.wait(() => helper.querySelector('.lazy-load-image-background img'));
12598+ const cdn = img.src.split(id)[0];
12599+ const imgNum = await helper.wait(() => helper.querySelectorAll('.lazy-load-image-background').length);
12600+ return helper.range(imgNum, i => `${cdn}${id}/${i + 1}.webp`);
12601+ },
12602+ SPA: {
12603+ isMangaPage: () => window.location.hash.startsWith('#/read/')
12604+ }
12605+ };
12606+ break;
12607+ }
12608+
1248512609 // #[無限動漫](https://www.comicabc.com)
1248612610 case '8.twobili.com':
1248712611 case 'a.twobili.com':
0 commit comments