成人交流 · text · 0 分 · 评论 0

仓鼠小助手2.0

本帖最後由 mrz001 於 2026-4-17 08:46 編輯

https://www.dmmbus.cyou/forum/forum.php?mod=viewthread&tid=138391&extra=

这个插件功能包括:
(1)会将javbus网站上本地已经有(搜刮好的)的电影给标记出来
(2)查看javbus网站上下载链接里面视频截图(可以看出这个视频有没有广告,有没有字幕)

关于这个插件是干什么的以及怎么使用详情请见上面这个帖子上↑↑↑↑↑↑↑

这次2.0增加了功能,增加了一个预览磁力链接里面视频图片的功能有啥用:可以看出这个视频有没有广告,有没有字幕

改进:
以前扫描本地视频功能中改进了一下,改成了根据.nfo文件所在目录作为关键字(无论是我写搜刮软件以及mdcx都可以兼容了),考虑到了仓库可能是多个磁盘的情况

以下两个图片是第二个功能的使用情况,





这次代码有点多,因为第二个功能我是使用ai写的,你可以下滑到最下面,点击左下角的复制按钮
// ==UserScript==
// @name         仓鼠小助手
// @version      2.0.0
// @author       佚名
// @description  这是一个仓鼠助手
// @license      MIT
// @icon         https://www.javbus.com/favicon.ico
// @match        *://*.javbus.com/*
// @match        *://*.dmmbus.cyou/*
// @match        *://*.busjav.cyou/*
// @match        *://*.cdnbus.bond/*
// @match        *://*.busdmm.bond/*
// ==/UserScript==

(function () {
    'use strict';

    // ========== 新增:弹窗样式 ==========
    function createModalStyle() {
        const style = document.createElement('style');
        style.textContent = `
            /* 弹窗遮罩层 */
            .hamster-modal-mask {
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background: rgba(0, 0, 0, 0.5);
                z-index: 99999;
                display: none;
            }
            /* 弹窗主体 */
            .hamster-modal {
                position: fixed;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                width: 800px; /* 加宽弹窗以容纳图片 */
                background: #fff;
                border-radius: 8px;
                box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
                z-index: 100000;
                display: none;
                font-family: Arial, sans-serif;
                overflow: hidden;
            }
            /* 弹窗头部 */
            .hamster-modal-header {
                padding: 15px 20px;
                border-bottom: 1px solid #eee;
                display: flex;
                justify-content: space-between;
                align-items: center;
                background: #f8f9fa;
                border-radius: 8px 8px 0 0;
            }
            .hamster-modal-title {
                font-size: 16px;
                font-weight: bold;
                color: #333;
                margin: 0;
            }
            .hamster-modal-close {
                width: 30px;
                height: 30px;
                line-height: 30px;
                text-align: center;
                cursor: pointer;
                font-size: 20px;
                color: #666;
                border: none;
                background: transparent;
                padding: 0;
            }
            .hamster-modal-close:hover {
                color: #ff4444;
            }
            /* 弹窗内容 */
            .hamster-modal-body {
                padding: 20px;
                max-height: 600px; /* 增高内容区 */
                overflow-y: auto;
            }
            .hamster-info-item {
                margin-bottom: 12px;
                display: flex;
                align-items: flex-start;
            }
            .hamster-info-label {
                width: 80px;
                font-weight: bold;
                color: #666;
                flex-shrink: 0;
                padding-top: 2px;
            }
            .hamster-info-value {
                flex: 1;
                color: #333;
                word-break: break-all;
                line-height: 1.4;
            }
            .hamster-magnet-link {
                color: #1976d2;
                text-decoration: none;
            }
            .hamster-magnet-link:hover {
                text-decoration: underline;
            }
            /* 轮播图区域 */
            .hamster-carousel-section {
                margin-top: 20px;
                padding-top: 20px;
                border-top: 1px dashed #ddd;
            }
            .hamster-carousel-title {
                font-size: 14px;
                font-weight: bold;
                color: #666;
                margin-bottom: 10px;
                display: flex;
                justify-content: space-between;
                align-items: center;
            }
            .hamster-carousel-counter {
                font-size: 12px;
                font-weight: normal;
                color: #999;
            }
            /* 轮播图容器 */
            .hamster-carousel {
                position: relative;
                width: 100%;
                min-height: 100px;
                background: #f5f5f5;
                border-radius: 4px;
                padding: 10px;
                overflow: hidden;
            }
            /* 轮播图图片容器 */
            .hamster-carousel-wrapper {
                display: flex;
                transition: transform 0.3s ease;
                height: 300px; /* 固定轮播图高度 */
            }
            .hamster-carousel-item {
                flex: 0 0 100%;
                width: 100%;
                height: 100%;
                display: flex;
                justify-content: center;
                align-items: center;
            }
            .hamster-carousel-image {
                max-width: 100%;
                max-height: 100%;
                object-fit: contain;
                border-radius: 4px;
                box-shadow: 0 2px 8px rgba(0,0,0,0.1);
            }
            /* 轮播图控制按钮 */
            .hamster-carousel-btn {
                position: absolute;
                top: 50%;
                transform: translateY(-50%);
                width: 40px;
                height: 40px;
                border-radius: 50%;
                background: rgba(0,0,0,0.5);
                color: white;
                border: none;
                cursor: pointer;
                font-size: 18px;
                display: flex;
                justify-content: center;
                align-items: center;
                z-index: 10;
                transition: background 0.2s;
            }
            .hamster-carousel-btn:hover {
                background: rgba(0,0,0,0.7);
            }
            .hamster-carousel-prev {
                left: 10px;
            }
            .hamster-carousel-next {
                right: 10px;
            }
            /* 轮播图指示器 */
            .hamster-carousel-indicators {
                position: absolute;
                bottom: 10px;
                left: 50%;
                transform: translateX(-50%);
                display: flex;
                gap: 8px;
                z-index: 10;
            }
            .hamster-carousel-dot {
                width: 8px;
                height: 8px;
                border-radius: 50%;
                background: rgba(255,255,255,0.5);
                cursor: pointer;
                transition: background 0.2s;
            }
            .hamster-carousel-dot.active {
                background: white;
                width: 20px;
                border-radius: 4px;
            }
            /* 轮播图状态提示 */
            .hamster-carousel-loading,
            .hamster-carousel-error {
                color: #999;
                font-size: 14px;
                text-align: center;
                padding: 20px;
                width: 100%;
            }
            .hamster-carousel-error {
                color: #ff4444;
            }
            /* 弹窗底部 */
            .hamster-modal-footer {
                padding: 12px 20px;
                border-top: 1px solid #eee;
                display: flex;
                justify-content: flex-end;
                gap: 10px;
                background: #f8f9fa;
                border-radius: 0 0 8px 8px;
            }
            .hamster-modal-btn {
                padding: 6px 16px;
                border-radius: 4px;
                border: none;
                cursor: pointer;
                font-size: 14px;
                transition: background 0.2s;
            }
            .hamster-copy-btn {
                background: #2196f3;
                color: white;
            }
            .hamster-copy-btn:hover {
                background: #1976d2;
            }
            .hamster-close-btn {
                background: #e0e0e0;
                color: #333;
            }
            .hamster-close-btn:hover {
                background: #d0d0d0;
            }
            /* 复制提示 */
            .hamster-copy-tip {
                position: fixed;
                top: 20px;
                left: 50%;
                transform: translateX(-50%);
                background: rgba(0, 0, 0, 0.7);
                color: white;
                padding: 8px 16px;
                border-radius: 4px;
                z-index: 100001;
                display: none;
                font-size: 14px;
            }
            /* 详情按钮列样式 */
            .detail-btn-cell {
                text-align: center;
                white-space: nowrap;
                width: 80px;
            }
            /* 柔和版详情按钮样式 */
            .detail-btn {
                border: none;
                background: #f5f5f5;
                color: #666;
                padding: 4px 10px;
                border-radius: 6px;
                cursor: pointer;
                font-size: 13px;
                transition: background 0.2s;
            }
            .detail-btn:hover {
                background: #e9e9e9;
                color: #128a34;
            }
        `;
        document.head.appendChild(style);
    }

    // ========== 新增:创建弹窗DOM ==========
    function createModalDOM() {
        // 遮罩层
        const mask = document.createElement('div');
        mask.className = 'hamster-modal-mask';
        mask.id = 'hamsterModalMask';

        // 弹窗主体
        const modal = document.createElement('div');
        modal.className = 'hamster-modal';
        modal.id = 'hamsterModal';

        // 弹窗头部
        const header = document.createElement('div');
        header.className = 'hamster-modal-header';

        const title = document.createElement('h3');
        title.className = 'hamster-modal-title';
        title.textContent = '磁力链接详情';

        const closeBtn = document.createElement('button');
        closeBtn.className = 'hamster-modal-close';
        closeBtn.innerHTML = '×';
        closeBtn.title = '关闭';

        header.appendChild(title);
        header.appendChild(closeBtn);

        // 弹窗内容 - 移除了行号和磁力名称字段
        const body = document.createElement('div');
        body.className = 'hamster-modal-body';
        body.innerHTML = `
            <div class="hamster-info-item">
                <div class="hamster-info-label">文件大小:</div>
                <div class="hamster-info-value" id="modalSize"></div>
            </div>
            <div class="hamster-info-item">
                <div class="hamster-info-label">分享日期:</div>
                <div class="hamster-info-value" id="modalDate"></div>
            </div>
            <div class="hamster-info-item">
                <div class="hamster-info-label">磁力链接:</div>
                <div class="hamster-info-value">
                    <a class="hamster-magnet-link" id="modalMagnet" target="_self"></a>
                </div>
            </div>
            <!-- 轮播图区域 -->
            <div class="hamster-carousel-section">
                <div class="hamster-carousel-title">
                    <span>预览图片</span>
                    <span class="hamster-carousel-counter" id="carouselCounter">0/0</span>
                </div>
                <div class="hamster-carousel" id="carouselContainer">
                    <div class="hamster-carousel-loading">加载预览图片中...</div>
                    <!-- 轮播图内容会动态生成 -->
                </div>
            </div>
        `;

        // 弹窗底部
        const footer = document.createElement('div');
        footer.className = 'hamster-modal-footer';

        const copyBtn = document.createElement('button');
        copyBtn.className = 'hamster-modal-btn hamster-copy-btn';
        copyBtn.textContent = '复制链接';
        copyBtn.id = 'hamsterCopyBtn';

        const closeBtn2 = document.createElement('button');
        closeBtn2.className = 'hamster-modal-btn hamster-close-btn';
        closeBtn2.textContent = '关闭';
        closeBtn2.id = 'hamsterCloseBtn';

        footer.appendChild(copyBtn);
        footer.appendChild(closeBtn2);

        // 组装弹窗
        modal.appendChild(header);
        modal.appendChild(body);
        modal.appendChild(footer);

        // 复制提示
        const copyTip = document.createElement('div');
        copyTip.className = 'hamster-copy-tip';
        copyTip.id = 'hamsterCopyTip';
        copyTip.textContent = '复制成功!';

        // 添加到页面
        document.body.appendChild(mask);
        document.body.appendChild(modal);
        document.body.appendChild(copyTip);

        // 绑定关闭事件
        mask.addEventListener('click', hideModal);
        closeBtn.addEventListener('click', hideModal);
        closeBtn2.addEventListener('click', hideModal);

        // 绑定复制事件
        copyBtn.addEventListener('click', function () {
            const magnetLink = document.getElementById('modalMagnet').href;
            if (magnetLink) {
                navigator.clipboard.writeText(magnetLink)
                    .then(() => {
                        const tip = document.getElementById('hamsterCopyTip');
                        tip.style.display = 'block';
                        setTimeout(() => {
                            tip.style.display = 'none';
                        }, 1500);
                    })
                    .catch(err => {
                        alert('复制失败:' + err.message);
                    });
            }
        });

        // ESC键关闭弹窗
        document.addEventListener('keydown', function (e) {
            if (e.key === 'Escape') {
                hideModal();
            }
        });
    }

    // ========== 轮播图控制函数 ==========
    function initCarousel(images) {
        const carouselContainer = document.getElementById('carouselContainer');
        const counterEl = document.getElementById('carouselCounter');
        let currentIndex = 0;

        // 清空容器
        carouselContainer.innerHTML = '';

        // 更新计数器
        counterEl.textContent = `${currentIndex + 1}/${images.length}`;

        // 创建轮播图包装器
        const wrapper = document.createElement('div');
        wrapper.className = 'hamster-carousel-wrapper';

        // 创建所有图片项
        images.forEach((imgUrl, index) => {
            const item = document.createElement('div');
            item.className = 'hamster-carousel-item';

            const img = document.createElement('img');
            img.className = 'hamster-carousel-image';
            img.src = imgUrl;
            img.alt = `预览图片 ${index + 1}`;

            // 图片加载失败处理
            img.onerror = function () {
                img.alt = `图片 ${index + 1} 加载失败`;
                img.style.display = 'none';
                item.innerHTML = `<div class="hamster-carousel-error">图片 ${index + 1} 加载失败</div>`;
            };

            item.appendChild(img);
            wrapper.appendChild(item);
        });

        // 创建控制按钮
        const prevBtn = document.createElement('button');
        prevBtn.className = 'hamster-carousel-btn hamster-carousel-prev';
        prevBtn.innerHTML = '<';
        prevBtn.addEventListener('click', () => {
            currentIndex = (currentIndex - 1 + images.length) % images.length;
            updateCarousel();
        });

        const nextBtn = document.createElement('button');
        nextBtn.className = 'hamster-carousel-btn hamster-carousel-next';
        nextBtn.innerHTML = '>';
        nextBtn.addEventListener('click', () => {
            currentIndex = (currentIndex + 1) % images.length;
            updateCarousel();
        });

        // 创建指示器
        const indicators = document.createElement('div');
        indicators.className = 'hamster-carousel-indicators';

        images.forEach((_, index) => {
            const dot = document.createElement('div');
            dot.className = `hamster-carousel-dot ${index === currentIndex ? 'active' : ''}`;
            dot.addEventListener('click', () => {
                currentIndex = index;
                updateCarousel();
            });
            indicators.appendChild(dot);
        });

        // 组装轮播图
        carouselContainer.appendChild(wrapper);
        carouselContainer.appendChild(prevBtn);
        carouselContainer.appendChild(nextBtn);
        carouselContainer.appendChild(indicators);

        // 更新轮播图状态
        function updateCarousel() {
            // 移动图片容器
            wrapper.style.transform = `translateX(-${currentIndex * 100}%)`;

            // 更新计数器
            counterEl.textContent = `${currentIndex + 1}/${images.length}`;

            // 更新指示器
            const dots = indicators.querySelectorAll('.hamster-carousel-dot');
            dots.forEach((dot, index) => {
                dot.className = `hamster-carousel-dot ${index === currentIndex ? 'active' : ''}`;
            });
        }
    }

    // ========== 显示/隐藏弹窗函数 ==========
    async function showModal(row, magnetLink, rowElement) {
        // 获取行数据(不再使用行号和磁力名称)
        const size = rowElement.find('td:nth-child(3) a').text().trim() || '无'; // 列索引适配
        const date = rowElement.find('td:nth-child(4) a').text().trim() || '无';   // 列索引适配

        // 填充基础数据(移除了行号和磁力名称的赋值)
        document.getElementById('modalSize').textContent = size;
        document.getElementById('modalDate').textContent = date;
        document.getElementById('modalMagnet').href = magnetLink;
        document.getElementById('modalMagnet').textContent = magnetLink;



        // 重置轮播图容器为加载状态
        const carouselContainer = document.getElementById('carouselContainer');
        const counterEl = document.getElementById('carouselCounter');
        carouselContainer.innerHTML = '<div class="hamster-carousel-loading">加载预览图片中...</div>';
        counterEl.textContent = '0/0';

        // 显示弹窗
        document.getElementById('hamsterModalMask').style.display = 'block';
        document.getElementById('hamsterModal').style.display = 'block';

        // 加载预览图片(轮播图版)
        try {
            const resp = await fetch('https://whatslink.info/api/v1/link?url=' + encodeURIComponent(magnetLink));
            if (!resp.ok) throw new Error(`HTTP错误:${resp.status}`);

            const json = await resp.json();

            if (json.screenshots && json.screenshots.length > 0) {
                // 提取所有图片URL
                const imageUrls = json.screenshots.map(item => item.screenshot);
                // 初始化轮播图
                initCarousel(imageUrls);
            } else {
                carouselContainer.innerHTML = '<div class="hamster-carousel-error">暂无预览图片</div>';
            }
        } catch (err) {
            console.error('加载预览图片失败:', err);
            carouselContainer.innerHTML = `<div class="hamster-carousel-error">获取图片失败:${err.message}</div>`;
        }
    }

    function hideModal() {
        document.getElementById('hamsterModalMask').style.display = 'none';
        document.getElementById('hamsterModal').style.display = 'none';

        // 隐藏时重置轮播图
        const carouselContainer = document.getElementById('carouselContainer');
        const counterEl = document.getElementById('carouselCounter');
        if (carouselContainer && counterEl) {
            carouselContainer.innerHTML = '<div class="hamster-carousel-loading">加载预览图片中...</div>';
            counterEl.textContent = '0/0';
        }
    }

    // 创建一个按钮元素
    let button = document.createElement('button');
    button.innerHTML = '仓';
    button.style.position = 'fixed';
    button.style.bottom = '1px';
    button.style.left = '1px';
    button.style.zIndex = '1000';
    button.style.alignContent = "center";
    button.style.width = "35px";
    button.style.height = "35px";

    // 添加按钮点击事件
    button.addEventListener('click', async function () {
        button.style.background = "#aaaaff";
        button.innerHTML = "....";
        try {
            await btnClick();
        } catch (e) {
            console.log('出错了', e);
            alert('加载出错:' + e);
        } finally {
            button.style.background = "";
            button.innerHTML = "仓"
        }
    });

    // 将按钮添加到页面中
    document.body.appendChild(button);
    let dian = 1;//进度
    async function btnClick() {
        const nfoFilesSet = new Set();
        const dirHandle = await window.showDirectoryPicker();
        if (!dirHandle) {
            alert("获取本地信息失败");
            return;
        }
        async function scanDirectory(handle) {
            for await (const entry of handle.values()) {
                dian++;
                button.innerHTML = "...".substring((dian % 3));
                if (entry.kind === 'directory') {
                    // 1. 如果是文件夹,先递归检查该文件夹内部
                    await scanDirectory(entry);
                } else if (entry.kind === 'file') {
                    // 2. 如果是文件,判断后缀是否为 .nfo
                    if (entry.name.toLowerCase().endsWith('.nfo')) {
                        // 3. 将包含该文件的父级文件夹名称存入 Set
                        nfoFilesSet.add(handle.name);
                    }
                }
            }
        }
        // 执行遍历
        await scanDirectory(dirHandle);

        const oldFilesStr = localStorage.getItem("nfoFiles");
        const oldFiles = JSON.parse(oldFilesStr || '[]');
        for (const name of oldFiles) {
            nfoFilesSet.add(name);
        }

        const str = JSON.stringify(Array.from(nfoFilesSet));
        localStorage.setItem("nfoFiles", str);
        console.log("设置成功,数量", oldFiles.length, '->', nfoFilesSet.size);
        start();
    }

    async function start() {
        const nfoFilesStr = localStorage.getItem('nfoFiles');
        if (!nfoFilesStr) {
            return;
        }
        const nfoFiles = JSON.parse(nfoFilesStr);
        console.log('共加载', nfoFiles.length, "个");

        $('#waterfall .movie-box').each((index, ele) => {
            var movieBox = $(ele);
            const url = movieBox.attr('href');
            const code = url.substring(url.lastIndexOf('/') + 1);
            if (nfoFiles.indexOf(code) != -1) {
                movieBox.css('background-color', 'red');
            }
        })

        const url = location.href;
        const code = url.substring(url.lastIndexOf('/') + 1);
        if (nfoFiles.indexOf(code) != -1) {
            $('div.row.movie').css('background-color', 'gold')
        }
    }
    start();

    async function linkInfo(loaded) {
        const mt = $('#magnet-table');
        if (!mt.length) {
            return;
        }

        if ($('#magnet-table tr').length > 1) {
            loaded = true;
        }



        if (!loaded) {
            const observer = new MutationObserver(function (mutations) {
                // 遍历所有DOM变化
                mutations.forEach(function (mutation) {
                    // 只处理"子节点添加/移除"的变化
                    if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                        // 延迟50ms执行(确保动态行完全加载)
                        observer.disconnect();
                        setTimeout(() => {
                            linkInfo(true)
                        }, 50);
                    }
                });
            });
            // 配置监听选项:监听tbody的子节点变化、子节点的子节点变化
            const observerConfig = {
                childList: true,    // 监听子节点添加/移除
                subtree: true,      // 监听子树(行内的td变化)
                attributes: false   // 不监听属性变化(可根据需要开启)
            };

            // 启动监听:监听表格tbody的变化
            const targetNode = document.querySelector('#magnet-table');
            if (targetNode) {
                observer.observe(targetNode, observerConfig);
            }
            return;
        }


        // 初始化弹窗样式和DOM
        createModalStyle();
        createModalDOM();

        // 1. 给表头新增"详情"列(第一列)
        const $headerRow = $('#magnet-table tbody tr:first');
        if ($headerRow.find('td').length >= 3) { // 确保表头存在
            // 在表头第一个位置插入详情列
            $headerRow.prepend('<td class="detail-btn-cell" style="text-align:center;white-space:nowrap">详情</td>');
        }

        // 2. 给每一行数据新增详情按钮列(第一列)
        $('#magnet-table tr:not(:first)').each(function (index) {
            const $row = $(this);
            // 检查是否已经添加过详情按钮(防止重复)
            if ($row.find('td').length >= 3 && !$row.find('.detail-btn-cell').length) {
                // 获取当前行的磁力链接(原第二列变为第三列)
                var magnetLink = $row.find('td:nth-child(2) a').attr('href'); // 列索引适配
                var rowIndex = index + 1; // 行号(从1开始)

                // 构建柔和版详情按钮的HTML(仅显示"详情"二字,无图标)
                var detailBtn = `
                    <td class="detail-btn-cell">
                        <button class="detail-btn"
                                data-magnet="${magnetLink}"
                                data-row="${rowIndex}">
                            详情
                        </button>
                    </td>
                `;

                // 将按钮添加到当前行的第一个位置
                $row.prepend(detailBtn);
            }
        });

        // 3. 给详情按钮绑定点击事件(使用事件委托防止重复绑定)
        $('#magnet-table').off('click', '.detail-btn').on('click', '.detail-btn', function () {
            var magnet = $(this).data('magnet'); // 获取磁力链接
            var row = $(this).data('row'); // 行号仍保留(内部使用,不显示)
            var $rowElement = $(this).closest('tr'); // 获取当前行元素

            // 显示自定义弹窗
            showModal(row, magnet, $rowElement);
        });
    }

    linkInfo();
})();複製代碼




评论 (0)

登录 后参与讨论。

还没有评论,来抢沙发吧。