GitHub 显示文件和文件夹大小

显示 GitHub 存储库中每个文件和文件夹的大小。它使用 GitHub API 获取详细信息,包括递归文件夹大小,从而轻松查看嵌套目录的总大小。 以 KB、MB 或 GB 显示文件大小

当前为 2025-05-26 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name        GitHub File Size Viewer
// @name:zh-CN   GitHub 显示文件和文件夹大小
// @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.
// @description:zh-CN 显示 GitHub 存储库中每个文件和文件夹的大小。它使用 GitHub API 获取详细信息,包括递归文件夹大小,从而轻松查看嵌套目录的总大小。 以 KB、MB 或 GB 显示文件大小
// @author       Abhay,人民的勤务员 <[email protected]>
// @namespace    https://github.com/ChinaGodMan/UserScripts
// @supportURL   https://github.com/ChinaGodMan/UserScripts/issues
// @homepageURL  https://github.com/ChinaGodMan/UserScripts
// @license      MIT
// @match        https://github.com/*
// @icon        https://raw.githubusercontent.com/ChinaGodMan/UserScriptsHistory/main/scriptsIcon/github-file-size-viewer.jpg
// @compatible   chrome
// @compatible   firefox
// @compatible   edge
// @compatible   opera
// @compatible   safari
// @compatible   kiwi
// @compatible   qq
// @compatible   via
// @compatible   brave
// @version      2025.5.26.1
// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==
/**
 * File: github-file-size-viewer.user.js
 * Project: UserScripts
 * File Created: 2025/05/26,Monday 18:12:15
 * Author: 人民的勤务员@ChinaGodMan ([email protected])
 * -----
 * Last Modified: 2025/05/26,Monday 18:37:36
 * Modified By: 人民的勤务员@ChinaGodMan ([email protected])
 * -----
 * License: MIT License
 * Copyright © 2024 - 2025 ChinaGodMan,Inc
 */

console.log('Content script loaded.')

let GITHUB_TOKEN = GM_getValue('GITHUB_TOKEN', '')
if (!GITHUB_TOKEN) {
    GM_setValue('GITHUB_TOKEN', prompt('Please enter your GitHub Token'))
    GITHUB_TOKEN = GM_getValue('GITHUB_TOKEN', '')
}

// Format the size in a human-readable way
function formatSize(bytes) {
    if (bytes < 1024 * 1024) {
        return (bytes / 1024).toFixed(2) + ' KB'
    } else if (bytes < 1024 * 1024 * 1024) {
        return (bytes / (1024 * 1024)).toFixed(2) + ' MB'
    } else {
        return (bytes / (1024 * 1024 * 1024)).toFixed(2) + ' GB'
    }
}

// Recursively calculate the total size of a folder
async function calculateFolderSize(apiUrl, headers) {
    //console.log('Calculating folder size for:', apiUrl)
    try {
        const response = await fetch(apiUrl, { headers })
        if (!response.ok) {
            console.error('Folder API error:', response.status, response.statusText)
            return 0
        }
        const data = await response.json()
        let totalSize = 0
        if (Array.isArray(data)) {
            const sizePromises = data.map((item) => {
                if (item.type === 'file' && typeof item.size === 'number') {
                    return Promise.resolve(item.size)
                } else if (item.type === 'dir') {
                    return calculateFolderSize(item.url, headers)
                } else {
                    return Promise.resolve(0)
                }
            })
            const sizes = await Promise.all(sizePromises)
            totalSize = sizes.reduce((sum, size) => sum + size, 0)
        }
        return totalSize
    } catch (error) {
        console.error('Error calculating folder size:', error)
        return 0
    }
}

// Fetch file/folder size from GitHub API with enhanced error handling
async function fetchFileSize(apiUrl) {
    const token = GITHUB_TOKEN
    let headers = { 'Accept': 'application/vnd.github.v3+json' }
    if (token) {
        headers['Authorization'] = 'token ' + token
    }
    //console.log('Fetching size for:', apiUrl)
    try {
        const response = await fetch(apiUrl, { headers })
        if (!response.ok) {
            console.error('GitHub API responded with error:', response.status, response.statusText)
            return 'N/A'
        }
        const data = await response.json()
        //console.log('Received data:', data)
        if (data && data.message) {
            console.error('GitHub API error message:', data.message)
            return 'N/A'
        }
        if (data && !Array.isArray(data) && data.type === 'file') {
            if (typeof data.size === 'number') {
                return formatSize(data.size)
            } else {
                console.error('File object missing size:', data)
                return 'N/A'
            }
        } else if (Array.isArray(data)) {
            const folderSize = await calculateFolderSize(apiUrl, headers)
            return folderSize > 0 ? formatSize(folderSize) : 'Folder'
        } else {
            return 'N/A'
        }
    } catch (error) {
        console.error('Error fetching file size:', error)
        return 'N/A'
    }
}

// Insert the size next to each file/folder link with GitHub-like styling
function insertSizeAfterLink(link, sizeText) {
    const sizeSpan = document.createElement('span')
    sizeSpan.style.marginLeft = '10px'
    sizeSpan.style.fontSize = 'smaller'
    sizeSpan.style.color = '#6a737d'
    sizeSpan.textContent = `(${sizeText})`
    link.insertAdjacentElement('afterend', sizeSpan)
}

// Main function: find all GitHub file/folder links, fetch sizes concurrently, display them
async function displayFileSizes() {
    console.log('Running displayFileSizes...')
    const table = document.querySelector('table tbody')
    // 不要在这里选择document,不然就把readme里面的文件大小也特么的添加上了
    const links = table.querySelectorAll('a[href*="/blob/"], a[href*="/tree/"]')
    console.log('Found potential file/folder links:', links.length)
    const promises = Array.from(links).map(async (link) => {
        const urlParts = link.href.split('/')
        const user = urlParts[3]
        const repo = urlParts[4]
        const typeSegment = link.href.includes('/blob/') ? 'blob' : 'tree'
        const branchIndex = urlParts.indexOf(typeSegment) + 1
        const branch = urlParts[branchIndex]
        const filePath = urlParts.slice(branchIndex + 1).join('/')
        const apiUrl = `https://api.github.com/repos/${user}/${repo}/contents/${filePath}?ref=${branch}`
        const sizeText = await fetchFileSize(apiUrl)
        insertSizeAfterLink(link, sizeText)
    })
    await Promise.all(promises)
}

window.addEventListener('load', () => {
    setTimeout(displayFileSizes, 2000)
})
function observeUrlChanges(callback, delay = 10) {
    let lastUrl = location.href
    const observer = new MutationObserver(() => {
        const url = location.href
        if (url !== lastUrl) {
            lastUrl = url
            setTimeout(() => {
                callback()
            }, delay)
        }
    })
    observer.observe(document, { subtree: true, childList: true })
    return observer
}
observeUrlChanges(displayFileSizes, 2000)