GitHub File Size Viewer

GitHub File Size Viewer is a lightweight userscript that displays the size of each file and folder in GitHub repositories. It uses the GitHub API to fetch details, including recursive folder sizes, making it easy to see the total size of nested directories.

目前為 2025-05-26 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name GitHub File Size Viewer
  3. // @name:zh-CN GitHub 显示文件和文件夹大小
  4. // @description GitHub File Size Viewer is a lightweight userscript that displays the size of each file and folder in GitHub repositories. It uses the GitHub API to fetch details, including recursive folder sizes, making it easy to see the total size of nested directories.
  5. // @description:zh-CN 显示 GitHub 存储库中每个文件和文件夹的大小。它使用 GitHub API 获取详细信息,包括递归文件夹大小,从而轻松查看嵌套目录的总大小。 以 KB、MB 或 GB 显示文件大小
  6. // @author Abhay,人民的勤务员 <china.qinwuyuan@gmail.com>
  7. // @namespace https://github.com/ChinaGodMan/UserScripts
  8. // @supportURL https://github.com/ChinaGodMan/UserScripts/issues
  9. // @homepageURL https://github.com/ChinaGodMan/UserScripts
  10. // @license MIT
  11. // @match https://github.com/*
  12. // @icon https://raw.githubusercontent.com/ChinaGodMan/UserScriptsHistory/main/scriptsIcon/github-file-size-viewer.jpg
  13. // @compatible chrome
  14. // @compatible firefox
  15. // @compatible edge
  16. // @compatible opera
  17. // @compatible safari
  18. // @compatible kiwi
  19. // @compatible qq
  20. // @compatible via
  21. // @compatible brave
  22. // @version 2025.5.26.1
  23. // @grant GM_setValue
  24. // @grant GM_getValue
  25. // ==/UserScript==
  26. /**
  27. * File: github-file-size-viewer.user.js
  28. * Project: UserScripts
  29. * File Created: 2025/05/26,Monday 18:12:15
  30. * Author: 人民的勤务员@ChinaGodMan (china.qinwuyuan@gmail.com)
  31. * -----
  32. * Last Modified: 2025/05/26,Monday 18:37:36
  33. * Modified By: 人民的勤务员@ChinaGodMan (china.qinwuyuan@gmail.com)
  34. * -----
  35. * License: MIT License
  36. * Copyright © 2024 - 2025 ChinaGodMan,Inc
  37. */
  38.  
  39. console.log('Content script loaded.')
  40.  
  41. let GITHUB_TOKEN = GM_getValue('GITHUB_TOKEN', '')
  42. if (!GITHUB_TOKEN) {
  43. GM_setValue('GITHUB_TOKEN', prompt('Please enter your GitHub Token'))
  44. GITHUB_TOKEN = GM_getValue('GITHUB_TOKEN', '')
  45. }
  46.  
  47. // Format the size in a human-readable way
  48. function formatSize(bytes) {
  49. if (bytes < 1024 * 1024) {
  50. return (bytes / 1024).toFixed(2) + ' KB'
  51. } else if (bytes < 1024 * 1024 * 1024) {
  52. return (bytes / (1024 * 1024)).toFixed(2) + ' MB'
  53. } else {
  54. return (bytes / (1024 * 1024 * 1024)).toFixed(2) + ' GB'
  55. }
  56. }
  57.  
  58. // Recursively calculate the total size of a folder
  59. async function calculateFolderSize(apiUrl, headers) {
  60. //console.log('Calculating folder size for:', apiUrl)
  61. try {
  62. const response = await fetch(apiUrl, { headers })
  63. if (!response.ok) {
  64. console.error('Folder API error:', response.status, response.statusText)
  65. return 0
  66. }
  67. const data = await response.json()
  68. let totalSize = 0
  69. if (Array.isArray(data)) {
  70. const sizePromises = data.map((item) => {
  71. if (item.type === 'file' && typeof item.size === 'number') {
  72. return Promise.resolve(item.size)
  73. } else if (item.type === 'dir') {
  74. return calculateFolderSize(item.url, headers)
  75. } else {
  76. return Promise.resolve(0)
  77. }
  78. })
  79. const sizes = await Promise.all(sizePromises)
  80. totalSize = sizes.reduce((sum, size) => sum + size, 0)
  81. }
  82. return totalSize
  83. } catch (error) {
  84. console.error('Error calculating folder size:', error)
  85. return 0
  86. }
  87. }
  88.  
  89. // Fetch file/folder size from GitHub API with enhanced error handling
  90. async function fetchFileSize(apiUrl) {
  91. const token = GITHUB_TOKEN
  92. let headers = { 'Accept': 'application/vnd.github.v3+json' }
  93. if (token) {
  94. headers['Authorization'] = 'token ' + token
  95. }
  96. //console.log('Fetching size for:', apiUrl)
  97. try {
  98. const response = await fetch(apiUrl, { headers })
  99. if (!response.ok) {
  100. console.error('GitHub API responded with error:', response.status, response.statusText)
  101. return 'N/A'
  102. }
  103. const data = await response.json()
  104. //console.log('Received data:', data)
  105. if (data && data.message) {
  106. console.error('GitHub API error message:', data.message)
  107. return 'N/A'
  108. }
  109. if (data && !Array.isArray(data) && data.type === 'file') {
  110. if (typeof data.size === 'number') {
  111. return formatSize(data.size)
  112. } else {
  113. console.error('File object missing size:', data)
  114. return 'N/A'
  115. }
  116. } else if (Array.isArray(data)) {
  117. const folderSize = await calculateFolderSize(apiUrl, headers)
  118. return folderSize > 0 ? formatSize(folderSize) : 'Folder'
  119. } else {
  120. return 'N/A'
  121. }
  122. } catch (error) {
  123. console.error('Error fetching file size:', error)
  124. return 'N/A'
  125. }
  126. }
  127.  
  128. // Insert the size next to each file/folder link with GitHub-like styling
  129. function insertSizeAfterLink(link, sizeText) {
  130. const sizeSpan = document.createElement('span')
  131. sizeSpan.style.marginLeft = '10px'
  132. sizeSpan.style.fontSize = 'smaller'
  133. sizeSpan.style.color = '#6a737d'
  134. sizeSpan.textContent = `(${sizeText})`
  135. link.insertAdjacentElement('afterend', sizeSpan)
  136. }
  137.  
  138. // Main function: find all GitHub file/folder links, fetch sizes concurrently, display them
  139. async function displayFileSizes() {
  140. console.log('Running displayFileSizes...')
  141. const table = document.querySelector('table tbody')
  142. // 不要在这里选择document,不然就把readme里面的文件大小也特么的添加上了
  143. const links = table.querySelectorAll('a[href*="/blob/"], a[href*="/tree/"]')
  144. console.log('Found potential file/folder links:', links.length)
  145. const promises = Array.from(links).map(async (link) => {
  146. const urlParts = link.href.split('/')
  147. const user = urlParts[3]
  148. const repo = urlParts[4]
  149. const typeSegment = link.href.includes('/blob/') ? 'blob' : 'tree'
  150. const branchIndex = urlParts.indexOf(typeSegment) + 1
  151. const branch = urlParts[branchIndex]
  152. const filePath = urlParts.slice(branchIndex + 1).join('/')
  153. const apiUrl = `https://api.github.com/repos/${user}/${repo}/contents/${filePath}?ref=${branch}`
  154. const sizeText = await fetchFileSize(apiUrl)
  155. insertSizeAfterLink(link, sizeText)
  156. })
  157. await Promise.all(promises)
  158. }
  159.  
  160. window.addEventListener('load', () => {
  161. setTimeout(displayFileSizes, 2000)
  162. })
  163. function observeUrlChanges(callback, delay = 10) {
  164. let lastUrl = location.href
  165. const observer = new MutationObserver(() => {
  166. const url = location.href
  167. if (url !== lastUrl) {
  168. lastUrl = url
  169. setTimeout(() => {
  170. callback()
  171. }, delay)
  172. }
  173. })
  174. observer.observe(document, { subtree: true, childList: true })
  175. return observer
  176. }
  177. observeUrlChanges(displayFileSizes, 2000)