Greasy Fork is available in English.
Adds cover sticker if needs updating for unsupported host / big size / small resolution
当前为
// ==UserScript==
// @name [RED] Cover Inspector
// @namespace http://greasyfork.icu/users/321857-anakunda
// @version 1.11.8
// @run-at document-end
// @description Adds cover sticker if needs updating for unsupported host / big size / small resolution
// @author Anakunda
// @copyright 2020, Anakunda (http://greasyfork.icu/users/321857-anakunda)
// @license GPL-3.0-or-later
// @iconURL https://i.ibb.co/mh2prQR/clouseau.png
// @match https://redacted.ch/torrents.php?id=*
// @match https://redacted.ch/torrents.php
// @match https://redacted.ch/torrents.php?action=advanced
// @match https://redacted.ch/torrents.php?action=advanced&*
// @match https://redacted.ch/torrents.php?*&action=advanced
// @match https://redacted.ch/torrents.php?*&action=advanced&*
// @match https://redacted.ch/torrents.php?action=basic
// @match https://redacted.ch/torrents.php?action=basic&*
// @match https://redacted.ch/torrents.php?*&action=basic
// @match https://redacted.ch/torrents.php?*&action=basic&*
// @match https://redacted.ch/torrents.php?page=*
// @match https://redacted.ch/torrents.php?action=notify
// @match https://redacted.ch/torrents.php?action=notify&*
// @match https://redacted.ch/torrents.php?type=*
// @match https://redacted.ch/artist.php?id=*
// @connect *
// @grant GM_xmlhttpRequest
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_openInTab
// @require https://openuserjs.org/src/libs/Anakunda/libLocks.min.js
// @require https://openuserjs.org/src/libs/Anakunda/gazelleApiLib.min.js
// ==/UserScript==
'use strict';
const siteApiTimeframeStorageKey = 'AJAX time frame';
let gazelleApiFrameReserve = 100; // reserve that amount of ms for service operations
let gazelleApiMutex = createMutex(), ajaxApiKey, ajaxApiLogger;
if (typeof GM_getValue == 'function') switch (document.domain) {
case 'redacted.ch': ajaxApiKey = GM_getValue('redacted_api_key'); break;
}
for (let key of ['ajaxApiKey', 'ajax_api_key', 'api_key'])
if (!ajaxApiKey && key in window.localStorage && (ajaxApiKey = window.localStorage[key]))
GM_setValue('redacted_api_key', ajaxApiKey);
function setAjaxApiLogger(callback) { ajaxApiLogger = typeof callback == 'function' ? callback : undefined }
function queryAjaxAPI(action, params, postData) {
return action ? new Promise(function(resolve, reject) {
let xhr = new XMLHttpRequest, retryCount = 0;
params = new URLSearchParams(Object.assign({ action: action }, params || undefined));
const url = '/ajax.php?' + params.toString();
if (postData && !(postData instanceof URLSearchParams)) switch (typeof postData) {
case 'object': postData = new URLSearchParams(postData); break;
case 'string': try { postData = new URLSearchParams(JSON.parse(postData)) } catch(e) { } break;
}
postData = postData instanceof URLSearchParams ? postData.toString() : undefined;
(function attempt() {
gazelleApiMutex.lock(function() {
let timeStamp = Date.now(), apiTimeFrame;
if (siteApiTimeframeStorageKey in window.localStorage) try {
apiTimeFrame = JSON.parse(window.localStorage[siteApiTimeframeStorageKey]);
} catch(e) { apiTimeFrame = { } } else apiTimeFrame = { };
if (!apiTimeFrame.timeLock || timeStamp > apiTimeFrame.timeLock) {
apiTimeFrame.timeLock = timeStamp + 10000 + gazelleApiFrameReserve;
apiTimeFrame.requestCounter = 1;
} else ++apiTimeFrame.requestCounter;
window.localStorage[siteApiTimeframeStorageKey] = JSON.stringify(apiTimeFrame);
gazelleApiMutex.unlock();
if (apiTimeFrame.requestCounter <= 5) {
xhr.open(postData ? 'POST' : 'GET', url, true);
xhr.setRequestHeader('Accept', 'application/json');
if (postData) xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');
if (ajaxApiKey) xhr.setRequestHeader('Authorization', ajaxApiKey);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.responseType = 'json';
xhr.onload = function() {
if (xhr.status == 404) return reject('not found');
if (xhr.status < 200 || xhr.status >= 400) return reject(defaultErrorHandler(xhr));
if (xhr.response.status == 'success') return resolve(xhr.response.response);
if (xhr.response.error == 'rate limit exceeded') {
console.debug(xhr.response.error + ':', 'action=' + action, apiTimeFrame, timeStamp, retryCount);
if (retryCount++ <= 10) return setTimeout(attempt, apiTimeFrame.timeLock - timeStamp);
} else console.warn('queryAjaxAPI.attempt(…):', xhr.response.status, xhr.response.error);
reject(xhr.response.status + ': ' + xhr.response.error);
};
xhr.onerror = () => { reject(defaultErrorHandler(xhr)) };
xhr.ontimeout = () => { reject(defaultTimeoutHandler(xhr)) };
xhr.timeout = 20000;
xhr.send(postData);
} else {
setTimeout(attempt, apiTimeFrame.timeLock - timeStamp);
if (typeof ajaxApiLogger == 'function') ajaxApiLogger(action, apiTimeFrame, timeStamp);
console.debug('AJAX API request quota exceeded: action=' + action, apiTimeFrame, timeStamp, retryCount);
}
});
})();
}) : Promise.reject('Action missing');
}
const ajaxGetArtist = artistName => queryAjaxAPI('artist', { artistname: artistName });
const ajaxGetRequest = id => queryAjaxAPI('request', { id: id });
function defaultErrorHandler(response) {
console.error('HTTP error:', response);
let e = 'HTTP error ' + response.status;
if (response.statusText) e += ' (' + response.statusText + ')';
if (response.error) e += ' (' + response.error + ')';
return e;
}
function defaultTimeoutHandler(response) {
console.error('HTTP timeout:', response);
const e = 'HTTP timeout';
return e;
}