Skip to content

Instantly share code, notes, and snippets.

@maanimis
Created April 10, 2025 16:47
Show Gist options
  • Save maanimis/b14985613dbb7f605c6febe8425474a8 to your computer and use it in GitHub Desktop.
Save maanimis/b14985613dbb7f605c6febe8425474a8 to your computer and use it in GitHub Desktop.
Backup GitHub repositories directly from user profile page
// ==UserScript==
// @name Backup GitHub Repository
// @namespace Violentmonkey Scripts
// @version 2.1
// @description Backup GitHub repositories directly from user profile page
// @author maanimis
// @match https://github.com/*?tab=repositories
// @grant GM_registerMenuCommand
// @grant GM_download
// @run-at document-end
// @license MIT
// @require https://update.greasyfork.org/scripts/530526/1558038/ProgressUI-Module.js
// ==/UserScript==
(function () {
"use strict";
function extractBaseUrl(urlString = location.href) {
const urlObj = new URL(urlString);
return `${urlObj.origin}${urlObj.pathname
.split("/")
.slice(0, 2)
.join("/")}`;
}
function extractRepoData() {
const ulElement = document.querySelector(
'ul[data-filterable-for="your-repos-filter"][data-filterable-type="substring"]'
);
if (!ulElement) {
alert("ulElement not found!!");
return;
}
const liElements = ulElement.querySelectorAll("li");
const liArray = Array.from(liElements);
return liArray
.map((li) => {
const repoLink = li.querySelector('a[itemprop="name codeRepository"]');
if (!repoLink) return null;
const repository = repoLink.href;
const name = repoLink.textContent.trim();
const language = li
.querySelector('span[itemprop="programmingLanguage"]')
?.textContent.trim();
const lastUpdated = li
.querySelector("relative-time")
?.getAttribute("datetime");
const isPublic =
li.querySelector(".Label.Label--secondary")?.textContent.trim() ===
"Public";
const downloadLinks = [
`${repository}/archive/refs/heads/master.zip`,
`${repository}/archive/refs/heads/main.zip`,
];
return {
name,
language,
lastUpdated,
isPublic,
repository,
downloadLinks,
};
})
.filter(Boolean);
}
function downloadRepos() {
const repos = extractRepoData();
if (repos.length === 0) {
ProgressUI.showQuick("No repositories found on this page", {
percent: 100,
theme: "dark",
duration: 3000,
});
return;
}
const progress = new ProgressUI({
position: "bottom-right",
theme: "dark",
title: "GitHub Repository Downloader",
closable: true,
width: "350px",
});
let downloadedCount = 0;
let failedCount = 0;
const totalCount = repos.length;
progress.update(`Starting download of ${totalCount} repositories...`, 0);
repos.forEach((repo, index) => {
const mainZipUrl = repo.downloadLinks[1];
const masterZipUrl = repo.downloadLinks[0];
setTimeout(() => {
progress.update(
`Downloading ${repo.name} (${index + 1}/${totalCount})...`,
(index / totalCount) * 100
);
GM_download({
url: mainZipUrl,
name: `${repo.name}-main.zip`,
onload: () => {
downloadedCount++;
updateProgressStatus(
progress,
downloadedCount,
failedCount,
totalCount
);
},
onerror: () => {
progress.update(
`Trying master branch for ${repo.name}...`,
(index / totalCount) * 100
);
GM_download({
url: masterZipUrl,
name: `${repo.name}-master.zip`,
onload: () => {
downloadedCount++;
updateProgressStatus(
progress,
downloadedCount,
failedCount,
totalCount
);
},
onerror: () => {
failedCount++;
progress.update(
`Failed to download ${repo.name}`,
(index / totalCount) * 100
);
updateProgressStatus(
progress,
downloadedCount,
failedCount,
totalCount
);
},
});
},
});
}, index * 1000);
});
}
function updateProgressStatus(progress, downloaded, failed, total) {
const completed = downloaded + failed;
const percent = Math.round((completed / total) * 100);
if (completed === total) {
if (failed === 0) {
progress.update(
`All ${total} repositories downloaded successfully!`,
100
);
} else {
progress.update(
`Download completed: ${downloaded} successful, ${failed} failed`,
100
);
}
progress.scheduleCleanup(5000);
} else {
progress.update(
`Downloaded: ${downloaded}, Failed: ${failed}, Remaining: ${
total - completed
}`,
percent
);
}
}
GM_registerMenuCommand("Download GitHub Repositories", downloadRepos);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment