BinB阅读器捕获脚本(v016061)

用于binb阅读器v016061版本的漫画的获取脚本

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name         BinB阅读器捕获脚本(v016061)
// @namespace    summer-script
// @version      0.2.2
// @description  用于binb阅读器v016061版本的漫画的获取脚本
// @author       summer
// @match        https://gammaplus.takeshobo.co.jp/_files/*
// @match        https://www.comicride.jp/viewer/*
// @exclude      https://www.comicride.jp/viewer/*/HTML5/*
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    var btn = createBtn();
    var jsonUrls = getJsonUrls();
    btn.addEventListener('click', function() {
        run(jsonUrls, btn);
    });

    function run(jsonUrls, btn, counter) {
        var jsonPath = jsonUrls.shift();
        if (undefined === jsonPath) {
            btn.innerText = '获取完毕';
            return;
        }
        var coords, ptimg;
        counter = counter ? counter : 0;
        counter++;
        jsonGet(jsonPath).then(function(ret) {
            ptimg = ret;
            coords = decodeCoords(ptimg.views[0].coords, ptimg.resources);
            var imgPath = ptimg.resources[coords[0].resid].src;
            var imgSrc = getImgUrl(imgPath, jsonPath);
            return loadImg(imgSrc);
        })
        .then(function(img) {
            btn.innerText = '正在获取...(剩余'+jsonUrls.length+'页)';
            var orgwidth = ptimg.views[0].width;
            var orgheight = ptimg.views[0].height;
            var prefix = document.title;
            var filename = prefix + '-' + counter + '.png';
            var cv = toCanvas(img, coords, orgwidth, orgheight);
            toFile(cv, filename);
            run(jsonUrls, btn, counter);
        });
    }

    function loadImg(src) {
        var func = function(resolve) {
            var img = new Image();
            img.onload = function() {
                resolve(img);
            };
            img.onerror = function() {
                console.log('image['+src+'] load fail, retrying...');
                setTimeout(function() {
                    func(resolve);
                }, 2000);
            };
            img.src = src;
        }
        return new Promise(func);
    }

    function toCanvas(img, coords, width, height) {
        var cv = document.createElement('canvas');
        cv.width = width;
        cv.height = height;
        var ctx = cv.getContext('2d');
        for(var i in coords) {
            ctx.drawImage(
                img,
                coords[i].xsrc,
                coords[i].ysrc,
                coords[i].width,
                coords[i].height,
                coords[i].xdest,
                coords[i].ydest,
                coords[i].width,
                coords[i].height
            );
        }
        return cv;
    }

    function toFile(canvas, filename) {
        canvas.toBlob(function(blob) {
            var url = URL.createObjectURL(blob);
            var a = document.createElement('a');
            a.download = filename;
            a.href = url;
            a.click();
        });
    }

    function jsonGet(path) {
        var func = function(resolve) {
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function() {
                if (XMLHttpRequest.DONE !== xhr.readyState) {
                    return;
                }
                if (200 !== xhr.status) {
                    return;
                }
                var ret;
                try {
                    ret = JSON.parse(xhr.responseText);
                } catch(err) {
                    ret = false;
                }
                resolve(ret);
            };
            xhr.open('GET', path);
            xhr.send();
        };
        return new Promise(func);
    }

    function createBtn() {
        var wrap = document.createElement('div');
        wrap.style.top = '6px';
        wrap.style.right = '8px';
        wrap.style.zIndex = '302';
        wrap.style.position = 'fixed';
        document.body.appendChild(wrap);
        var btn = document.createElement('button');
        btn.style.marginLeft = '8px';
        btn.style.padding = '8px';
        btn.style.background = '#fff';
        btn.style.border = '1px solid #aaa';
        btn.style.borderRadius = '4px';
        btn.style.minWidth = '112px';
        btn.style.color = '#000';
        btn.style.float = 'right';
        btn.style.cursor = 'pointer';
        btn.innerText = '下载';
        wrap.appendChild(btn);
        return btn;
    }

    function getJsonUrls() {
        var misc = document.getElementById('content')
                           .getElementsByTagName('div');
        var contents = [];
        for (var i = 0; i < misc.length; i++) {
            if (misc[i].hasAttribute('data-ptimg')) {
                contents.push(misc[i].getAttribute('data-ptimg'));
            }
        }
        return contents;
    }

    function decodeCoords(coordsData, resources) {
        var coordData, coordsNum = [];
        for (var i = 0; i < coordsData.length; i++) {
            coordData = coordsData[i];
            var reg = /^([^:]+):(\d+),(\d+)\+(\d+),(\d+)>(\d+),(\d+)$/;
            var n = coordData.match(reg);
            if (!n) {
                return false;
            }
            var r = n[1];
            if (!(r in resources)) {
                return false;
            }
            coordsNum.push({
                resid: r,
                xsrc: parseInt(n[2], 10),
                ysrc: parseInt(n[3], 10),
                width: parseInt(n[4], 10),
                height: parseInt(n[5], 10),
                xdest: parseInt(n[6], 10),
                ydest: parseInt(n[7], 10)
            });
        }
        return coordsNum;
    }

    function getImgUrl(imgPath, jsonPath) {
        var r;
        if (imgPath.indexOf('http://') + imgPath.indexOf('https://') !== -2) {
            r = imgPath;
        } else if ("" === imgPath) {
            r = jsonPath;
        } else if ("/" === imgPath[0]) {
            r = imgPath;
        } else if (".." === jsonPath){
            r = "../" + imgPath;
        } else if (jsonPath.match(/\/\.\.$/)){
            r = jsonPath + "/" + imgPath;
        } else if (-1 === jsonPath.indexOf("/")) {
            r = imgPath;
        } else {
            r = jsonPath.replace(/\/[^\/]*$/, "/") + imgPath;
        }
        if ("" === r) {
            return "";
        }
        var n = [];
        var i = r.replace(/\/+/g, "/")
                 .replace(/\/\.$/, "/")
                 .replace(/(^|\/)\.\.$/, "$1../")
                 .split("/").filter(function(t, i, n) {
                    return "." !== t
                  });
        for (r = 0; r < i.length; r++) {
            if (".." === i[r]) {
                if (n.length > 0) {
                    var e = n.pop();
                    "" === e ? n.push("") : ".." === e && n.push("..", "..")
                } else {
                    n.push("..");
                }
            } else {
                n.push(i[r]);
            }
        }
        var s = n.join("/");
        "" === s && (s = ".");
        return s;
    }
})();