// ==UserScript== // @name huggingface与hf-mirror相互跳转 // @namespace http://tampermonkey.net/ // @version 2.0 // @description 在 Hugging Face 和 hf-mirror.com 的 blob 和 tree 页面添加相互跳转链接,并优化下载,包括 tree 页面文件行镜像下载按钮 // @author flyway // @match https://huggingface.co/*/blob/* // @match https://hf-mirror.com/*/blob/* // @match https://huggingface.co/* // @match https://hf-mirror.com/* // @grant none // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/530508/huggingface%E4%B8%8Ehf-mirror%E7%9B%B8%E4%BA%92%E8%B7%B3%E8%BD%AC.user.js // @updateURL https://update.greasyfork.icu/scripts/530508/huggingface%E4%B8%8Ehf-mirror%E7%9B%B8%E4%BA%92%E8%B7%B3%E8%BD%AC.meta.js // ==/UserScript== (function() { 'use strict'; var currentUrl = window.location.href; var isMirrorPage = currentUrl.includes('hf-mirror.com'); var isBlobPage = currentUrl.includes('/blob/'); var isTreePage = currentUrl.includes('/tree/main'); // 为 blob 页面添加下载链接(仅 Hugging Face 添加,镜像站无内容) function addBlobLinks() { var messageDiv = document.querySelector('div.p-4.py-8.text-center'); if (messageDiv && !messageDiv.querySelector('.custom-links')) { var newP = document.createElement('p'); newP.className = 'custom-links'; newP.style.marginTop = '20px'; if (isMirrorPage) { // 镜像站的 blob 页面不添加任何内容 return; } else { var downloadLink = document.querySelector('a[href*="/resolve/"]'); if (downloadLink) { var originalDownloadUrl = downloadLink.href; var urlObj = new URL(originalDownloadUrl); var mirrorDownloadUrl = urlObj.origin.replace('huggingface.co', 'hf-mirror.com') + urlObj.pathname; var downloadLinkMirror = document.createElement('a'); downloadLinkMirror.href = mirrorDownloadUrl; downloadLinkMirror.textContent = '使用 hf-mirror 下载'; downloadLinkMirror.style.color = 'green'; downloadLinkMirror.style.textDecoration = 'underline'; // 添加下划线 downloadLinkMirror.style.marginRight = '10px'; downloadLinkMirror.target = '_blank'; downloadLinkMirror.rel = 'noopener noreferrer'; newP.appendChild(downloadLinkMirror); } } messageDiv.appendChild(newP); } } // 为 tree 和 blob 页面添加 tab-alternate 样式的跳转链接 function addTabLink() { var tabContainer = document.querySelector('div.-mb-px.flex.h-12.items-center.overflow-x-auto.overflow-y-hidden'); if (tabContainer && !tabContainer.querySelector('.custom-tab')) { var jumpLink = document.createElement('a'); jumpLink.className = 'tab-alternate custom-tab'; if (isMirrorPage) { jumpLink.href = currentUrl.replace('hf-mirror.com', 'huggingface.co'); jumpLink.textContent = '切换到 huggingface 页面'; jumpLink.style.color = 'orange'; } else { jumpLink.href = currentUrl.replace('huggingface.co', 'hf-mirror.com'); jumpLink.textContent = '切换到 hf-mirror 页面'; jumpLink.style.color = 'green'; } jumpLink.style.marginLeft = '10px'; tabContainer.appendChild(jumpLink); } } // 为 tree 页面文件行添加绿色 hf-mirror 下载按钮 function addTreeDownloadButtons() { // 检查页面是否符合条件,如果不是则退出 if (!isTreePage || isMirrorPage) return; // 更新 CSS 选择器以匹配新的 HTML 结构 // 新的 a 标签 class 为 "group col-span-9 flex items-center ..." // 并且链接必须包含 "/resolve/" var fileLinks = document.querySelectorAll('a.group.col-span-9.flex.items-center[href*="/resolve/"]'); fileLinks.forEach(function(link) { // 确保不会重复添加按钮 if (!link.querySelector('.custom-mirror-download')) { console.log('Found file link:', link.href); var originalHref = link.href; var mirrorHref = originalHref.replace('huggingface.co', 'hf-mirror.com').split('?')[0]; // 原始下载按钮现在是 a 标签的直接子元素 // 它的 class 现在是 "group-hover:shadow-xs ml-2 flex h-5 w-5 ..." // 无需修改其 class,因为新结构中已经包含 group-hover 样式 var originalDownloadBtn = link.querySelector('div.group-hover\\:shadow-xs.ml-2.flex.h-5.w-5'); // 如果找到原始下载按钮,则添加镜像下载按钮 if (originalDownloadBtn) { // 创建新的 hf-mirror 下载按钮 div var mirrorDownloadBtn = document.createElement('div'); mirrorDownloadBtn.className = 'ml-2 flex h-5 w-5 items-center justify-center rounded-sm border text-green-500 hover:bg-gray-50 hover:text-green-800 dark:border-gray-800 dark:hover:bg-gray-800 dark:hover:text-green-300 xl:ml-4 custom-mirror-download'; mirrorDownloadBtn.innerHTML = ''; // 将新按钮插入到原始下载按钮的后面 link.insertBefore(mirrorDownloadBtn, originalDownloadBtn.nextSibling); console.log('Added mirror download button to:', link.href); } } }); } // 初始执行 if (isBlobPage) addBlobLinks(); addTabLink(); addTreeDownloadButtons(); // 使用 MutationObserver 监听 DOM 变化 var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (isBlobPage) addBlobLinks(); addTabLink(); addTreeDownloadButtons(); }); }); observer.observe(document.body, { childList: true, subtree: true }); window.addEventListener('load', function() { if (isBlobPage) addBlobLinks(); addTabLink(); addTreeDownloadButtons(); }); })();