diff --git a/SpotifyFollowedArtistsBackup.py b/SpotifyFollowedArtistsBackup.py new file mode 100644 index 0000000000000000000000000000000000000000..842d8a853adee0440bb87a97b63a451bc561185a --- /dev/null +++ b/SpotifyFollowedArtistsBackup.py @@ -0,0 +1,99 @@ +import csv +import json +import os +from datetime import datetime, timedelta +from typing import 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('SpotifyFollowerBackup', logFormat=LOG_FORMAT) + + +class SpotifyFollowedArtistsBackup: + DATE_FORMAT = '%Y-%m-%d_%H-%M-%S' + DATE_FORMAT_SHORT = '%Y-%m-%d' + INVALID_CHARS = [' ', '\t', '\n'] + REPLACE_CHAR = '_' + + def __init__(self, clientID: str, exportFolder: str): + self._exportFolder = exportFolder + if self._exportFolder: + os.makedirs(self._exportFolder, exist_ok=True) + + client_credentials_manager = SpotifyPKCE(client_id=clientID, redirect_uri='http://localhost:8080', + scope='user-follow-read') + self._spotify = spotipy.Spotify(client_credentials_manager=client_credentials_manager) + + def backup_followed_artists(self): + followedArtists = self.__get_followed_artists() + + exportPath = self.__determine_export_path('followed_artists') + + LOGGER.info(f'>>> Exporting followed artists to "{exportPath}"...') + self.__export_csv(exportPath, followedArtists) + LOGGER.info('Exporting DONE') + + def __get_followed_artists(self): + followedArtists = self._spotify.current_user_followed_artists(limit=20, after=None)['artists'] + + results = followedArtists['items'] + while followedArtists['next']: + followedArtists = self._spotify.next(followedArtists)['artists'] + results.extend(followedArtists['items']) + + LOGGER.info(f'Fetched {len(results)} followed artists') + return results + + def __determine_export_path(self, fileName): + for item in self.INVALID_CHARS: + fileName = fileName.replace(item, self.REPLACE_CHAR) + completeFileName = f'{datetime.strftime(datetime.now(), self.DATE_FORMAT)}_{fileName}.csv' + exportPath = os.path.join(self._exportFolder, completeFileName) + return exportPath + + def __export_csv(self, exportPath: str, artists: List): + with open(exportPath, 'w', newline='', encoding='utf-8') as f: + writer = csv.writer(f, delimiter=';', quotechar='|', quoting=csv.QUOTE_MINIMAL) + writer.writerow(('id', 'name', 'url', 'uri')) + + for artist in artists: + try: + id = artist['id'] + name = artist['name'] + url = artist['external_urls'].get('spotify', '') + uri = artist['uri'] + + writer.writerow((id, name, url, uri)) + except Exception: + LOGGER.exception(f'Error while exporting artist "{artist}"') + + def clean_old_exports(self, daysToKeep): + if not daysToKeep: + LOGGER.info('Skipping deletion of old files') + return + + minimumDate = datetime.today() - timedelta(days=int(daysToKeep)) + LOGGER.info(f'>>> Deleting files older than {minimumDate} ({daysToKeep} days)') + + files = [file for file in sorted(os.listdir(self._exportFolder)) if + os.path.isfile(os.path.join(self._exportFolder, file))] + + for file in files: + parts = file.split('_') + creationDate = datetime.strptime(parts[0], self.DATE_FORMAT_SHORT) + if creationDate < minimumDate: + LOGGER.info(f'Removing old file "{file}"') + os.remove(os.path.join(self._exportFolder, file)) + + +if __name__ == '__main__': + with open('config/settings.json', 'r', encoding='utf-8') as f: + SETTINGS = json.load(f) + + spotifyBackup = SpotifyFollowedArtistsBackup(SETTINGS['spotifyAPI']['clientID'], SETTINGS['exportFolder']) + spotifyBackup.clean_old_exports(SETTINGS['daysToKeep']) + spotifyBackup.backup_followed_artists() + LOGGER.info('### DONE ###')