Bandcamp download mp3s (single + bulk)

Adds per-track and bulk download links for Bandcamp preview mp3s

// ==UserScript==
// @name         Bandcamp download mp3s (single + bulk)
// @description  Adds per-track and bulk download links for Bandcamp preview mp3s
// @namespace    83BandcampScript
// @version      2
// @license      GPL-3.0-or-later
// @match        https://*.bandcamp.com/*
// @grant        unsafeWindow
// @grant        GM.xmlHttpRequest
// @grant        GM_download
// @connect      bandcamp.com
// @connect      bcbits.com
// @icon         https://bandcamp.com/img/favicon/apple-touch-icon.png
// ==/UserScript==

/* globals GM, unsafeWindow, GM_download */
/* jshint asi: true, esversion: 8 */

(function () {
  function fixFilename (s) {
    const forbidden = '*"/\\[]:|,<>?\n\t\0'.split('')
    forbidden.forEach(char => { s = s.replace(char, '') })
    return s
  }

  function addTrackDownload (hoverdiv, t, albumArtist) {
    if (!t.file || !t.file['mp3-128']) return null
    const mp3 = t.file['mp3-128'].replace(/^\/\//, 'https://')
    const filename =
      (t.track_num > 9 ? '' : '0') +
      t.track_num +
      '. ' +
      fixFilename(albumArtist + ' - ' + t.title) +
      '.mp3'

    const a = document.createElement('a')
    a.className = 'downloaddisk'
    a.href = mp3
    a.download = filename
    a.textContent = '💾'
    a.addEventListener('click', function (ev) {
      ev.preventDefault()
      GM_download({ url: mp3, name: filename })
    })

    hoverdiv.appendChild(a)
    return { url: mp3, filename }
  }

  function addBulkButton (allTracks, albumTitle) {
    const btn = document.createElement('a')
    btn.textContent = '🔗💾'
    btn.style.marginLeft = '10px'
    btn.style.cursor = 'pointer'
    btn.style.fontSize = '1.2em'
    btn.title = 'Download all tracks'

    btn.addEventListener('click', (ev) => {
      ev.preventDefault()
      let delay = 0
      for (const track of allTracks) {
        setTimeout(() => {
          GM_download({ url: track.url, name: track.filename })
        }, delay)
        delay += 1000
      }
    })

    document.querySelector('.trackTitle')?.parentNode.prepend(btn)
  }

  setTimeout(function () {
    const TralbumData = unsafeWindow.TralbumData
    if (!TralbumData || !TralbumData.hasAudio || !TralbumData.trackinfo) return

    let hoverdivs = document.querySelectorAll('.download-col div')
    if (hoverdivs.length === 0) {
      hoverdivs = document.querySelectorAll('h2.trackTitle')
    }

    const allTracks = []
    for (let i = 0; i < TralbumData.trackinfo.length; i++) {
      const track = TralbumData.trackinfo[i]
      const entry = addTrackDownload(hoverdivs[i], track, TralbumData.artist)
      if (entry) allTracks.push(entry)
    }

    if (allTracks.length > 0) {
      addBulkButton(allTracks, TralbumData.current.title)
    }
  }, 500)
})()

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址