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