Last active
September 10, 2022 14:15
-
-
Save BrokenEagle/669ac9d84bb31c607451927b2623945c to your computer and use it in GitHub Desktop.
Client-side option for downloading images and uploading them to Danbooru.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name ProxyUploader | |
// @namespace https://gist.github.com/BrokenEagle | |
// @version 4.0 | |
// @description Shows the tag changes made since the original upload. | |
// @source https://danbooru.donmai.us/users/23799 | |
// @author BrokenEagle | |
// @match *://*.donmai.us/uploads/* | |
// @exclude /^https?://\w+\.donmai\.us/.*\.(xml|json|atom)(\?|$)/ | |
// @grant GM.xmlHttpRequest | |
// @run-at document-end | |
// @downloadURL https://gist.github.com/BrokenEagle/669ac9d84bb31c607451927b2623945c/raw/ProxyUploader.user.js | |
// @updateURL https://gist.github.com/BrokenEagle/669ac9d84bb31c607451927b2623945c/raw/ProxyUploader.user.js | |
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js | |
// @require https://cdnjs.cloudflare.com/ajax/libs/localforage/1.10.0/localforage.min.js | |
// @require https://cdn.jsdelivr.net/npm/xregexp@5.1.0/xregexp-all.min.js | |
// @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/core.min.js | |
// @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/lib-typedarrays.js | |
// @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/md5.min.js | |
// @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20220515/lib/module.js | |
// @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20220515/lib/debug.js | |
// @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20220515/lib/utility.js | |
// @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20220515/lib/validate.js | |
// @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20220515/lib/storage.js | |
// @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20220515/lib/notice.js | |
// @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20220515/lib/network.js | |
// @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20220515/lib/danbooru.js | |
// @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20220515/lib/load.js | |
// @connect donmai.us | |
// @connect pximg.net | |
// @connect pixiv.net | |
// ==/UserScript== | |
// eslint-disable-next-line no-redeclare | |
/* global $ _$ XRegExp localforage Danbooru JSPLib CryptoJS GM unsafeWindow */ | |
/****Global variables****/ | |
//Variables for load.js | |
const PROGRAM_LOAD_REQUIRED_VARIABLES = ['unsafeWindow.jQuery', 'window.Danbooru']; | |
const PROGRAM_LOAD_OPTIONAL_SELECTORS = ['#c-uploads #a-new', '#c-uploads #a-show']; | |
//Program name constants | |
const PROGRAM_NAME = 'ProxyUploader'; | |
const PROGRAM_CLICK = 'click.pu'; | |
//Main program variable | |
const PU = {}; | |
//CSS constants | |
const PROGRAM_CSS = ` | |
#pu-menu { | |
text-align: center; | |
} | |
#pu-menu > div { | |
display: inline-block; | |
border: 1px dotted #CCC; | |
width: 100em; | |
padding: 1em; | |
margin: 1em; | |
} | |
#pu-display { | |
margin-bottom: 1em; | |
} | |
.pu-image-preview { | |
display: inline-block; | |
width: 304px; | |
height: 300px; | |
} | |
.pu-image-preview img { | |
max-width: 300px; | |
max-height: 300px; | |
margin-bottom: 0.25em; | |
} | |
.pu-image-download-progress, | |
.pu-image-upload-progress { | |
height: 1em; | |
margin: 0 1em; | |
} | |
#pu-download-progress .ui-progressbar-value, | |
.pu-image-download-progress .ui-progressbar-value { | |
background-color: lightblue; | |
} | |
#pu-download-progress .ui-progressbar-complete, | |
.pu-image-download-progress .ui-progressbar-complete { | |
background-color: limegreen; | |
} | |
#pu-upload-progress .ui-progressbar-value, | |
.pu-image-upload-progress .ui-progressbar-value { | |
background-color: darkgrey; | |
} | |
#pu-upload-progress .ui-progressbar-complete, | |
.pu-image-upload-progress .ui-progressbar-complete { | |
background-color: green; | |
} | |
.pu-image-download-progress.pu-progress-warning .ui-progressbar-complete { | |
background-color: orange; | |
} | |
.pu-image-download-progress.pu-progress-error .ui-progressbar-complete { | |
background-color: red; | |
} | |
#pu-progress { | |
margin-bottom: 1em; | |
} | |
.pu-progressbar { | |
width: 80%; | |
height: 30px; | |
margin: 0 auto; | |
} | |
.pu-progress-text { | |
font-size: 20px; | |
} | |
#pu-preview img { | |
max-width: 100%; | |
max-height: 80vh; | |
} | |
#pu-control { | |
display: inline-block; | |
background-color: teal; | |
border: 1px solid grey; | |
border-radius: 25px; | |
font-size: 24px; | |
margin-bottom: 0.5em; | |
} | |
#pu-control:hover { | |
filter: brightness(1.2); | |
} | |
#pu-control a { | |
color: white; | |
padding: 0.5em 4em; | |
padding: -0.5em; | |
} | |
#pu-proxy-url { | |
width: 80em; | |
}`; | |
//HTML constants | |
const PROXY_CONTROLS = ` | |
<div id="pu-menu"> | |
<div> | |
<div id="pu-display"> | |
</div> | |
<div id="pu-control"> | |
<a href="#">Proxy Upload</a> | |
</div> | |
<div class="pu-input input"> | |
<input type="text" name="proxy_url" id="pu-proxy-url"> | |
</div> | |
</div> | |
</div>`; | |
const SINGLE_PROXY_DISPLAY = ` | |
<div id="pu-single-display"> | |
<div id="pu-progress" style="display: none;"> | |
<h2>Downloading</h2> | |
<div id="pu-download-progress" class="pu-progressbar"> | |
<div class="pu-progress-text">0%</div> | |
</div> | |
<h2>Uploading</h2> | |
<div id="pu-upload-progress" class="pu-progressbar"> | |
<div class="pu-progress-text">0%</div> | |
</div> | |
</div> | |
<div id="pu-preview" style="display: none;"><img></div> | |
</div>`; | |
const RELATED_TAG_COLUMN = `<div class="source-related-tags-columns"> | |
<div class="tag-column translated-tags-related-tags-column" style="width: 18em;"> | |
<h3 class="flex items-center space-x-1"> | |
<input type="checkbox" class="invisible"> | |
<span>Translated Tags</span> | |
</h3> | |
<ul class="tag-list simple-tag-list"> | |
%s | |
</ul> | |
</div> | |
</div>`; | |
const TAG_SELECTOR = ` | |
<li class="flex items-center space-x-1"> | |
<input type="checkbox" tabindex="-1"> | |
<a class="search-tag tag-type-%CATEGORY%" data-tag-name="%NAME%" href="/posts?tags=%ENCODED_NAME%">%NAME%</a> | |
</li>`; | |
//Regexes | |
const PX_IMAGE_RG = XRegExp.tag('x')` | |
^https?://[^.]+\.pximg\.net # Hostname | |
(?:/c/\w+)? # Size 1 | |
/(?:img-original|img-master|custom-thumb) # Path | |
/img | |
/(?<date>\d{4}/\d{2}/\d{2}/\d{2}/\d{2}/\d{2}) # Date | |
/(?<id>\d+)_ # ID | |
p(?<order>\d+) # Order | |
(?:_(?:master|square|custom)1200)? # Size 2 | |
\.(?<ext>jpg|png|gif|mp4|zip) # Extension | |
`; | |
const PX_PAGE_RG = XRegExp.tag('x')` | |
^https?://www\.pixiv\.net # Hostname | |
(?:/en)? # Size | |
/(?:artworks) # Path | |
/(?<id>\d+) # ID | |
`; | |
const FILENAME_RG = XRegExp.tag('x')` | |
^file:// | |
(?<id>\d+) | |
- | |
(?<name> | |
(?<stem>[^.]+) | |
\. | |
(?<ext>jpg|png) | |
)$ | |
`; | |
const COMMON_HASHTAG_REGEXES = [ | |
/生誕祭\d*$/, | |
/誕生祭\d*$/, | |
/版もうひとつの深夜の真剣お絵描き60分一本勝負(?:_\d+$|$)/, | |
/版深夜の真剣お絵描き60分一本勝負(?:_\d+$|$)/, | |
/深夜の真剣お絵描き60分一本勝負(?:_\d+$|$)/, | |
/版深夜のお絵描き60分一本勝負(?:_\d+$|$)/, | |
/版真剣お絵描き60分一本勝負(?:_\d+$|$)/, | |
/版真剣お絵描き60分一本勝(?:_\d+$|$)/, | |
/版お絵描き60分一本勝負(?:_\d+$|$)/, | |
]; | |
//Site constants | |
let SITE_HEADERS = { | |
'pximg.net': { | |
Referer: 'https://www.pixiv.net', | |
}, | |
'sinaimg.cn': { | |
Referer: 'https://weibo.com', | |
}, | |
'yande.re': { | |
Referer: 'yande.re', | |
}, | |
'konachan.com': { | |
Referer: 'konachan.com', | |
}, | |
}; | |
const HANDLED_IMAGES = [{ | |
regex: PX_IMAGE_RG, | |
format: `https://i.pximg.net/img-original/img/%date%/%id%_p%order%.%ext%`, | |
handler: (image_match) => GetPixivData(image_match), | |
}]; | |
const HANDLED_PAGES = [{ | |
regex: PX_PAGE_RG, | |
format: `https://www.pixiv.net/en/artworks/%id%`, | |
handler: (page_match) => GetPixivData(page_match), | |
}]; | |
//Other constants | |
const MIME_TYPES = { | |
jpg: 'image/jpeg', | |
gif: 'image/gif', | |
png: 'image/png', | |
}; | |
const API_STORAGE = localforage.createInstance({ | |
name: 'API storage', | |
driver: [ | |
localforage.INDEXEDDB, | |
] | |
}); | |
/****Functions****/ | |
//Library functions | |
JSPLib.utility.fixedEncodeURIComponent = function (str) { | |
return encodeURIComponent(str).replace(/[!'()*]/g, function(c) { | |
return '%' + c.charCodeAt(0).toString(16); | |
}); | |
}; | |
JSPLib.load._isVariableDefined = function(variable_name) { | |
let variable_hierarchy = variable_name.split('.'); | |
let window_name = null; | |
if (variable_hierarchy[0] === 'window' || variable_hierarchy[0] === 'unsafeWindow') { | |
window_name = variable_hierarchy.shift(); | |
} | |
let curr_obj = (window_name === 'unsafeWindow' ? unsafeWindow : JSPLib._window); | |
for (let i = 0;i < variable_hierarchy.length;i++) { | |
if (!(variable_hierarchy[i] in curr_obj)) { | |
return false; | |
} | |
curr_obj = curr_obj[variable_hierarchy[i]]; | |
if ((typeof curr_obj !== 'object' || curr_obj == null) && (i < (variable_hierarchy.length - 1))) { | |
return false; | |
} | |
} | |
return true; | |
}; | |
JSPLib.network.ajax = function (self, url, options) { | |
if (!JSPLib._jquery_installed) { | |
return Promise.resolve(null); | |
} | |
if (JSPLib.validate.isHash(url)) { | |
options = url; | |
url = null; | |
} else if (JSPLib.validate.isString(url)) { | |
options ||= {}; | |
} else { | |
JSPLib.utility.throwError("JSPLib: Invalid ajax settings."); | |
} | |
self.debug('logLevel', {url, options}, JSPLib.debug.VERBOSE); | |
if (url) { | |
return JSPLib._jQuery.ajax(url, options); | |
} else { | |
return JSPLib._jQuery.ajax(options); | |
} | |
}; | |
JSPLib.network.GM_XHR = function () { | |
const open_params = ['type', 'url', 'async', 'username', 'password']; | |
Object.assign(this, {headers: {}}, ...open_params.concat(['status', 'readyState']).map((key) => ({[key]: null}))); | |
this.abort = function() { | |
this.readyState = 0; | |
}; | |
this.getAllResponseHeaders = function() { | |
return (this.readyState !== 4 ? "" : this.responseHeaders); | |
}; | |
this.getResponseHeader = function(name) { | |
var regexp = new RegExp('^' + name + ': (.*)$', 'im'); | |
var match = regexp.exec(this.responseHeaders); | |
return (match ? match[1] : ''); | |
}; | |
this.open = function(...outerargs) { | |
let context = this; | |
open_params.forEach((arg, i) => { | |
context[arg] = outerargs[i] || null; | |
}); | |
this.readyState = 1; | |
}; | |
this.setRequestHeader = function(name, value) { | |
this.headers[name] = value; | |
}; | |
this.onresponse = function (handler) { | |
let context = this; | |
return function (resp) { | |
['readyState', 'responseHeaders', 'responseText', 'response', 'status', 'statusText', 'lengthComputable', 'loaded', 'total'].forEach((key) => { | |
context[key] = resp[key]; | |
}); | |
if (context[handler]) { | |
context[handler](context); | |
} else if (context.onreadystatechange) { | |
context.onreadystatechange(); | |
} | |
}; | |
}; | |
this.send = function(data) { | |
const gm_params = ['url', 'headers', 'data', 'responseType', 'cookie', 'binary', 'nocache', 'revalidate', 'timeout', 'context', 'overrideMimeType', 'anonymous', 'fetch', 'password']; | |
this.data = !this.anonymous || data ? data : undefined; | |
let standard_params = { | |
method: this.type, | |
//jQuery uses username, Tampermonkey uses user | |
user: this.username, | |
onload: this.onresponse("onload"), | |
onerror: this.onresponse("onerror"), | |
onprogress: this.onresponse("onprogress"), | |
}; | |
let send_data = Object.assign(standard_params, ...gm_params.map((key) => (this[key] !== undefined ? {[key]: this[key]} : {}))); | |
return GM.xmlHttpRequest(send_data); | |
}; | |
}; | |
JSPLib.debug.addModuleLogs('network', ['ajax']); | |
//Auxiliary functions | |
async function GetFullImageURL(image_url) { | |
for (let i = 0; i < HANDLED_IMAGES.length; i++) { | |
let config = HANDLED_IMAGES[i]; | |
let match = XRegExp.exec(image_url, config.regex); | |
if (!match) continue; | |
let image_data = await config.handler(match); | |
return image_data; | |
} | |
return null; | |
} | |
async function GetFullImageURLs(page_url) { | |
for (let i = 0; i < HANDLED_PAGES.length; i++) { | |
let config = HANDLED_PAGES[i]; | |
let match = XRegExp.exec(page_url, config.regex); | |
if (!match) continue; | |
let image_data = await config.handler(match); | |
return image_data; | |
} | |
return null; | |
} | |
function GetRelatedTagData(tags, profile_urls) { | |
let promise_array = []; | |
promise_array.push(QueryArtistUrlData(profile_urls)); | |
if (tags.length) { | |
promise_array.push(QueryWikiData(tags)); | |
} else { | |
promise_array.push(Promise.resolve([])); | |
} | |
let valid_tags = tags.filter((tag) => tag.match(/^[\u0020-\u0024\u0026-\u0029\u002B\u002D-\u007F]+$/)).map((tag) => tag.toLowerCase()); | |
if (valid_tags.length) { | |
promise_array.push(QueryTagData(valid_tags)); | |
} else { | |
promise_array.push(Promise.resolve([])); | |
} | |
return promise_array; | |
} | |
function GetBufferChecksum(blob) { | |
return new Promise((resolve)=>{ | |
let fileReader = new FileReader(); | |
fileReader.onload = function(event){ | |
let arrayBuffer = event.target.result; | |
let wordArray = CryptoJS.lib.WordArray.create(arrayBuffer); | |
let md5 = CryptoJS.MD5(wordArray).toString(); | |
resolve(md5); | |
}; | |
fileReader.readAsArrayBuffer(blob); | |
}); | |
} | |
function InitializeRelatedTags(tags, profile_urls) { | |
Promise.all(GetRelatedTagData(tags, profile_urls)).then(([artist_urls, wikis, tags])=>{ | |
let artist_names = JSPLib.utility.arrayUnique(artist_urls.map((artist_url) => artist_url.artist.name)); | |
let other_tags = {}; | |
wikis.forEach((wiki)=>{ | |
other_tags[wiki.title] = (wiki.tag && wiki.tag.category) || 0; | |
}); | |
tags.forEach((tag)=>{ | |
other_tags[tag.name] = tag.category; | |
}); | |
$('#related-tags-container .source-related-tags-columns').replaceWith(RenderTranslatedTagsColumn(artist_names, other_tags)); | |
Danbooru.RelatedTag.update_selected(); | |
}); | |
} | |
function ErrorHandler(error, func_name, image_url, message) { | |
let process_error = JSPLib.network.processError(error, `ProxyImageDownload-${func_name}`); | |
let error_key = `ProxyImageDownload-${func_name}-${image_url}`; | |
JSPLib.network.logError(error_key, process_error); | |
JSPLib.network.notifyError(process_error, message); | |
} | |
function PublicSubmitRequest(...args) { | |
window.jQuery = PU.public_jquery; | |
let data = JSPLib.danbooru.submitRequest(...args); | |
window.jQuery = PU.private_jquery; | |
return data; | |
} | |
//Render functions | |
function RenderBatchDisplay(image_data) { | |
return image_data.map((url_data)=>(` | |
<article class="pu-image-preview" data-id="${url_data.id}" data-order="${url_data.order}"> | |
<a> | |
<img></img> | |
</a> | |
<div class="pu-image-download-progress"></div> | |
<div class="pu-image-upload-progress"></div> | |
</article>`)).join(""); | |
} | |
function RenderTagSelector(name, category) { | |
return JSPLib.utility.regexReplace(TAG_SELECTOR, { | |
NAME: name, | |
ENCODED_NAME: JSPLib.utility.fixedEncodeURIComponent(name), | |
CATEGORY: category, | |
}); | |
} | |
function RenderTranslatedTagsColumn(artist_names, other_tags) { | |
let html = ''; | |
artist_names.forEach((name)=>{ | |
html += RenderTagSelector(name, '1'); | |
}); | |
for (let name in other_tags) { | |
let category = String(other_tags[name]); | |
html += RenderTagSelector(name, category); | |
} | |
return JSPLib.utility.sprintf(RELATED_TAG_COLUMN, html); | |
} | |
//Site functions | |
async function GetPixivData(match) { | |
let illust_promise = JSPLib.network.getJSON(`https://www.pixiv.net/ajax/illust/${match.groups.id}`) | |
.then((data)=>data, (resp)=>resp.responseJSON); | |
let resp = await illust_promise; | |
if (resp.error) { | |
console.warn("Pixiv API illust error:", resp.message); | |
return null; | |
} | |
let artwork_data = resp.body; | |
let illust_key = 'pixiv-illust-' + match.groups.id; | |
JSPLib.storage.saveData(illust_key, artwork_data, API_STORAGE); | |
PU.api_keys.push(illust_key); | |
let image_order = parseInt(match.groups.order); | |
var full_urls, preview_urls; | |
if (artwork_data.pageCount > 1 && (Number.isNaN(image_order) || image_order > 0)) { | |
let pages_promise = JSPLib.network.getJSON(`https://www.pixiv.net/ajax/illust/${match.groups.id}/pages`) | |
.then((data)=>data, (resp)=>resp.responseJSON); | |
let resp = await pages_promise; | |
if (resp.error) { | |
console.warn("Pixiv API illust error:", resp.message); | |
return null; | |
} | |
let pages_data = resp.body; | |
let pages_key = 'pixiv-pages-' + match.groups.id; | |
JSPLib.storage.saveData(pages_key, pages_data, API_STORAGE); | |
PU.api_keys.push(illust_key); | |
full_urls = pages_data.map((url_data) => url_data.urls.original); | |
preview_urls = pages_data.map((url_data) => url_data.urls.small); | |
} else { | |
full_urls = [artwork_data.urls.original]; | |
preview_urls = [artwork_data.urls.small]; | |
} | |
if (match.groups.order) { | |
return {full_url: full_urls[image_order], preview_url: preview_urls[image_order], order: 0, id: match.groups.id}; | |
} else { | |
return full_urls.map((full_url, i) => ({full_url, preview_url: preview_urls[i], order: i, id: match.groups.id})); | |
} | |
} | |
async function LoadPixivData(api_keys, match) { | |
let illust_key = api_keys.find((key) => key.startsWith('pixiv-illust')); | |
if (!illust_key) return; | |
let illust_data = await JSPLib.storage.retrieveData(illust_key, false, API_STORAGE); | |
if (!illust_data) return; | |
let index = parseInt(match.groups.stem.match(/\d+$/)[0]); | |
var source = ""; | |
if (index === 0) { | |
source = illust_data.urls.original; | |
} else { | |
let pages_key = api_keys.find((key) => key.startsWith('pixiv-pages')); | |
if (pages_key) { | |
let pages_data = await JSPLib.storage.retrieveData(pages_key, false, API_STORAGE); | |
if (pages_data) { | |
source = pages_data[index].urls.original; | |
} | |
} | |
} | |
setTimeout(()=>{ | |
$('#post_source').val(source); | |
}, JSPLib.utility.one_second * 1); | |
let title = illust_data.title; | |
let description = (!illust_data.description ? "" : illust_data?.extraData?.meta?.twitter?.description || ""); | |
if (title || description) { | |
$('#post_artist_commentary_title').val(title); | |
$('#post_artist_commentary_description').val(description); | |
Danbooru.Upload.toggle_commentary(); | |
} | |
let tags = JSPLib.utility.getObjectAttributes(illust_data.tags.tags, 'tag'); | |
let profile_urls = ['https://www.pixiv.net/users/' + illust_data.userId]; | |
InitializeRelatedTags(tags, profile_urls); | |
} | |
//Network functions | |
async function ProxyImageToLocal(image_url, show_progress) { | |
let [, ext] = JSPLib.utility.getFileURLNameExt(image_url); | |
let mime_type = MIME_TYPES[ext]; | |
let domain_name = JSPLib.utility.getDomainName(image_url, 2); | |
let headers = SITE_HEADERS[domain_name]; | |
let ajax_options = {}; | |
if (headers) { | |
const beforeSend = function (request) { | |
for (let key in headers) { | |
request.setRequestHeader(key, headers[key]); | |
} | |
}; | |
ajax_options.beforeSend = beforeSend; | |
} | |
var xhr_options; | |
if (show_progress) { | |
xhr_options = { | |
onprogress: show_progress, | |
}; | |
} | |
for (let i = 0; i < 3; i++) { | |
JSPLib.debug.debuglog('ProxyImageToLocal-download-' + i, image_url); | |
let blob = await JSPLib.network.getData(image_url, {ajax_options, xhr_options}); | |
if (blob) { | |
JSPLib.debug.debuglog('ProxyImageToLocal-data-' + i, blob); | |
let image_blob = blob.slice(0, blob.size, mime_type); | |
return image_blob; | |
} | |
ajax_options.cache = false; | |
JSPLib.debug.debugwarn('ProxyImageToLocal-error-' + i, image_url); | |
await JSPLib.utility.sleep(JSPLib.utility.one_second); | |
} | |
} | |
function UploadImageToDanbooru(image_blob, filename, show_progress) { | |
let uploadFormData = new FormData(); | |
uploadFormData.append('upload[files][0]', image_blob, PU.unique_id + '-' + filename); | |
const ajax_options = { | |
url : '/uploads.json', | |
type : "POST", | |
enctype: 'multipart/form-data', | |
data : uploadFormData, | |
cache : false, | |
contentType : false, | |
processData : false, | |
}; | |
if (show_progress) { | |
ajax_options.xhr = function() { | |
let xhr = new window.XMLHttpRequest(); | |
xhr.upload.addEventListener("progress", show_progress, false); | |
return xhr; | |
}; | |
} | |
return _$.ajax(ajax_options); | |
} | |
function QueryArtistUrlData(profile_urls) { | |
let artist_query = { | |
search: { | |
url_lower_array: profile_urls, | |
}, | |
only: 'artist[name]', | |
}; | |
return PublicSubmitRequest('artist_urls', artist_query); | |
} | |
function QueryWikiData(tags) { | |
// Add the tags as they are | |
let other_names = new Set(tags); | |
// Add the tags with common appelations discarded | |
tags.forEach((other_name)=>{ | |
for (let regexp of COMMON_HASHTAG_REGEXES) { | |
let normalized_name = other_name.replace(regexp, ""); | |
if (normalized_name != other_name) { | |
other_names.add(normalized_name); | |
break; | |
} | |
} | |
}); | |
let wiki_query = { | |
search: { | |
other_names_include_any_lower_array: [...other_names], | |
is_deleted: false | |
}, | |
only: 'title,other_names,tag[category]', | |
}; | |
return PublicSubmitRequest('wiki_pages', wiki_query); | |
} | |
function QueryTagData(tags) { | |
let tag_query = { | |
search: { | |
name_lower_comma: tags.join(','), | |
}, | |
only: 'name,category,post_count', | |
}; | |
return PublicSubmitRequest('tags', tag_query); | |
} | |
// Main execution functions | |
async function ProxyImageDownloadCheck() { | |
let url = $('#pu-proxy-url').val() || PU.params.proxy_url || $('#upload_source').val() || PU.params.url; | |
if (!url) { | |
JSPLib.notice.error("No URL found."); | |
return; | |
} | |
let image_data = await GetFullImageURL(url); | |
if (image_data) { | |
return ProxyImageDownloadSingle(image_data); | |
} | |
image_data = await GetFullImageURLs(url); | |
if (image_data?.length === 1) { | |
return ProxyImageDownloadSingle(image_data[0]); | |
} else if (image_data?.length > 1) { | |
return ProxyImageDownloadBatch(image_data); | |
} | |
JSPLib.notice.notice("URL not valid."); | |
} | |
function ProxyImageDownloadSingle(image_data) { | |
function imageProgress($progressbar) { | |
return function (event) { | |
if (event.lengthComputable) { | |
let percent_complete = parseInt(100 * (event.loaded / event.total)); | |
$progressbar.progressbar('option', 'value', percent_complete); | |
$progressbar.find('.pu-progress-text').text(Math.round(percent_complete) + '%'); | |
} | |
}; | |
} | |
_$('#pu-display').html(SINGLE_PROXY_DISPLAY); | |
let [name, ext] = JSPLib.utility.getFileURLNameExt(image_data.full_url); | |
let filename = name + '.' + ext; | |
PU.$download_progress = _$('#pu-download-progress').progressbar({value: 0, max: 100}); | |
PU.$upload_progress = _$('#pu-upload-progress').progressbar({value: 0, max: 100}); | |
_$('#pu-progress').show(); | |
ProxyImageToLocal(image_data.full_url, imageProgress(PU.$download_progress)).then( | |
async (image_blob)=>{ | |
PU.$download_progress.progressbar('option', 'value', 100); | |
PU.$download_progress.find('.pu-progress-text').text('100%'); | |
JSPLib.debug.debuglog("Blob:", image_blob); | |
let blob_url = window.URL.createObjectURL(image_blob); | |
$('#pu-preview img').attr('src', blob_url); | |
$('#pu-preview').show(); | |
setTimeout(()=>{$('#pu-menu').get(0).scrollIntoView(true);}, 100); | |
let md5 = await GetBufferChecksum(image_blob); | |
let uploads = await PublicSubmitRequest('uploads', {search: {media_assets: {md5}}, only: 'id,media_asset_count,media_assets[id,md5],upload_media_assets[id,media_asset_id]'}); | |
if (uploads.length) { | |
let upload = uploads[0]; | |
JSPLib.debug.debuglog("Upload:", upload); | |
JSPLib.notice.notice("Upload found... redirecting."); | |
window.location.replace('/uploads/' + upload.id); | |
return; | |
} | |
UploadImageToDanbooru(image_blob, filename, imageProgress(PU.$upload_progress)).then( | |
(upload_data)=>{ | |
JSPLib.debug.debuglog("Upload:", upload_data); | |
JSPLib.notice.notice("Upload completed... redirecting."); | |
setTimeout(()=>{window.location.replace('/uploads/' + upload_data.id);}, JSPLib.utility.one_second); | |
}, | |
(error)=>{ | |
ErrorHandler(error, 'UploadImageToDanbooru', image_data.full_url, "Unable to upload downloaded image to Danbooru."); | |
}, | |
); | |
}, | |
(error)=>{ | |
ErrorHandler(error, 'ProxyImageToLocal', image_data.full_url, "Unable to download image from source."); | |
}, | |
); | |
} | |
function ProxyImageDownloadBatch(image_data) { | |
function imageProgress($preview, type) { | |
return function (event) { | |
if (event.lengthComputable) { | |
let percent_complete = parseInt(100 * (event.loaded / event.total)); | |
$preview.find(`.pu-image-${type}-progress`).progressbar('option', 'value', percent_complete); | |
} | |
}; | |
} | |
_$('#pu-display').html(RenderBatchDisplay(image_data)); | |
let preview_promises = []; | |
let image_promises = []; | |
image_data.forEach((url_data)=>{ | |
let $preview = _$(`.pu-image-preview[data-id="${url_data.id}"][data-order="${url_data.order}"]`); | |
$preview.find('.pu-image-download-progress').progressbar({value: 0, max: 100}); | |
$preview.find('.pu-image-upload-progress').progressbar({value: 0, max: 100}); | |
let preview_promise = ProxyImageToLocal(url_data.preview_url).then((image_blob)=>{ | |
if (!image_blob) return; | |
let blob_url = window.URL.createObjectURL(image_blob); | |
$preview.find('img').attr('src', blob_url); | |
}); | |
preview_promises.push(preview_promise); | |
let image_promise = ProxyImageToLocal(url_data.full_url, imageProgress($preview, 'download')).then( | |
async (image_blob)=>{ | |
$preview.find('.pu-image-download-progress').progressbar('option', 'value', 100); | |
if (!image_blob) { | |
$preview.addClass('pu-progress-warning'); | |
return; | |
} | |
let md5 = await GetBufferChecksum(image_blob); | |
let uploads = await PublicSubmitRequest('uploads', {search: {media_assets: {md5}}, only: 'id,media_asset_count,media_assets[id,md5],upload_media_assets[id,media_asset_id]'}); | |
if (uploads.length) { | |
let upload = uploads[0]; | |
if (upload.media_asset_count === 1) { | |
$preview.find('a').attr('href', `/uploads/${upload.id}`); | |
} else { | |
let media_asset = upload.media_assets.find((media_asset) => media_asset.md5 === md5); | |
let upload_media_asset = upload.upload_media_assets.find((upload_media_asset) => upload_media_asset.media_asset_id === media_asset?.id); | |
if (upload_media_asset) { | |
$preview.find('a').attr('href', `/uploads/${upload.id}/assets/${upload_media_asset.id}`); | |
} | |
} | |
$preview.addClass('pu-progress-warning'); | |
$preview.find('.pu-image-upload-progress').progressbar('option', 'value', 100); | |
return; | |
} | |
let [name, ext] = JSPLib.utility.getFileURLNameExt(url_data.full_url); | |
let filename = name + '.' + ext; | |
UploadImageToDanbooru(image_blob, filename, imageProgress($preview, 'upload')).then( | |
(upload_data)=>{ | |
JSPLib.debug.debuglog("Upload:", upload_data); | |
$preview.find('a').attr('href', `/uploads/${upload_data.id}`); | |
}, | |
(error)=>{ | |
ErrorHandler(error, 'UploadImageToDanbooru', url_data.full_url, "Unable to upload downloaded image to Danbooru."); | |
$preview.addClass('pu-progress-error'); | |
}, | |
).always(()=>{ | |
$preview.find('.pu-image-upload-progress').progressbar('option', 'value', 100); | |
}); | |
}, | |
(error)=>{ | |
ErrorHandler(error, 'ProxyImageToLocal', url_data.full_url, "Unable to download image from source."); | |
$preview.addClass('pu-progress-error'); | |
}, | |
); | |
image_promises.push(image_promise); | |
}); | |
Promise.all(preview_promises).then(()=>{ | |
JSPLib.notice.notice('All previews loaded.'); | |
}); | |
Promise.all(image_promises).then(()=>{ | |
JSPLib.notice.notice('All images loaded.'); | |
}); | |
} | |
// Event handlers | |
function ProxyImageDownload(event) { | |
ProxyImageDownloadCheck().then(()=>{ | |
if (PU.api_keys.length) { | |
JSPLib.storage.saveData('pu-api-' + PU.unique_id, PU.api_keys); | |
} | |
}); | |
event.preventDefault(); | |
} | |
//Main program | |
async function Main() { | |
Object.assign(PU, { | |
params: JSPLib.utility.parseParams(location.search.slice(1)), | |
drop_target: $('[data-drop-target]').data('drop-target') || '[data-drop-target]', | |
unique_id: JSPLib.utility.getUniqueID(), | |
api_keys: [], | |
controller: document.body.dataset.controller, | |
action: document.body.dataset.action, | |
public_jquery: unsafeWindow.jQuery, | |
private_jquery: window.jQuery, | |
}); | |
window._$ = unsafeWindow.jQuery; | |
window.jQuery.expando = unsafeWindow.jQuery.expando; | |
JSPLib.network.jQuerySetup(); | |
if (PU.controller === 'uploads' && PU.action === 'new') { | |
JSPLib.utility.recheckTimer({ | |
check: ()=>JSPLib.utility.isNamespaceBound(PU.drop_target, 'paste', 'danbooru'), | |
exec: ()=>{ | |
_$(PU.drop_target).off('paste.danbooru'); | |
}, | |
}, 500, JSPLib.utility.one_second * 15); | |
$('#a-new').append(PROXY_CONTROLS); | |
$('#pu-proxy-url').val(PU.params.proxy_url); | |
$('#pu-menu #pu-control a').on(PROGRAM_CLICK, ProxyImageDownload); | |
JSPLib.utility.setCSSStyle(PROGRAM_CSS, 'program'); | |
} else if (PU.controller === 'uploads' && PU.action === 'show') { | |
let source = $('#post_source').val(); | |
let match = XRegExp.exec(source, FILENAME_RG); | |
if (!match) return; | |
let unique_id = match[1]; | |
JSPLib.storage.retrieveData('pu-api-' + unique_id).then((api_keys)=>{ | |
if (!Array.isArray(api_keys) || api_keys.length === 0) return; | |
let site = api_keys[0].match(/^[a-z]+/)[0]; | |
if (site === 'pixiv') { | |
LoadPixivData(api_keys, match); | |
} | |
}); | |
} | |
} | |
/****Initialization****/ | |
//Variables for debug.js | |
JSPLib.debug.debug_console = false; | |
JSPLib.debug.level = JSPLib.debug.INFO; | |
//Export JSPLib | |
JSPLib.load.exportData(PROGRAM_NAME, PU, {other_data: {PX_IMAGE_RG, XRegExp, HANDLED_IMAGES, GetFullImageURL, GMXML: GM.xmlHttpRequest, jQuery: window.jQuery, GetPixivData, GetFullImageURLs, API_STORAGE, FILENAME_RG, ProxyImageDownloadBatch, RenderBatchDisplay, ProxyImageToLocal, CryptoJS}}); | |
/****Execution start****/ | |
JSPLib.load.programInitialize(Main, {program_name: PROGRAM_NAME, required_variables: PROGRAM_LOAD_REQUIRED_VARIABLES, optional_selectors: PROGRAM_LOAD_OPTIONAL_SELECTORS}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment