Greasy Fork is available in English.
在PT站点添加加载更多功能和删除行功能,适用于多个相似结构的PT站点
// ==UserScript==
// @name PT站点加载更多种子
// @namespace http://tampermonkey.net/
// @version 0.7
// @description 在PT站点添加加载更多功能和删除行功能,适用于多个相似结构的PT站点
// @author Andiedie
// @match https://pt.agsvpt.cn/torrents.php*
// @match https://piggo.me/special.php*
// @match https://piggo.me/search.php*
// @match https://piggo.me/torrents.php*
// @match https://ourbits.club/torrents.php*
// @match https://www.qingwapt.com/torrents.php*
// @match https://www.agsvpt.com/torrents.php*
// @match https://audiences.me/torrents.php*
// @match https://www.ptzone.xyz/torrents.php*
// @match https://rousi.zip/torrents.php*
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// 记录已加载的页面和最大已加载页码
let loadedPages = new Set();
let maxLoadedPageDisplay = 1; // 从1开始计数的显示页码
// 主函数
function init() {
// 寻找分页元素(使用新函数)
const paginationElements = findPaginationElements();
if (paginationElements.length === 0) {
console.log('未找到分页元素');
return;
}
// 从URL获取当前页码
const urlParams = new URLSearchParams(window.location.search);
const currentPage = parseInt(urlParams.get('page') || 0);
// 将当前页面标记为已加载
loadedPages.add(currentPage);
maxLoadedPageDisplay = currentPage + 1; // 当前页码(从0开始) + 1 = 显示页码(从1开始)
// 找到最后一页的页码
const lastPageNumber = findLastPageNumber(paginationElements[0]);
// 检查是否在第一页
if (currentPage === 0) {
// 添加输入框和按钮到分页元素
addLoadMoreInterface(paginationElements, lastPageNumber);
}
// 添加统计信息到分页元素
updateStatistics(paginationElements);
// 为现有的种子行添加删除按钮
addDeleteButtonsToExistingRows();
}
// 函数:查找分页元素
function findPaginationElements() {
let elements = [];
// 策略1: 使用原有的类名选择器(保持向后兼容)
const nexusPagination = document.querySelectorAll('.nexus-pagination');
if (nexusPagination.length > 0) {
return Array.from(nexusPagination);
}
// 策略2: 查找包含"上一页"和"下一页"文本的p元素
const allParagraphs = document.querySelectorAll('p');
for (const p of allParagraphs) {
const text = p.textContent.toLowerCase();
if ((text.includes('上一页') || text.includes('previous')) &&
(text.includes('下一页') || text.includes('next'))) {
elements.push(p);
continue;
}
// 策略3: 查找包含页码范围格式的p元素
if (p.textContent.match(/\d+\s*-\s*\d+/)) {
// 确认这个元素包含多个页面链接
const links = p.querySelectorAll('a');
if (links.length >= 3 && links[0].href.includes('page=')) {
elements.push(p);
continue;
}
}
// 策略4: 查找带有align="center"属性且包含页面链接的p元素
if (p.getAttribute('align') === 'center') {
const links = p.querySelectorAll('a');
if (links.length >= 3) {
let isPageLinks = false;
for (const link of links) {
if (link.href.includes('page=')) {
isPageLinks = true;
break;
}
}
if (isPageLinks) {
elements.push(p);
continue;
}
}
}
}
return elements;
}
// 函数:为已存在的种子行添加删除按钮
function addDeleteButtonsToExistingRows() {
const torrentTable = document.querySelector('.torrents>tbody');
if (!torrentTable) return;
// 获取所有种子行(跳过表头)
const torrentRows = Array.from(torrentTable.querySelectorAll(':scope>tr')).slice(1);
// 为每一行添加删除按钮
torrentRows.forEach(row => {
addDeleteButtonToRow(row);
});
}
// 函数:为单个种子行添加删除按钮
function addDeleteButtonToRow(row) {
// 创建一个新的表格单元格
const deleteCell = document.createElement('td');
deleteCell.className = 'row-delete-cell';
deleteCell.style.textAlign = 'center';
// 创建删除按钮
const deleteButton = document.createElement('button');
deleteButton.textContent = 'x';
deleteButton.style.padding = '8px';
deleteButton.style.cursor = 'pointer';
deleteButton.style.backgroundColor = '#d0d0d0';
deleteButton.style.color = 'black';
deleteButton.style.border = 'none';
deleteButton.style.borderRadius = '3px';
// 添加删除功能
deleteButton.addEventListener('click', function() {
// 删除行
row.remove();
// 更新统计信息
updateStatistics(findPaginationElements());
});
deleteCell.appendChild(deleteButton);
row.appendChild(deleteCell);
}
// 函数:寻找最后一页的页码
function findLastPageNumber(paginationElement) {
// 寻找分页中的所有链接
const links = paginationElement.querySelectorAll('a');
let lastPageNumber = 0;
// 遍历链接找到最高页码
links.forEach(link => {
const href = link.getAttribute('href');
if (href) {
const pageMatch = href.match(/[?&]page=(\d+)/);
if (pageMatch && pageMatch[1]) {
const pageNum = parseInt(pageMatch[1]);
if (pageNum > lastPageNumber) {
lastPageNumber = pageNum;
}
}
}
});
return lastPageNumber;
}
// 函数:添加加载更多的界面
function addLoadMoreInterface(paginationElements, lastPageNumber) {
paginationElements.forEach(pagination => {
const container = document.createElement('div');
container.className = 'load-more-container';
container.style.margin = '10px 0';
container.style.textAlign = 'center';
const label = document.createElement('span');
label.textContent = '加载到第 ';
const input = document.createElement('input');
input.type = 'number';
input.min = 1;
input.max = lastPageNumber + 1; // +1 因为页面显示从1开始
input.value = lastPageNumber + 1; // 默认显示到最后一页
input.style.width = '60px';
input.style.marginRight = '5px';
input.style.marginLeft = '5px';
const postLabel = document.createElement('span');
postLabel.textContent = ' 页 ';
const button = document.createElement('button');
button.textContent = '加载更多';
button.addEventListener('click', () => {
// 将用户输入的页码(从1开始)转换为系统页码(从0开始)
const targetPageDisplay = parseInt(input.value);
if (targetPageDisplay > 0 && targetPageDisplay <= lastPageNumber + 1) {
const targetPage = targetPageDisplay - 1; // 转换为从0开始的索引
// 获取当前页码
const currentUrl = new URL(window.location.href);
const urlParams = new URLSearchParams(currentUrl.search);
const currentPage = parseInt(urlParams.get('page') || 0);
// 设置最大已加载页码
maxLoadedPageDisplay = Math.max(maxLoadedPageDisplay, targetPageDisplay);
// 开始递归加载页面
button.disabled = true;
button.textContent = '加载中...';
// 从当前页+1开始加载,直到目标页
loadMultiplePages(currentPage + 1, targetPage);
} else {
alert('请输入有效的页码 (1-' + (lastPageNumber + 1) + ')');
}
});
container.appendChild(label);
container.appendChild(input);
container.appendChild(postLabel);
container.appendChild(button);
pagination.appendChild(container);
});
}
// 函数:递归加载多个页面
function loadMultiplePages(currentPage, targetPage) {
// 如果当前页已超过目标页,则停止加载
if (currentPage > targetPage) {
document.querySelectorAll('.load-more-container button').forEach(btn => {
btn.disabled = false;
btn.textContent = '加载更多';
});
// 更新统计信息
updateStatistics(findPaginationElements());
return;
}
// 如果当前页已经加载过,跳到下一页
if (loadedPages.has(currentPage)) {
loadMultiplePages(currentPage + 1, targetPage);
return;
}
// 加载当前页
const loadingText = `加载中 ${currentPage + 1}/${targetPage + 1}...`;
document.querySelectorAll('.load-more-container button').forEach(btn => {
btn.textContent = loadingText;
});
loadOnePage(currentPage, () => {
// 加载完成后,记录并继续加载下一页
loadedPages.add(currentPage);
loadMultiplePages(currentPage + 1, targetPage);
});
}
// 函数:加载单个页面的种子
function loadOnePage(pageNumber, callback) {
// 创建目标页面的URL
const currentUrl = new URL(window.location.href);
const urlParams = new URLSearchParams(currentUrl.search);
urlParams.set('page', pageNumber);
currentUrl.search = urlParams.toString();
// 获取目标页面
fetch(currentUrl.toString())
.then(response => response.text())
.then(html => {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
// 在获取的页面中找到种子表格
const fetchedTorrentTable = doc.querySelector('.torrents>tbody');
if (!fetchedTorrentTable) {
console.error('在获取的页面中未找到种子表格');
callback();
return;
}
// 获取种子行(跳过表头)- 使用直接子选择器
const torrentRows = Array.from(fetchedTorrentTable.querySelectorAll(':scope>tr')).slice(1);
// 在当前页面找到种子表格
const currentTorrentTable = document.querySelector('.torrents>tbody');
if (!currentTorrentTable) {
console.error('在当前页面未找到种子表格');
callback();
return;
}
// 将种子行添加到当前表格,并为每一行添加删除按钮
torrentRows.forEach(row => {
const newRow = row.cloneNode(true);
addDeleteButtonToRow(newRow);
currentTorrentTable.appendChild(newRow);
});
// 更新统计信息(只针对计数和总大小,不更新页码显示)
updateStatistics(findPaginationElements(), false);
console.log(`已添加 ${torrentRows.length} 个种子,来自第 ${pageNumber + 1} 页`);
// 执行回调
callback();
})
.catch(error => {
console.error(`获取第 ${pageNumber + 1} 页种子时出错:`, error);
// 即使出错也执行回调以继续后续操作
callback();
});
}
// 函数:从单元格解析大小
function parseSize(sizeCell) {
// 获取单元格的文本内容
const sizeText = sizeCell.textContent.replace(/\s+/g, ' ').trim();
// 使用更灵活的正则表达式提取数字和单位
const match = sizeText.match(/([\d.]+)\s*([KMGT]?B)/i);
if (!match) return 0;
const value = parseFloat(match[1]);
const unit = match[2].toUpperCase();
// 转换为字节
switch (unit) {
case 'KB': return value * 1024;
case 'MB': return value * 1024 * 1024;
case 'GB': return value * 1024 * 1024 * 1024;
case 'TB': return value * 1024 * 1024 * 1024 * 1024;
default: return value; // 假设为字节(B)或未知单位
}
}
// 函数:将字节格式化为适当的单位
function formatSize(bytes) {
if (bytes === 0) return '0 B';
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
let index = 0;
let size = bytes;
while (size >= 1024 && index < units.length - 1) {
size /= 1024;
index++;
}
return size.toFixed(2) + ' ' + units[index];
}
// 函数:更新统计信息
function updateStatistics(paginationElements, updatePageNumber = true) {
// 找到所有种子行(使用直接子选择器)
const torrentTable = document.querySelector('.torrents>tbody');
if (!torrentTable) return;
const torrentRows = torrentTable.querySelectorAll(':scope>tr');
// 跳过表头行
const torrentCount = torrentRows.length - 1;
// 计算总大小
let totalBytes = 0;
Array.from(torrentRows).slice(1).forEach(row => {
if (row.cells.length >= 5) {
const sizeCell = row.cells[4]; // 第5列(从0开始索引)
if (sizeCell) {
totalBytes += parseSize(sizeCell);
}
}
});
// 格式化总大小
const formattedSize = formatSize(totalBytes);
// 创建或更新统计元素
paginationElements.forEach(pagination => {
let statsElement = pagination.querySelector('.torrent-stats');
if (!statsElement) {
statsElement = document.createElement('div');
statsElement.className = 'torrent-stats';
statsElement.style.margin = '10px 0';
statsElement.style.fontWeight = 'bold';
pagination.appendChild(statsElement);
}
statsElement.textContent = `当前显示: ${torrentCount} 个种子,总体积: ${formattedSize},已加载到第 ${maxLoadedPageDisplay} 页`;
});
}
// 初始化脚本
init();
})();