Implemented folder creation

This commit is contained in:
RemixDev 2020-02-24 18:36:11 +01:00
parent 4f3182783e
commit 7d24d1f25e
3 changed files with 234 additions and 46 deletions

View File

@ -1,18 +1,17 @@
{ {
"downloadLocation": "", "downloadLocation": "",
"tracknameTemplate": "%artist% - %title%", "tracknameTemplate": "%artist% - %title%",
"albumTracknameTemplate": "%number% - %title%", "albumTracknameTemplate": "%tracknumber% - %title%",
"playlistTracknameTemplate": "%position% - %artist% - %title%", "playlistTracknameTemplate": "%position% - %artist% - %title%",
"createPlaylistFolder": true, "createPlaylistFolder": true,
"playlistNameTemplate": "%name%", "playlistNameTemplate": "%playlist%",
"createArtistFolder": false, "createArtistFolder": false,
"artistNameTemplate": "%name%", "artistNameTemplate": "%artist%",
"createAlbumFolder": true, "createAlbumFolder": true,
"albumNameTemplate": "%artist% - %album%", "albumNameTemplate": "%artist% - %album%",
"createCDFolder": true, "createCDFolder": true,
"createStructurePlaylist": false, "createStructurePlaylist": false,
"createSingleFolder": false, "createSingleFolder": false,
"saveFullArtists": false,
"padTracks": true, "padTracks": true,
"paddingSize": "0", "paddingSize": "0",
"illegalCharacterReplacer": "_", "illegalCharacterReplacer": "_",

View File

@ -1,7 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from deemix.api.deezer import Deezer, APIError from deemix.api.deezer import Deezer, APIError
from deemix.utils.taggers import tagID3, tagFLAC from deemix.utils.taggers import tagID3, tagFLAC
from deemix.utils.pathtemplates import generateFilename, generateFilepath
import os.path import os.path
from os import makedirs
from urllib.error import HTTPError from urllib.error import HTTPError
dz = Deezer() dz = Deezer()
@ -16,6 +18,38 @@ extensions = {
13: '.mp4' 13: '.mp4'
} }
def getPreferredBitrare(filesize, bitrate):
bitrateFound = False;
selectedFormat = 0
selectedFilesize = 0
if int(bitrate) == 9:
selectedFormat = 9
selectedFilesize = filesize['flac']
if filesize['flac'] > 0:
bitrateFound = True
else:
bitrateFound = False
bitrate = 3
if int(bitrate) == 3:
selectedFormat = 3
selectedFilesize = filesize['mp3_320']
if filesize['mp3_320'] > 0:
bitrateFound = True
else:
bitrateFound = False
bitrate = 1
if int(bitrate) == 1:
selectedFormat = 3
selectedFilesize = filesize['mp3_320']
if filesize['mp3_320'] > 0:
bitrateFound = True
else:
bitrateFound = False
if not bitrateFound:
selectedFormat = 8
selectedFilesize = filesize['default']
return (selectedFormat, selectedFilesize)
def parseEssentialTrackData(track, trackAPI): def parseEssentialTrackData(track, trackAPI):
track['id'] = trackAPI['SNG_ID'] track['id'] = trackAPI['SNG_ID']
track['duration'] = trackAPI['DURATION'] track['duration'] = trackAPI['DURATION']
@ -57,7 +91,10 @@ def getTrackData(trackAPI):
track['mainArtist'] = {} track['mainArtist'] = {}
track['mainArtist']['id'] = 0 track['mainArtist']['id'] = 0
track['mainArtist']['name'] = trackAPI['ART_NAME'] track['mainArtist']['name'] = trackAPI['ART_NAME']
track['artistArray'] = [trackAPI['ART_NAME']] track['artists'] = [trackAPI['ART_NAME']]
track['aritst'] = {
'Main': [trackAPI['ART_NAME']]
}
track['date'] = { track['date'] = {
'day': 0, 'day': 0,
'month': 0, 'month': 0,
@ -72,10 +109,12 @@ def getTrackData(trackAPI):
track['explicit'] = trackAPI['EXPLICIT_LYRICS'] != "0" track['explicit'] = trackAPI['EXPLICIT_LYRICS'] != "0"
if 'COPYRIGHT' in trackAPI: if 'COPYRIGHT' in trackAPI:
track['copyright'] = trackAPI['COPYRIGHT'] track['copyright'] = trackAPI['COPYRIGHT']
track['replayGain'] = "{0:.2f} dB".format((float(trackAPI['GAIN']) + 18.4) * -1) track['replayGain'] = "{0:.2f} dB".format((float(trackAPI['GAIN']) + 18.4) * -1) if 'GAIN' in trackAPI else None
track['ISRC'] = trackAPI['ISRC'] track['ISRC'] = trackAPI['ISRC']
track['trackNumber'] = trackAPI['TRACK_NUMBER'] track['trackNumber'] = trackAPI['TRACK_NUMBER']
track['contributors'] = trackAPI['SNG_CONTRIBUTORS'] track['contributors'] = trackAPI['SNG_CONTRIBUTORS']
if 'POSITION' in trackAPI:
track['position'] = trackAPI['POSITION']
track['lyrics'] = {} track['lyrics'] = {}
if 'LYRICS_ID' in trackAPI: if 'LYRICS_ID' in trackAPI:
@ -130,8 +169,8 @@ def getTrackData(trackAPI):
track['album']['label'] = albumAPI['label'] if 'label' in albumAPI else "Unknown" track['album']['label'] = albumAPI['label'] if 'label' in albumAPI else "Unknown"
if not 'pic' in track['album']: if not 'pic' in track['album']:
track['album']['pic'] = albumAPI['cover_small'][43:-24] track['album']['pic'] = albumAPI['cover_small'][43:-24]
if 'release_date' in albumAPI and not 'date' in track: if 'release_date' in albumAPI:
track['date'] = { track['album']['date'] = {
'day': albumAPI["release_date"][8:10], 'day': albumAPI["release_date"][8:10],
'month': albumAPI["release_date"][5:7], 'month': albumAPI["release_date"][5:7],
'year': albumAPI["release_date"][0:4] 'year': albumAPI["release_date"][0:4]
@ -155,18 +194,21 @@ def getTrackData(trackAPI):
track['album']['label'] = albumAPI2['LABEL_NAME'] if 'LABEL_NAME' in albumAPI2 else "Unknown" track['album']['label'] = albumAPI2['LABEL_NAME'] if 'LABEL_NAME' in albumAPI2 else "Unknown"
if not 'pic' in track['album']: if not 'pic' in track['album']:
track['album']['pic'] = albumAPI2['ALB_PICTURE'] track['album']['pic'] = albumAPI2['ALB_PICTURE']
if 'PHYSICAL_RELEASE_DATE' in albumAPI2 and not 'date' in track: if 'PHYSICAL_RELEASE_DATE' in albumAPI2:
track['date'] = { track['album']['date'] = {
'day': albumAPI2["PHYSICAL_RELEASE_DATE"][8:10], 'day': albumAPI2["PHYSICAL_RELEASE_DATE"][8:10],
'month': albumAPI2["PHYSICAL_RELEASE_DATE"][5:7], 'month': albumAPI2["PHYSICAL_RELEASE_DATE"][5:7],
'year': albumAPI2["PHYSICAL_RELEASE_DATE"][0:4] 'year': albumAPI2["PHYSICAL_RELEASE_DATE"][0:4]
} }
track['album']['genre'] = [] track['album']['genre'] = []
if 'date' in track['album']:
track['date'] = track['album']['date']
trackAPI2 = dz.get_track(track['id']) trackAPI2 = dz.get_track(track['id'])
track['bpm'] = trackAPI2['bpm'] track['bpm'] = trackAPI2['bpm']
if not 'replayGain' in track: if not 'replayGain' in track:
track['replayGain'] = "{0:.2f} dB".format((float(trackAPI2['gain']) + 18.4) * -1) track['replayGain'] = "{0:.2f} dB".format((float(trackAPI2['gain']) + 18.4) * -1) if 'GAIN' in trackAPI else ""
if not 'explicit' in track: if not 'explicit' in track:
track['explicit'] = trackAPI2['explicit_lyrics'] track['explicit'] = trackAPI2['explicit_lyrics']
if not 'discNumber' in track: if not 'discNumber' in track:
@ -204,43 +246,19 @@ def downloadTrackObj(trackAPI, settings, overwriteBitrate=False, extraTrack=None
bitrate = overwriteBitrate bitrate = overwriteBitrate
else: else:
bitrate = settings['maxBitrate'] bitrate = settings['maxBitrate']
bitrateFound = False; (format, filesize) = getPreferredBitrare(track['filesize'], bitrate)
if int(bitrate) == 9: track['selectedFormat'] = format
track['selectedFormat'] = 9 track['selectedFilesize'] = filesize
track['selectedFilesize'] = track['filesize']['flac'] track['album']['bitrate'] = format
if track['filesize']['flac'] > 0:
bitrateFound = True
else:
bitrateFound = False
bitrate = 3
if int(bitrate) == 3:
track['selectedFormat'] = 3
track['selectedFilesize'] = track['filesize']['mp3_320']
if track['filesize']['mp3_320'] > 0:
bitrateFound = True
else:
bitrateFound = False
bitrate = 1
if int(bitrate) == 1:
track['selectedFormat'] = 3
track['selectedFilesize'] = track['filesize']['mp3_320']
if track['filesize']['mp3_320'] > 0:
bitrateFound = True
else:
bitrateFound = False
if not bitrateFound:
track['selectedFormat'] = 8
track['selectedFilesize'] = track['filesize']['default']
track['album']['bitrate'] = track['selectedFormat']
track['album']['picUrl'] = "http://e-cdn-images.deezer.com/images/cover/{}/{}x{}-000000-80-0-0.jpg".format(track['album']['pic'], settings['embeddedArtworkSize'], settings['embeddedArtworkSize']) track['album']['picUrl'] = "http://e-cdn-images.deezer.com/images/cover/{}/{}x{}-000000-80-0-0.jpg".format(track['album']['pic'], settings['embeddedArtworkSize'], settings['embeddedArtworkSize'])
# Create the filename # Create the filename
filename = "{artist} - {title}".format(title=track['title'], artist=track['mainArtist']['name']) + extensions[ filename = generateFilename(track, trackAPI, settings) + extensions[track['selectedFormat']]
track['selectedFormat']] (filepath, artistPath, coverPath, extrasPath) = generateFilepath(track, trackAPI, settings)
writepath = os.path.join(settings['downloadLocation'], filename)
track['downloadUrl'] = dz.get_track_stream_url(track['id'], track['MD5'], track['mediaVersion'], makedirs(filepath, exist_ok=True)
track['selectedFormat']) writepath = os.path.join(filepath, filename)
track['downloadUrl'] = dz.get_track_stream_url(track['id'], track['MD5'], track['mediaVersion'], track['selectedFormat'])
with open(writepath, 'wb') as stream: with open(writepath, 'wb') as stream:
try: try:
dz.stream_track(track['id'], track['downloadUrl'], stream) dz.stream_track(track['id'], track['downloadUrl'], stream)
@ -268,6 +286,8 @@ def downloadTrackObj(trackAPI, settings, overwriteBitrate=False, extraTrack=None
def download_track(id, settings, overwriteBitrate=False): def download_track(id, settings, overwriteBitrate=False):
trackAPI = dz.get_track_gw(id) trackAPI = dz.get_track_gw(id)
trackAPI['FILENAME_TEMPLATE'] = settings['tracknameTemplate']
trackAPI['SINGLE_TRACK'] = True
downloadTrackObj(trackAPI, settings, overwriteBitrate) downloadTrackObj(trackAPI, settings, overwriteBitrate)
def download_album(id, settings, overwriteBitrate=False): def download_album(id, settings, overwriteBitrate=False):
@ -278,16 +298,21 @@ def download_album(id, settings, overwriteBitrate=False):
if albumAPI['nb_tracks'] == 1: if albumAPI['nb_tracks'] == 1:
trackAPI = dz.get_track_gw(albumAPI['tracks']['data'][0]['id']) trackAPI = dz.get_track_gw(albumAPI['tracks']['data'][0]['id'])
trackAPI['ALBUM_EXTRA'] = albumAPI trackAPI['ALBUM_EXTRA'] = albumAPI
trackAPI['FILENAME_TEMPLATE'] = settings['tracknameTemplate']
trackAPI['SINGLE_TRACK'] = True
downloadTrackObj(trackAPI, settings, overwriteBitrate) downloadTrackObj(trackAPI, settings, overwriteBitrate)
else: else:
tracksArray = dz.get_album_tracks_gw(id) tracksArray = dz.get_album_tracks_gw(id)
for trackAPI in tracksArray: for trackAPI in tracksArray:
trackAPI['ALBUM_EXTRA'] = albumAPI trackAPI['ALBUM_EXTRA'] = albumAPI
trackAPI['FILENAME_TEMPLATE'] = settings['albumTracknameTemplate']
downloadTrackObj(trackAPI, settings, overwriteBitrate) downloadTrackObj(trackAPI, settings, overwriteBitrate)
def download_playlist(id, settings, overwriteBitrate=False): def download_playlist(id, settings, overwriteBitrate=False):
playlistAPI = dz.get_playlist(id) playlistAPI = dz.get_playlist(id)
playlistTracksAPI = dz.get_playlist_tracks_gw(id) playlistTracksAPI = dz.get_playlist_tracks_gw(id)
for trackAPI in playlistTracksAPI: for pos, trackAPI in enumerate(playlistTracksAPI, start=1):
trackAPI['PLAYLIST_EXTRA'] = playlistAPI trackAPI['PLAYLIST_EXTRA'] = playlistAPI
trackAPI['POSITION'] = pos
trackAPI['FILENAME_TEMPLATE'] = settings['playlistTracknameTemplate']
downloadTrackObj(trackAPI, settings, overwriteBitrate) downloadTrackObj(trackAPI, settings, overwriteBitrate)

View File

@ -0,0 +1,164 @@
#!/usr/bin/env python3
import re
from os.path import sep as pathSep
bitrateLabels = {
15: "360 HQ",
14: "360 MQ",
13: "360 LQ",
9: "FLAC",
3: "320",
1: "128",
8: "128"
}
def fixName(txt, char='_'):
txt = str(txt)
txt = re.sub(r'[\0\/\\:*?"<>|]', char, txt)
return txt
def fixLongName(name):
if pathSep in name:
name2 = name.split(pathSep)
name = ""
for txt in name:
txt = txt[:200]
name += txt+pathSep
name = name[:-1]
else:
name = name[:200]
return name
def antiDot(str):
while str[-1:] == "." or str[-1:] == " " or str[-1:] == "\n":
str = str[:-1]
if len(str) < 1:
str = "dot"
return str
def pad(num, max, dopad=True):
paddingsize = len(str(max))
if dopad:
return str(num).zfill(paddingsize)
else:
return str(num)
def generateFilename(track, trackAPI, settings):
if trackAPI['FILENAME_TEMPLATE'] == "":
filename = "%artist% - %title%"
else:
filename = trackAPI['FILENAME_TEMPLATE']
return settingsRegex(filename, track, settings, trackAPI['PLAYLIST_EXTRA'] if 'PLAYLIST_EXTRA' in trackAPI else None)
def generateFilepath(track, trackAPI, settings):
filepath = settings['downloadLocation']
if filepath[-1:] != pathSep:
filepath += pathSep
artistPath = None
coverPath = None
extrasPath = None
if settings['createPlaylistFolder'] and 'PLAYLIST_EXTRA' in trackAPI and not settings['savePlaylistAsCompilation']:
filepath += antiDot(settingsRegexPlaylist(settings['playlistNameTemplate'], trackAPI['PLAYLIST_EXTRA'], settings)) + pathSep
if 'PLAYLIST_EXTRA' in trackAPI and not settings['savePlaylistAsCompilation']:
extrasPath = filepath
if (
settings['createArtistFolder'] and not 'PLAYLIST_EXTRA' in trackAPI or
(settings['createArtistFolder'] and 'PLAYLIST_EXTRA' in trackAPI and settings['savePlaylistAsCompilation']) or
(settings['createArtistFolder'] and 'PLAYLIST_EXTRA' in trackAPI and settings['createStructurePlaylist'])
):
if (track['id']<0 and not 'artist' in track['album']):
track['album']['artist'] = track['mainArtist']
filepath += antiDot(settingsRegexArtist(settings['artistNameTemplate'], track['album']['artist'], settings)) + pathSep
artistPath = filepath
if (settings['createAlbumFolder'] and
(not 'SINGLE_TRACK' in trackAPI or ('SINGLE_TRACK' in trackAPI and settings['createSingleFolder'])) and
(not 'PLAYLIST_EXTRA' in trackAPI or ('PLAYLIST_EXTRA' in trackAPI and settings['savePlaylistAsCompilation']) or ('PLAYLIST_EXTRA' in trackAPI and settings['createStructurePlaylist']))
):
filepath += antiDot(settingsRegexAlbum(settings['albumNameTemplate'], track['album'], settings)) + pathSep
coverPath = filepath
if not ('PLAYLIST_EXTRA' in trackAPI and not settings['savePlaylistAsCompilation']):
extrasPath = filepath
if (
int(track['album']['discTotal']) > 1 and (
(settings['createAlbumFolder'] and settings['createCDFolder']) and
(not 'SINGLE_TRACK' in trackAPI or ('SINGLE_TRACK' in trackAPI and settings['createSingleFolder'])) and
(not 'PLAYLIST_EXTRA' in trackAPI or ('PLAYLIST_EXTRA' in trackAPI and settings['savePlaylistAsCompilation']) or ('PLAYLIST_EXTRA' in trackAPI and settings['createStructurePlaylist']))
)):
filepath += 'CD'+str(track['discNumber']) + pathSep
return (filepath, artistPath, coverPath, extrasPath)
def settingsRegex(filename, track, settings, playlist=None):
filename = filename.replace("%title%", fixName(track['title'], settings['illegalCharacterReplacer']))
filename = filename.replace("%artist%", fixName(track['mainArtist']['name'], settings['illegalCharacterReplacer']))
filename = filename.replace("%album%", fixName(track['album']['title'], settings['illegalCharacterReplacer']))
filename = filename.replace("%albumartist%", fixName(track['album']['artist']['name'], settings['illegalCharacterReplacer']))
filename = filename.replace("%tracknumber%", pad(track['trackNumber'], track['album']['trackTotal'], settings['padTracks']))
filename = filename.replace("%tracktotal%", str(track['album']['trackTotal']))
filename = filename.replace("%discnumber%", str(track['discNumber']))
filename = filename.replace("%disctotal%", str(track['album']['discTotal']))
if len(track['album']['genre'])>0:
filename = filename.replace("%genre%", fixName(track['album']['genre'][0], settings['illegalCharacterReplacer']))
else:
filename = filename.replace("%genre%", "Unknown")
filename = filename.replace("%year%", str(track['date']['year']))
filename = filename.replace("%date%", "{}-{}-{}".format(str(track['date']['year']), str(track['date']['month']), str(track['date']['day'])))
filename = filename.replace("%bpm%", str(track['bpm']))
filename = filename.replace("%label%", fixName(track['album']['label'], settings['illegalCharacterReplacer']))
filename = filename.replace("%isrc%", track['ISRC'])
filename = filename.replace("%upc%", track['album']['barcode'])
filename = filename.replace("%explicit%", "(Explicit)" if track['explicit'] else "")
filename = filename.replace("%track_id%", str(track['id']))
filename = filename.replace("%album_id%", str(track['album']['id']))
filename = filename.replace("%artist_id%", str(track['mainArtist']['id']))
if playlist:
filename = filename.replace("%playlist_id%", str(playlist['id']))
filename = filename.replace("%position%", pad(track['position'], playlist['nb_tracks'], settings['padTracks']))
else:
filename = filename.replace("%position%", pad(track['trackNumber'], track['album']['trackTotal'], settings['padTracks']))
filename = re.sub(r'[/\\]', pathSep, filename)
return antiDot(fixLongName(filename))
def settingsRegexAlbum(foldername, album, settings):
foldername = foldername.replace("%album_id%", str(album['id']))
foldername = foldername.replace("%album%", fixName(album['title'], settings['illegalCharacterReplacer']))
foldername = foldername.replace("%artist%", fixName(album['artist']['name'], settings['illegalCharacterReplacer']))
foldername = foldername.replace("%artist_id%", str(album['artist']['id']))
foldername = foldername.replace("%tracktotal%", str(album['trackTotal']))
foldername = foldername.replace("%disctotal%", str(album['discTotal']))
foldername = foldername.replace("%type%", fixName(album['recordType'], settings['illegalCharacterReplacer']))
foldername = foldername.replace("%upc%", album['barcode'])
foldername = foldername.replace("%label%", fixName(album['label'], settings['illegalCharacterReplacer']))
if len(album['genre'])>0:
foldername = foldername.replace("%genre%", fixName(album['genre'][0], settings['illegalCharacterReplacer']))
else:
foldername = foldername.replace("%genre%", "Unknown")
foldername = foldername.replace("%year%", str(album['date']['year']))
foldername = foldername.replace("%date%", "{}-{}-{}".format(str(album['date']['year']), str(album['date']['month']), str(album['date']['day'])))
foldername = foldername.replace("%bitrate%", bitrateLabels[int(album['bitrate'])])
foldername = re.sub(r'[/\\]', pathSep, foldername)
return antiDot(fixLongName(foldername))
def settingsRegexArtist(foldername, artist, settings):
foldername = foldername.replace("%artist%", fixName(artist['name'], settings['illegalCharacterReplacer']))
foldername = foldername.replace("%artist_id%", str(artist['id']))
foldername = re.sub(r'[/\\]', pathSep, foldername)
return antiDot(fixLongName(foldername))
def settingsRegexPlaylist(foldername, playlist, settings):
foldername = foldername.replace("%playlist%", fixName(playlist['title'], settings['illegalCharacterReplacer']))
foldername = foldername.replace("%playlist_id%", fixName(playlist['id'], settings['illegalCharacterReplacer']))
foldername = foldername.replace("%owner%", fixName(playlist['creator']['name'], settings['illegalCharacterReplacer']))
foldername = foldername.replace("%owner_id%", str(playlist['creator']['id']))
foldername = foldername.replace("%year%", str(playlist['creation_date'][:4]))
foldername = foldername.replace("%date%", str(playlist['creation_date'][:10]))
foldername = re.sub(r'[/\\]', pathSep, foldername)
return antiDot(fixLongName(foldername))