ostrich

Reposting music torrents on sites built on Gazelle and other frameworks

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

Advertisement:

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

Advertisement:

// ==UserScript==
// @name               ostrich
// @namespace          https://dvxg.de/
// @version            0.3.10
// @author             davidxuang
// @description        Reposting music torrents on sites built on Gazelle and other frameworks
// @description:zh-CN  适用于Gazelle等架构站点的音乐转种工具
// @license            AGPL-3.0-only
// @icon               https://cdn.jsdelivr.net/gh/microsoft/fluentui-emoji/assets/Musical%20score/3D/musical_score_3d.png
// @homepage           https://github.com/davidxuang/ostrich
// @homepageURL        https://github.com/davidxuang/ostrich
// @match              https://logs.musichoarders.xyz/
// @match              https://*.dicmusic.com/torrents.php*
// @match              https://*.dicmusic.com/artist.php*
// @match              https://*.dicmusic.com/collages.php*
// @match              https://*.dicmusic.com/upload.php*
// @match              https://*.jpopsuki.eu/torrents.php*
// @match              https://*.jpopsuki.eu/artist.php*
// @match              https://*.jpopsuki.eu/collages.php*
// @match              https://*.jpopsuki.eu/upload.php*
// @match              https://*.orpheus.network/torrents.php*
// @match              https://*.orpheus.network/artist.php*
// @match              https://*.orpheus.network/collages.php*
// @match              https://*.orpheus.network/upload.php*
// @match              https://*.redacted.sh/torrents.php*
// @match              https://*.redacted.sh/artist.php*
// @match              https://*.redacted.sh/collages.php*
// @match              https://*.redacted.sh/upload.php*
// @match              https://*.open.cd/plugin_details.php*
// @match              https://*.open.cd/plugin_upload.php*
// @match              https://*.tjupt.org/details.php*
// @match              https://*.tjupt.org/upload.php*
// @resource           brotli_wasm_bg  https://cdn.jsdelivr.net/npm/[email protected]/pkg.web/brotli_wasm_bg.wasm
// @grant              GM.getResourceUrl
// @grant              GM.xmlHttpRequest
// ==/UserScript==

(async function() {
	"use strict";
	var __create = Object.create;
	var __defProp = Object.defineProperty;
	var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
	var __getOwnPropNames = Object.getOwnPropertyNames;
	var __getProtoOf = Object.getPrototypeOf;
	var __hasOwnProp = Object.prototype.hasOwnProperty;
	var __commonJSMin$1 = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
	var __exportAll = (all, no_symbols) => {
		let target = {};
		for (var name in all) __defProp(target, name, {
			get: all[name],
			enumerable: true
		});
		if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
		return target;
	};
	var __copyProps = (to, from, except, desc) => {
		if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
			key = keys[i];
			if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
				get: ((k) => from[k]).bind(null, key),
				enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
			});
		}
		return to;
	};
	var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
		value: mod,
		enumerable: true
	}) : target, mod));
	var _GM = (() => typeof GM != "undefined" ? GM : void 0)();
	var require_truncate = __commonJSMin$1(((exports, module) => {
		function isHighSurrogate(codePoint) {
			return codePoint >= 55296 && codePoint <= 56319;
		}
		function isLowSurrogate(codePoint) {
			return codePoint >= 56320 && codePoint <= 57343;
		}
		module.exports = function truncate(getLength, string, byteLength) {
			if (typeof string !== "string") throw new Error("Input must be string");
			var charLength = string.length;
			var curByteLength = 0;
			var codePoint;
			var segment;
			for (var i = 0; i < charLength; i += 1) {
				codePoint = string.charCodeAt(i);
				segment = string[i];
				if (isHighSurrogate(codePoint) && isLowSurrogate(string.charCodeAt(i + 1))) {
					i += 1;
					segment += string[i];
				}
				curByteLength += getLength(segment);
				if (curByteLength === byteLength) return string.slice(0, i + 1);
				else if (curByteLength > byteLength) return string.slice(0, i - segment.length + 1);
			}
			return string;
		};
	}));
	var require_browser$1 = __commonJSMin$1(((exports, module) => {
		function isHighSurrogate(codePoint) {
			return codePoint >= 55296 && codePoint <= 56319;
		}
		function isLowSurrogate(codePoint) {
			return codePoint >= 56320 && codePoint <= 57343;
		}
		module.exports = function getByteLength(string) {
			if (typeof string !== "string") throw new Error("Input must be string");
			var charLength = string.length;
			var byteLength = 0;
			var codePoint = null;
			var prevCodePoint = null;
			for (var i = 0; i < charLength; i++) {
				codePoint = string.charCodeAt(i);
				if (isLowSurrogate(codePoint)) if (prevCodePoint != null && isHighSurrogate(prevCodePoint)) byteLength += 1;
				else byteLength += 3;
				else if (codePoint <= 127) byteLength += 1;
				else if (codePoint >= 128 && codePoint <= 2047) byteLength += 2;
				else if (codePoint >= 2048 && codePoint <= 65535) byteLength += 3;
				prevCodePoint = codePoint;
			}
			return byteLength;
		};
	}));
	var require_browser = __commonJSMin$1(((exports, module) => {
		var truncate = require_truncate();
		var getLength = require_browser$1();
		module.exports = truncate.bind(null, getLength);
	}));
	var import_sanitize_filename = __toESM(__commonJSMin$1(((exports, module) => {
		var truncate = require_browser();
		var illegalRe = /[\/\?<>\\:\*\|"]/g;
		var controlRe = /[\x00-\x1f\x80-\x9f]/g;
		var reservedRe = /^\.+$/;
		var windowsReservedRe = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i;
		function replaceTrailingDotsAndSpaces(str, replacement) {
			var end = str.length;
			while (end > 0 && (str[end - 1] === "." || str[end - 1] === " ")) end--;
			return end < str.length ? str.slice(0, end) + replacement : str;
		}
		function sanitize(input, replacement) {
			if (typeof input !== "string") throw new Error("Input must be string");
			var sanitized = input.replace(illegalRe, replacement).replace(controlRe, replacement).replace(reservedRe, replacement).replace(windowsReservedRe, replacement);
			sanitized = replaceTrailingDotsAndSpaces(sanitized, replacement);
			return truncate(sanitized, 255);
		}
		module.exports = function(input, options) {
			var replacement = options && options.replacement || "";
			var output = sanitize(input, replacement);
			if (replacement === "") return output;
			return sanitize(output, "");
		};
	}))(), 1);
	var version = "3.7.8";
	var VERSION = version;
	var _hasBuffer = typeof Buffer === "function";
	var _TD = typeof TextDecoder === "function" ? new TextDecoder() : void 0;
	var _TE = typeof TextEncoder === "function" ? new TextEncoder() : void 0;
	var b64chs = Array.prototype.slice.call("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");
	var b64tab = ((a) => {
		let tab = {};
		a.forEach((c, i) => tab[c] = i);
		return tab;
	})(b64chs);
	var b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;
	var _fromCC = String.fromCharCode.bind(String);
	var _U8Afrom = typeof Uint8Array.from === "function" ? Uint8Array.from.bind(Uint8Array) : (it) => new Uint8Array(Array.prototype.slice.call(it, 0));
	var _mkUriSafe = (src) => src.replace(/=/g, "").replace(/[+\/]/g, (m0) => m0 == "+" ? "-" : "_");
	var _tidyB64 = (s) => s.replace(/[^A-Za-z0-9\+\/]/g, "");
	var btoaPolyfill = (bin) => {
		let u32, c0, c1, c2, asc = "";
		const pad = bin.length % 3;
		for (let i = 0; i < bin.length;) {
			if ((c0 = bin.charCodeAt(i++)) > 255 || (c1 = bin.charCodeAt(i++)) > 255 || (c2 = bin.charCodeAt(i++)) > 255) throw new TypeError("invalid character found");
			u32 = c0 << 16 | c1 << 8 | c2;
			asc += b64chs[u32 >> 18 & 63] + b64chs[u32 >> 12 & 63] + b64chs[u32 >> 6 & 63] + b64chs[u32 & 63];
		}
		return pad ? asc.slice(0, pad - 3) + "===".substring(pad) : asc;
	};
	var _btoa = typeof btoa === "function" ? (bin) => btoa(bin) : _hasBuffer ? (bin) => Buffer.from(bin, "binary").toString("base64") : btoaPolyfill;
	var _fromUint8Array = _hasBuffer ? (u8a) => Buffer.from(u8a).toString("base64") : (u8a) => {
		const maxargs = 4096;
		let strs = [];
		for (let i = 0, l = u8a.length; i < l; i += maxargs) strs.push(_fromCC.apply(null, u8a.subarray(i, i + maxargs)));
		return _btoa(strs.join(""));
	};
	var fromUint8Array = (u8a, urlsafe = false) => urlsafe ? _mkUriSafe(_fromUint8Array(u8a)) : _fromUint8Array(u8a);
	var cb_utob = (c) => {
		if (c.length < 2) {
			var cc = c.charCodeAt(0);
			return cc < 128 ? c : cc < 2048 ? _fromCC(192 | cc >>> 6) + _fromCC(128 | cc & 63) : _fromCC(224 | cc >>> 12 & 15) + _fromCC(128 | cc >>> 6 & 63) + _fromCC(128 | cc & 63);
		} else {
			var cc = 65536 + (c.charCodeAt(0) - 55296) * 1024 + (c.charCodeAt(1) - 56320);
			return _fromCC(240 | cc >>> 18 & 7) + _fromCC(128 | cc >>> 12 & 63) + _fromCC(128 | cc >>> 6 & 63) + _fromCC(128 | cc & 63);
		}
	};
	var re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g;
	var utob = (u) => u.replace(re_utob, cb_utob);
	var _encode = _hasBuffer ? (s) => Buffer.from(s, "utf8").toString("base64") : _TE ? (s) => _fromUint8Array(_TE.encode(s)) : (s) => _btoa(utob(s));
	var encode$2 = (src, urlsafe = false) => urlsafe ? _mkUriSafe(_encode(src)) : _encode(src);
	var encodeURI = (src) => encode$2(src, true);
	var re_btou = /[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g;
	var cb_btou = (cccc) => {
		switch (cccc.length) {
			case 4:
				var offset = ((7 & cccc.charCodeAt(0)) << 18 | (63 & cccc.charCodeAt(1)) << 12 | (63 & cccc.charCodeAt(2)) << 6 | 63 & cccc.charCodeAt(3)) - 65536;
				return _fromCC((offset >>> 10) + 55296) + _fromCC((offset & 1023) + 56320);
			case 3: return _fromCC((15 & cccc.charCodeAt(0)) << 12 | (63 & cccc.charCodeAt(1)) << 6 | 63 & cccc.charCodeAt(2));
			default: return _fromCC((31 & cccc.charCodeAt(0)) << 6 | 63 & cccc.charCodeAt(1));
		}
	};
	var btou = (b) => b.replace(re_btou, cb_btou);
	var atobPolyfill = (asc) => {
		asc = asc.replace(/\s+/g, "");
		if (!b64re.test(asc)) throw new TypeError("malformed base64.");
		asc += "==".slice(2 - (asc.length & 3));
		let u24, r1, r2;
		let binArray = [];
		for (let i = 0; i < asc.length;) {
			u24 = b64tab[asc.charAt(i++)] << 18 | b64tab[asc.charAt(i++)] << 12 | (r1 = b64tab[asc.charAt(i++)]) << 6 | (r2 = b64tab[asc.charAt(i++)]);
			if (r1 === 64) binArray.push(_fromCC(u24 >> 16 & 255));
			else if (r2 === 64) binArray.push(_fromCC(u24 >> 16 & 255, u24 >> 8 & 255));
			else binArray.push(_fromCC(u24 >> 16 & 255, u24 >> 8 & 255, u24 & 255));
		}
		return binArray.join("");
	};
	var _atob = typeof atob === "function" ? (asc) => atob(_tidyB64(asc)) : _hasBuffer ? (asc) => Buffer.from(asc, "base64").toString("binary") : atobPolyfill;
	var _toUint8Array = _hasBuffer ? (a) => _U8Afrom(Buffer.from(a, "base64")) : (a) => _U8Afrom(_atob(a).split("").map((c) => c.charCodeAt(0)));
	var toUint8Array = (a) => _toUint8Array(_unURI(a));
	var _decode = _hasBuffer ? (a) => Buffer.from(a, "base64").toString("utf8") : _TD ? (a) => _TD.decode(_toUint8Array(a)) : (a) => btou(_atob(a));
	var _unURI = (a) => _tidyB64(a.replace(/[-_]/g, (m0) => m0 == "-" ? "+" : "/"));
	var decode$2 = (src) => _decode(_unURI(src));
	var isValid = (src) => {
		if (typeof src !== "string") return false;
		const s = src.replace(/\s+/g, "").replace(/={0,2}$/, "");
		return !/[^\s0-9a-zA-Z\+/]/.test(s) || !/[^\s0-9a-zA-Z\-_]/.test(s);
	};
	var _noEnum = (v) => {
		return {
			value: v,
			enumerable: false,
			writable: true,
			configurable: true
		};
	};
	var extendString = function() {
		const _add = (name, body) => Object.defineProperty(String.prototype, name, _noEnum(body));
		_add("fromBase64", function() {
			return decode$2(this);
		});
		_add("toBase64", function(urlsafe) {
			return encode$2(this, urlsafe);
		});
		_add("toBase64URI", function() {
			return encode$2(this, true);
		});
		_add("toBase64URL", function() {
			return encode$2(this, true);
		});
		_add("toUint8Array", function() {
			return toUint8Array(this);
		});
	};
	var extendUint8Array = function() {
		const _add = (name, body) => Object.defineProperty(Uint8Array.prototype, name, _noEnum(body));
		_add("toBase64", function(urlsafe) {
			return fromUint8Array(this, urlsafe);
		});
		_add("toBase64URI", function() {
			return fromUint8Array(this, true);
		});
		_add("toBase64URL", function() {
			return fromUint8Array(this, true);
		});
	};
	var extendBuiltins = () => {
		extendString();
		extendUint8Array();
	};
	var gBase64 = {
		version,
		VERSION,
		atob: _atob,
		atobPolyfill,
		btoa: _btoa,
		btoaPolyfill,
		fromBase64: decode$2,
		toBase64: encode$2,
		encode: encode$2,
		encodeURI,
		encodeURL: encodeURI,
		utob,
		btou,
		decode: decode$2,
		isValid,
		fromUint8Array,
		toUint8Array,
		extendString,
		extendUint8Array,
		extendBuiltins
	};
	var brotli_wasm_exports = __exportAll({
		BrotliStreamResult: () => BrotliStreamResult,
		BrotliStreamResultCode: () => BrotliStreamResultCode,
		CompressStream: () => CompressStream,
		DecompressStream: () => DecompressStream,
		compress: () => compress,
		decompress: () => decompress,
		default: () => init
	});
	var wasm;
	var heap = new Array(32).fill(void 0);
	heap.push(void 0, null, true, false);
	function getObject(idx) {
		return heap[idx];
	}
	var cachedTextDecoder = new TextDecoder("utf-8", {
		ignoreBOM: true,
		fatal: true
	});
	cachedTextDecoder.decode();
	var cachegetUint8Memory0 = null;
	function getUint8Memory0() {
		if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
		return cachegetUint8Memory0;
	}
	function getStringFromWasm0(ptr, len) {
		return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
	}
	var heap_next = heap.length;
	function addHeapObject(obj) {
		if (heap_next === heap.length) heap.push(heap.length + 1);
		const idx = heap_next;
		heap_next = heap[idx];
		heap[idx] = obj;
		return idx;
	}
	var WASM_VECTOR_LEN = 0;
	var cachedTextEncoder = new TextEncoder("utf-8");
	var encodeString = typeof cachedTextEncoder.encodeInto === "function" ? function(arg, view) {
		return cachedTextEncoder.encodeInto(arg, view);
	} : function(arg, view) {
		const buf = cachedTextEncoder.encode(arg);
		view.set(buf);
		return {
			read: arg.length,
			written: buf.length
		};
	};
	function passStringToWasm0(arg, malloc, realloc) {
		if (realloc === void 0) {
			const buf = cachedTextEncoder.encode(arg);
			const ptr = malloc(buf.length);
			getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf);
			WASM_VECTOR_LEN = buf.length;
			return ptr;
		}
		let len = arg.length;
		let ptr = malloc(len);
		const mem = getUint8Memory0();
		let offset = 0;
		for (; offset < len; offset++) {
			const code = arg.charCodeAt(offset);
			if (code > 127) break;
			mem[ptr + offset] = code;
		}
		if (offset !== len) {
			if (offset !== 0) arg = arg.slice(offset);
			ptr = realloc(ptr, len, len = offset + arg.length * 3);
			const view = getUint8Memory0().subarray(ptr + offset, ptr + len);
			const ret = encodeString(arg, view);
			offset += ret.written;
		}
		WASM_VECTOR_LEN = offset;
		return ptr;
	}
	var cachegetInt32Memory0 = null;
	function getInt32Memory0() {
		if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
		return cachegetInt32Memory0;
	}
	function dropObject(idx) {
		if (idx < 36) return;
		heap[idx] = heap_next;
		heap_next = idx;
	}
	function takeObject(idx) {
		const ret = getObject(idx);
		dropObject(idx);
		return ret;
	}
	function passArray8ToWasm0(arg, malloc) {
		const ptr = malloc(arg.length * 1);
		getUint8Memory0().set(arg, ptr / 1);
		WASM_VECTOR_LEN = arg.length;
		return ptr;
	}
	var stack_pointer = 32;
	function addBorrowedObject(obj) {
		if (stack_pointer == 1) throw new Error("out of js stack");
		heap[--stack_pointer] = obj;
		return stack_pointer;
	}
	function getArrayU8FromWasm0(ptr, len) {
		return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len);
	}
	function compress(buf, raw_options) {
		try {
			const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
			const ptr0 = passArray8ToWasm0(buf, wasm.__wbindgen_malloc);
			const len0 = WASM_VECTOR_LEN;
			wasm.compress(retptr, ptr0, len0, addBorrowedObject(raw_options));
			var r0 = getInt32Memory0()[retptr / 4 + 0];
			var r1 = getInt32Memory0()[retptr / 4 + 1];
			var r2 = getInt32Memory0()[retptr / 4 + 2];
			if (getInt32Memory0()[retptr / 4 + 3]) throw takeObject(r2);
			var v1 = getArrayU8FromWasm0(r0, r1).slice();
			wasm.__wbindgen_free(r0, r1 * 1);
			return v1;
		} finally {
			wasm.__wbindgen_add_to_stack_pointer(16);
			heap[stack_pointer++] = void 0;
		}
	}
	function decompress(buf) {
		try {
			const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
			const ptr0 = passArray8ToWasm0(buf, wasm.__wbindgen_malloc);
			const len0 = WASM_VECTOR_LEN;
			wasm.decompress(retptr, ptr0, len0);
			var r0 = getInt32Memory0()[retptr / 4 + 0];
			var r1 = getInt32Memory0()[retptr / 4 + 1];
			var r2 = getInt32Memory0()[retptr / 4 + 2];
			if (getInt32Memory0()[retptr / 4 + 3]) throw takeObject(r2);
			var v1 = getArrayU8FromWasm0(r0, r1).slice();
			wasm.__wbindgen_free(r0, r1 * 1);
			return v1;
		} finally {
			wasm.__wbindgen_add_to_stack_pointer(16);
		}
	}
	function isLikeNone(x) {
		return x === void 0 || x === null;
	}
	var BrotliStreamResultCode = Object.freeze({
		ResultSuccess: 1,
		"1": "ResultSuccess",
		NeedsMoreInput: 2,
		"2": "NeedsMoreInput",
		NeedsMoreOutput: 3,
		"3": "NeedsMoreOutput"
	});
	var BrotliStreamResult = class BrotliStreamResult {
		static __wrap(ptr) {
			const obj = Object.create(BrotliStreamResult.prototype);
			obj.ptr = ptr;
			return obj;
		}
		__destroy_into_raw() {
			const ptr = this.ptr;
			this.ptr = 0;
			return ptr;
		}
		free() {
			const ptr = this.__destroy_into_raw();
			wasm.__wbg_brotlistreamresult_free(ptr);
		}
		get code() {
			return wasm.__wbg_get_brotlistreamresult_code(this.ptr) >>> 0;
		}
		set code(arg0) {
			wasm.__wbg_set_brotlistreamresult_code(this.ptr, arg0);
		}
		get buf() {
			try {
				const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
				wasm.__wbg_get_brotlistreamresult_buf(retptr, this.ptr);
				var r0 = getInt32Memory0()[retptr / 4 + 0];
				var r1 = getInt32Memory0()[retptr / 4 + 1];
				var v0 = getArrayU8FromWasm0(r0, r1).slice();
				wasm.__wbindgen_free(r0, r1 * 1);
				return v0;
			} finally {
				wasm.__wbindgen_add_to_stack_pointer(16);
			}
		}
		set buf(arg0) {
			const ptr0 = passArray8ToWasm0(arg0, wasm.__wbindgen_malloc);
			const len0 = WASM_VECTOR_LEN;
			wasm.__wbg_set_brotlistreamresult_buf(this.ptr, ptr0, len0);
		}
		get input_offset() {
			return wasm.__wbg_get_brotlistreamresult_input_offset(this.ptr) >>> 0;
		}
		set input_offset(arg0) {
			wasm.__wbg_set_brotlistreamresult_input_offset(this.ptr, arg0);
		}
	};
	var CompressStream = class CompressStream {
		static __wrap(ptr) {
			const obj = Object.create(CompressStream.prototype);
			obj.ptr = ptr;
			return obj;
		}
		__destroy_into_raw() {
			const ptr = this.ptr;
			this.ptr = 0;
			return ptr;
		}
		free() {
			const ptr = this.__destroy_into_raw();
			wasm.__wbg_compressstream_free(ptr);
		}
		constructor(quality) {
			const ret = wasm.compressstream_new(!isLikeNone(quality), isLikeNone(quality) ? 0 : quality);
			return CompressStream.__wrap(ret);
		}
		compress(input_opt, output_size) {
			try {
				const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
				var ptr0 = isLikeNone(input_opt) ? 0 : passArray8ToWasm0(input_opt, wasm.__wbindgen_malloc);
				var len0 = WASM_VECTOR_LEN;
				wasm.compressstream_compress(retptr, this.ptr, ptr0, len0, output_size);
				var r0 = getInt32Memory0()[retptr / 4 + 0];
				var r1 = getInt32Memory0()[retptr / 4 + 1];
				if (getInt32Memory0()[retptr / 4 + 2]) throw takeObject(r1);
				return BrotliStreamResult.__wrap(r0);
			} finally {
				wasm.__wbindgen_add_to_stack_pointer(16);
			}
		}
		total_out() {
			return wasm.compressstream_total_out(this.ptr) >>> 0;
		}
	};
	var DecompressStream = class DecompressStream {
		static __wrap(ptr) {
			const obj = Object.create(DecompressStream.prototype);
			obj.ptr = ptr;
			return obj;
		}
		__destroy_into_raw() {
			const ptr = this.ptr;
			this.ptr = 0;
			return ptr;
		}
		free() {
			const ptr = this.__destroy_into_raw();
			wasm.__wbg_decompressstream_free(ptr);
		}
		constructor() {
			const ret = wasm.decompressstream_new();
			return DecompressStream.__wrap(ret);
		}
		decompress(input, output_size) {
			try {
				const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
				const ptr0 = passArray8ToWasm0(input, wasm.__wbindgen_malloc);
				const len0 = WASM_VECTOR_LEN;
				wasm.decompressstream_decompress(retptr, this.ptr, ptr0, len0, output_size);
				var r0 = getInt32Memory0()[retptr / 4 + 0];
				var r1 = getInt32Memory0()[retptr / 4 + 1];
				if (getInt32Memory0()[retptr / 4 + 2]) throw takeObject(r1);
				return BrotliStreamResult.__wrap(r0);
			} finally {
				wasm.__wbindgen_add_to_stack_pointer(16);
			}
		}
		total_out() {
			return wasm.decompressstream_total_out(this.ptr) >>> 0;
		}
	};
	async function load(module, imports) {
		if (typeof Response === "function" && module instanceof Response) {
			if (typeof WebAssembly.instantiateStreaming === "function") try {
				return await WebAssembly.instantiateStreaming(module, imports);
			} catch (e) {
				if (module.headers.get("Content-Type") != "application/wasm") console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
				else throw e;
			}
			const bytes = await module.arrayBuffer();
			return await WebAssembly.instantiate(bytes, imports);
		} else {
			const instance = await WebAssembly.instantiate(module, imports);
			if (instance instanceof WebAssembly.Instance) return {
				instance,
				module
			};
			else return instance;
		}
	}
	async function init(input) {
		const imports = {};
		imports.wbg = {};
		imports.wbg.__wbindgen_is_undefined = function(arg0) {
			return getObject(arg0) === void 0;
		};
		imports.wbg.__wbindgen_is_object = function(arg0) {
			const val = getObject(arg0);
			return typeof val === "object" && val !== null;
		};
		imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
			return addHeapObject(getStringFromWasm0(arg0, arg1));
		};
		imports.wbg.__wbindgen_error_new = function(arg0, arg1) {
			return addHeapObject(new Error(getStringFromWasm0(arg0, arg1)));
		};
		imports.wbg.__wbindgen_json_serialize = function(arg0, arg1) {
			const obj = getObject(arg1);
			const ptr0 = passStringToWasm0(JSON.stringify(obj === void 0 ? null : obj), wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
			const len0 = WASM_VECTOR_LEN;
			getInt32Memory0()[arg0 / 4 + 1] = len0;
			getInt32Memory0()[arg0 / 4 + 0] = ptr0;
		};
		imports.wbg.__wbg_new_693216e109162396 = function() {
			return addHeapObject(new Error());
		};
		imports.wbg.__wbg_stack_0ddaca5d1abfb52f = function(arg0, arg1) {
			const ret = getObject(arg1).stack;
			const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
			const len0 = WASM_VECTOR_LEN;
			getInt32Memory0()[arg0 / 4 + 1] = len0;
			getInt32Memory0()[arg0 / 4 + 0] = ptr0;
		};
		imports.wbg.__wbg_error_09919627ac0992f5 = function(arg0, arg1) {
			try {
				console.error(getStringFromWasm0(arg0, arg1));
			} finally {
				wasm.__wbindgen_free(arg0, arg1);
			}
		};
		imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
			takeObject(arg0);
		};
		imports.wbg.__wbindgen_throw = function(arg0, arg1) {
			throw new Error(getStringFromWasm0(arg0, arg1));
		};
		if (typeof input === "string" || typeof Request === "function" && input instanceof Request || typeof URL === "function" && input instanceof URL) input = fetch(input);
		const { instance, module } = await load(await input, imports);
		wasm = instance.exports;
		init.__wbindgen_wasm_module = module;
		return wasm;
	}
	async function _init() {
		const url = await _GM.getResourceUrl("brotli_wasm_bg");
		await init(gBase64.toUint8Array(url.slice(url.indexOf(","))).buffer);
		return brotli_wasm_exports;
	}
	var brotli = await(_init());
	var base64url = {
		decode: gBase64.decode,
		encode: (v) => gBase64.encode(v, true),
		decodeBytes: (v) => gBase64.toUint8Array(v),
		encodeBytes: (v) => gBase64.fromUint8Array(v, true)
	};
	function marshal(object) {
		return base64url.encodeBytes(brotli.compress(new TextEncoder().encode(JSON.stringify(object))));
	}
	function unmarshal(str) {
		return JSON.parse(new TextDecoder().decode(brotli.decompress(base64url.decodeBytes(str))));
	}
	function parseHeaders(value) {
		return Object.fromEntries(value.split("\r\n").map((line) => {
			var c = line.indexOf(":");
			return [line.substring(0, c), line.substring(c + 1).trimStart()];
		}));
	}
	function toDataTransfer(file) {
		const data = new DataTransfer();
		if (file instanceof Array) file.forEach((f) => {
			data.items.add(f);
		});
		else data.items.add(file);
		return data;
	}
	function unescapeHtml(value) {
		const textarea = document.createElement("textarea");
		textarea.innerHTML = value;
		return textarea.value;
	}
	async function nextMutation(target, type, subtree, timeout, sender) {
		try {
			console.debug(`Waiting for mutations with timeout of ${timeout ??= 3e4}.`);
			return await Promise.race([new Promise((_, reject) => {
				setTimeout(() => reject("Timeout"), timeout);
			}), new Promise((resolve) => {
				const observer = new MutationObserver((mutations) => {
					if (mutations.some((m) => m.type === type)) {
						observer.disconnect();
						resolve();
					}
				});
				observer.observe(target, {
					[type]: true,
					subtree
				});
				sender?.dispatchEvent(new Event("change"));
			})]);
		} catch (reason) {
			console.warn(reason);
		}
	}
	function onDescendantAdded(target, subtree, callback) {
		new MutationObserver((mutations) => {
			mutations.filter((m) => m.type === "childList" && m.addedNodes.length).flatMap((m) => [...m.addedNodes]).forEach(callback);
		}).observe(target, {
			childList: true,
			subtree
		});
	}
	var _reSplit = /^((?:\p{L}|\p{P}|\s)+)\s+[(()]((?:\p{L}|\p{P}|\s)+)[))]$/u;
	function trySelect(select, name) {
		name = name.toLowerCase();
		let options = [...select.options].map((option) => ({
			name: option.textContent?.toLowerCase(),
			key: option.value
		})).filter((p) => p.name);
		let seq = options.filter((p) => p.name === name);
		if (seq.length) return seq.length === 1 && (select.value = seq.at(0).key);
		seq = options.filter((p) => (p.name?.indexOf(name) ?? NaN) >= 0 || p.name && (name?.indexOf(p.name) ?? NaN) >= 0);
		if (seq.length) return seq.length === 1 && (select.value = seq.at(0).key);
		const match = name.match(_reSplit);
		const names = match ? [match[1], match[2]] : [name];
		options = options.flatMap((p) => {
			const m = p.name.match(_reSplit);
			return m ? [{
				name: m[1],
				key: p.key
			}, {
				name: m[2],
				key: p.key
			}] : p;
		}).filter((p) => p.name);
		seq = options.filter((p) => names.some((n) => (p.name?.indexOf(n) ?? NaN) >= 0 || p.name && (n?.indexOf(p.name) ?? NaN) >= 0));
		return seq.length === 1 && (select.value = seq.at(0).key);
	}
	var l10n_default = {
		parse: function(val) {
			val = val.trim().normalize();
			const match = val.match(/^(.+?)\s*\((.+)\)$/);
			if (!match) return val;
			let [latin, native] = match.slice(1);
			const trilingual = native.trim().match(/^(.+?)(?:\s+\/\s+|\s*/\s*)(.*\p{Script=Han}.*)$/u);
			if (trilingual) return {
				latin,
				native: trilingual[1]
			};
			let l = match[1].match(/[^\P{L}\p{Script=Latin}]/gu)?.length ?? 0;
			let r = match[2].match(/[^\P{L}\p{Script=Latin}]/gu)?.length ?? 0;
			if (l === r) {
				l = match[1].match(/[^\P{L}a-zA-Z]/gu)?.length ?? 0;
				r = match[2].match(/[^\P{L}a-zA-Z]/gu)?.length ?? 0;
			}
			if (l > r) [latin, native] = [native, latin];
			else if (l === r && latin.length < native.length) [latin, native] = [native, latin];
			return {
				latin,
				native
			};
		},
		format: function(val, prefer = "latin") {
			if (typeof val === "string") return val;
			switch (prefer) {
				default: return `${val.latin} (${val.native})`;
				case "native": return `${val.native} (${val.latin})`;
				case "cjk": return this.format(val, /\p{Script=Han}|\p{Script=Hiragana}|\p{Script=Katakana}|\p{Script=Hangul}/u.test(val.native) ? "native" : "latin");
				case "zh":
					const z = /\p{Script=Han}/u.test(val.native) && !/\p{Script=Hiragana}|\p{Script=Katakana}|\p{Script=Hangul}/u.test(val.native);
					return this.format(val, z ? "native" : "latin");
			}
		},
		select: function(val, prefer = "latin") {
			if (typeof val === "string") return val;
			return prefer === "latin" ? val.latin : val.native;
		}
	};
	function _throw(ex) {
		throw ex;
	}
	var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
	var alphabet = "0123456789abcdef";
	var encodeLookup = [];
	var decodeLookup = [];
	for (let i = 0; i < 256; i++) {
		encodeLookup[i] = alphabet[i >> 4 & 15] + alphabet[i & 15];
		if (i < 16) if (i < 10) decodeLookup[48 + i] = i;
		else decodeLookup[87 + i] = i;
	}
	var arr2hex = (data) => {
		const length = data.length;
		let string = "";
		let i = 0;
		while (i < length) string += encodeLookup[data[i++]];
		return string;
	};
	var hex2arr = (str) => {
		const sizeof = str.length >> 1;
		const length = sizeof << 1;
		const array = new Uint8Array(sizeof);
		let n = 0;
		let i = 0;
		while (i < length) array[n++] = decodeLookup[str.charCodeAt(i++)] << 4 | decodeLookup[str.charCodeAt(i++)];
		return array;
	};
	var concat = (chunks, size = 0) => {
		const length = chunks.length || 0;
		if (!size) {
			let i = length;
			while (i--) size += chunks[i].length;
		}
		const b = new Uint8Array(size);
		let offset = size;
		let i = length;
		while (i--) {
			offset -= chunks[i].length;
			b.set(chunks[i], offset);
		}
		return b;
	};
	var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	var lookup = typeof Uint8Array === "undefined" ? [] : new Uint8Array(256);
	for (var i = 0; i < chars.length; i++) lookup[chars.charCodeAt(i)] = i;
	var encode$1 = function(arraybuffer) {
		var bytes = new Uint8Array(arraybuffer), i, len = bytes.length, base64 = "";
		for (i = 0; i < len; i += 3) {
			base64 += chars[bytes[i] >> 2];
			base64 += chars[(bytes[i] & 3) << 4 | bytes[i + 1] >> 4];
			base64 += chars[(bytes[i + 1] & 15) << 2 | bytes[i + 2] >> 6];
			base64 += chars[bytes[i + 2] & 63];
		}
		if (len % 3 === 2) base64 = base64.substring(0, base64.length - 1) + "=";
		else if (len % 3 === 1) base64 = base64.substring(0, base64.length - 2) + "==";
		return base64;
	};
	var decoder = new TextDecoder();
	var arr2text = (data, enc) => {
		if (!enc) return decoder.decode(data);
		return new TextDecoder(enc).decode(data);
	};
	var encoder$1 = new TextEncoder();
	var text2arr = (str) => encoder$1.encode(str);
	var arr2base = (data) => encode$1(data);
	var scope = typeof window !== "undefined" ? window : self;
	var crypto = scope.crypto || scope.msCrypto || {};
	var subtle = crypto.subtle || crypto.webkitSubtle;
	var formatMap = {
		hex: arr2hex,
		base64: arr2base
	};
	var hash = async (data, format, algo = "sha-1") => {
		if (!subtle) throw new Error("no web crypto support");
		if (typeof data === "string") data = text2arr(data);
		const out = new Uint8Array(await subtle.digest(algo, data));
		return format ? formatMap[format](out) : out;
	};
	function digitCount(value) {
		const sign = value < 0 ? 1 : 0;
		value = Math.abs(Number(value || 1));
		return Math.floor(Math.log10(value)) + 1 + sign;
	}
	function getType(value) {
		if (ArrayBuffer.isView(value)) return "arraybufferview";
		if (Array.isArray(value)) return "array";
		if (value instanceof Number) return "number";
		if (value instanceof Boolean) return "boolean";
		if (value instanceof Set) return "set";
		if (value instanceof Map) return "map";
		if (value instanceof String) return "string";
		if (value instanceof ArrayBuffer) return "arraybuffer";
		return typeof value;
	}
	function encode(data, buffer, offset) {
		const buffers = [];
		let result = null;
		encode._encode(buffers, data);
		result = concat(buffers);
		encode.bytes = result.length;
		if (ArrayBuffer.isView(buffer)) {
			buffer.set(result, offset);
			return buffer;
		}
		return result;
	}
	encode.bytes = -1;
	encode._floatConversionDetected = false;
	encode._encode = function(buffers, data) {
		if (data == null) return;
		switch (getType(data)) {
			case "object":
				encode.dict(buffers, data);
				break;
			case "map":
				encode.dictMap(buffers, data);
				break;
			case "array":
				encode.list(buffers, data);
				break;
			case "set":
				encode.listSet(buffers, data);
				break;
			case "string":
				encode.string(buffers, data);
				break;
			case "number":
				encode.number(buffers, data);
				break;
			case "boolean":
				encode.number(buffers, data);
				break;
			case "arraybufferview":
				encode.buffer(buffers, new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
				break;
			case "arraybuffer":
				encode.buffer(buffers, new Uint8Array(data));
				break;
		}
	};
	var buffE = new Uint8Array([101]);
	var buffD = new Uint8Array([100]);
	var buffL = new Uint8Array([108]);
	encode.buffer = function(buffers, data) {
		buffers.push(text2arr(data.length + ":"), data);
	};
	encode.string = function(buffers, data) {
		buffers.push(text2arr(text2arr(data).byteLength + ":" + data));
	};
	encode.number = function(buffers, data) {
		if (Number.isInteger(data)) return buffers.push(text2arr("i" + BigInt(data) + "e"));
		const maxLo = 2147483648;
		const hi = data / maxLo << 0;
		const lo = data % maxLo << 0;
		const val = hi * maxLo + lo;
		buffers.push(text2arr("i" + val + "e"));
		if (val !== data && !encode._floatConversionDetected) {
			encode._floatConversionDetected = true;
			console.warn("WARNING: Possible data corruption detected with value \"" + data + "\":", "Bencoding only defines support for integers, value was converted to \"" + val + "\"");
			console.trace();
		}
	};
	encode.dict = function(buffers, data) {
		buffers.push(buffD);
		let j = 0;
		let k;
		const keys = Object.keys(data).sort();
		const kl = keys.length;
		for (; j < kl; j++) {
			k = keys[j];
			if (data[k] == null) continue;
			encode.string(buffers, k);
			encode._encode(buffers, data[k]);
		}
		buffers.push(buffE);
	};
	encode.dictMap = function(buffers, data) {
		buffers.push(buffD);
		const keys = Array.from(data.keys()).sort();
		for (const key of keys) {
			if (data.get(key) == null) continue;
			ArrayBuffer.isView(key) ? encode._encode(buffers, key) : encode.string(buffers, String(key));
			encode._encode(buffers, data.get(key));
		}
		buffers.push(buffE);
	};
	encode.list = function(buffers, data) {
		let i = 0;
		const c = data.length;
		buffers.push(buffL);
		for (; i < c; i++) {
			if (data[i] == null) continue;
			encode._encode(buffers, data[i]);
		}
		buffers.push(buffE);
	};
	encode.listSet = function(buffers, data) {
		buffers.push(buffL);
		for (const item of data) {
			if (item == null) continue;
			encode._encode(buffers, item);
		}
		buffers.push(buffE);
	};
	var INTEGER_START = 105;
	var STRING_DELIM = 58;
	var DICTIONARY_START = 100;
	var LIST_START = 108;
	var END_OF_TYPE = 101;
	function getIntFromBuffer(buffer, start, end) {
		let sum = 0;
		let sign = 1;
		for (let i = start; i < end; i++) {
			const num = buffer[i];
			if (num < 58 && num >= 48) {
				sum = sum * 10 + (num - 48);
				continue;
			}
			if (i === start && num === 43) continue;
			if (i === start && num === 45) {
				sign = -1;
				continue;
			}
			if (num === 46) break;
			throw new Error("not a number: buffer[" + i + "] = " + num);
		}
		return sum * sign;
	}
	function decode$1(data, start, end, encoding) {
		if (data == null || data.length === 0) return null;
		if (typeof start !== "number" && encoding == null) {
			encoding = start;
			start = void 0;
		}
		if (typeof end !== "number" && encoding == null) {
			encoding = end;
			end = void 0;
		}
		decode$1.position = 0;
		decode$1.encoding = encoding || null;
		decode$1.data = !ArrayBuffer.isView(data) ? text2arr(data) : new Uint8Array(data.slice(start, end));
		decode$1.bytes = decode$1.data.length;
		return decode$1.next();
	}
	decode$1.bytes = 0;
	decode$1.position = 0;
	decode$1.data = null;
	decode$1.encoding = null;
	decode$1.next = function() {
		switch (decode$1.data[decode$1.position]) {
			case DICTIONARY_START: return decode$1.dictionary();
			case LIST_START: return decode$1.list();
			case INTEGER_START: return decode$1.integer();
			default: return decode$1.buffer();
		}
	};
	decode$1.find = function(chr) {
		let i = decode$1.position;
		const c = decode$1.data.length;
		const d = decode$1.data;
		while (i < c) {
			if (d[i] === chr) return i;
			i++;
		}
		throw new Error("Invalid data: Missing delimiter \"" + String.fromCharCode(chr) + "\" [0x" + chr.toString(16) + "]");
	};
	decode$1.dictionary = function() {
		decode$1.position++;
		const dict = {};
		while (decode$1.data[decode$1.position] !== END_OF_TYPE) {
			const buffer = decode$1.buffer();
			let key = arr2text(buffer);
			if (key.includes("�")) key = arr2hex(buffer);
			dict[key] = decode$1.next();
		}
		decode$1.position++;
		return dict;
	};
	decode$1.list = function() {
		decode$1.position++;
		const lst = [];
		while (decode$1.data[decode$1.position] !== END_OF_TYPE) lst.push(decode$1.next());
		decode$1.position++;
		return lst;
	};
	decode$1.integer = function() {
		const end = decode$1.find(END_OF_TYPE);
		const number = getIntFromBuffer(decode$1.data, decode$1.position + 1, end);
		decode$1.position += end + 1 - decode$1.position;
		return number;
	};
	decode$1.buffer = function() {
		let sep = decode$1.find(STRING_DELIM);
		const length = getIntFromBuffer(decode$1.data, decode$1.position, sep);
		const end = ++sep + length;
		decode$1.position = end;
		return decode$1.encoding ? arr2text(decode$1.data.slice(sep, end)) : decode$1.data.slice(sep, end);
	};
	function listLength(list) {
		let length = 2;
		for (const value of list) length += encodingLength$1(value);
		return length;
	}
	function mapLength(map) {
		let length = 2;
		for (const [key, value] of map) {
			const keyLength = text2arr(key).byteLength;
			length += digitCount(keyLength) + 1 + keyLength;
			length += encodingLength$1(value);
		}
		return length;
	}
	function objectLength(value) {
		let length = 2;
		const keys = Object.keys(value);
		for (let i = 0; i < keys.length; i++) {
			const keyLength = text2arr(keys[i]).byteLength;
			length += digitCount(keyLength) + 1 + keyLength;
			length += encodingLength$1(value[keys[i]]);
		}
		return length;
	}
	function stringLength(value) {
		const length = text2arr(value).byteLength;
		return digitCount(length) + 1 + length;
	}
	function arrayBufferLength(value) {
		const length = value.byteLength - value.byteOffset;
		return digitCount(length) + 1 + length;
	}
	function encodingLength$1(value) {
		const length = 0;
		if (value == null) return length;
		const type = getType(value);
		switch (type) {
			case "arraybufferview": return arrayBufferLength(value);
			case "string": return stringLength(value);
			case "array":
			case "set": return listLength(value);
			case "number": return 1 + digitCount(Math.floor(value)) + 1;
			case "bigint": return 1 + value.toString().length + 1;
			case "object": return objectLength(value);
			case "map": return mapLength(value);
			default: throw new TypeError(`Unsupported value of type "${type}"`);
		}
	}
	var bencode_default = {
		encode,
		decode: decode$1,
		byteLength: encodingLength$1,
		encodingLength: encodingLength$1
	};
	__commonJSMin(((exports, module) => {
		module.exports = {};
	}))();
	self.Blob;
	self.File;
	self.FormData;
	self.Headers;
	self.Request;
	self.Response;
	self.AbortController;
	self.AbortSignal;
	self.fetch;
	var byteTable = [
		255,
		255,
		26,
		27,
		28,
		29,
		30,
		31,
		255,
		255,
		255,
		255,
		255,
		255,
		255,
		255,
		255,
		0,
		1,
		2,
		3,
		4,
		5,
		6,
		7,
		8,
		9,
		10,
		11,
		12,
		13,
		14,
		15,
		16,
		17,
		18,
		19,
		20,
		21,
		22,
		23,
		24,
		25,
		255,
		255,
		255,
		255,
		255,
		255,
		0,
		1,
		2,
		3,
		4,
		5,
		6,
		7,
		8,
		9,
		10,
		11,
		12,
		13,
		14,
		15,
		16,
		17,
		18,
		19,
		20,
		21,
		22,
		23,
		24,
		25,
		255,
		255,
		255,
		255,
		255
	];
	var decode = function(encoded) {
		if (!ArrayBuffer.isView(encoded) && typeof encoded !== "string") throw new TypeError("base32.decode only takes Buffer or string as parameter");
		let shiftIndex = 0;
		let plainDigit = 0;
		let plainChar;
		let plainPos = 0;
		if (!ArrayBuffer.isView(encoded)) encoded = text2arr(encoded);
		const decoded = new Uint8Array(Math.ceil(encoded.length * 5 / 8));
		for (let i = 0; i < encoded.length; i++) {
			if (encoded[i] === 61) break;
			const encodedByte = encoded[i] - 48;
			if (encodedByte < byteTable.length) {
				plainDigit = byteTable[encodedByte];
				if (shiftIndex <= 3) {
					shiftIndex = (shiftIndex + 5) % 8;
					if (shiftIndex === 0) {
						plainChar |= plainDigit;
						decoded[plainPos] = plainChar;
						plainPos++;
						plainChar = 0;
					} else plainChar |= 255 & plainDigit << 8 - shiftIndex;
				} else {
					shiftIndex = (shiftIndex + 5) % 8;
					plainChar |= 255 & plainDigit >>> shiftIndex;
					decoded[plainPos] = plainChar;
					plainPos++;
					plainChar = 255 & plainDigit << 8 - shiftIndex;
				}
			} else throw new Error("Invalid input - it is not base32 encoded string");
		}
		return decoded.subarray(0, plainPos);
	};
	function parseRange(range) {
		const generateRange = (start, end = start) => Array.from({ length: end - start + 1 }, (cur, idx) => idx + start);
		return range.reduce((acc, cur, idx, arr) => {
			const r = cur.split("-").map((cur) => parseInt(cur));
			return acc.concat(generateRange(...r));
		}, []);
	}
	function magnetURIDecode(uri) {
		const result = {};
		const data = uri.split("magnet:?")[1];
		(data && data.length >= 0 ? data.split("&") : []).forEach((param) => {
			const keyval = param.split("=");
			if (keyval.length !== 2) return;
			const key = keyval[0];
			let val = keyval[1];
			if (key === "dn") val = decodeURIComponent(val).replace(/\+/g, " ");
			if (key === "tr" || key === "xs" || key === "as" || key === "ws") val = decodeURIComponent(val);
			if (key === "kt") val = decodeURIComponent(val).split("+");
			if (key === "ix") val = Number(val);
			if (key === "so") val = parseRange(decodeURIComponent(val).split(","));
			if (result[key]) {
				if (!Array.isArray(result[key])) result[key] = [result[key]];
				result[key].push(val);
			} else result[key] = val;
		});
		let m;
		if (result.xt) (Array.isArray(result.xt) ? result.xt : [result.xt]).forEach((xt) => {
			if (m = xt.match(/^urn:btih:(.{40})/)) result.infoHash = m[1].toLowerCase();
			else if (m = xt.match(/^urn:btih:(.{32})/)) result.infoHash = arr2hex(decode(m[1]));
			else if (m = xt.match(/^urn:btmh:1220(.{64})/)) result.infoHashV2 = m[1].toLowerCase();
		});
		if (result.xs) (Array.isArray(result.xs) ? result.xs : [result.xs]).forEach((xs) => {
			if (m = xs.match(/^urn:btpk:(.{64})/)) result.publicKey = m[1].toLowerCase();
		});
		if (result.infoHash) result.infoHashBuffer = hex2arr(result.infoHash);
		if (result.infoHashV2) result.infoHashV2Buffer = hex2arr(result.infoHashV2);
		if (result.publicKey) result.publicKeyBuffer = hex2arr(result.publicKey);
		if (result.dn) result.name = result.dn;
		if (result.kt) result.keywords = result.kt;
		result.announce = [];
		if (typeof result.tr === "string" || Array.isArray(result.tr)) result.announce = result.announce.concat(result.tr);
		result.urlList = [];
		if (typeof result.as === "string" || Array.isArray(result.as)) result.urlList = result.urlList.concat(result.as);
		if (typeof result.ws === "string" || Array.isArray(result.ws)) result.urlList = result.urlList.concat(result.ws);
		result.peerAddresses = [];
		if (typeof result["x.pe"] === "string" || Array.isArray(result["x.pe"])) result.peerAddresses = result.peerAddresses.concat(result["x.pe"]);
		result.announce = Array.from(new Set(result.announce));
		result.urlList = Array.from(new Set(result.urlList));
		result.peerAddresses = Array.from(new Set(result.peerAddresses));
		return result;
	}
	function join(...args) {
		return args.filter(Boolean).join("/").replace(/\/+/g, "/");
	}
	var path_default = {
		sep: "/",
		join
	};
	__commonJSMin(((exports, module) => {
		var promise;
		module.exports = typeof queueMicrotask === "function" ? queueMicrotask.bind(typeof window !== "undefined" ? window : global) : (cb) => (promise || (promise = Promise.resolve())).then(cb).catch((err) => setTimeout(() => {
			throw err;
		}, 0));
	}))();
	async function parseTorrent(torrentId) {
		if (typeof torrentId === "string" && /^(stream-)?magnet:/.test(torrentId)) {
			const torrentObj = magnetURIDecode(torrentId);
			if (!torrentObj.infoHash) throw new Error("Invalid torrent identifier");
			return torrentObj;
		} else if (typeof torrentId === "string" && (/^[a-f0-9]{40}$/i.test(torrentId) || /^[a-z2-7]{32}$/i.test(torrentId))) return magnetURIDecode(`magnet:?xt=urn:btih:${torrentId}`);
		else if (ArrayBuffer.isView(torrentId) && torrentId.length === 20) return magnetURIDecode(`magnet:?xt=urn:btih:${arr2hex(torrentId)}`);
		else if (ArrayBuffer.isView(torrentId)) return await decodeTorrentFile(torrentId);
		else if (torrentId && torrentId.infoHash) {
			torrentId.infoHash = torrentId.infoHash.toLowerCase();
			if (!torrentId.announce) torrentId.announce = [];
			if (typeof torrentId.announce === "string") torrentId.announce = [torrentId.announce];
			if (!torrentId.urlList) torrentId.urlList = [];
			return torrentId;
		} else throw new Error("Invalid torrent identifier");
	}
	async function decodeTorrentFile(torrent) {
		if (ArrayBuffer.isView(torrent)) torrent = bencode_default.decode(torrent);
		ensure(torrent.info, "info");
		ensure(torrent.info["name.utf-8"] || torrent.info.name, "info.name");
		ensure(torrent.info["piece length"], "info['piece length']");
		ensure(torrent.info.pieces, "info.pieces");
		if (torrent.info.files) torrent.info.files.forEach((file) => {
			ensure(typeof file.length === "number", "info.files[0].length");
			ensure(file["path.utf-8"] || file.path, "info.files[0].path");
		});
		else ensure(typeof torrent.info.length === "number", "info.length");
		const result = {
			info: torrent.info,
			infoBuffer: bencode_default.encode(torrent.info),
			name: arr2text(torrent.info["name.utf-8"] || torrent.info.name),
			announce: []
		};
		result.infoHashBuffer = await hash(result.infoBuffer);
		result.infoHash = arr2hex(result.infoHashBuffer);
		if (torrent.info.private !== void 0) result.private = !!torrent.info.private;
		if (torrent["creation date"]) result.created = new Date(torrent["creation date"] * 1e3);
		if (torrent["created by"]) result.createdBy = arr2text(torrent["created by"]);
		if (ArrayBuffer.isView(torrent.comment)) result.comment = arr2text(torrent.comment);
		if (Array.isArray(torrent["announce-list"]) && torrent["announce-list"].length > 0) torrent["announce-list"].forEach((urls) => {
			urls.forEach((url) => {
				result.announce.push(arr2text(url));
			});
		});
		else if (torrent.announce) result.announce.push(arr2text(torrent.announce));
		if (ArrayBuffer.isView(torrent["url-list"])) torrent["url-list"] = torrent["url-list"].length > 0 ? [torrent["url-list"]] : [];
		result.urlList = (torrent["url-list"] || []).map((url) => arr2text(url));
		result.announce = Array.from(new Set(result.announce));
		result.urlList = Array.from(new Set(result.urlList));
		let sum = 0;
		result.files = (torrent.info.files || [torrent.info]).map((file, i) => {
			const parts = [].concat(result.name, file["path.utf-8"] || file.path || []).map((p) => ArrayBuffer.isView(p) ? arr2text(p) : p);
			sum += file.length;
			return {
				path: path_default.join.apply(null, [path_default.sep].concat(parts)).slice(1),
				name: parts[parts.length - 1],
				length: file.length,
				offset: sum - file.length
			};
		});
		result.length = sum;
		const lastFile = result.files[result.files.length - 1];
		result.pieceLength = torrent.info["piece length"];
		result.lastPieceLength = (lastFile.offset + lastFile.length) % result.pieceLength || result.pieceLength;
		result.pieces = splitPieces(torrent.info.pieces);
		return result;
	}
	function encodeTorrentFile(parsed) {
		const torrent = { info: parsed.info };
		torrent["announce-list"] = (parsed.announce || []).map((url) => {
			if (!torrent.announce) torrent.announce = url;
			url = text2arr(url);
			return [url];
		});
		torrent["url-list"] = parsed.urlList || [];
		if (parsed.private !== void 0) torrent.private = Number(parsed.private);
		if (parsed.created) torrent["creation date"] = parsed.created.getTime() / 1e3 | 0;
		if (parsed.createdBy) torrent["created by"] = parsed.createdBy;
		if (parsed.comment) torrent.comment = parsed.comment;
		return bencode_default.encode(torrent);
	}
	function splitPieces(buf) {
		const pieces = [];
		for (let i = 0; i < buf.length; i += 20) pieces.push(arr2hex(buf.slice(i, i + 20)));
		return pieces;
	}
	function ensure(bool, fieldName) {
		if (!bool) throw new Error(`Torrent is missing required field: ${fieldName}`);
	}
	async function patch(torrent, source) {
		const record = await parseTorrent(torrent);
		record.announce = [];
		record.comment = "";
		record.info.private = true;
		record.info.source = source;
		return encodeTorrentFile(record);
	}
	var data_default = {
		base: {
			"gazelle": {
				"include": {
					"source": [
						"/torrents.php",
						"/artist.php",
						"/collages.php"
					],
					"target": "/upload.php"
				},
				"exclude": { "download": "/torrents.php" },
				"actions": { "log": "viewlog" }
			},
			"nexusphp": {
				"include": {
					"source": "/plugin_details.php",
					"target": "/plugin_upload.php"
				},
				"exclude": { "download": "/download.php" }
			}
		},
		override: {
			"gazelle": {
				"DIC": {
					"hostname": "dicmusic.com",
					"selects": { "releaseType": {
						"1": "Album",
						"3": "Soundtrack",
						"5": "EP",
						"6": "Anthology",
						"7": "Compilation",
						"9": "Single",
						"11": "Live album",
						"13": "Remix",
						"14": "Bootleg",
						"15": "Interview",
						"16": "Mixtape",
						"17": "Demo",
						"18": "Concert recording",
						"19": "DJ Mix",
						"21": "Unknown"
					} }
				},
				"JPopsuki": {
					"hostname": "jpopsuki.eu",
					"selects": { "releaseType": {
						"Album": "Album",
						"Single": "Single",
						"PV": "PV",
						"DVD": "DVD",
						"TV-Music": "TV-Music",
						"TV-Variety": "TV-Variety",
						"TV-Drama": "TV-Drama",
						"Fansubs": "Fansubs",
						"Pictures": "Pictures",
						"Misc": "Misc"
					} },
					"actions": { "log": null }
				},
				"OPS": {
					"hostname": "orpheus.network",
					"selects": { "releaseType": {
						"1": "Album",
						"3": "Soundtrack",
						"5": "EP",
						"6": "Anthology",
						"7": "Compilation",
						"9": "Single",
						"11": "Live album",
						"13": "Remix",
						"14": "Bootleg",
						"15": "Interview",
						"16": "Mixtape",
						"17": "DJ Mix",
						"18": "Concert recording",
						"21": "Unknown"
					} }
				},
				"Redacted": {
					"hostname": "redacted.sh",
					"selects": { "releaseType": {
						"1": "Album",
						"3": "Soundtrack",
						"5": "EP",
						"6": "Anthology",
						"7": "Compilation",
						"9": "Single",
						"11": "Live album",
						"13": "Remix",
						"14": "Bootleg",
						"15": "Interview",
						"16": "Mixtape",
						"17": "Demo",
						"18": "Concert Recording",
						"19": "DJ Mix",
						"21": "Unknown"
					} },
					"actions": { "log": "loglist" }
				}
			},
			"nexusphp": {
				"OpenCD": { "hostname": "open.cd" },
				"TJUPT": {
					"hostname": "tjupt.org",
					"include": {
						"source": "/details.php",
						"target": "/upload.php"
					}
				}
			}
		}
	};
	var data = {};
	Object.keys(data_default.override).forEach((fw) => ((fw) => {
		data[fw] = {};
		Object.keys(data_default.override[fw]).forEach((st) => {
			const base = data_default.base[fw];
			let site = data_default.override[fw][st];
			site.include = {
				...base.include,
				...site.include
			};
			site.exclude = {
				...base.exclude,
				...site.exclude
			};
			site.actions = {
				...base.actions,
				...site.actions
			};
			data[fw][st] = site;
		});
	})(fw));
	var VoidElement = class {
		"#";
		constructor(tag) {
			this["#"] = tag;
		}
	};
	function Void(tag) {
		return () => new VoidElement(tag);
	}
	var ValueVoidElement = class {
		"#";
		$;
		constructor(tag, value) {
			this["#"] = tag;
			this.$ = value;
		}
	};
	var ViewElement = class {
		"#";
		$$;
		constructor(tag, children) {
			this["#"] = tag;
			this.$$ = children;
		}
	};
	function View(tag) {
		return (children) => new ViewElement(tag, children);
	}
	var ValueViewElement = class {
		"#";
		$;
		$$;
		constructor(tag, value, children) {
			this["#"] = tag;
			this.$$ = children;
			this.$ = value;
		}
	};
	function ValueView(tag) {
		return (value, children) => new ValueViewElement(tag, value, children);
	}
	var Bold = View("b");
	var Italic = View("i");
	var Underline = View("u");
	var Strikethrough = View("s");
	var Font = ValueView("font");
	var Size = ValueView("size");
	var Color = ValueView("color");
	var Heading = ValueView("h");
	var Sub = View("sub");
	var Sup = View("sup");
	var Anchor = ValueView("url");
	var ImageElement = class extends ValueVoidElement {
		alt;
		constructor(value, alt) {
			super("img", value);
			this.alt = alt;
		}
	};
	var Image = (value, alt) => new ImageElement(value, alt);
	var Code = View("code");
	var Spoiler = View("#spoiler");
	var HorizontalRule = Void("hr");
	var Align = ValueView("align");
	var Quote = ValueView("quote");
	var Pre = ValueView("pre");
	var Collapse = ValueView("#collapse");
	var ListItem = View("li");
	var UList = View("ul");
	var OList = View("ol");
	var Table = View("table");
	var TableRow = View("tr");
	var TableCell = View("td");
	var is = {
		void: (node) => (() => {
			return (input) => "hr" === input;
		})()(node["#"]),
		valueVoid: (node) => (() => {
			return (input) => "img" === input;
		})()(node["#"]),
		view: (node) => (() => {
			const _iv1 = new Set([
				"b",
				"i",
				"u",
				"s",
				"sub",
				"sup",
				"code",
				"#spoiler",
				"li",
				"ul",
				"ol",
				"table",
				"tr",
				"td"
			]);
			return (input) => true === _iv1.has(input);
		})()(node["#"]),
		valueView: (node) => (() => {
			return (input) => "font" === input || "size" === input || "color" === input || "h" === input || "url" === input || "align" === input || "quote" === input || "pre" === input || "#collapse" === input;
		})()(node["#"])
	};
	var BBText = class {
		"#" = "#text";
		$;
		constructor(value) {
			this.$ = value;
		}
	};
	function _clamp(value, min, max) {
		return Math.min(Math.max(value, min), max);
	}
	function _wrapNewline(nodes, position) {
		if (position === 2 || position === 3) {
			let f = nodes.at(0);
			if (f instanceof BBText) f.$ = f.$.replace(/^ */, "\n");
			else nodes.unshift(new BBText("\n"));
		}
		if (position === 1 || position === 3) {
			let l = nodes.at(-1);
			if (l instanceof BBText) l.$ = l.$.replace(/ *$/, "\n");
			else nodes.push(new BBText("\n"));
		}
		return nodes;
	}
	function _fromHTML(parent, baseURL, raw = false) {
		let index = -1;
		const origin = [...parent.childNodes];
		const result = [];
		while (++index < origin.length) {
			const n = origin[index];
			if (n instanceof globalThis.Text) {
				let last = result.at(-1);
				if (raw) result.push(new BBText(n.textContent ?? ""));
				else if (last instanceof BBText) last.$ = last.$ + (last.$.match(/\s$/) ? n.textContent?.trimStart() ?? "" : n.textContent ?? "").replace(/\s+/g, " ");
				else result.push(new BBText(n.textContent?.replace(/\s+/g, " ") ?? ""));
			} else if (n instanceof HTMLBRElement) _wrapNewline(result, 1);
			else if (n instanceof HTMLUListElement) result.push(UList([...n.children].map((item) => ListItem(_fromHTML(item, baseURL)))));
			else if (n instanceof HTMLOListElement) result.push(OList([...n.children].map((item) => ListItem(_fromHTML(item, baseURL)))));
			else if (n instanceof HTMLTableElement) if (n.classList.contains("hide")) result.push(Collapse(n.tBodies.item(0)?.rows?.item(0)?.cells?.item(0)?.textContent?.trim() ?? _throw(n), _fromHTML(n.tBodies.item(0)?.rows?.item(1)?.cells?.item(0) ?? _throw(n), baseURL)));
			else {
				const caption = [...n.children].at(0);
				if (caption instanceof HTMLTableCaptionElement) result.push(Align("center", _fromHTML(caption, baseURL)));
				result.push(Table([n.tHead, ...n.tBodies].filter((sec) => sec).flatMap((sec) => [...sec.rows]).map((row) => TableRow([...row.cells].map((cell) => TableCell(_fromHTML(cell, baseURL)))))));
			}
			else if (n instanceof HTMLQuoteElement) result.push(Quote("", _fromHTML(n, baseURL)));
			else if (n instanceof HTMLPreElement) result.push(Pre("", _fromHTML(n, baseURL, true)));
			else if (n instanceof HTMLFieldSetElement) {
				const firstChild = [...n.children].at(0);
				if (firstChild instanceof HTMLLegendElement) {
					n.removeChild(firstChild);
					result.push(Quote(firstChild.textContent?.trim() ?? "", _fromHTML(n, baseURL)));
				} else result.push(Quote("", _fromHTML(n, baseURL)));
			} else if (n instanceof HTMLFontElement) {
				let outer;
				const children = _fromHTML(n, baseURL);
				if (n.face) outer = Font(n.face, children);
				if (n.size) outer = Font(n.size, outer ? [outer] : children);
				if (n.color) outer = Font(n.color, outer ? [outer] : children);
				if (outer) result.push(outer);
				else result.push(...children);
			} else if (n instanceof HTMLHeadingElement) result.push(Heading(parseInt(n.tagName.slice(1)), _fromHTML(n, baseURL)));
			else if (n instanceof HTMLAnchorElement) {
				let href = n.href;
				if (!href.match(/^[a-z+]+:\/\//)) href = new URL(href, baseURL).toString();
				if (n.attributes.getNamedItem("onclick")?.value?.startsWith("QuoteJump")) {
					let offset = 0;
					while (offset++ < origin.length) if (origin.at(index + offset) instanceof HTMLQuoteElement) break;
					if (index + offset === origin.length) throw n;
					result.push(Quote([...n.children].at(0)?.textContent?.trim() ?? "", _fromHTML(origin.at(index + offset), baseURL)));
					index += offset;
				} else result.push(Anchor(href, _fromHTML(n, baseURL)));
			} else if (n instanceof HTMLImageElement) if (n.classList.contains("listicon")) result.push(ListItem([]));
			else result.push(Image(n.src, n.alt));
			else if (n instanceof HTMLHRElement) result.push(HorizontalRule());
			else if (n instanceof HTMLElement) {
				if (n.classList.contains("mature")) continue;
				const tagName = n.tagName.toLowerCase();
				const nextElement = origin.slice(index).filter((node) => node instanceof HTMLElement && !(node instanceof HTMLBRElement)).at(1);
				if (n.classList.contains("quoteheader")) {
					let offset = 0;
					while (offset++ < origin.length) if (origin.at(index + offset) instanceof HTMLQuoteElement) break;
					if (index + offset === origin.length) throw n;
					result.push(Quote(n.textContent?.trim() ?? "", _fromHTML(origin.at(index + offset), baseURL)));
					index += offset;
				} else if (tagName === "strong" && nextElement instanceof HTMLAnchorElement && (nextElement.attributes.getNamedItem("onclick")?.value?.indexOf("spoiler") ?? NaN) >= 0) {
					let offset = 0;
					while (offset++ < origin.length) if (origin.at(index + offset) instanceof HTMLQuoteElement) break;
					if (index + offset === origin.length) throw n;
					result.push(Collapse(n.textContent?.trim() ?? "", _fromHTML(origin.at(index + offset), baseURL)));
					index += offset;
					index += 3;
				} else if (n.classList.contains("codetop")) {
					let nextElement;
					do {
						nextElement = origin[++index];
						if (nextElement === void 0) throw n;
					} while (!(nextElement instanceof HTMLElement));
					result.push(..._fromHTML(nextElement, baseURL));
					let offset = 1;
					nextElement = origin[index + offset];
					while (!(nextElement instanceof HTMLElement) && nextElement) nextElement = origin[index + ++offset];
					if (nextElement instanceof HTMLElement) {
						if (nextElement.children.item(0)?.classList?.contains("codemain")) {
							result.push(..._fromHTML(nextElement.children.item(0), baseURL));
							index += offset;
						}
					}
				} else if (tagName === "b" || tagName === "strong") result.push(Bold(_fromHTML(n, baseURL)));
				else if (tagName === "i" || tagName === "i") result.push(Italic(_fromHTML(n, baseURL)));
				else if (tagName === "u" || tagName === "ins") result.push(Underline(_fromHTML(n, baseURL)));
				else if (tagName === "s" || tagName === "strike" || tagName === "del") result.push(Strikethrough(_fromHTML(n, baseURL)));
				else if (tagName === "code" || tagName === "tt") result.push(Code(_fromHTML(n, baseURL)));
				else if (tagName === "sub") result.push(Sub(_fromHTML(n, baseURL)));
				else if (tagName === "sup") result.push(Sup(_fromHTML(n, baseURL)));
				else if (tagName === "center" || tagName === "marquee") result.push(Align("center", _fromHTML(n, baseURL)));
				else {
					let outer;
					let allowUnwrap = false;
					const children = _fromHTML(n, baseURL);
					const classes = [...n.classList];
					const fontWeight = n.style.fontWeight.toLowerCase();
					if (parseFloat(fontWeight) >= 550 || fontWeight === "bold" || fontWeight === "bolder") outer = Bold(children);
					const fontStyle = n.style.fontStyle.toLowerCase();
					if (fontStyle === "italic" || fontStyle.startsWith("oblique")) outer = Italic(outer ? [outer] : children);
					const textDecoration = n.style.textDecoration.toLowerCase().split(" ");
					const textDecorationLine = n.style.textDecorationLine.toLowerCase();
					if (textDecorationLine === "underline" || textDecoration.indexOf("underline") >= 0) outer = Underline(outer ? [outer] : children);
					else if (textDecorationLine === "line-through" || textDecoration.indexOf("line-through") >= 0) outer = Strikethrough(outer ? [outer] : children);
					const fontFamily = n.style.fontFamily.toLowerCase().split(";").at(0);
					if (fontFamily) if (n.children.item(0) instanceof HTMLPreElement) allowUnwrap = true;
					else outer = Font(fontFamily, outer ? [outer] : children);
					const fontSize = n.style.fontSize.toLowerCase();
					const sizeClasses = classes.filter((x) => x.startsWith("size"));
					if (sizeClasses.length) outer = Size(_clamp(parseInt(sizeClasses.at(0).slice(4)), 0, 7), outer ? [outer] : children);
					else if (fontSize) console.warn(fontSize);
					const color = n.style.color.toLowerCase();
					if (color) outer = Color(color, outer ? [outer] : children);
					const align = n.style.textAlign.toLowerCase();
					if ((() => {
						return (input) => "left" === input || "center" === input || "right" === input;
					})()(align)) outer = Align(align, outer ? [outer] : children);
					if (tagName === "span") {
						if (!(n.attributes.getNamedItem("class")?.value || n.attributes.getNamedItem("style")?.value)) allowUnwrap = true;
						else if (n.classList.contains("mask")) outer = Spoiler(children);
					}
					if (tagName === "p") if (n.classList.contains("sub")) result.push(Heading(2, children));
					else result.push(..._wrapNewline(outer ? [outer] : children, 3));
					else if (outer) if (tagName === "p") {} else result.push(outer);
					else if (allowUnwrap) result.push(...children);
					else {
						console.warn(n);
						console.log(n.outerHTML);
						if (tagName === "span") result.push(...children);
					}
				}
			} else throw n;
		}
		return result;
	}
	function fromHTML(parent, baseURL) {
		return _fromHTML(parent, baseURL);
	}
	var bbcode_default$2 = {
		fromHTML,
		is
	};
	var _isBigEndian = (() => {
		const u32 = new Uint32Array([1464816196]);
		const u8 = new Uint8Array(u32.buffer);
		switch (u8[0]) {
			case 87: return true;
			case 68: return false;
			default: throw u8;
		}
	})();
	var _bomUtf16LE = new Uint8Array([255, 254]);
	function toFile(logs, title) {
		if (logs.recovered) console.warn(logs);
		return logs.encoded ? logs.encoded.map((log, l) => new File([base64url.decodeBytes(log)], (0, import_sanitize_filename.default)(logs.encoded.length > 1 ? `${title}.${l + 1}.log` : `${title}.log`), { type: "text/plain" })) : logs.plain.map((log, l) => {
			log = log.trim();
			const name = (0, import_sanitize_filename.default)(logs.plain.length > 1 ? `${title}.${l + 1}.log` : `${title}.log`);
			if (log.startsWith("Exact Audio Copy")) {
				const array = new Uint16Array(log.length);
				let c = log.length;
				while (c >= 0) array[c] = log.charCodeAt(--c);
				if (_isBigEndian) {
					c = array.length;
					while (c >= 0) {
						const ch = array[--c];
						array[c] = (ch >> 8) + ((ch & 255) << 8);
					}
				}
				return new File([_bomUtf16LE, array.buffer], name, { type: "text/plain" });
			} else if (log.startsWith("X Lossless Decoder")) return new File([new TextEncoder().encode(`${log}\n`).buffer], name, { type: "text/plain" });
			else throw log;
		});
	}
	var utf8 = new TextDecoder("utf-8", {
		ignoreBOM: true,
		fatal: true
	});
	var utf16BE = new TextDecoder("utf-16be", {
		ignoreBOM: true,
		fatal: true
	});
	var utf16LE = new TextDecoder("utf-16le", {
		ignoreBOM: true,
		fatal: true
	});
	function toString(logs) {
		return logs.encoded ? logs.encoded.map((log) => {
			const array = base64url.decodeBytes(log);
			if (array[0] === 255 && array[1] === 254) return utf16LE.decode(array);
			else if (array[0] === 255 && array[1] === 254) return utf16BE.decode(array);
			else return utf8.decode(array);
		}) : logs.plain;
	}
	var log_default = {
		toFile,
		toString
	};
	function _dump$1(n, ordered = false) {
		let tag = n["#"];
		switch (n["#"]) {
			case "#text": return n.$.match(/\[(\/?(?:\w+(?:=.+?)?|\*|#))\]/g) ? `[plain]${n.$}[/plain]` : n.$;
			case "li":
				tag = ordered ? "#" : "*";
				return `[${tag}]${n.$$.map((n) => _dump$1(n)).join("")}`;
			case "img": return `[${tag}=${n.$}]${n.alt}[/${tag}]`;
			case "ul": return n.$$.map((n) => _dump$1(n)).join("\n");
			case "ol": return n.$$.map((n) => _dump$1(n, true)).join("\n");
			case "#collapse":
			case "#spoiler":
				tag = "hide";
				break;
		}
		if (bbcode_default$2.is.void(n)) return `[${tag}]`;
		else if (bbcode_default$2.is.valueVoid(n)) return n;
		else if (bbcode_default$2.is.view(n)) return `[${tag}]${n.$$.map((x) => _dump$1(x)).join("")}[/${tag}]`;
		else if (bbcode_default$2.is.valueView(n)) return n.$ ? `[${tag}=${n.$}]${n.$$.map((x) => _dump$1(x)).join("")}[/${tag}]` : `[${tag}]${n.$$.map((x) => _dump$1(x)).join("")}[/${tag}]`;
		else throw n;
	}
	function dump$1(root) {
		return root.map((node) => _dump$1(node)).join("").trim();
	}
	var bbcode_default$1 = {
		...bbcode_default$2,
		dump: dump$1
	};
	function dic_default(def) {
		def.lang = "zh";
		def.adapt = async (site, payload, callback) => {
			const record = payload.record;
			const gazelle = await getGazelle(site, payload);
			if (gazelle) {
				gazelle.group.wikiBody = "";
				gazelle.torrent.description = "";
				await adaptAuto(gazelle, payload.record, callback);
				await adaptDescriptions(record, callback);
			} else await adaptGeneric(site, payload.record, callback);
			if (record.item.logs) await adaptLogs(record.item.logs, record.group.name);
		};
	}
	function jpopsuki_default(def) {
		def.lang = "native";
		def.validate = void 0;
		def.extract = async (site, callback) => {
			$(".group_torrent:not(.edition)").each((_, header) => {
				let initialised = false;
				let busy = false;
				const details = header.nextElementSibling;
				if (details?.classList?.contains("pad") !== true) return;
				const header_buttons = $(header).find("> td > span");
				const repost_button = $("<a>RE</a>").attr("href", "#repost")[0];
				header_buttons.find("> a").last().after(document.createTextNode(" | "), repost_button);
				repost_button.onclick = async () => {
					const torrent_url = header_buttons.children().filter((_, e) => e instanceof HTMLAnchorElement).map((_, e) => new URL(e.href, location.href)).filter((_, url) => url.searchParams.get("action") === "download")[0];
					const links_container = $(details).removeClass("hide").find("blockquote + blockquote").first();
					if (!links_container.hasClass("ostrich")) links_container.addClass("ostrich").append($("<br>"), $("<br>"));
					if (initialised || busy) return;
					busy = true;
					try {
						const links = $(`<span>`).text("Repost to...").appendTo(links_container);
						const h2 = $("h2").single();
						const h3 = $("h3").get(0);
						const header_info = header_buttons[0].nextElementSibling;
						const type = h2.firstChild?.textContent?.match(/\[([^\]]+)\]/)?.[1] ?? "";
						const latin_artist = h2.querySelector("a")?.textContent?.trim() ?? "";
						const [, latin_title = "", date = ""] = h2.lastChild?.textContent?.trim()?.match(/^-\s*(.+?)\s*\[([\d.]+)\]$/) ?? [];
						let native_artist = h3.querySelector("a")?.textContent?.trim();
						let [, native_title = void 0] = h3.lastChild?.textContent?.trim().match(/^-\s*(.+?)\s*\)$/) ?? [];
						if (native_artist == latin_artist) native_artist = void 0;
						if (native_title == latin_title) native_title = void 0;
						const [, format = "", encoding = "", media = ""] = header_info?.textContent?.match(/(\w.+?)\s*\/\s*(\w.+?)\s*\/\s*(\w.+?)\s*$/) ?? [];
						const record = {
							site: site.name,
							group: {
								type,
								name: native_title ? {
									latin: latin_title,
									native: native_title
								} : latin_title,
								artists: [native_artist ? {
									latin: latin_artist,
									native: native_artist
								} : latin_artist],
								guests: $(".box:has(.head:contains(\"Contributing Artists\")) .stats.nobullet").first().find("li a:first-of-type").toArray().map((a) => a.textContent).filter((t) => t !== void 0),
								composers: [],
								conductor: [],
								producer: [],
								dj: [],
								remixer: [],
								label: "",
								catalogue: "",
								year: parseInt(date.slice(0, 4)),
								image: $(".sidebar a:has(img)").get(0)?.href ?? "",
								description_tree: bbcode_default$1.fromHTML($(".torrent_table + .box .body").single(), new URL(location.href))
							},
							item: {
								media,
								encoding,
								format,
								scene: false,
								uploaded_by: "",
								description_tree: $(details).find("blockquote + blockquote + blockquote").first().map((_, bq) => bbcode_default$1.fromHTML(bq, new URL(location.href))).toArray() ?? []
							}
						};
						await callback(links, {
							torrent: torrent_url.toString(),
							record
						});
						initialised = true;
					} finally {
						busy = false;
					}
				};
			});
		};
		def.adapt = async (site, payload, callback) => {
			const record = payload.record;
			const gazelle = await getGazelle(site, payload);
			trySelect($("#categories").single(), record.group.type);
			const artist = record.group.artists[0];
			if (typeof artist === "string") $("#artist").single().value = artist;
			else {
				$("#artist").single().value = artist.latin;
				$("#artistjp").single().value = artist.native;
			}
			if (typeof record.group.name === "string") $("#title").single().value = record.group.name;
			else {
				$("#title").single().value = record.group.name.latin;
				$("#titlejp").single().value = record.group.name.native;
			}
			$("#releasedate").single().value = record.group.year?.toString() ?? "";
			if (record.item.name || record.item.year && record.item.year !== record.group.year) {
				$("#remaster").single().click();
				await new Promise((resolve) => setTimeout(resolve, 1e3));
				$("#remaster_year").single().value = record.item.year?.toString() ?? "";
				$("#remaster_title").single().value = record.item.name ?? "";
			}
			await adaptGenericCore(record, callback);
			if (gazelle) $("#tags").single().value = gazelle.group.tags.join(",");
		};
	}
	function ops_default(def) {
		def.adapt = async (site, payload, callback) => {
			const record = payload.record;
			const gazelle = await getGazelle(site, payload);
			if (gazelle) await adaptAuto(gazelle, payload.record, callback);
			else await adaptGeneric(site, payload.record, callback);
			if (record.item.logs) await adaptLogs(record.item.logs, record.group.name);
		};
	}
	function redacted_default(def) {
		def.adapt = async (site, payload, callback) => {
			const record = payload.record;
			const gazelle = await getGazelle(site, payload);
			await adaptGeneric(site, record, callback);
			if (record.item.logs) await adaptLogs(record.item.logs, record.group.name);
			if (gazelle) adaptGazelle(site, gazelle);
		};
	}
	var encoder = new TextEncoder();
	function _toRecord(site, e, base, logs) {
		const group_bb = e.group.bbBody ?? e.group.wikiBBcode;
		return {
			site: site.name,
			group: {
				type: site.selects?.releaseType[e.group.releaseType.toString()] ?? e.group.releaseType.toString(),
				name: l10n_default.parse(unescapeHtml(e.group.name)),
				artists: e.group.musicInfo.artists.map((a) => l10n_default.parse(a.name)),
				guests: e.group.musicInfo.with.map((a) => l10n_default.parse(a.name)),
				composers: e.group.musicInfo.composers.map((a) => l10n_default.parse(a.name)),
				conductor: e.group.musicInfo.conductor.map((a) => l10n_default.parse(a.name)),
				producer: e.group.musicInfo.producer.map((a) => l10n_default.parse(a.name)),
				dj: e.group.musicInfo.dj.map((a) => l10n_default.parse(a.name)),
				remixer: e.group.musicInfo.remixedBy.map((a) => l10n_default.parse(a.name)),
				description: group_bb ? unescapeHtml(group_bb) : void 0,
				description_tree: bbcode_default$2.fromHTML(new DOMParser().parseFromString(`<html><body>${e.group.wikiBody}</body></html>`, "text/html").body, base),
				label: unescapeHtml(e.group.recordLabel),
				catalogue: unescapeHtml(e.group.catalogueNumber),
				year: e.group.year,
				image: e.group.wikiImage
			},
			item: {
				name: unescapeHtml(e.torrent.remasterTitle),
				description: unescapeHtml(e.torrent.description),
				label: unescapeHtml(e.torrent.remasterRecordLabel),
				catalogue: unescapeHtml(e.torrent.remasterCatalogueNumber),
				year: e.torrent.remasterYear,
				media: e.torrent.media,
				encoding: e.torrent.encoding,
				format: e.torrent.format,
				scene: e.torrent.scene,
				uploaded_by: e.torrent.username,
				logs
			}
		};
	}
	function _mostCommon(array, invalid) {
		let compare = 0;
		let common = invalid;
		array.reduce((acc, val) => {
			if (val in acc) acc[val]++;
			else acc[val] = 1;
			if (acc[val] > compare) {
				compare = acc[val];
				common = val;
			}
			return acc;
		}, {});
		return common;
	}
	function _recoverLog(log) {
		if (log.startsWith("Exact Audio Copy") || log.startsWith("EAC extraction logfile")) return log.replace("\r\n", "\n").replace(/^Used [Dd]rive( +: .+?)(?: \(not found in database\))?$/m, "Used drive$1").replace(/Additional command line options +: .+(\n.+)+/m, (sub) => sub.replace("\n", "")).split("\n\n").map((block) => {
			let lines = block.split("\n");
			if (lines.some((line) => line.startsWith("Delete leading and trailing silent blocks"))) {
				const common_pos = _mostCommon(lines.map((line) => line.indexOf(":")), -1);
				lines = lines.map((line) => {
					const pos = line.indexOf(":");
					return line.slice(0, pos).padEnd(common_pos) + line.slice(pos);
				});
			}
			return lines.join("\n");
		}).join("\n\n").replace("\n", "\r\n");
		else if (log.startsWith("X Lossless Decoder") || log.startsWith("XLD extraction logfile")) return log.replace("\r\n", "\n").replace(/^Used [Dd]rive( +: .+?)(?: \(not found in database\))?$/m, "Used drive$1").replace(/(Absolute +\| +Relative +\| +Confidence)$/m, "$1 ").replace(/(?<!\n)\n(\n\nAccurateRip)/, "$1");
		else {
			console.warn(log);
			return log;
		}
	}
	async function extract(site, callback) {
		$("tr.torrent_row, .group_torrent:not(.edition)").each((_, header) => {
			let initialised = false;
			let busy = false;
			const header_buttons = $(header).find(".torrent_action_buttons, .td_info > span");
			const repost_button = $("<a>RE</a>").attr("href", "#repost")[0];
			header_buttons.find("> a").last().after(document.createTextNode(" | "), repost_button);
			repost_button.onclick = async () => {
				const torrent_url = header_buttons.children().filter((_, e) => e instanceof HTMLAnchorElement).map((_, e) => new URL(e.href, location.href)).filter((_, url) => url.searchParams.get("action") === "download")[0];
				const torrent_id = torrent_url.searchParams.get("id");
				let links_container;
				if (header.id) {
					links_container = $(`#torrent_${torrent_id}`).removeClass("hidden").find("blockquote").first();
					if (!links_container.hasClass("ostrich")) links_container.addClass("ostrich").append($("<br>"), $("<br>"));
				} else {
					links_container = $(header).next();
					if (links_container.length === 0 || !links_container.hasClass("torrentdetails")) links_container = $(`<td>`).attr("colspan", 9).appendTo($(`<tr>`).addClass("torrentdetails").addClass("pad").insertAfter(header));
				}
				if (initialised || busy) return;
				busy = true;
				try {
					const links = $(`<span>`).text("Repost to...").appendTo(links_container);
					const gazelle_url = new URL(`/ajax.php?action=torrent&id=${torrent_id}`, location.href).toString();
					const record = await _GM.xmlHttpRequest({
						method: "GET",
						url: gazelle_url,
						responseType: "json"
					}).then(async (event) => {
						let r = event.response;
						if (r.status !== "success") throw r.error;
						let logs;
						if (r.response.torrent.hasLog) logs = await _GM.xmlHttpRequest({
							method: "GET",
							url: `${site.exclude.download}?action=${site.actions?.log || _throw(site)}&torrentid=${torrent_id}`,
							responseType: "document"
						}).then(async (event) => {
							const body = $(event.response?.body);
							const sections = body.find(".log_section");
							if (sections.length) return {
								encoded: await Promise.all(sections.toArray().map(async (section) => {
									const event = await _GM.xmlHttpRequest({
										method: "GET",
										url: new URL(($(section).find("a.brackets")[0] ?? _throw(section)).href, location.href).toString(),
										responseType: "arraybuffer"
									});
									return base64url.encodeBytes(new Uint8Array(event.response));
								})),
								score: r.response.torrent.logScore
							};
							else return {
								plain: body.find("pre").map((_, pre) => pre.textContent).toArray().map(_recoverLog),
								recovered: true,
								score: r.response.torrent.logScore
							};
						});
						else logs = void 0;
						return _toRecord(site, r.response, new URL(location.href), logs);
					});
					links.text("Repost to: ");
					await callback(links, {
						torrent: torrent_url.toString(),
						record,
						gazelle: gazelle_url
					});
					initialised = true;
				} finally {
					busy = false;
				}
			};
		});
	}
	async function validate(callback) {
		await callback($("#upload_logs .label"), "#file[name^=logfiles], #logfile_1[name^=logfile]");
		const form = $("#dynamic_form")[0];
		if (form) onDescendantAdded($(form).single(), false, async (node) => {
			const label = $(node).find("#upload_logs .label");
			if (label.length) await callback(label, "#file[name^=logfiles], #logfile_1[name^=logfile]");
		});
	}
	async function getJson(gazelle, type) {
		return _GM.xmlHttpRequest({
			method: "GET",
			url: gazelle,
			responseType: type
		}).then((event) => event.response);
	}
	async function getGazelle(site, payload) {
		if (payload["gazelle"]) {
			const json = await getJson(payload["gazelle"], "json");
			if (json.status === "failure") throw json;
			json.response.group.name = l10n_default.format(l10n_default.parse(json.response.group.name), site.lang);
			json.response.group.musicInfo.artists = json.response.group.musicInfo.artists.map((a) => ({
				...a,
				name: l10n_default.format(l10n_default.parse(a.name), site.lang)
			}));
			return json.response;
		}
	}
	async function adaptAuto(gazelle, record, callback) {
		if (gazelle.torrent.remasterTitle || gazelle.torrent.remasterYear || gazelle.torrent.remasterRecordLabel || gazelle.torrent.remasterCatalogueNumber) gazelle.torrent.remastered = true;
		const json_input = $("#torrent-json-file").single();
		const response = {
			status: "success",
			response: gazelle
		};
		json_input.files = toDataTransfer(new File([encoder.encode(JSON.stringify(response)).buffer], "gazelle.json", { type: "application/json" })).files;
		await nextMutation($("#dynamic_form").single(), "childList", void 0, void 0, json_input);
		await new Promise((resolve) => setTimeout(resolve, 1e3));
		await adaptDescriptions(record, callback);
	}
	function _adaptArtistRole(role) {
		switch (role) {
			case "artists": return "1";
			case "with":
			case "guests": return "2";
			case "composers": return "3";
			case "conductor": return "4";
			case "dj": return "5";
			case "remixedBy":
			case "remixer": return "6";
			case "producer": return "7";
			default: throw role;
		}
	}
	async function adaptDescriptions(record, callback) {
		await callback($("tr:has(#album_desc) > .label"), $("#album_desc"), dumpDescriptions([record.group], bbcode_default$1.dump));
		await callback($("tr:has(#release_desc) > .label"), $("#release_desc"), dumpDescriptions([record.item], bbcode_default$1.dump));
	}
	function adaptArtists(artists) {
		const [artist_add, artist_rm] = $("#artistfields .brackets").toArray();
		while ($(`#artist_1`).length > 0) artist_rm.click();
		artists.forEach(([artist, role], i) => {
			let input;
			if (i === 0) input = $("#artist").single();
			else {
				artist_add.click();
				input = $(`#artist_${i}`).single();
			}
			input.value = artist;
			input.nextElementSibling.value = _adaptArtistRole(role);
		});
	}
	async function adaptGenericCore(record, callback) {
		const format_select = $("#format").single();
		trySelect(format_select, record.item.format);
		$(format_select).trigger("change");
		const encoding_select = $("#bitrate").single();
		if (!trySelect(encoding_select, record.item.encoding)) {
			trySelect(encoding_select, "Other");
			$(encoding_select).trigger("change");
			$("#other_bitrate").single().value = record.item.encoding;
		}
		trySelect($("#media, select[name=media]").single(), record.item.media);
		$("#image").single().value = record.group.image;
		await adaptDescriptions(record, callback);
	}
	async function adaptGeneric(site, record, callback) {
		$("#title").single().value = l10n_default.format(record.group.name, site.lang);
		adaptArtists(Object.keys(record.group).filter((k) => (() => {
			return (input) => "artists" === input || "composers" === input || "conductor" === input || "dj" === input || "producer" === input || "guests" === input || "remixer" === input;
		})()(k)).flatMap((role) => record.group[role].map((artist) => [l10n_default.format(artist, site.lang), role])));
		$("#year").single().value = record.group.year.toString();
		$("#remaster_year").single().value = record.item.year?.toString() ?? "";
		$("#remaster_title").single().value = record.item.name ?? "";
		$("#remaster_record_label").single().value = record.item.label ?? "";
		$("#remaster_catalogue_number").single().value = record.item.catalogue ?? "";
		await adaptGenericCore(record, callback);
	}
	function _adaptReleaseType(select, value, source, target) {
		const name = Object.entries(data.gazelle[source].selects.releaseType).find(([k, _v]) => k === value)?.[1]?.toLowerCase();
		if (!name) return;
		const mapped = Object.entries(data.gazelle[target].selects.releaseType).find(([_, v]) => v.toLowerCase() === name)?.[0];
		if (mapped === void 0) return;
		select.value = mapped;
	}
	async function adaptGazelle(site, gazelle) {
		const group = gazelle.group;
		const release = gazelle.torrent;
		adaptArtists(Object.entries(group.musicInfo).flatMap(([role, artists]) => {
			return artists.map((artist) => [artist.name, role]);
		}));
		if ((() => {
			return (input) => "DIC" === input || "JPopsuki" === input || "OPS" === input || "Redacted" === input;
		})()(site.name)) _adaptReleaseType($("#releasetype").single(), group.releaseType.toString(), site.name, "Redacted");
		else console.warn(site.name);
		if (release.scene) $("#scene").single().click();
		$("#tags").single().value = group.tags.join(",");
	}
	async function adaptLogs(logs, name) {
		const log_input = $("#file[name^=logfiles], #logfile_1[name^=logfile]").single();
		log_input.files = toDataTransfer(log_default.toFile(logs, l10n_default.select(name, "native"))).files;
	}
	function gazelle_default(framework) {
		Object.entries(framework).forEach(([st, site]) => {
			site.extract = extract;
			site.validate = validate;
			switch (st) {
				case "DIC": return dic_default(site);
				case "JPopsuki": return jpopsuki_default(site);
				case "OPS": return ops_default(site);
				case "Redacted": return redacted_default(site);
			}
		});
	}
	var _is = {
		void: (node) => (() => {
			return (input) => "hr" === input;
		})()(node["#"]),
		valueVoid: (node) => (() => {
			return (input) => "img" === input;
		})()(node["#"]),
		view: (node) => (() => {
			const _iv1 = new Set([
				"b",
				"i",
				"u",
				"s",
				"center",
				"sup",
				"sub",
				"mask",
				"pre",
				"*",
				"table",
				"tr",
				"td"
			]);
			return (input) => true === _iv1.has(input);
		})()(node["#"]),
		valueView: (node) => (() => {
			return (input) => "color" === input || "font" === input || "url" === input || "quote" === input || "hide" === input || "code" === input || "size" === input;
		})()(node["#"])
	};
	function _dump(n, depth = -1) {
		let np = (() => {
			switch (n["#"]) {
				case "h": return ValueView("size")(8 - n.$, n.$$);
				case "code": return ValueView("font")("monospace", n.$$);
				case "#spoiler": return {
					...n,
					"#": "mask"
				};
				case "align": if (n.$ === "center") return View("center")(n.$$);
				else {
					console.warn(n);
					return n.$$.map((c) => _dump(c, depth)).join("");
				}
				case "#collapse": return {
					...n,
					"#": "hide"
				};
				case "li":
					console.warn(n);
					return {
						...n,
						"#": "*"
					};
				case "ul":
					++depth;
					return n.$$.map((li) => `${"    ".repeat(depth)}[*] ${li.$$.map((c) => _dump(c, depth)).join()}`).join("\n");
				case "ol":
					++depth;
					return n.$$.map((li, i) => `${"    ".repeat(depth)}[*] ${i + 1}. ${li.$$.map((c) => _dump(c, depth)).join()}`).join("\n");
				default: return n;
			}
		})();
		if (typeof np === "string") return np;
		switch (np["#"]) {
			case "#text": return np.$.replace(/\[(\/?(?:\w+(?:=.+?)?|\*|#))\]/g, "[​$1​]");
			case "*": return `[${np["#"]}]${np.$$.map((n) => _dump(n)).join("")}`;
		}
		if (_is.void(np)) return `[${np["#"]}]`;
		else if (_is.valueVoid(np)) return np.$ ? `[${np["#"]}=${np.$}]` : `[${np["#"]}]`;
		else if (_is.view(np)) return `[${np["#"]}]${np.$$.map((c) => _dump(c)).join("")}[/${np["#"]}]`;
		else if (_is.valueView(np)) return np.$ ? `[${np["#"]}=${np.$}]${np.$$.map((c) => _dump(c)).join("")}[/${np["#"]}]` : `[${np["#"]}]${np.$$.map((c) => _dump(c)).join("")}[/${np["#"]}]`;
		else throw n;
	}
	function dump(root) {
		return root.map((node) => _dump(node)).join("").trim();
	}
	var bbcode_default = {
		...bbcode_default$2,
		dump
	};
	function opencd_default(def) {
		def.lang = "native";
		def.validate = async (callback) => {
			await callback($(".rowhead[msg^=NFO]"), "input[name^=nfo1]");
		};
		def.adapt = async (site, payload, callback) => {
			const record = payload.record;
			const cover_task = _GM.xmlHttpRequest({
				method: "GET",
				url: record.group.image,
				responseType: "arraybuffer"
			}).then((event) => {
				const transfer = toDataTransfer(new File([event.response], new URL(event.finalUrl).pathname.split("/").filter((x) => x).at(-1)?.substring(1) ?? _throw(event), { type: parseHeaders(event.responseHeaders)["content-type"].split(";")[0] }));
				$("td:has(> #cover) iframe").single().contentDocument.querySelector("#file").files = transfer.files;
			});
			const desc_parent = $("#editer_description").single();
			if (desc_parent.children.length === 0) await nextMutation(desc_parent, "childList");
			$("#browsecat").single().value = "408";
			$("#artist").single().value = record.group.artists.map((a) => l10n_default.format(a, site.lang)).join(" / ");
			$("#resource_name").single().value = l10n_default.format(record.group.name, site.lang);
			const year = $("#year").single();
			year.value = record.group.year.toString();
			year.dispatchEvent(new Event("change"));
			trySelect($("#standard").single(), record.item.format);
			trySelect($("#medium").single(), record.item.media);
			const repost_input = $("#boardid1").single();
			repost_input.checked = true;
			$(repost_input).trigger("change");
			$("#small_descr").single().value = [
				record.item.name,
				record.group.year !== record.item.year && record.item.year,
				record.item.label ? record.item.label : record.group.label,
				record.item.catalogue ? record.item.catalogue : record.group.catalogue
			].filter((i) => i).join(" / ");
			if (record.item.logs) {
				const log_add = $("#nfoadd").single();
				log_default.toFile(record.item.logs, l10n_default.select(record.group.name, "native")).forEach(async (f, i) => {
					if (i > 0) log_add.click();
					$(`[name=nfo${"1".repeat(i + 1)}]`).single().files = toDataTransfer(f).files;
				});
			}
			await callback($("tr:has(#descr) > .rowhead"), $("#descr"), dumpDescriptions([record.group, record.item], bbcode_default.dump));
			await cover_task;
		};
	}
	function tjupt_default(def) {
		def.lang = "native";
		def.adapt = async (site, payload, callback) => {
			const record = payload.record;
			const select_cat = $("#browsecat").single();
			select_cat.value = "406";
			$(select_cat).trigger("change");
			const form_div = select_cat.nextElementSibling ?? _throw(select_cat);
			while (form_div.children.length <= 1) await nextMutation(select_cat.nextElementSibling ?? _throw(select_cat), "childList");
			$("#hqname").single().value = l10n_default.format(record.group.name, site.lang);
			$("#artist").single().value = record.group.artists.map((a) => l10n_default.format(a, site.lang)).join(" / ");
			$("#issuedate").single().value = record.group.year.toString();
			$("#specificcat").single().value = record.item.media;
			$("#format").single().value = record.item.format;
			$("#hqtone").single().value = record.item.encoding;
			$("input[name=small_descr]").single().value = [
				record.item.name,
				record.group.year !== record.item.year && record.item.year,
				record.item.label ? record.item.label : record.group.label,
				record.item.catalogue ? record.item.catalogue : record.group.catalogue,
				record.item.logs?.score && `Log (${record.item.logs.score}%)`
			].filter((i) => i).join(" / ");
			const logs = record.item.logs ? log_default.toString(record.item.logs).map((log) => `[hide=Log][code]${log}[/code][/hide]`).join("\n") : void 0;
			await callback($("tr:has(#descr) > .rowhead"), $("#descr"), dumpDescriptions([record.group, record.item], bbcode_default.dump).map((s) => [
				`[img]${record.group.image}[/img]`,
				s,
				logs
			].filter((i) => i).join("\n[hr]\n")));
		};
	}
	function nexusphp_default(framework) {
		Object.entries(framework).forEach(([st, site]) => {
			switch (st) {
				case "OpenCD": return opencd_default(site);
				case "TJUPT": return tjupt_default(site);
			}
		});
	}
	gazelle_default(data.gazelle);
	nexusphp_default(data.nexusphp);
	function parseSites(location) {
		if (location.hostname.toLowerCase() === "logs.musichoarders.xyz") return [
			"gazelle",
			{},
			"validate"
		];
		const [fw, st, site] = Object.entries(data).flatMap(([fw, framework]) => Object.entries(framework).map(([st, site]) => [
			fw,
			st,
			site
		])).find(([_f, _s, site]) => location.hostname.endsWith(site.hostname)) ?? _throw(location);
		const [cat] = Object.entries(site.include).find(([cat, fw_path]) => {
			const path = site.include?.[cat] ?? fw_path;
			return path instanceof Array ? path.filter((path) => path === location.pathname).length > 0 : path === location.pathname;
		}) ?? _throw(location);
		return [
			fw,
			{
				...site,
				name: st
			},
			cat
		];
	}
	function dumpDescriptions(descriptions, dump) {
		const array = descriptions.map((d) => [d.description, d.description_tree ? dump(d.description_tree) : void 0]);
		function* _join() {
			if (array[0][0] !== void 0) yield [array[0][0], ...array.slice(1).map((p) => p[0] ?? p[1] ?? "")].filter((d) => d).join("\n[hr]\n");
			if (array[0][1] !== void 0) yield [array[0][1], ...array.slice(1).map((p) => p[1] ?? p[0] ?? "")].filter((d) => d).join("\n[hr]\n");
		}
		return Array.from(_join());
	}
	var sites_default = data;
	var [fw, site, cat] = parseSites(location);
	if (cat === "validate") {
		const psuedo_url = new URL(new URL(location.href).hash.substring(1), location.origin);
		if (psuedo_url.pathname === "/ostrich") {
			const input = document.querySelector("#cursorfiles");
			if (input) {
				input.files = toDataTransfer(new File([base64url.decodeBytes(psuedo_url.hash.slice(1))], "text.log", { type: "text/plain" })).files;
				await(new Promise((resolve) => setTimeout(resolve, 1e3)));
				input.dispatchEvent(new Event("change"));
			}
		}
	} else {
		if ($?.fn?.jquery == void 0) $ = jQuery?.fn?.jquery ? jQuery : await(import("https://cdn.jsdelivr.net/npm/jquery@3/dist/jquery.slim.min.js"));
		$.fn.extend({ single() {
			return this.length === 1 ? this[0] : _throw(this);
		} });
		if (cat === "source") {
			if (site.extract) await(site.extract(site, async (container, payload) => {
				console.debug(payload);
				const params = new URLSearchParams();
				Object.entries(payload).forEach(([k, v]) => {
					params.set(k, marshal(v));
				});
				const p = params.toString();
				Object.entries(sites_default).flatMap(([_fw, framework]) => Object.entries(framework)).filter(([_st, site]) => site.adapt && !location.hostname.endsWith(site.hostname)).forEach(([st, site], s) => {
					if (s !== 0) container.append(document.createTextNode(" · "));
					$(`<a>`).appendTo(container).text(st).attr("target", "_blank").attr("href", `https://${site.hostname}${site.include.target}#ostrich?${p}`);
				});
			}));
		} else if (cat === "target") {
			const psuedo_url = new URL(new URL(location.href).hash.substring(1), location.origin);
			if (site.validate) await(site.validate(async (container, selector) => {
				const anchor = $("<a>").appendTo(container.append($("<br>"))).text("Validate").attr("href", "#_ostrich").single();
				anchor.onclick = async () => {
					await Promise.all($(selector).toArray().filter((input) => input.files).flatMap((input) => [...input.files]).map(async (file) => {
						window.open(`https://logs.musichoarders.xyz#ostrich#${base64url.encodeBytes(new Uint8Array(await file.arrayBuffer()))}`, "_blank");
					}));
				};
			}));
			if (psuedo_url.pathname === "/ostrich") {
				const payload = Object.fromEntries([...psuedo_url.searchParams.entries()].map(([k, v]) => [k, unmarshal(v)]));
				const record = payload.record;
				console.debug(payload);
				const torrent_task = (async () => {
					let buffer = (await _GM.xmlHttpRequest({
						method: "GET",
						url: payload.torrent,
						responseType: "arraybuffer"
					})).response;
					buffer = (await patch(new Uint8Array(buffer), site.name)).buffer;
					return toDataTransfer(new File([buffer], (0, import_sanitize_filename.default)(`${l10n_default.select(record.group.name, "native")}.torrent`), { type: "application/x-bittorrent" }));
				})();
				if (site.adapt) {
					const input = (() => {
						switch (fw) {
							case "gazelle": return $("#file[name=file_input]").single();
							case "nexusphp": return $("#torrent").single();
							default: throw new Error(`Unsupported framework: ${fw}`);
						}
					})();
					input.addEventListener("contextmenu", (event) => {
						event.preventDefault();
						for (const file of input.files ?? []) {
							const a = document.createElement("a");
							a.style.display = "none";
							a.href = URL.createObjectURL(file);
							a.download = file.name;
							input.insertAdjacentElement("afterend", a);
							a.click();
							a.remove();
							URL.revokeObjectURL(a.href);
						}
					});
					await(torrent_task.then((data) => {
						input.files = data.files;
					}));
					await(site.adapt(site, payload, async (container, input, selections) => {
						selections = selections.filter((s) => s);
						if (selections.length >= 1) {
							input.single().value = selections[0];
							if (selections.length > 1) {
								const anchor = $("<a>").appendTo(container.append($("<br>"))).text("Toggle").attr("href", "#_ostrich").single();
								anchor.onclick = () => {
									selections.push(selections.shift());
									input.single().value = selections[0];
								};
							}
						}
					}));
				}
			}
		}
	}
})();