DM5動漫屋_閱讀輔助

DM5動漫屋_瀑布流閱讀連續載入圖片(自用)。

  1. // ==UserScript==
  2. // @name DM5動漫屋_閱讀輔助
  3. // @name:en DM5 Read Helpr
  4. // @name:zh-CN DM5动漫屋_阅读辅助
  5. // @name:zh-TW DM5動漫屋_閱讀輔助
  6. // @version 1.0.5
  7. // @description DM5動漫屋_瀑布流閱讀連續載入圖片(自用)。
  8. // @description:en DM5 read infinite scroll
  9. // @description:zh-CN DM5动漫屋_瀑布流阅读连续载入图片(自用)。
  10. // @description:zh-TW DM5動漫屋_瀑布流閱讀連續載入圖片(自用)。
  11. // @author tony0809
  12. // @match *://*.dm5.com/m*/
  13. // @match *://*.dm5.cn/m*/
  14. // @match *://*.1kkk.com/ch*/
  15. // @match *://*.1kkk.com/vol*/
  16. // @match *://*.1kkk.com/other*/
  17. // @icon https://www.google.com/s2/favicons?sz=64&domain=dm5.com
  18. // @grant none
  19. // @license MIT
  20. // @namespace https://greasyfork.org/users/20361
  21. // ==/UserScript==
  22.  
  23. (() => {
  24. 'use strict';
  25. const options = { //true 開啟,false 關閉
  26. aH: true, //載入下一話時添加瀏覽器歷史紀錄。
  27. pln: true, //PC條漫版和手機版預讀下一頁的圖片,減少等待加載圖片的時間。
  28. remove: [true, 3] //!!!不能小於2!!!閱讀載入超過n話時刪除前面話數的圖片。
  29. };
  30. const ge = (selector, doc) => (doc || document).querySelector(selector);
  31. const gae = (selector, doc) => (doc || document).querySelectorAll(selector);
  32. const gx = (xpath, doc) => (doc || document).evaluate(xpath, (doc || document), null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  33. const gax = (xpath, doc) => {
  34. let nodes = [];
  35. let results = (doc || document).evaluate(xpath, (doc || document), null, XPathResult.ANY_TYPE, null);
  36. let node;
  37. while (node = results.iterateNext()) {
  38. nodes.push(node);
  39. }
  40. return nodes;
  41. };
  42. const pcMode1 = () => {
  43. if (ge('#chapterpager')) {
  44. return true;
  45. }
  46. return false;
  47. };
  48. const pcMode2 = () => {
  49. if (ge('#barChapter')) {
  50. return true;
  51. }
  52. return false;
  53. };
  54. const mobile = () => {
  55. if (gx("//script[contains(text(),'newImgs')]")) {
  56. return true;
  57. }
  58. return false;
  59. };
  60. const runCode = code => new Function('return ' + code)();
  61. const addGlobalStyle = css => {
  62. let style = document.createElement('style');
  63. style.type = 'text/css';
  64. style.innerHTML = css;
  65. document.head.appendChild(style);
  66. };
  67. const pcCss = `
  68. a[href^='j'],
  69. .chapterpager,
  70. #lb-win {
  71. display: none !important;
  72. }
  73. .view-comment {
  74. display: none ;
  75. }
  76. .chapterTitle {
  77. width: auto;
  78. height: 30px;
  79. font-size: 24px;
  80. font-family: Arial,sans-serif!important;
  81. line-height: 32px;
  82. text-align: center;
  83. margin: 10px 5px;
  84. border: 1px solid #e0e0e0;
  85. background-color: #f0f0f0;
  86. background: -webkit-gradient(linear, 0 0, 0 100%, from(#f9f9f9), to(#f0f0f0));
  87. background: -moz-linear-gradient(top, #f9f9f9, #f0f0f0);
  88. box-shadow: 0 0 5px rgba(0, 0, 0, 0.6);
  89. border-radius: 5px;
  90. }
  91. `;
  92. const mCss = `
  93. .view-fix-bottom-bar-item[onclick],
  94. .guide {
  95. display: none !important;
  96. }
  97. .view-fix-bottom-bar-item {
  98. width: 25% !important;
  99. }
  100. .chapterTitle {
  101. width: auto;
  102. height: 30px;
  103. font-size: 20px;
  104. font-family: Arial,sans-serif!important;
  105. line-height: 32px;
  106. text-align: center;
  107. overflow: hidden;
  108. display: -webkit-box;
  109. -webkit-box-orient: vertical;
  110. -webkit-line-clamp: 1;
  111. margin: 10px 5px;
  112. border: 1px solid #e0e0e0;
  113. background-color: #f0f0f0;
  114. background: -webkit-gradient(linear, 0 0, 0 100%, from(#f9f9f9), to(#f0f0f0));
  115. background: -moz-linear-gradient(top, #f9f9f9, #f0f0f0);
  116. box-shadow: 0 0 5px rgba(0, 0, 0, 0.6);
  117. border-radius: 5px;
  118. }
  119. `;
  120. const css = `
  121. #cp_img>img,#barChapter>img {
  122. width: auto !important;
  123. height: auto !important;
  124. max-width: 100% !important;
  125. display: block !important;
  126. margin: 0 auto !important
  127. }
  128. .chapterLoading {
  129. font-size: 20px;
  130. height: 30px;
  131. line-height: 32px;
  132. text-align: center;
  133. }
  134. `;
  135. const showElement = () => {
  136. let ele = gx("//a[text()='下一章']");
  137. if (!ele) {
  138. ge('.view-comment').style.display = 'block';
  139. }
  140. };
  141. const editElement = () => {
  142. let end = gx("//ul[@class='view-bottom-bar']//a[contains(@href,'-end')]");
  143. if (end) {
  144. end.href = "/manhua-list-s2/";
  145. end.innerText = '最近更新';
  146. }
  147. };
  148. const addHistory = (title, url) => {
  149. history.pushState(null, title, url);
  150. document.title = title;
  151. };
  152. const addLoad = () => {
  153. let cl = document.createElement('div');
  154. cl.className = 'chapterLoading';
  155. cl.innerText = 'Loading...';
  156. ge('#cp_img,#barChapter').appendChild(cl);
  157. };
  158. const removeLoad = () => {
  159. ge('.chapterLoading').remove();
  160. };
  161. const addTitle = title => {
  162. let t = document.createElement('div');
  163. t.className = 'chapterTitle';
  164. t.innerText = title;
  165. let load = ge('.chapterLoading');
  166. load.parentNode.insertBefore(t, load);
  167. };
  168. const parseHTML = str => {
  169. var doc = null;
  170. try {
  171. doc = new DOMParser().parseFromString(str, 'text/html');
  172. } catch (e) {}
  173. if (!doc) {
  174. doc = document.implementation.createHTMLDocument('');
  175. doc.documentElement.innerHTML = str;
  176. }
  177. return doc;
  178. };
  179. const mobilePreloadNext = () => {
  180. let next = gx("//ul[@class='view-bottom-bar']//a[text()='下一章' and not(contains(@href,'-end'))]");
  181. if (next) {
  182. let url = location.origin + next.href.split("'")[1];
  183. fetch(url).then(res => res.text()).then(res => {
  184. let doc = parseHTML(res);
  185. let title = gx("//title", doc).innerText.match(/_([^_]+)/)[1].replace(/,$/, '');
  186. let imgs = runCode(runCode(Array.from(doc.scripts).find(s => s.innerHTML.search(/newImgs/) > -1).innerHTML.trim().slice(4)).replace('var', ''));
  187. let F = new DocumentFragment();
  188. imgs.forEach(e => {
  189. let temp = new Image();
  190. temp.src = e;
  191. F.appendChild(temp);
  192. temp.onload = () => {
  193. temp = null;
  194. };
  195. });
  196. console.log(`[${title}] 圖片預讀:\n`, F);
  197. doc = null;title = null;imgs = null;F = null;
  198. });
  199. }
  200. };
  201. const pcMode2PreloadNext = () => {
  202. let next = gx("//a[text()='下一章']");
  203. if (next) {
  204. fetch(next.href).then(res => res.text()).then(res => {
  205. let doc = parseHTML(res);
  206. let title = gx("//span[@class='active right-arrow']", doc).innerText.trim();
  207. let imgs = gae('#barChapter>img[data-src]', doc);
  208. let F = new DocumentFragment();
  209. imgs.forEach(e => {
  210. let temp = new Image();
  211. temp.src = e.dataset.src;
  212. F.appendChild(temp);
  213. temp.onload = () => {
  214. temp = null;
  215. };
  216. });
  217. console.log(`[${title}] 圖片預讀:\n`, F);
  218. doc = null;title = null;imgs = null;F = null;
  219. });
  220. }
  221. };
  222. const fetchData = url => {
  223. fetch(url).then(res => res.text()).then(res => {
  224. let doc = parseHTML(res);
  225. if (options.aH) {
  226. addHistory(doc.title, url);
  227. }
  228. if (!pcMode2()) {
  229. var code = null;
  230. if (mobile()) {
  231. code = runCode(Array.from(doc.scripts).find(s => s.innerHTML.search(/newImgs/) > -1).innerHTML.trim().slice(4));
  232. } else {
  233. code = Array.from(doc.scripts).find(s => s.innerHTML.search(/DM5_IMAGE_COUNT/) > -1).innerHTML;
  234. }
  235. let script = document.createElement('script');
  236. script.type = 'text/javascript';
  237. script.innerHTML = code;
  238. ge('#cp_img').appendChild(script);
  239. }
  240. insertData(doc);
  241. }).catch(error => {
  242. console.error(error);
  243. ge('.chapterLoading').innerText = '连接出错,可能下一章为付费章节。';
  244. });
  245. };
  246. const insertData = doc => {
  247. const pcData = async () => {
  248. if (!mkey) {
  249. var mkey = '';
  250. }
  251. for (let i = 1; i <= DM5_IMAGE_COUNT; i++) {
  252. let apiUrl = location.origin + DM5_CURL + 'chapterfun.ashx' + `?cid=${DM5_CID}&page=${i}&key=${mkey}&language=1>k=6&_cid=${DM5_CID}&_mid=${DM5_MID}&_dt=${DM5_VIEWSIGN_DT}&_sign=${DM5_VIEWSIGN}`;
  253. let res = await fetch(apiUrl);
  254. let resText = await res.text();
  255. let imgSrc = await runCode(resText)[0];
  256. let img = new Image();
  257. img.src = imgSrc;
  258. ge('#cp_img').appendChild(img);
  259. }
  260. addNextObserver();
  261. };
  262. const pc2Data = () => {
  263. let F = new DocumentFragment();
  264. let imgs = gae('#barChapter>img[data-src]', doc);
  265. imgs.forEach(e => {
  266. let img = new Image();
  267. img.src = e.dataset.src;
  268. F.appendChild(img);
  269. });
  270. ge('#barChapter').appendChild(F);
  271. imgs = null;
  272. setTimeout(() => {
  273. addNextObserver();
  274. }, 1700);
  275. };
  276. const mobileData = () => {
  277. let F = new DocumentFragment();
  278. newImgs.forEach(e => {
  279. let img = new Image();
  280. img.src = e;
  281. F.appendChild(img);
  282. });
  283. ge('#cp_img').appendChild(F);
  284. setTimeout(() => {
  285. addNextObserver();
  286. }, 1300);
  287. };
  288. let load = ge('.chapterLoading');
  289. if (load) {
  290. let title;
  291. if (mobile()) {
  292. ["//div[@class='view-fix-top-bar']", "//ul[@class='view-bottom-bar']", "//div[@class='view-fix-bottom-bar']"].forEach(e => {
  293. gx(e).outerHTML = gx(e, doc).outerHTML;
  294. });
  295. title = gx("//title", doc).innerText.match(/_([^_]+)/)[1].replace(/,$/, '');
  296. } else {
  297. ["//div[@class='view-paging']", "//div[@class='rightToolBar']"].forEach(x => {
  298. gax(x).forEach(e => {
  299. e.outerHTML = gx(x, doc).outerHTML;
  300. });
  301. });
  302. title = gx("//span[@class='active right-arrow']", doc).innerText.trim();
  303. }
  304. addTitle(title);
  305. if (options.remove[0] && options.remove[1] > 1) {
  306. removeOldChapter();
  307. }
  308. setTimeout(() => {
  309. removeLoad();
  310. if (mobile()) {
  311. mobileData();
  312. editElement();
  313. } else if (pcMode1()) {
  314. pcData();
  315. showElement();
  316. } else {
  317. pc2Data();
  318. showElement();
  319. }
  320. }, 300);
  321. } else {
  322. if (mobile()) {
  323. ge('#cp_img').innerHTML = '';
  324. mobileData();
  325. editElement();
  326. } else if (pcMode1()) {
  327. document.body.style.overflow = 'scroll';
  328. ge('#cp_img').innerHTML = '';
  329. pcData();
  330. showElement();
  331. } else {
  332. gae('#barChapter>img[data-src]').forEach(e => {
  333. e.outerHTML = `<img src="${e.dataset.src}">`;
  334. });
  335. setTimeout(() => {
  336. addNextObserver();
  337. }, 1300);
  338. showElement();
  339. }
  340. }
  341. };
  342. const nextObserver = new IntersectionObserver((entries, observer) => {
  343. entries.forEach(entry => {
  344. if (entry.isIntersecting) {
  345. observer.unobserve(entry.target);
  346. let url, next;
  347. if (mobile()) {
  348. next = gx("//ul[@class='view-bottom-bar']//a[text()='下一章' and not(contains(@href,'-end'))]");
  349. if (next) url = location.origin + next.href.split("'")[1];
  350. } else {
  351. next = gx("//a[text()='下一章']");
  352. if (next) url = next.href;
  353. }
  354. if (next) {
  355. console.log(`觸發載入下一話\n${url}`);
  356. addLoad();
  357. fetchData(url);
  358. }
  359. }
  360. });
  361. });
  362. const addNextObserver = () => {
  363. let lastImg = [...gae('#cp_img img,#barChapter img')].pop();
  364. let loadImg = new Image();
  365. loadImg.src = lastImg.src;
  366. loadImg.onload = () => {
  367. nextObserver.observe(lastImg);
  368. if (mobile() && options.pln) {
  369. mobilePreloadNext();
  370. } else if (pcMode2() && options.pln) {
  371. pcMode2PreloadNext();
  372. }
  373. loadImg = null;
  374. };
  375. loadImg.onerror = () => {
  376. nextObserver.observe(lastImg);
  377. };
  378. };
  379. const removeOldChapter = () => {
  380. let titles = gae('.chapterTitle');
  381. if (titles.length > options.remove[1]) {
  382. titles[0].remove();
  383. let removes = gae('#cp_img>*,#barChapter>*');
  384. for (let i in removes) {
  385. if (/chapterTitle/.test(removes[i].className)) {
  386. break;
  387. }
  388. removes[i].remove();
  389. }
  390. }
  391. };
  392.  
  393. if (pcMode1()) {
  394. console.log('DM5_PcMode1');
  395. addGlobalStyle(pcCss);
  396. addGlobalStyle(css);
  397. if (ge('#lb-win')) {
  398. ge('#lb-win').remove();
  399. }
  400. let loop = setInterval(() => {
  401. let set = ge('#cp_image');
  402. if (set) {
  403. clearInterval(loop);
  404. insertData();
  405. set.remove();
  406. }
  407. }, 100);
  408. } else if (pcMode2()) {
  409. console.log('DM5_PcMode2');
  410. addGlobalStyle(pcCss);
  411. addGlobalStyle(css);
  412. if (ge('#lb-win')) {
  413. ge('#lb-win').remove();
  414. }
  415. insertData();
  416. } else if (mobile()) {
  417. console.log('DM5_mobile');
  418. addGlobalStyle(mCss);
  419. addGlobalStyle(css);
  420. if (gae('.view-bottom-bar>li').length == 4) {
  421. addGlobalStyle(`.view-bottom-bar>li:nth-child(n+2):nth-child(-n+3){display:none !important}.view-bottom-bar li{width:50% !important}`);
  422. }
  423. insertData(document);
  424. }
  425.  
  426. })();