// ==UserScript== // @name xeHentai Helper // @version 0.41 // @description Become a hentai // @namespace https://yooooo.us // @include http*://*e-hentai.org/* // @include http*://exhentai.org/* // @license GNU General Public License (GPL) // @run-at document-end // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @grant GM_xmlHttpRequest // @grant GM.getValue // @grant GM.setValue // @grant GM.deleteValue // @grant GM.xmlHttpRequest // @homepageURL https://github.com/fffonion/xeHentaiHelper.user.js // @supportURL https://github.com/fffonion/xeHentaiHelper.user.js/issues // @downloadURL https://update.sleazyfork.org/scripts/398519/xeHentai%20Helper.user.js // @updateURL https://update.sleazyfork.org/scripts/398519/xeHentai%20Helper.meta.js // ==/UserScript== // ==== ARIA2 class taken from Binux's ThunderLixianExported === // (function () { var JSONRPC = (function () { var jsonrpc_version = '2.0'; function get_auth(url) { return url.match(/^(?:(?![^:@]+:[^:@\/]*@)[^:\/?#.]+:)?(?:\/\/)?(?:([^:@]*(?::[^:@]*)?)?@)?/)[1]; }; function request(jsonrpc_path, method, params) { var auth = get_auth(jsonrpc_path); jsonrpc_path = jsonrpc_path.replace(/^((?![^:@]+:[^:@\/]*@)[^:\/?#.]+:)?(\/\/)?(?:(?:[^:@]*(?::[^:@]*)?)?@)?(.*)/, '$1$2$3'); // auth string not allowed in url for firefox var request_obj = { jsonrpc: jsonrpc_version, method: method, id: (new Date()).getTime().toString(), }; if (params) request_obj.params = params; if (auth && auth.indexOf('token:') == 0) params.unshift(auth); var headers = { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", } if (auth && auth.indexOf('token:') != 0) { headers["Authorization"] = "Basic " + btoa(auth); } var err = function () { console.error(method, params[params.length-2], params[params.length-1], "=>") }; var r; if (jsonrpc_path.match(/\/\/localhost:/)) { // there's not CORS on both sites, we only need GM_xmlHttpRequest // to bypass insecure http downgrade error // but chrome is happy to send to http://localhost // use original XHR gives us better error message r = GM_xmlHttpRequest_fallback; } else { r = GM_xmlHttpRequest; } new Promise((_, reject) => { try { r({ method: "POST", url: jsonrpc_path + "?tm=" + (new Date()).getTime().toString(), headers: headers, data: JSON.stringify(request_obj), onerror: err, onabort: err, ontimeout: err, onload: function (r) { console.info(method, params[params.length-2], params[params.length-1] , "=>", JSON.parse(r.responseText)) }, }) } catch (error) { reject(error); } }); }; return function (jsonrpc_path) { this.jsonrpc_path = jsonrpc_path; this.addTask = function (uri, options) { request(this.jsonrpc_path, 'xeH.addTask', [ [uri, ], options ]); }; this.setCookie = function (cookie) { request(this.jsonrpc_path, 'xeH.setCookie', [ [cookie, ], {} ]); }; return this; }; })(); // dom helper functions function newWrapperDiv(label, dom) { var grp = document.createElement("div"); grp.innerHTML = label + ":"; grp.appendChild(dom); return grp; } function newInput() { var input = document.createElement("input"); input.type = "text"; input.size = 50; input.style = "margin-bottom: 5px;" return input; } function newButton(label, style, f) { var btn = document.createElement("div"); btn.innerHTML = "" + label + ""; btn.style = "position: absolute; " + (style || ""); btn.onclick = f; btn.className = "gt"; return btn; } if (GM !== undefined) { this.GM_getValue = GM.getValue; this.GM_setValue = GM.setValue; this.GM_deleteValue = GM.deleteValue; this.GM_xmlHttpRequest = GM.xmlHttpRequest; } if (!this.GM_getValue || (this.GM_getValue.toString && this.GM_getValue.toString().indexOf("not supported") > -1)) { console.info("[XEH] using fallback set/getValue") this.GM_getValue = function (key, def) { return localStorage[key] || def; }; this.GM_setValue = function (key, value) { return localStorage[key] = value; }; this.GM_deleteValue = function (key) { return delete localStorage[key]; }; } var GM_xmlHttpRequest_fallback = function (opts) { var xhr = new XMLHttpRequest(); xhr.open(opts.method, opts.url, !opts.synchronous); for (let key in opts.headers || {}) { xhr.setRequestHeader(key, opts.headers[key]); } xhr.onerror = opts.onerror; xhr.onabort = opts.onabort; xhr.onload = function () { if (xhr.readyState === xhr.DONE) { opts.onload(xhr); } } xhr.send(opts.data); } if (!this.GM_xmlHttpRequest) { console.info("[XEH] using fallback XHR") this.GM_xmlHttpRequest = GM_xmlHttpRequest_fallback; } XEH = { config_keys: ["host", "port", "token", "name"], }; (function (XEH) { // JSONRPC client, will be initlized in the config area var jr; var gd5 = document.getElementById("gd5"); if (gd5) { // gallery page var p = document.createElement("p"); p.innerHTML = ' 添加到xeHentai'; p.className = "g2"; p.onclick = function () { if (document.cookie.indexOf("ipb_pass_hash") != -1 && document.cookie.indexOf("ipb_member_id") != -1) { jr.setCookie(document.cookie); } jr.addTask(location.protocol + '//' + location.host + location.pathname, {}); }; gd5.childNodes[gd5.childNodes.length - 1].className = "g2"; gd5.appendChild(p); } var glnames = document.getElementsByClassName("glname"); if (glnames && glnames.length) { // index page function saveInputState() { var checked = {}; for (var i = 0; i < allinps.length; ++i) { if (allinps[i].checked) { checked[allinps[i].value] = 1; } } checked.ts = new Date().getTime(); GM_setValue("xeh_checked", JSON.stringify(checked)); } function loadInputState() { var checked = JSON.parse(GM_getValue("xeh_checked", "0")); if (!checked) return; // ignore saved states longer than 30 minutes if (checked.ts && new Date().getTime() - checked.ts > 10 * 60 * 1000) { return; } for (var i = 0; i < allinps.length; ++i) { if (checked[allinps[i].value] === 1) { allinps[i].checked = true; } } } var inp_onclick = function (e) { e.stopPropagation(); saveInputState(); }; var inputSize = "0.7em"; if (glnames[0].className.search(/gl\dm/) != -1) { // minimal, minimal+ } else if (glnames[0].className.search(/gl\dc/) != -1) { // compact inputSize = "0.8em"; } else if (glnames[0].className.search(/gl\d[te]/) != 1) { // extended or thumbail mode inputSize = "0.9em"; } var allinps = []; for (var i = 0; i < glnames.length; ++i) { var glname = glnames[i]; var ip = document.createElement("input"); ip.type = "checkbox"; ip.style = "float:left;font-size:20px;width:" + inputSize + ";height:" + inputSize + ";top:0;"; var href; var href_dom = glname; for (var j = 0; j < 3; j++) { href = href_dom.innerHTML.match(/\/g\/\d+\/[a-f0-9]+\//); if (href) break; href_dom = href_dom.parentNode; } ip.value = decodeURIComponent(location.protocol + "//" + location.hostname + href[0]); ip.onclick = inp_onclick; var doms = href_dom.childNodes; for (var k = 0; k < doms.length; k++) { if (doms[k].tagName === "A") { doms[k].insertBefore(ip, doms[k].childNodes[0]); } } allinps.push(ip); } var titlebar = document.getElementsByClassName("itg")[0].childNodes[0].childNodes[0]; var titleInnerHTML = '反选' + ' 清空' + ' 导出选中项到xeHentai'; titleInnerHTML = titleInnerHTML.replaceAll("##SIZE##", inputSize); if (glnames[0].className.search(/gl\d[te]/) != -1) { // extended or thumbail mode var el = document.createElement("tr"); el.style = "padding:0;font-size:14px;"; var tbl = document.getElementsByClassName("itg")[0]; if (glnames[0].className.search(/gl\dt/) != -1) { // thumbnail is not a table var tt = document.createElement("table"); tt.className = "itg"; tbl.parentNode.insertBefore(tt, tbl); tbl = tt; titleInnerHTML += "
"; } else { // extended, shift right by one cell titleInnerHTML = "