漫畫閱讀器

漫畫自動連續載入

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name           漫畫閱讀器
// @version        2.2.3
// @namespace      486FF937-D190-42D3-B107-6ED190443E1C
// @description    漫畫自動連續載入
// @include        /https?:\/\/e[x-]hentai\.org\/s\/\w+\/\w+/
// @include        /https?:\/\/www\.dm5\.com\/m\d+/
// @include        /https?:\/\/www\.1kkk\.com\/ch\d+-\d+/
// @include        /https?:\/\/comicbus\.\w+\/online\/[\w-]+\.html\?ch=\d+/
// @match          *://*.manhuagui.com/comic/*/*.html*
// @match          *://www.cartoonmad.cc/comic/*.html*
// @match          *://www.gufengmh8.com/manhua/*/*.html*
// @match          *://www.manhuabei.com/manhua/*/*.html*
// @match          *://www.manhuafen.com/comic/*/*.html*
// @match          *://www.36mh.com/manhua/*/*.html*
// @match          *://www.90mh.com/manhua/*/*.html*
// @run-at         document-end
// @grant          GM_xmlhttpRequest
// @grant          unsafeWindow
// ==/UserScript==


Element.prototype.offset = function(){
  let rect = this.getBoundingClientRect();
  
  return {
    top: Math.round(scrollY + rect.top),
    bottom: Math.round(scrollY + rect.top + rect.height)
  }
}

Element.prototype.goTo = function(n){
  viewport.scrollTop = this.offset().top + (n||0);
}

function clone (obj) {
  return JSON.parse(JSON.stringify(obj))
}

function log(s){
  if(isLog){
    console.log(s.toString());
  }
}

function getEl(s, d){
  return (d || document).querySelector(s);
}

function img_loaded(){
  this.removeAttribute('style');
  
  //橫圖
  if (this.naturalWidth > this.naturalHeight) {
    this.originalWidth = this.naturalWidth
    if(this.naturalWidth > max_h_width){
      this.style.width = max_h_width + 'px'
      this.originalWidth = max_h_width
    }else if(this.naturalWidth < min_h_width){
      this.style.width = min_h_width + 'px'
      this.originalWidth = min_h_width
    }
  //直圖 or 正方形
  } else {
    this.originalHeight = this.naturalHeight
    if (this.naturalHeight < min_v_height) {
      this.style.height = min_v_height + 'px'
      this.originalHeight = min_v_height
    } else if (this.naturalHeight > max_v_height) {
      this.style.height = max_v_height + 'px'
      this.originalHeight = max_v_height
    }
  }

  this.onclick = click_RWD;
}

function img_error(e) {
  let link = new URL(this.src)
  let count = parseInt(link.searchParams.get('try'), 10) || 0
  if (count < 2) {
    link.searchParams.set('try', count + 1)
    this.src = link.href
  } else {
    console.log('error', this.src)
  }
}

function click_RWD() {
  if (this.height === this.originalHeight || this.width === this.originalWidth) {
    let gap = 10 / devicePixelRatio
    this.style.height = (innerHeight - gap) + 'px'
    this.style.width = null
    this.goTo(-gap / 2);
  } else if (this.originalHeight !== undefined) {
    this.style.height = this.originalHeight + 'px'
    this.style.width = null
  } else {
    this.style.width = this.originalWidth + 'px'
    this.style.height = null
  }
}

function Img(n){
  let pic = new Image();
  pic.style.border = '3px dashed #FF0000';
  pic.style.maxWidth = max_h_width + 'px';
  pic.style.maxHeight = max_v_height + 'px';
  pic.onload = img_loaded;
  pic.onerror = img_error;

  (function(){
    if (picUrls[n - 1]) {
      pic.src = picUrls[n - 1];
    } else {
      setTimeout(arguments.callee, 500);
    }
  })();
  
  return pic;
}

function bookmark(n){
  let d = document.createElement('div');
  d.setAttribute('style', 'height: 22px; background: #E6E6E6; margin: 30px 0px; font: bold 18px Arial; color: black;');
  d.innerHTML = '第 ' + n + ' 頁';
  
  return d;
}

function preload(count){
  let index = pics.length;
  let end = index + (count||preCount);
  if(end > picCount) end = picCount;
  
  while(index<end){
    log('preload page: ' + (index+1));
    pics[index] = Img(index+1);
    index++;
  }
}

function showPics(count){
  let end = pageNum + (count||preCount) - 1;
  if(end > picCount) end = picCount;
  
  while(pageNum<=end){
    log('show pic: ' + pageNum);
    picArea.appendChild(pics[pageNum - 1]);
    picArea.appendChild(bookmark(pageNum));
    pageNum++;
  }
}

function dm5(){
  pageNum = parseInt(getEl('#chapterpager>.current').textContent, 10);
  picCount = document.head.innerHTML.match(/DM5_IMAGE_COUNT *= *"?(.+?)"?;/)[1];
  picArea = getEl('#showimage');
  picUrls = new Array(pageNum-1);
  pics = new Array(pageNum-1);
  
  let getPicUrls_error = 0;
  let DM5_CID = document.head.innerHTML.match(/DM5_CID *= *"?(.+?)"?;/)[1];
  let DM5_MID = document.head.innerHTML.match(/DM5_MID *= *"?(.+?)"?;/)[1];
  let DM5_VIEWSIGN_DT = document.head.innerHTML.match(/DM5_VIEWSIGN_DT *= *"?(.+?)"?;/)[1];
  let DM5_VIEWSIGN = document.head.innerHTML.match(/DM5_VIEWSIGN *= *"?(.+?)"?;/)[1];
  
  (function getPicUrls(){
    let xhr = new XMLHttpRequest();
    xhr.timeout = 10000;
    
    xhr.open('GET', 'chapterfun.ashx?' +
      'cid=' + DM5_CID +
      '&page=' + (picUrls.length+1) +
      '&_cid=' + DM5_CID +
      '&_mid=' + DM5_MID +
      '&_dt=' + DM5_VIEWSIGN_DT +
      '&_sign=' + DM5_VIEWSIGN
    );
    
    xhr.onload = function(){
      picUrls = picUrls.concat(eval(xhr.responseText));
      log('getPicUrls onload: ' + picUrls.slice(-1));
      if(picUrls.length < picCount){
      // 間隔3秒一次請求,可以一次拿到最大數量20張,小於3秒一次2張,大於3秒最多還是一次20張
        setTimeout(function(){
          getPicUrls();
        },(picUrls.length < 8) ? 0 : 3000);
      }
    };
    
    xhr.onerror = function(){
      if(++getPicUrls_error < 3){
        setTimeout(function(){
          getPicUrls();
        }, 1000);
      }else{
        alert('getPicUrls error');
      }
    };
    
    xhr.send();
  })();
  
  getEl('.rightToolBar').remove();
}

function manhuaren(){
  pageNum = parseInt(getEl('#chapterpager>.current').textContent, 10);
  picCount = document.head.innerHTML.match(/DM5_IMAGE_COUNT *= *"?(.+?)"?;/)[1];
  picArea = getEl('#showimage');
  picUrls = new Array(pageNum-1);
  pics = new Array(pageNum-1);
  console.log('https://www.manhuaren.com' + location.pathname)
  
  GM_xmlhttpRequest({
    'method': 'GET',
    'url': 'https://www.manhuaren.com' + location.pathname,
    'timeout': 10000,
    'onload': function(res){
      let doc = new DOMParser().parseFromString(res.responseText, 'text/html');

      eval(doc.scripts[10].innerHTML);
      picUrls = newImgs;
    },
    'onerror': function(res){
      alert('getPicUrls error');
    }
  });
  
  getEl('.rightToolBar').remove();
}

function eh(){
  pageNum = parseInt(getEl('.sn span:nth-of-type(1)').textContent, 10);
  picCount = parseInt(getEl('.sn span:nth-of-type(2)').textContent, 10);
  picArea = getEl('#i3');
  picUrls = new Array(pageNum-1);
  pics = new Array(pageNum-1);
  preCount = 2;
  overRange = 1000;
  
  getEl('#i1').removeAttribute('style');
  picUrls.push(getEl('#img').src);
  
  let getPicUrls_error = 0;
  (function getPicUrls(url){
    if(picUrls.length-pics.length<preCount){
      let xhr = new XMLHttpRequest();
      xhr.timeout = 10000;
      
      xhr.onload = function(){
        let doc = new DOMParser().parseFromString(xhr.responseText, 'text/html');
        picUrls.push(getEl('#img', doc).src);
        log('getPicUrls onload: ' + picUrls.length);
        
        if(picUrls.length<picCount){
          setTimeout(function(){
            getPicUrls(getEl('#i3 > a', doc).href);
          }, 0);
        }
      };
      
      xhr.onerror = function(){
        if(++getPicUrls_error<3){
          setTimeout(function(){
            getPicUrls(url);
          }, 1000);
        }else{
          alert('getPicUrls error');
        }
      };
      
      xhr.open('GET', url);
      xhr.send();
    }else{
      log('getPicUrls wait...');
      setTimeout(function(){
        getPicUrls(url);
      }, 500);
    }
  })(getEl('#i3 > a').href);
}

function manhuagui(){
  // 全局物件
  // servs: 圖片 server 相關
  // pVars: 從 imgData 中設定出來的,有著當個章節的資訊
  // 但因為有了 imgData,這個變數的值都可以計算出來,所以沒啥用
  // SMH: 頁面加載漫畫的核心物件
  
  pageNum = unsafeWindow.pVars.page;
  picCount = unsafeWindow.cInfo.len;
  pics = new Array(pageNum - 1);
  
  picArea = getEl('table.pr').parentElement;
  picArea.setAttribute('align', 'center');
  getEl('#servList').remove();
  getEl('.sub-btn').remove();
  getEl('.backToTop').remove();
  
  // 拿到混淆後的代碼 => '(function(){}())'
  let code = Array.from(document.scripts)
    .find(s => s.innerHTML.search(/window\["\\x65\\x76\\x61\\x6c"\]/) > -1)
    .innerHTML
    .slice(26, -1)

  // 後續的執行中會用到頁面的字串原型擴充方法
  String.prototype.splic = unsafeWindow.String.prototype.splic
  
  // 拿到執行 SMH.imgData({xxx}) 的代碼,切出漫畫網址的物件資料 ({xxx})
  // 前後保留括號,方便下一步 return 物件出來
  let imgDataCode = eval(code).slice(11, -11)
  let imgData = eval(imgDataCode)

  // servs[pVars.curServ].hosts[pVars.curHost].h
  let domain = 'https://us.hamreus.com/'
  
  // webp
  // picUrls = imgData.files.map(fileName => domain + imgData.path + fileName + '?e=' + imgData.sl.e + '&m=' + imgData.sl.m)
  // jpg
  picUrls = imgData.files.map(fileName => domain + imgData.path + fileName.slice(0, -5) + '?e=' + imgData.sl.e + '&m=' + imgData.sl.m)
  
  // function showChapters(){
  //  $(".result-none").replaceWith(LZString.decompressFromBase64($("#__VIEWSTATE").val()));
  //  $("#__VIEWSTATE").remove();
  // }

  // function chgCountry(){
  //  document.cookie = "country=HX;domain=.manhuagui.com;path=/;expires=" + (new Date(2100, 0)).toUTCString() + ";";
  // }
}

function comicvip(){
  pageNum = unsafeWindow.p;
  picCount = unsafeWindow.ps;
  picArea = getEl('#TheImg').parentElement;
  picUrls = new Array(pageNum-1);
  pics = new Array(pageNum-1);
  
  let su = unsafeWindow.su;
  let ti = unsafeWindow.ti;
  let nn = unsafeWindow.nn;
  let mm = unsafeWindow.mm;

  let imgurl;
  for(let s of document.scripts){
    imgurl = s.innerHTML.match(/ge\('TheImg'\)\.src *?= *?(.+?;)/);
    if(imgurl){
      imgurl = imgurl[1];
      for(let i of s.innerHTML.match(/var +?\w+? *?= *?lc\(/g)){
        eval(i.slice(0, -3) + 'unsafeWindow.' + i.match(/var +?(\w+?) *?= *?lc\(/)[1]);
      }
      break;
    }
  }
  
  for(var i=0; i<picCount; i++){
    let p = i + 1;
    picUrls[i] = eval(imgurl);
  }
  
  getEl('#TheImg').remove();

  // 新增上下一話按鈕
  let prev_button = getEl('#prevvol').cloneNode(true);
  let next_button = getEl('#nextvol').cloneNode(true);
  prev_button.removeAttribute('id');
  next_button.removeAttribute('id');

  let bottom = getEl('#prevnext2');
  while (bottom.firstChild) bottom.firstChild.remove();

  bottom.appendChild(prev_button);
  let sp = document.createElement("div")
  sp.style.display = 'inline-block';
  sp.style.width = '6em';
  bottom.appendChild(sp);
  bottom.appendChild(next_button);
}

function cartoonmad(){
  let s = getEl('select');
  pageNum = parseInt(s.value.match(/\d+/)[0], 10);
  picCount = s.options.length - 1;
  picUrls = new Array(pageNum-1);
  pics = new Array(pageNum-1);
  picArea = getEl('body > table:nth-child(1) > tbody:nth-child(1) > tr:nth-child(4) > td');
  picArea.style.paddingTop = '30px'

  let baseUrl = picArea.querySelector('img').src.match(/.+\//)[0]
  for (let i = 0; i < picCount; i++){
    picUrls[i] = baseUrl + (i + 1).toString().padStart(3, '0')
  }
}

function gufengmh8() {
  let chapterImages = clone(unsafeWindow.chapterImages)
  
  pageNum = unsafeWindow.SinTheme.getPage()
  picCount = chapterImages.length
  pics = Array(pageNum - 1).fill()
  
  picArea = getEl('#tbCenter')
  picArea.classList.remove('tbCenter')
  picArea.setAttribute('align', 'center');
  getEl('#subNav').remove()
  
  // SinConf.resHost
  let domain = unsafeWindow.SinConf.resHost[0].domain[0]
  picUrls = chapterImages.map(fileName => domain + '/' + unsafeWindow.chapterPath + fileName)
}

function manhuabei() {
  let code = (function () {
    let key = CryptoJS.enc.Utf8.parse('123456781234567G')
    let iv = CryptoJS.enc.Utf8.parse('ABCDEF1G34123412')
    let decrypt = CryptoJS.AES.decrypt(chapterImages, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 })
    let decryptedStr = decrypt.toString(CryptoJS.enc.Utf8)
    return decryptedStr.toString()
  }).toString()

  let chapterImages = JSON.parse(unsafeWindow.eval('(' + code + ')()'))
  pageNum = unsafeWindow.SinTheme.getPage()
  picCount = chapterImages.length
  picArea = getEl('.comic_wraCon')
  picArea.classList.remove('comic_wraCon')
  
  let domain = unsafeWindow.SinConf.resHost[0].domain[0]

  if (unsafeWindow.chapterPath === '') picUrls = chapterImages
  else picUrls = chapterImages.map(fileName => domain + '/' + unsafeWindow.chapterPath + fileName)

  pics = Array(pageNum - 1).fill()
  getEl('#mainNavDl').remove()
  getEl('.side_bar').remove()
  getEl('#sidePublic').remove()
}

document.documentElement.style.overflowY = 'scroll';

var isLog = false
var viewport = (document.compatMode == 'BackCompat') ? document.body : document.documentElement;
var zoomScreen = (() => {
  let isWebKit = navigator.userAgent.indexOf('WebKit') > -1

  return {
    width: isWebKit ? screen.width / devicePixelRatio : screen.width,
    height: isWebKit ? screen.height / devicePixelRatio : screen.height,
  }
})()

var min_h_width = Math.round(zoomScreen.width * 0.7)
var max_h_width = Math.round(zoomScreen.width - (26 / devicePixelRatio))
var min_v_height = Math.round(zoomScreen.height * 1.25)
var max_v_height = Math.round(zoomScreen.height * 1.3)

var preCount = 5;
var overRange = zoomScreen.height * 2;

var pageNum;
var picCount;
var picArea;
var picUrls;
var pics;

switch(location.hostname){
  case 'www.dm5.com':
  case 'www.1kkk.com':
    // manhuaren();
    dm5()
    break;
    
  case 'exhentai.org':
  case 'e-hentai.org':
    eh();
    break;
    
  case 'comicbus.com':
  case 'comicbus.live':
    comicvip();
    break;
    
  case 'www.manhuagui.com':
  case 'tw.manhuagui.com':
    manhuagui();
    break;
    
  case 'www.cartoonmad.cc':
    cartoonmad();
    break;
    
  case 'www.gufengmh8.com':
  case 'www.36mh.com':
  case 'www.90mh.com':
    gufengmh8();
    break
    
  case 'www.manhuabei.com':
  case 'www.manhuafen.com':
    manhuabei();
    break
}

picArea.innerHTML = '';

preload();
showPics();
preload();

(function(){
  log('onscroll');
  
  if(picArea.scrollHeight > 1000){
    log('onscroll start');
    
    onscroll = function(){
      if(picArea.offset().bottom-viewport.scrollTop < overRange){
        log('scrollTop: ' + viewport.scrollTop + '\npicArea.bottom: ' + picArea.offset().bottom);
        
        showPics();
        preload();
        
        if(pageNum>picCount){
          log('onscroll stop, pageNum: ' + pageNum);
          onscroll = undefined;
        }
      }
    };
  }else if(pageNum<=picCount){
    setTimeout(arguments.callee, 500);
  }
})();