漫畫閱讀器

漫畫自動連續載入

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==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);
  }
})();