NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name Ehentai Uploader Smart Sort
// @namespace https://github.com/Grinch27
// @version 0.1.6
// @description help Ehentai Uploader Sort uploade-Img Smarter!
// @icon https://e-hentai.org/favicon.ico
// @author Grinch27
// @match *://exhentai.org/upld/*
// @match *://upld.e-hentai.org/*
// @exclude https://exhentai.org/upld/manage
// @exclude https://upld.e-hentai.org/manage
// @grant none
// @copyright 2022, Grinch27 (https://github.com/Grinch27)
// @license GPL-3.0-or-later; https://www.gnu.org/licenses/gpl-3.0-standalone.html
// @homepageURL https://github.com/Grinch27/EH-Uploader-Toolkit/blob/main/Ehentai%20Uploader%20Smart%20Sort.js
// @updateURL https://openuserjs.org/meta/Grinch27/Ehentai_Uploader_Smart_Sort.meta.js
// @downloadURL https://openuserjs.org/install/Grinch27/Ehentai_Uploader_Smart_Sort.user.js
// @run-at document-end
// ==/UserScript==
/**
* @author Grinch27
* @copyright Grinch27
* @license GNU General Public License v3.0 or later
*
* GNU General Public License
* <https://www.gnu.org/licenses/>.
*/
(function () {
'use strict';
// class define
class onclickJump_Button {
constructor(innerHTML) {
this.innerHTML = innerHTML;
this.Button = document.createElement('button');
this.Button.innerHTML = innerHTML;
this.Button.setAttribute("class", 'Tampermonkey');
}
onclickJump(URL) {
this.Button.setAttribute("href", URL);
this.Button.setAttribute("onclick", `javascript:window.open('${URL}')`);
this.Button.setAttribute("class", 'Tampermonkey-Jump');
}
clickToCopy(str_copy) {
this.Button.setAttribute('id', 'copy_btn');
this.Button.setAttribute("data-clipboard-text", str_copy);
this.Button.setAttribute("class", 'Tampermonkey-Copy');
}
append(Node) {
Node.appendChild(this.Button);
}
}
class EhentaiManageCell {
constructor(cell_node) {
this.cell_id_ = null;
this.cell_href_ = null;
this.span_id_ = null;
this.span_value_ = null;
this.img_id_ = null;
this.img_alt_ = null;
this.img_src_ = null;
this.series_ = null;
this.index_ = null;
this.SetCellNode(cell_node);
}
SetCellNode(cell_node) {
this.cell_id_ = cell_node.getAttribute('id');
this.SetSpanNode(cell_node.querySelector('input'));
this.SetImgNode(cell_node.querySelector('img'));
this.SetHrefNode(cell_node.querySelector('a[href]'));
}
SetSpanNode(span_node) {
this.span_id_ = span_node.getAttribute('id');
this.span_value_ = Number(span_node.getAttribute('value'));
}
SetImgNode(img_node) {
this.img_id_ = img_node.getAttribute('id');
this.img_alt_ = img_node.getAttribute('alt');
this.img_src_ = img_node.getAttribute('src');
this.MatchSeriesIndex(this.img_alt_.match(/\d+/g));
}
MatchSeriesIndex(match_array) {
if (match_array != null) {
switch (match_array.length) {
case 1:
this.series_ = Number(match_array[0]);
this.index_ = 0;
break
default:
this.series_ = Number(match_array[0]);
this.index_ = Number(match_array[1]);
break
}
}
}
SetHrefNode(href_node) {
if (href_node != null) {
this.cell_href_ = href_node.getAttribute('href');
let temp_href = this.cell_href_.split('/').slice(-2)[0];
this.cell_href_ = this.cell_href_.replace(temp_href, temp_href.match(/.{10}/gi)[0]);
temp_href = undefined;
}
else {
this.cell_href_ = null;
}
}
}
function dynamicSort(property) {
let sortOrder = 1;
if (property[0] === '-') {
sortOrder = -1;
property = property.substr(1);
}
return function (a, b) {
/* next line works with strings and numbers,
* and you may want to customize it to your needs
*/
let result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
return result * sortOrder
}
}
function dynamicSortMultiple() {
/* refer: sort-array-of-objects-by-string-property-value
* save the arguments object as it will be overwritten
* note that arguments object is an array-like object
* consisting of the names of the properties to sort by
*/
let props = arguments;
return function (obj1, obj2) {
let i = 0,
result = 0;
const numberOfProperties = props.length;
/* try getting a different result from 0 (equal)
* as long as we have extra properties to compare
*/
while (result === 0 && i < numberOfProperties) {
result = dynamicSort(props[i])(obj1, obj2);
i++;
}
return result
}
}
function removeDuplicatesFromArrayOfObjects(target_array, property) {
// refer: how-to-remove-all-duplicates-from-an-array-of-objects
return target_array.filter((v, i, a) => a.findIndex(v2 => (v2[property] === v[property])) === i) // or .findLastIndex
}
function mergeArrayToLeft(left_array, right_array, property) {
// refer: combine-json-arrays-by-key-javascript
return left_array.map(x => Object.assign(x, right_array.find(y => y[property] == x[property]))); //merge to left
}
function countSeriesDuplicates(data_array) {
let property = 'series_';
// refer: javascript-counting-duplicates-in-object-array-and-storing-the-count-as-a-new
// refer: get-duplicates-in-array-of-strings-and-count-number-of-duplicates
let res_count = Object.values(
data_array.reduce(
function (elem, {
series_
}) {
elem[series_] = elem[series_] || {
series_,
count: 0
};
elem[series_].count++;
return elem
}, Object.create(null)
)
);
data_array = mergeArrayToLeft(data_array, res_count, property);
// TODO: upgrade series_ and count(need_) to property(varialbe)
return data_array
}
function scanCellInfo(cell_selector) {
// collect img_cell to json_array
let cell_node_array = document.querySelectorAll(cell_selector);
let cell_json_array = [];
for (let i = 0, cell_node; cell_node = cell_node_array[i]; i++) {
cell_node = new EhentaiManageCell(cell_node);
cell_json_array.push(JSON.parse(JSON.stringify(cell_node)))
}
cell_json_array = countSeriesDuplicates(cell_json_array)
alert(`${cell_json_array.length} ImgCell Scanned`);
// Data log for Scan debug
let property = 'series_'
let series_cell_array = removeDuplicatesFromArrayOfObjects(cell_json_array, property)
console.log(series_cell_array);
return cell_json_array
}
function sortPageselCell() {
let cell_json_array = scanCellInfo('div[id^="cell"]');
// sort the json array
cell_json_array.sort(dynamicSortMultiple("-series_", "index_"));
// convert json_array to text ready for POST
let post_text = 'do_reorder=manual';
for (let i = 0, each_param; each_param = cell_json_array[i]; i++) {
post_text += `&${each_param.span_id_}=${i+1}`;
}
post_text += '&autosort=';
return post_text
}
function postEhentaiManageSort(post_text) {
if (document.URL.search(/(?<=(ulgid\=))\d+/gi) == -1) {
alert("Not ulgid Page!\nAbort Post!");
}
else {
let ulgid_num = document.URL.match(/(?<=(ulgid\=))\d+/gi)[0];
let domain_org = document.URL.replace(/.*\/\//gi, '').replace(/managegallery.*/gi, '');
try {
fetch(`https://${domain_org}managegallery?ulgid=${ulgid_num}`, {
method: 'POST',
headers: {
'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
'Content-Type': 'application/x-www-form-urlencoded',
//'queryString': JSON.stringify({'ulgid': ulgid_num}),
//'Cookie': document.cookie,
'Cache-Control': 'max-age=0',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-User': '?1',
'Upgrade-Insecure-Requests': 1,
},
body: JSON.parse(JSON.stringify(post_text))
})
alert("POST OK\nPlease Reflash");
}
catch {
alert("POST fail!");
}
}
}
function userBoard(name_sortBoard) {
//let name_sortBoard = 'SmartSort';
let user_flexbox = document.createElement('flexbox');
user_flexbox.setAttribute('class', `Tampermonkey-${name_sortBoard}`);
user_flexbox.style = 'display: flex; flex-direction: row; justify-content: center; align-items: center;'
document.querySelector('a[name="reorder"]').appendChild(user_flexbox)
let style_EHbutton = "min-height: 26px; line-height: 20px; padding: 1px 5px 2px; border: 2px solid #8d8d8d; border-radius: 3px; opacity: 1.0;";
let id_ScanSmart = 'scan-smart';
let id_PostSmart = 'post-smart';
let button_sort = new onclickJump_Button("Scan Smart");
button_sort.Button.style = style_EHbutton;
button_sort.Button.setAttribute("id", `${id_ScanSmart}`);
button_sort.Button.onclick = function () {
document.querySelector(`button[id="${id_ScanSmart}"]`).value = sortPageselCell();
};
button_sort.append(document.querySelector(`flexbox[class="Tampermonkey-${name_sortBoard}"]`));
let button_post = new onclickJump_Button("Post Smart");
button_post.Button.style = style_EHbutton;
button_post.Button.setAttribute("id", `${id_PostSmart}`);
button_post.Button.onclick = function () {
let post_text = document.querySelector(`button[id="${id_ScanSmart}"]`).value;
if (post_text.search(/autosort/gi) != -1) {
postEhentaiManageSort(post_text);
}
else {
alert("Scan Error!\nAbort Post!\nPlease check Scan Smart Button Value");
}
}
button_post.append(document.querySelector(`flexbox[class="Tampermonkey-${name_sortBoard}"]`));
}
// run
userBoard('SmartSort');
})();