Greasy Fork

Greasy Fork is available in English.

HF 镜像跳转

在 Hugging Face 仓库页面添加镜像跳转按钮

当前为 2024-12-01 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name            HF 镜像跳转
// @name:en         HF Mirror Redirect
// @namespace       https://github.com/zhzLuke96/hf-links
// @version         v1.3
// @description     在 Hugging Face 仓库页面添加镜像跳转按钮
// @description:en  Add mirror redirect buttons on Hugging Face repository pages.
// @author          zhzluke96
// @match           https://huggingface.co/*
// @icon            https://www.google.com/s2/favicons?sz=64&domain=huggingface.co
// @grant           none
// @license         MIT
// @supportURL      https://github.com/zhzLuke96/hf-links/issues
// ==/UserScript==

(function () {
  "use strict";

  const exit = (message, code = 0) =>
    console.log(`[hf-links|${code}] ${message}`);

  /**
   * @type {typeof document.querySelector}
   */
  const $ = document.querySelector.bind(document);

  const $header = $(
    "body > div > main > div.SVELTE_HYDRATER.contents > header > div > h1"
  );
  if (!$header) {
    exit("没找到header");
    return;
  }

  const frag = document.createDocumentFragment();
  const div = document.createElement("div");
  frag.appendChild(div);

  const build = (html) => {
    div.innerHTML = html;
    const elem = div.firstElementChild;
    div.innerHTML = "";
    return elem;
  };

  const Button = ({ text, icon, onClick }) => {
    const elem = build(`
  <div
    class="inline-flex items-center overflow-hidden whitespace-nowrap rounded-md border bg-white text-sm leading-none text-gray-500 mr-2"
  >
    <button
      class="relative flex items-center overflow-hidden from-red-50 to-transparent dark:from-red-900 px-1.5 py-1 hover:bg-gradient-to-t focus:outline-none"
      title="${text}"
    >
      <small
        class="left-1.5 absolute"
      >${icon}</small>
      <span class="ml-4 pl-0.5">${text}</span>
    </button>
  </div>
  
      `);
    onClick && elem.addEventListener("click", onClick);
    return elem;
  };

  const parse_hf_repo = () => {
    {
      // 有可能是 datasets
      const match = location.pathname.match(/^\/datasets\/([^/]+)\/([^/]+)/);
      if (match) {
        return {
          kind: "datasets",
          repo_owner: match[1],
          repo_name: match[2],
          pathname: `/datasets/${match[1]}/${match[2]}`,
        };
      }
    }

    // 从 URL 中解析仓库名和所有者
    const match = location.pathname.match(/^\/([^/]+)\/([^/]+)/);
    if (match) {
      return {
        kind: "models",
        repo_owner: match[1],
        repo_name: match[2],
        pathname: `/${match[1]}/${match[2]}`,
      };
    }
    return {};
  };

  const { pathname, repo_name } = parse_hf_repo();

  if (!pathname || !repo_name) {
    exit("解析repo名字失败");
    return;
  }

  const buttons = [
    {
      href: `https://hf-mirror.com${pathname}`,
      label: "hf-mirror",
      icon: "🤗",
    },
    {
      // href: `https://modelscope.cn/search?search=${encodeURIComponent(
      //   `${repo_owner}/${repo_name}`
      // )}`,

      // NOTE: 只搜索 repo name 因为一般都是搬运, owner 不一样
      href: `https://modelscope.cn/search?search=${encodeURIComponent(
        `${repo_name}`
      )}`,
      label: "model-scope",
      icon: "👾",
    },
  ];

  for (const button of buttons) {
    const node = Button({
      link: button.href,
      text: button.label,
      icon: button.icon,
      onClick: () => {
        const nw = window.open(button.href, "_blank", "noopener,noreferrer");
        if (nw) nw.opener = null;
      },
    });
    $header.appendChild(node);

    // NOTE: header 不知道为啥 rerender... 所以要检测一下,最多检测 10 次
    // NOTE: 用 MutationObserver 可能好点,但是卡死了... 所以用 interval

    let check_times = 0;
    const timer = setInterval(() => {
      if (check_times >= 10) {
        clearInterval(timer);
        return;
      }
      check_times++;
      if ($header.innerHTML.includes(button.href)) {
        return;
      }
      $header.appendChild(node);
    }, 500);
  }

  exit("按钮添加成功");
})();