PT Helper

A helper for private trackers

目前為 2023-02-23 提交的版本,檢視 最新版本

// ==UserScript==
// @name PT Helper
// @name:zh-CN PT 助手
// @version 0.1.0
// @namespace https://github.com/amorphobia/pt-helper
// @description A helper for private trackers
// @description:zh-CN 私密种子站点的助手
// @author amorphobia
// @homepage https://github.com/amorphobia/pt-helper
// @icon data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+IDxzdmcgaWQ9IkxheWVyXzEiIGRhdGEtbmFtZT0iTGF5ZXIgMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmQzOGQ7fS5jbHMtMntmaWxsOiM2ZDQxMzU7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5zaGFyZS1maWxsZWQ8L3RpdGxlPjxjaXJjbGUgY2xhc3M9ImNscy0xIiBjeD0iNDA1LjMzIiBjeT0iMTA2LjY3IiByPSI4NS4zMyIvPjxjaXJjbGUgY2xhc3M9ImNscy0xIiBjeD0iNDA1LjMzIiBjeT0iNDA1LjMzIiByPSI4NS4zMyIvPjxjaXJjbGUgY2xhc3M9ImNscy0xIiBjeD0iMTA2LjY3IiBjeT0iMjU2IiByPSI4NS4zMyIvPjxwYXRoIGNsYXNzPSJjbHMtMiIgZD0iTTQwNS4zMywyOTguNjdhMTA2LjU0LDEwNi41NCwwLDAsMC04My45LDQwLjg2TDIwOS42OSwyODMuNjZhMTA2Ljc4LDEwNi43OCwwLDAsMCwwLTU1LjMxbDExMS43NS01NS44N2ExMDYuMjcsMTA2LjI3LDAsMSwwLTE5LjEzLTM4LjE1TDE5MC41NiwxOTAuMmExMDYuNjcsMTA2LjY3LDAsMSwwLDAsMTMxLjZsMTExLjc1LDU1Ljg3YTEwNi42NywxMDYuNjcsMCwxLDAsMTAzLTc5Wm0wLTI1NmE2NCw2NCwwLDEsMS02NCw2NEE2NC4wNyw2NC4wNywwLDAsMSw0MDUuMzMsNDIuNjdaTTEwNi42NywzMjBhNjQsNjQsMCwxLDEsNjQtNjRBNjQuMDcsNjQuMDcsMCwwLDEsMTA2LjY3LDMyMFpNNDA1LjMzLDQ2OS4zM2E2NCw2NCwwLDEsMSw2NC02NEE2NC4wNyw2NC4wNywwLDAsMSw0MDUuMzMsNDY5LjMzWiIvPjwvc3ZnPg==
// @supportURL https://github.com/amorphobia/pt-helper/issues
// @license AGPL-3.0-or-later
// @match *://hhanclub.top/*
// @match *://nanyangpt.com/*
// @match *://pt.sjtu.edu.cn/*
// @match *://tjupt.org/*
// @grant GM_addStyle
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_openInTab
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_notification
// @grant GM_xmlhttpRequest
// @noframes
// ==/UserScript==

/******/ (() => { // webpackBootstrap
/******/ 	"use strict";
/******/ 	var __webpack_modules__ = ([
/* 0 */,
/* 1 */
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.Hhanclub = void 0;
const index_1 = __webpack_require__(2);
class Hhanclub extends index_1.NexusPHP {
    constructor() {
        super("hhanclub.top");
        this.menu_items = [
            {
                "id": "bannerFold",
                "type": "switch",
                "display": "自动折叠横幅(隐藏时折叠设置无效)",
                "name": "自动折叠横幅",
                "value": true
            },
            {
                "id": "bannerHide",
                "type": "switch",
                "display": "隐藏横幅(隐藏时折叠设置无效)",
                "name": "隐藏横幅",
                "value": false
            }
        ].concat(this.menu_items);
    }
    onLoad() {
        super.onLoad();
        if (this.getHostValue("bannerHide")) {
            this.css += `
td.clear.nowrap img {
    display: none;
}`;
        }
        else if (this.getHostValue("bannerFold")) {
            const banner = document.querySelector("td.clear.nowrap");
            const original_height = banner === null || banner === void 0 ? void 0 : banner.clientHeight;
            this.css += `
td.clear.nowrap img {
    height: 10px;
    overflow: hidden;
    transition: height 0.5s;
}

td.clear.nowrap img:hover {
    height: ${original_height}px;
}`;
        }
    }
}
exports.Hhanclub = Hhanclub;


/***/ }),
/* 2 */
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.NexusPHP = void 0;
const common_1 = __webpack_require__(3);
class NexusPHP extends common_1.Common {
    constructor(host) {
        super(host);
        this.passkey = "";
        this.menu_items = [
            {
                "id": "thanks",
                "type": "switch",
                "display": "自动说谢谢",
                "name": "自动说谢谢",
                "value": true
            }
        ].concat(this.menu_items);
    }
    onLoad() {
        super.onLoad();
        this.getPasskey();
        if (this.getHostValue("thanks") && location.href.indexOf("/details.php") >= 0) {
            this.sayThanks();
        }
    }
    getPasskey() {
        const value = this.getHostValue("passkey");
        let passkey = "";
        if (value) {
            passkey = String(value);
        }
        if (passkey != "") {
            this.passkey = passkey;
            return;
        }
        const cp_url = "https://" + this.host + "/usercp.php";
        GM_xmlhttpRequest({
            method: "GET",
            url: cp_url,
            onload: (response) => {
                if (response.status != 200) {
                    console.log("Failed to get passkey.");
                    return;
                }
                const container = document.implementation.createHTMLDocument().documentElement;
                container.innerHTML = response.responseText;
                const tds = container.querySelectorAll("td.rowfollow");
                for (const td of tds) {
                    const tc = td;
                    const re = /[\w\d]{32}/;
                    const result = re.exec(tc.innerText);
                    if (result) {
                        this.setHostValue("passkey", result[0]);
                        this.passkey = result[0];
                        return;
                    }
                }
            },
            onabort: () => {
                console.log("Abort to get passkey");
            },
            onerror: () => {
                console.log("Error to get passkey");
            }
        });
    }
    sayThanks() {
        this.wait(2000).then(() => {
            const input = document.querySelector("#saythanks");
            if (input && !input.disabled) {
                input.click();
            }
        }).catch(() => { console.log("Failed to say thanks."); });
    }
}
exports.NexusPHP = NexusPHP;


/***/ }),
/* 3 */
/***/ ((__unused_webpack_module, exports) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.Common = void 0;
const helper_home = "https://github.com/amorphobia/pt-helper";
const num_emoji = ["0️⃣", "1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣", "🔟"];
class Common {
    constructor(host) {
        this.host = "";
        this.menu_items = [{
                "id": "feedback",
                "type": "link",
                "display": "💬反馈与建议",
                "value": helper_home + "/issues"
            }];
        this.host = host;
        this.registered_items = [];
        this.css = "";
    }
    init() {
        for (const item of this.menu_items) {
            item.id = this.host + "_" + item.id;
            if ((item.type == "switch" || item.type == "selection") && GM_getValue(item.id) == null) {
                GM_setValue(item.id, item.value);
            }
        }
        this.registerMenu();
    }
    onLoad() { }
    addStyle() {
        if (typeof GM_addStyle !== "undefined") {
            GM_addStyle(this.css);
        }
        else {
            const style = document.createElement("style");
            style.appendChild(document.createTextNode(this.css));
            (document.querySelector("head") || document.documentElement).appendChild(style);
        }
    }
    registerMenu() {
        for (const item of this.registered_items) {
            GM_unregisterMenuCommand(item);
        }
        this.registered_items = [];
        for (const item of this.menu_items) {
            const value = GM_getValue(item.id);
            if (value && value != null) {
                item.value = value;
            }
            let reg_item;
            switch (item.type) {
                case "switch":
                    reg_item = GM_registerMenuCommand(`${item.value ? "✅" : "❌"}${item.display}`, () => {
                        this.toggleSwitch(item);
                    });
                    break;
                case "selection":
                    reg_item = GM_registerMenuCommand(`${num_emoji[item.value]}${item.display[item.value]}`, () => {
                        this.nextSelection(item);
                    });
                    break;
                case "link":
                    reg_item = GM_registerMenuCommand(`${item.display}`, () => {
                        GM_openInTab(item.value, { active: true, insert: true, setParent: true });
                    });
                    break;
                case "text":
                    reg_item = GM_registerMenuCommand(`${item.display}`, () => { });
                    break;
                default:
                    console.log(`Unrecognized menu item: ${item.id}`);
                    break;
            }
            if (reg_item !== undefined) {
                this.registered_items.push(reg_item);
            }
        }
    }
    toggleSwitch(item) {
        const status = item.value ? "关闭" : "开启";
        GM_setValue(item.id, !item.value);
        GM_notification({
            text: `已${status}「${item.name}」\n(点击刷新网页后生效)`,
            timeout: 3500,
            onclick: () => { location.reload(); }
        });
        this.registerMenu();
    }
    nextSelection(item) {
        const new_value = (item.value + 1) % item.display.length;
        GM_setValue(item.id, new_value);
        GM_notification({
            text: `切换为「${item.display[new_value]}」\n(点击刷新网页后生效)`,
            timeout: 3500,
            onclick: () => { location.reload(); }
        });
        this.registerMenu();
    }
    wait(ms) {
        return new Promise((resolve, reject) => {
            setTimeout(resolve, ms);
        });
    }
    getHostValue(id) {
        return GM_getValue(this.host + "_" + id);
    }
    setHostValue(id, value) {
        GM_setValue(this.host + "_" + id, value);
    }
}
exports.Common = Common;


/***/ }),
/* 4 */
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.NanyangPT = void 0;
const NexusPHP_1 = __webpack_require__(2);
class NanyangPT extends NexusPHP_1.NexusPHP {
    constructor() {
        super("nanyangpt.com");
        this.menu_items = [
            {
                "id": "bannerHide",
                "type": "switch",
                "display": "隐藏横幅",
                "name": "隐藏横幅",
                "value": false
            }
        ].concat(this.menu_items);
    }
    onLoad() {
        super.onLoad();
        if (this.getHostValue("bannerHide")) {
            const info = document.querySelector("#info_block");
            const info_height = (info === null || info === void 0 ? void 0 : info.clientHeight) ? info.clientHeight + 5 : 30;
            this.css += `
table.head {
    display: none;
}

table.mainouter {
    margin-top: ${info_height}px;
}`;
        }
    }
}
exports.NanyangPT = NanyangPT;


/***/ }),
/* 5 */
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.SJTU = void 0;
const index_1 = __webpack_require__(2);
class SJTU extends index_1.NexusPHP {
    constructor() {
        super("pt.sjtu.edu.cn");
    }
}
exports.SJTU = SJTU;


/***/ }),
/* 6 */
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.TJUPT = void 0;
const index_1 = __webpack_require__(2);
class TJUPT extends index_1.NexusPHP {
    constructor() {
        super("tjupt.org");
        this.menu_items = [
            {
                "id": "bannerFold",
                "type": "switch",
                "display": "自动折叠横幅(隐藏时折叠设置无效)",
                "name": "自动折叠横幅",
                "value": true
            },
            {
                "id": "bannerHide",
                "type": "switch",
                "display": "隐藏横幅(隐藏时折叠设置无效)",
                "name": "隐藏横幅",
                "value": false
            },
            {
                "id": "stickyHide",
                "type": "selection",
                "display": [
                    "显示所有置顶",
                    "隐藏一重置顶",
                    "隐藏一、二重置顶",
                    "隐藏所有置顶"
                ],
                "value": 0
            },
            {
                "id": "directLink",
                "type": "switch",
                "display": "种子直链按钮(左键点击按钮复制直链)",
                "name": "种子直链",
                "value": true
            },
            {
                "id": "colorBlind",
                "type": "switch",
                "display": "色盲模式",
                "name": "色盲模式",
                "value": false
            }
        ].concat(this.menu_items);
    }
    onLoad() {
        var _a;
        super.onLoad();
        if (this.getHostValue("bannerHide")) {
            this.css += `
.logo_img img {
    display: none;
}`;
        }
        else if (this.getHostValue("bannerFold")) {
            const logo_img = document.querySelector(".logo_img");
            const original_height = logo_img === null || logo_img === void 0 ? void 0 : logo_img.clientHeight;
            this.css += `
.logo_img {
    height: 10px;
    overflow: hidden;
    transition: height 0.5s;
}

.logo_img:hover {
    height: ${original_height}px;
}`;
        }
        switch (this.getHostValue("stickyHide")) {
            case 3:
                this.css += `
.triple_sticky_bg {
    display: none;
}`;
            case 2:
                this.css += `
.double_sticky_bg {
    display: none;
}`;
            case 1:
                this.css += `
.sticky_bg {
    display: none;
}`;
            default:
                break;
        }
        if (this.getHostValue("directLink") && this.passkey != "") {
            const id_re = /id=[\d]+/;
            const tds = document.querySelectorAll("table.torrentname > tbody > tr:nth-of-type(1) > td:nth-of-type(3)");
            for (const td of tds) {
                const dl = td.querySelector("a");
                const result = id_re.exec((_a = dl === null || dl === void 0 ? void 0 : dl.href) !== null && _a !== void 0 ? _a : "");
                if (!result) {
                    continue;
                }
                const direct_link = `https://www.${this.host}/download.php?${result[0]}&passkey=${this.passkey}`;
                const img = document.createElement("img");
                img.setAttribute("src", "pic/trans.gif");
                img.setAttribute("class", "torrent_direct_link");
                img.setAttribute("alt", "DL");
                const a = document.createElement("a");
                a.setAttribute("title", "左键单击复制,链接中包含个人秘钥Passkey,切勿泄露!");
                a.setAttribute("onclick", "return false");
                a.setAttribute("id", "direct_link");
                a.setAttribute("href", direct_link);
                a.setAttribute("data-clipboard-text", direct_link);
                a.appendChild(img);
                td.prepend(a);
            }
            this.css += `
img.torrent_direct_link {
    width: 16px;
    height: 16px;
    background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAH5QTFRFR3BMyKN4cz0Td3d3sLCw6enp////qHg4oaGhcz0TlpaWkV8opnU2lmQrjVklqHg4m2kvo3I03ruJiFMhn24y4r+N2raG5cSP9+jQg04e1rKD68uUnYpv6ceS0q5/fkkaoaGhzqp8h4eHr6+v+Pj4ekQXdkAV+/v78fHxy6Z6f0p3WgAAAAp0Uk5TAP///////5aWlrne7esAAACHSURBVBjTbc5HEsIwEERRA5qxLeecc77/BTEN0oq/m1ddKhnG36xxtPRhBq2UxyFlG5gAt5zXk+hc59IFRM3yQksTAdKOf3UpICxYCEFEXIQAL2NCnHkAJ/4s7jh2AH6uFrkPSOrvgrhOAFWvFn0FGCYWhDemAbBd6h/XBtgfuh1gP3X2fb4BlrkIUt3i2kgAAAAASUVORK5CYII=');
    padding-bottom: 1px;
}`;
            location.assign("javascript:registerClipboardJS('#direct_link');void(0)");
        }
        if (this.getHostValue("colorBlind")) {
            if (location.href.indexOf("/classes.php") >= 0) {
                const spans = document.querySelectorAll("table.main > tbody > tr > td:nth-of-type(2) > ul > li > span[style=\"color: green\"]");
                for (const span of spans) {
                    span.setAttribute("style", "color: blue");
                }
            }
        }
    }
    getPasskey() {
        const value = this.getHostValue("passkey");
        let passkey = "";
        if (value) {
            passkey = String(value);
        }
        if (passkey != "") {
            this.passkey = passkey;
            return;
        }
        const link = document.querySelector("[title=\"Latest Torrents\"]");
        const re = /passkey=([\d\w]+)/;
        const result = re.exec(link.href);
        this.passkey = result && result.length > 1 ? result[1] : "";
        if (this.passkey != "") {
            this.setHostValue("passkey", this.passkey);
        }
    }
}
exports.TJUPT = TJUPT;


/***/ })
/******/ 	]);
/************************************************************************/
/******/ 	// The module cache
/******/ 	var __webpack_module_cache__ = {};
/******/ 	
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/ 		// Check if module is in cache
/******/ 		var cachedModule = __webpack_module_cache__[moduleId];
/******/ 		if (cachedModule !== undefined) {
/******/ 			return cachedModule.exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = __webpack_module_cache__[moduleId] = {
/******/ 			// no module.id needed
/******/ 			// no module.loaded needed
/******/ 			exports: {}
/******/ 		};
/******/ 	
/******/ 		// Execute the module function
/******/ 		__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/ 	
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/ 	
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
var exports = __webpack_exports__;

Object.defineProperty(exports, "__esModule", ({ value: true }));
const hhanclub_top_1 = __webpack_require__(1);
const nanyangpt_com_1 = __webpack_require__(4);
const pt_sjtu_edu_cn_1 = __webpack_require__(5);
const index_1 = __webpack_require__(6);
const host = window.location.host;
const sites = new Map([
    ["hhanclub.top", new hhanclub_top_1.Hhanclub()],
    ["nanyangpt.com", new nanyangpt_com_1.NanyangPT()],
    ["pt.sjtu.edu.cn", new pt_sjtu_edu_cn_1.SJTU()],
    ["tjupt.org", new index_1.TJUPT()]
]);
const site = sites.get(host);
if (site) {
    site.init();
    site.onLoad();
    site.addStyle();
}

})();

/******/ })()
;