Skip to content
Snippets Groups Projects
SpotifyBackup.py 4.55 KiB
Newer Older
  • Learn to ignore specific revisions
  • Robert Goldmann's avatar
    Robert Goldmann committed
    import csv
    import json
    import os
    
    from datetime import datetime, timedelta
    
    Robert Goldmann's avatar
    Robert Goldmann committed
    
    import spotipy
    
    Robert Goldmann's avatar
    Robert Goldmann committed
    from TheCodeLabs_BaseUtils.DefaultLogger import DefaultLogger
    
    Robert Goldmann's avatar
    Robert Goldmann committed
    from spotipy.oauth2 import SpotifyClientCredentials
    
    
    Robert Goldmann's avatar
    Robert Goldmann committed
    LOG_FORMAT = '[%(levelname)-7s] - %(asctime)s - %(message)s'
    LOGGER = DefaultLogger().create_logger_if_not_exists('SpotifyBackup', logFormat=LOG_FORMAT)
    
    Robert Goldmann's avatar
    Robert Goldmann committed
    
    
    class SpotifyBackup:
        DATE_FORMAT = '%Y-%m-%d_%H-%M-%S'
    
        DATE_FORMAT_SHORT = '%Y-%m-%d'
    
    Robert Goldmann's avatar
    Robert Goldmann committed
        INVALID_CHARS = [' ', '\t', '\n']
        REPLACE_CHAR = '_'
    
        def __init__(self, clientID: str, clientSecret: str, exportFolder: str):
            self._exportFolder = exportFolder
            if self._exportFolder:
                os.makedirs(self._exportFolder, exist_ok=True)
    
            client_credentials_manager = SpotifyClientCredentials(client_id=clientID, client_secret=clientSecret)
            self._spotify = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
    
        def backup_playlist(self, username: str, playlistID: str):
            playlist = self.__get_playlist(username, playlistID)
            tracks = self.__get_tracks(playlist)
    
            exportPath = self.__determine_export_path(playlist['name'])
    
    
            LOGGER.info(f'>>> Exporting tracks to "{exportPath}"...')
    
    Robert Goldmann's avatar
    Robert Goldmann committed
            self.__export_csv(exportPath, tracks)
            LOGGER.info('Exporting DONE')
    
        def __get_playlist(self, username: str, playlistID: str):
    
            LOGGER.info(f'>>> Fetching playlist with ID: {playlistID} by {username}...')
    
    Robert Goldmann's avatar
    Robert Goldmann committed
            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):
            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 __determine_export_path(self, playlistName):
            for item in self.INVALID_CHARS:
                playlistName = playlistName.replace(item, self.REPLACE_CHAR)
            fileName = f'{datetime.strftime(datetime.now(), self.DATE_FORMAT)}_{playlistName}.csv'
            exportPath = os.path.join(self._exportFolder, fileName)
            return exportPath
    
        def __export_csv(self, exportPath, tracks):
            with open(exportPath, 'w', newline='', encoding='utf-8') as f:
                writer = csv.writer(f, delimiter=';', quotechar='|', quoting=csv.QUOTE_MINIMAL)
                writer.writerow(('title', 'artists', 'album', 'addedBy', 'addedAt', 'url'))
    
                for track in tracks:
                    try:
                        trackInfo = track['track']
                        title = trackInfo['name']
                        artists = trackInfo['artists']
                        artists = ' & '.join(artist['name'] for artist in artists)
                        album = trackInfo['album']['name']
                        author = track['added_by']['id']
                        addedAt = track['added_at']
                        url = trackInfo['external_urls'].get('spotify', '')
    
                        writer.writerow((title, artists, album, author, addedAt, url))
                    except Exception:
                        LOGGER.exception(f'Error while exporting track "{track}"')
    
    
        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))
    
    
    Robert Goldmann's avatar
    Robert Goldmann committed
    
    if __name__ == '__main__':
    
        with open('config/settings.json', 'r', encoding='utf-8') as f:
    
    Robert Goldmann's avatar
    Robert Goldmann committed
            SETTINGS = json.load(f)
    
        spotifyBackup = SpotifyBackup(SETTINGS['spotifyAPI']['clientID'],
                                      SETTINGS['spotifyAPI']['clientSecret'],
                                      SETTINGS['exportFolder'])
    
    
        spotifyBackup.clean_old_exports(SETTINGS['daysToKeep'])
    
    
    Robert Goldmann's avatar
    Robert Goldmann committed
        for playlist in SETTINGS['playlists']:
            spotifyBackup.backup_playlist(playlist['user'], playlist['id'])
    
        LOGGER.info('### DONE ###')