Skip to content
Snippets Groups Projects
Commit fef0b491 authored by Robert Goldmann's avatar Robert Goldmann
Browse files

initial commit

parents
Branches
Tags
No related merge requests found
exports/
settings.json
\ No newline at end of file
Pipfile 0 → 100644
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
[requires]
python_version = "3.7"
[packages]
spotipy = "==2.10.0"
[dev-packages]
{
"_meta": {
"hash": {
"sha256": "d7d655d3f7405b14ff6cd02bc99ff67cf971ff9149e0f5b692282a1fe52ffec3"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.7"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.python.org/simple",
"verify_ssl": true
}
]
},
"default": {
"certifi": {
"hashes": [
"sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304",
"sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"
],
"version": "==2020.4.5.1"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version": "==3.0.4"
},
"idna": {
"hashes": [
"sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
"sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
],
"version": "==2.9"
},
"requests": {
"hashes": [
"sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee",
"sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"
],
"version": "==2.23.0"
},
"six": {
"hashes": [
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
"sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
],
"version": "==1.14.0"
},
"spotipy": {
"hashes": [
"sha256:4cb4ce6509ea2641b59fd8a3791d145865f7589f2c901ddcf0316cd147724cfc",
"sha256:ed95a516d3f3297665db4baf5be363ac9af6b826cb466692defa370ca7597c79",
"sha256:f5bd0d04f4fd37d3b38cd3b35d2e9ce05182a57bc52403d6bad14d07bb4f4e68"
],
"index": "pypi",
"version": "==2.10.0"
},
"urllib3": {
"hashes": [
"sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
"sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
],
"version": "==1.25.8"
}
},
"develop": {}
}
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 ###')
{
"spotifyAPI": {
"clientID": "",
"clientSecret": ""
},
"exportFolder": "",
"playlists": [
{
"user": "",
"id": ""
}
]
}
\ No newline at end of file
{
"version": {
"name": "v1.0.0",
"code": 1,
"date": "09.04.20"
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment