NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Enhance SauceNao // @version 2017.10.04 // @author hybridsong // @icon https://secure.gravatar.com/avatar/187e65a2ca69335f7483a4f19812050a // @match *://saucenao.com/* // @run-at document-end // @grant GM_listValues // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // ==/UserScript== /* * Enhance SauceNao * by hybridsong */ /* FileSaver.js * A saveAs() FileSaver implementation. * 1.3.2 * 2016-06-16 18:25:19 * * By Eli Grey, http://eligrey.com * License: MIT * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md */ /*global self */ /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */ /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ var saveAs = saveAs || (function(view) { "use strict"; // IE <10 is explicitly unsupported if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) { return; } var doc = view.document // only get URL when necessary in case Blob.js hasn't overridden it yet , get_URL = function() { return view.URL || view.webkitURL || view; } , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a") , can_use_save_link = "download" in save_link , click = function(node) { var event = new MouseEvent("click"); node.dispatchEvent(event); } , is_safari = /constructor/i.test(view.HTMLElement) || view.safari , is_chrome_ios =/CriOS\/[\d]+/.test(navigator.userAgent) , throw_outside = function(ex) { (view.setImmediate || view.setTimeout)(function() { throw ex; }, 0); } , force_saveable_type = "application/octet-stream" // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to , arbitrary_revoke_timeout = 1000 * 40 // in ms , revoke = function(file) { var revoker = function() { if (typeof file === "string") { // file is an object URL get_URL().revokeObjectURL(file); } else { // file is a File file.remove(); } }; setTimeout(revoker, arbitrary_revoke_timeout); } , dispatch = function(filesaver, event_types, event) { event_types = [].concat(event_types); var i = event_types.length; while (i--) { var listener = filesaver["on" + event_types[i]]; if (typeof listener === "function") { try { listener.call(filesaver, event || filesaver); } catch (ex) { throw_outside(ex); } } } } , auto_bom = function(blob) { // prepend BOM for UTF-8 XML and text/* types (including HTML) // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type}); } return blob; } , FileSaver = function(blob, name, no_auto_bom) { if (!no_auto_bom) { blob = auto_bom(blob); } // First try a.download, then web filesystem, then object URLs var filesaver = this , type = blob.type , force = type === force_saveable_type , object_url , dispatch_all = function() { dispatch(filesaver, "writestart progress write writeend".split(" ")); } // on any filesys errors revert to saving with object URLs , fs_error = function() { if ((is_chrome_ios || (force && is_safari)) && view.FileReader) { // Safari doesn't allow downloading of blob urls var reader = new FileReader(); reader.onloadend = function() { var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;'); var popup = view.open(url, '_blank'); if(!popup) view.location.href = url; url=undefined; // release reference before dispatching filesaver.readyState = filesaver.DONE; dispatch_all(); }; reader.readAsDataURL(blob); filesaver.readyState = filesaver.INIT; return; } // don't create more object URLs than needed if (!object_url) { object_url = get_URL().createObjectURL(blob); } if (force) { view.location.href = object_url; } else { var opened = view.open(object_url, "_blank"); if (!opened) { // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html view.location.href = object_url; } } filesaver.readyState = filesaver.DONE; dispatch_all(); revoke(object_url); } ; filesaver.readyState = filesaver.INIT; if (can_use_save_link) { object_url = get_URL().createObjectURL(blob); setTimeout(function() { save_link.href = object_url; save_link.download = name; click(save_link); dispatch_all(); revoke(object_url); filesaver.readyState = filesaver.DONE; }); return; } fs_error(); } , FS_proto = FileSaver.prototype , saveAs = function(blob, name, no_auto_bom) { return new FileSaver(blob, name || blob.name || "download", no_auto_bom); } ; // IE 10+ (native saveAs) if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) { return function(blob, name, no_auto_bom) { name = name || blob.name || "download"; if (!no_auto_bom) { blob = auto_bom(blob); } return navigator.msSaveOrOpenBlob(blob, name); }; } FS_proto.abort = function(){}; FS_proto.readyState = FS_proto.INIT = 0; FS_proto.WRITING = 1; FS_proto.DONE = 2; FS_proto.error = FS_proto.onwritestart = FS_proto.onprogress = FS_proto.onwrite = FS_proto.onabort = FS_proto.onerror = FS_proto.onwriteend = null; return saveAs; }( typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content )); // `self` is undefined in Firefox for Android content script context // while `this` is nsIContentFrameMessageManager // with an attribute `content` that corresponds to the window if (typeof module !== "undefined" && module.exports) { module.exports.saveAs = saveAs; } else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) { define("FileSaver.js", function() { return saveAs; }); } function rand (min, max) { // discuss at: http://locutus.io/php/rand/ // original by: Leslie Hoare // bugfixed by: Onno Marsman (https://twitter.com/onnomarsman) // note 1: See the commented out code below for a version which // note 1: will work with our experimental (though probably unnecessary) // note 1: srand() function) // example 1: rand(1, 1) // returns 1: 1 var argc = arguments.length if (argc === 0) { min = 0 max = 2147483647 } else if (argc === 1) { throw new Error('Warning: rand() expects exactly 2 parameters, 1 given') } return Math.floor(Math.random() * (max - min + 1)) + min } let main_style = "\ #ES-images { position: fixed; top: 0; right: 5px; width: 125px; height: 100%; overflow-y: auto; z-index:1; display:none }\ #ES-total { position: fixed; bottom: 5; right: 50px; background: #1d1d1d; padding: 0 5px; opacity: 0.5; z-index:1 }\ #ES-namefix { position: fixed; bottom: 25; right: 50px; background: #1d1d1d; padding: 0 5px; opacity: 0.5; z-index:1 }\ #ES-results { position: absolute; top:0; height: 100%; background: #1d1d1d; display: none }\ " let head = document.querySelector('head'), style = document.createElement("style") style.type = "text/css" style.append(main_style) head.append(style) 'use strict' let doc_url = new URL(document.location.href), NF_useboard = "danbooru", NF_delimiter = "_", NF_debug = false const hosts = [ 'www.pixiv.net', 'danbooru.donmai.us', 'chan.sankakucomplex.com', 'yande.re', ] function createObjectURL(object) { return (window.URL) ? window.URL.createObjectURL(object) : window.webkitURL.createObjectURL(object); } function revokeObjectURL(url) { return (window.URL) ? window.URL.revokeObjectURL(url) : window.webkitURL.revokeObjectURL(url); } function filename (str) { var name = new String(str).substring(str.lastIndexOf('/') + 1); return name; } function extname (path) { var filename = path.split('\\').pop().split('/').pop(); return filename.substr(( Math.max(0, filename.lastIndexOf(".")) || Infinity) + 1); } if (doc_url.pathname == '/') { // root let base = document.createElement("base") base.target = '_self' document.head.append(base) let mount_scripts = () => { if (!document.getElementById('mounted_scripts')) { let scripts = results_container.querySelectorAll('script') for (let i=0; i<scripts.length; i++) { let script = document.createElement("script") script.textContent = scripts[i].innerText script.id = 'mounted_scripts' document.body.append(script) scripts[i].remove() } downloadJSAtOnload() // load jQuery } } let adjust_results_page = () => { base.target = '_blank' results_container.querySelector('#Search').remove() } const get_link = (host) => { let links = results_container.querySelectorAll('\ .result:not(.hidden) .resultmiscinfo a,\ .result:not(.hidden) .resultcontentcolumn>a\ ') for (let link of links) { let m = link.href.match(/\:\/\/(.+?)\//) if (m && m[1]) { if (host == m[1]) return link } } } let xhr = new XMLHttpRequest(), get_results = (file, img_btn, next) => { //return formData = new FormData() formData.append('file', file) formData.append('url', '') formData.append('frame', 1) formData.append('hide', 0) formData.append('database', 999) xhr.open("POST", '/search.php') xhr.onreadystatechange = function() { if(xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200) { if (!img_btn.searched) { img_btn.searched = 1 total_searched++ total.update() } img_btn.style.opacity = 0.5 results.style.display = 'block' GM_setValue(file.name, true) results_container.innerHTML = xhr.response mount_scripts() adjust_results_page() if (next) next() } messages.innerText = `${xhr.statusText} (${xhr.status})` } xhr.send(formData) } let total_searched = 0, total_images = 0 let image_list = null, load_images = (files) => { images.style.display = 'block' total_images = 0 total_searched = 0 image_list = [] next_state.i = 0 if(files.length) { for(let i=0; i<files.length; i++) { var src = createObjectURL(files[i]) var image = new Image() image.src = src image.style.width = 100 image.searched = 0 if (GM_getValue(files[i].name)) { total_searched++ total.update() image.searched = 1 image.style.opacity = 0.5 } image.onclick = function () { next_state.i = i+1 this.style.opacity = 0.9 get_results(files[i], this) } images.append(image) total_images++ total.update() image_list[i] = { file: files[i], image:image } } } } let namefix_state = { i: 0, save: function (id, file) { if (id) saveAs(file, `${file.name.split('.')[0]}${NF_delimiter}${NF_useboard}${NF_delimiter}${id}.${extname(file.name)}`) else saveAs(file, `${file.name.split('.')[0]}${NF_delimiter}search${NF_delimiter}failed.${extname(file.name)}`) }, proc: function () { if (this.i == image_list.length) return let { file, image } = image_list[this.i] if (!image.searched) { image.style.opacity = 0.9 get_results(file, image, () => { let links = results_container.querySelectorAll('.result:not(.hidden) .resultmiscinfo a'), urls = [], id = null links.forEach((link, i) => { let m = link.href.match(/\:\/\/(.+?)\//) if (m && m[1]) { let host = m[1] if (host == 'danbooru.donmai.us') urls.push(link.href) } }) if (urls.length) { let m = urls[0].match(/\/(\d+)/) id = m ? m[1] : null } if (NF_debug) console.log(urls, links, id) this.save(id, file) setTimeout(() => this.proc(), 2000) }) } else { if (this.i < image_list.length) setTimeout(() => this.proc(), 0) } this.i++ } } let next_state = { i: 0, loading: 0, load: function () { if (this.i == image_list.length || this.loading) return let { file, image } = image_list[this.i] images.scrollTop = image.offsetTop image.style.opacity = 0.9 this.loading = 1 get_results(file, image, () => { this.i++ this.loading = 0 }) } } let select_button = document.createElement('input') select_button.multiple = true select_button.type = 'file' select_button.style.width = '80px' select_button.onchange = function () { load_images(this.files) } document.querySelector('form').insertBefore(select_button, document.querySelector('form p')) document.querySelector('#Search').style.width = '450px' let images = document.createElement('div') images.id = "ES-images" let total = document.createElement('div') total.id = "ES-total" total.update = () => { total.innerText = `${total_images}/${total_searched}` } total.onclick = () => { if (confirm("Clean")) clean() } images.append(total) let namefix = document.createElement('div') namefix.id = "ES-namefix" namefix.innerText = 'NF' namefix.onclick = () => { if (confirm("Name Fix")) { namefix.onclick = null namefix_state.proc() } } images.append(namefix) document.body.append(images) let results = document.createElement('div') results.id = 'ES-results' let widget = document.createElement('div') widget.style.position = 'fixed' widget.style.bottom = 0 widget.style.right = '135px' widget.style.color = '#666' results.append(widget) let messages = document.createElement('div') widget.append(messages) let back = document.createElement('div') back.append('Back') back.onclick = () => { results.style.display = 'none' } widget.append(back) let results_container = document.createElement('div') results.append(results_container) document.body.append(results) window.onbeforeunload = () => { if (total_searched == total_images && image_list.length) clean() } let clean = () => { let vals = GM_listValues() for (let i=0; i<vals.length; i++) GM_deleteValue(vals[i]) } document.onpaste = e => { load_images(e.clipboardData.files) next_state.load() } document.onkeydown = (e) => { let key = String.fromCharCode(e.keyCode) switch (key) { case 'N': next_state.load() break } if (key.match(/\d/)) { const link = get_link(hosts[key-1]) if (link) link.click() } } } // root