Greasy Fork

Greasy Fork is available in English.

qB WebUI 加PT站点标签

qBittorrent WebUI 根据tracker中的关键字给种子增加标签

当前为 2024-03-17 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name            qB WebUI 加PT站点标签
// @version         0.1.1
// @author          cO_ob
// @description     qBittorrent WebUI 根据tracker中的关键字给种子增加标签
// @license         MIT
// @grant           GM_registerMenuCommand
// @grant           GM_setValue
// @grant           GM_getValue
// @match           http://127.0.0.1:8080/
// @namespace       http://greasyfork.icu/users/1270887
// @require         https://cdn.bootcdn.net/ajax/libs/jquery/3.6.3/jquery.min.js
// ==/UserScript==

//require qB API v2.3.0 +

const host = window.location.href;
const baseURL = host + 'api/v2/torrents/';
let trackerMappings = [
	{ url: 'agsvpt', tags: 'agsv' },
	{ url: 'btschool', tags: 'BTSCHOOL' },
	{ url: 'chdbits', tags: 'CHDBits' },
	{ url: 'daydream', tags: 'U2' },
	{ url: 'eastgame', tags: 'TLFBits' },
	{ url: 'et8.org', tags: 'TorrentCCF' },
	{ url: 'hdatmos', tags: 'HDATMOS' },
	{ url: 'hd4fans', tags: 'HD4FANS' },
	{ url: 'hdarea', tags: 'HDArea' },
	{ url: 'hdfans', tags: 'HDFans' },
	{ url: 'hdhome', tags: 'HDHome' },
	{ url: 'hdsky', tags: 'HDSky' },
	{ url: 'hdkyl', tags: 'HDKylin-麒麟' },
	{ url: 'leaves', tags: '红叶' },
	{ url: 'm-team', tags: 'M-Team' },
	{ url: 'open.cd', tags: 'OpenCD' },
	{ url: 'ourbits', tags: 'OurBits' },
	{ url: 'pttime', tags: 'pttime' },
	{ url: 'sharkpt', tags: 'SharkPT' },
	{ url: 'totheglory', tags: 'TTG' }
];

function loadConfig() {
    const savedMappings = localStorage.getItem('trackerMappings');
    if (savedMappings) {
        trackerMappings = JSON.parse(savedMappings);
    }
}
function saveConfig() {
    localStorage.setItem('trackerMappings', JSON.stringify(trackerMappings));
}

async function getFetch(route) {
    try {
        const response = await fetch(baseURL + route);
        if (!response.ok) {
            throw new Error('Error fetching info!');
        }
        const data = await response.json();
        return data;
    } catch (error) {
        console.error(error);
        return null;
    }
}

loadConfig();

async function processTorrents() {
    try {
        const torrentList = await getFetch('info');
        let totalTorrents = 0;
        let currentProcessed = 0;
        totalTorrents = torrentList.length;
        for (const torrent of torrentList) {
            currentProcessed++;
            jQuery(".js-modal").text(`加标签 ${currentProcessed}/${totalTorrents}`);
            const trackers = await getFetch(`trackers?hash=${torrent.hash}`);
            for (let i = 0; i < trackers.length; i++) {
                const tracker = trackers[i];
                if (tracker.status != 0) {
                    let torrentTags = [];
                    let foundMapping = false;
                    for (const mapping of trackerMappings) {
                        if (tracker.url.includes(mapping.url)) {
                            torrentTags = [mapping.tags];
                            foundMapping = true;
                            break;
                        }
                    }

                    if (!foundMapping) {
                        const newMappingUrl = prompt(`输入新的关键字:\n${tracker.url}`);
                        if (newMappingUrl !== null) {
                            const newMappingTags = prompt(`新关键字要使用的标签:\n${tracker.url}`);
                            if (newMappingTags !== null) {
                                trackerMappings.push({ url: newMappingUrl, tags: newMappingTags });
                                torrentTags = [newMappingTags];
                            }
                        }
                    }

                    const tags = torrentTags.join(",");

                    //const response = await fetch(`${baseURL}addTags?hashes=${torrent.hash}&tags=${tags}`); //GET method. only for qb version under v4.5.0
                    const url = `${baseURL}addTags`;
                    const data = new URLSearchParams();
                    data.append('hashes', torrent.hash);
                    data.append('tags', tags);
                    fetch(url, {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/x-www-form-urlencoded'
                        },
                        body: data
                    })
                        .then(response => {
                        console.log(response);
                    })
                        .catch(error => {
                        console.error('Error:', error);
                    });
                    break;
                }
            }
        }
        console.log('Done.');
    } catch (error) {
        console.error('Error:', error.message);
    }
    jQuery(".js-modal").text(`完成`);
}

function configureTrackerMappings() {
    const dialog = document.createElement('div');
    dialog.style.position = 'fixed';
    dialog.style.top = '50%';
    dialog.style.left = '50%';
    dialog.style.transform = 'translate(-50%, -50%)';
    dialog.style.backgroundColor = '#fff';
    dialog.style.padding = '20px';
    dialog.style.border = '1px solid #ccc';
    dialog.style.zIndex = '9999';
    dialog.style.maxHeight = '400px';
    dialog.style.overflowY = 'auto';
    dialog.style.display = 'flex';
    dialog.style.flexDirection = 'column';

    const tableContainer = document.createElement('div');
    tableContainer.style.overflowY = 'auto';
    tableContainer.style.flex = '1';

    const table = document.createElement('table');
    table.style.width = '100%';

    const headerRow = table.insertRow();
    const urlHeader = headerRow.insertCell();
    urlHeader.textContent = '关键字';
    const tagsHeader = headerRow.insertCell();
    tagsHeader.textContent = '标签';
    const actionsHeader = headerRow.insertCell();
    actionsHeader.textContent = '增减';

    trackerMappings.forEach((mapping, index) => {
        const row = table.insertRow();
        const urlCell = row.insertCell();
        urlCell.textContent = mapping.url;
        const tagsCell = row.insertCell();
        tagsCell.textContent = mapping.tags;
        const actionsCell = row.insertCell();
        const deleteButton = document.createElement('button');
        deleteButton.textContent = '-';
        deleteButton.addEventListener('click', () => {
            event.stopPropagation();
            trackerMappings.splice(index, 1);
            saveConfig();
            table.deleteRow(index + 1);
        });
        actionsCell.appendChild(deleteButton);
    });

    tableContainer.appendChild(table);
    dialog.appendChild(tableContainer);

    const form = document.createElement('form');
    form.style.marginTop = '20px';
    form.style.display = 'flex';
    form.style.alignItems = 'center';

    const newUrlInput = document.createElement('input');
    newUrlInput.placeholder = '';
    newUrlInput.style.width = '130px';
    form.appendChild(newUrlInput);

    const newTagsInput = document.createElement('input');
    newTagsInput.placeholder = '';
    newTagsInput.style.width = '140px';
    form.appendChild(newTagsInput);

    const addButton = document.createElement('button');
    addButton.textContent = '+';
    addButton.style.marginLeft = '5px';
    addButton.addEventListener('click', () => {
        const newUrl = newUrlInput.value;
        const newTags = newTagsInput.value;
        if (newUrl && newTags) {
            trackerMappings.push({ url: newUrl, tags: newTags });
            saveConfig();
            const newRow = table.insertRow();
            const urlCell = newRow.insertCell();
            urlCell.textContent = newUrl;
            const tagsCell = newRow.insertCell();
            tagsCell.textContent = newTags;
            const actionsCell = newRow.insertCell();
            const deleteButton = document.createElement('button');
            deleteButton.textContent = '-';
            deleteButton.addEventListener('click', () => {
                event.stopPropagation();
                const index = trackerMappings.length - 1;
                trackerMappings.splice(index, 1);
                saveConfig();
                table.deleteRow(index + 1);
            });
            actionsCell.appendChild(deleteButton);
            newUrlInput.value = '';
            newTagsInput.value = '';
        }
        event.preventDefault()
    });
    form.appendChild(addButton);

    dialog.appendChild(form);
    document.body.appendChild(dialog);
    document.addEventListener('click', function(event) {
        if (!dialog.contains(event.target)) {
            saveConfig();
            document.body.removeChild(dialog);
            document.removeEventListener('click', arguments.callee);
        }
    });
}

GM_registerMenuCommand('设置', configureTrackerMappings);

jQuery("#desktopNavbar > ul").append(
    "<li><a class='js-modal'> 加标签 </a></li>",
);

jQuery(".js-modal").click(async function () {
    await processTorrents();
    saveConfig();
});