GitHub Repo File Downloader

Allows you to download individual files directly from GitHub repository pages.

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

  1. // ==UserScript==
  2. // @name GitHub Repo File Downloader
  3. // @description Allows you to download individual files directly from GitHub repository pages.
  4. // @icon https://github.githubassets.com/favicons/favicon-dark.svg
  5. // @version 1.0
  6. // @author afkarxyz
  7. // @namespace https://github.com/afkarxyz/misc-scripts/
  8. // @supportURL https://github.com/afkarxyz/misc-scripts/issues
  9. // @license MIT
  10. // @match https://github.com/*
  11. // @grant none
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. function isIconModified(svg) {
  18. return svg.dataset.customIcon === 'true';
  19. }
  20.  
  21. function modifySvgContent(svg, newContent, downloadUrl, fileName) {
  22. if (svg && !isIconModified(svg)) {
  23. const wrapper = document.createElement('div');
  24. wrapper.style.cursor = 'pointer';
  25. wrapper.style.display = 'inline-block';
  26. wrapper.style.transition = 'transform 0.1s ease-in-out';
  27. const newSvg = svg.cloneNode(true);
  28. newSvg.innerHTML = newContent;
  29. newSvg.dataset.customIcon = 'true';
  30. wrapper.appendChild(newSvg);
  31. wrapper.addEventListener('click', (e) => {
  32. e.preventDefault();
  33. e.stopPropagation();
  34. downloadFile(downloadUrl, fileName);
  35. });
  36. wrapper.addEventListener('mouseenter', () => {
  37. wrapper.style.transform = 'scale(1.1)';
  38. });
  39. wrapper.addEventListener('mouseleave', () => {
  40. wrapper.style.transform = 'scale(1)';
  41. });
  42. svg.parentNode.replaceChild(wrapper, svg);
  43. }
  44. }
  45.  
  46. async function downloadFile(url, fileName) {
  47. try {
  48. const response = await fetch(url);
  49. const blob = await response.blob();
  50. const link = document.createElement('a');
  51. link.href = window.URL.createObjectURL(blob);
  52. link.download = fileName;
  53. link.style.display = 'none';
  54. document.body.appendChild(link);
  55. link.click();
  56. document.body.removeChild(link);
  57. } catch (error) {
  58. console.error('Download failed:', error);
  59. }
  60. }
  61.  
  62. function updateIcons() {
  63. const fileRows = document.querySelectorAll('.react-directory-row');
  64. let modifiedFileCount = 0;
  65.  
  66. fileRows.forEach(row => {
  67. const svgs = row.querySelectorAll('.react-directory-filename-column svg:not([data-custom-icon="true"])');
  68.  
  69. svgs.forEach(svg => {
  70. if (svg.classList.contains('color-fg-muted')) {
  71. const fileLink = row.querySelector('a[href]');
  72. if (!fileLink) return;
  73. const downloadUrl = fileLink.href
  74. .replace('github.com', 'raw.githubusercontent.com')
  75. .replace('/blob/', '/');
  76. const fileName = fileLink.textContent.trim();
  77.  
  78. const newFileContent = `
  79. <path d="M14.5,3.4l-2.9-2.9C11.2,0.2,10.8,0,10.3,0H3.8C2.8,0,2,0.8,2,1.8v12.5c0,1,0.8,1.8,1.8,1.8h9.5c1,0,1.8-0.8,1.8-1.8V4.7
  80. C15,4.2,14.8,3.8,14.5,3.4z M10.5,1.6L10.5,1.6l2.9,2.9l0,0h-2.7c-0.1,0-0.2-0.1-0.2-0.2V1.6z M13.5,14.2c0,0.1-0.1,0.2-0.2,0.2
  81. H3.8c-0.1,0-0.2-0.1-0.2-0.2V1.8c0-0.1,0.1-0.2,0.2-0.2H9v2.8C9,5.2,9.8,6,10.8,6h2.8V14.2z"/>
  82. <path d="M9.1,10.6V7.3c0-0.3-0.3-0.6-0.6-0.6S7.9,7,7.9,7.3v3.3L6.5,9.3C6.3,9,5.9,9,5.7,9.3c-0.2,0.2-0.2,0.6,0,0.8l2.4,2.4
  83. c0.2,0.2,0.6,0.2,0.8,0h0l2.4-2.4c0.2-0.2,0.2-0.6,0-0.8c-0.2-0.2-0.6-0.2-0.8,0L9.1,10.6z"/>
  84. `;
  85. modifySvgContent(svg, newFileContent, downloadUrl, fileName);
  86. modifiedFileCount++;
  87. }
  88. });
  89. });
  90.  
  91. if (modifiedFileCount > 0) {
  92. console.log(`Modified ${modifiedFileCount} file icons with direct download functionality and hover effects.`);
  93. }
  94. }
  95.  
  96. function runIconUpdate() {
  97. function debouncedUpdate() {
  98. updateIcons();
  99. }
  100.  
  101. debouncedUpdate();
  102.  
  103. const observer = new MutationObserver((mutations) => {
  104. mutations.forEach((mutation) => {
  105. if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
  106. debouncedUpdate();
  107. }
  108. });
  109. });
  110.  
  111. observer.observe(document.body, { childList: true, subtree: true });
  112.  
  113. console.log('GitHub Repo File Downloader: Script is now running.');
  114. }
  115.  
  116. if (document.readyState === 'loading') {
  117. document.addEventListener('DOMContentLoaded', runIconUpdate);
  118. } else {
  119. runIconUpdate();
  120. }
  121. })();

QingJ © 2025

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