From 25629490ed00c8e1ff8bba202489d240f360c51e Mon Sep 17 00:00:00 2001
From: Robert Goldmann <deadlocker@gmx.de>
Date: Sat, 26 Mar 2022 22:36:17 +0100
Subject: [PATCH] added new class to fetch latest tracks (sorted by add date)
 and add them to a new playlist

---
 .gitignore                           |   1 +
 SpotifyAutoPlaylistCreator.py        | 106 +++++++++++++++++++++++++++
 config/settings-creator-example.json |  18 +++++
 3 files changed, 125 insertions(+)
 create mode 100644 SpotifyAutoPlaylistCreator.py
 create mode 100644 config/settings-creator-example.json

diff --git a/.gitignore b/.gitignore
index 7ac1915..082430d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 exports/
 config/settings.json
 config/settings-import.json
+config/settings-creator.json
 Pipfile.lock
\ No newline at end of file
diff --git a/SpotifyAutoPlaylistCreator.py b/SpotifyAutoPlaylistCreator.py
new file mode 100644
index 0000000..d9e2d71
--- /dev/null
+++ b/SpotifyAutoPlaylistCreator.py
@@ -0,0 +1,106 @@
+import json
+from typing import Dict, List
+
+import spotipy
+from TheCodeLabs_BaseUtils.DefaultLogger import DefaultLogger
+from spotipy.oauth2 import SpotifyPKCE
+
+LOG_FORMAT = '[%(levelname)-7s] - %(asctime)s - %(message)s'
+LOGGER = DefaultLogger().create_logger_if_not_exists('SpotifyAutoPlaylistCreator', logFormat=LOG_FORMAT)
+
+
+class SpotifyAutoPlaylistCreator:
+    def __init__(self, clientID: str, clientSecret: str, playlistInfo: List[Dict[str, str]],
+                 destinationPlaylistInfo: Dict[str, str], numberOfTracks: int):
+        self._clientID = clientID
+        self._clientSecret = clientSecret
+        self._playlistInfo = playlistInfo
+        self._destinationPlaylistInfo = destinationPlaylistInfo
+        self._numberOfTracks = numberOfTracks
+
+        self._spotify = self.login()
+
+    def login(self) -> spotipy.Spotify:
+        client_credentials_manager = SpotifyPKCE(client_id=self._clientID, redirect_uri='http://localhost:8080',
+                                                 scope='playlist-modify-private,playlist-modify-public')
+        return spotipy.Spotify(client_credentials_manager=client_credentials_manager)
+
+    def run(self):
+        destinationPlaylist = self.__get_playlist(self._destinationPlaylistInfo['user'],
+                                                  self._destinationPlaylistInfo['id'])
+
+        self.__CleanupDestinationPlaylist(destinationPlaylist)
+
+        LOGGER.info(f'>>> Fetching tracks for all source playlists...')
+        allTracks = []
+        for playlistInfo in SETTINGS['playlists']:
+            playlist = self.__get_playlist(playlistInfo['user'], playlistInfo['id'])
+            allTracks.extend(self.__get_tracks(playlist))
+
+        sortedTracks = sorted(allTracks, key=lambda d: d['added_at'], reverse=True)
+
+        LOGGER.info(f'>>> Collecting latest tracks (limit: {self._numberOfTracks})...')
+        latestTrackUris = self.__CollectLatestTracks(sortedTracks)
+
+        LOGGER.info(f'>>> Found {len(latestTrackUris)} latest tracks (limit: {self._numberOfTracks})')
+
+        LOGGER.info(f'>>> Adding tracks to destination playlist "{destinationPlaylist["name"]}"...')
+        self._spotify.playlist_add_items(self._destinationPlaylistInfo['id'], latestTrackUris)
+
+    def __CollectLatestTracks(self, sortedTracks):
+        tracksToAdd = []
+        for track in sortedTracks:
+            if len(tracksToAdd) >= self._numberOfTracks:
+                break
+
+            if track['is_local']:
+                LOGGER.info(f'Skipping local track "{track["track"]["name"]}"')
+                continue
+
+            LOGGER.info(f'Appending track "{track["track"]["name"]}"')
+            tracksToAdd.append(track)
+
+        return self.__extract_track_uris(tracksToAdd)
+
+    def __CleanupDestinationPlaylist(self, destinationPlaylist):
+        existingTracks = self.__get_tracks(destinationPlaylist)
+        LOGGER.info(f'>>> Removing {len(existingTracks)} tracks from destination '
+                    f'playlist "{destinationPlaylist["name"]}"...')
+        existingTracksUris = self.__extract_track_uris(existingTracks)
+        self._spotify.playlist_remove_all_occurrences_of_items(self._destinationPlaylistInfo['id'], existingTracksUris)
+
+    def __get_playlist(self, username: str, playlistID: str) -> Dict:
+        LOGGER.info(f'>>> Fetching playlist with ID: {playlistID} by {username}...')
+        identifier = f'spotify:user:{username}:playlist:{playlistID}'
+        playlist = self._spotify.playlist(identifier)
+        LOGGER.info(f'Found playlist "{playlist["name"]}"')
+        return playlist
+
+    def __get_tracks(self, playlist) -> List[Dict]:
+        tracks = playlist['tracks']
+        results = tracks['items']
+
+        while tracks['next']:
+            tracks = self._spotify.next(tracks)
+            results.extend(tracks['items'])
+
+        LOGGER.info(f'Fetched {len(results)} tracks')
+        return results
+
+    def __extract_track_uris(self, tracks: List) -> List[str]:
+        return [track['track']['uri'] for track in tracks]
+
+
+if __name__ == '__main__':
+    with open('config/settings-creator.json', 'r', encoding='utf-8') as f:
+        SETTINGS = json.load(f)
+
+    spotifyBackup = SpotifyAutoPlaylistCreator(SETTINGS['spotifyAPI']['clientID'],
+                                               SETTINGS['spotifyAPI']['clientSecret'],
+                                               SETTINGS['playlists'],
+                                               SETTINGS['destinationPlaylist'],
+                                               SETTINGS['numberOfTracks'])
+
+    spotifyBackup.run()
+
+    LOGGER.info('### DONE ###')
diff --git a/config/settings-creator-example.json b/config/settings-creator-example.json
new file mode 100644
index 0000000..67972d2
--- /dev/null
+++ b/config/settings-creator-example.json
@@ -0,0 +1,18 @@
+{
+    "spotifyAPI": {
+        "clientID": "",
+        "clientSecret": ""
+    },
+    "playlists": [
+        {
+            "user": "",
+            "id": ""
+        }
+    ],
+    "destinationPlaylist":
+    {
+        "user": "",
+        "id": ""
+    },
+    "numberOfTracks": 10
+}
\ No newline at end of file
-- 
GitLab