// ==UserScript==
// @name Catbox Droptarget for Oppaitime
// @namespace https://greasyfork.org/users/390979-parliament
// @version 1.51
// @description Upload/rehost images to catbox.moe directly from upload page
// @author Anakunda
// @iconURL https://catbox.moe/pictures/favicon.ico
// @match https://oppaiti.me/upload.php*
// @match https://oppaiti.me/torrents.php?action=edit*
// @match https://oppaiti.me/requests.php?action=new*
// @match https://oppaiti.me/requests.php?action=edit*
// @match https://oppaiti.me/reports.php?action=report*
// @match https://oppaiti.me/reportsv2.php?*
// @match https://oppaiti.me/artist.php?action=edit*
// @connect catbox.moe
// @grant GM_xmlhttpRequest
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_log
// ==/UserScript==
'use strict';
String.prototype.toASCII = function() {
return this.normalize("NFKD").replace(/[\x00-\x1F\u0080-\uFFFF]/g, '');
}
document.head.appendChild(document.createElement('style')).innerHTML = `
.catbox-droptarget {
margin-left: 8px; padding: 5px;
background-color: #fff9cc; border: solid thin black;
align-content: center; vertical-align: 1px;
}
.catbox-img {
height: 25px;
vertical-align: middle;
}
`;
var userhash = GM_getValue('userhash');
var image;
bindAll();
onReportTypeChange();
if (document.location.pathname.toLowerCase() != '/requests.php') {
var rlsTypeSelect = document.querySelector('select#categories');
if (rlsTypeSelect != null) rlsTypeSelect.addEventListener('change', onRlsTypeChange);
}
var reportTypeSelect = document.querySelector('select#type');
if (reportTypeSelect != null) reportTypeSelect.addEventListener('change', onReportTypeChange);
GM_setValue('userhash', userhash || '');
function onReportTypeChange(evt) {
setTimeout(function() {
if (evt instanceof Event) {
image = document.querySelector('input#proofimages') || document.querySelector('input#image');
if (image != null) image.parentNode.append(createDropTarget(image));
}
bindToTextarea('extra');
}, 1000);
}
function onRlsTypeChange(evt) { setTimeout(bindAll, 1000) }
function imageDropHandler(evt) {
evt.preventDefault();
uploadFiles(evt.currentTarget, evt.dataTransfer.files);
}
function clickHandler(evt) {
evt.preventDefault();
if (!evt.currentTarget.boundElement) throw new Error('boundElement not set');
if (evt.currentTarget.boundElement.nodeName == 'INPUT' && /^https?:\/\//i.test(evt.currentTarget.boundElement.value)
&& !evt.currentTarget.boundElement.value.toLowerCase().includes('catbox.moe/')) {
rehostUrl(evt.currentTarget, evt.currentTarget.boundElement.value);
} else {
let currentTarget = evt.currentTarget;
let inputElement = document.createElement("input");
inputElement.type = "file";
inputElement.accept = '.jpg, .jpeg, .jfif, .png, .gif, .webp';
inputElement.multiple = true;
inputElement.onchange = evt => { uploadFiles(currentTarget, inputElement.files) };
inputElement.dispatchEvent(new MouseEvent("click"));
}
}
function voidDragHandler(evt) { evt.preventDefault() }
function uploadFiles(evtSrc, files) {
if (files.length <= 0) return;
if (!evtSrc.boundElement) throw new Error('boundElement not set');
if (evtSrc.busy) throw new Error('Wait till current upload finishes');
evtSrc.busy = true;
if (evtSrc.hTimer) {
clearTimeout(evtSrc.hTimer);
delete evtSrc.hTimer;
}
evtSrc.style.backgroundColor = 'red';
Promise.all(upload2Catbox(files))
.then(function(results) {
if (results.length > 0) {
switch (evtSrc.boundElement.nodeName) {
case 'INPUT':
evtSrc.boundElement.value = results[0];
break;
case 'TEXTAREA':
evtSrc.boundElement.value += results.join('\n');
break;
}
evtSrc.style.backgroundColor = '#00C000';
evtSrc.hTimer = setTimeout(function() {
evtSrc.style.backgroundColor = null;
delete evtSrc.hTimer;
}, 3000);
} else evtSrc.style.backgroundColor = null;
}).catch(function(e) {
alert(e);
evtSrc.style.backgroundColor = null;
}).then(function() {
evtSrc.busy = false;
});
};
function rehostUrl(evtSrc, url) {
if (!/^https?:\/\//i.test(url)) return;
if (!evtSrc.boundElement) throw new Error('boundElement not set');
if (evtSrc.busy) throw new Error('Wait till current upload finishes');
evtSrc.busy = true;
if (evtSrc.hTimer) {
clearTimeout(evtSrc.hTimer);
delete evtSrc.hTimer;
}
evtSrc.style.backgroundColor = 'red';
rehost2Catbox(evtSrc.boundElement.value).then(function(result) {
evtSrc.boundElement.value = result;
evtSrc.style.backgroundColor = '#00C000';
evtSrc.hTimer = setTimeout(function() {
delete evtSrc.hTimer;
evtSrc.style.backgroundColor = null;
}, 3000);
}).catch(function(e) {
alert(e);
evtSrc.style.backgroundColor = null;
}).then(function() {
evtSrc.busy = false;
});
}
function bindAll() {
if ((image = document.getElementById('image')) != null) {
image.parentNode.append(createDropTarget(image));
} else if ((image = document.querySelector('input[name="image"]')) != null) {
image.parentNode.insertBefore(createDropTarget(image), image.parentNode.querySelector(':scope > br'));
}
['album_desc', 'release_desc', 'desc', 'body', 'description', 'screenshots'].forEach(bindToTextarea);
}
function bindToTextarea(id) {
var desc = document.querySelector('textarea#' + id);
if (desc != null) {
var btn = desc.parentNode.parentNode.querySelector('div > input[class^="button_preview"]');
if (btn != null) {
btn.parentNode.append(createDropTarget(desc));
} else if ((btn = desc.parentNode.parentNode.querySelector(':scope > td.label')) != null) {
var div = document.createElement('div');
div.style.marginTop = '60px';
div.append(createDropTarget(desc));
btn.append(div);
} else if ((btn = desc.parentNode.querySelector('div#Bbcode_Toolbar > div[style]:last-of-type')) != null) {
btn.parentNode.insertBefore(createDropTarget(desc), btn);
}
return btn != null;
} else if ((desc = document.querySelector('textarea[name="' + id + '"]')) != null
&& (btn = desc.parentNode.querySelector(':scope > div > input[value="Submit"]')) != null) {
btn.parentNode.append(createDropTarget(desc));
return true;
}
return false;
}
function createDropTarget(boundElement) {
if (!(boundElement instanceof HTMLElement)) throw new Error('invalid boundElement');
var dropTarget = document.createElement('span');
dropTarget.boundElement = boundElement;
dropTarget.className = 'catbox-droptarget';
dropTarget.ondragover = voidDragHandler;
dropTarget.ondrop = imageDropHandler; // upload
dropTarget.onclick = clickHandler; // rehost
var img = document.createElement('img');
img.src = 'data:image/png;base64,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';
img.onerror = function() { this.src = 'https://catbox.moe/pictures/logo.png' };
img.className = 'catbox-img';
dropTarget.append(img);
return dropTarget;
}
function upload2Catbox(files) {
if (!(files instanceof FileList)) return Promise.reject('Bad parameter (files)');
return Array.from(files)
.sort((file1, file2) => file1.name.localeCompare(file2.name))
.map(file => new Promise(function(resolve, reject) {
var fr = new Promise(function(resolve) {
var reader = new FileReader();
reader.onload = function() { resolve(reader.result) }
reader.readAsBinaryString/*readAsArrayBuffer(file)*/(file);
});
fr.then(function(result) {
const boundary = '----WebKitFormBoundaryTID_GM';
var data = '--' + boundary + '\r\n';
data += 'Content-Disposition: form-data; name="reqtype"\r\n\r\n';
data += 'fileupload\r\n';
if (userhash) {
data += '--' + boundary + '\r\n';
data += 'Content-Disposition: form-data; name="userhash"\r\n\r\n';
data += userhash + '\r\n';
}
data += '--' + boundary + '\r\n';
data += 'Content-Disposition: form-data; name="fileToUpload"; filename="' + file.name.toASCII() + '"\r\n';
data += 'Content-Type: ' + file.type + '\r\n\r\n';
data += result + '\r\n';
data += '--' + boundary + '--\r\n';
GM_xmlhttpRequest({
method: 'POST',
url: 'https://catbox.moe/user/api.php',
responseType: 'text',
headers: {
'Content-Type': 'multipart/form-data; boundary=' + boundary,
'Content-Length': data.length,
},
data: data,
binary: true,
onload: function(response) {
if (response.status != 200) reject('Response error ' + response.status + ' (' + response.statusText + ')');
resolve(response.response);
},
onerror: response => { reject('Response error ' + response.status + ' (' + response.statusText + ')') },
ontimeout: function() { reject('Timeout') },
});
});
}));
}
function rehost2Catbox(url) {
if (typeof url != 'string' || !url) return Promise.reject('Bad parameter (url)');
return new Promise(function(resolve, reject) {
var data = new URLSearchParams({
reqtype: 'urlupload',
url: url.trim(),
});
if (userhash) data.set('userhash', userhash);
GM_xmlhttpRequest({
method: 'POST',
url: 'https://catbox.moe/user/api.php',
responseType: 'text',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': data.toString().length,
},
data: data.toString(),
onload: function(response) {
if (response.status != 200) reject('Response error ' + response.status + ' (' + response.statusText + ')');
resolve(response.response);
},
onerror: response => { reject('Response error ' + response.status + ' (' + response.statusText + ')') },
ontimeout: function() { reject('Timeout') },
});
});
}