Fix original pictures not load

This commit is contained in:
2023-02-19 15:51:12 +08:00
committed by GitHub
parent b09f52bf01
commit e9e689c50f

View File

@@ -1,7 +1,7 @@
// ==UserScript==
// @name E-Hentai better viewer
// @namespace https://github.com/lifegpc/userscript
// @version 0.2.2
// @version 0.3.0
// @description Add a viewer to view original picture on website.
// @author lifegpc
// @match https://*.e-hentai.org/s/*/*
@@ -10,14 +10,82 @@
// @grant GM_getResourceText
// @grant GM_addElement
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @require https://github.com/lifegpc/viewerjs/raw/main/dist/viewer.min.js
// @require https://github.com/emn178/js-sha512/raw/v0.8.0/build/sha512.min.js
// @require https://openuserjs.org/src/libs/sizzle/GM_config.js
// @resource viewercss https://github.com/lifegpc/viewerjs/raw/main/dist/viewer.min.css
// @run-at document-start
// ==/UserScript==
GM_config.init({
id: 'e-hentai',
fields: {
enableRedirectAPI: {
type: 'checkbox',
label: 'Enable redirect api. This need a thirdparty server.',
default: false
},
APIEndpoint: {
type: 'hidden'
},
tempAPIEndpoint: {
label: 'The endpoint of the API:',
type: 'text',
save: false
},
APISecret: {
label: 'The API secret:',
type: 'text'
},
LoadOriginalWhenOpen: {
type: 'checkbox',
label: 'Load original pictures when opening the viewer modal.',
default: false
},
},
events: {
init: () => {
GM_config.set("tempAPIEndpoint", GM_config.get("APIEndpoint"))
},
open: () => {
GM_config.set("tempAPIEndpoint", GM_config.get("APIEndpoint"))
},
save: (values) => {
console.log(values);
if (GM_config.get('enableRedirectAPI')) {
if (values.tempAPIEndpoint !== null) {
try {
GM_config.set("APIEndpoint", new URL(values.tempAPIEndpoint).toString());
} catch (e) {
GM_config.set("enableRedirectAPI", false);
console.log(e);
}
}
if (!GM_config.get("APISecret")) {
GM_config.set("enableRedirectAPI", false);
}
}
}
}
})
GM_registerMenuCommand("Edit Settings", () => { GM_config.open() }, "e");
GM_addStyle(GM_getResourceText("viewercss"));
function indirectEval(script) {
return eval(`"use strict";${script}`);
}
function parse_cookies() {
let cookies = document.cookie.split(";");
let o = {};
for (let c of cookies) {
c = c.trim().split("=");
let k = c[0];
let v = c.slice(1).join("=");
o[k] = v;
}
return o;
}
function find_script() {
let cols = document.getElementsByTagName("script");
for (let col of cols) {
@@ -26,6 +94,140 @@ function find_script() {
}
}
}
/**
* encodeURIComponent as python's quote_plus behavoir
* @param {string} str
* @returns
*/
function py_quote(str) {
let s = encodeURIComponent(str);
while (s.includes('%20')) {
s = s.replace('%20', '+');
}
while (s.includes('!')) {
s = s.replace('!', '%21');
}
while (s.includes("'")) {
s = s.replace("'", '%27');
}
while (s.includes('(')) {
s = s.replace('(', '%28');
}
while (s.includes(')')) {
s = s.replace(')', '%29');
}
while (s.includes('*')) {
s = s.replace('*', '%2A');
}
return s;
}
class URLParams extends URLSearchParams {
toString() {
let r = "";
for (let p of this.entries()) {
if (r.length) r += "&";
r += py_quote(p[0]) + "=" + py_quote(p[1]);
}
return r;
}
}
/**
* 发送GET请求
* @param {string} url 网站
* @param {Object<string, string>|Array<Array<string>|string>} data 字典
* @param {(content: string)=>void} callback 回调函数
* @param {()=>void} failedCallback 失败回调函数
* @param {Object<string, string>} headers HTTP头部
*/
function get(url, data, callback, failedCallback, headers) {
var xhr = new XMLHttpRequest();
var uri = new URL(url, window.location.href);
if (data == undefined);
else if (Array.isArray(data)) {
for (let i = 0; i < data.length; i++) {
var pair = data[i];
if (Array.isArray(pair)) {
uri.searchParams.append(pair[0], pair.length > 1 ? pair[1] : "");
} else if (typeof pair == "string") {
uri.searchParams.append(pair, "");
}
}
} else {
Object.getOwnPropertyNames(data).forEach((key) => {
if (typeof data[key] == "string")
uri.searchParams.append(key, data[key]);
})
}
xhr.open("GET", uri.href);
if (callback != undefined) xhr.onload = () => {
callback(xhr.responseText);
};
if (failedCallback != undefined) xhr.onerror = failedCallback;
if (headers != undefined) {
Object.getOwnPropertyNames(headers).forEach((key) => {
if (typeof headers[key] == "string")
xhr.setRequestHeader(key, headers[key])
})
}
try {
xhr.send();
} catch (e) {
if (failedCallback != undefined) failedCallback();
}
}
/**
* Generate sign for data
* @param {Object<string, string | Array<string>>|FormData} data Data
* @param {string} secret Secret
* @param {(data: string) => string|undefined} hash The hash function
* @returns {string}
*/
function genGetSign(data, secret, hash) {
if (!hash) hash = sha512;
/**@type {Array<{k: string, v: string}>} */
let arr = [];
if (data.constructor.name == "FormData") {
for (let pair of data.entries()) {
if (typeof pair[1] != "string") continue;
arr.push({ k: pair[0], v: pair[1] });
}
} else {
Object.getOwnPropertyNames(data).forEach((key) => {
let v = data[key];
if (typeof v == "string") arr.push({ k: key, v: v });
else if (Array.isArray(v)) {
v.forEach((v) => {
if (typeof v == "string") arr.push({ k: key, v: v });
})
}
})
}
arr.sort((a, b) => {
return a.k == b.k ? a.v == b.v ? 0 : a.v > b.v ? 1 : -1 : a.k > b.k ? 1 : -1;
})
let par = new URLParams();
arr.forEach((v) => {
par.append(v.k, v.v);
})
return hash(secret + par.toString());
}
function redirect_url(url) {
return new Promise((resolve, reject) => {
let api = GM_config.get("APIEndpoint");
let screct = GM_config.get("APISecret");
if (!api || !screct) {
reject("Endpoint or screct not found.");
return;
}
let data = {'t': url, 'ar': '0', 'rraj': '1', 'c': JSON.stringify(parse_cookies()), 'r': 'https://e-hentai.org/' };
data['sign'] = genGetSign(data, screct);
get(api, data, (c) => {
resolve(JSON.parse(c)['location']);
}, () => {
reject("Error");
})
})
}
let base_url = undefined;
let cur_img = null;
let cur_viewer = null;
@@ -34,6 +236,8 @@ let api_url = undefined;
let gid = undefined;
let showkey = undefined;
let img_keys = {};
let original_url = null;
let redirected_url = null;
let cur_page = null;
let total_page = null;
function get_api_url() {
@@ -104,6 +308,10 @@ async function loadPage(page) {
let xhr = await api_call({ method: "showpage", gid, page, imgkey, showkey });
let a = JSON.parse(xhr.responseText);
if (cur_viewer) cur_viewer.destroy();
cur_img = null;
cur_viewer = null;
original_url = null;
redirected_url = null;
document.getElementById("i1").style.width = a.x + "px";
document.getElementById("i2").innerHTML = a.n + a.i;
document.getElementById("i3").innerHTML = a.i3;
@@ -126,6 +334,20 @@ function replaceLink(a) {
na.addEventListener("click", () => { loadPage(num); });
a.replaceWith(na);
}
async function loadOriginalImage() {
let url = original_url;
if (!url) return false;
if (GM_config.get("enableRedirectAPI")) {
if (redirected_url) {
url = redirected_url;
} else {
redirected_url = await redirect_url(url);
url = redirected_url;
}
}
cur_img.src = url;
return true;
}
let load = () => {
let img = document.getElementById("img");
if (img == null) {
@@ -150,13 +372,15 @@ let load = () => {
let original = document.querySelector("#i7>a");
if (original != null) {
let ourl = original.href;
console.log(ourl);
options.url = ourl;
original_url = ourl;
}
console.log(options);
let viewer = new Viewer(img, options);
let click = async () => {
img.removeEventListener("click", click);
if (GM_config.get("LoadOriginalWhenOpen")) {
await loadOriginalImage();
}
await viewer.init();
console.log("Inited complete");
await viewer.show();