Merge branch 'master' of TrDex/deemix into master

This commit is contained in:
RemixDev 2020-04-17 10:49:33 +00:00 committed by Gogs
commit a3d42b82b3
14 changed files with 1969 additions and 1837 deletions

3
.gitignore vendored
View File

@ -6,6 +6,7 @@ __pycache__
/dist /dist
# local env files # local env files
/venv/
.env.local .env.local
.env.*.local .env.*.local
@ -21,4 +22,4 @@ yarn-error.log*
*.ntvs* *.ntvs*
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?

View File

@ -1,16 +1,19 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import click import click
import deemix.app.cli as app import deemix.app.cli as app
from deemix.app.settings import initSettings from deemix.app.settings import initSettings
@click.command() @click.command()
@click.option('-b', '--bitrate', default=None, help='Overwrites the default bitrate selected') @click.option('-b', '--bitrate', default=None, help='Overwrites the default bitrate selected')
@click.argument('url') @click.argument('url')
def download(bitrate, url): def download(bitrate, url):
settings = initSettings() settings = initSettings()
app.login() app.login()
app.downloadLink(url, settings, bitrate) app.downloadLink(url, settings, bitrate)
click.echo("All done!") click.echo("All done!")
if __name__ == '__main__': if __name__ == '__main__':
download() download()

View File

@ -1,2 +1,2 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
#Empty File # Empty File

View File

@ -1,332 +1,343 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import binascii import binascii
import time
import requests
from Cryptodome.Cipher import Blowfish, AES
from Cryptodome.Hash import MD5 from Cryptodome.Hash import MD5
from Cryptodome.Util.Padding import pad from Cryptodome.Util.Padding import pad
from Cryptodome.Cipher import Blowfish, AES USER_AGENT_HEADER = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) " \
import requests "Chrome/79.0.3945.130 Safari/537.36"
import time
USER_AGENT_HEADER = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36"
class Deezer: class Deezer:
def __init__(self): def __init__(self):
self.api_url = "http://www.deezer.com/ajax/gw-light.php" self.api_url = "http://www.deezer.com/ajax/gw-light.php"
self.legacy_api_url = "https://api.deezer.com/" self.legacy_api_url = "https://api.deezer.com/"
self.http_headers = { self.http_headers = {
"User-Agent": USER_AGENT_HEADER "User-Agent": USER_AGENT_HEADER
} }
self.album_pictures_host = "https://e-cdns-images.dzcdn.net/images/cover/" self.album_pictures_host = "https://e-cdns-images.dzcdn.net/images/cover/"
self.artist_pictures_host = "https://e-cdns-images.dzcdn.net/images/artist/" self.artist_pictures_host = "https://e-cdns-images.dzcdn.net/images/artist/"
self.user = {} self.user = {}
self.session = requests.Session() self.session = requests.Session()
self.logged_in = False self.logged_in = False
self.session.post("http://www.deezer.com/", headers=self.http_headers) self.session.post("http://www.deezer.com/", headers=self.http_headers)
self.sid = self.session.cookies.get('sid') self.sid = self.session.cookies.get('sid')
def get_token(self): def get_token(self):
token_data = self.gw_api_call('deezer.getUserData') token_data = self.gw_api_call('deezer.getUserData')
return token_data["results"]["checkForm"] return token_data["results"]["checkForm"]
def get_track_md5(self, sng_id): def get_track_md5(self, sng_id):
try: try:
site = self.session.post( site = self.session.post(
"https://api.deezer.com/1.0/gateway.php", "https://api.deezer.com/1.0/gateway.php",
params={ params={
'api_key': "4VCYIJUCDLOUELGD1V8WBVYBNVDYOXEWSLLZDONGBBDFVXTZJRXPR29JRLQFO6ZE", 'api_key': "4VCYIJUCDLOUELGD1V8WBVYBNVDYOXEWSLLZDONGBBDFVXTZJRXPR29JRLQFO6ZE",
'sid': self.sid, 'sid': self.sid,
'input': '3', 'input': '3',
'output': '3', 'output': '3',
'method': 'song_getData' 'method': 'song_getData'
}, },
timeout=30, timeout=30,
json={'sng_id': sng_id}, json={'sng_id': sng_id},
headers=self.http_headers headers=self.http_headers
) )
except: except:
time.sleep(2) time.sleep(2)
return self.get_track_md5(sng_id) return self.get_track_md5(sng_id)
response = site.json() response = site.json()
return response['results']['PUID'] return response['results']['PUID']
def gw_api_call(self, method, args={}): def gw_api_call(self, method, args=None):
try: if args is None:
result = self.session.post( args = {}
self.api_url, try:
params={ result = self.session.post(
'api_version': "1.0", self.api_url,
'api_token': 'null' if method == 'deezer.getUserData' else self.get_token(), params={
'input': '3', 'api_version': "1.0",
'method': method 'api_token': 'null' if method == 'deezer.getUserData' else self.get_token(),
}, 'input': '3',
timeout=30, 'method': method
json=args, },
headers=self.http_headers timeout=30,
) json=args,
except: headers=self.http_headers
time.sleep(2) )
return self.gw_api_call(method, args) except:
return result.json() time.sleep(2)
return self.gw_api_call(method, args)
return result.json()
def api_call(self, method, args={}): def api_call(self, method, args=None):
try: if args is None:
result = self.session.get( args = {}
self.legacy_api_url + method, try:
params=args, result = self.session.get(
headers=self.http_headers, self.legacy_api_url + method,
timeout=30 params=args,
) headers=self.http_headers,
result_json = result.json() timeout=30
except: )
time.sleep(2) result_json = result.json()
return self.api_call(method, args) except:
if 'error' in result_json.keys(): time.sleep(2)
raise APIError() return self.api_call(method, args)
return result_json if 'error' in result_json.keys():
raise APIError()
return result_json
def login(self, email, password, re_captcha_token): def login(self, email, password, re_captcha_token):
check_form_login = self.gw_api_call("deezer.getUserData") check_form_login = self.gw_api_call("deezer.getUserData")
login = self.session.post( login = self.session.post(
"https://www.deezer.com/ajax/action.php", "https://www.deezer.com/ajax/action.php",
data={ data={
'type': 'login', 'type': 'login',
'mail': email, 'mail': email,
'password': password, 'password': password,
'checkFormLogin': check_form_login['results']['checkFormLogin'], 'checkFormLogin': check_form_login['results']['checkFormLogin'],
'reCaptchaToken': re_captcha_token 'reCaptchaToken': re_captcha_token
}, },
headers={'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', **self.http_headers} headers={'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', **self.http_headers}
) )
if 'success' not in login.text: if 'success' not in login.text:
self.logged_in = False self.logged_in = False
return False return False
user_data = self.gw_api_call("deezer.getUserData") user_data = self.gw_api_call("deezer.getUserData")
self.user = { self.user = {
'email': email, 'email': email,
'id': user_data["results"]["USER"]["USER_ID"], 'id': user_data["results"]["USER"]["USER_ID"],
'name': user_data["results"]["USER"]["BLOG_NAME"], 'name': user_data["results"]["USER"]["BLOG_NAME"],
'picture': user_data["results"]["USER"]["USER_PICTURE"] if "USER_PICTURE" in user_data["results"][ 'picture': user_data["results"]["USER"]["USER_PICTURE"] if "USER_PICTURE" in user_data["results"][
"USER"] else "" "USER"] else ""
} }
self.logged_in = True self.logged_in = True
return True return True
def login_via_arl(self, arl): def login_via_arl(self, arl):
cookie_obj = requests.cookies.create_cookie( cookie_obj = requests.cookies.create_cookie(
domain='deezer.com', domain='deezer.com',
name='arl', name='arl',
value=arl, value=arl,
path="/", path="/",
rest={'HttpOnly': True} rest={'HttpOnly': True}
) )
self.session.cookies.set_cookie(cookie_obj) self.session.cookies.set_cookie(cookie_obj)
user_data = self.gw_api_call("deezer.getUserData") user_data = self.gw_api_call("deezer.getUserData")
if user_data["results"]["USER"]["USER_ID"] == 0: if user_data["results"]["USER"]["USER_ID"] == 0:
self.logged_in = False self.logged_in = False
return 0 return 0
self.user = { self.user = {
'id': user_data["results"]["USER"]["USER_ID"], 'id': user_data["results"]["USER"]["USER_ID"],
'name': user_data["results"]["USER"]["BLOG_NAME"], 'name': user_data["results"]["USER"]["BLOG_NAME"],
'picture': user_data["results"]["USER"]["USER_PICTURE"] if "USER_PICTURE" in user_data["results"][ 'picture': user_data["results"]["USER"]["USER_PICTURE"] if "USER_PICTURE" in user_data["results"][
"USER"] else "" "USER"] else ""
} }
self.logged_in = True self.logged_in = True
return 1 return 1
def get_track_gw(self, sng_id): def get_track_gw(self, sng_id):
if int(sng_id) < 0: if int(sng_id) < 0:
body = self.gw_api_call('song.getData', {'sng_id': sng_id}) body = self.gw_api_call('song.getData', {'sng_id': sng_id})
else: else:
body = self.gw_api_call('deezer.pageTrack', {'sng_id': sng_id}) body = self.gw_api_call('deezer.pageTrack', {'sng_id': sng_id})
if 'LYRICS' in body['results']: if 'LYRICS' in body['results']:
body['results']['DATA']['LYRICS'] = body['results']['LYRICS'] body['results']['DATA']['LYRICS'] = body['results']['LYRICS']
body['results'] = body['results']['DATA'] body['results'] = body['results']['DATA']
return body['results'] return body['results']
def get_tracks_gw(self, ids): def get_tracks_gw(self, ids):
tracks_array = [] tracks_array = []
body = self.gw_api_call('song.getListData', {'sng_ids': ids}) body = self.gw_api_call('song.getListData', {'sng_ids': ids})
errors = 0 errors = 0
for i in range(len(ids)): for i in range(len(ids)):
if ids[i] != 0: if ids[i] != 0:
tracks_array.append(body['results']['data'][i - errors]) tracks_array.append(body['results']['data'][i - errors])
else: else:
errors += 1 errors += 1
tracks_array.append({ tracks_array.append({
'SNG_ID': 0, 'SNG_ID': 0,
'SNG_TITLE': '', 'SNG_TITLE': '',
'DURATION': 0, 'DURATION': 0,
'MD5_ORIGIN': 0, 'MD5_ORIGIN': 0,
'MEDIA_VERSION': 0, 'MEDIA_VERSION': 0,
'FILESIZE': 0, 'FILESIZE': 0,
'ALB_TITLE': "", 'ALB_TITLE': "",
'ALB_PICTURE': "", 'ALB_PICTURE': "",
'ART_ID': 0, 'ART_ID': 0,
'ART_NAME': "" 'ART_NAME': ""
}) })
return tracks_array return tracks_array
def get_album_gw(self, alb_id): def get_album_gw(self, alb_id):
return self.gw_api_call('album.getData', {'alb_id': alb_id})['results'] return self.gw_api_call('album.getData', {'alb_id': alb_id})['results']
def get_album_tracks_gw(self, alb_id): def get_album_tracks_gw(self, alb_id):
tracks_array = [] tracks_array = []
body = self.gw_api_call('song.getListByAlbum', {'alb_id': alb_id, 'nb': -1}) body = self.gw_api_call('song.getListByAlbum', {'alb_id': alb_id, 'nb': -1})
for track in body['results']['data']: for track in body['results']['data']:
_track = track _track = track
_track['position'] = body['results']['data'].index(track) _track['position'] = body['results']['data'].index(track)
tracks_array.append(_track) tracks_array.append(_track)
return tracks_array return tracks_array
def get_artist_gw(self, art_id): def get_artist_gw(self, art_id):
return self.gw_api_call('deezer.pageArtist', {'art_id': art_id}) return self.gw_api_call('deezer.pageArtist', {'art_id': art_id})
def get_playlist_gw(self, playlist_id): def get_playlist_gw(self, playlist_id):
return self.gw_api_call('deezer.pagePlaylist', {'playlist_id': playlist_id}) return self.gw_api_call('deezer.pagePlaylist', {'playlist_id': playlist_id})
def get_playlist_tracks_gw(self, playlist_id): def get_playlist_tracks_gw(self, playlist_id):
tracks_array = [] tracks_array = []
body = self.gw_api_call('playlist.getSongs', {'playlist_id': playlist_id, 'nb': -1}) body = self.gw_api_call('playlist.getSongs', {'playlist_id': playlist_id, 'nb': -1})
for track in body['results']['data']: for track in body['results']['data']:
track['position'] = body['results']['data'].index(track) track['position'] = body['results']['data'].index(track)
tracks_array.append(track) tracks_array.append(track)
return tracks_array return tracks_array
def get_artist_toptracks_gw(self, art_id): def get_artist_toptracks_gw(self, art_id):
tracks_array = [] tracks_array = []
body = self.gw_api_call('artist.getTopTrack', {'art_id': art_id, 'nb': 100}) body = self.gw_api_call('artist.getTopTrack', {'art_id': art_id, 'nb': 100})
for track in body['results']['data']: for track in body['results']['data']:
track['position'] = body['results']['data'].index(track) track['position'] = body['results']['data'].index(track)
tracks_array.append(track) tracks_array.append(track)
return tracks_array return tracks_array
def search_main_gw(self, term): def search_main_gw(self, term):
results = self.gw_api_call('deezer.pageSearch', {"query": term, "start": 0, "nb": 40, "suggest": True, "artist_suggest": True, "top_tracks": True})['results'] results = self.gw_api_call('deezer.pageSearch',
order = [] {"query": term, "start": 0, "nb": 40, "suggest": True, "artist_suggest": True,
for x in results['ORDER']: "top_tracks": True})['results']
if x in ['TOP_RESULT', 'TRACK', 'ALBUM', 'ARTIST', 'PLAYLIST']: order = []
order.append(x) for x in results['ORDER']:
results['ORDER'] = order if x in ['TOP_RESULT', 'TRACK', 'ALBUM', 'ARTIST', 'PLAYLIST']:
return results order.append(x)
results['ORDER'] = order
return results
def search_gw(self, term, type, start, nb=20): def search_gw(self, term, type, start, nb=20):
return self.gw_api_call('search.music', {"query": term, "filter":"ALL", "output":type, "start": start, "nb": nb})['results'] return \
self.gw_api_call('search.music',
{"query": term, "filter": "ALL", "output": type, "start": start, "nb": nb})[
'results']
def get_lyrics_gw(self, sng_id): def get_lyrics_gw(self, sng_id):
return self.gw_api_call('song.getLyrics', {'sng_id': sng_id})["results"] return self.gw_api_call('song.getLyrics', {'sng_id': sng_id})["results"]
def get_user_playlist(self, user_id): def get_user_playlist(self, user_id):
return self.api_call('user/' + str(user_id) + '/playlists', {'limit': -1}) return self.api_call('user/' + str(user_id) + '/playlists', {'limit': -1})
def get_track(self, user_id): def get_track(self, user_id):
return self.api_call('track/' + str(user_id)) return self.api_call('track/' + str(user_id))
def get_track_by_ISRC(self, isrc): def get_track_by_ISRC(self, isrc):
return self.api_call('track/isrc:' + isrc) return self.api_call('track/isrc:' + isrc)
def get_charts_top_country(self): def get_charts_top_country(self):
return self.get_user_playlist('637006841') return self.get_user_playlist('637006841')
def get_playlist(self, playlist_id): def get_playlist(self, playlist_id):
return self.api_call('playlist/' + str(playlist_id)) return self.api_call('playlist/' + str(playlist_id))
def get_playlist_tracks(self, playlist_id): def get_playlist_tracks(self, playlist_id):
return self.api_call('playlist/' + str(playlist_id) + '/tracks', {'limit': -1}) return self.api_call('playlist/' + str(playlist_id) + '/tracks', {'limit': -1})
def get_album(self, album_id): def get_album(self, album_id):
return self.api_call('album/' + str(album_id)) return self.api_call('album/' + str(album_id))
def get_album_by_UPC(self, upc): def get_album_by_UPC(self, upc):
return self.api_call('album/upc:' + str(upc)) return self.api_call('album/upc:' + str(upc))
def get_album_tracks(self, album_id): def get_album_tracks(self, album_id):
return self.api_call('album/' + str(album_id) + '/tracks', {'limit': -1}) return self.api_call('album/' + str(album_id) + '/tracks', {'limit': -1})
def get_artist(self, artist_id): def get_artist(self, artist_id):
return self.api_call('artist/' + str(artist_id)) return self.api_call('artist/' + str(artist_id))
def get_artist_albums(self, artist_id): def get_artist_albums(self, artist_id):
return self.api_call('artist/' + str(artist_id) + '/albums', {'limit': -1}) return self.api_call('artist/' + str(artist_id) + '/albums', {'limit': -1})
def search(self, term, search_type, limit=30): def search(self, term, search_type, limit=30):
return self.api_call('search/' + search_type, {'q': term, 'limit': limit}) return self.api_call('search/' + search_type, {'q': term, 'limit': limit})
def decrypt_track(self, track_id, input, output): def decrypt_track(self, track_id, input, output):
response = open(input, 'rb') response = open(input, 'rb')
outfile = open(output, 'wb') outfile = open(output, 'wb')
blowfish_key = str.encode(self._get_blowfish_key(str(track_id))) blowfish_key = str.encode(self._get_blowfish_key(str(track_id)))
i = 0 i = 0
while True: while True:
chunk = response.read(2048) chunk = response.read(2048)
if not chunk: if not chunk:
break break
if (i % 3) == 0 and len(chunk) == 2048: if (i % 3) == 0 and len(chunk) == 2048:
chunk = Blowfish.new(blowfish_key, Blowfish.MODE_CBC, b"\x00\x01\x02\x03\x04\x05\x06\x07").decrypt(chunk) chunk = Blowfish.new(blowfish_key, Blowfish.MODE_CBC, b"\x00\x01\x02\x03\x04\x05\x06\x07").decrypt(
outfile.write(chunk) chunk)
i += 1 outfile.write(chunk)
i += 1
def stream_track(self, track_id, url, stream): def stream_track(self, track_id, url, stream):
try: try:
request = requests.get(url, headers=self.http_headers, stream=True, timeout=30) request = requests.get(url, headers=self.http_headers, stream=True, timeout=30)
except: except:
time.sleep(2) time.sleep(2)
return self.stream_track(track_id, url, stream) return self.stream_track(track_id, url, stream)
request.raise_for_status() request.raise_for_status()
blowfish_key = str.encode(self._get_blowfish_key(str(track_id))) blowfish_key = str.encode(self._get_blowfish_key(str(track_id)))
i = 0 i = 0
for chunk in request.iter_content(2048): for chunk in request.iter_content(2048):
if (i % 3) == 0 and len(chunk) == 2048: if (i % 3) == 0 and len(chunk) == 2048:
chunk = Blowfish.new(blowfish_key, Blowfish.MODE_CBC, b"\x00\x01\x02\x03\x04\x05\x06\x07").decrypt(chunk) chunk = Blowfish.new(blowfish_key, Blowfish.MODE_CBC, b"\x00\x01\x02\x03\x04\x05\x06\x07").decrypt(
stream.write(chunk) chunk)
i += 1 stream.write(chunk)
i += 1
def _md5(self, data): def _md5(self, data):
h = MD5.new() h = MD5.new()
h.update(str.encode(data) if isinstance(data, str) else data) h.update(str.encode(data) if isinstance(data, str) else data)
return h.hexdigest() return h.hexdigest()
def _get_blowfish_key(self, trackId): def _get_blowfish_key(self, trackId):
SECRET = 'g4el58wc' + '0zvf9na1' SECRET = 'g4el58wc' + '0zvf9na1'
idMd5 = self._md5(trackId) idMd5 = self._md5(trackId)
bfKey = "" bfKey = ""
for i in range(16): for i in range(16):
bfKey += chr(ord(idMd5[i]) ^ ord(idMd5[i + 16]) ^ ord(SECRET[i])) bfKey += chr(ord(idMd5[i]) ^ ord(idMd5[i + 16]) ^ ord(SECRET[i]))
return bfKey return bfKey
def get_track_stream_url(self, sng_id, md5, media_version, format): def get_track_stream_url(self, sng_id, md5, media_version, format):
urlPart = b'\xa4'.join( urlPart = b'\xa4'.join(
[str.encode(md5), str.encode(str(format)), str.encode(str(sng_id)), str.encode(str(media_version))]) [str.encode(md5), str.encode(str(format)), str.encode(str(sng_id)), str.encode(str(media_version))])
md5val = self._md5(urlPart) md5val = self._md5(urlPart)
step2 = str.encode(md5val) + b'\xa4' + urlPart + b'\xa4' step2 = str.encode(md5val) + b'\xa4' + urlPart + b'\xa4'
step2 = pad(step2, 16) step2 = pad(step2, 16)
urlPart = binascii.hexlify(AES.new(b'jo6aey6haid2Teih', AES.MODE_ECB).encrypt(step2)) urlPart = binascii.hexlify(AES.new(b'jo6aey6haid2Teih', AES.MODE_ECB).encrypt(step2))
return "https://e-cdns-proxy-" + md5[0] + ".dzcdn.net/mobile/1/" + urlPart.decode("utf-8") return "https://e-cdns-proxy-" + md5[0] + ".dzcdn.net/mobile/1/" + urlPart.decode("utf-8")
def get_track_from_metadata(self, artist, track, album): def get_track_from_metadata(self, artist, track, album):
artist = artist.replace("","-").replace("", "'") artist = artist.replace("", "-").replace("", "'")
track = track.replace("","-").replace("", "'") track = track.replace("", "-").replace("", "'")
album = album.replace("","-").replace("", "'") album = album.replace("", "-").replace("", "'")
resp = self.search(f'artist:"{artist}" track:"{track}" album:"{album}"', "track", 1) resp = self.search(f'artist:"{artist}" track:"{track}" album:"{album}"', "track", 1)
if len(resp['data'])>0: if len(resp['data']) > 0:
return resp['data'][0]['id'] return resp['data'][0]['id']
resp = self.search(f'artist:"{artist}" track:"{track}"', "track", 1) resp = self.search(f'artist:"{artist}" track:"{track}"', "track", 1)
if len(resp['data'])>0: if len(resp['data']) > 0:
return resp['data'][0]['id'] return resp['data'][0]['id']
if "(" in track and ")" in track and track.find("(") < track.find(")"): if "(" in track and ")" in track and track.find("(") < track.find(")"):
resp = self.search(f'artist:"{artist}" track:"{track[:track.find("(")]}"', "track", 1) resp = self.search(f'artist:"{artist}" track:"{track[:track.find("(")]}"', "track", 1)
if len(resp['data'])>0: if len(resp['data']) > 0:
return resp['data'][0]['id'] return resp['data'][0]['id']
elif " - " in track: elif " - " in track:
resp = self.search(f'artist:"{artist}" track:"{track[:track.find(" - ")]}"', "track", 1) resp = self.search(f'artist:"{artist}" track:"{track[:track.find(" - ")]}"', "track", 1)
if len(resp['data'])>0: if len(resp['data']) > 0:
return resp['data'][0]['id'] return resp['data'][0]['id']
else: else:
return 0 return 0
return 0 return 0
class APIError(Exception): class APIError(Exception):
pass pass

View File

@ -1,36 +1,38 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from deemix.api.deezer import Deezer
import deemix.utils.localpaths as localpaths
from deemix.utils.misc import getIDFromLink, getTypeFromLink, getBitrateInt
from deemix.app.queuemanager import addToQueue
from deemix.app.spotify import SpotifyHelper
from os import system as execute
import os.path as path import os.path as path
from os import mkdir from os import mkdir
import deemix.utils.localpaths as localpaths
from deemix.api.deezer import Deezer
from deemix.app.queuemanager import addToQueue
from deemix.app.spotify import SpotifyHelper
dz = Deezer() dz = Deezer()
sp = SpotifyHelper() sp = SpotifyHelper()
def requestValidArl(): def requestValidArl():
while True: while True:
arl = input("Paste here your arl:") arl = input("Paste here your arl:")
if dz.login_via_arl(arl): if dz.login_via_arl(arl):
break break
return arl return arl
def login(): def login():
configFolder = localpaths.getConfigFolder() configFolder = localpaths.getConfigFolder()
if not path.isdir(configFolder): if not path.isdir(configFolder):
mkdir(configFolder) mkdir(configFolder)
if path.isfile(path.join(configFolder, '.arl')): if path.isfile(path.join(configFolder, '.arl')):
with open(path.join(configFolder, '.arl'), 'r') as f: with open(path.join(configFolder, '.arl'), 'r') as f:
arl = f.read() arl = f.read()
if not dz.login_via_arl(arl): if not dz.login_via_arl(arl):
arl = requestValidArl() arl = requestValidArl()
else: else:
arl = requestValidArl() arl = requestValidArl()
with open(path.join(configFolder, '.arl'), 'w') as f: with open(path.join(configFolder, '.arl'), 'w') as f:
f.write(arl) f.write(arl)
def downloadLink(url, settings, bitrate=None): def downloadLink(url, settings, bitrate=None):
addToQueue(dz, sp, url, settings, bitrate) addToQueue(dz, sp, url, settings, bitrate)

View File

@ -1,69 +1,69 @@
{ {
"downloadLocation": "", "downloadLocation": "",
"tracknameTemplate": "%artist% - %title%", "tracknameTemplate": "%artist% - %title%",
"albumTracknameTemplate": "%tracknumber% - %title%", "albumTracknameTemplate": "%tracknumber% - %title%",
"playlistTracknameTemplate": "%position% - %artist% - %title%", "playlistTracknameTemplate": "%position% - %artist% - %title%",
"createPlaylistFolder": true, "createPlaylistFolder": true,
"playlistNameTemplate": "%playlist%", "playlistNameTemplate": "%playlist%",
"createArtistFolder": false, "createArtistFolder": false,
"artistNameTemplate": "%artist%", "artistNameTemplate": "%artist%",
"createAlbumFolder": true, "createAlbumFolder": true,
"albumNameTemplate": "%artist% - %album%", "albumNameTemplate": "%artist% - %album%",
"createCDFolder": true, "createCDFolder": true,
"createStructurePlaylist": false, "createStructurePlaylist": false,
"createSingleFolder": false, "createSingleFolder": false,
"padTracks": true, "padTracks": true,
"paddingSize": "0", "paddingSize": "0",
"illegalCharacterReplacer": "_", "illegalCharacterReplacer": "_",
"queueConcurrency": 3, "queueConcurrency": 3,
"maxBitrate": "3", "maxBitrate": "3",
"fallbackBitrate": true, "fallbackBitrate": true,
"fallbackSearch": false, "fallbackSearch": false,
"logErrors": true, "logErrors": true,
"logSearched": false, "logSearched": false,
"createM3U8File": false, "createM3U8File": false,
"syncedLyrics": false, "syncedLyrics": false,
"embeddedArtworkSize": 800, "embeddedArtworkSize": 800,
"localArtworkSize": 1400, "localArtworkSize": 1400,
"saveArtwork": true, "saveArtwork": true,
"coverImageTemplate": "cover", "coverImageTemplate": "cover",
"saveArtworkArtist": false, "saveArtworkArtist": false,
"artistImageTemplate": "folder", "artistImageTemplate": "folder",
"PNGcovers": false, "PNGcovers": false,
"jpegImageQuality": 80, "jpegImageQuality": 80,
"dateFormat": "Y-M-D", "dateFormat": "Y-M-D",
"removeAlbumVersion": false, "removeAlbumVersion": false,
"featuredToTitle": "0", "featuredToTitle": "0",
"titleCasing": "nothing", "titleCasing": "nothing",
"artistCasing": "nothing", "artistCasing": "nothing",
"executeCommand": "", "executeCommand": "",
"tags": { "tags": {
"title": true, "title": true,
"artist": true, "artist": true,
"album": true, "album": true,
"cover": true, "cover": true,
"trackNumber": true, "trackNumber": true,
"trackTotal": false, "trackTotal": false,
"discNumber": true, "discNumber": true,
"discTotal": false, "discTotal": false,
"albumArtist": true, "albumArtist": true,
"genre": true, "genre": true,
"year": true, "year": true,
"date": true, "date": true,
"explicit": false, "explicit": false,
"isrc": true, "isrc": true,
"length": true, "length": true,
"barcode": true, "barcode": true,
"bpm": true, "bpm": true,
"replayGain": false, "replayGain": false,
"label": true, "label": true,
"lyrics": false, "lyrics": false,
"copyright": false, "copyright": false,
"composer": false, "composer": false,
"involvedPeople": false, "involvedPeople": false,
"savePlaylistAsCompilation": false, "savePlaylistAsCompilation": false,
"useNullSeparator": false, "useNullSeparator": false,
"saveID3v1": true, "saveID3v1": true,
"multitagSeparator": "default" "multitagSeparator": "default"
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,5 @@
from deemix.utils.misc import getIDFromLink, getTypeFromLink, getBitrateInt
from concurrent.futures import ProcessPoolExecutor
from deemix.app.downloader import download from deemix.app.downloader import download
from deemix.utils.misc import getIDFromLink, getTypeFromLink, getBitrateInt
queue = [] queue = []
queueList = {} queueList = {}
@ -26,244 +25,264 @@ if its an album/playlist
collection collection
""" """
def generateQueueItem(dz, sp, url, settings, bitrate=None, albumAPI=None, interface=None): def generateQueueItem(dz, sp, url, settings, bitrate=None, albumAPI=None, interface=None):
forcedBitrate = getBitrateInt(bitrate) forcedBitrate = getBitrateInt(bitrate)
bitrate = forcedBitrate if forcedBitrate else settings['maxBitrate'] bitrate = forcedBitrate if forcedBitrate else settings['maxBitrate']
type = getTypeFromLink(url) type = getTypeFromLink(url)
id = getIDFromLink(url, type) id = getIDFromLink(url, type)
result = {} result = {}
if type == None or id == None: if type == None or id == None:
print("URL not recognized") print("URL not recognized")
result['error'] = "URL not recognized" result['error'] = "URL not recognized"
elif type == "track": elif type == "track":
trackAPI = dz.get_track_gw(id) trackAPI = dz.get_track_gw(id)
if albumAPI: if albumAPI:
trackAPI['_EXTRA_ALBUM'] = albumAPI trackAPI['_EXTRA_ALBUM'] = albumAPI
trackAPI['FILENAME_TEMPLATE'] = settings['tracknameTemplate'] trackAPI['FILENAME_TEMPLATE'] = settings['tracknameTemplate']
trackAPI['SINGLE_TRACK'] = True trackAPI['SINGLE_TRACK'] = True
result['title'] = trackAPI['SNG_TITLE'] result['title'] = trackAPI['SNG_TITLE']
if 'VERSION' in trackAPI and trackAPI['VERSION']: if 'VERSION' in trackAPI and trackAPI['VERSION']:
result['title'] += " " + trackAPI['VERSION'] result['title'] += " " + trackAPI['VERSION']
result['artist'] = trackAPI['ART_NAME'] result['artist'] = trackAPI['ART_NAME']
result['cover'] = f"https://e-cdns-images.dzcdn.net/images/cover/{trackAPI['ALB_PICTURE']}/75x75-000000-80-0-0.jpg" result[
result['size'] = 1 'cover'] = f"https://e-cdns-images.dzcdn.net/images/cover/{trackAPI['ALB_PICTURE']}/75x75-000000-80-0-0.jpg"
result['downloaded'] = 0 result['size'] = 1
result['failed'] = 0 result['downloaded'] = 0
result['progress'] = 0 result['failed'] = 0
result['type'] = 'track' result['progress'] = 0
result['id'] = id result['type'] = 'track'
result['bitrate'] = bitrate result['id'] = id
result['uuid'] = f"{result['type']}_{id}_{bitrate}" result['bitrate'] = bitrate
result['settings'] = settings or {} result['uuid'] = f"{result['type']}_{id}_{bitrate}"
result['single'] = trackAPI result['settings'] = settings or {}
result['single'] = trackAPI
elif type == "album": elif type == "album":
albumAPI = dz.get_album(id) albumAPI = dz.get_album(id)
albumAPI_gw = dz.get_album_gw(id) albumAPI_gw = dz.get_album_gw(id)
albumAPI['nb_disk'] = albumAPI_gw['NUMBER_DISK'] albumAPI['nb_disk'] = albumAPI_gw['NUMBER_DISK']
albumAPI['copyright'] = albumAPI_gw['COPYRIGHT'] albumAPI['copyright'] = albumAPI_gw['COPYRIGHT']
if albumAPI['nb_tracks'] == 1: if albumAPI['nb_tracks'] == 1:
return generateQueueItem(dz, sp, f"https://www.deezer.com/track/{albumAPI['tracks']['data'][0]['id']}", settings, bitrate, albumAPI) return generateQueueItem(dz, sp, f"https://www.deezer.com/track/{albumAPI['tracks']['data'][0]['id']}",
tracksArray = dz.get_album_tracks_gw(id) settings, bitrate, albumAPI)
tracksArray = dz.get_album_tracks_gw(id)
result['title'] = albumAPI['title'] result['title'] = albumAPI['title']
result['artist'] = albumAPI['artist']['name'] result['artist'] = albumAPI['artist']['name']
result['cover'] = albumAPI['cover_small'][:-24]+'/75x75-000000-80-0-0.jpg' result['cover'] = albumAPI['cover_small'][:-24] + '/75x75-000000-80-0-0.jpg'
result['size'] = albumAPI['nb_tracks'] result['size'] = albumAPI['nb_tracks']
result['downloaded'] = 0 result['downloaded'] = 0
result['failed'] = 0 result['failed'] = 0
result['progress'] = 0 result['progress'] = 0
result['type'] = 'album' result['type'] = 'album'
result['id'] = id result['id'] = id
result['bitrate'] = bitrate result['bitrate'] = bitrate
result['uuid'] = f"{result['type']}_{id}_{bitrate}" result['uuid'] = f"{result['type']}_{id}_{bitrate}"
result['settings'] = settings or {} result['settings'] = settings or {}
totalSize = len(tracksArray) totalSize = len(tracksArray)
result['collection'] = [] result['collection'] = []
for pos, trackAPI in enumerate(tracksArray, start=1): for pos, trackAPI in enumerate(tracksArray, start=1):
trackAPI['_EXTRA_ALBUM'] = albumAPI trackAPI['_EXTRA_ALBUM'] = albumAPI
trackAPI['POSITION'] = pos trackAPI['POSITION'] = pos
trackAPI['SIZE'] = totalSize trackAPI['SIZE'] = totalSize
trackAPI['FILENAME_TEMPLATE'] = settings['albumTracknameTemplate'] trackAPI['FILENAME_TEMPLATE'] = settings['albumTracknameTemplate']
result['collection'].append(trackAPI) result['collection'].append(trackAPI)
elif type == "playlist": elif type == "playlist":
playlistAPI = dz.get_playlist(id) playlistAPI = dz.get_playlist(id)
playlistTracksAPI = dz.get_playlist_tracks_gw(id) playlistTracksAPI = dz.get_playlist_tracks_gw(id)
playlistAPI['various_artist'] = dz.get_artist(5080) playlistAPI['various_artist'] = dz.get_artist(5080)
result['title'] = playlistAPI['title'] result['title'] = playlistAPI['title']
result['artist'] = playlistAPI['creator']['name'] result['artist'] = playlistAPI['creator']['name']
result['cover'] = playlistAPI['picture_small'][:-24]+'/75x75-000000-80-0-0.jpg' result['cover'] = playlistAPI['picture_small'][:-24] + '/75x75-000000-80-0-0.jpg'
result['size'] = playlistAPI['nb_tracks'] result['size'] = playlistAPI['nb_tracks']
result['downloaded'] = 0 result['downloaded'] = 0
result['failed'] = 0 result['failed'] = 0
result['progress'] = 0 result['progress'] = 0
result['type'] = 'playlist' result['type'] = 'playlist'
result['id'] = id result['id'] = id
result['bitrate'] = bitrate result['bitrate'] = bitrate
result['uuid'] = f"{result['type']}_{id}_{bitrate}" result['uuid'] = f"{result['type']}_{id}_{bitrate}"
result['settings'] = settings or {} result['settings'] = settings or {}
totalSize = len(playlistTracksAPI) totalSize = len(playlistTracksAPI)
result['collection'] = [] result['collection'] = []
for pos, trackAPI in enumerate(playlistTracksAPI, start=1): for pos, trackAPI in enumerate(playlistTracksAPI, start=1):
trackAPI['_EXTRA_PLAYLIST'] = playlistAPI trackAPI['_EXTRA_PLAYLIST'] = playlistAPI
trackAPI['POSITION'] = pos trackAPI['POSITION'] = pos
trackAPI['SIZE'] = totalSize trackAPI['SIZE'] = totalSize
trackAPI['FILENAME_TEMPLATE'] = settings['playlistTracknameTemplate'] trackAPI['FILENAME_TEMPLATE'] = settings['playlistTracknameTemplate']
result['collection'].append(trackAPI) result['collection'].append(trackAPI)
elif type == "artist":
artistAPI = dz.get_artist(id)
if interface:
interface.send("toast",
{'msg': f"Adding {artistAPI['name']} albums to queue", 'icon': 'loading', 'dismiss': False,
'id': 'artist_' + str(artistAPI['id'])})
artistAPITracks = dz.get_artist_albums(id)
albumList = []
for album in artistAPITracks['data']:
albumList.append(generateQueueItem(dz, sp, album['link'], settings, bitrate))
if interface:
interface.send("toast",
{'msg': f"Added {artistAPI['name']} albums to queue", 'icon': 'done', 'dismiss': True,
'id': 'artist_' + str(artistAPI['id'])})
return albumList
elif type == "spotifytrack":
result = {}
if not sp.spotifyEnabled:
print("Spotify Features is not setted up correctly.")
result['error'] = "Spotify Features is not setted up correctly."
return result
track_id = sp.get_trackid_spotify(dz, id, settings['fallbackSearch'])
if track_id != 0:
return generateQueueItem(dz, sp, f'https://www.deezer.com/track/{track_id}', settings, bitrate)
else:
print("Track not found on deezer!")
result['error'] = "Track not found on deezer!"
elif type == "spotifyalbum":
result = {}
if not sp.spotifyEnabled:
print("Spotify Features is not setted up correctly.")
result['error'] = "Spotify Features is not setted up correctly."
return result
album_id = sp.get_albumid_spotify(dz, id)
if album_id != 0:
return generateQueueItem(dz, sp, f'https://www.deezer.com/album/{album_id}', settings, bitrate)
else:
print("Album not found on deezer!")
result['error'] = "Album not found on deezer!"
elif type == "spotifyplaylist":
result = {}
if not sp.spotifyEnabled:
print("Spotify Features is not setted up correctly.")
result['error'] = "Spotify Features is not setted up correctly."
return result
if interface:
interface.send("toast",
{'msg': f"Converting spotify tracks to deezer tracks", 'icon': 'loading', 'dismiss': False,
'id': 'spotifyplaylist_' + str(id)})
playlist = sp.convert_spotify_playlist(dz, id, settings)
playlist['bitrate'] = bitrate
playlist['uuid'] = f"{playlist['type']}_{id}_{bitrate}"
result = playlist
if interface:
interface.send("toast", {'msg': f"Spotify playlist converted", 'icon': 'done', 'dismiss': True,
'id': 'spotifyplaylist_' + str(id)})
else:
print("URL not supported yet")
result['error'] = "URL not supported yet"
return result
elif type == "artist":
artistAPI = dz.get_artist(id)
if interface:
interface.send("toast", {'msg': f"Adding {artistAPI['name']} albums to queue", 'icon': 'loading', 'dismiss': False, 'id': 'artist_'+str(artistAPI['id'])})
artistAPITracks = dz.get_artist_albums(id)
albumList = []
for album in artistAPITracks['data']:
albumList.append(generateQueueItem(dz, sp, album['link'], settings, bitrate))
if interface:
interface.send("toast", {'msg': f"Added {artistAPI['name']} albums to queue", 'icon': 'done', 'dismiss': True, 'id': 'artist_'+str(artistAPI['id'])})
return albumList
elif type == "spotifytrack":
result = {}
if not sp.spotifyEnabled:
print("Spotify Features is not setted up correctly.")
result['error'] = "Spotify Features is not setted up correctly."
return result
track_id = sp.get_trackid_spotify(dz, id, settings['fallbackSearch'])
if track_id != 0:
return generateQueueItem(dz, sp, f'https://www.deezer.com/track/{track_id}', settings, bitrate)
else:
print("Track not found on deezer!")
result['error'] = "Track not found on deezer!"
elif type == "spotifyalbum":
result = {}
if not sp.spotifyEnabled:
print("Spotify Features is not setted up correctly.")
result['error'] = "Spotify Features is not setted up correctly."
return result
album_id = sp.get_albumid_spotify(dz, id)
if album_id != 0:
return generateQueueItem(dz, sp, f'https://www.deezer.com/album/{album_id}', settings, bitrate)
else:
print("Album not found on deezer!")
result['error'] = "Album not found on deezer!"
elif type == "spotifyplaylist":
result = {}
if not sp.spotifyEnabled:
print("Spotify Features is not setted up correctly.")
result['error'] = "Spotify Features is not setted up correctly."
return result
if interface:
interface.send("toast", {'msg': f"Converting spotify tracks to deezer tracks", 'icon': 'loading', 'dismiss': False, 'id': 'spotifyplaylist_'+str(id)})
playlist = sp.convert_spotify_playlist(dz, id, settings)
playlist['bitrate'] = bitrate
playlist['uuid'] = f"{playlist['type']}_{id}_{bitrate}"
result = playlist
if interface:
interface.send("toast", {'msg': f"Spotify playlist converted", 'icon': 'done', 'dismiss': True, 'id': 'spotifyplaylist_'+str(id)})
else:
print("URL not supported yet")
result['error'] = "URL not supported yet"
return result
def addToQueue(dz, sp, url, settings, bitrate=None, interface=None): def addToQueue(dz, sp, url, settings, bitrate=None, interface=None):
global currentItem, queueList, queue global currentItem, queueList, queue
if not dz.logged_in: if not dz.logged_in:
return "Not logged in" return "Not logged in"
queueItem = generateQueueItem(dz, sp, url, settings, bitrate, interface=interface) queueItem = generateQueueItem(dz, sp, url, settings, bitrate, interface=interface)
if type(queueItem) is list: if type(queueItem) is list:
for x in queueItem: for x in queueItem:
if 'error' in x: if 'error' in x:
continue continue
if x['uuid'] in list(queueList.keys()): if x['uuid'] in list(queueList.keys()):
print("Already in queue!") print("Already in queue!")
continue continue
if interface: if interface:
interface.send("addedToQueue", x) interface.send("addedToQueue", x)
queue.append(x['uuid']) queue.append(x['uuid'])
queueList[x['uuid']] = x queueList[x['uuid']] = x
else: else:
if 'error' in queueItem: if 'error' in queueItem:
if interface: if interface:
interface.send("toast", {'msg': queueItem['error'], 'icon': 'error'}) interface.send("toast", {'msg': queueItem['error'], 'icon': 'error'})
return False return False
if queueItem['uuid'] in list(queueList.keys()): if queueItem['uuid'] in list(queueList.keys()):
print("Already in queue!") print("Already in queue!")
if interface: if interface:
interface.send("toast", {'msg': f"{queueItem['title']} is already in queue!", 'icon': 'playlist_add_check'}) interface.send("toast",
return False {'msg': f"{queueItem['title']} is already in queue!", 'icon': 'playlist_add_check'})
if interface: return False
interface.send("addedToQueue", queueItem) if interface:
interface.send("toast", {'msg': f"{queueItem['title']} added to queue", 'icon': 'playlist_add'}) interface.send("addedToQueue", queueItem)
queue.append(queueItem['uuid']) interface.send("toast", {'msg': f"{queueItem['title']} added to queue", 'icon': 'playlist_add'})
queueList[queueItem['uuid']] = queueItem queue.append(queueItem['uuid'])
nextItem(dz, interface) queueList[queueItem['uuid']] = queueItem
return True nextItem(dz, interface)
return True
def nextItem(dz, interface=None): def nextItem(dz, interface=None):
global currentItem, queueList, queue global currentItem, queueList, queue
if currentItem != "": if currentItem != "":
return None return None
else: else:
if len(queue)>0: if len(queue) > 0:
currentItem = queue.pop(0) currentItem = queue.pop(0)
else: else:
return None return None
if interface: if interface:
interface.send("startDownload", currentItem) interface.send("startDownload", currentItem)
result = download(dz, queueList[currentItem], interface) result = download(dz, queueList[currentItem], interface)
callbackQueueDone(result) callbackQueueDone(result)
def callbackQueueDone(result): def callbackQueueDone(result):
global currentItem, queueList, queueComplete global currentItem, queueList, queueComplete
if 'cancel' in queueList[currentItem]: if 'cancel' in queueList[currentItem]:
del queueList[currentItem] del queueList[currentItem]
else: else:
queueComplete.append(currentItem) queueComplete.append(currentItem)
currentItem = "" currentItem = ""
nextItem(result['dz'], result['interface']) nextItem(result['dz'], result['interface'])
def getQueue(): def getQueue():
global currentItem, queueList, queue, queueComplete global currentItem, queueList, queue, queueComplete
return (queue, queueComplete, queueList, currentItem) return (queue, queueComplete, queueList, currentItem)
def removeFromQueue(uuid, interface=None): def removeFromQueue(uuid, interface=None):
global currentItem, queueList, queue, queueComplete global currentItem, queueList, queue, queueComplete
if uuid == currentItem: if uuid == currentItem:
if interface: if interface:
interface.send('toast', {'msg': "Cancelling current item.", 'icon':'loading', 'dismiss': False, 'id':'cancelling_'+uuid}) interface.send('toast', {'msg': "Cancelling current item.", 'icon': 'loading', 'dismiss': False,
queueList[uuid]['cancel'] = True 'id': 'cancelling_' + uuid})
elif uuid in queue: queueList[uuid]['cancel'] = True
queue.remove(uuid) elif uuid in queue:
del queueList[uuid] queue.remove(uuid)
if interface: del queueList[uuid]
interface.send("removedFromQueue", uuid) if interface:
elif uuid in queueComplete: interface.send("removedFromQueue", uuid)
queueComplete.remove(uuid) elif uuid in queueComplete:
del queueList[uuid] queueComplete.remove(uuid)
if interface: del queueList[uuid]
interface.send("removedFromQueue", uuid) if interface:
interface.send("removedFromQueue", uuid)
def cancelAllDownloads(interface=None): def cancelAllDownloads(interface=None):
global currentItem, queueList, queue, queueComplete global currentItem, queueList, queue, queueComplete
queue = [] queue = []
queueComplete = [] queueComplete = []
if currentItem != "": if currentItem != "":
if interface: if interface:
interface.send('toast', {'msg': "Cancelling current item.", 'icon':'loading', 'dismiss': False, 'id':'cancelling_'+currentItem}) interface.send('toast', {'msg': "Cancelling current item.", 'icon': 'loading', 'dismiss': False,
queueList[currentItem]['cancel'] = True 'id': 'cancelling_' + currentItem})
for uuid in list(queueList.keys()): queueList[currentItem]['cancel'] = True
if uuid != currentItem: for uuid in list(queueList.keys()):
del queueList[uuid] if uuid != currentItem:
if interface: del queueList[uuid]
interface.send("removedAllDownloads", currentItem) if interface:
interface.send("removedAllDownloads", currentItem)
def removeFinishedDownloads(interface=None): def removeFinishedDownloads(interface=None):
global queueList, queueComplete global queueList, queueComplete
for uuid in queueComplete: for uuid in queueComplete:
del queueList[uuid] del queueList[uuid]
queueComplete = [] queueComplete = []
if interface: if interface:
interface.send("removedFinishedDownloads") interface.send("removedFinishedDownloads")

View File

@ -1,57 +1,61 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os.path as path
from os import mkdir, rmdir
import json import json
import os.path as path
from os import mkdir
import deemix.utils.localpaths as localpaths import deemix.utils.localpaths as localpaths
settings = {} settings = {}
defaultSettings = {} defaultSettings = {}
def initSettings(): def initSettings():
global settings global settings
global defaultSettings global defaultSettings
currentFolder = path.abspath(path.dirname(__file__)) currentFolder = path.abspath(path.dirname(__file__))
configFolder = localpaths.getConfigFolder() configFolder = localpaths.getConfigFolder()
if not path.isdir(configFolder): if not path.isdir(configFolder):
mkdir(configFolder) mkdir(configFolder)
with open(path.join(currentFolder, 'default.json'), 'r') as d: with open(path.join(currentFolder, 'default.json'), 'r') as d:
defaultSettings = json.load(d) defaultSettings = json.load(d)
if not path.isfile(path.join(configFolder, 'config.json')): if not path.isfile(path.join(configFolder, 'config.json')):
with open(path.join(configFolder, 'config.json'), 'w') as f: with open(path.join(configFolder, 'config.json'), 'w') as f:
json.dump(defaultSettings, f, indent=2) json.dump(defaultSettings, f, indent=2)
with open(path.join(configFolder, 'config.json'), 'r') as configFile: with open(path.join(configFolder, 'config.json'), 'r') as configFile:
settings = json.load(configFile) settings = json.load(configFile)
settingsCheck() settingsCheck()
if settings['downloadLocation'] == "": if settings['downloadLocation'] == "":
settings['downloadLocation'] = path.join(localpaths.getHomeFolder(), 'deemix Music') settings['downloadLocation'] = path.join(localpaths.getHomeFolder(), 'deemix Music')
saveSettings(settings) saveSettings(settings)
if not path.isdir(settings['downloadLocation']): if not path.isdir(settings['downloadLocation']):
mkdir(settings['downloadLocation']) mkdir(settings['downloadLocation'])
return settings return settings
def getSettings(): def getSettings():
global settings global settings
return settings return settings
def saveSettings(newSettings): def saveSettings(newSettings):
global settings global settings
settings = newSettings settings = newSettings
with open(path.join(localpaths.getConfigFolder(), 'config.json'), 'w') as configFile: with open(path.join(localpaths.getConfigFolder(), 'config.json'), 'w') as configFile:
json.dump(settings, configFile, indent=2) json.dump(settings, configFile, indent=2)
return True return True
def settingsCheck(): def settingsCheck():
global settings global settings
global defaultSettings global defaultSettings
changes = 0 changes = 0
for x in defaultSettings: for x in defaultSettings:
if not x in settings or type(settings[x]) != type(defaultSettings[x]): if not x in settings or type(settings[x]) != type(defaultSettings[x]):
settings[x] = defaultSettings[x] settings[x] = defaultSettings[x]
changes+=1 changes += 1
for x in defaultSettings['tags']: for x in defaultSettings['tags']:
if not x in settings['tags'] or type(settings['tags'][x]) != type(defaultSettings['tags'][x]): if not x in settings['tags'] or type(settings['tags'][x]) != type(defaultSettings['tags'][x]):
settings['tags'][x] = defaultSettings['tags'][x] settings['tags'][x] = defaultSettings['tags'][x]
changes+=1 changes += 1
if changes > 0: if changes > 0:
saveSettings(settings) saveSettings(settings)

View File

@ -1,172 +1,179 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os.path as path
from os import mkdir, rmdir
import json import json
import os.path as path
import deemix.utils.localpaths as localpaths from os import mkdir
import spotipy import spotipy
from spotipy.oauth2 import SpotifyClientCredentials from spotipy.oauth2 import SpotifyClientCredentials
import deemix.utils.localpaths as localpaths
class SpotifyHelper: class SpotifyHelper:
def __init__(self): def __init__(self):
self.credentials = {} self.credentials = {}
self.spotifyEnabled = False self.spotifyEnabled = False
self.sp = None self.sp = None
self.initCredentials() self.initCredentials()
def initCredentials(self): def initCredentials(self):
configFolder = localpaths.getConfigFolder() configFolder = localpaths.getConfigFolder()
if not path.isdir(configFolder): if not path.isdir(configFolder):
mkdir(configFolder) mkdir(configFolder)
if not path.isfile(path.join(configFolder, 'authCredentials.json')): if not path.isfile(path.join(configFolder, 'authCredentials.json')):
with open(path.join(configFolder, 'authCredentials.json'), 'w') as f: with open(path.join(configFolder, 'authCredentials.json'), 'w') as f:
json.dump({'clientId': "", 'clientSecret': ""}, f, indent=2) json.dump({'clientId': "", 'clientSecret': ""}, f, indent=2)
with open(path.join(configFolder, 'authCredentials.json'), 'r') as credentialsFile: with open(path.join(configFolder, 'authCredentials.json'), 'r') as credentialsFile:
self.credentials = json.load(credentialsFile) self.credentials = json.load(credentialsFile)
self.checkCredentials() self.checkCredentials()
def checkCredentials(self): def checkCredentials(self):
if self.credentials['clientId'] == "" or self.credentials['clientSecret'] == "": if self.credentials['clientId'] == "" or self.credentials['clientSecret'] == "":
spotifyEnabled = False spotifyEnabled = False
else: else:
try: try:
self.createSpotifyConnection() self.createSpotifyConnection()
self.sp.user_playlists('spotify') self.sp.user_playlists('spotify')
self.spotifyEnabled = True self.spotifyEnabled = True
except Exception as e: except Exception as e:
self.spotifyEnabled = False self.spotifyEnabled = False
return self.spotifyEnabled return self.spotifyEnabled
def getCredentials(self): def getCredentials(self):
return self.credentials return self.credentials
def setCredentials(self, spotifyCredentials): def setCredentials(self, spotifyCredentials):
configFolder = localpaths.getConfigFolder() configFolder = localpaths.getConfigFolder()
with open(path.join(configFolder, 'authCredentials.json'), 'w') as f: with open(path.join(configFolder, 'authCredentials.json'), 'w') as f:
json.dump(spotifyCredentials, f, indent=2) json.dump(spotifyCredentials, f, indent=2)
self.credentials = spotifyCredentials self.credentials = spotifyCredentials
self.checkCredentials() self.checkCredentials()
def createSpotifyConnection(self): def createSpotifyConnection(self):
client_credentials_manager = SpotifyClientCredentials(client_id=self.credentials['clientId'], client_secret=self.credentials['clientSecret']) client_credentials_manager = SpotifyClientCredentials(client_id=self.credentials['clientId'],
self.sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager) client_secret=self.credentials['clientSecret'])
self.sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
def _convert_playlist_structure(self, spotify_obj): def _convert_playlist_structure(self, spotify_obj):
if len(spotify_obj['images']): if len(spotify_obj['images']):
url = spotify_obj['images'][0]['url'] url = spotify_obj['images'][0]['url']
else: else:
url = "https://e-cdns-images.dzcdn.net/images/cover/d41d8cd98f00b204e9800998ecf8427e/75x75-000000-80-0-0.jpg" url = "https://e-cdns-images.dzcdn.net/images/cover/d41d8cd98f00b204e9800998ecf8427e/75x75-000000-80-0-0.jpg"
deezer_obj = { deezer_obj = {
'checksum': spotify_obj['snapshot_id'], 'checksum': spotify_obj['snapshot_id'],
'collaborative': spotify_obj['collaborative'], 'collaborative': spotify_obj['collaborative'],
'creation_date': "????-00-00", 'creation_date': "????-00-00",
'creator': {'id': spotify_obj['owner']['id'], 'name': spotify_obj['owner']['display_name'], 'tracklist': spotify_obj['owner']['href'], 'type': "user"}, 'creator': {'id': spotify_obj['owner']['id'], 'name': spotify_obj['owner']['display_name'],
'description': spotify_obj['description'], 'tracklist': spotify_obj['owner']['href'], 'type': "user"},
'duration': 0, 'description': spotify_obj['description'],
'fans': spotify_obj['followers']['total'], 'duration': 0,
'id': spotify_obj['id'], 'fans': spotify_obj['followers']['total'],
'is_loved_track': False, 'id': spotify_obj['id'],
'link': spotify_obj['external_urls']['spotify'], 'is_loved_track': False,
'nb_tracks': spotify_obj['tracks']['total'], 'link': spotify_obj['external_urls']['spotify'],
'picture': url, 'nb_tracks': spotify_obj['tracks']['total'],
'picture_big': url, 'picture': url,
'picture_medium': url, 'picture_big': url,
'picture_small': url, 'picture_medium': url,
'picture_xl': url, 'picture_small': url,
'public': spotify_obj['public'], 'picture_xl': url,
'share': spotify_obj['external_urls']['spotify'], 'public': spotify_obj['public'],
'title': spotify_obj['name'], 'share': spotify_obj['external_urls']['spotify'],
'tracklist': spotify_obj['tracks']['href'], 'title': spotify_obj['name'],
'type': "playlist" 'tracklist': spotify_obj['tracks']['href'],
} 'type': "playlist"
return deezer_obj }
return deezer_obj
def get_trackid_spotify(self, dz, track_id, fallbackSearch, spotifyTrack=None): def get_trackid_spotify(self, dz, track_id, fallbackSearch, spotifyTrack=None):
if not self.spotifyEnabled: if not self.spotifyEnabled:
raise spotifyFeaturesNotEnabled raise spotifyFeaturesNotEnabled
if not spotifyTrack: if not spotifyTrack:
spotify_track = self.sp.track(track_id) spotify_track = self.sp.track(track_id)
else: else:
spotify_track = spotifyTrack spotify_track = spotifyTrack
dz_track = 0 dz_track = 0
if 'external_ids' in spotify_track and 'isrc' in spotify_track['external_ids']: if 'external_ids' in spotify_track and 'isrc' in spotify_track['external_ids']:
try: try:
dz_track = dz.get_track_by_ISRC(spotify_track['external_ids']['isrc']) dz_track = dz.get_track_by_ISRC(spotify_track['external_ids']['isrc'])
dz_track = dz_track['id'] if 'id' in dz_track else 0 dz_track = dz_track['id'] if 'id' in dz_track else 0
except: except:
dz_track = dz.get_track_from_metadata(spotify_track['artists'][0]['name'], spotify_track['name'], spotify_track['album']['name']) if fallbackSearch else 0 dz_track = dz.get_track_from_metadata(spotify_track['artists'][0]['name'], spotify_track['name'],
elif fallbackSearch: spotify_track['album']['name']) if fallbackSearch else 0
dz_track = dz.get_track_from_metadata(spotify_track['artists'][0]['name'], spotify_track['name'], spotify_track['album']['name']) elif fallbackSearch:
return dz_track dz_track = dz.get_track_from_metadata(spotify_track['artists'][0]['name'], spotify_track['name'],
spotify_track['album']['name'])
return dz_track
def get_albumid_spotify(self, dz, album_id): def get_albumid_spotify(self, dz, album_id):
if not self.spotifyEnabled: if not self.spotifyEnabled:
raise spotifyFeaturesNotEnabled raise spotifyFeaturesNotEnabled
spotify_album = self.sp.album(album_id) spotify_album = self.sp.album(album_id)
dz_album = 0 dz_album = 0
if 'external_ids' in spotify_album and 'upc' in spotify_album['external_ids']: if 'external_ids' in spotify_album and 'upc' in spotify_album['external_ids']:
try: try:
dz_album = dz.get_album_by_UPC(spotify_album['external_ids']['upc']) dz_album = dz.get_album_by_UPC(spotify_album['external_ids']['upc'])
dz_album = dz_album['id'] if 'id' in dz_album else 0 dz_album = dz_album['id'] if 'id' in dz_album else 0
except: except:
try: try:
dz_album = dz.get_album_by_UPC(int(spotify_album['external_ids']['upc'])) dz_album = dz.get_album_by_UPC(int(spotify_album['external_ids']['upc']))
dz_album = dz_album['id'] if 'id' in dz_album else 0 dz_album = dz_album['id'] if 'id' in dz_album else 0
except: except:
dz_album = 0 dz_album = 0
return dz_album return dz_album
def convert_spotify_playlist(self, dz, playlist_id, settings):
if not self.spotifyEnabled:
raise spotifyFeaturesNotEnabled
spotify_playlist = self.sp.playlist(playlist_id)
result = {
'title': spotify_playlist['name'],
'artist': spotify_playlist['owner']['display_name'],
'size': spotify_playlist['tracks']['total'],
'downloaded': 0,
'failed': 0,
'progress': 0,
'type': 'spotify_playlist',
'settings': settings or {},
'id': playlist_id
}
if len(spotify_playlist['images']):
result['cover'] = spotify_playlist['images'][0]['url']
else:
result[
'cover'] = "https://e-cdns-images.dzcdn.net/images/cover/d41d8cd98f00b204e9800998ecf8427e/75x75-000000-80-0-0.jpg"
playlistAPI = self._convert_playlist_structure(spotify_playlist)
playlistAPI['various_artist'] = dz.get_artist(5080)
tracklist = spotify_playlist['tracks']['items']
result['collection'] = []
while spotify_playlist['tracks']['next']:
spotify_playlist['tracks'] = self.sp.next(spotify_playlist['tracks'])
tracklist += spotify_playlist['tracks']['items']
totalSize = len(tracklist)
for pos, track in enumerate(tracklist, start=1):
trackID = self.get_trackid_spotify(dz, 0, settings['fallbackSearch'], track['track'])
if trackID == 0:
deezerTrack = {
'SNG_ID': 0,
'SNG_TITLE': track['track']['name'],
'DURATION': 0,
'MD5_ORIGIN': 0,
'MEDIA_VERSION': 0,
'FILESIZE': 0,
'ALB_TITLE': track['track']['album']['name'],
'ALB_PICTURE': "",
'ART_ID': 0,
'ART_NAME': track['track']['artists'][0]['name']
}
else:
deezerTrack = dz.get_track_gw(trackID)
deezerTrack['_EXTRA_PLAYLIST'] = playlistAPI
deezerTrack['POSITION'] = pos
deezerTrack['SIZE'] = totalSize
deezerTrack['FILENAME_TEMPLATE'] = settings['playlistTracknameTemplate']
result['collection'].append(deezerTrack)
return result
def convert_spotify_playlist(self, dz, playlist_id, settings):
if not self.spotifyEnabled:
raise spotifyFeaturesNotEnabled
spotify_playlist = self.sp.playlist(playlist_id)
result = {
'title': spotify_playlist['name'],
'artist': spotify_playlist['owner']['display_name'],
'size': spotify_playlist['tracks']['total'],
'downloaded': 0,
'failed': 0,
'progress': 0,
'type': 'spotify_playlist',
'settings': settings or {},
'id': playlist_id
}
if len(spotify_playlist['images']):
result['cover'] = spotify_playlist['images'][0]['url']
else:
result['cover'] = "https://e-cdns-images.dzcdn.net/images/cover/d41d8cd98f00b204e9800998ecf8427e/75x75-000000-80-0-0.jpg"
playlistAPI = self._convert_playlist_structure(spotify_playlist)
playlistAPI['various_artist'] = dz.get_artist(5080)
tracklist = spotify_playlist['tracks']['items']
result['collection'] = []
while spotify_playlist['tracks']['next']:
spotify_playlist['tracks'] = self.sp.next(spotify_playlist['tracks'])
tracklist += spotify_playlist['tracks']['items']
totalSize = len(tracklist)
for pos, track in enumerate(tracklist, start=1):
trackID = self.get_trackid_spotify(dz, 0, settings['fallbackSearch'], track['track'])
if trackID == 0:
deezerTrack = {
'SNG_ID': 0,
'SNG_TITLE': track['track']['name'],
'DURATION': 0,
'MD5_ORIGIN': 0,
'MEDIA_VERSION': 0,
'FILESIZE': 0,
'ALB_TITLE': track['track']['album']['name'],
'ALB_PICTURE': "",
'ART_ID': 0,
'ART_NAME': track['track']['artists'][0]['name']
}
else:
deezerTrack = dz.get_track_gw(trackID)
deezerTrack['_EXTRA_PLAYLIST'] = playlistAPI
deezerTrack['POSITION'] = pos
deezerTrack['SIZE'] = totalSize
deezerTrack['FILENAME_TEMPLATE'] = settings['playlistTracknameTemplate']
result['collection'].append(deezerTrack)
return result
class spotifyFeaturesNotEnabled(Exception): class spotifyFeaturesNotEnabled(Exception):
pass pass

View File

@ -1,21 +1,24 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import sys
import os.path as path import os.path as path
import sys
from os import getenv from os import getenv
userdata = "" userdata = ""
homedata = path.expanduser("~") homedata = path.expanduser("~")
if getenv("APPDATA"): if getenv("APPDATA"):
userdata = getenv("APPDATA") + path.sep + "deemix" + path.sep userdata = getenv("APPDATA") + path.sep + "deemix" + path.sep
elif sys.platform.startswith('darwin'): elif sys.platform.startswith('darwin'):
userdata = homedata + '/Library/Application Support/deemix/' userdata = homedata + '/Library/Application Support/deemix/'
elif getenv("XDG_CONFIG_HOME"): elif getenv("XDG_CONFIG_HOME"):
userdata = getenv("XDG_CONFIG_HOME") + '/deemix/'; userdata = getenv("XDG_CONFIG_HOME") + '/deemix/';
else: else:
userdata = homedata + '/.config/deemix/'; userdata = homedata + '/.config/deemix/';
def getHomeFolder(): def getHomeFolder():
return homedata return homedata
def getConfigFolder(): def getConfigFolder():
return userdata return userdata

View File

@ -1,94 +1,98 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import re import re
def getBitrateInt(txt): def getBitrateInt(txt):
txt = str(txt) txt = str(txt)
if txt in ['flac', 'lossless', '9']: if txt in ['flac', 'lossless', '9']:
return 9 return 9
elif txt in ['mp3', '320', '3']: elif txt in ['mp3', '320', '3']:
return 3 return 3
elif txt in ['128', '1']: elif txt in ['128', '1']:
return 1 return 1
elif txt in ['360', '360_hq', '15']: elif txt in ['360', '360_hq', '15']:
return 15 return 15
elif txt in ['360_mq', '14']: elif txt in ['360_mq', '14']:
return 14 return 14
elif txt in ['360_lq', '13']: elif txt in ['360_lq', '13']:
return 13 return 13
else: else:
return None return None
def changeCase(string, type): def changeCase(string, type):
if type == "lower": if type == "lower":
return string.lower() return string.lower()
elif type == "upper": elif type == "upper":
return string.upper() return string.upper()
elif type == "start": elif type == "start":
string = string.split(" ") string = string.split(" ")
res = [] res = []
for index, value in enumerate(string): for index, value in enumerate(string):
res.append(value[0].upper() + value[0:].lower()) res.append(value[0].upper() + value[0:].lower())
res = " ".join(res) res = " ".join(res)
return res return res
elif type == "sentence": elif type == "sentence":
res = string[0].upper() + string[0:].lower() res = string[0].upper() + string[0:].lower()
return res return res
else: else:
return string return string
def getIDFromLink(link, type): def getIDFromLink(link, type):
if '?' in link: if '?' in link:
link = link[:link.find('?')] link = link[:link.find('?')]
if link.endswith("/"): if link.endswith("/"):
link = link[:-1] link = link[:-1]
if link.startswith("http") and 'open.spotify.com/' in link: if link.startswith("http") and 'open.spotify.com/' in link:
if type == "spotifyplaylist": if type == "spotifyplaylist":
return link[link.find("/playlist/") + 10:] return link[link.find("/playlist/") + 10:]
if type == "spotifytrack": if type == "spotifytrack":
return link[link.find("/track/") + 7:] return link[link.find("/track/") + 7:]
if type == "spotifyalbum": if type == "spotifyalbum":
return link[link.find("/album/") + 7:] return link[link.find("/album/") + 7:]
elif link.startswith("spotify:"): elif link.startswith("spotify:"):
if type == "spotifyplaylist": if type == "spotifyplaylist":
return link[link.find("playlist:") + 9:] return link[link.find("playlist:") + 9:]
if type == "spotifytrack": if type == "spotifytrack":
return link[link.find("track:") + 6:] return link[link.find("track:") + 6:]
if type == "spotifyalbum": if type == "spotifyalbum":
return link[link.find("album:") + 6:] return link[link.find("album:") + 6:]
elif type == "artisttop": elif type == "artisttop":
return re.search(r"\/artist\/(\d+)\/top_track", link)[1] return re.search(r"\/artist\/(\d+)\/top_track", link)[1]
else: else:
return link[link.rfind("/") + 1:] return link[link.rfind("/") + 1:]
def getTypeFromLink(link): def getTypeFromLink(link):
type = '' type = ''
if 'spotify' in link: if 'spotify' in link:
type = 'spotify' type = 'spotify'
if 'playlist' in link: if 'playlist' in link:
type += 'playlist' type += 'playlist'
elif 'track' in link: elif 'track' in link:
type += 'track' type += 'track'
elif 'album' in link: elif 'album' in link:
type += 'album' type += 'album'
elif 'deezer' in link: elif 'deezer' in link:
if '/track' in link: if '/track' in link:
type = 'track' type = 'track'
elif '/playlist' in link: elif '/playlist' in link:
type = 'playlist' type = 'playlist'
elif '/album' in link: elif '/album' in link:
type = 'album' type = 'album'
elif re.search("\/artist\/(\d+)\/top_track", link): elif re.search("\/artist\/(\d+)\/top_track", link):
type = 'artisttop' type = 'artisttop'
elif '/artist' in link: elif '/artist' in link:
type = 'artist' type = 'artist'
return type return type
def isValidLink(text): def isValidLink(text):
if text.lower().startswith("http"): if text.lower().startswith("http"):
if "deezer.com" in text.lower() or "open.spotify.com" in text.lower(): if "deezer.com" in text.lower() or "open.spotify.com" in text.lower():
return True return True
elif text.lower().startswith("spotify:"): elif text.lower().startswith("spotify:"):
return True return True
return False return False

View File

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

View File

@ -1,135 +1,139 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from mutagen.flac import FLAC, Picture from mutagen.flac import FLAC, Picture
from mutagen.id3 import ID3, ID3NoHeaderError, TXXX, TIT2, TPE1, TALB, TPE2, TRCK, TPOS, TCON, TYER, TDAT, TLEN, TBPM, \ from mutagen.id3 import ID3, ID3NoHeaderError, TXXX, TIT2, TPE1, TALB, TPE2, TRCK, TPOS, TCON, TYER, TDAT, TLEN, TBPM, \
TPUB, TSRC, USLT, APIC, IPLS, TCOM, TCOP, TCMP TPUB, TSRC, USLT, APIC, IPLS, TCOM, TCOP, TCMP
def tagID3(stream, track, save): def tagID3(stream, track, save):
try: try:
tag = ID3(stream) tag = ID3(stream)
except ID3NoHeaderError: except ID3NoHeaderError:
tag = ID3() tag = ID3()
if save['title']: if save['title']:
tag.add(TIT2(text=track['title'])) tag.add(TIT2(text=track['title']))
if save['artist']: if save['artist']:
if save['multitagSeparator'] != "default": if save['multitagSeparator'] != "default":
tag.add(TPE1(text=track['artistsString'])) tag.add(TPE1(text=track['artistsString']))
tag.add(TXXX(desc="ARTISTS", text=track['artists'])) tag.add(TXXX(desc="ARTISTS", text=track['artists']))
else: else:
tag.add(TPE1(text=track['artists'])) tag.add(TPE1(text=track['artists']))
if save['album']: if save['album']:
tag.add(TALB(text=track['album']['title'])) tag.add(TALB(text=track['album']['title']))
if save['albumArtist']: if save['albumArtist']:
tag.add(TPE2(text=track['album']['artists'])) tag.add(TPE2(text=track['album']['artists']))
if save['trackNumber']: if save['trackNumber']:
tag.add(TRCK(text=str(track['trackNumber'])+("/"+str(track['album']['trackTotal']) if save['trackTotal'] else ""))) tag.add(TRCK(
if save['discNumber']: text=str(track['trackNumber']) + ("/" + str(track['album']['trackTotal']) if save['trackTotal'] else "")))
tag.add(TPOS(text=str(track['discNumber'])+("/"+str(track['album']['discTotal']) if save['discTotal'] else ""))) if save['discNumber']:
if save['genre']: tag.add(
tag.add(TCON(text=track['album']['genre'])) TPOS(text=str(track['discNumber']) + ("/" + str(track['album']['discTotal']) if save['discTotal'] else "")))
if save['year']: if save['genre']:
tag.add(TYER(text=str(track['date']['year']))) tag.add(TCON(text=track['album']['genre']))
if save['date']: if save['year']:
tag.add(TDAT(text=str(track['date']['month']) + str(track['date']['day']))) tag.add(TYER(text=str(track['date']['year'])))
if save['length']: if save['date']:
tag.add(TLEN(text=str(track['duration']))) tag.add(TDAT(text=str(track['date']['month']) + str(track['date']['day'])))
if save['bpm']: if save['length']:
tag.add(TBPM(text=str(track['bpm']))) tag.add(TLEN(text=str(track['duration'])))
if save['label']: if save['bpm']:
tag.add(TPUB(text=track['album']['label'])) tag.add(TBPM(text=str(track['bpm'])))
if save['isrc']: if save['label']:
tag.add(TSRC(text=track['ISRC'])) tag.add(TPUB(text=track['album']['label']))
if save['barcode']: if save['isrc']:
tag.add(TXXX(desc="BARCODE", text=track['album']['barcode'])) tag.add(TSRC(text=track['ISRC']))
if save['explicit']: if save['barcode']:
tag.add(TXXX(desc="ITUNESADVISORY", text="1" if track['explicit'] else "0")) tag.add(TXXX(desc="BARCODE", text=track['album']['barcode']))
if save['replayGain']: if save['explicit']:
tag.add(TXXX(desc="REPLAYGAIN_TRACK_GAIN", text=track['replayGain'])) tag.add(TXXX(desc="ITUNESADVISORY", text="1" if track['explicit'] else "0"))
if 'unsync' in track['lyrics'] and save['lyrics']: if save['replayGain']:
tag.add(USLT(text=track['lyrics']['unsync'])) tag.add(TXXX(desc="REPLAYGAIN_TRACK_GAIN", text=track['replayGain']))
involved_people = [] if 'unsync' in track['lyrics'] and save['lyrics']:
for role in track['contributors']: tag.add(USLT(text=track['lyrics']['unsync']))
if role in ['author', 'engineer', 'mixer', 'producer', 'writer']: involved_people = []
for person in track['contributors'][role]: for role in track['contributors']:
involved_people.append([role, person]) if role in ['author', 'engineer', 'mixer', 'producer', 'writer']:
elif role == 'composer' and save['composer']: for person in track['contributors'][role]:
tag.add(TCOM(text=track['contributors']['composer'])) involved_people.append([role, person])
if len(involved_people) > 0 and save['involvedPeople']: elif role == 'composer' and save['composer']:
tag.add(IPLS(people=involved_people)) tag.add(TCOM(text=track['contributors']['composer']))
if save['copyright']: if len(involved_people) > 0 and save['involvedPeople']:
tag.add(TCOP(text=track['copyright'])) tag.add(IPLS(people=involved_people))
if save['savePlaylistAsCompilation']: if save['copyright']:
tag.add(TCMP(text="1")) tag.add(TCOP(text=track['copyright']))
if save['cover'] and track['album']['picPath']: if save['savePlaylistAsCompilation']:
with open(track['album']['picPath'], 'rb') as f: tag.add(TCMP(text="1"))
tag.add(APIC(3, 'image/jpeg' if track['album']['picPath'].endswith('jpg') else 'image/png', 3, data=f.read())) if save['cover'] and track['album']['picPath']:
with open(track['album']['picPath'], 'rb') as f:
tag.add(
APIC(3, 'image/jpeg' if track['album']['picPath'].endswith('jpg') else 'image/png', 3, data=f.read()))
tag.save(stream, v1=2 if save['saveID3v1'] else 0, v2_version=3, v23_sep=None if save['useNullSeparator'] else ' / ') tag.save(stream, v1=2 if save['saveID3v1'] else 0, v2_version=3,
v23_sep=None if save['useNullSeparator'] else ' / ')
def tagFLAC(stream, track, save): def tagFLAC(stream, track, save):
tag = FLAC(stream) tag = FLAC(stream)
if save['title']: if save['title']:
tag["TITLE"] = track['title'] tag["TITLE"] = track['title']
if save['artist']: if save['artist']:
if save['multitagSeparator'] != "default": if save['multitagSeparator'] != "default":
tag["ARTIST"] = track['artistsString'] tag["ARTIST"] = track['artistsString']
tag["ARTISTS"] = track['artists'] tag["ARTISTS"] = track['artists']
else: else:
tag["ARTIST"] = track['artists'] tag["ARTIST"] = track['artists']
if save['album']: if save['album']:
tag["ALBUM"] = track['album']['title'] tag["ALBUM"] = track['album']['title']
if save['albumArtist']: if save['albumArtist']:
tag["ALBUMARTIST"] = track['album']['artists'] tag["ALBUMARTIST"] = track['album']['artists']
if save['trackNumber']: if save['trackNumber']:
tag["TRACKNUMBER"] = str(track['trackNumber']) tag["TRACKNUMBER"] = str(track['trackNumber'])
if save['trackTotal']: if save['trackTotal']:
tag["TRACKTOTAL"] = str(track['album']['trackTotal']) tag["TRACKTOTAL"] = str(track['album']['trackTotal'])
if save['discNumber']: if save['discNumber']:
tag["DISCNUMBER"] = str(track['discNumber']) tag["DISCNUMBER"] = str(track['discNumber'])
if save['discTotal']: if save['discTotal']:
tag["DISCTOTAL"] = str(track['album']['discTotal']) tag["DISCTOTAL"] = str(track['album']['discTotal'])
if save['genre']: if save['genre']:
tag["GENRE"] = track['album']['genre'] tag["GENRE"] = track['album']['genre']
if save['year']: if save['year']:
tag["YEAR"] = str(track['date']['year']) tag["YEAR"] = str(track['date']['year'])
if save['date']: if save['date']:
tag["DATE"] = track['dateString'] tag["DATE"] = track['dateString']
if save['length']: if save['length']:
tag["LENGTH"] = str(track['duration']) tag["LENGTH"] = str(track['duration'])
if save['bpm']: if save['bpm']:
tag["BPM"] = str(track['bpm']) tag["BPM"] = str(track['bpm'])
if save['label']: if save['label']:
tag["PUBLISHER"] = track['album']['label'] tag["PUBLISHER"] = track['album']['label']
if save['isrc']: if save['isrc']:
tag["ISRC"] = track['ISRC'] tag["ISRC"] = track['ISRC']
if save['barcode']: if save['barcode']:
tag["BARCODE"] = track['album']['barcode'] tag["BARCODE"] = track['album']['barcode']
if save['explicit']: if save['explicit']:
tag["ITUNESADVISORY"] = "1" if track['explicit'] else "0" tag["ITUNESADVISORY"] = "1" if track['explicit'] else "0"
if save['replayGain']: if save['replayGain']:
tag["REPLAYGAIN_TRACK_GAIN"] = track['replayGain'] tag["REPLAYGAIN_TRACK_GAIN"] = track['replayGain']
if 'unsync' in track['lyrics'] and save['lyrics']: if 'unsync' in track['lyrics'] and save['lyrics']:
tag["LYRICS"] = track['lyrics']['unsync'] tag["LYRICS"] = track['lyrics']['unsync']
for role in track['contributors']: for role in track['contributors']:
if role in ['author', 'engineer', 'mixer', 'producer', 'writer', 'composer']: if role in ['author', 'engineer', 'mixer', 'producer', 'writer', 'composer']:
if save['involvedPeople'] and role != 'composer' or role == 'composer' and save['composer']: if save['involvedPeople'] and role != 'composer' or role == 'composer' and save['composer']:
tag[role] = track['contributors'][role] tag[role] = track['contributors'][role]
elif role == 'musicpublisher' and save['involvedPeople']: elif role == 'musicpublisher' and save['involvedPeople']:
tag["ORGANIZATION"] = track['contributors']['musicpublisher'] tag["ORGANIZATION"] = track['contributors']['musicpublisher']
if save['copyright']: if save['copyright']:
tag["COPYRIGHT"] = track['copyright'] tag["COPYRIGHT"] = track['copyright']
if save['savePlaylistAsCompilation']: if save['savePlaylistAsCompilation']:
tag["COMPILATION"] = "1" tag["COMPILATION"] = "1"
if save['cover'] and track['album']['picPath']: if save['cover'] and track['album']['picPath']:
image = Picture() image = Picture()
image.type = 3 image.type = 3
image.mime = 'image/jpeg' if track['album']['picPath'].endswith('jpg') else 'image/png' image.mime = 'image/jpeg' if track['album']['picPath'].endswith('jpg') else 'image/png'
with open(track['album']['picPath'], 'rb') as f: with open(track['album']['picPath'], 'rb') as f:
image.data = f.read() image.data = f.read()
tag.add_picture(image) tag.add_picture(image)
tag.save(deleteid3=True) tag.save(deleteid3=True)