吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 946|回复: 39
收起左侧

[其他转载] 收藏夹链接有效性检测

  [复制链接]
lcg888 发表于 2025-4-15 17:34
本帖最后由 lcg888 于 2025-4-15 17:37 编辑






AI生成的感觉还可以但并不是那么的完美

功能说明
这个HTML文件实现了一个完整的书签链接检测工具,具有以下特点:

现代化界面设计:

响应式布局,适配不同屏幕尺寸
清晰的视觉反馈和状态指示
拖放文件上传支持
完整的检测功能:

可配置的超时时间、检测次数和并发数
使用HEAD方法提高检测效率
支持中途停止检测
全面的状态显示:

实时进度条和统计信息
详细的书签状态分类(有效/无效/超时)
每次检测的详细结果统计
便捷的操作功能:

单个书签测试功能
一键访问链接
全选/取消全选支持
强大的导出功能:

按状态筛选导出
仅导出选中书签
生成标准HTML书签格式
高效的管理功能:

一键删除无效链接
按分类查看不同状态的链接
标签页切换浏览不同状态的书签
这个工具完全在浏览器中运行,不需要服务器支持,所有数据处理都在客户端完成,保护用户隐私
[Asm] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>书签链接检测工具</title>
    <style>
        :root {
            --primary-color: #4285f4;
            --success-color: #34a853;
            --danger-color: #ea4335;
            --warning-color: #fbbc05;
            --light-color: #f8f9fa;
            --dark-color: #343a40;
        }
        
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            line-height: 1.6;
            color: #333;
            background-color: #f5f5f5;
            margin: 0;
            padding: 0;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }
        
        header {
            background-color: var(--primary-color);
            color: white;
            padding: 20px 0;
            margin-bottom: 30px;
            border-radius: 5px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
        }
        
        header h1 {
            margin: 0;
            text-align: center;
            font-weight: 500;
        }
        
        .card {
            background-color: white;
            border-radius: 5px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            padding: 20px;
            margin-bottom: 20px;
        }
        
        .card-title {
            margin-top: 0;
            color: var(--primary-color);
            border-bottom: 1px solid #eee;
            padding-bottom: 10px;
        }
        
        .form-group {
            margin-bottom: 15px;
        }
        
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: 500;
        }
        
        input[type="number"], select {
            width: 100%;
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 4px;
            box-sizing: border-box;
        }
        
        .btn {
            display: inline-block;
            background-color: var(--primary-color);
            color: white;
            padding: 10px 15px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            transition: background-color 0.3s;
        }
        
        .btn:hover {
            background-color: #3367d6;
        }
        
        .btn-success {
            background-color: var(--success-color);
        }
        
        .btn-success:hover {
            background-color: #2d9249;
        }
        
        .btn-danger {
            background-color: var(--danger-color);
        }
        
        .btn-danger:hover {
            background-color: #d33426;
        }
        
        .btn-warning {
            background-color: var(--warning-color);
        }
        
        .btn-warning:hover {
            background-color: #e6ac00;
        }
        
        .progress-container {
            margin-top: 20px;
            background-color: #eee;
            border-radius: 5px;
            height: 20px;
            overflow: hidden;
        }
        
        .progress-bar {
            height: 100%;
            background-color: var(--primary-color);
            width: 0%;
            transition: width 0.3s;
        }
        
        .stats {
            display: flex;
            justify-content: space-between;
            margin-top: 15px;
            font-size: 14px;
        }
        
        .stat-item {
            text-align: center;
            flex: 1;
        }
        
        .stat-value {
            font-size: 18px;
            font-weight: bold;
        }
        
        .bookmark-list {
            margin-top: 20px;
            border: 1px solid #ddd;
            border-radius: 5px;
            overflow: hidden;
        }
        
        .bookmark-header {
            display: flex;
            background-color: #f8f9fa;
            padding: 10px 15px;
            font-weight: bold;
            border-bottom: 1px solid #ddd;
        }
        
        .bookmark-header div {
            flex: 1;
        }
        
        .bookmark-header .checkbox {
            flex: 0 0 40px;
        }
        
        .bookmark-header .status {
            flex: 0 0 100px;
        }
        
        .bookmark-header .actions {
            flex: 0 0 150px;
        }
        
        .bookmark-item {
            display: flex;
            padding: 10px 15px;
            border-bottom: 1px solid #eee;
            align-items: center;
        }
        
        .bookmark-item:last-child {
            border-bottom: none;
        }
        
        .bookmark-item:hover {
            background-color: #f8f9fa;
        }
        
        .bookmark-item div {
            flex: 1;
            word-break: break-all;
        }
        
        .bookmark-item .checkbox {
            flex: 0 0 40px;
        }
        
        .bookmark-item .status {
            flex: 0 0 100px;
        }
        
        .bookmark-item .actions {
            flex: 0 0 150px;
        }
        
        .status-badge {
            display: inline-block;
            padding: 3px 8px;
            border-radius: 12px;
            font-size: 12px;
            font-weight: bold;
        }
        
        .status-valid {
            background-color: #e6f4ea;
            color: var(--success-color);
        }
        
        .status-invalid {
            background-color: #fce8e6;
            color: var(--danger-color);
        }
        
        .status-timeout {
            background-color: #fef7e0;
            color: var(--warning-color);
        }
        
        .status-pending {
            background-color: #e8f0fe;
            color: var(--primary-color);
        }
        
        .action-btn {
            background: none;
            border: none;
            color: var(--primary-color);
            cursor: pointer;
            margin-right: 5px;
            font-size: 14px;
        }
        
        .action-btn:hover {
            text-decoration: underline;
        }
        
        .footer {
            text-align: center;
            margin-top: 30px;
            color: #777;
            font-size: 14px;
        }
        
        .drag-drop-area {
            border: 2px dashed #ccc;
            border-radius: 5px;
            padding: 30px;
            text-align: center;
            margin-bottom: 20px;
            cursor: pointer;
            transition: background-color 0.3s;
        }
        
        .drag-drop-area:hover {
            background-color: #f8f9fa;
        }
        
        .drag-drop-area.active {
            border-color: var(--primary-color);
            background-color: #e8f0fe;
        }
        
        .tab-container {
            margin-bottom: 20px;
        }
        
        .tab-buttons {
            display: flex;
            border-bottom: 1px solid #ddd;
        }
        
        .tab-button {
            padding: 10px 20px;
            background: none;
            border: none;
            cursor: pointer;
            font-size: 16px;
            color: #666;
            position: relative;
        }
        
        .tab-button.active {
            color: var(--primary-color);
            font-weight: bold;
        }
        
        .tab-button.active::after {
            content: '';
            position: absolute;
            bottom: -1px;
            left: 0;
            right: 0;
            height: 2px;
            background-color: var(--primary-color);
        }
        
        .tab-content {
            display: none;
            padding: 20px 0;
        }
        
        .tab-content.active {
            display: block;
        }
        
        .checkbox {
            text-align: center;
        }
        
        .select-all {
            margin-right: 10px;
        }
        
        @media (max-width: 768px) {
            .bookmark-header, .bookmark-item {
                flex-wrap: wrap;
            }
            
            .bookmark-header div, .bookmark-item div {
                flex: 0 0 100%;
                margin-bottom: 5px;
            }
            
            .bookmark-header .checkbox, .bookmark-item .checkbox {
                flex: 0 0 30px;
            }
            
            .bookmark-header .status, .bookmark-item .status {
                flex: 0 0 80px;
            }
            
            .bookmark-header .actions, .bookmark-item .actions {
                flex: 0 0 100%;
                text-align: right;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>书签链接检测工具</h1>
        </header>
        
        <div class="card">
            <h2 class="card-title">导入书签</h2>
            <div class="drag-drop-area" id="dragDropArea">
                <p>拖放浏览器导出的HTML书签文件到这里</p>
                <p>或</p>
                <input type="file" id="fileInput" accept=".html" style="display: none;">
                <button class="btn" id="selectFileBtn">选择文件</button>
            </div>
        </div>
        
        <div class="card">
            <h2 class="card-title">检测设置</h2>
            <div class="form-group">
                <label for="timeout">超时时间 (毫秒)</label>
                <input type="number" id="timeout" min="500" max="10000" value="3000">
            </div>
            <div class="form-group">
                <label for="retryCount">每个链接检测次数 (1-5次)</label>
                <input type="number" id="retryCount" min="1" max="5" value="3">
            </div>
            <div class="form-group">
                <label for="concurrency">并发请求数 (1-10个)</label>
                <input type="number" id="concurrency" min="1" max="10" value="5">
            </div>
            <button class="btn btn-success" id="startCheckBtn">开始检测</button>
            <button class="btn btn-danger" id="stopCheckBtn" disabled>停止检测</button>
            
            <div class="progress-container" id="progressContainer" style="display: none;">
                <div class="progress-bar" id="progressBar"></div>
            </div>
            
            <div class="stats" id="stats" style="display: none;">
                <div class="stat-item">
                    <div class="stat-label">总数</div>
                    <div class="stat-value" id="totalCount">0</div>
                </div>
                <div class="stat-item">
                    <div class="stat-label">已完成</div>
                    <div class="stat-value" id="completedCount">0</div>
                </div>
                <div class="stat-item">
                    <div class="stat-label">有效</div>
                    <div class="stat-value" id="validCount">0</div>
                </div>
                <div class="stat-item">
                    <div class="stat-label">无效</div>
                    <div class="stat-value" id="invalidCount">0</div>
                </div>
                <div class="stat-item">
                    <div class="stat-label">超时</div>
                    <div class="stat-value" id="timeoutCount">0</div>
                </div>
            </div>
        </div>
        
        <div class="card">
            <div class="tab-container">
                <div class="tab-buttons">
                    <button class="tab-button active" data-tab="all">全部书签</button>
                    <button class="tab-button" data-tab="valid">有效链接</button>
                    <button class="tab-button" data-tab="invalid">无效链接</button>
                    <button class="tab-button" data-tab="timeout">超时链接</button>
                </div>
                
                <div class="tab-content active" id="tab-all">
                    <div class="bookmark-actions">
                        <input type="checkbox" id="selectAll" class="select-all">
                        <label for="selectAll">全选</label>
                        <button class="btn btn-warning" id="deleteInvalidBtn">删除无效链接</button>
                        <button class="btn" id="exportSelectedBtn">导出选中</button>
                        <select id="exportFilter">
                            <option value="all">全部</option>
                            <option value="valid">仅有效</option>
                            <option value="invalid">仅无效</option>
                            <option value="timeout">仅超时</option>
                        </select>
                    </div>
                    
                    <div class="bookmark-list">
                        <div class="bookmark-header">
                            <div class="checkbox"></div>
                            <div>标题</div>
                            <div>URL</div>
                            <div class="status">状态</div>
                            <div class="actions">操作</div>
                        </div>
                        <div id="bookmarkList">
                            <!-- 书签列表将在这里动态生成 -->
                            <div class="empty-message">请先导入书签文件</div>
                        </div>
                    </div>
                </div>
                
                <div class="tab-content" id="tab-valid">
                    <div class="bookmark-list">
                        <div class="bookmark-header">
                            <div class="checkbox"></div>
                            <div>标题</div>
                            <div>URL</div>
                            <div class="status">状态</div>
                            <div class="actions">操作</div>
                        </div>
                        <div id="validBookmarkList">
                            <!-- 有效书签列表将在这里动态生成 -->
                        </div>
                    </div>
                </div>
                
                <div class="tab-content" id="tab-invalid">
                    <div class="bookmark-list">
                        <div class="bookmark-header">
                            <div class="checkbox"></div>
                            <div>标题</div>
                            <div>URL</div>
                            <div class="status">状态</div>
                            <div class="actions">操作</div>
                        </div>
                        <div id="invalidBookmarkList">
                            <!-- 无效书签列表将在这里动态生成 -->
                        </div>
                    </div>
                </div>
                
                <div class="tab-content" id="tab-timeout">
                    <div class="bookmark-list">
                        <div class="bookmark-header">
                            <div class="checkbox"></div>
                            <div>标题</div>
                            <div>URL</div>
                            <div class="status">状态</div>
                            <div class="actions">操作</div>
                        </div>
                        <div id="timeoutBookmarkList">
                            <!-- 超时书签列表将在这里动态生成 -->
                        </div>
                    </div>
                </div>
            </div>
        </div>
        
        <div class="footer">
            <p>书签链接检测工具 &copy; 2023 | 使用HTTP HEAD方法检测链接有效性</p>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // 全局变量
            let bookmarks = [];
            let isChecking = false;
            let stopRequested = false;
            let currentTab = 'all';
            
            // DOM元素
            const dragDropArea = document.getElementById('dragDropArea');
            const fileInput = document.getElementById('fileInput');
            const selectFileBtn = document.getElementById('selectFileBtn');
            const startCheckBtn = document.getElementById('startCheckBtn');
            const stopCheckBtn = document.getElementById('stopCheckBtn');
            const progressBar = document.getElementById('progressBar');
            const progressContainer = document.getElementById('progressContainer');
            const stats = document.getElementById('stats');
            const bookmarkList = document.getElementById('bookmarkList');
            const validBookmarkList = document.getElementById('validBookmarkList');
            const invalidBookmarkList = document.getElementById('invalidBookmarkList');
            const timeoutBookmarkList = document.getElementById('timeoutBookmarkList');
            const totalCountEl = document.getElementById('totalCount');
            const completedCountEl = document.getElementById('completedCount');
            const validCountEl = document.getElementById('validCount');
            const invalidCountEl = document.getElementById('invalidCount');
            const timeoutCountEl = document.getElementById('timeoutCount');
            const selectAllCheckbox = document.getElementById('selectAll');
            const deleteInvalidBtn = document.getElementById('deleteInvalidBtn');
            const exportSelectedBtn = document.getElementById('exportSelectedBtn');
            const exportFilter = document.getElementById('exportFilter');
            const tabButtons = document.querySelectorAll('.tab-button');
            const tabContents = document.querySelectorAll('.tab-content');
            
            // 事件监听器
            selectFileBtn.addEventListener('click', function() {
                fileInput.click();
            });
            
            fileInput.addEventListener('change', handleFileSelect);
            
            dragDropArea.addEventListener('dragover', function(e) {
                e.preventDefault();
                dragDropArea.classList.add('active');
            });
            
            dragDropArea.addEventListener('dragleave', function() {
                dragDropArea.classList.remove('active');
            });
            
            dragDropArea.addEventListener('drop', function(e) {
                e.preventDefault();
                dragDropArea.classList.remove('active');
                
                if (e.dataTransfer.files.length) {
                    fileInput.files = e.dataTransfer.files;
                    handleFileSelect({ target: fileInput });
                }
            });
            
            startCheckBtn.addEventListener('click', startChecking);
            stopCheckBtn.addEventListener('click', stopChecking);
            
            selectAllCheckbox.addEventListener('change', function() {
                const checkboxes = document.querySelectorAll('.bookmark-checkbox');
                checkboxes.forEach(checkbox => {
                    checkbox.checked = selectAllCheckbox.checked;
                });
            });
            
            deleteInvalidBtn.addEventListener('click', deleteInvalidBookmarks);
            exportSelectedBtn.addEventListener('click', exportSelectedBookmarks);
            
            tabButtons.forEach(button => {
                button.addEventListener('click', function() {
                    const tabId = this.getAttribute('data-tab');
                    switchTab(tabId);
                });
            });
            
            // 函数定义
            function handleFileSelect(event) {
                const file = event.target.files[0];
                if (!file) return;
                
                const reader = new FileReader();
                reader.onload = function(e) {
                    try {
                        const content = e.target.result;
                        bookmarks = parseBookmarks(content);
                        renderBookmarkList();
                        updateStats();
                        
                        // 重置状态
                        resetCheckingState();
                    } catch (error) {
                        alert('解析书签文件失败: ' + error.message);
                    }
                };
                reader.readAsText(file);
            }
            
            function parseBookmarks(html) {
                const parser = new DOMParser();
                const doc = parser.parseFromString(html, 'text/html');
                const links = doc.querySelectorAll('a');
                
                return Array.from(links).map(link => {
                    return {
                        title: link.textContent.trim(),
                        url: link.getAttribute('href'),
                        status: 'pending',
                        retries: 0,
                        validCount: 0,
                        invalidCount: 0,
                        timeoutCount: 0,
                        selected: false
                    };
                });
            }
            
            function renderBookmarkList() {
                if (bookmarks.length === 0) {
                    bookmarkList.innerHTML = '<div class="empty-message">请先导入书签文件</div>';
                    return;
                }
                
                bookmarkList.innerHTML = '';
                validBookmarkList.innerHTML = '';
                invalidBookmarkList.innerHTML = '';
                timeoutBookmarkList.innerHTML = '';
                
                bookmarks.forEach((bookmark, index) => {
                    const bookmarkItem = createBookmarkElement(bookmark, index);
                    bookmarkList.appendChild(bookmarkItem);
                    
                    // 根据状态添加到对应的列表
                    if (bookmark.status === 'valid') {
                        validBookmarkList.appendChild(createBookmarkElement(bookmark, index));
                    } else if (bookmark.status === 'invalid') {
                        invalidBookmarkList.appendChild(createBookmarkElement(bookmark, index));
                    } else if (bookmark.status === 'timeout') {
                        timeoutBookmarkList.appendChild(createBookmarkElement(bookmark, index));
                    }
                });
            }
            
            function createBookmarkElement(bookmark, index) {
                const item = document.createElement('div');
                item.className = 'bookmark-item';
                
                // 状态徽章
                let statusBadge = '';
                let statusText = '';
                let statusClass = '';
                
                if (bookmark.status === 'valid') {
                    statusText = '有效';
                    statusClass = 'status-valid';
                } else if (bookmark.status === 'invalid') {
                    statusText = '无效';
                    statusClass = 'status-invalid';
                } else if (bookmark.status === 'timeout') {
                    statusText = '超时';
                    statusClass = 'status-timeout';
                } else {
                    statusText = '待检测';
                    statusClass = 'status-pending';
                }
                
                // 检测统计
                let statsText = '';
                if (bookmark.retries > 0) {
                    statsText = `${bookmark.validCount}√/${bookmark.invalidCount}×/${bookmark.timeoutCount}&#8987;`;
                }
                
                item.innerHTML = `
                    <div class="checkbox">
                        <input type="checkbox" class="bookmark-checkbox" data-index="${index}" ${bookmark.selected ? 'checked' : ''}>
                    </div>
                    <div>${bookmark.title || '无标题'}</div>
                    <div>${bookmark.url}</div>
                    <div class="status">
                        <span class="status-badge ${statusClass}">${statusText}</span>
                        ${statsText ? `<br><small>${statsText}</small>` : ''}
                    </div>
                    <div class="actions">
                        <button class="action-btn test-btn" data-index="${index}">测试</button>
                        <button class="action-btn visit-btn" data-index="${index}">访问</button>
                    </div>
                `;
                
                // 添加事件监听器
                const checkbox = item.querySelector('.bookmark-checkbox');
                checkbox.addEventListener('change', function() {
                    bookmarks[index].selected = this.checked;
                    updateSelectAllState();
                });
                
                const testBtn = item.querySelector('.test-btn');
                testBtn.addEventListener('click', function() {
                    testSingleBookmark(index);
                });
                
                const visitBtn = item.querySelector('.visit-btn');
                visitBtn.addEventListener('click', function() {
                    window.open(bookmark.url, '_blank');
                });
                
                return item;
            }
            
            function startChecking() {
                if (bookmarks.length === 0) {
                    alert('请先导入书签文件');
                    return;
                }
                
                if (isChecking) {
                    alert('检测正在进行中');
                    return;
                }
                
                isChecking = true;
                stopRequested = false;
                startCheckBtn.disabled = true;
                stopCheckBtn.disabled = false;
                
                const timeout = parseInt(document.getElementById('timeout').value) || 3000;
                const retryCount = parseInt(document.getElementById('retryCount').value) || 3;
                const concurrency = parseInt(document.getElementById('concurrency').value) || 5;
                
                progressContainer.style.display = 'block';
                stats.style.display = 'flex';
                
                // 重置所有书签状态
                bookmarks.forEach(bookmark => {
                    bookmark.status = 'pending';
                    bookmark.retries = 0;
                    bookmark.validCount = 0;
                    bookmark.invalidCount = 0;
                    bookmark.timeoutCount = 0;
                });
                
                renderBookmarkList();
                updateStats();
                
                // 使用并发控制检测书签
                const total = bookmarks.length;
                let currentIndex = 0;
                let activeRequests = 0;
                
                function processNextBatch() {
                    if (stopRequested || currentIndex >= total) {
                        if (activeRequests === 0) {
                            finishChecking();
                        }
                        return;
                    }
                    
                    const batchSize = Math.min(concurrency - activeRequests, total - currentIndex);
                    
                    for (let i = 0; i < batchSize; i++) {
                        if (currentIndex < total) {
                            activeRequests++;
                            checkBookmark(currentIndex, timeout, retryCount)
                                .finally(() => {
                                    activeRequests--;
                                    processNextBatch();
                                });
                            currentIndex++;
                        }
                    }
                }
                
                processNextBatch();
            }
            
            async function checkBookmark(index, timeout, maxRetries) {
                if (stopRequested) return;
                
                const bookmark = bookmarks[index];
                bookmark.retries = 0;
                bookmark.validCount = 0;
                bookmark.invalidCount = 0;
                bookmark.timeoutCount = 0;
                
                for (let i = 0; i < maxRetries; i++) {
                    if (stopRequested) break;
                    
                    bookmark.retries++;
                    try {
                        const isValid = await checkUrl(bookmark.url, timeout);
                        if (isValid) {
                            bookmark.validCount++;
                        } else {
                            bookmark.invalidCount++;
                        }
                    } catch (error) {
                        if (error.message === 'Timeout') {
                            bookmark.timeoutCount++;
                        } else {
                            bookmark.invalidCount++;
                        }
                    }
                    
                    // 更新UI
                    updateBookmarkStatus(index);
                    updateStats();
                }
                
                // 根据多数结果确定最终状态
                const counts = [
                    { status: 'valid', count: bookmark.validCount },
                    { status: 'invalid', count: bookmark.invalidCount },
                    { status: 'timeout', count: bookmark.timeoutCount }
                ];
                
                counts.sort((a, b) => b.count - a.count);
                bookmark.status = counts[0].count > 0 ? counts[0].status : 'invalid';
                
                updateBookmarkStatus(index);
                updateStats();
            }
            
            function checkUrl(url, timeout) {
                return new Promise((resolve, reject) => {
                    if (!url || !url.startsWith('http')) {
                        resolve(false);
                        return;
                    }
                    
                    const controller = new AbortController();
                    const timeoutId = setTimeout(() => {
                        controller.abort();
                        reject(new Error('Timeout'));
                    }, timeout);
                    
                    fetch(url, {
                        method: 'HEAD',
                        mode: 'no-cors',
                        signal: controller.signal
                    })
                    .then(response => {
                        clearTimeout(timeoutId);
                        // 在no-cors模式下,我们无法读取响应状态,所以假设它是有效的
                        resolve(true);
                    })
                    .catch(error => {
                        clearTimeout(timeoutId);
                        if (error.name === 'AbortError') {
                            reject(new Error('Timeout'));
                        } else {
                            // 其他错误视为无效
                            resolve(false);
                        }
                    });
                });
            }
            
            function stopChecking() {
                stopRequested = true;
                stopCheckBtn.disabled = true;
            }
            
            function finishChecking() {
                isChecking = false;
                startCheckBtn.disabled = false;
                stopCheckBtn.disabled = true;
                
                if (!stopRequested) {
                    progressBar.style.width = '100%';
                }
                
                // 重新渲染列表以更新分类
                renderBookmarkList();
            }
            
            function updateBookmarkStatus(index) {
                const bookmark = bookmarks[index];
                const items = document.querySelectorAll(`.bookmark-checkbox[data-index="${index}"]`);
                
                items.forEach(item => {
                    const itemContainer = item.closest('.bookmark-item');
                    if (itemContainer) {
                        const statusBadge = itemContainer.querySelector('.status-badge');
                        const statusText = itemContainer.querySelector('.status small');
                        
                        if (statusBadge) {
                            // 更新状态徽章
                            let statusClass = '';
                            let text = '';
                            
                            if (bookmark.status === 'valid') {
                                text = '有效';
                                statusClass = 'status-valid';
                            } else if (bookmark.status === 'invalid') {
                                text = '无效';
                                statusClass = 'status-invalid';
                            } else if (bookmark.status === 'timeout') {
                                text = '超时';
                                statusClass = 'status-timeout';
                            } else {
                                text = '检测中';
                                statusClass = 'status-pending';
                            }
                            
                            statusBadge.className = `status-badge ${statusClass}`;
                            statusBadge.textContent = text;
                            
                            // 更新统计信息
                            if (statusText) {
                                statusText.textContent = `${bookmark.validCount}√/${bookmark.invalidCount}×/${bookmark.timeoutCount}&#8987;`;
                            }
                        }
                    }
                });
            }
            
            function updateStats() {
                const total = bookmarks.length;
                const completed = bookmarks.filter(b => b.retries > 0).length;
                const valid = bookmarks.filter(b => b.status === 'valid').length;
                const invalid = bookmarks.filter(b => b.status === 'invalid').length;
                const timeout = bookmarks.filter(b => b.status === 'timeout').length;
                
                totalCountEl.textContent = total;
                completedCountEl.textContent = completed;
                validCountEl.textContent = valid;
                invalidCountEl.textContent = invalid;
                timeoutCountEl.textContent = timeout;
                
                // 更新进度条
                if (total > 0) {
                    const progress = (completed / total) * 100;
                    progressBar.style.width = `${progress}%`;
                }
            }
            
            function resetCheckingState() {
                isChecking = false;
                stopRequested = false;
                startCheckBtn.disabled = false;
                stopCheckBtn.disabled = true;
                progressBar.style.width = '0%';
                progressContainer.style.display = 'none';
                stats.style.display = 'none';
            }
            
            function testSingleBookmark(index) {
                const timeout = parseInt(document.getElementById('timeout').value) || 3000;
                const retryCount = parseInt(document.getElementById('retryCount').value) || 3;
                
                bookmarks[index].status = 'pending';
                bookmarks[index].retries = 0;
                bookmarks[index].validCount = 0;
                bookmarks[index].invalidCount = 0;
                bookmarks[index].timeoutCount = 0;
                
                updateBookmarkStatus(index);
                
                checkBookmark(index, timeout, retryCount);
            }
            
            function deleteInvalidBookmarks() {
                if (confirm('确定要删除所有无效链接吗?')) {
                    bookmarks = bookmarks.filter(bookmark => bookmark.status !== 'invalid');
                    renderBookmarkList();
                    updateStats();
                }
            }
            
            function exportSelectedBookmarks() {
                const filter = exportFilter.value;
                const selectedBookmarks = bookmarks.filter(bookmark => {
                    if (!bookmark.selected) return false;
                    
                    if (filter === 'valid') return bookmark.status === 'valid';
                    if (filter === 'invalid') return bookmark.status === 'invalid';
                    if (filter === 'timeout') return bookmark.status === 'timeout';
                    return true;
                });
                
                if (selectedBookmarks.length === 0) {
                    alert('没有选中的书签');
                    return;
                }
                
                const html = generateBookmarkHTML(selectedBookmarks);
                downloadHTML(html, 'bookmarks_export.html');
            }
            
            function generateBookmarkHTML(bookmarksToExport) {
                let html = `<!DOCTYPE NETSCAPE-Bookmark-file-1>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks</H1>
<DL><p>
`;
                
                bookmarksToExport.forEach(bookmark => {
                    html += `    <DT><A HREF="${bookmark.url}" ADD_DATE="${Math.floor(Date.now()/1000)}">${bookmark.title || bookmark.url}</A>\n`;
                });
                
                html += `</DL><p>`;
                return html;
            }
            
            function downloadHTML(content, filename) {
                const blob = new Blob([content], { type: 'text/html' });
                const url = URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = url;
                a.download = filename;
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
                URL.revokeObjectURL(url);
            }
            
            function updateSelectAllState() {
                const selectedCount = bookmarks.filter(b => b.selected).length;
                selectAllCheckbox.checked = selectedCount > 0 && selectedCount === bookmarks.length;
                selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < bookmarks.length;
            }
            
            function switchTab(tabId) {
                // 更新按钮状态
                tabButtons.forEach(button => {
                    button.classList.remove('active');
                    if (button.getAttribute('data-tab') === tabId) {
                        button.classList.add('active');
                    }
                });
                
                // 更新内容区域
                tabContents.forEach(content => {
                    content.classList.remove('active');
                    if (content.id === `tab-${tabId}`) {
                        content.classList.add('active');
                    }
                });
                
                currentTab = tabId;
            }
        });
    </script>
</body>
</html>

免费评分

参与人数 7吾爱币 +11 热心值 +5 收起 理由
苏紫方璇 + 5 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
hwh425 + 1 谢谢@Thanks!
acail + 1 + 1 谢谢@Thanks!
grrr_zhao + 1 + 1 谢谢@Thanks!
jj131028 + 1 谢谢@Thanks!
42328669425 + 1 + 1 我很赞同!
sakura1204 + 1 + 1 谢谢@Thanks!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| lcg888 发表于 2025-4-15 18:08
fiyta 发表于 2025-4-15 18:01
这个很有实用性的,如果可以直接从收藏夹检测并操作删掉,就更加方便了

我记得论坛有个脚本可以  我忘记是哪个帖子了  但是感觉有时候不准确所以就让AI写了一个测试了一下比脚本强那么一丢丢吧  
 楼主| lcg888 发表于 2025-4-23 16:29
heisajoker 发表于 2025-4-23 16:15
一堆问题,根本不能用

我这东西不是为你这种人准备的,你大可不必用。别人都可以就你不行?这是东西的问题还是你的问题已经显而易见。不能用就别用呗,免费分享又不是强迫你用
xxkz 发表于 2025-4-15 17:44
本帖最后由 xxkz 于 2025-4-15 17:49 编辑

另存为的时候选择UTF-8编码,不然会成乱码
好像有点小bug,删除无效链接后,有效链接导出不了
MinaCN 发表于 2025-4-15 17:47
这是啥好东西哦啊
 楼主| lcg888 发表于 2025-4-15 17:48
xxkz 发表于 2025-4-15 17:44
另存为的时候选择UTF-8编码,不然会成乱码

我没测试直接本地看的
 楼主| lcg888 发表于 2025-4-15 17:49
MinaCN 发表于 2025-4-15 17:47
这是啥好东西哦啊

检测你收藏的宝藏网址有效性的
fiyta 发表于 2025-4-15 18:01
这个很有实用性的,如果可以直接从收藏夹检测并操作删掉,就更加方便了
afti 发表于 2025-4-15 18:21
这样排除无效链接就方便了
 楼主| lcg888 发表于 2025-4-15 18:22
afti 发表于 2025-4-15 18:21
这样排除无效链接就方便了

AI方便了我们,需要啥让它写 累不死它的 哈哈哈哈
wslh 发表于 2025-4-15 19:23
感谢分享,很实用的一个小工具。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-4-25 03:34

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表