Greasy Fork is available in English.

melonbooks图源获取

melonbooks阅读器图源自动下载

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name         melonbooks图源获取
// @namespace    summer-script
// @version      0.3
// @description  melonbooks阅读器图源自动下载
// @author       summer
// @match        https://www.melonbooks.co.jp/viewer/*
// @icon         https://www.melonbooks.co.jp/favicon.ico
// @license      GPL
// @grant        unsafeWindow
// @grant        GM_xmlhttpRequest
// @grant        GM_download
// @connect      cloudfront.net
// ==/UserScript==

(function () {
    'use strict';

    // 界面按钮
    var ui = {
        btn: null,
        appendStartBtn: function (callback) {
            var btn = document.createElement('button');
            btn.innerText = '下载图源';
            btn.style.position = 'fixed';
            btn.style.top = '20px';
            btn.style.right = '20px';
            btn.style.zIndex = '10001';
            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';

            if (callback) {
                btn.onclick = function () {
                    callback(btn);
                };
            }
            document.body.appendChild(btn);
            this.btn = btn;
        },
        updateBtnText: function (text) {
            this.btn.innerText = text;
        }
    };

    // 加载图像, 获取书籍相关参数
    var loader = {
        checkValidity: function () {
            if (!unsafeWindow.GUARDIAN_SERVER) {
                return false;
            }
            if (!unsafeWindow.book_data) {
                return false;
            }
            if (!unsafeWindow.pages_data) {
                return false;
            }
            if (!unsafeWindow.pages_data.keys) {
                return false;
            }

            return true;
        },
        getImageUrl: function (page) {
            var url = unsafeWindow.GUARDIAN_SERVER + '/';
            url += unsafeWindow.book_data.s3_key;
            url += page + '.jpg';

            return url;
        },
        getPageKey: function (page) {
            return unsafeWindow.pages_data.keys[page - 1];
        },
        getMaxPage: function () {
            return unsafeWindow.book_data.page_count;
        },
        getBookName: function () {
            return unsafeWindow.book_data.title;
        },
        loadPage: function (page, callback) {
            // 因为画布被污染后无法下载, 改用可跨域的XHR获取图片
            var img = new Image();
            var imgKey = this.getPageKey(page);
            if (callback) {
                img.onload = function () {
                    callback(img, imgKey);
                };
            }
            var imgSrc = this.getImageUrl(page);
            GM_xmlhttpRequest({
                method: 'GET',
                url: imgSrc,
                responseType: 'blob',
                onload: function (resp) {
                    img.src = URL.createObjectURL(resp.response);
                }
            });
        }
    };

    // 绘制并下载正确的图像
    var render = {
        canvas: null,
        ctx: null,
        drawImage: function (img, coorArr) {
            var canvas = document.createElement('canvas');
            canvas.width = img.naturalWidth;
            canvas.height = img.naturalHeight;
            var ctx = canvas.getContext('2d');
            this.drawOrigin(ctx, img, coorArr);
            URL.revokeObjectURL(img.src);
            this.canvas = canvas;
            this.ctx = ctx;
        },
        downloadImage: function (filename) {
            if (!this.canvas) {
                return;
            }
            var fullname = filename + '.png';
            GM_download(this.canvas.toDataURL(), fullname);
        },
        drawOrigin: function (ctx, img, coorArr) {
            var blockEachLine = Math.floor(img.naturalWidth / 96);
            ctx.drawImage(img, 0, 0);
            for (var i = 0; i < coorArr.length; i++) {
                var dstX = Math.floor(coorArr[i] % blockEachLine) * 96;
                var dstY = Math.floor(coorArr[i] / blockEachLine) * 128;
                var srcX = Math.floor(i % blockEachLine) * 96;
                var srcY = Math.floor(i / blockEachLine) * 128;
                ctx.drawImage(img, srcX, srcY, 96, 128, dstX, dstY, 96, 128);
            }
        }
    };

    // 原网页复制过来的洗牌方法
    // 每页对应一个key, key作为随机种子还原真实顺序
    var randomizer = {
        PARAM_A: 1103515245,
        PARAM_B: 12345,
        RAND_MAX: 32767,

        init: function (e) {
            this.next = this._str_to_int(e);
        },
        rand: function (t) {
            var n;
            return null != t ? (n = t + 1,
                Math.floor(this._next_int() / (Math.floor(this.RAND_MAX / n) + 1))) : this._next_int();
        },
        shuffle: function (e) {
            var t, n, i, r, o, s;
            o = [].concat(e);
            r = o.length;
            for (t = i = 0; 0 <= r ? i < r : i > r; t = 0 <= r ? ++i : --i)
                n = this.rand(o.length - 1),
                s = o[n],
                o[n] = o[t],
                o[t] = s;
            return o;
        },
        _next_int: function () {
            this.next = (this.next * this.PARAM_A + this.PARAM_B) % (this.RAND_MAX + 1);
            return this.next;
        },
        _str_to_int: function (e) {
            var t, n, i, r, o, s;
            if (s = 0,
                null != e)
                for (t = e.split(""); t.length > 0;)
                    n = t.shift(),
                    i = t.shift(),
                    r = n.charCodeAt(0),
                    o = 0,
                    i && (o = i.charCodeAt(0)),
                    s += r << 8 | o;
            return s;
        },
        getImgCoorArr: function (img) {
            var o = Math.floor(img.naturalWidth / 96);
            var s = Math.floor(img.naturalHeight / 128);
            var r = [];

            for (var l = 0; l < o * s; ++l) {
                r.push(l);
            }

            return this.shuffle(r);
        }
    };

    ui.appendStartBtn(function (btn) {
        btn.disabled = true;
        if (!loader.checkValidity()) {
            alert('脚本已失效');
            return;
        }
        taskRun();
    });

    // 顺序处理
    function taskRun(pageNow) {
        if (!pageNow) {
            pageNow = 1;
        }
        var pageMax = loader.getMaxPage();
        var fileNo = pageNow.toString().padStart(pageMax.toString().length, '0');
        var fileName = loader.getBookName() + '_' + fileNo;

        if (pageNow > pageMax) {
            ui.updateBtnText('下载完毕');
            return;
        }
        ui.updateBtnText('下载中: ' + pageNow + '/' + pageMax);
        loader.loadPage(pageNow, function (img, imgKey) {
            randomizer.init(imgKey);
            var imgCoor = randomizer.getImgCoorArr(img);
            render.drawImage(img, imgCoor);
            render.downloadImage(fileName);
            pageNow++;
            // 防止请求过快延迟1秒
            setTimeout(function () {
                taskRun(pageNow);
            }, 1000);
        });
    }
})();