// ==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 https://greasyfork.org/scripts/2350-filesaver-js/code/filesaverjs.js?version=6255 // @require http://cdnjs.cloudflare.com/ajax/libs/jszip/2.3.0/jszip.min.js // @require https://greasyfork.org/scripts/2963-gif-js/code/gifjs.js?version=8462 // @version 0.3.1.1 // @grant GM_registerMenuCommand // @grant GM_openInTab // @description A small script to download the new png animated image in pixiv // @downloadURL none // ==/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($('').html(modal_css)); var modal = $([ "
", "", "
" ].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($('

').text(file.fileName)); modalContent.append($('').attr('src', url)); }) }); }); } var showSmallGif = (function(){ var cachedFileUrl = null; function showSmallGif() { checkUp() if (cachedFileUrl) { modalContent.html(''); modal.modal(); var url = cachedFileUrl; modalContent.append($('').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($('

').text('生成中,請稍候')); extractFiles(blob, function(files){ getGIF(files, delays, size, function(blob){ modalContent.html(''); var url = cachedFileUrl =getUrl(blob); modalContent.append($('').attr('src', url)); }, function(p) { modalContent.html(''); modalContent.append($('

').text('生成中,請稍候')); modalContent.append($('

').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($('').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($('

').text('生成中,請稍候')); extractFiles(blob, function(files){ getGIF(files, delays, size, function(blob){ modalContent.html(''); var url = cachedFileUrl =getUrl(blob); modalContent.append($('').attr('src', url)); }, function(p) { modalContent.html(''); modalContent.append($('

').text('生成中,請稍候')); modalContent.append($('

').text( Math.floor(p * 100) + ' %')); }); }); }); } return showGif; }()) GM_registerMenuCommand( '下載檔案!(小圖)', downloadSmall); GM_registerMenuCommand( '下載檔案!(大圖)', downloadFullScreen); GM_registerMenuCommand( '檢視個別檔案!(大圖)', showPictures); GM_registerMenuCommand( '存為gif!(小圖)', showSmallGif); GM_registerMenuCommand( '存為gif!(大圖)', showGif);