GitHub镜像

GitHub镜像,加速访问GitHub,支持Clone、Release、Raw、Zip加速。

目前为 2021-03-07 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name GitHub镜像
  3. // @name:en GitHub Mirror
  4. // @description GitHub镜像,加速访问GitHub,支持Clone、Release、Raw、Zip加速。
  5. // @description:en GitHub mirror. Accelerate access to GitHub. Support Clone, Release, RAW and ZIP acceleration.
  6. // @namespace https://github.com/HaleShaw
  7. // @version 1.0.0
  8. // @author HaleShaw
  9. // @copyright 2021+, HaleShaw (https://github.com/HaleShaw)
  10. // @license AGPL-3.0-or-later
  11. // @homepage https://github.com/HaleShaw/TM-GitHubMirror
  12. // @supportURL https://github.com/HaleShaw/TM-GitHubMirror/issues
  13. // @contributionURL https://www.jianwudao.com/
  14. // @icon https://github.githubassets.com/favicon.ico
  15. // @require https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js
  16. // @include *://github.com/*
  17. // @include *://github*
  18. // @compatible Chrome
  19. // @run-at document-end
  20. // @grant GM_addStyle
  21. // @grant GM_getValue
  22. // @grant GM_setValue
  23. // ==/UserScript==
  24.  
  25. // ==OpenUserJS==
  26. // @author HaleShaw
  27. // @collaborator HaleShaw
  28. // ==/OpenUserJS==
  29.  
  30. (function () {
  31. ('use strict');
  32.  
  33. const style = `
  34. /* The menu container */
  35. .menuContainer {
  36. width: 600px;
  37. }
  38.  
  39. .menuBlock {
  40. padding: 4px 0;
  41. color: #990000;
  42. }
  43.  
  44. .menuLeftIcon{
  45. margin-right:5px;
  46. }
  47.  
  48. .menuButtonLabel{
  49. margin-right: 2rem;
  50. }
  51.  
  52. .menuButtonCheck{
  53. vertical-align: text-bottom;
  54. margin: 0 3px;
  55. }
  56.  
  57. .clone {
  58. padding-left: 0 !important;
  59. width: calc(100% - 21px) !important;
  60. }
  61. `;
  62.  
  63. const mirrors = [
  64. {
  65. id: 0,
  66. name: 'CnpmJS',
  67. url: 'https://github.com.cnpmjs.org',
  68. description: 'cnpmjs.org'
  69. },
  70. {
  71. id: 1,
  72. name: 'FastGit',
  73. url: 'https://hub.fastgit.org',
  74. description: 'KevinZonda'
  75. },
  76. {
  77. id: 2,
  78. name: 'FastGit',
  79. url: 'https://download.fastgit.org',
  80. description: 'KevinZonda'
  81. },
  82. {
  83. id: 3,
  84. name: 'wuyanzheshui',
  85. url: 'https://github.wuyanzheshui.workers.dev',
  86. description: 'wuyanzheshui,每日10万次调用上限'
  87. },
  88. {
  89. id: 4,
  90. name: 'RC1844',
  91. url: 'https://github.rc1844.workers.dev',
  92. description: 'RC1844,每日10万次调用上限'
  93. },
  94. {
  95. id: 5,
  96. name: 'jsDelivr',
  97. url: 'https://cdn.jsdelivr.net/gh',
  98. description: '项目当前分支总文件大小不可超过50MB'
  99. },
  100. {
  101. id: 6,
  102. name: 'IAPK',
  103. url: 'https://github.iapk.cc',
  104. description: 'IAPK工具箱,Github下载器'
  105. },
  106. {
  107. id: 7,
  108. name: 'Ecalose',
  109. url: 'https://gh.haval.gq',
  110. description: 'Ecalose,每日10万次调用上限'
  111. },
  112. {
  113. id: 8,
  114. name: 'IAPK',
  115. url: 'https://iapk.cc/github?url=https://github.com',
  116. description: 'IAPK工具箱,Github下载器'
  117. }
  118. ];
  119.  
  120. //添加对应索引即可使用
  121. const cloneSet = [0, 1, 3];
  122. const browseSet = [0, 1, 3, 4, 6, 7];
  123. const downloadSet = [2, 3, 4, 7, 8];
  124. const rawSet = [3, 4, 7, 8];
  125.  
  126. const messages = {
  127. en: {
  128. menuButton: {
  129. name: 'CloneMirror',
  130. title: 'Open List',
  131. header: 'Quickly clone and Mirror sites',
  132. block:
  133. 'Please do not login in the mirror site. I will not be responsible for any loss caused by this.'
  134. }
  135. },
  136. zh: {
  137. menuButton: {
  138. name: '克隆与镜像',
  139. title: '打开列表',
  140. header: '快速克隆与镜像站点',
  141. block: '请不要在镜像网站登录(不可用)账号,若因此造成任何损失本人概不负责'
  142. }
  143. }
  144. };
  145.  
  146. const icons = {
  147. closeIcon: `
  148. <svg aria-label="Close menu" class="octicon octicon-x" width="16" height="16" role="img">
  149. <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>
  150. </svg>`,
  151. copyIcon: `
  152. <svg class="octicon octicon-clippy" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">
  153. <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>
  154. </svg>`,
  155. commandIcon: `
  156. <svg class="octicon octicon-terminal menuLeftIcon" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">
  157. <path fill-rule="evenodd" d="M0 2.75C0 1.784.784 1 1.75 1h12.5c.966 0 1.75.784 1.75 1.75v10.5A1.75 1.75 0 0114.25 15H1.75A1.75 1.75 0 010 13.25V2.75zm1.75-.25a.25.25 0 00-.25.25v10.5c0 .138.112.25.25.25h12.5a.25.25 0 00.25-.25V2.75a.25.25 0 00-.25-.25H1.75zM7.25 8a.75.75 0 01-.22.53l-2.25 2.25a.75.75 0 11-1.06-1.06L5.44 8 3.72 6.28a.75.75 0 111.06-1.06l2.25 2.25c.141.14.22.331.22.53zm1.5 1.5a.75.75 0 000 1.5h3a.75.75 0 000-1.5h-3z"></path>
  158. </svg>`,
  159. linkIcon: `
  160. <svg class="octicon octicon-link color-text-secondary menuLeftIcon" alt="custom" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">
  161. <path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path>
  162. </svg>`
  163. };
  164.  
  165. const clonePrefix = 'git clone ';
  166. const depthPrefix = '--depth=1 ';
  167. let message;
  168. let settingHtml;
  169.  
  170. main();
  171.  
  172. function main() {
  173. GM_addStyle(style);
  174. logInfo(GM_info.script.name, GM_info.script.version);
  175. initSetting();
  176. message = getMessage(true, true);
  177. settingHtml = getSettingHtml();
  178. let info = getMenuButtonPrefix() + getCloneList() + getBrowseList() + getMenuButtonSuffix();
  179. $('h1.flex-wrap.break-word.text-normal').append(info);
  180. }
  181.  
  182. /**
  183. * Initialize setting.
  184. */
  185. function initSetting() {
  186. let lang = GM_getValue('lang');
  187. let clone = GM_getValue('clone');
  188. let depth = GM_getValue('depth');
  189. if (lang == undefined) {
  190. GM_setValue('lang', 'zh');
  191. }
  192. if (clone == undefined) {
  193. GM_setValue('clone', true);
  194. }
  195. if (depth == undefined) {
  196. GM_setValue('depth', true);
  197. }
  198. }
  199.  
  200. function getMenuButtonPrefix() {
  201. return `
  202. <details class="details-reset details-overlay mr-0 mb-0" id="mirror-menu">
  203. <summary class="btn ml-2 btn-primary" id="menuButtonTitle" data-hotkey="m" title="${message.menuButton.title}" aria-haspopup="menu" role="button">
  204. <span class="css-truncate-target" id="menuButtonName" data-menu-button="">${message.menuButton.name}</span>
  205. <span class="dropdown-caret"></span>
  206. </summary>
  207.  
  208. <details-menu class="SelectMenu SelectMenu--hasFilter" role="menu">
  209. <div class="SelectMenu-modal menuContainer">
  210. <header class="SelectMenu-header">
  211. <span class="SelectMenu-title" id="menuButtonHeader">${message.menuButton.header}</span>
  212. ${settingHtml}
  213. <button class="SelectMenu-closeButton" type="button" data-toggle-for="mirror-menu">
  214. ${icons.closeIcon}
  215. </button>
  216. </header>
  217. <tab-container class="d-flex flex-column js-branches-tags-tabs" style="min-height: 0;">
  218. <div role="tabpanel" class="d-flex flex-column flex-auto" tabindex="0">
  219. <div class="SelectMenu-list" data-filter-list="">
  220. <div class="btn-block flash-error menuBlock" id="menuButtonBlock" role="alert">
  221. ${message.menuButton.block}
  222. </div>`;
  223. }
  224.  
  225. function getSettingHtml() {
  226. const clone = GM_getValue('clone');
  227. const depth = GM_getValue('depth');
  228. const lang = GM_getValue('lang');
  229. const cloneStatus = clone ? ' checked' : '';
  230. const depthStatus = depth ? ' checked' : '';
  231. const langStatus = lang == 'en' ? ' checked' : '';
  232. return `
  233. <label class="menuButtonLabel"><input id="menuButtonClone" class="menuButtonCheck" type="checkbox"${cloneStatus}>Clone</input></label>
  234. <label class="menuButtonLabel"><input id="menuButtonDepth" class="menuButtonCheck" type="checkbox"${depthStatus}>Depth</input></label>
  235. <label class="menuButtonLabel"><input id="menuButtonLang" class="menuButtonCheck" type="checkbox"${langStatus}>English</input></label>
  236. `;
  237. }
  238.  
  239. /**
  240. * Clone Checkbox event.
  241. */
  242. $('#menuButtonClone').change(function () {
  243. const status = $('#menuButtonClone').is(':checked');
  244. GM_setValue('clone', status);
  245. const inputs = $('.clone');
  246. for (let i = 0; i < inputs.length; i++) {
  247. let value = inputs[i].value;
  248. if (status) {
  249. value = clonePrefix + value;
  250. } else {
  251. value = value.replace(clonePrefix, '');
  252. }
  253. inputs[i].value = value;
  254. $(inputs[i]).next().children().attr('value', value);
  255. }
  256. });
  257.  
  258. /**
  259. * Depth Checkbox event.
  260. */
  261. $('#menuButtonDepth').change(function () {
  262. const status = $('#menuButtonDepth').is(':checked');
  263. GM_setValue('depth', status);
  264. const inputs = $('.form-control.input-monospace.input-sm.clone');
  265. const index = clonePrefix.length;
  266. for (let i = 0; i < inputs.length; i++) {
  267. let value = inputs[i].value;
  268. if (status) {
  269. const length = value.length;
  270. if (value.startsWith(clonePrefix)) {
  271. value = value.slice(0, index) + depthPrefix + value.slice(index, length);
  272. } else {
  273. value = depthPrefix + value;
  274. }
  275. } else {
  276. value = value.replace(depthPrefix, '');
  277. }
  278. inputs[i].value = value;
  279. $(inputs[i]).next().children().attr('value', value);
  280. }
  281. });
  282.  
  283. /**
  284. * Language Checkbox event.
  285. */
  286. $('#menuButtonLang').change(function () {
  287. const status = $('#menuButtonLang').is(':checked');
  288. const value = status ? 'en' : 'zh';
  289. GM_setValue('lang', value);
  290. message = getMessage();
  291. updateMessage();
  292. });
  293.  
  294. /**
  295. * Update message by target language.
  296. */
  297. function updateMessage() {
  298. $('#menuButtonTitle').attr('title', message.menuButton.title);
  299. $('#menuButtonName').html(message.menuButton.name);
  300. $('#menuButtonHeader').html(message.menuButton.header);
  301. $('#menuButtonBlock').html(message.menuButton.block);
  302. }
  303.  
  304. /**
  305. * Get the clone list.
  306. */
  307. function getCloneList() {
  308. const href = window.location.href.split('/');
  309. const git = href[3] + '/' + href[4] + '.git';
  310. let info = '';
  311. const prefix = getClonePrefix();
  312. cloneSet.forEach(id => {
  313. info += getCloneHtml(prefix + mirrors[id]['url'] + '/' + git, mirrors[id]['name']);
  314. });
  315. return info;
  316. }
  317.  
  318. function getMenuButtonSuffix() {
  319. return `</div></div></tab-container></div></details-menu></details>`;
  320. }
  321.  
  322. /**
  323. * Get the clone command prefix.
  324. */
  325. function getClonePrefix() {
  326. let prefix = '';
  327. let clone = GM_getValue('clone');
  328. let depth = GM_getValue('depth');
  329. if (clone) {
  330. prefix += 'git clone ';
  331. }
  332. if (depth) {
  333. prefix += '--depth=1 ';
  334. }
  335. return prefix;
  336. }
  337.  
  338. /**
  339. * Get the clone button html string.
  340. * @param {String} url url.
  341. * @param {tip} tip tip.
  342. */
  343. function getCloneHtml(url, tip) {
  344. return `
  345. <div class="input-group" style="padding: 0 16px;" title="${tip}">
  346. ${icons.commandIcon}
  347. <input type="text" class="form-control input-monospace input-sm clone" value="${url}" readonly=""
  348. data-autoselect="">
  349. <div class="input-group-button">
  350. <clipboard-copy value="${url}" class="btn btn-sm">
  351. ${icons.copyIcon}
  352. </clipboard-copy>
  353. </div>
  354. </div>`;
  355. }
  356.  
  357. /**
  358. * 添加镜像浏览列表
  359. */
  360. function getBrowseList() {
  361. let info = ``;
  362. const href = window.location.href.split('/');
  363. const path = window.location.pathname;
  364. browseSet.forEach(id => {
  365. info += getBrowseHtml(
  366. mirrors[id]['url'] + path,
  367. mirrors[id]['name'],
  368. mirrors[id]['description']
  369. );
  370. });
  371. if (href.length == 5 || path.includes('/tree/') || path.includes('/blob/')) {
  372. var html = mirrors[5]['url'] + path.replace('/tree/', '@').replace('/blob/', '@');
  373. if (!path.includes('/blob/')) {
  374. html += '/';
  375. }
  376. info += getBrowseHtml(html, mirrors[5]['name'], mirrors[5]['description']);
  377. }
  378. if (location.hostname != 'github.com') {
  379. info += getBrowseHtml(`https://github.com${path}`, '返回GitHub');
  380. }
  381. return info;
  382. }
  383.  
  384. function getBrowseHtml(url, name, tip = '') {
  385. return `
  386. <a class="SelectMenu-item" href="${url}" target="_blank" title="${tip}" role="menuitemradio" aria-checked="false" rel="nofollow">
  387. ${icons.linkIcon}
  388. <span class="css-truncate css-truncate-overflow" style="width: 520px; overflow: hidden; word-break:keep-all; white-space:nowrap; text-overflow:ellipsis;">${url}</span>
  389. <span class="css-truncate css-truncate-overflow" style="width: 80px; text-align: right;">${name}</span>
  390. </a>`;
  391. }
  392.  
  393. /**
  394. * Get message by setting.
  395. */
  396. function getMessage() {
  397. return 'zh' == GM_getValue('lang') ? messages.zh : messages.en;
  398. }
  399.  
  400. /**
  401. * Log the title and version at the front of the console.
  402. * @param {String} title title.
  403. * @param {String} version script version.
  404. */
  405. function logInfo(title, version) {
  406. const titleStyle = 'color:white;background-color:#606060';
  407. const versionStyle = 'color:white;background-color:#1475b2';
  408. const logTitle = ' ' + title + ' ';
  409. const logVersion = ' ' + version + ' ';
  410. console.log('%c' + logTitle + '%c' + logVersion, titleStyle, versionStyle);
  411. }
  412. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址