Greasy Fork

Greasy Fork is available in English.

Get Free Torrents

该插件主要用于抓取指定页面(即 "torrents.php")中的免费种子信息,并将其按照剩余时间从短到长排序后,以表格形式呈现给用户。用户可以一键复制所有展示种子的链接,同时具备筛选功能,允许用户设定自定义时间阈值,仅复制剩余时间超过该阈值的种子链接。此外,插件还支持添加自定义URL参数以扩展功能或满足个性化需求。

目前为 2024-03-22 提交的版本,查看 最新版本

// ==UserScript==
// @name Get Free Torrents
// @namespace http://tampermonkey.net/
// @version 1.0.1
// @description 该插件主要用于抓取指定页面(即 "torrents.php")中的免费种子信息,并将其按照剩余时间从短到长排序后,以表格形式呈现给用户。用户可以一键复制所有展示种子的链接,同时具备筛选功能,允许用户设定自定义时间阈值,仅复制剩余时间超过该阈值的种子链接。此外,插件还支持添加自定义URL参数以扩展功能或满足个性化需求。
// @author 飞天小猪
// @match http*://*/*torrents*.php*
// @icon https://gongjux.com/files/3/4453uhm5937m/32/favicon.ico
// @grant none
// @require http://greasyfork.icu/scripts/453166-jquery/code/jquery.js?version=1105525
// @require http://greasyfork.icu/scripts/28502-jquery-ui-v1-11-4/code/jQuery%20UI%20-%20v1114.js?version=187735
// @license MIT
// ==/UserScript==

(function () {
    'use strict';
    let info = []
    function selectTDsWithFreeClassAncestors() {
        var $matchingTds = $();

        // 选择所有class包含"free"的后代元素
        var $freeElements = $('[alt*="Free"]');

        // 遍历这些元素,找到它们的所有祖先td元素
        $freeElements.each(function () {
            var $ancestorsWithClass = $(this).parentsUntil('table', 'tr');

            // 把找到的td元素加入结果集
            $matchingTds = $matchingTds.add($ancestorsWithClass);
        });

        // 返回结果集
        return $matchingTds;
    }

    // 调用函数并进行操作
    function normalizeUrl(url) {
        // 使用正则表达式匹配双斜杠并替换为单斜杠
        return url.replace(/\/\//g, '/');
    }
    function timeSort(data) {
        return data.sort((a, b) => {
            if (a.time === 'infinite') {
                return 1;
            } else if (b.time === 'infinite') {
                return -1;
            } else {
                return new Date(a.time) - new Date(b.time);
            }
        });
    }
    function calcRestTime(timeStr) {
        if (timeStr === 'infinite') {
            return { restTime: '无限', days: 9999, hours: 9999, minutes: 9999 };
        }
        const date = new Date(timeStr);
        const now = new Date();
        const sub = Math.abs(date.getTime() - now.getTime())

        // 转换为天数、小时数、分钟数和秒数
        const days = Math.floor(sub / (1000 * 60 * 60 * 24));
        const hours = Math.floor((sub % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
        const minutes = Math.floor((sub % (1000 * 60 * 60)) / (1000 * 60));

        return {
            restTime: `${days > 0 ? days + '天 ' : ''}${hours > 0 ? hours + '小时 ' : ''}${minutes}分`,
            days,
            hours,
            minutes
        }
    }
    function getInfo() {
        const selectedTds = selectTDsWithFreeClassAncestors().toArray();
        const result = selectedTds.map(i => {
            const $tdElement = $(i);
            const dateTimeRegex = /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/;
            const title = $($tdElement.find('a[href*="details.php"]')[0]).attr('title')
            const $spansWithTitle = $tdElement.find('span[title]');
            const url = $($tdElement.find('a[href*="download.php"]')[0]).attr('href')
            const titles = $spansWithTitle.filter(function () {
                return dateTimeRegex.test($(this).attr('title'));
            }).get();
            if (titles.length) {
                return {
                    time: $(titles[0]).attr('title'),
                    url: normalizeUrl(location.origin + '/' + url),
                    title
                }
            } else {
                return { time: 'infinite', url: normalizeUrl(location.origin + '/' + url), title }
            }
        })
        result.forEach(i => {
            const { restTime, days, hours, minutes } = calcRestTime(i.time)
            i.restTime = restTime
            i.days = days
            i.hours = hours
            i.minutes = minutes
        })
        return timeSort(result)
    }
    // 复制文字到剪贴板
    async function copyToClipboard(text) {
        try {
            await navigator.clipboard.writeText(text);
        } catch (err) {
            console.error('Failed to copy to clipboard: ', err);
        }
    }
    function setButton() {
        // 创建一个新的button元素
        var button = document.createElement('button');
        button.textContent = '获取信息'; // 设置按钮文字

        // 设置按钮的基本样式(包括固定定位与默认透明度)
        button.style.cssText = `
position: fixed; /* 或者 absolute,取决于您的布局需求 */
top: 20px; /* 举例位置,您可以自定义 */
right: 20px; /* 举例位置,您可以自定义 */
background-color: #007bff;
color: white;
padding: 6px 12px;
border: none;
border-radius: 5px;
cursor: pointer;
opacity: 0.3;
transition: opacity 0.3s ease;
`;


        // 添加鼠标悬浮时的透明度变化
        button.addEventListener('mouseover', function () {
            this.style.opacity = 1;
        });

        // 添加鼠标离开时的透明度变化
        button.addEventListener('mouseout', function () {
            this.style.opacity = 0.3;
        });
        button.addEventListener('click', setTable)
        // 将按钮添加到文档中
        document.body.appendChild(button);
    }
    function setTable() {
        const sortedData = getInfo()
        const mask = document.createElement('div');
        mask.addEventListener('click', (event) => {
            // 判断点击的是mask本身还是其子元素
            if (event.target === mask) {
                document.body.removeChild(mask);
            } else {
                event.stopPropagation(); // 阻止子元素点击事件向上冒泡到mask
            }
        });
        mask.classList.add('mask');
        mask.style.cssText = `
background-color: rgba(0, 0, 0, 0.2);
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000;
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
`
        document.body.appendChild(mask);
        const tableWrap = document.createElement('div')
        tableWrap.classList.add('table-wrap');
        tableWrap.style.cssText = `
max-width: 1000px;
max-height: 800px;
min-height: 400px;
min-width: 500px;
width: 50vw;
height: 50vh;
background-color: #fff;
overflow-y: auto;
padding: 10px;
`

        // 创建操作区域
        const btnArea = document.createElement('div')
        btnArea.style.cssText = `
height: 40px;
margin-bottom: 8px;
box-sizing: border-box;
padding: 4px 0;
`
        // 创建关闭按钮
        const closeBtn = document.createElement('button')
        closeBtn.textContent = '关闭'
        closeBtn.style.cssText = `
background-color: #F56C6C;
color: white;
padding: 6px 12px;
border: none;
border-radius: 5px;
cursor: pointer;
margin-right: 10px;
`
        closeBtn.addEventListener('click', () => {
            document.body.removeChild(mask);
        })
        // 创建复制链接按钮
        const copyLink = document.createElement('button')
        copyLink.textContent = '复制下载链接'
        copyLink.style.cssText = `
background-color: #007bff;
color: white;
padding: 6px 12px;
border: none;
border-radius: 5px;
cursor: pointer;
margin-right: 10px;
`
        const filterInput = document.createElement('input')
        filterInput.type = 'number'
        filterInput.placeholder = '剩余时间 > ?'
        filterInput.style.cssText = `
width: 90px;
margin-right: 10px;
padding: 6px;
border-radius: 4px;
border: 1px solid #2165f9;
`
        copyLink.addEventListener('click', () => {
            let suffix = ''
            if (textArea.value) {
                suffix = `?${textArea.value.split('\n').join('&')}`
          }
            if (filterInput.value) {
                const limit = +filterInput.value
                const filterData = sortedData.filter(i => i.days * 24 + i.hours > limit)
                const str = filterData.map(i => i.url + suffix).join('\n')
                copyToClipboard(str)
                alert('复制成功')
            } else {
                const str = sortedData.map(i => i.url + suffix).join('\n')
                copyToClipboard(str)
                alert('复制成功')
            }
        })
        // 创建复制cookie按钮
        const copyCookie = document.createElement('button')
        copyCookie.textContent = '复制Cookie'
        copyCookie.style.cssText = `
background-color: #007bff;
color: white;
padding: 6px 12px;
border: none;
border-radius: 5px;
cursor: pointer;
`
        copyCookie.addEventListener('click', () => {
            const cookie = document.cookie
            if (cookie) {
                copyToClipboard(cookie)
                alert('复制成功')
            } else {
                alert('Cookie 为空')
            }
        })
        btnArea.appendChild(closeBtn)
        btnArea.appendChild(filterInput)
        btnArea.appendChild(copyLink)
        btnArea.appendChild(copyCookie)

        const textAreaWrap = document.createElement('div')
        textAreaWrap.style.cssText = `
height: 160px;
width: 100%;
overflow-y: auto;
padding: 10px;
box-sizing: border-box;
`
        const textArea = document.createElement('textarea')
        textArea.placeholder = '请输入自定义参数 1行一条,格式为 key=value'
        textArea.style.cssText = `
width: 100%;
height: 100%;
border: 1px solid #2165f9;
box-sizing: border-box;
padding: 10px;
border-radius: 4px;
`
        textAreaWrap.appendChild(textArea)

        // 创建表格元素
        const table = document.createElement('table');
        table.style.cssText = `
width: 100%;
height: 100%;
`;
        tableWrap.appendChild(btnArea)
        tableWrap.appendChild(textAreaWrap)


        const tableBox = document.createElement('div');
        tableBox.style.cssText = `
height: calc(100% - 208px);
width: 100%;
overflow-y: auto;
`
        tableWrap.appendChild(tableBox);
        tableBox.appendChild(table);
        mask.appendChild(tableWrap);

        // 创建表头
        const thead = document.createElement('thead');
        const headerRow = document.createElement('tr');
        ['剩余时间', '标题', '下载链接'].forEach(header => {
            const th = document.createElement('th');
            th.textContent = header;
            headerRow.appendChild(th);
            th.style.cssText = `
position: sticky;
top: 0;
background-color: white; /* 添加背景颜色以防止内容滚动时穿透 */
z-index: 2;
`
        });
        thead.appendChild(headerRow);
        table.appendChild(thead);

        // 创建表体
        const tbody = document.createElement('tbody');
        sortedData.forEach(item => {
            const row = document.createElement('tr');

            const timeCell = document.createElement('td');
            timeCell.textContent = item.restTime;
            row.appendChild(timeCell);

            const titleCell = document.createElement('td');
            titleCell.textContent = item.title
            row.appendChild(titleCell);

            const linkCell = document.createElement('td');
            linkCell.textContent = item.url
            row.appendChild(linkCell);

            if (item.days <= 0 && item.hours <= 0) {
                timeCell.style.color = 'red'; titleCell.style.color = 'red';
                linkCell.style.color = 'red';
            } else if (item.days <= 0 && item.hours <= 12) {
                timeCell.style.color = 'orange';
                titleCell.style.color = 'orange'; linkCell.style.color = 'orange';
            } tbody.appendChild(row);
        });
        table.appendChild(tbody);
    } setButton()
})();