(async function() {
const buttons = document.querySelectorAll('.track-item .action button');
if (buttons.length === 0) {
console.error("No tracks found on the page! Make sure the playlist track list is loaded first.");
alert("Please paste the playlist link, click Download, and make sure the tracks are loaded before running the script!");
return;
}
console.log(`Found ${buttons.length} tracks. Starting download...`);
// Create or locate a visual status banner on the webpage so you don't have to keep Console open
let statusEl = document.getElementById('bulk-download-status');
if (!statusEl) {
statusEl = document.createElement('div');
statusEl.id = 'bulk-download-status';
statusEl.style.padding = '15px';
statusEl.style.margin = '20px auto';
statusEl.style.background = '#1e1e1e';
statusEl.style.border = '2px solid #1DB954';
statusEl.style.borderRadius = '12px';
statusEl.style.color = '#fff';
statusEl.style.maxWidth = '620px';
statusEl.style.textAlign = 'center';
statusEl.style.fontFamily = 'Figtree, sans-serif';
statusEl.style.fontSize = '15px';
statusEl.style.boxShadow = '0 4px 12px rgba(0,0,0,0.5)';
// Insert it right above the track list
const info = document.getElementById('info');
info.parentNode.insertBefore(statusEl, info);
}
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
for (let i = 0; i < buttons.length; i++) {
const btn = buttons[i];
let trackData;
try {
trackData = JSON.parse(decodeURIComponent(btn.dataset.track));
} catch (e) {
console.error(`Failed to parse track data for item ${i + 1}`, e);
continue;
}
if (!trackData.get_download_url) {
console.warn(`Skipping: ${trackData.name} (no download URL)`);
continue;
}
const progressMessage = `[${i + 1}/${buttons.length}] Downloading: "${trackData.name}" by ${trackData.artists}`;
console.log(progressMessage);
statusEl.innerHTML = `Bulk Downloading:
${i + 1} of ${buttons.length}
${trackData.name} - ${trackData.artists}`;
try {
const url = trackData.get_download_url.indexOf('http') === 0 ? trackData.get_download_url : (host + trackData.get_download_url);
const response = await apiGet(url);
if (response.result && response.result.status == 3 && response.result.audios && response.result.audios.length > 0) {
const audio = response.result.audios[0];
const downloadLink = audio.url.indexOf('http') === 0 ? audio.url : (host + audio.url);
// Create a temporary link to trigger the download
const a = document.createElement('a');
a.href = downloadLink;
// Suggest a clean, friendly name for the downloaded file
let cleanFilename = `${trackData.artists} - ${trackData.name}.mp3`;
a.download = cleanFilename;
a.style.display = 'none';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
} else {
console.error(`-> Error resolving track URL for ${trackData.name}`);
statusEl.innerHTML += `
Failed to get download URL for: ${trackData.name}`;
}
} catch (err) {
console.error(`-> Error:`, err);
statusEl.innerHTML += `
Error: ${err.message || err}`;
}
// Wait 10 seconds between downloads to avoid Cloudflare/API rate limits
await delay(10000);
}
console.log("All tracks processed!");
statusEl.innerHTML = `🎉 All Downloads Finished!
Processed ${buttons.length} tracks.`;
})();