// ==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.greatest.deepsurf.us/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);
})();