Greasy Fork

Greasy Fork is available in English.

PT 助手

私密种子站点的助手

当前为 2023-02-23 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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();
}

})();

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