Backup GitHub Repository

Backup GitHub repositories directly from user profile page

  1. // ==UserScript==
  2. // @name Backup GitHub Repository
  3. // @namespace Violentmonkey Scripts
  4. // @version 2.1
  5. // @description Backup GitHub repositories directly from user profile page
  6. // @author maanimis
  7. // @match https://github.com/*?tab=repositories
  8. // @grant GM_registerMenuCommand
  9. // @grant GM_download
  10. // @run-at document-end
  11. // @license MIT
  12. // @require https://update.greatest.deepsurf.us/scripts/530526/1558038/ProgressUI-Module.js
  13. // ==/UserScript==
  14.  
  15. (function() {
  16. 'use strict';
  17.  
  18. function extractBaseUrl(urlString = location.href) {
  19. const urlObj = new URL(urlString);
  20. return `${urlObj.origin}${urlObj.pathname.split("/").slice(0, 2).join("/")}`;
  21. }
  22.  
  23. function extractRepoData() {
  24. const ulElement = document.querySelector('ul[data-filterable-for="your-repos-filter"][data-filterable-type="substring"]');
  25. if (!ulElement){
  26. alert("ulElement not found!!")
  27. return
  28. }
  29. const liElements = ulElement.querySelectorAll("li");
  30. const liArray = Array.from(liElements);
  31. return liArray.map(li => {
  32. const repoLink = li.querySelector('a[itemprop="name codeRepository"]');
  33. if (!repoLink) return null;
  34. const repository = repoLink.href;
  35.  
  36. const name = repoLink.textContent.trim();
  37.  
  38. const language = li.querySelector('span[itemprop="programmingLanguage"]')?.textContent.trim();
  39.  
  40. const lastUpdated = li.querySelector("relative-time")?.getAttribute("datetime");
  41.  
  42. const isPublic = li.querySelector(".Label.Label--secondary")?.textContent.trim() === "Public";
  43. const downloadLinks = [
  44. `${repository}/archive/refs/heads/master.zip`,
  45. `${repository}/archive/refs/heads/main.zip`
  46. ];
  47. return {
  48. name,
  49. language,
  50. lastUpdated,
  51. isPublic,
  52. repository,
  53. downloadLinks
  54. };
  55. }).filter(Boolean);
  56. }
  57.  
  58. function downloadRepos() {
  59. const repos = extractRepoData();
  60. if (repos.length === 0) {
  61. ProgressUI.showQuick('No repositories found on this page', {
  62. percent: 100,
  63. theme: 'dark',
  64. duration: 3000
  65. });
  66. return;
  67. }
  68.  
  69. const progress = new ProgressUI({
  70. position: 'bottom-right',
  71. theme: 'dark',
  72. title: 'GitHub Repository Downloader',
  73. closable: true,
  74. width: '350px',
  75. });
  76.  
  77. let downloadedCount = 0;
  78. let failedCount = 0;
  79. const totalCount = repos.length;
  80.  
  81. progress.update(`Starting download of ${totalCount} repositories...`, 0);
  82.  
  83. repos.forEach((repo, index) => {
  84. const mainZipUrl = repo.downloadLinks[1];
  85. const masterZipUrl = repo.downloadLinks[0];
  86. setTimeout(() => {
  87. progress.update(`Downloading ${repo.name} (${index + 1}/${totalCount})...`, (index / totalCount) * 100);
  88. GM_download({
  89. url: mainZipUrl,
  90. name: `${repo.name}-main.zip`,
  91. onload: () => {
  92. downloadedCount++;
  93. updateProgressStatus(progress, downloadedCount, failedCount, totalCount);
  94. },
  95. onerror: () => {
  96. progress.update(`Trying master branch for ${repo.name}...`, (index / totalCount) * 100);
  97. GM_download({
  98. url: masterZipUrl,
  99. name: `${repo.name}-master.zip`,
  100. onload: () => {
  101. downloadedCount++;
  102. updateProgressStatus(progress, downloadedCount, failedCount, totalCount);
  103. },
  104. onerror: () => {
  105. failedCount++;
  106. progress.update(`Failed to download ${repo.name}`, (index / totalCount) * 100);
  107. updateProgressStatus(progress, downloadedCount, failedCount, totalCount);
  108. }
  109. });
  110. }
  111. });
  112. }, index * 1000);
  113. });
  114. }
  115.  
  116. function updateProgressStatus(progress, downloaded, failed, total) {
  117. const completed = downloaded + failed;
  118. const percent = Math.round((completed / total) * 100);
  119. if (completed === total) {
  120. if (failed === 0) {
  121. progress.update(`All ${total} repositories downloaded successfully!`, 100);
  122. } else {
  123. progress.update(`Download completed: ${downloaded} successful, ${failed} failed`, 100);
  124. }
  125. progress.scheduleCleanup(5000);
  126. } else {
  127. progress.update(`Downloaded: ${downloaded}, Failed: ${failed}, Remaining: ${total - completed}`, percent);
  128. }
  129. }
  130.  
  131. GM_registerMenuCommand("Download GitHub Repositories", downloadRepos);
  132. })();