修复copymanga图片错误

处理图片资源加载失败时自动重新加载

目前为 2022-07-10 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name 修复copymanga图片错误
  3. // @namespace https://github.com/IronKinoko/copymanga
  4. // @version 1.1.0
  5. // @license MIT
  6. // @description 处理图片资源加载失败时自动重新加载
  7. // @author IronKinoko
  8. // @match https://www.copymanga.org/*
  9. // @icon https://www.google.com/s2/favicons?domain=www.copymanga.org
  10. // @grant none
  11. // @noframes
  12. // ==/UserScript==
  13. (function () {
  14. 'use strict';
  15.  
  16. function s2d(string) {
  17. return new DOMParser().parseFromString(string, "text/html").body.firstChild;
  18. }
  19. function addErrorListener(img) {
  20. if (img.dataset.inject === "true")
  21. return;
  22. img.dataset.inject = "true";
  23. img.onerror = () => {
  24. const url = new URL(img.src);
  25. let v = parseInt(url.searchParams.get("v")) || 0;
  26. if (v > 5)
  27. return img.onerror = null;
  28. url.searchParams.set("v", ++v + "");
  29. img.src = url.toString();
  30. img.alt = "\u56FE\u7247\u52A0\u8F7D\u51FA\u9519";
  31. };
  32. }
  33.  
  34. function sleep(time) {
  35. return new Promise((resolve) => {
  36. setTimeout(resolve, time);
  37. });
  38. }
  39.  
  40. async function waitDOM(selector) {
  41. return new Promise((resolve, reject) => {
  42. const now = Date.now();
  43. function getDOM() {
  44. if (Date.now() - now > 5e3)
  45. reject();
  46. const dom = document.querySelector(selector);
  47. if (dom) {
  48. resolve(dom);
  49. } else {
  50. requestAnimationFrame(getDOM);
  51. }
  52. }
  53. getDOM();
  54. });
  55. }
  56.  
  57. async function openControl() {
  58. const li = await waitDOM("li.comicContentPopupImageItem");
  59. li.dispatchEvent(fakeClickEvent());
  60. await sleep(0);
  61. li.dispatchEvent(fakeClickEvent());
  62. }
  63. function fakeClickEvent() {
  64. const { width, height } = document.body.getBoundingClientRect();
  65. return new MouseEvent("click", { clientX: width / 2, clientY: height / 2 });
  66. }
  67. async function currentPage() {
  68. try {
  69. if (!/h5\/comicContent\/.*/.test(location.href))
  70. return;
  71. const scrollHeight = document.scrollingElement.scrollTop;
  72. const list = await waitHasComicContent();
  73. let height = 0;
  74. for (let i = 0; i < list.length; i++) {
  75. const item = list[i];
  76. height += item.getBoundingClientRect().height;
  77. if (height > scrollHeight) {
  78. const dom = document.querySelector(".comicContentPopup .comicFixed");
  79. dom.textContent = dom.textContent.replace(/(.*)\//, `${i + 1}/`);
  80. break;
  81. }
  82. }
  83. } catch (e) {
  84. }
  85. }
  86. async function runH5main() {
  87. try {
  88. if (!/h5\/comicContent\/.*/.test(location.href))
  89. return;
  90. const ulDom = await waitDOM(".comicContentPopupImageList");
  91. const uuid = getComicId();
  92. const domUUID = ulDom.dataset.uuid;
  93. if (domUUID !== uuid) {
  94. ulDom.dataset.uuid = uuid;
  95. }
  96. injectFixImg$1();
  97. const main = ulDom.parentElement;
  98. main.style.position = "unset";
  99. main.style.overflowY = "unset";
  100. let nextPartDom = document.querySelector("#comicContentMain #nextpart");
  101. let nextButton = document.querySelector(".comicControlBottomTop > div:nth-child(3) > span");
  102. if (!nextPartDom) {
  103. if (!nextButton) {
  104. await openControl();
  105. nextButton = document.querySelector(".comicControlBottomTop > div:nth-child(3) > span");
  106. }
  107. nextPartDom = document.createElement("div");
  108. nextPartDom.style.textAlign = "center";
  109. nextPartDom.style.lineHeight = "50px";
  110. nextPartDom.style.fontSize = "16px";
  111. nextPartDom.style.paddingBottom = "100px";
  112. nextPartDom.textContent = "\u4E0B\u4E00\u8BDD";
  113. nextPartDom.id = "nextpart";
  114. nextPartDom.onclick = async (e) => {
  115. e.stopPropagation();
  116. nextButton && nextButton.click();
  117. document.scrollingElement.scrollTop = 0;
  118. };
  119. document.getElementById("comicContentMain").appendChild(nextPartDom);
  120. }
  121. nextPartDom.style.display = nextButton.parentElement.classList.contains("noneUuid") ? "none" : "block";
  122. } catch (error) {
  123. console.error(error);
  124. }
  125. }
  126. function getComicId() {
  127. const [, uuid] = location.href.match(/h5\/comicContent\/.*\/(.*)/);
  128. return uuid;
  129. }
  130. async function waitHasComicContent() {
  131. return document.querySelectorAll(".comicContentPopupImageItem");
  132. }
  133. async function addH5HistoryListener() {
  134. history.pushState = _historyWrap("pushState");
  135. history.replaceState = _historyWrap("replaceState");
  136. window.addEventListener("pushState", runH5main);
  137. window.addEventListener("replaceState", runH5main);
  138. window.addEventListener("popstate", runH5main);
  139. window.addEventListener("scroll", currentPage);
  140. runH5main();
  141. }
  142. const _historyWrap = function(type) {
  143. const orig = history[type];
  144. const e = new Event(type);
  145. return function() {
  146. const rv = orig.apply(this, arguments);
  147. window.dispatchEvent(e);
  148. return rv;
  149. };
  150. };
  151. async function injectFixImg$1() {
  152. const listDOM = await waitDOM(".comicContentPopupImageList");
  153. async function injectEvent() {
  154. const imgs = document.querySelectorAll("ul li img");
  155. imgs.forEach(addErrorListener);
  156. }
  157. const ob = new MutationObserver(injectEvent);
  158. ob.observe(listDOM, { childList: true, subtree: true });
  159. injectEvent();
  160. }
  161. function h5() {
  162. addH5HistoryListener();
  163. }
  164.  
  165. function replaceHeader() {
  166. const header = document.querySelector(".container.header-log .row");
  167. if (header) {
  168. header.style.flexWrap = "nowrap";
  169. header.querySelector("div:nth-child(6)").replaceWith(s2d(`<div class="col-1">
  170. <div class="log-txt">
  171. <a href="/web/person/shujia">\u6211\u7684\u4E66\u67B6</a>
  172. <div class="log-unboder"></div>
  173. </div>
  174. </div>`));
  175. header.querySelector("div:nth-child(7)").replaceWith(s2d(`<div class="col-1">
  176. <div class="log-txt">
  177. <a href="/web/person/liulan">\u6211\u7684\u6D4F\u89C8</a>
  178. <div class="log-unboder"></div>
  179. </div>
  180. </div>`));
  181. header.querySelector("div:nth-child(8)").className = "col";
  182. header.querySelector("div.col > div > div").style.justifyContent = "flex-end";
  183. }
  184. }
  185. async function injectFixImg() {
  186. const listDOM = await waitDOM("ul.comicContent-list");
  187. async function injectEvent() {
  188. const imgs = document.querySelectorAll("ul li img");
  189. imgs.forEach(addErrorListener);
  190. }
  191. const ob = new MutationObserver(injectEvent);
  192. ob.observe(listDOM, { childList: true, subtree: true });
  193. injectEvent();
  194. }
  195. function pc() {
  196. if (/comic\/.*\/chapter/.test(location.href)) {
  197. injectFixImg();
  198. }
  199. replaceHeader();
  200. }
  201.  
  202. if (location.pathname.startsWith("/h5")) {
  203. h5();
  204. } else {
  205. pc();
  206. }
  207.  
  208. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址