secuvera / SpotMyBackup

Backup and Restore your Spotify Playlists and "My Music"
GNU General Public License v2.0
983 stars 246 forks source link

Update #59

Open Cyb10101 opened 1 year ago

Cyb10101 commented 1 year ago

I know this is a big pull request, but I think it should work. If something is unclear, just ask.

I changed the configuration to a JSON file. This makes it pretty easy to fetch and overwrite with the default configuration. It's probably easier to read the README.md file before writing this.

When a user opens the page via file://, a message is displayed: 0

If the configuration was not loaded: 1

Otherwise the usual prompt for the login: 2

If the user has previously logged in, the accessToken in the session storage is used. I saved myself from having to renew it and just let the login be displayed instead: 3

The export is started automatically and what is being done is shown step by step. If something goes wrong, an error message should be displayed and possibly more in the log file. Otherwise there is the export as a download and an import file button: 4

When the import file is loaded, you can see a few weirdos just like in the export: 5

...and when it's done: 6

I haven't built in a function to align the export with the import. It's probably safer to just reload the page.

The full export/import file looks something like this:

{
  "playlists": [
    {
      "name": "Playlist name",
      "description": "Playlist description",
      "public": true,
      "collaborative": false,
      "id": "Playlist...Id",
      "tracks": [
        {
          "id": "1mzGywacjpeik00PVLBPpF",
          "uri": "spotify:track:1mzGywacjpeik00PVLBPpF",
          "name": "All of Me - Tiësto's Birthday Treatment Remix - Radio Edit",
          "album": "All of Me (Tiësto's Birthday Treatment Remix - Radio Edit)",
          "artists": [
            "John Legend",
            "Jason Agel",
            "Tiësto"
          ]
        }
      ]
    }
  ],
  "saved": [
    {
      "id": "5QjRCSuTa9HLD9Fxnuth16",
      "uri": "spotify:track:5QjRCSuTa9HLD9Fxnuth16",
      "name": "Just shortened..."
    }
  ],
  "starred": [
    {
      "id": "5QjRCSuTa9HLD9Fxnuth16",
      "uri": "spotify:track:5QjRCSuTa9HLD9Fxnuth16"
    }
  ]
}

Minimum required is playlist name and track id or uri. But it can be advantageous that you can still find the artist and song name. This can be set roughly in the configuration.

Starred should be checked again, but will be imported as a playlist.

Saved is transferred in (50 ids) parts as a PUT request.

Playlist is transferred in (100 uris) parts as a POST request.

By default, playlists are not public, i.e. private. If a playlist is marked as public but also as collaborative, the playlist is automatically set as private.

It is first searched for the playlist id, then for the identical name. If both do not exist, a new playlist is created.

The whole thing is now built as async and await instead of callbacks. This makes the code a bit easier to read, but may now contain duplicate code in some places because that's pretty enough for now.

There are still a few todos (@todo) in the source code, but I don't know if you should do that either.

For example, a todo would be to select what to export before exporting. But then it's hard work, for the fact that it's not used.

Another todo might be to create Id and Uri fallback.

I think that was all... :-)

Cyb10101 commented 1 year ago

If you need a test import.json file:

{
  "playlists": [
    {
      "name": "🚧 Import not existing playlist",
      "description": "A playlist with not exists.",
      "public": true,
      "tracks": [
        {
          "id": "2zzdnRWE3z6QP3FoVlnWHO",
          "uri": "spotify:track:2zzdnRWE3z6QP3FoVlnWHO",
          "name": "Ramin Djawadi - Main Title Theme - Westworld"
        },
        {
          "id": "7dsFibmr82sHiuPv0MyKJR",
          "uri": "spotify:track:7dsFibmr82sHiuPv0MyKJR",
          "name": "Nerf Herder - Buffy The Vampire Slayer Theme"
        }
      ]
    },
    {
      "name": "🚧 Import with identical name",
      "description": "A playlist with an identical name, but no id.",
      "public": false,
      "collaborative": true,
      "tracks": [
        {
          "id": "7dsFibmr82sHiuPv0MyKJR",
          "uri": "spotify:track:7dsFibmr82sHiuPv0MyKJR",
          "name": "Nerf Herder - Buffy The Vampire Slayer Theme"
        },
        {
          "id": "2zzdnRWE3z6QP3FoVlnWHO",
          "uri": "spotify:track:2zzdnRWE3z6QP3FoVlnWHO",
          "name": "Ramin Djawadi - Main Title Theme - Westworld"
        }
      ]
    },
    {
      "name": "🚧 Import with id, but other playlist name",
      "description": "A playlist with an existing id, but different name.",
      "id": "1aIxhMT3cUWXzHzoGGSVKV",
      "tracks": [
        {
          "id": "1NOKnYogOMqhKops8RMmkX",
          "uri": "spotify:track:1NOKnYogOMqhKops8RMmkX",
          "name": "Overture",
          "album": "TRON: Legacy"
        },
        {
          "id": "5d5AjRMgz2TO50BCoXRu7Z",
          "uri": "spotify:track:5d5AjRMgz2TO50BCoXRu7Z",
          "name": "Recognizer"
        },
        {
          "id": "20RZ5i4Z7ZPmhceFIMz2x9",
          "uri": "spotify:track:20RZ5i4Z7ZPmhceFIMz2x9",
          "name": "Rinzler"
        },
        {
          "id": "7wH4RngcoLXIUf4OtuJdGu",
          "uri": "spotify:track:7wH4RngcoLXIUf4OtuJdGu",
          "name": "End of Line"
        },
        {
          "id": "4YQwWwUaeyuIYxqqKmJFnP",
          "uri": "spotify:track:4YQwWwUaeyuIYxqqKmJFnP",
          "name": "Solar Sailer"
        },
        {
          "id": "0V4wGPzv90fKur0eNvOHmI",
          "uri": "spotify:track:0V4wGPzv90fKur0eNvOHmI",
          "name": "Flynn Lives"
        },
        {
          "id": "7HwTTqEchYMIBOUY5ym1Ki",
          "uri": "spotify:track:7HwTTqEchYMIBOUY5ym1Ki",
          "name": "TRON Legacy (End Titles)"
        }
      ]
    }
  ],
  "saved": [
    {
      "id": "5QjRCSuTa9HLD9Fxnuth16",
      "uri": "spotify:track:5QjRCSuTa9HLD9Fxnuth16",
      "name": "Otto Waalkes - Otto und die Schlümpfe - Weihnachtsversion"
    },
    {
      "id": "3CVx6OD7xNJX34zCwydEqh",
      "uri": "spotify:track:3CVx6OD7xNJX34zCwydEqh",
      "name": "Otto Waalkes - In Ottos Bäckerei"
    },
    {
      "id": "2ba5sAQjJJwM5bKmiSu0Mc",
      "uri": "spotify:track:2ba5sAQjJJwM5bKmiSu0Mc",
      "name": "Otto Waalkes - Werbespots"
    },
    {
      "id": "0Iu4yVU01XEDJoHdCb4lo0",
      "uri": "spotify:track:0Iu4yVU01XEDJoHdCb4lo0",
      "name": "Otto Waalkes - Leben an der Autobahn"
    },
    {
      "id": "09Wt3LE4dtJdUYUY144tkJ",
      "uri": "spotify:track:09Wt3LE4dtJdUYUY144tkJ",
      "name": "Otto Waalkes - Hänsel und Gretel"
    },
    {
      "id": "7v1IxEKZFriBePPIttN5Kf",
      "uri": "spotify:track:7v1IxEKZFriBePPIttN5Kf",
      "name": "Otto Waalkes - Friesenjung (Englishman in New York) - Live"
    }
  ],
  "starred": [
    {
      "id": "2xjNYJDEFwcFRKjPEpL0kh",
      "uri": "spotify:track:2xjNYJDEFwcFRKjPEpL0kh",
      "name": "The Killigans - Self Deprecated"
    }
  ]
}
Cyb10101 commented 1 year ago

...and in my opinion, who still uses Internet Explorer, actually has other problems. I would be for the lines in the index.html simply to remove.

boradwell commented 1 year ago

This is awesome. Thank you!!

Cyb10101 commented 1 year ago

The last commit 801da83 is only for filter not playable tracks. This isn't absolutely necessary for backup, but it's certainly helpful when it comes to finding missing songs.

I have already explained this in the Readme.md under "Filter not playable tracks": https://github.com/secuvera/SpotMyBackup/blob/801da83d302affe66c98f48c60746fc3ea7edf7f/README.md#filter-not-playable-tracks