GitHub 访问代理助手

GitHub 访问加速助手。支持 GitHub 的 clone、release/raw/zip 下载加速

当前为 2023-03-14 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name          GitHub 访问代理助手
// @namespace     lzwme.github.fast
// @author        lzwme
// @homepageURL   https://github.com/lzwme/vm-gh-proxy-cn
// @supportURL    https://github.com/lzwme/vm-gh-proxy-cn/issues
// @license       MIT License
// @description   GitHub 访问加速助手。支持 GitHub 的 clone、release/raw/zip 下载加速
// @include       *://github.com/*
// @include       *://github*
// @include       *://hub.fastgit.xyz/*
// @require       https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.slim.min.js
// @icon          https://github.githubassets.com/favicon.ico
// @version       1.0.3
// ==/UserScript==

(function () {
  const clonePrefix = 'git clone --depth 1 ';
  const Mirrors = {
    ghproxy: {
      url: 'https://ghproxy.com/github.com',
      name: 'ghproxy',
      desc: 'ghproxy 代理',
      types: ['clone', 'download', 'raw'],
      format: url => `${Mirrors.ghproxy.url}/${url.replace(/^\//, '')}`,
    },
    ghproxy1: {
      url: 'https://gh.api.99988866.xyz/github.com',
      name: 'ghproxy-xyz',
      desc: 'ghproxy 代理(演示站)',
      types: ['clone', 'download', 'raw'],
      format: url => `${Mirrors.ghproxy1.url}/${url.replace(/^\//, '')}`,
    },
    netnr: {
      "url": "https://cors.zme.ink/https://github.com",
      "name": "netnr",
      "desc": "由@netnr提供",
      types: ['mirror', 'raw', 'download'],
    },
    fastgitSsh: {
      "url": "[email protected]",
      "name": "FastGit",
      "desc": "fastgit ssh",
      types: ['clone'],
    },
    fastgitSsh: {
      "url": "https://raw.fastgit.org",
      "name": "FastGit-raw",
      "desc": "fastgit raw",
      types: ['raw'],
    },
    zhlh6: {
      "url": "[email protected]",
      "name": "加速你的Github",
      "desc": "利用ucloud提供的GlobalSSH",
      types: ['clone'],
    },
    jsDelivr: {
      "url": "https://cdn.jsdelivr.net/gh",
      "name": "jsDelivr",
      "desc": "项目当前分支总文件大小不可超过 50MB",
      types: ['raw'], // download
    },
    staticaly: {
      "url": "https://cdn.staticaly.com/gh",
      "name": "Statically",
      "desc": "只能浏览图片和源代码文件,文件大小限制为30MB",
      types: ['raw'],
    },
    gitmirror: {
      // url: 'https://gitmirror.com',
      url: 'https://hub.gitmirror.com',
      desc: 'GitMirror 为您提供 Github 静态资源加速服务',
      types: ['download', 'raw'],
    },
    // fastgitdl: {
    //   url: 'https://download.fastgit.org', // todo: only release files、zip
    //   name: 'fastgit-dl',
    //   desc: 'fastgit download',
    //   types: ['download'],
    // },
    // wuyanzheshui: {
    //   url: 'https://github.wuyanzheshui.workers.dev',
    //   name: 'CF加速 1',
    //   desc: '【CF加速】每日10万次调用上限,由 wuyanzheshui 提供',
    //   types: ['mirror', 'clone', 'download'],
    // },
    // rc1844: {
    //   "url": "https://github.rc1844.workers.dev",
    //   "name": "CF加速 2",
    //   "desc": "【CF加速】每日10万次调用上限,由 @rc1844 提供",
    //   types: ['clone', 'download', 'raw'],
    // },
    // lzwme: {
    //   "url": "https://gh.lzwme.workers.dev",
    //   "name": "CF加速 2",
    //   "desc": "【CF加速】每日10万次调用上限,由 @renxia 提供",
    //   types: ['clone', 'download', 'raw', 'mirror'],
    // },
    // gitclone: {
    //   "url": "https://gitclone.com/github.com",
    //   "name": "GitClone",
    //   "desc": "GitHub clone 缓存加速网站",
    //   types: ['clone'],
    // },
  };
  const OtherUrl = [
    ["https://github.com/lzwme/vm-gh-proxy-cn", "脚本Github仓库地址,点个赞谢谢"],
    ["https://greasyfork.org/zh-CN/scripts/461115", "GreasyFork 地址,请评分收藏"],
    ["https://doc.fastgit.org/", "FastGit,请仔细甄别"],
    ["https://d.serctl.com", "GitHub中转下载"],
    ["https://gitee.com/organizations/mirrors/projects", "Gitee 极速下载"],
  ];
  const MirrorsList = Object.values(Mirrors);

  MirrorsList.forEach(item => {
    if (!item.format) {
      item.format = (href, type) => {
        if (type === 'raw' && ['Statically', 'jsDelivr'].includes(item.name)) {
          return item.url + href.replace(`${repo}/raw/`, `${repo}@`);
        }
        const sep = item.url.includes('@') ? ':' : '/';
        return `${item.url}${sep}${href.replace(/^\//, '')}`;
      };
    }

    if (!item.types) item.types = ['raw'];
    if (!item.name) item.name = item.url.split('.')[1];
    if (!item.desc) item.desc = item.name;
  });

  const CloneSet = MirrorsList.filter(d => d.types.includes('clone'));
  const MirrorSet = MirrorsList.filter(d => d.types.includes('mirror'));
  const DownloadSet = MirrorsList.filter(d => d.types.includes('download'));
  const RawSet = MirrorsList.filter(d => d.types.includes('raw'));
  const isPC = !/Android|iPhone|iPad|iPod|Windows Phone|SymbianOS/i.test(navigator.userAgent);
  const observer = new MutationObserver(() => setTimeout(run, 1500));
  observer.observe(document.querySelector('head'), { attributes: true, childList: true });

  let pathname = '';
  let repo = '';
  function run() {
    if (location.pathname != pathname) {
      pathname = location.pathname;
      repo = location.href.match(/github.com\/([^\/]+\/[^\/]+)\/?/)?.[1];
      addMenus();
      addRawList();
      if (location.pathname.split("/")[3] == "releases") addReleasesList();
      if (isPC) addDownloadZip();
    }
  }
  run();
  initProxyButton();
  initEvents();

  /**
   * 添加Raw列表
   */
  function addRawList() {
    let rawUrl = $("#raw-url");
    if (!rawUrl.length) rawUrl = $(`a[data-testid="raw-button"]:last`);
    const rawHref = rawUrl.attr('href');
    if (!rawHref) return;

    $('.raw-btn-proxy').remove();
    RawSet.forEach((item) => {
      const span = rawUrl.clone().removeAttr('id');

      span.attr({
        href: item.format(rawHref, 'raw'),
        title: item.desc,
        target: "_blank",
      });
      span.text(item.name).addClass('raw-btn-proxy');
      rawUrl.before(span);
    });
  }

  /**
   * Fast Download ZIP
   */
  function addDownloadZip() {
    $("a[data-open-app='link']").each(function () {
      const span = $(`<li class="Box-row p-0"></li>`);
      const href = $(this).attr("href");
      const clone = $(this)
        .clone()
        .removeAttr("data-hydro-click data-hydro-click-hmac data-ga-click")
        .addClass("Box-row Box-row--hover-gray");

      DownloadSet.forEach(item => {
        const span1 = $(clone.html().replace("Download ZIP", `Download ZIP(${item.name})`)).attr({
          href: item.format(href),
          title: item.desc,
        });
        span.append(span1);
      });
      $(this).parent().after(span);
    });
  }
  /**
   * 添加Releases列表
   */
  function addReleasesList(retry = 3) {
    const $alist = $(".Box--condensed").find("[href]");
    if (!$alist.length && retry) return setTimeout(() => addReleasesList(--retry), 1000);
    $alist.each(function() {
      const $el = $(this);
      const href = $el.attr('href');
      $el.parent()
      .after(`<div style="position: absolute; right: 180px; top: 0;">${
        DownloadSet.map((item) => {
          return `<a class="flex-1 btn btn-outline get-repo-btn BtnGroup-item" style="float: none; border-color: var(--color-btn-outline-text)"
            href="${item.format(href)}" title="${item.desc}">${item.name}</a>`;
        }).join('')
      }</div>`);
      $el.parent().parent().css({ position: 'relative' });
    });
  }
  /**
   * 添加菜单列表
   */
  function addMenus() {
    if (!repo) return;
    const repoGit = `${repo}.git`;
    const info = `<details class="details-reset details-overlay mr-0 mb-0" id="mirror-menu">
      <summary class="btn  ml-2 btn-primary" data-hotkey="m" title="打开列表" aria-haspopup="menu" role="button">
        <span class="css-truncate-target" data-menu-button="">克隆与镜像</span>
        <span class="dropdown-caret"></span>
      </summary>

      <details-menu class="SelectMenu SelectMenu--hasFilter" role="menu">
        <div class="SelectMenu-modal" style="width: 400px;">
          <header class="SelectMenu-header">
            <span class="SelectMenu-title">镜像站点与快速克隆</span>
            <button class="SelectMenu-closeButton" type="button" data-toggle-for="mirror-menu"><svg aria-label="Close menu"
                class="octicon octicon-x" width="16" height="16" role="img">
                <path fill-rule="evenodd"
                  d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z">
                </path>
              </svg></button>
          </header>

          <tab-container class="d-flex flex-column js-branches-tags-tabs" style="min-height: 0;">
            <div class="SelectMenu-tabs" role="tablist">
              <button class="SelectMenu-tab" type="button" role="tab"
                aria-selected="true" tabindex="0">主要</button>
              <button class="SelectMenu-tab" type="button" role="tab"
                aria-selected="false" tabindex="-1">其他</button>
            </div>

            <div role="tabpanel" class="flex-column flex-auto overflow-auto" tabindex="0">
              <div class="SelectMenu-list" data-filter-list="">
                <div class="btn-block" style="padding: 4px;background-color: #ffffdd;color: #996600;" role="alert">clone、depth命令的插入可手动编辑代码关闭</div>
                <div class="btn-block flash-error" style="padding: 4px;color: #990000;" role="alert">请不要在镜像网站登录账号,若因此造成任何损失本人概不负责</div> ${
                    CloneSet.map((item) => cloneHtml(`${clonePrefix}${item.format(repoGit, 'clone')}`, item.name)).join('')
                  }${
                    cloneHtml("git remote set-url origin https://github.com/" + repoGit, "还原GitHub仓库地址")
                  } ${
                    addBrowseList()
                  }</div>
              </div>
              <div role="tabpanel" class="flex-column flex-auto overflow-auto" tabindex="0" hidden="">
                <div class="SelectMenu-list">${
                  OtherUrl.map((d) => listHtml(d[0], d[1])).join('')
                }</div>
              </div>
            </tab-container>
          </div>
        </details-menu>
    </details>`;

    $('#mirror-menu').remove(); // remove old
    $(".pagehead-actions").before(info);

    function cloneHtml(Url, Tip) {
      return `<div class="input-group notranslate" title="${Tip}">
        <input type="text" class="form-control input-monospace input-sm" value="${Url}" readonly=""
          data-autoselect="">
        <div class="input-group-button">
          <clipboard-copy value="${Url}" class="btn btn-sm"><svg class="octicon octicon-clippy"
              viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">
              <path fill-rule="evenodd"
                d="M5.75 1a.75.75 0 00-.75.75v3c0 .414.336.75.75.75h4.5a.75.75 0 00.75-.75v-3a.75.75 0 00-.75-.75h-4.5zm.75 3V2.5h3V4h-3zm-2.874-.467a.75.75 0 00-.752-1.298A1.75 1.75 0 002 3.75v9.5c0 .966.784 1.75 1.75 1.75h8.5A1.75 1.75 0 0014 13.25v-9.5a1.75 1.75 0 00-.874-1.515.75.75 0 10-.752 1.298.25.25 0 01.126.217v9.5a.25.25 0 01-.25.25h-8.5a.25.25 0 01-.25-.25v-9.5a.25.25 0 01.126-.217z">
              </path>
            </svg></clipboard-copy>
        </div>
      </div>`;
    }
  }

  /**
   * 添加镜像浏览列表
   */
  function addBrowseList() {
    const path = window.location.pathname;
    const info = MirrorSet.map(item => listHtml(item.format(path, 'mirror'), `镜像浏览(${item.name})`, item.desc));

    if (location.hostname != "github.com") info.push(listHtml(`https://github.com${path}`, "返回GitHub"));

    return info.join('');
  }

  function initProxyButton() {
    $('#js-repo-pjax-container').off('mouseenter', 'a').on('mouseenter', 'a', function(ev) {
      const $el = $(this);
      if ($el.attr('id') === 'gh-proxy-btn') return;

      const preBtn = $('#gh-proxy-btn');
      const href =$el.attr('href').replace(/^https:\/\/github\.com/, '');
      const proxyHref = `${Mirrors.ghproxy.url}${href}`;
      if (!/\/(blob|release|archive)\//.test(href) || href.startsWith('http') || preBtn.attr('href') === proxyHref) return;

      $('#gh-proxy-btn').remove();
      const $btn = $(`<a class="btn" href="${proxyHref}" target="_blank" title="proxy link" id="gh-proxy-btn">🚀</a>`);

      $el.parent().append($btn).css({ position: 'relative', overflow: 'visible' });
      $btn.css({ position: 'absolute', left: 120, top: 2 });
      $btn.on('mouseleave', () => $btn.remove());
    });
  }

  function initEvents() {
    $(document).on('click', 'a', function(ev) {
      const $el = $(this);
      const href = $el.attr('href');

      if (['cors.zme.ink'].includes(location.host) && href.startsWith('/')) {
        ev.preventDefault();
        const hrefFixed = `${location.origin}/https://github.com${href}`;
        $el.attr('target') === '_blank' ? window.open(hrefFixed) : location.href = hrefFixed;
      }
    });
  }

  function listHtml(url, name, desc = "") {
    return `<a class="SelectMenu-item" href="${url}" target="_blank" title="${desc}" role="menuitemradio" aria-checked="false" rel="nofollow">
      <span class="css-truncate css-truncate-overflow" style="text-align:center;">${name}</span></a>`;
  }
})();