VSCode Extension Downloader

在VS Code marketplace添加VSIX下载按钮

  1. // ==UserScript==
  2. // @name VSCode Extension Downloader
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1
  5. // @description 在VS Code marketplace添加VSIX下载按钮
  6. // @author microchang
  7. // @match https://marketplace.visualstudio.com/items*
  8. // @grant none
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. // 创建一个悬浮按钮
  16. function createFloatingButton() {
  17. const button = document.createElement('button');
  18. button.innerHTML = 'download';
  19. button.style.position = 'fixed';
  20. button.style.bottom = '20px';
  21. button.style.right = '20px';
  22. button.style.zIndex = '9999';
  23. button.style.padding = '10px 20px';
  24. button.style.backgroundColor = '#0078D4';
  25. button.style.color = 'white';
  26. button.style.border = 'none';
  27. button.style.borderRadius = '4px';
  28. button.style.cursor = 'pointer';
  29. button.style.fontWeight = 'bold';
  30. button.style.boxShadow = '0 2px 5px rgba(0,0,0,0.2)';
  31.  
  32. // 悬停效果
  33. button.onmouseover = function() {
  34. this.style.backgroundColor = '#106EBE';
  35. };
  36. button.onmouseout = function() {
  37. this.style.backgroundColor = '#0078D4';
  38. };
  39.  
  40. // 点击事件处理
  41. button.onclick = function() {
  42. // 执行第一个下载脚本
  43. executeFirstScript();
  44. // 延迟1秒后执行第二个脚本作为备选
  45. setTimeout(executeSecondScript, 1000);
  46. };
  47.  
  48. document.body.appendChild(button);
  49. }
  50.  
  51. // 实现第一个下载脚本
  52. function executeFirstScript() {
  53. const extensionData = {
  54. version: "",
  55. publisher: "",
  56. identifier: "",
  57. getDownloadUrl: function() {
  58. // 添加调试日志
  59. console.log('Debug info:', {
  60. version: this.version,
  61. publisher: this.publisher,
  62. identifier: this.identifier
  63. });
  64.  
  65. const publisher = this.publisher.replace('@', '');
  66. const extension = this.identifier.split('.')[1];
  67.  
  68. // 验证数据是否存在
  69. if (!publisher || !extension || !this.version) {
  70. console.error('Missing required data:', {publisher, extension, version: this.version});
  71. return null;
  72. }
  73.  
  74. return `https://${publisher}.gallery.vsassets.io/_apis/public/gallery/publisher/${publisher}/extension/${extension}/${this.version}/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage`;
  75. },
  76. getFileName: function() {
  77. return `${this.identifier}_${this.version}.vsix`;
  78. }
  79. };
  80.  
  81. const metadataMap = {
  82. Version: "version",
  83. Publisher: "publisher",
  84. "Unique Identifier": "identifier"
  85. };
  86.  
  87. // 修改选择器以确保能找到正确的元素
  88. const metadataRows = document.querySelectorAll("table.ux-table-metadata tr, .ux-table-metadata tr");
  89.  
  90. let foundData = false;
  91. for (let i = 0; i < metadataRows.length; i++) {
  92. const row = metadataRows[i];
  93. const cells = row.querySelectorAll("td");
  94. if (cells.length === 2) {
  95. const key = cells[0].innerText.trim();
  96. const value = cells[1].innerText.trim();
  97. if (metadataMap.hasOwnProperty(key)) {
  98. extensionData[metadataMap[key]] = value;
  99. foundData = true;
  100. console.log(`Found ${key}: ${value}`);
  101. }
  102. }
  103. }
  104.  
  105. // 验证是否成功获取到数据
  106. if (!foundData) {
  107. console.error('Failed to find metadata in the page');
  108. return;
  109. }
  110.  
  111. // 获取下载URL
  112. const downloadUrl = extensionData.getDownloadUrl();
  113. if (!downloadUrl) {
  114. console.error('Failed to generate download URL');
  115. return;
  116. }
  117.  
  118. // 创建下载链接并触发下载
  119. const link = document.createElement("a");
  120. link.href = downloadUrl;
  121. link.download = extensionData.getFileName();
  122. link.click();
  123. }
  124.  
  125. // 实现第二个下载脚本(作为备选方案)
  126. function executeSecondScript() {
  127. const URL_VSIX_PATTERN = 'https://marketplace.visualstudio.com/_apis/public/gallery/publishers/${publisher}/vsextensions/${extension}/${version}/vspackage';
  128. const itemName = new URL(window.location.href).searchParams.get('itemName');
  129. if (!itemName) return;
  130.  
  131. const [publisher, extension] = itemName.split('.');
  132. const versionElement = document.querySelector('#versionHistoryTab tbody tr .version-history-container-column');
  133. if (!versionElement) return;
  134.  
  135. const version = versionElement.textContent;
  136. const url = URL_VSIX_PATTERN
  137. .replace('${publisher}', publisher)
  138. .replace('${extension}', extension)
  139. .replace('${version}', version);
  140.  
  141. window.open(url, '_blank');
  142. }
  143.  
  144. // 等待页面加载完成后创建按钮
  145. window.addEventListener('load', function() {
  146. createFloatingButton();
  147. });
  148. })();
  149.