Spotify Enhancer (Track Downloader) - Cross-Version

Integrate a download button for tracks on Spotify Web to download audio at 320kbps

  1. // ==UserScript==
  2. // @name Spotify Enhancer (Track Downloader) - Cross-Version
  3. // @description Integrate a download button for tracks on Spotify Web to download audio at 320kbps
  4. // @version 1.4
  5. // @match https://open.spotify.com/* // Adicionando a diretiva @match
  6. // @namespace https://greasyfork.org/users/1388220
  7. // ==/UserScript==
  8.  
  9. const API_HEADERS = {
  10. 'Host': 'api.spotifydown.com',
  11. 'Referer': 'https://spotifydown.com/',
  12. 'Origin': 'https://spotifydown.com',
  13. };
  14.  
  15. const BACKOFF_CONFIG = { initialDelay: 1000, maxDelay: 5000, factor: 2 };
  16.  
  17. const style = document.createElement('style');
  18. style.id = 'spotify-enhancer-320-styles';
  19. document.body.appendChild(style);
  20.  
  21. function applyStyles() {
  22. const otherDownloader = document.querySelector('.btn');
  23. style.innerHTML = `
  24. [role='grid'] { margin-left: ${otherDownloader ? '90px' : '50px'}; }
  25. [data-testid='tracklist-row'] { position: relative; }
  26. .btn-320 { /* button styles */ width: 40px; /* etc */ }`;
  27. }
  28.  
  29. function addDownloadButton(element, trackInfo, spotifyId) {
  30. if (element.querySelector('.btn-320')) return;
  31. const button = document.createElement('button');
  32. button.className = 'btn-320';
  33. button.onclick = function() { initiateDownload(spotifyId, trackInfo, this); };
  34. element.appendChild(button);
  35. }
  36.  
  37. function initiateDownload(spotifyId, trackInfo, button) {
  38. button.classList.add('loading');
  39. let delay = BACKOFF_CONFIG.initialDelay;
  40.  
  41. const downloadAttempt = function() {
  42. downloadTrack(spotifyId).then(response => {
  43. const link = document.createElement('a');
  44. link.href = response.link;
  45. link.download = `${trackInfo.title} - ${trackInfo.artist}.mp3`;
  46. document.body.appendChild(link);
  47. link.click();
  48. document.body.removeChild(link);
  49. button.classList.remove('loading');
  50. }).catch(() => {
  51. setTimeout(() => {
  52. delay = Math.min(delay * BACKOFF_CONFIG.factor, BACKOFF_CONFIG.maxDelay);
  53. downloadAttempt(); // Retry download attempt
  54. }, delay);
  55. });
  56. };
  57. downloadAttempt(); // Start the first download attempt
  58. }
  59.  
  60. function downloadTrack(spotifyId) {
  61. return new Promise((resolve, reject) => {
  62. GM_xmlhttpRequest({
  63. method: "GET",
  64. url: `https://api.spotifydown.com/download/${spotifyId}`,
  65. headers: API_HEADERS,
  66. onload: function(response) {
  67. try {
  68. const result = JSON.parse(response.responseText);
  69. if (result.success) {
  70. resolve(result);
  71. } else {
  72. reject(new Error('Download failed'));
  73. }
  74. } catch (error) {
  75. reject(new Error('Failed to parse response'));
  76. }
  77. },
  78. onerror: reject
  79. });
  80. });
  81. }
  82.  
  83. function extractTrackInfo(trackElement) {
  84. const title = trackElement.querySelector('[data-encore-id="text"][dir="auto"]').textContent.trim();
  85. const artist = Array.from(trackElement.querySelectorAll('a[href^="/artist"]')).map(a => a.textContent.trim()).join(', ');
  86. return { title, artist };
  87. }
  88.  
  89. function attachButtons() {
  90. const tracks = document.querySelectorAll('[data-testid="tracklist-row"]');
  91. tracks.forEach(track => {
  92. const spotifyId = track.querySelector('[href^="/track"]').href.split('/').pop();
  93. const trackInfo = extractTrackInfo(track);
  94. addDownloadButton(track, trackInfo, spotifyId);
  95. });
  96. }
  97.  
  98. new MutationObserver(function() {
  99. applyStyles();
  100. attachButtons();
  101. }).observe(document.body, { childList: true, subtree: true });