Save Amazon Music playlist as JSON file

This script adds an export button at Amazon Music playlist pages

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name               Save Amazon Music playlist as JSON file
// @name:fr            Save Amazon Music playlist as JSON file
// @name:de            Save Amazon Music playlist as JSON file
// @name:it            Save Amazon Music playlist as JSON file
// @name:es            Save Amazon Music playlist as JSON file
// @name:ja            Save Amazon Music playlist as JSON file
// @name:pt-BR         Save Amazon Music playlist as JSON file
// @namespace          https://github.com/projecteurlumiere/save-amazon-music-playlist
// @version            2024-05-15
// @description        This script adds an export button at Amazon Music playlist pages
// @description:fr     This script adds an export button at Amazon Music playlist pages
// @description:de     This script adds an export button at Amazon Music playlist pages
// @description:it     This script adds an export button at Amazon Music playlist pages
// @description:es     This script adds an export button at Amazon Music playlist pages
// @description:ja     This script adds an export button at Amazon Music playlist pages
// @description:pt-BR  This script adds an export button at Amazon Music playlist pages
// @author             projecteurlumiere
// @match              https://music.amazon.com/*
// @match              https://music.amazon.co.uk/*
// @match              https://music.amazon.fr/*
// @match              https://music.amazon.de/*
// @match              https://music.amazon.it/*
// @match              https://music.amazon.es/*
// @match              https://music.amazon.co.jp/*
// @match              https://music.amazon.ca/*
// @match              https://music.amazon.com.au/*
// @match              https://music.amazon.com.mx/*
// @match              https://music.amazon.com.br/*
// @match              https://music.amazon.in/*
// @grant              none
// @license            MIT
// ==/UserScript==

(function() {
  'use strict';

  whenPlaylistAvailable("music-container music-image-row").then(() => { insertButtons(); })

  async function whenPlaylistAvailable(selector) {
    while (document.querySelectorAll(selector).length === 0) {
      console.log("waiting for playlist");
      await new Promise(r => setTimeout(r, 1000));
    }
  }

  function insertButtons() {
    console.log("inserting buttons");

    document.querySelector("body").insertAdjacentHTML('beforeend', `
      <style>
        #download-playlist-via-script {
          position: fixed !important; 
          left: 45%; top: 75px;
        }

        #download-playlist-via-script button {
          padding: 15px 30px;
          background-color: rgb(168, 237, 240);
        }
      </style>
      <div id="download-playlist-via-script">
        <button>Download playlist</button>
        <button>Scroll ↓</button>
        <button>Scroll ↑</button>
      </div>
    `.trim()
    )

    const firstButton = "#download-playlist-via-script button:nth-child(1)"
    const secondButton = "#download-playlist-via-script button:nth-child(2)"
    const thirdButton = "#download-playlist-via-script button:nth-child(3)"

    document.querySelector(firstButton).addEventListener("click", () => {
      try {
        getPlaylist()
      }
      catch {
        if (window.location.href.includes("playlist")) {
          alert("Error! Perhaps, your viewport is not wide enough: " +
          "try to maximize your window and make sure album names are positioned next to song durations, and not below song titles")
        }
        else {
          alert("Error! Make sure you're on the playlist page you want to export (albums are not supported)");
        }
      }
    })

    document.querySelector(secondButton).addEventListener("click", () => {
      window.scrollTo(0, document.body.scrollHeight);
    })

   document.querySelector(thirdButton).addEventListener("click", () => {
      window.scrollTo(0, 0);
    })
  }

  function getPlaylist() {
    const entries =
      Array.from(document.querySelectorAll("music-container music-image-row"));

    let playlist = [];
    let n_total = 0;

    entries.forEach((e) => {
      playlist.push({
        index: parseInt(e.querySelector(".index").innerText),
        title: e.querySelector(".col1 > music-link").title,
        artist: e.querySelector(".col2 > music-link").title,
        album: e.querySelector(".col3 > music-link").title,
        duration: e.querySelector(".col4 > music-link").title
      })
      n_total += 1
    })

    const n_first = playlist[0].index
    const n_last = playlist[playlist.length - 1].index

    let pretty_json = JSON.stringify(playlist, null, 2);
    download(pretty_json, "application/json", composeName(n_first, n_last, n_total));
  }

  function download(content, mimeType, filename) {
    const a = document.createElement('a') // Create "a" element
    const blob = new Blob([content], {type: mimeType}) // Create a blob (file-like object)
    const url = URL.createObjectURL(blob) // Create an object URL from blob
    a.setAttribute('href', url) // Set "a" element link
    a.setAttribute('download', filename) // Set download filename
    a.click() // Start downloading
    a.remove()
  }

  function composeName(n_first, n_last, n_total) {
    const header = document.querySelector("music-detail-header[label]").shadowRoot;
    const playlistType =
          capitalize(header.querySelector(".label").innerText.toLowerCase());
    const playlistTitle =
          header.querySelector("[title]").innerText

    if (n_first != 1) { 
      alert("The first song(s) has/have not been exported. Try to scroll to the top of the page and retry exporting" +
        "(this way the song(s) on the top of the page should become visible for exporting)")
    }

    if (n_last % 500 === 0) {
      alert("Your playlist may be incomplete: scroll to the bottom of the page until you get the entire playlist")
    }

    return `${playlistType} - ${playlistTitle} - ${n_total} song(s) in total - from ${n_first} to ${n_last}`
  }

  function capitalize(str) {
    return str.charAt(0).toUpperCase() + str.slice(1)
  }
})();