Hugging Face Repo Size

Show total storage size (MB/GB) of any Hugging Face model, dataset, or space as soon as the page loads.

// ==UserScript==
// @name         Hugging Face Repo Size
// @namespace    HF Repo Size
// @version      0.2
// @description  Show total storage size (MB/GB) of any Hugging Face model, dataset, or space as soon as the page loads.
// @author       sm18lr88
// @license      MIT
// @match        https://huggingface.co/*
// @grant        none
// ==/UserScript==

(function () {
  "use strict";

  /** Format bytes → human-readable MB / GB string */
  function fmt(bytes) {
    if (!bytes) return " – ";
    const mb = bytes / (1024 ** 2);
    return mb < 1000 ? `${mb.toFixed(2)} MB` : `${(mb / 1024).toFixed(2)} GB`;
  }

  /** Extract “owner/repo” path and choose the correct API endpoint */
  function apiInfo(urlPath) {
    const clean = urlPath.replace(/^\/|\/$/g, "");
    if (!clean) return null;
    if (clean.startsWith("datasets/"))
      return { api: `https://huggingface.co/api/datasets/${clean.slice(9)}` };
    if (clean.startsWith("spaces/"))
      return { api: `https://huggingface.co/api/spaces/${clean.slice(7)}` };
    return { api: `https://huggingface.co/api/models/${clean}` };
  }

  /** Compute total bytes from API response */
  function totalBytes(info) {
    if (typeof info.usedStorage === "number") return info.usedStorage;

    if (Array.isArray(info.siblings)) {
      return info.siblings.reduce((sum, s) => {
        if (typeof s.size === "number") return sum + s.size;
        if (s.LFS && typeof s.LFS.size === "number") return sum + s.LFS.size;
        return sum;
      }, 0);
    }
    return null;
  }

  /** Insert badge next to page title */
  function show(str) {
    const bannerId = "hf-size-badge";
    if (document.getElementById(bannerId)) return;

    const title = document.querySelector("h1");
    if (!title) return; // title not yet in DOM

    const span = document.createElement("span");
    span.id = bannerId;
    span.textContent = ` (${str})`;
    span.style.cssText =
      "margin-left:4px;padding:2px 6px;background:#2d6590;color:#fff;border-radius:4px;font-size:13px;";
    title.appendChild(span);
  }

  /** Fetch & display size */
  async function process() {
    const info = apiInfo(location.pathname);
    if (!info) return;

    try {
      const res = await fetch(info.api, { credentials: "omit" });
      if (!res.ok) throw new Error(`API ${res.status}`);
      const data = await res.json();
      show(fmt(totalBytes(data)));
    } catch {
      show("size N/A");
    }
  }

  //------------------------------------------------------------------
  // Run on initial load and whenever HF’s client-side routing changes
  //------------------------------------------------------------------
  const pushState = history.pushState;
  history.pushState = function () {
    pushState.apply(this, arguments);
    setTimeout(process, 50);
  };
  window.addEventListener("popstate", () => setTimeout(process, 50));
  document.addEventListener("DOMContentLoaded", process);
  process();
})();