Greasy Fork

Greasy Fork is available in English.

pixiv helper

A small script to download the new png animated image in pixiv

当前为 2014-07-03 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        pixiv helper
// @namespace   http://mfish.twbbs.org/
// @include     http://www.pixiv.net/member_illust.php?mode=medium&illust_id=*
// @require     http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js
// @require     http://greasyfork.icu/scripts/2350-filesaver-js/code/filesaverjs.js?version=6255
// @require     http://cdnjs.cloudflare.com/ajax/libs/jszip/2.3.0/jszip.min.js
// @require     http://greasyfork.icu/scripts/2963-gif-js/code/gifjs.js?version=8462
// @version     0.3.1
// @grant       GM_registerMenuCommand
// @grant       GM_openInTab
// @description A small script to download the new png animated image in pixiv
// ==/UserScript==

var global = unsafeWindow;
this.$ = this.jQuery = $.noConflict();

//https://gist.github.com/lsauer/5196979
//lsauer.com , lo sauer 2013
//JavaScript List of selected MIME types
//A comprehensive MIME List is available here: https://gist.github.com/lsauer/2838503

var mimeTypes =
    {
        'a' : 'application/octet-stream',
        'ai' : 'application/postscript',
        'aif' : 'audio/x-aiff',
        'aifc' : 'audio/x-aiff',
        'aiff' : 'audio/x-aiff',
        'au' : 'audio/basic',
        'avi' : 'video/x-msvideo',
        'bat' : 'text/plain',
        'bin' : 'application/octet-stream',
        'bmp' : 'image/x-ms-bmp',
        'c' : 'text/plain',
        'cdf' : 'application/x-cdf',
        'csh' : 'application/x-csh',
        'css' : 'text/css',
        'dll' : 'application/octet-stream',
        'doc' : 'application/msword',
        'dot' : 'application/msword',
        'dvi' : 'application/x-dvi',
        'eml' : 'message/rfc822',
        'eps' : 'application/postscript',
        'etx' : 'text/x-setext',
        'exe' : 'application/octet-stream',
        'gif' : 'image/gif',
        'gtar' : 'application/x-gtar',
        'h' : 'text/plain',
        'hdf' : 'application/x-hdf',
        'htm' : 'text/html',
        'html' : 'text/html',
        'jpe' : 'image/jpeg',
        'jpeg' : 'image/jpeg',
        'jpg' : 'image/jpeg',
        'js' : 'application/x-javascript',
        'ksh' : 'text/plain',
        'latex' : 'application/x-latex',
        'm1v' : 'video/mpeg',
        'man' : 'application/x-troff-man',
        'me' : 'application/x-troff-me',
        'mht' : 'message/rfc822',
        'mhtml' : 'message/rfc822',
        'mif' : 'application/x-mif',
        'mov' : 'video/quicktime',
        'movie' : 'video/x-sgi-movie',
        'mp2' : 'audio/mpeg',
        'mp3' : 'audio/mpeg',
        'mp4' : 'video/mp4',
        'mpa' : 'video/mpeg',
        'mpe' : 'video/mpeg',
        'mpeg' : 'video/mpeg',
        'mpg' : 'video/mpeg',
        'ms' : 'application/x-troff-ms',
        'nc' : 'application/x-netcdf',
        'nws' : 'message/rfc822',
        'o' : 'application/octet-stream',
        'obj' : 'application/octet-stream',
        'oda' : 'application/oda',
        'pbm' : 'image/x-portable-bitmap',
        'pdf' : 'application/pdf',
        'pfx' : 'application/x-pkcs12',
        'pgm' : 'image/x-portable-graymap',
        'png' : 'image/png',
        'pnm' : 'image/x-portable-anymap',
        'pot' : 'application/vnd.ms-powerpoint',
        'ppa' : 'application/vnd.ms-powerpoint',
        'ppm' : 'image/x-portable-pixmap',
        'pps' : 'application/vnd.ms-powerpoint',
        'ppt' : 'application/vnd.ms-powerpoint',
        'pptx' : 'application/vnd.ms-powerpoint',
        'ps' : 'application/postscript',
        'pwz' : 'application/vnd.ms-powerpoint',
        'py' : 'text/x-python',
        'pyc' : 'application/x-python-code',
        'pyo' : 'application/x-python-code',
        'qt' : 'video/quicktime',
        'ra' : 'audio/x-pn-realaudio',
        'ram' : 'application/x-pn-realaudio',
        'ras' : 'image/x-cmu-raster',
        'rdf' : 'application/xml',
        'rgb' : 'image/x-rgb',
        'roff' : 'application/x-troff',
        'rtx' : 'text/richtext',
        'sgm' : 'text/x-sgml',
        'sgml' : 'text/x-sgml',
        'sh' : 'application/x-sh',
        'shar' : 'application/x-shar',
        'snd' : 'audio/basic',
        'so' : 'application/octet-stream',
        'src' : 'application/x-wais-source',
        'swf' : 'application/x-shockwave-flash',
        't' : 'application/x-troff',
        'tar' : 'application/x-tar',
        'tcl' : 'application/x-tcl',
        'tex' : 'application/x-tex',
        'texi' : 'application/x-texinfo',
        'texinfo': 'application/x-texinfo',
        'tif' : 'image/tiff',
        'tiff' : 'image/tiff',
        'tr' : 'application/x-troff',
        'tsv' : 'text/tab-separated-values',
        'txt' : 'text/plain',
        'ustar' : 'application/x-ustar',
        'vcf' : 'text/x-vcard',
        'wav' : 'audio/x-wav',
        'wiz' : 'application/msword',
        'wsdl' : 'application/xml',
        'xbm' : 'image/x-xbitmap',
        'xlb' : 'application/vnd.ms-excel',
        'xls' : 'application/vnd.ms-excel',
        'xlsx' : 'application/vnd.ms-excel',
        'xml' : 'text/xml',
        'xpdl' : 'application/xml',
        'xpm' : 'image/x-xpixmap',
        'xsl' : 'application/xml',
        'xwd' : 'image/x-xwindowdump',
        'zip' : 'application/zip'
    }

function last(arr) {
    var l = arr.length;
    return arr[l - 1];
}

var Deferer = (function(){
    function Deferer () {
        this.called = 0;
        this.count = 0;
    }
    Deferer.prototype.done = function done() {
        /*override this to your callback*/
        console.log('seems forgot to bind a callback')
    }
    Deferer.prototype.tick = function tick() {
        //console.log('ticked');
        this.called++;
        if (this.count === this.called) {
            this.done();
        }
    }
    return Deferer;
}())

function getMIME(name) {
    return mimeTypes[getFileExtension(name)] || '';
}

function getFileExtension(name) {
    return last(name.split('.'));
}

var extractFiles = (function () {
    var fileCache = [];
    var unzippedCache = [];
    
    function extractFiles(blob, callback) {
        
        var index = fileCache.indexOf(blob)
        if (index >= 0) {
            setTimeout(function(){
                callback(unzippedCache[index]);
            }, 0)
            return;
        }
        
        var fileReader = new FileReader();
        fileReader.onload = function() {
            var arrayBuffer = this.result;
            
            var zip = new JSZip(arrayBuffer, {checkCRC32:true});
            var file = zip.file(/\d+.(?:jpg|png|gif)/i);
            
            var unzippedFiles = [];
            
            console.log(file);
            file.forEach(function(file){
                var fileName, arrayBuffer, mime, blob;
                if(file.dir) {return;}
                fileName = file.name;
                arrayBuffer = file.asArrayBuffer();
                mime = getMIME(fileName);
                //console.log(arrayBuffer);
                //console.log(mime);
                blob = new Blob([arrayBuffer], {type : mime});
                unzippedFiles.push({
                    blob : blob,
                    fileName : fileName,
                    mime : mime
                })
                
                fileCache.push(blob);
                unzippedCache.push(unzippedFiles);
                
            })
            callback(unzippedFiles.slice(0));
        };
        fileReader.readAsArrayBuffer(blob);
    }
    return extractFiles;
    
}());

var download = function(){
    var cachedFile = {};
    function download(src, callback) {
        if (cachedFile[src]) {
            setTimeout(function(){
                callback(cachedFile[src]);
            }, 0)
            return;
        }
        
        //console.log(src);
        var oReq = new XMLHttpRequest();
        oReq.open("GET", src, true);
        oReq.responseType = "blob";
        
        oReq.onload = function (oEvent) {
            var blob, objectURL;
            blob = oReq.response; // Note: not oReq.responseText
            if (blob) {
                cachedFile[src] = blob;
                callback(blob);
                //objectURL = URL.createObjectURL(blob);
                //GM_openInTab(objectURL);
            } else {
                console.log('fail to download', src);
            }
        };
        oReq.send(null);
    }
    return download;
}();

function getUrl (blob, name) {
    return URL.createObjectURL(blob);
}

var getGIF = (function(){
    function getGIF (files, delays, size, callback, onprogress) {
        var imageCount = 0;
        var defer = new Deferer();
        console.log(defer);
        var gif = new GIF({
            workers : 2,
            quality : 10,
            workerScript : GIF_worker_URL,
            width : size[0],
            height : size[1]
        });
        files.forEach(function(file){
            var delay, filename, imgElement;
            imgElement = document.createElement("img");
            imgElement.src = getUrl(file.blob);
            
            defer.count++;
            $(imgElement).on('load', defer.tick.bind(defer));
            
            delays.some(function(element){
                if (element.file === file.fileName ) {
                    delay = element.delay;
                    return true;
                } else {
                    return false;
                }
            });
            console.log(imgElement, delay, file.fileName);
            gif.addFrame(imgElement, {delay: delay});
        })
        gif.on('finished', function(blob) {
            callback(blob);
        });
        if (onprogress) {
            gif.on('progress', function(p) {
                onprogress(p);
            });
        }
        defer.done = function(){
            gif.render();
        }
    }
    return getGIF;
}());

$('body').append(modal);
(function($){
    var defaultOption = {
        stat : 'on'
    };
    function off(el) {
        el = $(el);
        el.find('.modal').slideUp(500,function(){
            el.fadeOut(200);
        });
    }
    function on(el) {
        el = $(el);
        el.find('.modal').hide();
        el.fadeIn(200,function(){
            el.find('.modal').slideDown(500);
        });
    }
    $.fn.modal = function modal(option) {
        option = option || {};
        $.extend(option, defaultOption);
        var _modal = $(this);
        if (!_modal.is('.inited')) {
            _modal.on('click', function(e){
                if ($(e.target).is('.mmis1000-modal .exit')) {
                    off(_modal);
                }
                if ($(e.target).is('.mmis1000-modal')) {
                    off(_modal);
                }
            });
            _modal.addClass('inited');
        }
        switch (option.stat) {
            case "on":
                on(_modal);
                break;
            case "off":
                off(_modal);
                break;
            default:
                on(_modal);
        }
    };
}(jQuery));

var modal_css = ".mmis1000-modal{background:rgba(128,128,128,0.5);bottom:0;display:none;left:0;overflow:hidden;position:fixed;right:0;top:0;z-index:99999}"+
    ".mmis1000-modal .modal{background:#fff;border-radius:10px;bottom:25px;left:25px;position:absolute;right:25px;top:25px}"+
    ".mmis1000-modal .modal .head{border-bottom-color:#ddd;border-bottom-style:solid;border-bottom-width:2px;height:30px;left:0;padding-left:5px;position:absolute;right:0;top:0}"+
    ".mmis1000-modal .modal .head .text{color:#666;font-size:20px;line-height:30px}"+
    ".mmis1000-modal .modal .head .exit{background:red;border-radius:4px;color:#fff;cursor:pointer;font-size:20px;height:20px;line-height:20px;position:absolute;right:5px;text-align:center;top:5px;width:20px}"+
    ".mmis1000-modal .modal .content-wrapper{bottom:10px;left:0;overflow:auto;position:absolute;right:0;top:32px}"+
    ".mmis1000-modal .modal .content-wrapper .content{font-size:18px;left:10px;position:absolute;right:10px;text-align:center;top:10px}"+
    ".mmis1000-modal .modal .content-wrapper .content img{max-width:100%}";
$('head').append($('<style></style>').html(modal_css));
var modal = $([
    "<div id='test' class='mmis1000-modal'>",
    "<div class='modal'>",
    "<div class='head'>",
    "<span class='text'>大圖檢視</span>",
    "<div class='exit'>",
    "X",
    "</div>",
    "</div>",
    "<div class='content-wrapper'>",
    "<div class='content'>",
    "</div>",
    "</div>",
    "</div>",
    "</div>"
].join(''));
$('body').append(modal);
var modalContent = modal.find('.content');
//$(modal).modal();//debug
/*=== main program starts from here ===*/

function checkUp() {
    if (!global.pixiv.context.ugokuIllustData) {
        alert('這看起來只是一般的gif或靜圖歐,請直接右鍵下載!');
        throw new Error('can not found zipped image source');
    }
}
function checkUpLogin() {
    if (!global.pixiv.context.ugokuIllustFullscreenData) {
        alert('沒登入的話,是無法瀏覽大圖的歐!');
        throw new Error('can not found zipped fullscreen image source');
    }
}
function downloadSmall() {
    checkUp();
    var context = global.pixiv.context;
    var illustrate = context.ugokuIllustData;
    var src = illustrate.src;
    var title = context.illustTitle;
    download(src, function(blob) {
        /*objectURL = getUrl(blob);
        GM_openInTab(objectURL);*/
        saveAs(blob, title + '.zip');
    });
}

function downloadFullScreen() {
    checkUp();
    checkUpLogin();
    var context = global.pixiv.context;
    var illustrate = context.ugokuIllustFullscreenData;
    var src = illustrate.src;
    var title = context.illustTitle;
    download(src, function(blob) {
        /*objectURL = getUrl(blob);
        GM_openInTab(objectURL);*/
        saveAs(blob, title + '(fullscreen).zip');
    });
}

function showPictures() {
    checkUp();
    checkUpLogin();
    var context = global.pixiv.context;
    var illustrate = context.ugokuIllustFullscreenData;
    var src = illustrate.src;
    download(src, function(blob) {
        modal.modal();
        modalContent.html('');
        extractFiles(blob, function(files){
            files.forEach(function(file){
                var url = getUrl(file.blob);
                modalContent.append($('<p></p>').text(file.fileName));
                modalContent.append($('<img/>').attr('src', url));
            })
        });
    });
}
var showSmallGif = (function(){
    var cachedFileUrl = null;
    function showSmallGif() {
        checkUp()
        if (cachedFileUrl) {
            modalContent.html('');
            modal.modal();
            var url = cachedFileUrl;
            modalContent.append($('<img/>').attr('src', url));
            return;
        }
        var context = global.pixiv.context;
        var illustrate = context.ugokuIllustData;
        var src = illustrate.src;
        var delays = global.pixiv.context.ugokuIllustData.frames.slice(0);
        var size = global.pixiv.context.ugokuIllustData.size.slice(0);
        download(src, function(blob) {
            modalContent.html('');
            modal.modal();
            modalContent.append($('<p></p>').text('生成中,請稍候'));
            extractFiles(blob, function(files){
                getGIF(files, delays, size, function(blob){
                    modalContent.html('');
                    var url = cachedFileUrl =getUrl(blob);
                    modalContent.append($('<img/>').attr('src', url));
                }, function(p) {
                    modalContent.html('');
                    modalContent.append($('<p></p>').text('生成中,請稍候'));
                    modalContent.append($('<p></p>').text( Math.floor(p * 100) + ' %'));
                });
            });
        });
    }
    return showSmallGif;
}());
var showGif = (function(){
    var cachedFileUrl = null;
    function showGif() {
        checkUp();
        checkUpLogin();
        if (cachedFileUrl) {
            modalContent.html('');
            modal.modal();
            var url = cachedFileUrl;
            modalContent.append($('<img/>').attr('src', url));
            return;
        }
        var context = global.pixiv.context;
        var illustrate = context.ugokuIllustFullscreenData;
        var src = illustrate.src;
        var delays = global.pixiv.context.ugokuIllustData.frames.slice(0);
        var size = global.pixiv.context.illustSize.slice(0);
        download(src, function(blob) {
            modalContent.html('');
            modal.modal();
            modalContent.append($('<p></p>').text('生成中,請稍候'));
            extractFiles(blob, function(files){
                getGIF(files, delays, size, function(blob){
                    modalContent.html('');
                    var url = cachedFileUrl =getUrl(blob);
                    modalContent.append($('<img/>').attr('src', url));
                }, function(p) {
                    modalContent.html('');
                    modalContent.append($('<p></p>').text('生成中,請稍候'));
                    modalContent.append($('<p></p>').text( Math.floor(p * 100) + ' %'));
                });
            });
        });
    }
    return showGif;
}())


GM_registerMenuCommand( '下載檔案!(小圖)', downloadSmall);
GM_registerMenuCommand( '下載檔案!(大圖)', downloadFullScreen);
GM_registerMenuCommand( '檢視個別檔案!(大圖)', showPictures);
GM_registerMenuCommand( '存為gif!(小圖)', showSmallGif);
GM_registerMenuCommand( '存為gif!(大圖)', showGif);