修复copymanga图片错误

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

目前為 2021-10-09 提交的版本,檢視 最新版本

// ==UserScript==
// @name         修复copymanga图片错误
// @namespace    https://github.com/IronKinoko/copymanga
// @version      1.0.7
// @license      MIT
// @description  处理图片资源加载失败时自动重新加载
// @author       IronKinoko
// @match        https://*.copymanga.com/*
// @icon         https://www.google.com/s2/favicons?domain=copymanga.com
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  async function waitDOM(query) {
    return new Promise((resolve, reject) => {
      const now = Date.now();
      function getDOM() {
        if (Date.now() - now > 5000) reject();
        const dom = document.querySelector(query);
        if (dom) {
          resolve(dom);
        } else {
          requestAnimationFrame(getDOM);
        }
      }
      getDOM();
    })
  }

  async function injectFixImg() {
    await waitDOM('ul.comicContent-list');

    async function injectEvent() {
      const imgs = document.querySelectorAll('ul li img:not([data-inject=true])');
      imgs.forEach((img) => {
        if (img.dataset.inject === 'true') return
        img.dataset.inject = 'true';
        img.onerror = () => {
          const url = new URL(img.src);
          let v = parseInt(url.searchParams.get('v')) || 0;
          if (v > 5) return (img.onerror = null)
          url.searchParams.set('v', ++v);
          img.src = url.toString();
        };
      });
    }

    const ob = new MutationObserver(injectEvent);
    const listDOM = document.querySelector('.comicContent-list');
    ob.observe(listDOM, { childList: true, subtree: true });
    injectEvent();
  }

  function $(string) {
    return new DOMParser().parseFromString(string, 'text/html').body.firstChild
  }

  function main() {
    if (window.__copymanga_autofix) return console.log('已经插入过了')
    window.__copymanga_autofix = true;

    if (/comic\/.*\/chapter/.test(location.href)) {
      injectFixImg();
    }

    if (location.pathname.startsWith('/h5')) {
      addH5HistoryListener();
    }
    replaceHeader();
  }

  const _historyWrap = function (type) {
    const orig = history[type];
    const e = new Event(type);
    return function () {
      const rv = orig.apply(this, arguments);
      e.arguments = arguments;
      window.dispatchEvent(e);
      return rv
    }
  };

  async function addH5HistoryListener() {
    history.pushState = _historyWrap('pushState');
    history.replaceState = _historyWrap('replaceState');

    window.addEventListener('pushState', runH5main);
    window.addEventListener('replaceState', runH5main);
    window.addEventListener('popstate', runH5main);
    window.addEventListener('scroll', currentPage);

    runH5main();
  }

  /**
   * @param {Event} e
   */
  async function currentPage() {
    try {
      if (!/h5\/comicContent\/.*/.test(location.href)) return
      const scrollHeight = document.scrollingElement.scrollTop;
      const list = await waitHasComicContent();
      let height = 0;
      for (let i = 0; i < list.length; i++) {
        const item = list[i];
        height += item.getBoundingClientRect().height;
        if (height > scrollHeight) {
          const dom = document.querySelector('.comicContentPopup .comicFixed');
          dom.textContent = dom.textContent.replace(/(.*)\//, `${i + 1}/`);
          break
        }
      }
    } catch (error) {}
  }

  async function runH5main() {
    try {
      if (!/h5\/comicContent\/.*/.test(location.href)) return
      const ulDom = await searchImgListDom();
      const uuid = getComicId();
      const domUUID = ulDom.dataset.uuid;
      if (domUUID !== uuid) {
        ulDom.dataset.uuid = uuid;
        const list = await waitHasComicContent();
        list.forEach((dom) => (dom.dataset.uuid = uuid));
        injectFixImg();
      }

      const main = ulDom.parentElement;
      main.style.position = 'unset';
      main.style.overflowY = 'unset';

      let nextPartDom = document.querySelector('#comicContentMain #nextpart');
      let nextButton = document.querySelector(
        '.comicControlBottomTop > div:nth-child(3) > span'
      );
      if (!nextPartDom) {
        if (!nextButton) {
          await openControl();
          nextButton = document.querySelector(
            '.comicControlBottomTop > div:nth-child(3) > span'
          );
        }

        nextPartDom = document.createElement('div');
        nextPartDom.style.textAlign = 'center';
        nextPartDom.style.lineHeight = '50px';
        nextPartDom.style.fontSize = '16px';
        nextPartDom.style.paddingBottom = '100px';
        nextPartDom.textContent = '下一话';
        nextPartDom.id = 'nextpart';

        nextPartDom.onclick = async (e) => {
          e.stopPropagation();
          nextButton && nextButton.click();
          document.scrollingElement.scrollTop = 0;
        };

        document.getElementById('comicContentMain').appendChild(nextPartDom);
      }
      nextPartDom.style.display = nextButton.parentElement.classList.contains(
        'noneUuid'
      )
        ? 'none'
        : 'block';
    } catch (error) {}
  }

  function getComicId() {
    const [_, uuid] = location.href.match(/h5\/comicContent\/.*\/(.*)/);
    return uuid
  }

  /**
   * @return {Promise<HTMLDivElement>}
   */
  async function searchImgListDom() {
    return new Promise((resolve, reject) => {
      const now = Date.now();
      function _searchImgListDom() {
        if (Date.now() - now > 5000) reject();
        const dom = document.querySelector('.comicContentPopupImageList');
        if (dom) resolve(dom);
        else requestAnimationFrame(_searchImgListDom);
      }
      _searchImgListDom();
    })
  }

  function replaceHeader() {
    const header = document.querySelector('.container.header-log .row');
    if (header) {
      header.style.flexWrap = 'nowrap';
      header.querySelector('div:nth-child(6)').replaceWith(
        $(
          `<div class="col-1">
          <div class="log-txt">
            <a href="/web/person/shujia">我的书架</a>
            <div class="log-unboder"></div>
          </div>
        </div>`
        )
      );
      header.querySelector('div:nth-child(7)').replaceWith(
        $(
          `<div class="col-1">
          <div class="log-txt">
            <a href="/web/person/liulan">我的浏览</a>
            <div class="log-unboder"></div>
          </div>
        </div>`
        )
      );

      header.querySelector('div:nth-child(8)').className = 'col';
      header.querySelector('div.col > div > div').style.justifyContent =
        'flex-end';
    }
  }

  main();

  function fakeClickEvent() {
    const { width, height } = document.body.getBoundingClientRect();
    return new MouseEvent('click', { clientX: width / 2, clientY: height / 2 })
  }

  function sleep(time) {
    return new Promise((resolve, reject) => {
      setTimeout(resolve, time);
    })
  }

  async function openControl() {
    const li = document.querySelector('li.comicContentPopupImageItem');
    li.dispatchEvent(fakeClickEvent());
    await sleep(0);
    li.dispatchEvent(fakeClickEvent());
  }

}());