Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import csv
import json
import logging
import os
import sys
from datetime import datetime
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
def prepare_logging() -> logging.Logger:
LOG_FORMAT = '[%(levelname)-7s] - %(asctime)s - %(message)s'
DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
LOG_FORMATTER = logging.Formatter(fmt=LOG_FORMAT, datefmt=DATE_FORMAT)
logger = logging.getLogger('SpotifyBackup')
logger.setLevel(logging.DEBUG)
outHandler = logging.StreamHandler(sys.stdout)
outHandler.setFormatter(LOG_FORMATTER)
outHandler.setLevel(logging.DEBUG)
outHandler.addFilter(lambda record: record.levelno <= logging.INFO)
logger.addHandler(outHandler)
errHandler = logging.StreamHandler(sys.stderr)
errHandler.setFormatter(LOG_FORMATTER)
errHandler.setLevel(logging.WARNING)
logger.addHandler(errHandler)
return logger
LOGGER = prepare_logging()
class SpotifyBackup:
DATE_FORMAT = '%Y-%m-%d_%H-%M-%S'
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}"...')
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}...')
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}"')
if __name__ == '__main__':
with open('settings.json', 'r', encoding='utf-8') as f:
SETTINGS = json.load(f)
spotifyBackup = SpotifyBackup(SETTINGS['spotifyAPI']['clientID'],
SETTINGS['spotifyAPI']['clientSecret'],
SETTINGS['exportFolder'])
for playlist in SETTINGS['playlists']:
spotifyBackup.backup_playlist(playlist['user'], playlist['id'])
LOGGER.info('### DONE ###')