Guest User

Untitled

a guest
Jul 23rd, 2025
20
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.27 KB | None | 0 0
  1. // ==UserScript==
  2. // @name Hugging Face Repo Size Display (Recursive with TB and Copy URLs)
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.5
  5. // @description Show repo and directory file sizes with recursive directory size calculation, TB support, and copy download URLs
  6. // @author You
  7. // @match https://huggingface.co/*/blob/*
  8. // @match https://huggingface.co/*/tree/*
  9. // @match https://huggingface.co/*/src/*
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15.  
  16. // Function to extract repo name and path from URL
  17. function getRepoInfo() {
  18. const url = window.location.pathname;
  19. const match = url.match(/\/([^\/]+)\/([^\/]+)(?:\/(?:blob|tree|src)\/([^\/]+)(\/.*)?)?/);
  20. if (!match) return null;
  21.  
  22. const [, username, reponame] = match;
  23. const repoFullName = `${username}/${reponame}`;
  24.  
  25. let path = '';
  26. if (match[4]) {
  27. path = match[4].startsWith('/') ? match[4].substring(1) : match[4];
  28. }
  29.  
  30. return { repoFullName, path };
  31. }
  32.  
  33. // Fetch repository files using Hugging Face API
  34. async function fetchRepoFiles(repoName) {
  35. try {
  36. const response = await fetch(`https://huggingface.co/api/models/${repoName}/tree/main?recursive=true`);
  37. if (!response.ok) throw new Error(`HTTP ${response.status}`);
  38. return await response.json();
  39. } catch (error) {
  40. console.log('Failed to fetch repo files:', error);
  41. return [];
  42. }
  43. }
  44.  
  45. // Recursively fetch and calculate size of a directory
  46. async function getDirectorySize(repoName, directoryPath) {
  47. try {
  48. const url = directoryPath ?
  49. `https://huggingface.co/api/models/${repoName}/tree/main/${directoryPath}?recursive=true` :
  50. `https://huggingface.co/api/models/${repoName}/tree/main?recursive=true`;
  51.  
  52. const response = await fetch(url);
  53. if (!response.ok) return 0;
  54.  
  55. const files = await response.json();
  56. let totalSize = 0;
  57.  
  58. files.forEach(file => {
  59. if (file.size && (file.type === 'file' || file.type === 'file')) {
  60. totalSize += file.size;
  61. }
  62. });
  63.  
  64. return totalSize;
  65. } catch (error) {
  66. console.log(`Failed to fetch directory size for ${directoryPath}:`, error);
  67. return 0;
  68. }
  69. }
  70.  
  71. // Calculate the total repository size by recursively processing all directories
  72. async function getRepoSize(repoName) {
  73. try {
  74. // Get the complete file tree
  75. const response = await fetch(`https://huggingface.co/api/models/${repoName}/tree/main?recursive=true`);
  76. if (!response.ok) return 0;
  77.  
  78. const files = await response.json();
  79. let totalSize = 0;
  80.  
  81. // Process each file/directory
  82. for (const item of files) {
  83. if (item.type === 'file' && item.size) {
  84. totalSize += item.size;
  85. } else if (item.type === 'dir') {
  86. // Recursively calculate size of this directory
  87. const dirSize = await getDirectorySize(repoName, item.path);
  88. totalSize += dirSize;
  89. }
  90. }
  91.  
  92. return totalSize;
  93. } catch (error) {
  94. console.log('Failed to calculate repository size:', error);
  95. return 0;
  96. }
  97. }
  98.  
  99. // Calculate the size of files in the current directory only
  100. async function getDirectorySizeCurrent(repoName, currentPath) {
  101. try {
  102. // If currentPath is empty, we're at root
  103. if (!currentPath) {
  104. // Get root-level files
  105. const response = await fetch(`https://huggingface.co/api/models/${repoName}/tree/main?recursive=false`);
  106. if (!response.ok) return 0;
  107.  
  108. const items = await response.json();
  109. let totalSize = 0;
  110.  
  111. items.forEach(item => {
  112. if (item.type === 'file' && item.size) {
  113. totalSize += item.size;
  114. }
  115. });
  116.  
  117. return totalSize;
  118. }
  119.  
  120. // For subdirectories, get files directly in this directory
  121. const response = await fetch(`https://huggingface.co/api/models/${repoName}/tree/main/${currentPath}?recursive=false`);
  122. if (!response.ok) return 0;
  123.  
  124. const items = await response.json();
  125. let totalSize = 0;
  126.  
  127. items.forEach(item => {
  128. if (item.type === 'file' && item.size) {
  129. totalSize += item.size;
  130. }
  131. });
  132.  
  133. return totalSize;
  134. } catch (error) {
  135. console.log('Failed to calculate directory size:', error);
  136. return 0;
  137. }
  138. }
  139.  
  140. // Format bytes to human readable format with TB support
  141. function formatBytes(bytes) {
  142. if (bytes === 0) return '0 B';
  143.  
  144. // Define units including TB
  145. const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
  146. const k = 1024;
  147.  
  148. // Calculate the power
  149. const i = Math.floor(Math.log(bytes) / Math.log(k));
  150.  
  151. // Cap at TB (index 4)
  152. const displayIndex = Math.min(i, 4);
  153. const divisor = Math.pow(k, displayIndex);
  154. const formatted = (bytes / divisor).toFixed(2);
  155.  
  156. return `${formatted} ${sizes[displayIndex]}`;
  157. }
  158.  
  159. // Create and style the size display element
  160. function createSizeDisplay() {
  161. const display = document.createElement('div');
  162. display.id = 'hf-size-display';
  163. display.style.cssText = `
  164. position: fixed;
  165. bottom: 10px;
  166. right: 10px;
  167. background: rgba(0, 0, 0, 0.7);
  168. color: white;
  169. padding: 8px 12px;
  170. border-radius: 6px;
  171. font-size: 12px;
  172. font-family: monospace;
  173. z-index: 10000;
  174. pointer-events: auto;
  175. opacity: 0.8;
  176. transition: opacity 0.3s;
  177. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
  178. cursor: pointer;
  179. `;
  180. display.title = 'Click to copy download URLs of files in current directory';
  181. document.body.appendChild(display);
  182. return display;
  183. }
  184.  
  185. // Generate download URLs for files in the current directory and copy to clipboard
  186. async function copyDirectoryFileUrls(repoName, directoryPath) {
  187. try {
  188. // Get the files in the current directory
  189. const url = directoryPath ?
  190. `https://huggingface.co/api/models/${repoName}/tree/main/${directoryPath}?recursive=false` :
  191. `https://huggingface.co/api/models/${repoName}/tree/main?recursive=false`;
  192.  
  193. const response = await fetch(url);
  194. if (!response.ok) {
  195. throw new Error(`Failed to fetch directory contents: ${response.status}`);
  196. }
  197.  
  198. const items = await response.json();
  199.  
  200. // Filter only files (not directories)
  201. const filesOnly = items.filter(item => item.type === 'file');
  202.  
  203. if (filesOnly.length === 0) {
  204. console.log('No files found in this directory.');
  205. return;
  206. }
  207.  
  208. // Generate download URLs for each file
  209. const fileUrls = [];
  210. filesOnly.forEach(file => {
  211. const filePath = file.path;
  212. // Create direct download URL
  213. const downloadUrl = `https://huggingface.co/${repoName}/resolve/main/${filePath}`;
  214. fileUrls.push(downloadUrl);
  215. });
  216.  
  217. // Copy to clipboard
  218. const urlsText = fileUrls.join('\n');
  219. navigator.clipboard.writeText(urlsText).then(() => {
  220. console.log('Download URLs copied to clipboard:', urlsText);
  221.  
  222. // Create a temporary notification element
  223. const notification = document.createElement('div');
  224. notification.style.cssText = `
  225. position: fixed;
  226. bottom: 40px;
  227. right: 10px;
  228. background: rgba(0, 0, 0, 0.8);
  229. color: #00ff00;
  230. padding: 8px 12px;
  231. border-radius: 6px;
  232. font-size: 12px;
  233. font-family: monospace;
  234. z-index: 10001;
  235. opacity: 1;
  236. transition: opacity 0.5s;
  237. pointer-events: none;
  238. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
  239. `;
  240. notification.textContent = '✓ URLs copied to clipboard!';
  241. document.body.appendChild(notification);
  242.  
  243. // Fade out and remove after 2 seconds
  244. setTimeout(() => {
  245. notification.style.opacity = '0';
  246. setTimeout(() => {
  247. if (notification.parentNode) {
  248. document.body.removeChild(notification);
  249. }
  250. }, 500);
  251. }, 2000);
  252. }).catch(err => {
  253. console.log('Failed to copy URLs to clipboard:', err);
  254. });
  255.  
  256. } catch (error) {
  257. console.log('Error generating download URLs:', error);
  258. }
  259. }
  260.  
  261. // Update the display with sizes
  262. function updateSizeDisplay(display, repoSize, dirSize) {
  263. const repoFormatted = formatBytes(repoSize);
  264. const dirFormatted = formatBytes(dirSize);
  265.  
  266. display.textContent = `📁 ${dirFormatted} | 💾 ${repoFormatted}`;
  267.  
  268. // Add hover effect for visibility
  269. display.style.opacity = '1';
  270. display.onmouseenter = () => display.style.opacity = '1';
  271. display.onmouseleave = () => display.style.opacity = '0.8';
  272. }
  273.  
  274. // Main function to calculate and display sizes
  275. async function calculateAndDisplaySizes() {
  276. const repoInfo = getRepoInfo();
  277. if (!repoInfo) return;
  278.  
  279. const display = document.getElementById('hf-size-display') || createSizeDisplay();
  280.  
  281. // Add click event listener to copy download URLs
  282. display.onclick = async () => {
  283. await copyDirectoryFileUrls(repoInfo.repoFullName, repoInfo.path);
  284. };
  285.  
  286. display.textContent = 'Loading...';
  287.  
  288. try {
  289. // Calculate repository size (includes recursive directory sizes)
  290. const repoSize = await getRepoSize(repoInfo.repoFullName);
  291.  
  292. // Calculate current directory size (only files in current directory)
  293. const dirSize = await getDirectorySizeCurrent(repoInfo.repoFullName, repoInfo.path);
  294.  
  295. // Update display
  296. updateSizeDisplay(display, repoSize, dirSize);
  297. } catch (error) {
  298. console.log('Error calculating sizes:', error);
  299. display.textContent = 'Error loading sizes';
  300. }
  301. }
  302.  
  303. // Run the calculation after a short delay
  304. setTimeout(() => {
  305. calculateAndDisplaySizes();
  306. }, 500);
  307.  
  308. })();
  309.  
Advertisement
Add Comment
Please, Sign In to add comment