A simple user script to show vsix download button from vs-code extension marketplace. It will allow to download a file with .vsix
// ==UserScript==
// @name VSIX download button
// @namespace http://tampermonkey.net/
// @version 2025-02-05
// @description A simple user script to show vsix download button from vs-code extension marketplace. It will allow to download a file with .vsix
// @author dnh2025
// @icon https://www.google.com/s2/favicons?sz=64&domain=githubusercontent.com
// @match https://marketplace.visualstudio.com/*
// @grant none
// @sources https://github.com/mjmirza/Download-VSIX-From-Visual-Studio-Market-Place/blob/main/downloadVSIX-V2.js
// @license MIT
// ==/UserScript==
(function () {
'use strict';
/***
// First Script: Extracts extension data and creates a download button
***/
// Object to store extension data (version, publisher, identifier)
const extensionData = {
version: "",
publisher: "",
identifier: "",
// Function to get the download URL for the VSIX file
getDownloadUrl: function () {
return `https://${this.identifier.split(".")[0]}.gallery.vsassets.io/_apis/public/gallery/publisher/${this.identifier.split(".")[0]}/extension/${this.identifier.split(".")[1]}/${this.version}/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage`;
},
// Function to get the filename for the downloaded VSIX file
getFileName: function () {
return `${this.identifier}_${this.version}.vsix`;
},
// Function to create the download button element
getDownloadButton: function () {
const button = document.createElement("a");
button.className = 'vsix-download-button';
button.innerHTML = "Download VSIX";
button.style.fontFamily = "wf_segoe-ui,Helvetica Neue,Helvetica,Arial,Verdana";
button.style.display = "inline-block";
button.style.padding = "10px 20px"; // Increased padding for a bigger button
button.style.background = "darkgreen";
button.style.color = "white";
button.style.fontWeight = "bold";
button.style.fontSize = "16px"; // Increased font size
button.style.margin = "2px 5px";
button.style.textDecoration = "none"; // Remove default link underline
// Store the download URL and filename in data attributes
button.setAttribute("data-download-url", this.getDownloadUrl());
button.setAttribute("data-download-filename", this.getFileName());
// Event handler for when the button is clicked
button.onclick = function (event) {
const downloadUrl = event.target.getAttribute("data-download-url");
const downloadFilename = event.target.getAttribute("data-download-filename");
/**
* This will not correct filename on chrome (edge)
*/
// // Create a temporary link element
// const link = document.createElement("a");
// link.href = downloadUrl;
// link.download = downloadFilename;
// // Trigger the download
// link.click();
//force filename with xhr blob
window.URL = window.URL || window.webkitURL;
let xhr = new XMLHttpRequest(),
a = document.createElement('a'), file;
xhr.open('GET', downloadUrl, true);
xhr.responseType = 'blob';
xhr.onload = function () {
file = new Blob([xhr.response], { type: 'application/octet-stream' });
a.href = window.URL.createObjectURL(file);
a.download = downloadFilename; // Set to whatever file name you want
// Now just click the link you created
// Note that you may have to append the a element to the body somewhere
// for this to work in Firefox
a.click();
};
xhr.send();
};
return button;
}
};
const start = async () => {
// Map to associate metadata table headers with extension data keys
const metadataMap = {
version: "version",
publisher: "publisher",
"unique-identifier": "identifier"
};
// Select all rows in the metadata table
let metadataValues = document.querySelectorAll(".ux-table-metadata tr td[role='definition']");
if (metadataValues.length === 0) { setTimeout(start, 500); return; }
// Iterate through each row to extract extension data
for (let i = 0; i < metadataValues.length; i++) {
const cell = metadataValues[i];
if (cell.getAttribute("aria-labelledby")) {
const key = cell.getAttribute("aria-labelledby").trim();
const value = cell.innerText.trim();
if (metadataMap.hasOwnProperty(key)) {
extensionData[metadataMap[key]] = value;
}
}
}
// Find the element with the class ".ux-oneclick-install-button-container"
const oneclickInstallButtonContainer = document.querySelector(".ux-oneclick-install-button-container");
if (oneclickInstallButtonContainer) {
// Append the download button to the parent element
oneclickInstallButtonContainer.appendChild(extensionData.getDownloadButton());
//có thể sẽ bị block bởi ads-blocker
//thêm 1 setTimeout kiểm tra 1 lần sau 1s
const checkLast = () => {
if (!document.querySelector('.vsix-download-button')) {
setTimeout(checkLast, 1000);
document.querySelector(".ux-oneclick-install-button-container")?.appendChild(extensionData.getDownloadButton());
};
}
setTimeout(checkLast, 1000);
} else {
console.error("Element with class 'ux-oneclick-install-button-container' not found.");
}
}
start();
})();