Bandcamp download mp3s

Shows download links for the preview files on bandcamp albums

スクリプトをインストール?
作者が勧める他のスクリプト

Bandcamp script (Deluxe Edition)も気に入るかもしれません。

スクリプトをインストール
このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name         Bandcamp download mp3s
// @description  Shows download links for the preview files on bandcamp albums
// @namespace    cuzi
// @oujs:author  cuzi
// @homepageURL  https://openuserjs.org/scripts/cuzi/Bandcamp_download_mp3s
// @version      9
// @license      GPL-3.0-or-later; http://www.gnu.org/licenses/gpl-3.0.txt
// @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 () {
  // Base64 function from https://gist.github.com/stubbetje/229984
  function base64BinaryString (s) {
    const base64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split('')
    const l = s.length
    const o = []
    let char0, char1, char2, char3
    let byte0, byte1, byte2
    let t
    for (let i = 0; i < l; i++) {
      byte0 = s.charCodeAt(i++) & 0xff
      byte1 = i < l ? s.charCodeAt(i++) & 0xff : 0
      byte2 = i < l ? s.charCodeAt(i) & 0xff : 0
      char0 = byte0 >> 2
      char1 = ((byte0 & 0x3) << 4) | (byte1 >> 4)
      char2 = ((byte1 & 0x0f) << 2) | (byte2 >> 6)
      char3 = byte2 & 0x3f
      t = i - l
      if (t === 1) {
        char3 = 64
      } else if (t === 2) {
        char3 = 64
        char2 = 64
      }
      o.push(base64[char0], base64[char1], base64[char2], base64[char3])
    }
    return o.join('')
  }

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

  const SPINNERCSS = '/* http://www.designcouch.com/home/why/2013/05/23/dead-simple-pure-css-loading-spinner/ */\n' +
    '.download-col .downloaddisk:hover {\n' +
    '  text-decoration:none\n' +
    '}\n' +
    '.downspinner {\n' +
    '  margin:0px auto;\n' +
    '  position:relative;\n' +
    '  display:inline-block;\n' +
    '  animation: spinnerrotation 3s infinite linear;\n' +
    '  cursor:wait;\n' +
    '}\n' +
    '@keyframes spinnerrotation {\n' +
    '  from {transform: rotate(0deg)}\n' +
    '  to {transform: rotate(359deg)}\n' +
    '}'

  function addSpinner (el) {
    el.style = ''
    el.classList.add('downspinner')
  }

  function removeSpinner (el) {
    el.classList.remove('downspinner')
    el.style = 'background:#1cea1c;border-radius:5px;padding:1px;opacity:0.5'
  }

  function download (ev) {
    const a = this
    const url = a.href

    if (GM_download) {
      // Use Tampermonkey GM_download function
      ev.preventDefault()
      addSpinner(a)
      GM_download({
        url,
        name: a.download,
        onerror: function () {
          window.alert('Could not download via GM_download')
          document.location.href = url
        },
        ontimeout: function () {
          window.alert('Could not download via GM_download. Time out.')
          document.location.href = url
        },
        onload: function () {
          window.setTimeout(() => removeSpinner(a), 500)
        }

      })
      return
    }

    if (!url.startsWith('http') || navigator.userAgent.indexOf('Chrome') !== -1) {
      // Just open the link normally (no prevent default)
      addSpinner(a)
      window.setTimeout(() => removeSpinner(a), 1000)
      return
    }

    // Use GM.xmlHttpRequest to download and offer data uri
    ev.preventDefault()

    addSpinner(a)

    GM.xmlHttpRequest({
      method: 'GET',
      overrideMimeType: 'text/plain; charset=x-user-defined',
      url,
      onload: function (response) {
        a.href = 'data:audio/mpeg;base64,' + base64BinaryString(response.responseText)
        window.setTimeout(() => a.click(), 10)
      },
      onerror: function (response) {
        window.alert('Could not download via GM.xmlHttpRequest')
        document.location.href = url
      }
    })
  }

  const style = document.createElement('style')
  style.appendChild(document.createTextNode(SPINNERCSS))
  document.head.appendChild(style)

  window.setTimeout(function () {
    const TralbumData = unsafeWindow.TralbumData
    if (TralbumData && TralbumData.hasAudio && !TralbumData.freeDownloadPage && TralbumData.trackinfo) {
      let hoverdiv = document.querySelectorAll('.download-col div')
      if (hoverdiv.length === 0) {
        hoverdiv = document.querySelectorAll('h2.trackTitle')
      }
      for (let i = 0; i < TralbumData.trackinfo.length; i++) {
        const t = TralbumData.trackinfo[i]
        for (const prop in t.file) {
          const mp3 = t.file[prop].replace(/^\/\//, 'http://')
          const a = document.createElement('a')
          a.className = 'downloaddisk'
          a.href = mp3
          a.download = (t.track_num > 9 ? '' : '0') + t.track_num + '. ' + fixFilename(TralbumData.artist + ' - ' + t.title) + '.mp3'
          a.title = 'Download ' + prop
          a.appendChild(document.createTextNode('\uD83D\uDCBE'))
          a.addEventListener('click', download)
          hoverdiv[i].appendChild(a)
          break
        }
      }
    }
  }, 500)
})()