您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
在HuggingFace页面提取下载链接,同时显示原始链接和hf-mirror.com镜像链接。v1.1: 清理文件名、识别主要文件、优化布局
当前为
// ==UserScript== // @name HuggingFace镜像链接提取器 // @namespace http://tampermonkey.net/ // @version 1.1 // @description 在HuggingFace页面提取下载链接,同时显示原始链接和hf-mirror.com镜像链接。v1.1: 清理文件名、识别主要文件、优化布局 // @author AI Assistant // @match https://huggingface.co/* // @match https://hf-mirror.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=huggingface.co // @grant none // ==/UserScript== (function() { 'use strict'; // 创建样式 const style = document.createElement('style'); style.textContent = ` .hf-extractor-btn { position: fixed; top: 20px; left: 20px; width: 60px; height: 60px; background: linear-gradient(45deg, #ff6b6b, #feca57); color: white; border: none; border-radius: 50%; cursor: pointer; font-size: 12px; font-weight: bold; z-index: 10000; box-shadow: 0 4px 15px rgba(0,0,0,0.3); transition: all 0.3s ease; } .hf-extractor-btn:hover { transform: scale(1.1); box-shadow: 0 6px 20px rgba(0,0,0,0.4); } .hf-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 9999; display: none; justify-content: center; align-items: center; } .hf-modal-content { background: white; border-radius: 15px; padding: 25px; max-width: 90vw; max-height: 90vh; overflow: auto; box-shadow: 0 10px 30px rgba(0,0,0,0.3); min-width: 700px; } .hf-header { text-align: center; margin-bottom: 20px; color: #333; border-bottom: 2px solid #eee; padding-bottom: 15px; } .hf-stats { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 15px; border-radius: 10px; text-align: center; margin-bottom: 20px; font-weight: bold; } .hf-buttons { display: flex; gap: 10px; margin-bottom: 20px; flex-wrap: wrap; } .hf-btn { padding: 10px 15px; border: none; border-radius: 8px; cursor: pointer; font-weight: bold; transition: all 0.3s ease; flex: 1; min-width: 120px; } .hf-btn:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.2); } .hf-btn-close { background: #e74c3c; color: white; } .hf-btn-copy-first { background: #3498db; color: white; } .hf-btn-copy-all-orig { background: #27ae60; color: white; } .hf-btn-copy-all-mirror { background: #f39c12; color: white; } .hf-link-item { background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 10px; padding: 12px; margin-bottom: 12px; transition: all 0.3s ease; } .hf-link-item.main-file { border: 2px solid #ff6b6b; background: linear-gradient(135deg, #fff5f5 0%, #ffe8e8 100%); } .hf-link-item:hover { box-shadow: 0 4px 15px rgba(0,0,0,0.1); transform: translateY(-2px); } .hf-file-name { font-weight: bold; color: #2c3e50; margin-bottom: 6px; font-size: 16px; display: flex; align-items: center; gap: 8px; } .hf-main-file { background: linear-gradient(45deg, #ff6b6b, #feca57); color: white; padding: 2px 8px; border-radius: 12px; font-size: 11px; font-weight: bold; } .hf-link-row { display: flex; align-items: center; margin-bottom: 4px; padding: 6px; background: white; border-radius: 5px; } .hf-link-label { font-weight: bold; min-width: 60px; margin-right: 8px; } .hf-link-url { flex: 1; font-family: monospace; font-size: 12px; word-break: break-all; margin-right: 8px; } .hf-copy-btn { padding: 5px 10px; border: none; border-radius: 5px; cursor: pointer; font-size: 11px; font-weight: bold; transition: all 0.2s ease; } .hf-copy-orig { background: #3498db; color: white; } .hf-copy-mirror { background: #f39c12; color: white; } .hf-copy-btn:hover { opacity: 0.8; } .hf-more-info { text-align: center; padding: 15px; color: #7f8c8d; font-style: italic; background: #ecf0f1; border-radius: 8px; } `; document.head.appendChild(style); // 创建提取按钮 const extractBtn = document.createElement('button'); extractBtn.className = 'hf-extractor-btn'; extractBtn.innerHTML = '🔗<br>提取'; document.body.appendChild(extractBtn); // 创建模态框 const modal = document.createElement('div'); modal.className = 'hf-modal'; modal.innerHTML = ` <div class="hf-modal-content"> <div class="hf-header"> <h2>🚀 HuggingFace 下载链接提取器</h2> <p>同时提供原始链接和镜像链接</p> </div> <div class="hf-stats" id="hf-stats"></div> <div class="hf-buttons"> <button class="hf-btn hf-btn-close" id="hf-close">❌ 关闭</button> <button class="hf-btn hf-btn-copy-first" id="hf-copy-first">📋 复制第一个</button> <button class="hf-btn hf-btn-copy-all-orig" id="hf-copy-all-orig">📄 复制全部原始</button> <button class="hf-btn hf-btn-copy-all-mirror" id="hf-copy-all-mirror">🚀 复制全部镜像</button> </div> <div id="hf-links-container"></div> </div> `; document.body.appendChild(modal); // 清理文件名,移除查询参数 function cleanFileName(fileName) { return fileName.replace(/\?.*$/, ''); } // 判断是否为主要文件 function isMainFile(fileName) { const mainFilePatterns = [ /^README\.md$/i, /^config\.json$/i, /^model\.safetensors$/i, /^pytorch_model\.bin$/i, /^model\.onnx$/i, /^tokenizer\.json$/i, /^tokenizer_config\.json$/i, /^vocab\.txt$/i, /^merges\.txt$/i, /\.py$/i, /^requirements\.txt$/i, /^setup\.py$/i, /^__init__\.py$/i ]; return mainFilePatterns.some(pattern => pattern.test(fileName)); } // 提取链接函数 function extractLinks() { const links = []; const elements = document.querySelectorAll('a[download][href]'); elements.forEach(element => { const href = element.getAttribute('href'); if (href) { const originalLink = href.startsWith('http') ? href : 'https://huggingface.co' + href; const mirrorLink = originalLink.replace('huggingface.co', 'hf-mirror.com'); const rawFileName = href.split('/').pop() || 'unknown'; const fileName = cleanFileName(rawFileName); const isMain = isMainFile(fileName); links.push({ original: originalLink, mirror: mirrorLink, fileName: fileName, isMainFile: isMain }); } }); // 将主要文件排在前面 return links.sort((a, b) => { if (a.isMainFile && !b.isMainFile) return -1; if (!a.isMainFile && b.isMainFile) return 1; return a.fileName.localeCompare(b.fileName); }); } // 复制到剪贴板 function copyToClipboard(text, button) { navigator.clipboard.writeText(text).then(() => { const originalText = button.textContent; button.textContent = '✅ 已复制!'; button.style.background = '#27ae60'; setTimeout(() => { button.textContent = originalText; button.style.background = ''; }, 1500); }).catch(err => { console.error('复制失败:', err); alert('复制失败,请手动复制'); }); } // 显示链接 function displayLinks(links) { const container = document.getElementById('hf-links-container'); const stats = document.getElementById('hf-stats'); const mainFileCount = links.filter(link => link.isMainFile).length; const statsText = mainFileCount > 0 ? `📊 共找到 <strong>${links.length}</strong> 个下载链接,其中 <strong>${mainFileCount}</strong> 个主要文件` : `📊 共找到 <strong>${links.length}</strong> 个下载链接`; stats.innerHTML = statsText; if (links.length === 0) { container.innerHTML = '<div class="hf-more-info">❌ 未找到任何下载链接</div>'; return; } const displayCount = Math.min(links.length, 5); let html = ''; for (let i = 0; i < displayCount; i++) { const link = links[i]; const mainFileClass = link.isMainFile ? ' main-file' : ''; const fileIcon = link.isMainFile ? '⭐' : '📁'; const mainFileTag = link.isMainFile ? '<span class="hf-main-file">主要文件</span>' : ''; html += ` <div class="hf-link-item${mainFileClass}"> <div class="hf-file-name"> ${fileIcon} 文件 ${i + 1}: ${link.fileName} ${mainFileTag} </div> <div class="hf-link-row"> <span class="hf-link-label" style="color: #3498db;">🔗 原始:</span> <span class="hf-link-url">${link.original}</span> <button class="hf-copy-btn hf-copy-orig" onclick="copyToClipboard('${link.original}', this)">复制</button> </div> <div class="hf-link-row"> <span class="hf-link-label" style="color: #f39c12;">🚀 镜像:</span> <span class="hf-link-url">${link.mirror}</span> <button class="hf-copy-btn hf-copy-mirror" onclick="copyToClipboard('${link.mirror}', this)">复制</button> </div> </div> `; } if (links.length > 5) { html += `<div class="hf-more-info">📝 还有 ${links.length - 5} 个链接未显示,请使用上方按钮复制全部链接</div>`; } container.innerHTML = html; } // 事件监听 extractBtn.addEventListener('click', () => { const links = extractLinks(); displayLinks(links); modal.style.display = 'flex'; // 更新按钮事件 document.getElementById('hf-close').onclick = () => { modal.style.display = 'none'; }; document.getElementById('hf-copy-first').onclick = () => { if (links.length > 0) { copyToClipboard(links[0].original, document.getElementById('hf-copy-first')); } }; document.getElementById('hf-copy-all-orig').onclick = () => { const allOriginal = links.map(link => link.original).join('\n'); copyToClipboard(allOriginal, document.getElementById('hf-copy-all-orig')); }; document.getElementById('hf-copy-all-mirror').onclick = () => { const allMirror = links.map(link => link.mirror).join('\n'); copyToClipboard(allMirror, document.getElementById('hf-copy-all-mirror')); }; }); // 点击模态框外部关闭 modal.addEventListener('click', (e) => { if (e.target === modal) { modal.style.display = 'none'; } }); // 全局函数供内联事件使用 window.copyToClipboard = copyToClipboard; console.log('🚀 HuggingFace镜像链接提取器已加载'); })();