// ==UserScript== // @name GitHub Download ZIP // @version 0.2.8 // @description A userscript adds download links so that downloaded filenames include the SHA // @license MIT // @author Rob Garrison // @namespace https://github.com/Mottie // @match https://github.com/* // @run-at document-idle // @grant GM_addStyle // @grant GM.addStyle // @grant GM_xmlhttpRequest // @grant GM.xmlHttpRequest // @connect api.github.com // @connect assets-cdn.github.com // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js?updated=20180103 // @require https://greasyfork.org/scripts/28721-mutations/code/mutations.js?version=1108163 // @icon https://github.githubassets.com/pinned-octocat.svg // @updateURL https://raw.githubusercontent.com/Mottie/Github-userscripts/master/github-download-zip.user.js // @downloadURL https://raw.githubusercontent.com/Mottie/Github-userscripts/master/github-download-zip.user.js // @supportURL https://github.com/Mottie/GitHub-userscripts/issues // ==/UserScript== /* global GM */ (() => { "use strict"; GM.addStyle(` .ghdz-releases { width:100% !important; padding:10px; } .ghdz-releases summary { text-align:left; padding-left:16px; } .ghdz-files.select-menu-modal { width:100%; border:0; box-shadow:none !important; margin-bottom:0; } .ghdz-file { text-align:left; padding-left:16px; } .commit-links-cell { min-width: 375px; width: auto; } `); const zipIcon = ` `; const span = document.createElement("span"); span.innerHTML = zipIcon; const link = document.createElement("a"); link.className = "btn btn-outline BtnGroup-item tooltipped tooltipped-s ghdz-btn"; link.setAttribute("aria-label", "Download ZIP"); link.innerHTML = zipIcon; const div = document.createElement("details"); div.className = "select-menu get-repo-btn ghdz-releases"; div.innerHTML = ` Latest Release Files
`; function buildURL(part) { const [, user, repo] = window.location.pathname.split("/"); return `https://api.github.com/repos/${user}/${repo}/zipball/${part}`; } function buildLink(url, text) { return ` ${text} `; } function buildReleases(result, container) { let html = `
${ typeof result === "string" ? result : "No release files or assets found" }
`; // Example page with release files & assets: // https://github.com/Maximus5/ConEmu/releases if ( Array.isArray(result) && result.length && Object.keys(result[0] || {}).length ) { html = ""; const last = result[0]; if (last.assets) { last.assets.forEach(release => { const url = release.browser_download_url; html += buildLink(url, url.split("/").slice(-1)); }); } html += buildLink(last.zipball_url, "Source code (zip)"); html += buildLink(last.tarball_url, "Source code (tar.gz)"); } $(".ghdz-files", container).innerHTML = html; } function getReleases(container) { if ($(".ghdz-file", container)) { // Already loaded return; } const [, user, repo] = window.location.pathname.split("/"); GM.xmlHttpRequest({ method : "GET", url : `https://api.github.com/repos/${user}/${repo}/releases`, onload : response => { if (response.status !== 200) { buildReleases(response.message, container); return console.error(response); } let json = false; try { json = JSON.parse(response.responseText); } catch (err) { return console.error(response); } if (json) { buildReleases(json, container); } } }); } function addBindings() { document.addEventListener("click", function(event) { const target = event.target.closest("details"); if (target && target.classList.contains("ghdz-releases")) { getReleases(target); } }); } function updateLinks() { // Branch dropdown on main repo page const branch = $("summary[data-hotkey='w'] span"); // Download link in "Clone or Download" dropdown const downloadLink = $("a[data-ga-click*='download zip']"); // Repo commits page const commits = $(".commits-listing"); if (downloadLink && branch && !$(".ghdz-releases", downloadLink.parentElement)) { const branchName = branch.textContent.indexOf("…") > -1 // Branch selector is showing trucated text; title has full text ? branch.parentNode.title : branch.textContent; downloadLink.href = buildURL(branchName.trim()); downloadLink.appendChild(span.cloneNode(true)); downloadLink.after(div.cloneNode(true)); downloadLink.parentElement.classList.add("flex-wrap"); } // Branch doesn't matter when you're using the SHA (first 7 values) if (commits) { [...document.querySelectorAll(".commit-group .commit .commit-links-group")].forEach(group => { if (!$(".ghdz-btn", group)) { const sha = $(".sha", group).textContent.trim(); const a = link.cloneNode(true); a.href = buildURL(sha); group.appendChild(a); } }); } } function $(selector, el) { return (el || document).querySelector(selector); } // DOM targets - to detect GitHub dynamic ajax page loading document.addEventListener("pjax:end", updateLinks); updateLinks(); addBindings(); })();