Greasy Fork

huggingface与hf-mirror相互跳转

在 Hugging Face 和 hf-mirror.com 的 blob 和 tree 页面添加相互跳转链接,并优化下载,包括 tree 页面文件行镜像下载按钮

目前为 2025-03-22 提交的版本。查看 最新版本

// ==UserScript==
// @name         huggingface与hf-mirror相互跳转
// @namespace    http://tampermonkey.net/
// @version      1.9
// @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
// ==/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; // 仅在 Hugging Face 的 tree 页面执行
        var fileLinks = document.querySelectorAll('a.group.col-span-4.flex.items-center.justify-self-end.truncate.text-right.font-mono.text-\\[0\\.8rem\\].leading-6.text-gray-400.md\\:col-span-3.lg\\:col-span-2.xl\\:pr-10[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];

                // 为原始下载按钮添加独立 hover 样式
                var originalDownloadBtn = link.querySelector('div.group-hover\\:shadow-xs.ml-2.flex.h-5.w-5');
                if (originalDownloadBtn) {
                    originalDownloadBtn.className = 'ml-2 flex h-5 w-5 items-center justify-center rounded-sm border text-gray-500 hover:bg-gray-50 hover:text-gray-800 dark:border-gray-800 dark:hover:bg-gray-800 dark:hover:text-gray-300 xl:ml-4';
                }

                // 添加绿色 hf-mirror 下载按钮
                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 = '<a href="' + mirrorHref + '" target="_blank" rel="noopener noreferrer" title="Download from hf-mirror"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 32 32"><path fill="currentColor" d="M26 24v4H6v-4H4v4a2 2 0 0 0 2 2h20a2 2 0 0 0 2-2v-4zm0-10l-1.41-1.41L17 20.17V2h-2v18.17l-7.59-7.58L6 14l10 10l10-10z"></path></svg></a>';
                link.appendChild(mirrorDownloadBtn);
                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();
    });
})();