Merge branch 'master' of TrDex/deemix into master
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -6,6 +6,7 @@ __pycache__ | ||||
| /dist | ||||
|  | ||||
| # local env files | ||||
| /venv/ | ||||
| .env.local | ||||
| .env.*.local | ||||
|  | ||||
| @ -21,4 +22,4 @@ yarn-error.log* | ||||
| *.ntvs* | ||||
| *.njsproj | ||||
| *.sln | ||||
| *.sw? | ||||
| *.sw? | ||||
|  | ||||
| @ -1,16 +1,19 @@ | ||||
| #!/usr/bin/env python3 | ||||
| import click | ||||
|  | ||||
| import deemix.app.cli as app | ||||
| from deemix.app.settings import initSettings | ||||
|  | ||||
|  | ||||
| @click.command() | ||||
| @click.option('-b', '--bitrate', default=None, help='Overwrites the default bitrate selected') | ||||
| @click.argument('url') | ||||
| def download(bitrate, url): | ||||
| 	settings = initSettings() | ||||
| 	app.login() | ||||
| 	app.downloadLink(url, settings, bitrate) | ||||
| 	click.echo("All done!") | ||||
|     settings = initSettings() | ||||
|     app.login() | ||||
|     app.downloadLink(url, settings, bitrate) | ||||
|     click.echo("All done!") | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
| 	download() | ||||
|     download() | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| #!/usr/bin/env python3 | ||||
| #Empty File | ||||
| # Empty File | ||||
|  | ||||
| @ -1,332 +1,343 @@ | ||||
| #!/usr/bin/env python3 | ||||
| import binascii | ||||
| import time | ||||
|  | ||||
| import requests | ||||
| from Cryptodome.Cipher import Blowfish, AES | ||||
| from Cryptodome.Hash import MD5 | ||||
| from Cryptodome.Util.Padding import pad | ||||
|  | ||||
| from Cryptodome.Cipher import Blowfish, AES | ||||
| import requests | ||||
|  | ||||
| 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" | ||||
| 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: | ||||
| 	def __init__(self): | ||||
| 		self.api_url = "http://www.deezer.com/ajax/gw-light.php" | ||||
| 		self.legacy_api_url = "https://api.deezer.com/" | ||||
| 		self.http_headers = { | ||||
| 			"User-Agent": USER_AGENT_HEADER | ||||
| 		} | ||||
| 		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.user = {} | ||||
| 		self.session = requests.Session() | ||||
| 		self.logged_in = False | ||||
| 		self.session.post("http://www.deezer.com/", headers=self.http_headers) | ||||
| 		self.sid = self.session.cookies.get('sid') | ||||
|     def __init__(self): | ||||
|         self.api_url = "http://www.deezer.com/ajax/gw-light.php" | ||||
|         self.legacy_api_url = "https://api.deezer.com/" | ||||
|         self.http_headers = { | ||||
|             "User-Agent": USER_AGENT_HEADER | ||||
|         } | ||||
|         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.user = {} | ||||
|         self.session = requests.Session() | ||||
|         self.logged_in = False | ||||
|         self.session.post("http://www.deezer.com/", headers=self.http_headers) | ||||
|         self.sid = self.session.cookies.get('sid') | ||||
|  | ||||
| 	def get_token(self): | ||||
| 		token_data = self.gw_api_call('deezer.getUserData') | ||||
| 		return token_data["results"]["checkForm"] | ||||
|     def get_token(self): | ||||
|         token_data = self.gw_api_call('deezer.getUserData') | ||||
|         return token_data["results"]["checkForm"] | ||||
|  | ||||
| 	def get_track_md5(self, sng_id): | ||||
| 		try: | ||||
| 			site = self.session.post( | ||||
| 				"https://api.deezer.com/1.0/gateway.php", | ||||
| 				params={ | ||||
| 					'api_key': "4VCYIJUCDLOUELGD1V8WBVYBNVDYOXEWSLLZDONGBBDFVXTZJRXPR29JRLQFO6ZE", | ||||
| 					'sid': self.sid, | ||||
| 					'input': '3', | ||||
| 					'output': '3', | ||||
| 					'method': 'song_getData' | ||||
| 				}, | ||||
| 				timeout=30, | ||||
| 				json={'sng_id': sng_id}, | ||||
| 				headers=self.http_headers | ||||
| 			) | ||||
| 		except: | ||||
| 			time.sleep(2) | ||||
| 			return self.get_track_md5(sng_id) | ||||
| 		response = site.json() | ||||
| 		return response['results']['PUID'] | ||||
|     def get_track_md5(self, sng_id): | ||||
|         try: | ||||
|             site = self.session.post( | ||||
|                 "https://api.deezer.com/1.0/gateway.php", | ||||
|                 params={ | ||||
|                     'api_key': "4VCYIJUCDLOUELGD1V8WBVYBNVDYOXEWSLLZDONGBBDFVXTZJRXPR29JRLQFO6ZE", | ||||
|                     'sid': self.sid, | ||||
|                     'input': '3', | ||||
|                     'output': '3', | ||||
|                     'method': 'song_getData' | ||||
|                 }, | ||||
|                 timeout=30, | ||||
|                 json={'sng_id': sng_id}, | ||||
|                 headers=self.http_headers | ||||
|             ) | ||||
|         except: | ||||
|             time.sleep(2) | ||||
|             return self.get_track_md5(sng_id) | ||||
|         response = site.json() | ||||
|         return response['results']['PUID'] | ||||
|  | ||||
| 	def gw_api_call(self, method, args={}): | ||||
| 		try: | ||||
| 			result = self.session.post( | ||||
| 				self.api_url, | ||||
| 				params={ | ||||
| 					'api_version': "1.0", | ||||
| 					'api_token': 'null' if method == 'deezer.getUserData' else self.get_token(), | ||||
| 					'input': '3', | ||||
| 					'method': method | ||||
| 				}, | ||||
| 				timeout=30, | ||||
| 				json=args, | ||||
| 				headers=self.http_headers | ||||
| 			) | ||||
| 		except: | ||||
| 			time.sleep(2) | ||||
| 			return self.gw_api_call(method, args) | ||||
| 		return result.json() | ||||
|     def gw_api_call(self, method, args=None): | ||||
|         if args is None: | ||||
|             args = {} | ||||
|         try: | ||||
|             result = self.session.post( | ||||
|                 self.api_url, | ||||
|                 params={ | ||||
|                     'api_version': "1.0", | ||||
|                     'api_token': 'null' if method == 'deezer.getUserData' else self.get_token(), | ||||
|                     'input': '3', | ||||
|                     'method': method | ||||
|                 }, | ||||
|                 timeout=30, | ||||
|                 json=args, | ||||
|                 headers=self.http_headers | ||||
|             ) | ||||
|         except: | ||||
|             time.sleep(2) | ||||
|             return self.gw_api_call(method, args) | ||||
|         return result.json() | ||||
|  | ||||
| 	def api_call(self, method, args={}): | ||||
| 		try: | ||||
| 			result = self.session.get( | ||||
| 				self.legacy_api_url + method, | ||||
| 				params=args, | ||||
| 				headers=self.http_headers, | ||||
| 				timeout=30 | ||||
| 			) | ||||
| 			result_json = result.json() | ||||
| 		except: | ||||
| 			time.sleep(2) | ||||
| 			return self.api_call(method, args) | ||||
| 		if 'error' in result_json.keys(): | ||||
| 			raise APIError() | ||||
| 		return result_json | ||||
|     def api_call(self, method, args=None): | ||||
|         if args is None: | ||||
|             args = {} | ||||
|         try: | ||||
|             result = self.session.get( | ||||
|                 self.legacy_api_url + method, | ||||
|                 params=args, | ||||
|                 headers=self.http_headers, | ||||
|                 timeout=30 | ||||
|             ) | ||||
|             result_json = result.json() | ||||
|         except: | ||||
|             time.sleep(2) | ||||
|             return self.api_call(method, args) | ||||
|         if 'error' in result_json.keys(): | ||||
|             raise APIError() | ||||
|         return result_json | ||||
|  | ||||
| 	def login(self, email, password, re_captcha_token): | ||||
| 		check_form_login = self.gw_api_call("deezer.getUserData") | ||||
| 		login = self.session.post( | ||||
| 			"https://www.deezer.com/ajax/action.php", | ||||
| 			data={ | ||||
| 				'type': 'login', | ||||
| 				'mail': email, | ||||
| 				'password': password, | ||||
| 				'checkFormLogin': check_form_login['results']['checkFormLogin'], | ||||
| 				'reCaptchaToken': re_captcha_token | ||||
| 			}, | ||||
| 			headers={'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', **self.http_headers} | ||||
| 		) | ||||
| 		if 'success' not in login.text: | ||||
| 			self.logged_in = False | ||||
| 			return False | ||||
| 		user_data = self.gw_api_call("deezer.getUserData") | ||||
| 		self.user = { | ||||
| 			'email': email, | ||||
| 			'id': user_data["results"]["USER"]["USER_ID"], | ||||
| 			'name': user_data["results"]["USER"]["BLOG_NAME"], | ||||
| 			'picture': user_data["results"]["USER"]["USER_PICTURE"] if "USER_PICTURE" in user_data["results"][ | ||||
| 				"USER"] else "" | ||||
| 		} | ||||
| 		self.logged_in = True | ||||
| 		return True | ||||
|     def login(self, email, password, re_captcha_token): | ||||
|         check_form_login = self.gw_api_call("deezer.getUserData") | ||||
|         login = self.session.post( | ||||
|             "https://www.deezer.com/ajax/action.php", | ||||
|             data={ | ||||
|                 'type': 'login', | ||||
|                 'mail': email, | ||||
|                 'password': password, | ||||
|                 'checkFormLogin': check_form_login['results']['checkFormLogin'], | ||||
|                 'reCaptchaToken': re_captcha_token | ||||
|             }, | ||||
|             headers={'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', **self.http_headers} | ||||
|         ) | ||||
|         if 'success' not in login.text: | ||||
|             self.logged_in = False | ||||
|             return False | ||||
|         user_data = self.gw_api_call("deezer.getUserData") | ||||
|         self.user = { | ||||
|             'email': email, | ||||
|             'id': user_data["results"]["USER"]["USER_ID"], | ||||
|             'name': user_data["results"]["USER"]["BLOG_NAME"], | ||||
|             'picture': user_data["results"]["USER"]["USER_PICTURE"] if "USER_PICTURE" in user_data["results"][ | ||||
|                 "USER"] else "" | ||||
|         } | ||||
|         self.logged_in = True | ||||
|         return True | ||||
|  | ||||
| 	def login_via_arl(self, arl): | ||||
| 		cookie_obj = requests.cookies.create_cookie( | ||||
| 			domain='deezer.com', | ||||
| 			name='arl', | ||||
| 			value=arl, | ||||
| 			path="/", | ||||
| 			rest={'HttpOnly': True} | ||||
| 		) | ||||
| 		self.session.cookies.set_cookie(cookie_obj) | ||||
| 		user_data = self.gw_api_call("deezer.getUserData") | ||||
| 		if user_data["results"]["USER"]["USER_ID"] == 0: | ||||
| 			self.logged_in = False | ||||
| 			return 0 | ||||
| 		self.user = { | ||||
| 			'id': user_data["results"]["USER"]["USER_ID"], | ||||
| 			'name': user_data["results"]["USER"]["BLOG_NAME"], | ||||
| 			'picture': user_data["results"]["USER"]["USER_PICTURE"] if "USER_PICTURE" in user_data["results"][ | ||||
| 				"USER"] else "" | ||||
| 		} | ||||
| 		self.logged_in = True | ||||
| 		return 1 | ||||
|     def login_via_arl(self, arl): | ||||
|         cookie_obj = requests.cookies.create_cookie( | ||||
|             domain='deezer.com', | ||||
|             name='arl', | ||||
|             value=arl, | ||||
|             path="/", | ||||
|             rest={'HttpOnly': True} | ||||
|         ) | ||||
|         self.session.cookies.set_cookie(cookie_obj) | ||||
|         user_data = self.gw_api_call("deezer.getUserData") | ||||
|         if user_data["results"]["USER"]["USER_ID"] == 0: | ||||
|             self.logged_in = False | ||||
|             return 0 | ||||
|         self.user = { | ||||
|             'id': user_data["results"]["USER"]["USER_ID"], | ||||
|             'name': user_data["results"]["USER"]["BLOG_NAME"], | ||||
|             'picture': user_data["results"]["USER"]["USER_PICTURE"] if "USER_PICTURE" in user_data["results"][ | ||||
|                 "USER"] else "" | ||||
|         } | ||||
|         self.logged_in = True | ||||
|         return 1 | ||||
|  | ||||
| 	def get_track_gw(self, sng_id): | ||||
| 		if int(sng_id) < 0: | ||||
| 			body = self.gw_api_call('song.getData', {'sng_id': sng_id}) | ||||
| 		else: | ||||
| 			body = self.gw_api_call('deezer.pageTrack', {'sng_id': sng_id}) | ||||
| 			if 'LYRICS' in body['results']: | ||||
| 				body['results']['DATA']['LYRICS'] = body['results']['LYRICS'] | ||||
| 			body['results'] = body['results']['DATA'] | ||||
| 		return body['results'] | ||||
|     def get_track_gw(self, sng_id): | ||||
|         if int(sng_id) < 0: | ||||
|             body = self.gw_api_call('song.getData', {'sng_id': sng_id}) | ||||
|         else: | ||||
|             body = self.gw_api_call('deezer.pageTrack', {'sng_id': sng_id}) | ||||
|             if 'LYRICS' in body['results']: | ||||
|                 body['results']['DATA']['LYRICS'] = body['results']['LYRICS'] | ||||
|             body['results'] = body['results']['DATA'] | ||||
|         return body['results'] | ||||
|  | ||||
| 	def get_tracks_gw(self, ids): | ||||
| 		tracks_array = [] | ||||
| 		body = self.gw_api_call('song.getListData', {'sng_ids': ids}) | ||||
| 		errors = 0 | ||||
| 		for i in range(len(ids)): | ||||
| 			if ids[i] != 0: | ||||
| 				tracks_array.append(body['results']['data'][i - errors]) | ||||
| 			else: | ||||
| 				errors += 1 | ||||
| 				tracks_array.append({ | ||||
| 					'SNG_ID': 0, | ||||
| 					'SNG_TITLE': '', | ||||
| 					'DURATION': 0, | ||||
| 					'MD5_ORIGIN': 0, | ||||
| 					'MEDIA_VERSION': 0, | ||||
| 					'FILESIZE': 0, | ||||
| 					'ALB_TITLE': "", | ||||
| 					'ALB_PICTURE': "", | ||||
| 					'ART_ID': 0, | ||||
| 					'ART_NAME': "" | ||||
| 				}) | ||||
| 		return tracks_array | ||||
|     def get_tracks_gw(self, ids): | ||||
|         tracks_array = [] | ||||
|         body = self.gw_api_call('song.getListData', {'sng_ids': ids}) | ||||
|         errors = 0 | ||||
|         for i in range(len(ids)): | ||||
|             if ids[i] != 0: | ||||
|                 tracks_array.append(body['results']['data'][i - errors]) | ||||
|             else: | ||||
|                 errors += 1 | ||||
|                 tracks_array.append({ | ||||
|                     'SNG_ID': 0, | ||||
|                     'SNG_TITLE': '', | ||||
|                     'DURATION': 0, | ||||
|                     'MD5_ORIGIN': 0, | ||||
|                     'MEDIA_VERSION': 0, | ||||
|                     'FILESIZE': 0, | ||||
|                     'ALB_TITLE': "", | ||||
|                     'ALB_PICTURE': "", | ||||
|                     'ART_ID': 0, | ||||
|                     'ART_NAME': "" | ||||
|                 }) | ||||
|         return tracks_array | ||||
|  | ||||
| 	def get_album_gw(self, alb_id): | ||||
| 		return self.gw_api_call('album.getData', {'alb_id': alb_id})['results'] | ||||
|     def get_album_gw(self, alb_id): | ||||
|         return self.gw_api_call('album.getData', {'alb_id': alb_id})['results'] | ||||
|  | ||||
| 	def get_album_tracks_gw(self, alb_id): | ||||
| 		tracks_array = [] | ||||
| 		body = self.gw_api_call('song.getListByAlbum', {'alb_id': alb_id, 'nb': -1}) | ||||
| 		for track in body['results']['data']: | ||||
| 			_track = track | ||||
| 			_track['position'] = body['results']['data'].index(track) | ||||
| 			tracks_array.append(_track) | ||||
| 		return tracks_array | ||||
|     def get_album_tracks_gw(self, alb_id): | ||||
|         tracks_array = [] | ||||
|         body = self.gw_api_call('song.getListByAlbum', {'alb_id': alb_id, 'nb': -1}) | ||||
|         for track in body['results']['data']: | ||||
|             _track = track | ||||
|             _track['position'] = body['results']['data'].index(track) | ||||
|             tracks_array.append(_track) | ||||
|         return tracks_array | ||||
|  | ||||
| 	def get_artist_gw(self, art_id): | ||||
| 		return self.gw_api_call('deezer.pageArtist', {'art_id': art_id}) | ||||
|     def get_artist_gw(self, art_id): | ||||
|         return self.gw_api_call('deezer.pageArtist', {'art_id': art_id}) | ||||
|  | ||||
| 	def get_playlist_gw(self, playlist_id): | ||||
| 		return self.gw_api_call('deezer.pagePlaylist', {'playlist_id': playlist_id}) | ||||
|     def get_playlist_gw(self, playlist_id): | ||||
|         return self.gw_api_call('deezer.pagePlaylist', {'playlist_id': playlist_id}) | ||||
|  | ||||
| 	def get_playlist_tracks_gw(self, playlist_id): | ||||
| 		tracks_array = [] | ||||
| 		body = self.gw_api_call('playlist.getSongs', {'playlist_id': playlist_id, 'nb': -1}) | ||||
| 		for track in body['results']['data']: | ||||
| 			track['position'] = body['results']['data'].index(track) | ||||
| 			tracks_array.append(track) | ||||
| 		return tracks_array | ||||
|     def get_playlist_tracks_gw(self, playlist_id): | ||||
|         tracks_array = [] | ||||
|         body = self.gw_api_call('playlist.getSongs', {'playlist_id': playlist_id, 'nb': -1}) | ||||
|         for track in body['results']['data']: | ||||
|             track['position'] = body['results']['data'].index(track) | ||||
|             tracks_array.append(track) | ||||
|         return tracks_array | ||||
|  | ||||
| 	def get_artist_toptracks_gw(self, art_id): | ||||
| 		tracks_array = [] | ||||
| 		body = self.gw_api_call('artist.getTopTrack', {'art_id': art_id, 'nb': 100}) | ||||
| 		for track in body['results']['data']: | ||||
| 			track['position'] = body['results']['data'].index(track) | ||||
| 			tracks_array.append(track) | ||||
| 		return tracks_array | ||||
|     def get_artist_toptracks_gw(self, art_id): | ||||
|         tracks_array = [] | ||||
|         body = self.gw_api_call('artist.getTopTrack', {'art_id': art_id, 'nb': 100}) | ||||
|         for track in body['results']['data']: | ||||
|             track['position'] = body['results']['data'].index(track) | ||||
|             tracks_array.append(track) | ||||
|         return tracks_array | ||||
|  | ||||
| 	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'] | ||||
| 		order = [] | ||||
| 		for x in results['ORDER']: | ||||
| 			if x in ['TOP_RESULT', 'TRACK', 'ALBUM', 'ARTIST', 'PLAYLIST']: | ||||
| 				order.append(x) | ||||
| 		results['ORDER'] = order | ||||
| 		return results | ||||
|     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'] | ||||
|         order = [] | ||||
|         for x in results['ORDER']: | ||||
|             if x in ['TOP_RESULT', 'TRACK', 'ALBUM', 'ARTIST', 'PLAYLIST']: | ||||
|                 order.append(x) | ||||
|         results['ORDER'] = order | ||||
|         return results | ||||
|  | ||||
| 	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'] | ||||
|     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'] | ||||
|  | ||||
| 	def get_lyrics_gw(self, sng_id): | ||||
| 		return self.gw_api_call('song.getLyrics', {'sng_id': sng_id})["results"] | ||||
|     def get_lyrics_gw(self, sng_id): | ||||
|         return self.gw_api_call('song.getLyrics', {'sng_id': sng_id})["results"] | ||||
|  | ||||
| 	def get_user_playlist(self, user_id): | ||||
| 		return self.api_call('user/' + str(user_id) + '/playlists', {'limit': -1}) | ||||
|     def get_user_playlist(self, user_id): | ||||
|         return self.api_call('user/' + str(user_id) + '/playlists', {'limit': -1}) | ||||
|  | ||||
| 	def get_track(self, user_id): | ||||
| 		return self.api_call('track/' + str(user_id)) | ||||
|     def get_track(self, user_id): | ||||
|         return self.api_call('track/' + str(user_id)) | ||||
|  | ||||
| 	def get_track_by_ISRC(self, isrc): | ||||
| 		return self.api_call('track/isrc:' + isrc) | ||||
|     def get_track_by_ISRC(self, isrc): | ||||
|         return self.api_call('track/isrc:' + isrc) | ||||
|  | ||||
| 	def get_charts_top_country(self): | ||||
| 		return self.get_user_playlist('637006841') | ||||
|     def get_charts_top_country(self): | ||||
|         return self.get_user_playlist('637006841') | ||||
|  | ||||
| 	def get_playlist(self, playlist_id): | ||||
| 		return self.api_call('playlist/' + str(playlist_id)) | ||||
|     def get_playlist(self, playlist_id): | ||||
|         return self.api_call('playlist/' + str(playlist_id)) | ||||
|  | ||||
| 	def get_playlist_tracks(self, playlist_id): | ||||
| 		return self.api_call('playlist/' + str(playlist_id) + '/tracks', {'limit': -1}) | ||||
|     def get_playlist_tracks(self, playlist_id): | ||||
|         return self.api_call('playlist/' + str(playlist_id) + '/tracks', {'limit': -1}) | ||||
|  | ||||
| 	def get_album(self, album_id): | ||||
| 		return self.api_call('album/' + str(album_id)) | ||||
|     def get_album(self, album_id): | ||||
|         return self.api_call('album/' + str(album_id)) | ||||
|  | ||||
| 	def get_album_by_UPC(self, upc): | ||||
| 		return self.api_call('album/upc:' + str(upc)) | ||||
|     def get_album_by_UPC(self, upc): | ||||
|         return self.api_call('album/upc:' + str(upc)) | ||||
|  | ||||
| 	def get_album_tracks(self, album_id): | ||||
| 		return self.api_call('album/' + str(album_id) + '/tracks', {'limit': -1}) | ||||
|     def get_album_tracks(self, album_id): | ||||
|         return self.api_call('album/' + str(album_id) + '/tracks', {'limit': -1}) | ||||
|  | ||||
| 	def get_artist(self, artist_id): | ||||
| 		return self.api_call('artist/' + str(artist_id)) | ||||
|     def get_artist(self, artist_id): | ||||
|         return self.api_call('artist/' + str(artist_id)) | ||||
|  | ||||
| 	def get_artist_albums(self, artist_id): | ||||
| 		return self.api_call('artist/' + str(artist_id) + '/albums', {'limit': -1}) | ||||
|     def get_artist_albums(self, artist_id): | ||||
|         return self.api_call('artist/' + str(artist_id) + '/albums', {'limit': -1}) | ||||
|  | ||||
| 	def search(self, term, search_type, limit=30): | ||||
| 		return self.api_call('search/' + search_type, {'q': term, 'limit': limit}) | ||||
|     def search(self, term, search_type, limit=30): | ||||
|         return self.api_call('search/' + search_type, {'q': term, 'limit': limit}) | ||||
|  | ||||
| 	def decrypt_track(self, track_id, input, output): | ||||
| 		response = open(input, 'rb') | ||||
| 		outfile = open(output, 'wb') | ||||
| 		blowfish_key = str.encode(self._get_blowfish_key(str(track_id))) | ||||
| 		i = 0 | ||||
| 		while True: | ||||
| 			chunk = response.read(2048) | ||||
| 			if not chunk: | ||||
| 				break | ||||
| 			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) | ||||
| 			outfile.write(chunk) | ||||
| 			i += 1 | ||||
|     def decrypt_track(self, track_id, input, output): | ||||
|         response = open(input, 'rb') | ||||
|         outfile = open(output, 'wb') | ||||
|         blowfish_key = str.encode(self._get_blowfish_key(str(track_id))) | ||||
|         i = 0 | ||||
|         while True: | ||||
|             chunk = response.read(2048) | ||||
|             if not chunk: | ||||
|                 break | ||||
|             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) | ||||
|             outfile.write(chunk) | ||||
|             i += 1 | ||||
|  | ||||
| 	def stream_track(self, track_id, url, stream): | ||||
| 		try: | ||||
| 			request = requests.get(url, headers=self.http_headers, stream=True, timeout=30) | ||||
| 		except: | ||||
| 			time.sleep(2) | ||||
| 			return self.stream_track(track_id, url, stream) | ||||
| 		request.raise_for_status() | ||||
| 		blowfish_key = str.encode(self._get_blowfish_key(str(track_id))) | ||||
| 		i = 0 | ||||
| 		for chunk in request.iter_content(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) | ||||
| 			stream.write(chunk) | ||||
| 			i += 1 | ||||
|     def stream_track(self, track_id, url, stream): | ||||
|         try: | ||||
|             request = requests.get(url, headers=self.http_headers, stream=True, timeout=30) | ||||
|         except: | ||||
|             time.sleep(2) | ||||
|             return self.stream_track(track_id, url, stream) | ||||
|         request.raise_for_status() | ||||
|         blowfish_key = str.encode(self._get_blowfish_key(str(track_id))) | ||||
|         i = 0 | ||||
|         for chunk in request.iter_content(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) | ||||
|             stream.write(chunk) | ||||
|             i += 1 | ||||
|  | ||||
| 	def _md5(self, data): | ||||
| 		h = MD5.new() | ||||
| 		h.update(str.encode(data) if isinstance(data, str) else data) | ||||
| 		return h.hexdigest() | ||||
|     def _md5(self, data): | ||||
|         h = MD5.new() | ||||
|         h.update(str.encode(data) if isinstance(data, str) else data) | ||||
|         return h.hexdigest() | ||||
|  | ||||
| 	def _get_blowfish_key(self, trackId): | ||||
| 		SECRET = 'g4el58wc' + '0zvf9na1' | ||||
| 		idMd5 = self._md5(trackId) | ||||
| 		bfKey = "" | ||||
| 		for i in range(16): | ||||
| 			bfKey += chr(ord(idMd5[i]) ^ ord(idMd5[i + 16]) ^ ord(SECRET[i])) | ||||
| 		return bfKey | ||||
|     def _get_blowfish_key(self, trackId): | ||||
|         SECRET = 'g4el58wc' + '0zvf9na1' | ||||
|         idMd5 = self._md5(trackId) | ||||
|         bfKey = "" | ||||
|         for i in range(16): | ||||
|             bfKey += chr(ord(idMd5[i]) ^ ord(idMd5[i + 16]) ^ ord(SECRET[i])) | ||||
|         return bfKey | ||||
|  | ||||
| 	def get_track_stream_url(self, sng_id, md5, media_version, format): | ||||
| 		urlPart = b'\xa4'.join( | ||||
| 			[str.encode(md5), str.encode(str(format)), str.encode(str(sng_id)), str.encode(str(media_version))]) | ||||
| 		md5val = self._md5(urlPart) | ||||
| 		step2 = str.encode(md5val) + b'\xa4' + urlPart + b'\xa4' | ||||
| 		step2 = pad(step2, 16) | ||||
| 		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") | ||||
|     def get_track_stream_url(self, sng_id, md5, media_version, format): | ||||
|         urlPart = b'\xa4'.join( | ||||
|             [str.encode(md5), str.encode(str(format)), str.encode(str(sng_id)), str.encode(str(media_version))]) | ||||
|         md5val = self._md5(urlPart) | ||||
|         step2 = str.encode(md5val) + b'\xa4' + urlPart + b'\xa4' | ||||
|         step2 = pad(step2, 16) | ||||
|         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") | ||||
|  | ||||
| 	def get_track_from_metadata(self, artist, track, album): | ||||
| 		artist = artist.replace("–","-").replace("’", "'") | ||||
| 		track = track.replace("–","-").replace("’", "'") | ||||
| 		album = album.replace("–","-").replace("’", "'") | ||||
|     def get_track_from_metadata(self, artist, track, album): | ||||
|         artist = artist.replace("–", "-").replace("’", "'") | ||||
|         track = track.replace("–", "-").replace("’", "'") | ||||
|         album = album.replace("–", "-").replace("’", "'") | ||||
|  | ||||
| 		resp = self.search(f'artist:"{artist}" track:"{track}" album:"{album}"', "track", 1) | ||||
| 		if len(resp['data'])>0: | ||||
| 			return resp['data'][0]['id'] | ||||
| 		resp = self.search(f'artist:"{artist}" track:"{track}"', "track", 1) | ||||
| 		if len(resp['data'])>0: | ||||
| 			return resp['data'][0]['id'] | ||||
| 		if "(" in track and ")" in track and track.find("(") < track.find(")"): | ||||
| 			resp = self.search(f'artist:"{artist}" track:"{track[:track.find("(")]}"', "track", 1) | ||||
| 			if len(resp['data'])>0: | ||||
| 				return resp['data'][0]['id'] | ||||
| 		elif " - " in track: | ||||
| 			resp = self.search(f'artist:"{artist}" track:"{track[:track.find(" - ")]}"', "track", 1) | ||||
| 			if len(resp['data'])>0: | ||||
| 				return resp['data'][0]['id'] | ||||
| 		else: | ||||
| 			return 0 | ||||
| 		return 0 | ||||
|         resp = self.search(f'artist:"{artist}" track:"{track}" album:"{album}"', "track", 1) | ||||
|         if len(resp['data']) > 0: | ||||
|             return resp['data'][0]['id'] | ||||
|         resp = self.search(f'artist:"{artist}" track:"{track}"', "track", 1) | ||||
|         if len(resp['data']) > 0: | ||||
|             return resp['data'][0]['id'] | ||||
|         if "(" in track and ")" in track and track.find("(") < track.find(")"): | ||||
|             resp = self.search(f'artist:"{artist}" track:"{track[:track.find("(")]}"', "track", 1) | ||||
|             if len(resp['data']) > 0: | ||||
|                 return resp['data'][0]['id'] | ||||
|         elif " - " in track: | ||||
|             resp = self.search(f'artist:"{artist}" track:"{track[:track.find(" - ")]}"', "track", 1) | ||||
|             if len(resp['data']) > 0: | ||||
|                 return resp['data'][0]['id'] | ||||
|         else: | ||||
|             return 0 | ||||
|         return 0 | ||||
|  | ||||
|  | ||||
| class APIError(Exception): | ||||
| 	pass | ||||
|     pass | ||||
|  | ||||
| @ -1,36 +1,38 @@ | ||||
| #!/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 | ||||
| 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() | ||||
| sp = SpotifyHelper() | ||||
|  | ||||
|  | ||||
| def requestValidArl(): | ||||
| 	while True: | ||||
| 		arl = input("Paste here your arl:") | ||||
| 		if dz.login_via_arl(arl): | ||||
| 			break | ||||
| 	return arl | ||||
|     while True: | ||||
|         arl = input("Paste here your arl:") | ||||
|         if dz.login_via_arl(arl): | ||||
|             break | ||||
|     return arl | ||||
|  | ||||
|  | ||||
| def login(): | ||||
| 	configFolder = localpaths.getConfigFolder() | ||||
| 	if not path.isdir(configFolder): | ||||
| 		mkdir(configFolder) | ||||
| 	if path.isfile(path.join(configFolder, '.arl')): | ||||
| 		with open(path.join(configFolder, '.arl'), 'r') as f: | ||||
| 			arl = f.read() | ||||
| 		if not dz.login_via_arl(arl): | ||||
| 			arl = requestValidArl() | ||||
| 	else: | ||||
| 		arl = requestValidArl() | ||||
| 	with open(path.join(configFolder, '.arl'), 'w') as f: | ||||
| 		f.write(arl) | ||||
|     configFolder = localpaths.getConfigFolder() | ||||
|     if not path.isdir(configFolder): | ||||
|         mkdir(configFolder) | ||||
|     if path.isfile(path.join(configFolder, '.arl')): | ||||
|         with open(path.join(configFolder, '.arl'), 'r') as f: | ||||
|             arl = f.read() | ||||
|         if not dz.login_via_arl(arl): | ||||
|             arl = requestValidArl() | ||||
|     else: | ||||
|         arl = requestValidArl() | ||||
|     with open(path.join(configFolder, '.arl'), 'w') as f: | ||||
|         f.write(arl) | ||||
|  | ||||
|  | ||||
| def downloadLink(url, settings, bitrate=None): | ||||
| 	addToQueue(dz, sp, url, settings, bitrate) | ||||
|     addToQueue(dz, sp, url, settings, bitrate) | ||||
|  | ||||
| @ -1,69 +1,69 @@ | ||||
| { | ||||
| 	"downloadLocation": "", | ||||
| 	"tracknameTemplate": "%artist% - %title%", | ||||
| 	"albumTracknameTemplate": "%tracknumber% - %title%", | ||||
| 	"playlistTracknameTemplate": "%position% - %artist% - %title%", | ||||
| 	"createPlaylistFolder": true, | ||||
| 	"playlistNameTemplate": "%playlist%", | ||||
| 	"createArtistFolder": false, | ||||
| 	"artistNameTemplate": "%artist%", | ||||
| 	"createAlbumFolder": true, | ||||
| 	"albumNameTemplate": "%artist% - %album%", | ||||
| 	"createCDFolder": true, | ||||
| 	"createStructurePlaylist": false, | ||||
| 	"createSingleFolder": false, | ||||
| 	"padTracks": true, | ||||
| 	"paddingSize": "0", | ||||
| 	"illegalCharacterReplacer": "_", | ||||
| 	"queueConcurrency": 3, | ||||
| 	"maxBitrate": "3", | ||||
| 	"fallbackBitrate": true, | ||||
| 	"fallbackSearch": false, | ||||
| 	"logErrors": true, | ||||
| 	"logSearched": false, | ||||
| 	"createM3U8File": false, | ||||
| 	"syncedLyrics": false, | ||||
| 	"embeddedArtworkSize": 800, | ||||
| 	"localArtworkSize": 1400, | ||||
| 	"saveArtwork": true, | ||||
| 	"coverImageTemplate": "cover", | ||||
| 	"saveArtworkArtist": false, | ||||
| 	"artistImageTemplate": "folder", | ||||
| 	"PNGcovers": false, | ||||
| 	"jpegImageQuality": 80, | ||||
| 	"dateFormat": "Y-M-D", | ||||
| 	"removeAlbumVersion": false, | ||||
| 	"featuredToTitle": "0", | ||||
| 	"titleCasing": "nothing", | ||||
| 	"artistCasing": "nothing", | ||||
| 	"executeCommand": "", | ||||
| 	"tags": { | ||||
| 		"title": true, | ||||
| 		"artist": true, | ||||
| 		"album": true, | ||||
| 		"cover": true, | ||||
| 		"trackNumber": true, | ||||
| 		"trackTotal": false, | ||||
| 		"discNumber": true, | ||||
| 		"discTotal": false, | ||||
| 		"albumArtist": true, | ||||
| 		"genre": true, | ||||
| 		"year": true, | ||||
| 		"date": true, | ||||
| 		"explicit": false, | ||||
| 		"isrc": true, | ||||
| 		"length": true, | ||||
| 		"barcode": true, | ||||
| 		"bpm": true, | ||||
| 		"replayGain": false, | ||||
| 		"label": true, | ||||
| 		"lyrics": false, | ||||
| 		"copyright": false, | ||||
| 		"composer": false, | ||||
| 		"involvedPeople": false, | ||||
| 		"savePlaylistAsCompilation": false, | ||||
| 		"useNullSeparator": false, | ||||
| 		"saveID3v1": true, | ||||
| 		"multitagSeparator": "default" | ||||
| 	} | ||||
|   "downloadLocation": "", | ||||
|   "tracknameTemplate": "%artist% - %title%", | ||||
|   "albumTracknameTemplate": "%tracknumber% - %title%", | ||||
|   "playlistTracknameTemplate": "%position% - %artist% - %title%", | ||||
|   "createPlaylistFolder": true, | ||||
|   "playlistNameTemplate": "%playlist%", | ||||
|   "createArtistFolder": false, | ||||
|   "artistNameTemplate": "%artist%", | ||||
|   "createAlbumFolder": true, | ||||
|   "albumNameTemplate": "%artist% - %album%", | ||||
|   "createCDFolder": true, | ||||
|   "createStructurePlaylist": false, | ||||
|   "createSingleFolder": false, | ||||
|   "padTracks": true, | ||||
|   "paddingSize": "0", | ||||
|   "illegalCharacterReplacer": "_", | ||||
|   "queueConcurrency": 3, | ||||
|   "maxBitrate": "3", | ||||
|   "fallbackBitrate": true, | ||||
|   "fallbackSearch": false, | ||||
|   "logErrors": true, | ||||
|   "logSearched": false, | ||||
|   "createM3U8File": false, | ||||
|   "syncedLyrics": false, | ||||
|   "embeddedArtworkSize": 800, | ||||
|   "localArtworkSize": 1400, | ||||
|   "saveArtwork": true, | ||||
|   "coverImageTemplate": "cover", | ||||
|   "saveArtworkArtist": false, | ||||
|   "artistImageTemplate": "folder", | ||||
|   "PNGcovers": false, | ||||
|   "jpegImageQuality": 80, | ||||
|   "dateFormat": "Y-M-D", | ||||
|   "removeAlbumVersion": false, | ||||
|   "featuredToTitle": "0", | ||||
|   "titleCasing": "nothing", | ||||
|   "artistCasing": "nothing", | ||||
|   "executeCommand": "", | ||||
|   "tags": { | ||||
|     "title": true, | ||||
|     "artist": true, | ||||
|     "album": true, | ||||
|     "cover": true, | ||||
|     "trackNumber": true, | ||||
|     "trackTotal": false, | ||||
|     "discNumber": true, | ||||
|     "discTotal": false, | ||||
|     "albumArtist": true, | ||||
|     "genre": true, | ||||
|     "year": true, | ||||
|     "date": true, | ||||
|     "explicit": false, | ||||
|     "isrc": true, | ||||
|     "length": true, | ||||
|     "barcode": true, | ||||
|     "bpm": true, | ||||
|     "replayGain": false, | ||||
|     "label": true, | ||||
|     "lyrics": false, | ||||
|     "copyright": false, | ||||
|     "composer": false, | ||||
|     "involvedPeople": false, | ||||
|     "savePlaylistAsCompilation": false, | ||||
|     "useNullSeparator": false, | ||||
|     "saveID3v1": true, | ||||
|     "multitagSeparator": "default" | ||||
|   } | ||||
| } | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -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.utils.misc import getIDFromLink, getTypeFromLink, getBitrateInt | ||||
|  | ||||
| queue = [] | ||||
| queueList = {} | ||||
| @ -26,244 +25,264 @@ if its an album/playlist | ||||
| 	collection | ||||
| """ | ||||
|  | ||||
|  | ||||
| def generateQueueItem(dz, sp, url, settings, bitrate=None, albumAPI=None, interface=None): | ||||
| 	forcedBitrate = getBitrateInt(bitrate) | ||||
| 	bitrate = forcedBitrate if forcedBitrate else settings['maxBitrate'] | ||||
| 	type = getTypeFromLink(url) | ||||
| 	id = getIDFromLink(url, type) | ||||
| 	result = {} | ||||
| 	if type == None or id == None: | ||||
| 		print("URL not recognized") | ||||
| 		result['error'] = "URL not recognized" | ||||
| 	elif type == "track": | ||||
| 		trackAPI = dz.get_track_gw(id) | ||||
| 		if albumAPI: | ||||
| 			trackAPI['_EXTRA_ALBUM'] = albumAPI | ||||
| 		trackAPI['FILENAME_TEMPLATE'] = settings['tracknameTemplate'] | ||||
| 		trackAPI['SINGLE_TRACK'] = True | ||||
|     forcedBitrate = getBitrateInt(bitrate) | ||||
|     bitrate = forcedBitrate if forcedBitrate else settings['maxBitrate'] | ||||
|     type = getTypeFromLink(url) | ||||
|     id = getIDFromLink(url, type) | ||||
|     result = {} | ||||
|     if type == None or id == None: | ||||
|         print("URL not recognized") | ||||
|         result['error'] = "URL not recognized" | ||||
|     elif type == "track": | ||||
|         trackAPI = dz.get_track_gw(id) | ||||
|         if albumAPI: | ||||
|             trackAPI['_EXTRA_ALBUM'] = albumAPI | ||||
|         trackAPI['FILENAME_TEMPLATE'] = settings['tracknameTemplate'] | ||||
|         trackAPI['SINGLE_TRACK'] = True | ||||
|  | ||||
| 		result['title'] = trackAPI['SNG_TITLE'] | ||||
| 		if 'VERSION' in trackAPI and trackAPI['VERSION']: | ||||
| 			result['title'] += " " + trackAPI['VERSION'] | ||||
| 		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['size'] = 1 | ||||
| 		result['downloaded'] = 0 | ||||
| 		result['failed'] = 0 | ||||
| 		result['progress'] = 0 | ||||
| 		result['type'] = 'track' | ||||
| 		result['id'] = id | ||||
| 		result['bitrate'] = bitrate | ||||
| 		result['uuid'] = f"{result['type']}_{id}_{bitrate}" | ||||
| 		result['settings'] = settings or {} | ||||
| 		result['single'] = trackAPI | ||||
|         result['title'] = trackAPI['SNG_TITLE'] | ||||
|         if 'VERSION' in trackAPI and trackAPI['VERSION']: | ||||
|             result['title'] += " " + trackAPI['VERSION'] | ||||
|         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['size'] = 1 | ||||
|         result['downloaded'] = 0 | ||||
|         result['failed'] = 0 | ||||
|         result['progress'] = 0 | ||||
|         result['type'] = 'track' | ||||
|         result['id'] = id | ||||
|         result['bitrate'] = bitrate | ||||
|         result['uuid'] = f"{result['type']}_{id}_{bitrate}" | ||||
|         result['settings'] = settings or {} | ||||
|         result['single'] = trackAPI | ||||
|  | ||||
| 	elif type == "album": | ||||
| 		albumAPI = dz.get_album(id) | ||||
| 		albumAPI_gw = dz.get_album_gw(id) | ||||
| 		albumAPI['nb_disk'] = albumAPI_gw['NUMBER_DISK'] | ||||
| 		albumAPI['copyright'] = albumAPI_gw['COPYRIGHT'] | ||||
| 		if albumAPI['nb_tracks'] == 1: | ||||
| 			return generateQueueItem(dz, sp, f"https://www.deezer.com/track/{albumAPI['tracks']['data'][0]['id']}", settings, bitrate, albumAPI) | ||||
| 		tracksArray = dz.get_album_tracks_gw(id) | ||||
|     elif type == "album": | ||||
|         albumAPI = dz.get_album(id) | ||||
|         albumAPI_gw = dz.get_album_gw(id) | ||||
|         albumAPI['nb_disk'] = albumAPI_gw['NUMBER_DISK'] | ||||
|         albumAPI['copyright'] = albumAPI_gw['COPYRIGHT'] | ||||
|         if albumAPI['nb_tracks'] == 1: | ||||
|             return generateQueueItem(dz, sp, f"https://www.deezer.com/track/{albumAPI['tracks']['data'][0]['id']}", | ||||
|                                      settings, bitrate, albumAPI) | ||||
|         tracksArray = dz.get_album_tracks_gw(id) | ||||
|  | ||||
| 		result['title'] = albumAPI['title'] | ||||
| 		result['artist'] = albumAPI['artist']['name'] | ||||
| 		result['cover'] = albumAPI['cover_small'][:-24]+'/75x75-000000-80-0-0.jpg' | ||||
| 		result['size'] = albumAPI['nb_tracks'] | ||||
| 		result['downloaded'] = 0 | ||||
| 		result['failed'] = 0 | ||||
| 		result['progress'] = 0 | ||||
| 		result['type'] = 'album' | ||||
| 		result['id'] = id | ||||
| 		result['bitrate'] = bitrate | ||||
| 		result['uuid'] = f"{result['type']}_{id}_{bitrate}" | ||||
| 		result['settings'] = settings or {} | ||||
| 		totalSize = len(tracksArray) | ||||
| 		result['collection'] = [] | ||||
| 		for pos, trackAPI in enumerate(tracksArray, start=1): | ||||
| 			trackAPI['_EXTRA_ALBUM'] = albumAPI | ||||
| 			trackAPI['POSITION'] = pos | ||||
| 			trackAPI['SIZE'] = totalSize | ||||
| 			trackAPI['FILENAME_TEMPLATE'] = settings['albumTracknameTemplate'] | ||||
| 			result['collection'].append(trackAPI) | ||||
|         result['title'] = albumAPI['title'] | ||||
|         result['artist'] = albumAPI['artist']['name'] | ||||
|         result['cover'] = albumAPI['cover_small'][:-24] + '/75x75-000000-80-0-0.jpg' | ||||
|         result['size'] = albumAPI['nb_tracks'] | ||||
|         result['downloaded'] = 0 | ||||
|         result['failed'] = 0 | ||||
|         result['progress'] = 0 | ||||
|         result['type'] = 'album' | ||||
|         result['id'] = id | ||||
|         result['bitrate'] = bitrate | ||||
|         result['uuid'] = f"{result['type']}_{id}_{bitrate}" | ||||
|         result['settings'] = settings or {} | ||||
|         totalSize = len(tracksArray) | ||||
|         result['collection'] = [] | ||||
|         for pos, trackAPI in enumerate(tracksArray, start=1): | ||||
|             trackAPI['_EXTRA_ALBUM'] = albumAPI | ||||
|             trackAPI['POSITION'] = pos | ||||
|             trackAPI['SIZE'] = totalSize | ||||
|             trackAPI['FILENAME_TEMPLATE'] = settings['albumTracknameTemplate'] | ||||
|             result['collection'].append(trackAPI) | ||||
|  | ||||
| 	elif type == "playlist": | ||||
| 		playlistAPI = dz.get_playlist(id) | ||||
| 		playlistTracksAPI = dz.get_playlist_tracks_gw(id) | ||||
| 		playlistAPI['various_artist'] = dz.get_artist(5080) | ||||
|     elif type == "playlist": | ||||
|         playlistAPI = dz.get_playlist(id) | ||||
|         playlistTracksAPI = dz.get_playlist_tracks_gw(id) | ||||
|         playlistAPI['various_artist'] = dz.get_artist(5080) | ||||
|  | ||||
| 		result['title'] = playlistAPI['title'] | ||||
| 		result['artist'] = playlistAPI['creator']['name'] | ||||
| 		result['cover'] = playlistAPI['picture_small'][:-24]+'/75x75-000000-80-0-0.jpg' | ||||
| 		result['size'] = playlistAPI['nb_tracks'] | ||||
| 		result['downloaded'] = 0 | ||||
| 		result['failed'] = 0 | ||||
| 		result['progress'] = 0 | ||||
| 		result['type'] = 'playlist' | ||||
| 		result['id'] = id | ||||
| 		result['bitrate'] = bitrate | ||||
| 		result['uuid'] = f"{result['type']}_{id}_{bitrate}" | ||||
| 		result['settings'] = settings or {} | ||||
| 		totalSize = len(playlistTracksAPI) | ||||
| 		result['collection'] = [] | ||||
| 		for pos, trackAPI in enumerate(playlistTracksAPI, start=1): | ||||
| 			trackAPI['_EXTRA_PLAYLIST'] = playlistAPI | ||||
| 			trackAPI['POSITION'] = pos | ||||
| 			trackAPI['SIZE'] = totalSize | ||||
| 			trackAPI['FILENAME_TEMPLATE'] = settings['playlistTracknameTemplate'] | ||||
| 			result['collection'].append(trackAPI) | ||||
|         result['title'] = playlistAPI['title'] | ||||
|         result['artist'] = playlistAPI['creator']['name'] | ||||
|         result['cover'] = playlistAPI['picture_small'][:-24] + '/75x75-000000-80-0-0.jpg' | ||||
|         result['size'] = playlistAPI['nb_tracks'] | ||||
|         result['downloaded'] = 0 | ||||
|         result['failed'] = 0 | ||||
|         result['progress'] = 0 | ||||
|         result['type'] = 'playlist' | ||||
|         result['id'] = id | ||||
|         result['bitrate'] = bitrate | ||||
|         result['uuid'] = f"{result['type']}_{id}_{bitrate}" | ||||
|         result['settings'] = settings or {} | ||||
|         totalSize = len(playlistTracksAPI) | ||||
|         result['collection'] = [] | ||||
|         for pos, trackAPI in enumerate(playlistTracksAPI, start=1): | ||||
|             trackAPI['_EXTRA_PLAYLIST'] = playlistAPI | ||||
|             trackAPI['POSITION'] = pos | ||||
|             trackAPI['SIZE'] = totalSize | ||||
|             trackAPI['FILENAME_TEMPLATE'] = settings['playlistTracknameTemplate'] | ||||
|             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): | ||||
| 	global currentItem, queueList, queue | ||||
| 	if not dz.logged_in: | ||||
| 		return "Not logged in" | ||||
| 	queueItem = generateQueueItem(dz, sp, url, settings, bitrate, interface=interface) | ||||
| 	if type(queueItem) is list: | ||||
| 		for x in queueItem: | ||||
| 			if 'error' in x: | ||||
| 				continue | ||||
| 			if x['uuid'] in list(queueList.keys()): | ||||
| 				print("Already in queue!") | ||||
| 				continue | ||||
| 			if interface: | ||||
| 				interface.send("addedToQueue", x) | ||||
| 			queue.append(x['uuid']) | ||||
| 			queueList[x['uuid']] = x | ||||
| 	else: | ||||
| 		if 'error' in queueItem: | ||||
| 			if interface: | ||||
| 				interface.send("toast", {'msg': queueItem['error'], 'icon': 'error'}) | ||||
| 			return False | ||||
| 		if queueItem['uuid'] in list(queueList.keys()): | ||||
| 			print("Already in queue!") | ||||
| 			if interface: | ||||
| 				interface.send("toast", {'msg': f"{queueItem['title']} is already in queue!", 'icon': 'playlist_add_check'}) | ||||
| 			return False | ||||
| 		if interface: | ||||
| 			interface.send("addedToQueue", queueItem) | ||||
| 			interface.send("toast", {'msg': f"{queueItem['title']} added to queue", 'icon': 'playlist_add'}) | ||||
| 		queue.append(queueItem['uuid']) | ||||
| 		queueList[queueItem['uuid']] = queueItem | ||||
| 	nextItem(dz, interface) | ||||
| 	return True | ||||
|     global currentItem, queueList, queue | ||||
|     if not dz.logged_in: | ||||
|         return "Not logged in" | ||||
|     queueItem = generateQueueItem(dz, sp, url, settings, bitrate, interface=interface) | ||||
|     if type(queueItem) is list: | ||||
|         for x in queueItem: | ||||
|             if 'error' in x: | ||||
|                 continue | ||||
|             if x['uuid'] in list(queueList.keys()): | ||||
|                 print("Already in queue!") | ||||
|                 continue | ||||
|             if interface: | ||||
|                 interface.send("addedToQueue", x) | ||||
|             queue.append(x['uuid']) | ||||
|             queueList[x['uuid']] = x | ||||
|     else: | ||||
|         if 'error' in queueItem: | ||||
|             if interface: | ||||
|                 interface.send("toast", {'msg': queueItem['error'], 'icon': 'error'}) | ||||
|             return False | ||||
|         if queueItem['uuid'] in list(queueList.keys()): | ||||
|             print("Already in queue!") | ||||
|             if interface: | ||||
|                 interface.send("toast", | ||||
|                                {'msg': f"{queueItem['title']} is already in queue!", 'icon': 'playlist_add_check'}) | ||||
|             return False | ||||
|         if interface: | ||||
|             interface.send("addedToQueue", queueItem) | ||||
|             interface.send("toast", {'msg': f"{queueItem['title']} added to queue", 'icon': 'playlist_add'}) | ||||
|         queue.append(queueItem['uuid']) | ||||
|         queueList[queueItem['uuid']] = queueItem | ||||
|     nextItem(dz, interface) | ||||
|     return True | ||||
|  | ||||
|  | ||||
| def nextItem(dz, interface=None): | ||||
| 	global currentItem, queueList, queue | ||||
| 	if currentItem != "": | ||||
| 		return None | ||||
| 	else: | ||||
| 		if len(queue)>0: | ||||
| 			currentItem = queue.pop(0) | ||||
| 		else: | ||||
| 			return None | ||||
| 		if interface: | ||||
| 			interface.send("startDownload", currentItem) | ||||
| 		result = download(dz, queueList[currentItem], interface) | ||||
| 		callbackQueueDone(result) | ||||
|     global currentItem, queueList, queue | ||||
|     if currentItem != "": | ||||
|         return None | ||||
|     else: | ||||
|         if len(queue) > 0: | ||||
|             currentItem = queue.pop(0) | ||||
|         else: | ||||
|             return None | ||||
|         if interface: | ||||
|             interface.send("startDownload", currentItem) | ||||
|         result = download(dz, queueList[currentItem], interface) | ||||
|         callbackQueueDone(result) | ||||
|  | ||||
|  | ||||
| def callbackQueueDone(result): | ||||
| 	global currentItem, queueList, queueComplete | ||||
| 	if 'cancel' in queueList[currentItem]: | ||||
| 		del queueList[currentItem] | ||||
| 	else: | ||||
| 		queueComplete.append(currentItem) | ||||
| 	currentItem = "" | ||||
| 	nextItem(result['dz'], result['interface']) | ||||
|     global currentItem, queueList, queueComplete | ||||
|     if 'cancel' in queueList[currentItem]: | ||||
|         del queueList[currentItem] | ||||
|     else: | ||||
|         queueComplete.append(currentItem) | ||||
|     currentItem = "" | ||||
|     nextItem(result['dz'], result['interface']) | ||||
|  | ||||
|  | ||||
| def getQueue(): | ||||
| 	global currentItem, queueList, queue, queueComplete | ||||
| 	return (queue, queueComplete, queueList, currentItem) | ||||
|     global currentItem, queueList, queue, queueComplete | ||||
|     return (queue, queueComplete, queueList, currentItem) | ||||
|  | ||||
|  | ||||
| def removeFromQueue(uuid, interface=None): | ||||
| 	global currentItem, queueList, queue, queueComplete | ||||
| 	if uuid == currentItem: | ||||
| 		if interface: | ||||
| 			interface.send('toast', {'msg': "Cancelling current item.", 'icon':'loading', 'dismiss': False, 'id':'cancelling_'+uuid}) | ||||
| 		queueList[uuid]['cancel'] = True | ||||
| 	elif uuid in queue: | ||||
| 		queue.remove(uuid) | ||||
| 		del queueList[uuid] | ||||
| 		if interface: | ||||
| 			interface.send("removedFromQueue", uuid) | ||||
| 	elif uuid in queueComplete: | ||||
| 		queueComplete.remove(uuid) | ||||
| 		del queueList[uuid] | ||||
| 		if interface: | ||||
| 			interface.send("removedFromQueue", uuid) | ||||
|     global currentItem, queueList, queue, queueComplete | ||||
|     if uuid == currentItem: | ||||
|         if interface: | ||||
|             interface.send('toast', {'msg': "Cancelling current item.", 'icon': 'loading', 'dismiss': False, | ||||
|                                      'id': 'cancelling_' + uuid}) | ||||
|         queueList[uuid]['cancel'] = True | ||||
|     elif uuid in queue: | ||||
|         queue.remove(uuid) | ||||
|         del queueList[uuid] | ||||
|         if interface: | ||||
|             interface.send("removedFromQueue", uuid) | ||||
|     elif uuid in queueComplete: | ||||
|         queueComplete.remove(uuid) | ||||
|         del queueList[uuid] | ||||
|         if interface: | ||||
|             interface.send("removedFromQueue", uuid) | ||||
|  | ||||
|  | ||||
| def cancelAllDownloads(interface=None): | ||||
| 	global currentItem, queueList, queue, queueComplete | ||||
| 	queue = [] | ||||
| 	queueComplete = [] | ||||
| 	if currentItem != "": | ||||
| 		if interface: | ||||
| 			interface.send('toast', {'msg': "Cancelling current item.", 'icon':'loading', 'dismiss': False, 'id':'cancelling_'+currentItem}) | ||||
| 		queueList[currentItem]['cancel'] = True | ||||
| 	for uuid in list(queueList.keys()): | ||||
| 		if uuid != currentItem: | ||||
| 			del queueList[uuid] | ||||
| 	if interface: | ||||
| 		interface.send("removedAllDownloads", currentItem) | ||||
|     global currentItem, queueList, queue, queueComplete | ||||
|     queue = [] | ||||
|     queueComplete = [] | ||||
|     if currentItem != "": | ||||
|         if interface: | ||||
|             interface.send('toast', {'msg': "Cancelling current item.", 'icon': 'loading', 'dismiss': False, | ||||
|                                      'id': 'cancelling_' + currentItem}) | ||||
|         queueList[currentItem]['cancel'] = True | ||||
|     for uuid in list(queueList.keys()): | ||||
|         if uuid != currentItem: | ||||
|             del queueList[uuid] | ||||
|     if interface: | ||||
|         interface.send("removedAllDownloads", currentItem) | ||||
|  | ||||
|  | ||||
| def removeFinishedDownloads(interface=None): | ||||
| 	global queueList, queueComplete | ||||
| 	for uuid in queueComplete: | ||||
| 		del queueList[uuid] | ||||
| 	queueComplete = [] | ||||
| 	if interface: | ||||
| 		interface.send("removedFinishedDownloads") | ||||
|     global queueList, queueComplete | ||||
|     for uuid in queueComplete: | ||||
|         del queueList[uuid] | ||||
|     queueComplete = [] | ||||
|     if interface: | ||||
|         interface.send("removedFinishedDownloads") | ||||
|  | ||||
| @ -1,57 +1,61 @@ | ||||
| #!/usr/bin/env python3 | ||||
| import os.path as path | ||||
| from os import mkdir, rmdir | ||||
| import json | ||||
| import os.path as path | ||||
| from os import mkdir | ||||
|  | ||||
| import deemix.utils.localpaths as localpaths | ||||
|  | ||||
| settings = {} | ||||
| defaultSettings = {} | ||||
|  | ||||
|  | ||||
| def initSettings(): | ||||
| 	global settings | ||||
| 	global defaultSettings | ||||
| 	currentFolder = path.abspath(path.dirname(__file__)) | ||||
| 	configFolder = localpaths.getConfigFolder() | ||||
| 	if not path.isdir(configFolder): | ||||
| 		mkdir(configFolder) | ||||
| 	with open(path.join(currentFolder, 'default.json'), 'r') as d: | ||||
| 		defaultSettings = json.load(d) | ||||
| 	if not path.isfile(path.join(configFolder, 'config.json')): | ||||
| 		with open(path.join(configFolder, 'config.json'), 'w') as f: | ||||
| 			json.dump(defaultSettings, f, indent=2) | ||||
| 	with open(path.join(configFolder, 'config.json'), 'r') as configFile: | ||||
| 		settings = json.load(configFile) | ||||
| 	settingsCheck() | ||||
| 	if settings['downloadLocation'] == "": | ||||
| 		settings['downloadLocation'] = path.join(localpaths.getHomeFolder(), 'deemix Music') | ||||
| 		saveSettings(settings) | ||||
| 	if not path.isdir(settings['downloadLocation']): | ||||
| 		mkdir(settings['downloadLocation']) | ||||
| 	return settings | ||||
|     global settings | ||||
|     global defaultSettings | ||||
|     currentFolder = path.abspath(path.dirname(__file__)) | ||||
|     configFolder = localpaths.getConfigFolder() | ||||
|     if not path.isdir(configFolder): | ||||
|         mkdir(configFolder) | ||||
|     with open(path.join(currentFolder, 'default.json'), 'r') as d: | ||||
|         defaultSettings = json.load(d) | ||||
|     if not path.isfile(path.join(configFolder, 'config.json')): | ||||
|         with open(path.join(configFolder, 'config.json'), 'w') as f: | ||||
|             json.dump(defaultSettings, f, indent=2) | ||||
|     with open(path.join(configFolder, 'config.json'), 'r') as configFile: | ||||
|         settings = json.load(configFile) | ||||
|     settingsCheck() | ||||
|     if settings['downloadLocation'] == "": | ||||
|         settings['downloadLocation'] = path.join(localpaths.getHomeFolder(), 'deemix Music') | ||||
|         saveSettings(settings) | ||||
|     if not path.isdir(settings['downloadLocation']): | ||||
|         mkdir(settings['downloadLocation']) | ||||
|     return settings | ||||
|  | ||||
|  | ||||
| def getSettings(): | ||||
| 	global settings | ||||
| 	return settings | ||||
|     global settings | ||||
|     return settings | ||||
|  | ||||
|  | ||||
| def saveSettings(newSettings): | ||||
| 	global settings | ||||
| 	settings = newSettings | ||||
| 	with open(path.join(localpaths.getConfigFolder(), 'config.json'), 'w') as configFile: | ||||
| 		json.dump(settings, configFile, indent=2) | ||||
| 	return True | ||||
|     global settings | ||||
|     settings = newSettings | ||||
|     with open(path.join(localpaths.getConfigFolder(), 'config.json'), 'w') as configFile: | ||||
|         json.dump(settings, configFile, indent=2) | ||||
|     return True | ||||
|  | ||||
|  | ||||
| def settingsCheck(): | ||||
| 	global settings | ||||
| 	global defaultSettings | ||||
| 	changes = 0 | ||||
| 	for x in defaultSettings: | ||||
| 		if not x in settings or type(settings[x]) != type(defaultSettings[x]): | ||||
| 			settings[x] = defaultSettings[x] | ||||
| 			changes+=1 | ||||
| 	for x in defaultSettings['tags']: | ||||
| 		if not x in settings['tags'] or type(settings['tags'][x]) != type(defaultSettings['tags'][x]): | ||||
| 			settings['tags'][x] = defaultSettings['tags'][x] | ||||
| 			changes+=1 | ||||
| 	if changes > 0: | ||||
| 		saveSettings(settings) | ||||
|     global settings | ||||
|     global defaultSettings | ||||
|     changes = 0 | ||||
|     for x in defaultSettings: | ||||
|         if not x in settings or type(settings[x]) != type(defaultSettings[x]): | ||||
|             settings[x] = defaultSettings[x] | ||||
|             changes += 1 | ||||
|     for x in defaultSettings['tags']: | ||||
|         if not x in settings['tags'] or type(settings['tags'][x]) != type(defaultSettings['tags'][x]): | ||||
|             settings['tags'][x] = defaultSettings['tags'][x] | ||||
|             changes += 1 | ||||
|     if changes > 0: | ||||
|         saveSettings(settings) | ||||
|  | ||||
| @ -1,172 +1,179 @@ | ||||
| #!/usr/bin/env python3 | ||||
| import os.path as path | ||||
| from os import mkdir, rmdir | ||||
| import json | ||||
|  | ||||
| import deemix.utils.localpaths as localpaths | ||||
| import os.path as path | ||||
| from os import mkdir | ||||
|  | ||||
| import spotipy | ||||
| from spotipy.oauth2 import SpotifyClientCredentials | ||||
|  | ||||
| import deemix.utils.localpaths as localpaths | ||||
|  | ||||
|  | ||||
| class SpotifyHelper: | ||||
| 	def __init__(self): | ||||
| 		self.credentials = {} | ||||
| 		self.spotifyEnabled = False | ||||
| 		self.sp = None | ||||
| 		self.initCredentials() | ||||
|     def __init__(self): | ||||
|         self.credentials = {} | ||||
|         self.spotifyEnabled = False | ||||
|         self.sp = None | ||||
|         self.initCredentials() | ||||
|  | ||||
| 	def initCredentials(self): | ||||
| 		configFolder = localpaths.getConfigFolder() | ||||
| 		if not path.isdir(configFolder): | ||||
| 			mkdir(configFolder) | ||||
| 		if not path.isfile(path.join(configFolder, 'authCredentials.json')): | ||||
| 			with open(path.join(configFolder, 'authCredentials.json'), 'w') as f: | ||||
| 				json.dump({'clientId': "", 'clientSecret': ""}, f, indent=2) | ||||
| 		with open(path.join(configFolder, 'authCredentials.json'), 'r') as credentialsFile: | ||||
| 			self.credentials = json.load(credentialsFile) | ||||
| 		self.checkCredentials() | ||||
|     def initCredentials(self): | ||||
|         configFolder = localpaths.getConfigFolder() | ||||
|         if not path.isdir(configFolder): | ||||
|             mkdir(configFolder) | ||||
|         if not path.isfile(path.join(configFolder, 'authCredentials.json')): | ||||
|             with open(path.join(configFolder, 'authCredentials.json'), 'w') as f: | ||||
|                 json.dump({'clientId': "", 'clientSecret': ""}, f, indent=2) | ||||
|         with open(path.join(configFolder, 'authCredentials.json'), 'r') as credentialsFile: | ||||
|             self.credentials = json.load(credentialsFile) | ||||
|         self.checkCredentials() | ||||
|  | ||||
| 	def checkCredentials(self): | ||||
| 		if self.credentials['clientId'] == "" or self.credentials['clientSecret'] == "": | ||||
| 			spotifyEnabled = False | ||||
| 		else: | ||||
| 			try: | ||||
| 				self.createSpotifyConnection() | ||||
| 				self.sp.user_playlists('spotify') | ||||
| 				self.spotifyEnabled = True | ||||
| 			except Exception as e: | ||||
| 				self.spotifyEnabled = False | ||||
| 		return self.spotifyEnabled | ||||
|     def checkCredentials(self): | ||||
|         if self.credentials['clientId'] == "" or self.credentials['clientSecret'] == "": | ||||
|             spotifyEnabled = False | ||||
|         else: | ||||
|             try: | ||||
|                 self.createSpotifyConnection() | ||||
|                 self.sp.user_playlists('spotify') | ||||
|                 self.spotifyEnabled = True | ||||
|             except Exception as e: | ||||
|                 self.spotifyEnabled = False | ||||
|         return self.spotifyEnabled | ||||
|  | ||||
| 	def getCredentials(self): | ||||
| 		return self.credentials | ||||
|     def getCredentials(self): | ||||
|         return self.credentials | ||||
|  | ||||
| 	def setCredentials(self, spotifyCredentials): | ||||
| 		configFolder = localpaths.getConfigFolder() | ||||
| 		with open(path.join(configFolder, 'authCredentials.json'), 'w') as f: | ||||
| 			json.dump(spotifyCredentials, f, indent=2) | ||||
| 		self.credentials = spotifyCredentials | ||||
| 		self.checkCredentials() | ||||
|     def setCredentials(self, spotifyCredentials): | ||||
|         configFolder = localpaths.getConfigFolder() | ||||
|         with open(path.join(configFolder, 'authCredentials.json'), 'w') as f: | ||||
|             json.dump(spotifyCredentials, f, indent=2) | ||||
|         self.credentials = spotifyCredentials | ||||
|         self.checkCredentials() | ||||
|  | ||||
| 	def createSpotifyConnection(self): | ||||
| 		client_credentials_manager = SpotifyClientCredentials(client_id=self.credentials['clientId'], client_secret=self.credentials['clientSecret']) | ||||
| 		self.sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager) | ||||
|     def createSpotifyConnection(self): | ||||
|         client_credentials_manager = SpotifyClientCredentials(client_id=self.credentials['clientId'], | ||||
|                                                               client_secret=self.credentials['clientSecret']) | ||||
|         self.sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager) | ||||
|  | ||||
| 	def _convert_playlist_structure(self, spotify_obj): | ||||
| 		if len(spotify_obj['images']): | ||||
| 			url = spotify_obj['images'][0]['url'] | ||||
| 		else: | ||||
| 			url = "https://e-cdns-images.dzcdn.net/images/cover/d41d8cd98f00b204e9800998ecf8427e/75x75-000000-80-0-0.jpg" | ||||
| 		deezer_obj = { | ||||
| 			'checksum': spotify_obj['snapshot_id'], | ||||
| 			'collaborative': spotify_obj['collaborative'], | ||||
| 			'creation_date': "????-00-00", | ||||
| 			'creator': {'id': spotify_obj['owner']['id'], 'name': spotify_obj['owner']['display_name'], 'tracklist': spotify_obj['owner']['href'], 'type': "user"}, | ||||
| 			'description': spotify_obj['description'], | ||||
| 			'duration': 0, | ||||
| 			'fans': spotify_obj['followers']['total'], | ||||
| 			'id': spotify_obj['id'], | ||||
| 			'is_loved_track': False, | ||||
| 			'link': spotify_obj['external_urls']['spotify'], | ||||
| 			'nb_tracks': spotify_obj['tracks']['total'], | ||||
| 			'picture': url, | ||||
| 			'picture_big': url, | ||||
| 			'picture_medium': url, | ||||
| 			'picture_small': url, | ||||
| 			'picture_xl': url, | ||||
| 			'public': spotify_obj['public'], | ||||
| 			'share': spotify_obj['external_urls']['spotify'], | ||||
| 			'title': spotify_obj['name'], | ||||
| 			'tracklist': spotify_obj['tracks']['href'], | ||||
| 			'type': "playlist" | ||||
| 		} | ||||
| 		return deezer_obj | ||||
|     def _convert_playlist_structure(self, spotify_obj): | ||||
|         if len(spotify_obj['images']): | ||||
|             url = spotify_obj['images'][0]['url'] | ||||
|         else: | ||||
|             url = "https://e-cdns-images.dzcdn.net/images/cover/d41d8cd98f00b204e9800998ecf8427e/75x75-000000-80-0-0.jpg" | ||||
|         deezer_obj = { | ||||
|             'checksum': spotify_obj['snapshot_id'], | ||||
|             'collaborative': spotify_obj['collaborative'], | ||||
|             'creation_date': "????-00-00", | ||||
|             'creator': {'id': spotify_obj['owner']['id'], 'name': spotify_obj['owner']['display_name'], | ||||
|                         'tracklist': spotify_obj['owner']['href'], 'type': "user"}, | ||||
|             'description': spotify_obj['description'], | ||||
|             'duration': 0, | ||||
|             'fans': spotify_obj['followers']['total'], | ||||
|             'id': spotify_obj['id'], | ||||
|             'is_loved_track': False, | ||||
|             'link': spotify_obj['external_urls']['spotify'], | ||||
|             'nb_tracks': spotify_obj['tracks']['total'], | ||||
|             'picture': url, | ||||
|             'picture_big': url, | ||||
|             'picture_medium': url, | ||||
|             'picture_small': url, | ||||
|             'picture_xl': url, | ||||
|             'public': spotify_obj['public'], | ||||
|             'share': spotify_obj['external_urls']['spotify'], | ||||
|             'title': spotify_obj['name'], | ||||
|             'tracklist': spotify_obj['tracks']['href'], | ||||
|             'type': "playlist" | ||||
|         } | ||||
|         return deezer_obj | ||||
|  | ||||
| 	def get_trackid_spotify(self, dz, track_id, fallbackSearch, spotifyTrack=None): | ||||
| 		if not self.spotifyEnabled: | ||||
| 			raise spotifyFeaturesNotEnabled | ||||
| 		if not spotifyTrack: | ||||
| 			spotify_track = self.sp.track(track_id) | ||||
| 		else: | ||||
| 			spotify_track = spotifyTrack | ||||
| 		dz_track = 0 | ||||
| 		if 'external_ids' in spotify_track and 'isrc' in spotify_track['external_ids']: | ||||
| 			try: | ||||
| 				dz_track = dz.get_track_by_ISRC(spotify_track['external_ids']['isrc']) | ||||
| 				dz_track = dz_track['id'] if 'id' in dz_track else 0 | ||||
| 			except: | ||||
| 				dz_track = dz.get_track_from_metadata(spotify_track['artists'][0]['name'], spotify_track['name'], spotify_track['album']['name']) if fallbackSearch else 0 | ||||
| 		elif fallbackSearch: | ||||
| 			dz_track = dz.get_track_from_metadata(spotify_track['artists'][0]['name'], spotify_track['name'], spotify_track['album']['name']) | ||||
| 		return dz_track | ||||
|     def get_trackid_spotify(self, dz, track_id, fallbackSearch, spotifyTrack=None): | ||||
|         if not self.spotifyEnabled: | ||||
|             raise spotifyFeaturesNotEnabled | ||||
|         if not spotifyTrack: | ||||
|             spotify_track = self.sp.track(track_id) | ||||
|         else: | ||||
|             spotify_track = spotifyTrack | ||||
|         dz_track = 0 | ||||
|         if 'external_ids' in spotify_track and 'isrc' in spotify_track['external_ids']: | ||||
|             try: | ||||
|                 dz_track = dz.get_track_by_ISRC(spotify_track['external_ids']['isrc']) | ||||
|                 dz_track = dz_track['id'] if 'id' in dz_track else 0 | ||||
|             except: | ||||
|                 dz_track = dz.get_track_from_metadata(spotify_track['artists'][0]['name'], spotify_track['name'], | ||||
|                                                       spotify_track['album']['name']) if fallbackSearch else 0 | ||||
|         elif fallbackSearch: | ||||
|             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): | ||||
| 		if not self.spotifyEnabled: | ||||
| 			raise spotifyFeaturesNotEnabled | ||||
| 		spotify_album = self.sp.album(album_id) | ||||
| 		dz_album = 0 | ||||
| 		if 'external_ids' in spotify_album and 'upc' in spotify_album['external_ids']: | ||||
| 			try: | ||||
| 				dz_album = dz.get_album_by_UPC(spotify_album['external_ids']['upc']) | ||||
| 				dz_album = dz_album['id'] if 'id' in dz_album else 0 | ||||
| 			except: | ||||
| 				try: | ||||
| 					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 | ||||
| 				except: | ||||
| 					dz_album = 0 | ||||
| 		return dz_album | ||||
|     def get_albumid_spotify(self, dz, album_id): | ||||
|         if not self.spotifyEnabled: | ||||
|             raise spotifyFeaturesNotEnabled | ||||
|         spotify_album = self.sp.album(album_id) | ||||
|         dz_album = 0 | ||||
|         if 'external_ids' in spotify_album and 'upc' in spotify_album['external_ids']: | ||||
|             try: | ||||
|                 dz_album = dz.get_album_by_UPC(spotify_album['external_ids']['upc']) | ||||
|                 dz_album = dz_album['id'] if 'id' in dz_album else 0 | ||||
|             except: | ||||
|                 try: | ||||
|                     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 | ||||
|                 except: | ||||
|                     dz_album = 0 | ||||
|         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): | ||||
| 	pass | ||||
|     pass | ||||
|  | ||||
| @ -1,21 +1,24 @@ | ||||
| #!/usr/bin/env python3 | ||||
| import sys | ||||
| import os.path as path | ||||
| import sys | ||||
| from os import getenv | ||||
|  | ||||
| userdata = "" | ||||
| homedata = path.expanduser("~") | ||||
|  | ||||
| if getenv("APPDATA"): | ||||
| 	userdata = getenv("APPDATA") + path.sep + "deemix" + path.sep | ||||
|     userdata = getenv("APPDATA") + path.sep + "deemix" + path.sep | ||||
| elif sys.platform.startswith('darwin'): | ||||
| 	userdata = homedata + '/Library/Application Support/deemix/' | ||||
|     userdata = homedata + '/Library/Application Support/deemix/' | ||||
| elif getenv("XDG_CONFIG_HOME"): | ||||
| 	userdata = getenv("XDG_CONFIG_HOME") + '/deemix/'; | ||||
|     userdata = getenv("XDG_CONFIG_HOME") + '/deemix/'; | ||||
| else: | ||||
| 	userdata = homedata + '/.config/deemix/'; | ||||
|     userdata = homedata + '/.config/deemix/'; | ||||
|  | ||||
|  | ||||
| def getHomeFolder(): | ||||
| 	return homedata | ||||
|     return homedata | ||||
|  | ||||
|  | ||||
| def getConfigFolder(): | ||||
| 	return userdata | ||||
|     return userdata | ||||
|  | ||||
| @ -1,94 +1,98 @@ | ||||
| #!/usr/bin/env python3 | ||||
| import re | ||||
|  | ||||
|  | ||||
| def getBitrateInt(txt): | ||||
| 	txt = str(txt) | ||||
| 	if txt in ['flac', 'lossless', '9']: | ||||
| 		return 9 | ||||
| 	elif txt in ['mp3', '320', '3']: | ||||
| 		return 3 | ||||
| 	elif txt in ['128', '1']: | ||||
| 		return 1 | ||||
| 	elif txt in ['360', '360_hq', '15']: | ||||
| 		return 15 | ||||
| 	elif txt in ['360_mq', '14']: | ||||
| 		return 14 | ||||
| 	elif txt in ['360_lq', '13']: | ||||
| 		return 13 | ||||
| 	else: | ||||
| 		return None | ||||
|     txt = str(txt) | ||||
|     if txt in ['flac', 'lossless', '9']: | ||||
|         return 9 | ||||
|     elif txt in ['mp3', '320', '3']: | ||||
|         return 3 | ||||
|     elif txt in ['128', '1']: | ||||
|         return 1 | ||||
|     elif txt in ['360', '360_hq', '15']: | ||||
|         return 15 | ||||
|     elif txt in ['360_mq', '14']: | ||||
|         return 14 | ||||
|     elif txt in ['360_lq', '13']: | ||||
|         return 13 | ||||
|     else: | ||||
|         return None | ||||
|  | ||||
|  | ||||
| def changeCase(string, type): | ||||
| 	if type == "lower": | ||||
| 		return string.lower() | ||||
| 	elif type == "upper": | ||||
| 		return string.upper() | ||||
| 	elif type == "start": | ||||
| 		string = string.split(" ") | ||||
| 		res = [] | ||||
| 		for index, value in enumerate(string): | ||||
| 			res.append(value[0].upper() + value[0:].lower()) | ||||
| 		res = " ".join(res) | ||||
| 		return res | ||||
| 	elif type == "sentence": | ||||
| 		res = string[0].upper() + string[0:].lower() | ||||
| 		return res | ||||
| 	else: | ||||
| 		return string | ||||
|     if type == "lower": | ||||
|         return string.lower() | ||||
|     elif type == "upper": | ||||
|         return string.upper() | ||||
|     elif type == "start": | ||||
|         string = string.split(" ") | ||||
|         res = [] | ||||
|         for index, value in enumerate(string): | ||||
|             res.append(value[0].upper() + value[0:].lower()) | ||||
|         res = " ".join(res) | ||||
|         return res | ||||
|     elif type == "sentence": | ||||
|         res = string[0].upper() + string[0:].lower() | ||||
|         return res | ||||
|     else: | ||||
|         return string | ||||
|  | ||||
|  | ||||
| def getIDFromLink(link, type): | ||||
| 	if '?' in link: | ||||
| 		link = link[:link.find('?')] | ||||
| 	if link.endswith("/"): | ||||
| 		link = link[:-1] | ||||
|     if '?' in link: | ||||
|         link = link[:link.find('?')] | ||||
|     if link.endswith("/"): | ||||
|         link = link[:-1] | ||||
|  | ||||
| 	if link.startswith("http") and 'open.spotify.com/' in link: | ||||
| 		if type == "spotifyplaylist": | ||||
| 			return link[link.find("/playlist/") + 10:] | ||||
| 		if type == "spotifytrack": | ||||
| 			return link[link.find("/track/") + 7:] | ||||
| 		if type == "spotifyalbum": | ||||
| 			return link[link.find("/album/") + 7:] | ||||
| 	elif link.startswith("spotify:"): | ||||
| 		if type == "spotifyplaylist": | ||||
| 			return link[link.find("playlist:") + 9:] | ||||
| 		if type == "spotifytrack": | ||||
| 			return link[link.find("track:") + 6:] | ||||
| 		if type == "spotifyalbum": | ||||
| 			return link[link.find("album:") + 6:] | ||||
| 	elif type == "artisttop": | ||||
| 		return re.search(r"\/artist\/(\d+)\/top_track", link)[1] | ||||
| 	else: | ||||
| 		return link[link.rfind("/") + 1:] | ||||
|     if link.startswith("http") and 'open.spotify.com/' in link: | ||||
|         if type == "spotifyplaylist": | ||||
|             return link[link.find("/playlist/") + 10:] | ||||
|         if type == "spotifytrack": | ||||
|             return link[link.find("/track/") + 7:] | ||||
|         if type == "spotifyalbum": | ||||
|             return link[link.find("/album/") + 7:] | ||||
|     elif link.startswith("spotify:"): | ||||
|         if type == "spotifyplaylist": | ||||
|             return link[link.find("playlist:") + 9:] | ||||
|         if type == "spotifytrack": | ||||
|             return link[link.find("track:") + 6:] | ||||
|         if type == "spotifyalbum": | ||||
|             return link[link.find("album:") + 6:] | ||||
|     elif type == "artisttop": | ||||
|         return re.search(r"\/artist\/(\d+)\/top_track", link)[1] | ||||
|     else: | ||||
|         return link[link.rfind("/") + 1:] | ||||
|  | ||||
|  | ||||
| def getTypeFromLink(link): | ||||
| 	type = '' | ||||
| 	if 'spotify' in link: | ||||
| 		type = 'spotify' | ||||
| 		if 'playlist' in link: | ||||
| 			type += 'playlist' | ||||
| 		elif 'track' in link: | ||||
| 			type += 'track' | ||||
| 		elif 'album' in link: | ||||
| 			type += 'album' | ||||
| 	elif 'deezer' in link: | ||||
| 		if '/track' in link: | ||||
| 			type = 'track' | ||||
| 		elif '/playlist' in link: | ||||
| 			type = 'playlist' | ||||
| 		elif '/album' in link: | ||||
| 			type = 'album' | ||||
| 		elif re.search("\/artist\/(\d+)\/top_track", link): | ||||
| 			type = 'artisttop' | ||||
| 		elif '/artist' in link: | ||||
| 			type = 'artist' | ||||
| 	return type | ||||
|     type = '' | ||||
|     if 'spotify' in link: | ||||
|         type = 'spotify' | ||||
|         if 'playlist' in link: | ||||
|             type += 'playlist' | ||||
|         elif 'track' in link: | ||||
|             type += 'track' | ||||
|         elif 'album' in link: | ||||
|             type += 'album' | ||||
|     elif 'deezer' in link: | ||||
|         if '/track' in link: | ||||
|             type = 'track' | ||||
|         elif '/playlist' in link: | ||||
|             type = 'playlist' | ||||
|         elif '/album' in link: | ||||
|             type = 'album' | ||||
|         elif re.search("\/artist\/(\d+)\/top_track", link): | ||||
|             type = 'artisttop' | ||||
|         elif '/artist' in link: | ||||
|             type = 'artist' | ||||
|     return type | ||||
|  | ||||
|  | ||||
| def isValidLink(text): | ||||
| 	if text.lower().startswith("http"): | ||||
| 		if "deezer.com" in text.lower() or "open.spotify.com" in text.lower(): | ||||
| 			return True | ||||
| 	elif text.lower().startswith("spotify:"): | ||||
| 		return True | ||||
| 	return False | ||||
|     if text.lower().startswith("http"): | ||||
|         if "deezer.com" in text.lower() or "open.spotify.com" in text.lower(): | ||||
|             return True | ||||
|     elif text.lower().startswith("spotify:"): | ||||
|         return True | ||||
|     return False | ||||
|  | ||||
| @ -3,166 +3,194 @@ 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" | ||||
|     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 | ||||
|     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 name2: | ||||
| 			txt = txt[:200] | ||||
| 			name += txt+pathSep | ||||
| 		name = name[:-1] | ||||
| 	else: | ||||
| 		name = name[:200] | ||||
| 	return name | ||||
|     if pathSep in name: | ||||
|         name2 = name.split(pathSep) | ||||
|         name = "" | ||||
|         for txt in name2: | ||||
|             txt = txt[:200] | ||||
|             name += txt + pathSep | ||||
|         name = name[:-1] | ||||
|     else: | ||||
|         name = name[:200] | ||||
|     return name | ||||
|  | ||||
|  | ||||
| def antiDot(string): | ||||
| 	while string[-1:] == "." or string[-1:] == " " or string[-1:] == "\n": | ||||
| 		string = string[:-1] | ||||
| 	if len(string) < 1: | ||||
| 		string = "dot" | ||||
| 	return string | ||||
|     while string[-1:] == "." or string[-1:] == " " or string[-1:] == "\n": | ||||
|         string = string[:-1] | ||||
|     if len(string) < 1: | ||||
|         string = "dot" | ||||
|     return string | ||||
|  | ||||
|  | ||||
| def pad(num, max, dopad=True): | ||||
| 	paddingsize = len(str(max)) | ||||
| 	if dopad: | ||||
| 		return str(num).zfill(paddingsize) | ||||
| 	else: | ||||
| 		return str(num) | ||||
|     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['_EXTRA_PLAYLIST'] if '_EXTRA_PLAYLIST' in trackAPI else None) | ||||
|     if trackAPI['FILENAME_TEMPLATE'] == "": | ||||
|         filename = "%artist% - %title%" | ||||
|     else: | ||||
|         filename = trackAPI['FILENAME_TEMPLATE'] | ||||
|     return settingsRegex(filename, track, settings, | ||||
|                          trackAPI['_EXTRA_PLAYLIST'] if '_EXTRA_PLAYLIST' in trackAPI else None) | ||||
|  | ||||
|  | ||||
| def generateFilepath(track, trackAPI, settings): | ||||
| 	filepath = settings['downloadLocation'] | ||||
| 	if filepath[-1:] != pathSep: | ||||
| 		filepath += pathSep | ||||
| 	artistPath = None | ||||
| 	coverPath = None | ||||
| 	extrasPath = None | ||||
|     filepath = settings['downloadLocation'] | ||||
|     if filepath[-1:] != pathSep: | ||||
|         filepath += pathSep | ||||
|     artistPath = None | ||||
|     coverPath = None | ||||
|     extrasPath = None | ||||
|  | ||||
| 	if settings['createPlaylistFolder'] and '_EXTRA_PLAYLIST' in trackAPI and not settings['tags']['savePlaylistAsCompilation']: | ||||
| 		filepath += antiDot(settingsRegexPlaylist(settings['playlistNameTemplate'], trackAPI['_EXTRA_PLAYLIST'], settings)) + pathSep | ||||
|     if settings['createPlaylistFolder'] and '_EXTRA_PLAYLIST' in trackAPI and not settings['tags'][ | ||||
|         'savePlaylistAsCompilation']: | ||||
|         filepath += antiDot( | ||||
|             settingsRegexPlaylist(settings['playlistNameTemplate'], trackAPI['_EXTRA_PLAYLIST'], settings)) + pathSep | ||||
|  | ||||
| 	if '_EXTRA_PLAYLIST' in trackAPI and not settings['tags']['savePlaylistAsCompilation']: | ||||
| 		extrasPath = filepath | ||||
|     if '_EXTRA_PLAYLIST' in trackAPI and not settings['tags']['savePlaylistAsCompilation']: | ||||
|         extrasPath = filepath | ||||
|  | ||||
| 	if ( | ||||
| 		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['createStructurePlaylist']) | ||||
| 	): | ||||
| 		if (int(track['id'])<0 and not 'mainArtist' in track['album']): | ||||
| 			track['album']['mainArtist'] = track['mainArtist'] | ||||
| 		filepath += antiDot(settingsRegexArtist(settings['artistNameTemplate'], track['album']['mainArtist'], settings)) + pathSep | ||||
| 		artistPath = filepath | ||||
|     if ( | ||||
|             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['createStructurePlaylist']) | ||||
|     ): | ||||
|         if (int(track['id']) < 0 and not 'mainArtist' in track['album']): | ||||
|             track['album']['mainArtist'] = track['mainArtist'] | ||||
|         filepath += antiDot( | ||||
|             settingsRegexArtist(settings['artistNameTemplate'], track['album']['mainArtist'], settings)) + pathSep | ||||
|         artistPath = filepath | ||||
|  | ||||
| 	if (settings['createAlbumFolder'] 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'])) | ||||
| 	): | ||||
| 		filepath += antiDot(settingsRegexAlbum(settings['albumNameTemplate'], track['album'], settings, trackAPI)) + pathSep | ||||
| 		coverPath = filepath | ||||
|     if (settings['createAlbumFolder'] 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'])) | ||||
|     ): | ||||
|         filepath += antiDot( | ||||
|             settingsRegexAlbum(settings['albumNameTemplate'], track['album'], settings, trackAPI)) + pathSep | ||||
|         coverPath = filepath | ||||
|  | ||||
| 	if not ('_EXTRA_PLAYLIST' in trackAPI and not settings['tags']['savePlaylistAsCompilation']): | ||||
| 		extrasPath = filepath | ||||
|     if not ('_EXTRA_PLAYLIST' in trackAPI and not settings['tags']['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 '_EXTRA_PLAYLIST' in trackAPI or ('_EXTRA_PLAYLIST' in trackAPI and settings['tags']['savePlaylistAsCompilation']) or ('_EXTRA_PLAYLIST' in trackAPI and settings['createStructurePlaylist'])) | ||||
| 	)): | ||||
| 		filepath += 'CD' + str(track['discNumber']) + pathSep | ||||
|     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 '_EXTRA_PLAYLIST' in trackAPI or ( | ||||
|                     '_EXTRA_PLAYLIST' in trackAPI and settings['tags']['savePlaylistAsCompilation']) or ( | ||||
|                      '_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): | ||||
| 	filename = filename.replace("%title%", fixName(track['title'], settings['illegalCharacterReplacer'])) | ||||
| 	filename = filename.replace("%artist%", fixName(track['mainArtist']['name'], settings['illegalCharacterReplacer'])) | ||||
| 	filename = filename.replace("%artists%", fixName(track['mainArtistsString'], 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("%tracknumber%", pad(track['trackNumber'], track['album']['trackTotal'] if int(settings['paddingSize']) == 0 else 10 ** (int(settings['paddingSize'])-1), 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%", track['dateString']) | ||||
| 	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("%title%", fixName(track['title'], settings['illegalCharacterReplacer'])) | ||||
|     filename = filename.replace("%artist%", fixName(track['mainArtist']['name'], settings['illegalCharacterReplacer'])) | ||||
|     filename = filename.replace("%artists%", fixName(track['mainArtistsString'], 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("%tracknumber%", pad(track['trackNumber'], track['album']['trackTotal'] if int( | ||||
|         settings['paddingSize']) == 0 else 10 ** (int(settings['paddingSize']) - 1), 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%", track['dateString']) | ||||
|     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'] 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): | ||||
| 	if trackAPI and '_EXTRA_PLAYLIST' in trackAPI and settings['tags']['savePlaylistAsCompilation']: | ||||
| 		foldername = foldername.replace("%album_id%", "pl_"+str(trackAPI['_EXTRA_PLAYLIST']['id'])) | ||||
| 	else: | ||||
| 		foldername = foldername.replace("%album_id%", str(album['id'])) | ||||
| 	foldername = foldername.replace("%album%", fixName(album['title'], settings['illegalCharacterReplacer'])) | ||||
| 	foldername = foldername.replace("%artist%", fixName(album['mainArtist']['name'], settings['illegalCharacterReplacer'])) | ||||
| 	foldername = foldername.replace("%artist_id%", str(album['mainArtist']['id'])) | ||||
| 	foldername = foldername.replace("%tracktotal%", str(album['trackTotal'])) | ||||
| 	foldername = foldername.replace("%disctotal%", str(album['discTotal'])) | ||||
| 	foldername = foldername.replace("%type%", fixName(album['recordType'][0].upper()+album['recordType'][1:].lower(), 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%", album['dateString']) | ||||
| 	foldername = foldername.replace("%bitrate%", bitrateLabels[int(album['bitrate'])]) | ||||
|     if trackAPI and '_EXTRA_PLAYLIST' in trackAPI and settings['tags']['savePlaylistAsCompilation']: | ||||
|         foldername = foldername.replace("%album_id%", "pl_" + str(trackAPI['_EXTRA_PLAYLIST']['id'])) | ||||
|     else: | ||||
|         foldername = foldername.replace("%album_id%", str(album['id'])) | ||||
|     foldername = foldername.replace("%album%", fixName(album['title'], settings['illegalCharacterReplacer'])) | ||||
|     foldername = foldername.replace("%artist%", | ||||
|                                     fixName(album['mainArtist']['name'], settings['illegalCharacterReplacer'])) | ||||
|     foldername = foldername.replace("%artist_id%", str(album['mainArtist']['id'])) | ||||
|     foldername = foldername.replace("%tracktotal%", str(album['trackTotal'])) | ||||
|     foldername = foldername.replace("%disctotal%", str(album['discTotal'])) | ||||
|     foldername = foldername.replace("%type%", fixName(album['recordType'][0].upper() + album['recordType'][1:].lower(), | ||||
|                                                       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%", 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): | ||||
| 	foldername = foldername.replace("%artist%", fixName(artist['name'], settings['illegalCharacterReplacer'])) | ||||
| 	foldername = foldername.replace("%artist_id%", str(artist['id'])) | ||||
| 	foldername = foldername.replace('\\', pathSep).replace('/', pathSep) | ||||
| 	return antiDot(fixLongName(foldername)) | ||||
|     foldername = foldername.replace("%artist%", fixName(artist['name'], settings['illegalCharacterReplacer'])) | ||||
|     foldername = foldername.replace("%artist_id%", str(artist['id'])) | ||||
|     foldername = foldername.replace('\\', pathSep).replace('/', pathSep) | ||||
|     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 = foldername.replace('\\', pathSep).replace('/', pathSep) | ||||
| 	return antiDot(fixLongName(foldername)) | ||||
|     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 = foldername.replace('\\', pathSep).replace('/', pathSep) | ||||
|     return antiDot(fixLongName(foldername)) | ||||
|  | ||||
| @ -1,135 +1,139 @@ | ||||
| #!/usr/bin/env python3 | ||||
| from mutagen.flac import FLAC, Picture | ||||
| 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): | ||||
| 	try: | ||||
| 		tag = ID3(stream) | ||||
| 	except ID3NoHeaderError: | ||||
| 		tag = ID3() | ||||
|     try: | ||||
|         tag = ID3(stream) | ||||
|     except ID3NoHeaderError: | ||||
|         tag = ID3() | ||||
|  | ||||
| 	if save['title']: | ||||
| 		tag.add(TIT2(text=track['title'])) | ||||
| 	if save['artist']: | ||||
| 		if save['multitagSeparator'] != "default": | ||||
| 			tag.add(TPE1(text=track['artistsString'])) | ||||
| 			tag.add(TXXX(desc="ARTISTS", text=track['artists'])) | ||||
| 		else: | ||||
| 			tag.add(TPE1(text=track['artists'])) | ||||
| 	if save['album']: | ||||
| 		tag.add(TALB(text=track['album']['title'])) | ||||
| 	if save['albumArtist']: | ||||
| 		tag.add(TPE2(text=track['album']['artists'])) | ||||
| 	if save['trackNumber']: | ||||
| 		tag.add(TRCK(text=str(track['trackNumber'])+("/"+str(track['album']['trackTotal']) if save['trackTotal'] else ""))) | ||||
| 	if save['discNumber']: | ||||
| 		tag.add(TPOS(text=str(track['discNumber'])+("/"+str(track['album']['discTotal']) if save['discTotal'] else ""))) | ||||
| 	if save['genre']: | ||||
| 		tag.add(TCON(text=track['album']['genre'])) | ||||
| 	if save['year']: | ||||
| 		tag.add(TYER(text=str(track['date']['year']))) | ||||
| 	if save['date']: | ||||
| 		tag.add(TDAT(text=str(track['date']['month']) + str(track['date']['day']))) | ||||
| 	if save['length']: | ||||
| 		tag.add(TLEN(text=str(track['duration']))) | ||||
| 	if save['bpm']: | ||||
| 		tag.add(TBPM(text=str(track['bpm']))) | ||||
| 	if save['label']: | ||||
| 		tag.add(TPUB(text=track['album']['label'])) | ||||
| 	if save['isrc']: | ||||
| 		tag.add(TSRC(text=track['ISRC'])) | ||||
| 	if save['barcode']: | ||||
| 		tag.add(TXXX(desc="BARCODE", text=track['album']['barcode'])) | ||||
| 	if save['explicit']: | ||||
| 		tag.add(TXXX(desc="ITUNESADVISORY", text="1" if track['explicit'] else "0")) | ||||
| 	if save['replayGain']: | ||||
| 		tag.add(TXXX(desc="REPLAYGAIN_TRACK_GAIN", text=track['replayGain'])) | ||||
| 	if 'unsync' in track['lyrics'] and save['lyrics']: | ||||
| 		tag.add(USLT(text=track['lyrics']['unsync'])) | ||||
| 	involved_people = [] | ||||
| 	for role in track['contributors']: | ||||
| 		if role in ['author', 'engineer', 'mixer', 'producer', 'writer']: | ||||
| 			for person in track['contributors'][role]: | ||||
| 				involved_people.append([role, person]) | ||||
| 		elif role == 'composer' and save['composer']: | ||||
| 			tag.add(TCOM(text=track['contributors']['composer'])) | ||||
| 	if len(involved_people) > 0 and save['involvedPeople']: | ||||
| 		tag.add(IPLS(people=involved_people)) | ||||
| 	if save['copyright']: | ||||
| 		tag.add(TCOP(text=track['copyright'])) | ||||
| 	if save['savePlaylistAsCompilation']: | ||||
| 		tag.add(TCMP(text="1")) | ||||
| 	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())) | ||||
|     if save['title']: | ||||
|         tag.add(TIT2(text=track['title'])) | ||||
|     if save['artist']: | ||||
|         if save['multitagSeparator'] != "default": | ||||
|             tag.add(TPE1(text=track['artistsString'])) | ||||
|             tag.add(TXXX(desc="ARTISTS", text=track['artists'])) | ||||
|         else: | ||||
|             tag.add(TPE1(text=track['artists'])) | ||||
|     if save['album']: | ||||
|         tag.add(TALB(text=track['album']['title'])) | ||||
|     if save['albumArtist']: | ||||
|         tag.add(TPE2(text=track['album']['artists'])) | ||||
|     if save['trackNumber']: | ||||
|         tag.add(TRCK( | ||||
|             text=str(track['trackNumber']) + ("/" + str(track['album']['trackTotal']) if save['trackTotal'] else ""))) | ||||
|     if save['discNumber']: | ||||
|         tag.add( | ||||
|             TPOS(text=str(track['discNumber']) + ("/" + str(track['album']['discTotal']) if save['discTotal'] else ""))) | ||||
|     if save['genre']: | ||||
|         tag.add(TCON(text=track['album']['genre'])) | ||||
|     if save['year']: | ||||
|         tag.add(TYER(text=str(track['date']['year']))) | ||||
|     if save['date']: | ||||
|         tag.add(TDAT(text=str(track['date']['month']) + str(track['date']['day']))) | ||||
|     if save['length']: | ||||
|         tag.add(TLEN(text=str(track['duration']))) | ||||
|     if save['bpm']: | ||||
|         tag.add(TBPM(text=str(track['bpm']))) | ||||
|     if save['label']: | ||||
|         tag.add(TPUB(text=track['album']['label'])) | ||||
|     if save['isrc']: | ||||
|         tag.add(TSRC(text=track['ISRC'])) | ||||
|     if save['barcode']: | ||||
|         tag.add(TXXX(desc="BARCODE", text=track['album']['barcode'])) | ||||
|     if save['explicit']: | ||||
|         tag.add(TXXX(desc="ITUNESADVISORY", text="1" if track['explicit'] else "0")) | ||||
|     if save['replayGain']: | ||||
|         tag.add(TXXX(desc="REPLAYGAIN_TRACK_GAIN", text=track['replayGain'])) | ||||
|     if 'unsync' in track['lyrics'] and save['lyrics']: | ||||
|         tag.add(USLT(text=track['lyrics']['unsync'])) | ||||
|     involved_people = [] | ||||
|     for role in track['contributors']: | ||||
|         if role in ['author', 'engineer', 'mixer', 'producer', 'writer']: | ||||
|             for person in track['contributors'][role]: | ||||
|                 involved_people.append([role, person]) | ||||
|         elif role == 'composer' and save['composer']: | ||||
|             tag.add(TCOM(text=track['contributors']['composer'])) | ||||
|     if len(involved_people) > 0 and save['involvedPeople']: | ||||
|         tag.add(IPLS(people=involved_people)) | ||||
|     if save['copyright']: | ||||
|         tag.add(TCOP(text=track['copyright'])) | ||||
|     if save['savePlaylistAsCompilation']: | ||||
|         tag.add(TCMP(text="1")) | ||||
|     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): | ||||
| 	tag = FLAC(stream) | ||||
|     tag = FLAC(stream) | ||||
|  | ||||
| 	if save['title']: | ||||
| 		tag["TITLE"] = track['title'] | ||||
| 	if save['artist']: | ||||
| 		if save['multitagSeparator'] != "default": | ||||
| 			tag["ARTIST"] = track['artistsString'] | ||||
| 			tag["ARTISTS"] = track['artists'] | ||||
| 		else: | ||||
| 			tag["ARTIST"] = track['artists'] | ||||
| 	if save['album']: | ||||
| 		tag["ALBUM"] = track['album']['title'] | ||||
| 	if save['albumArtist']: | ||||
| 		tag["ALBUMARTIST"] = track['album']['artists'] | ||||
| 	if save['trackNumber']: | ||||
| 		tag["TRACKNUMBER"] = str(track['trackNumber']) | ||||
| 	if save['trackTotal']: | ||||
| 		tag["TRACKTOTAL"] = str(track['album']['trackTotal']) | ||||
| 	if save['discNumber']: | ||||
| 		tag["DISCNUMBER"] = str(track['discNumber']) | ||||
| 	if save['discTotal']: | ||||
| 		tag["DISCTOTAL"] = str(track['album']['discTotal']) | ||||
| 	if save['genre']: | ||||
| 		tag["GENRE"] = track['album']['genre'] | ||||
| 	if save['year']: | ||||
| 		tag["YEAR"] = str(track['date']['year']) | ||||
| 	if save['date']: | ||||
| 		tag["DATE"] = track['dateString'] | ||||
| 	if save['length']: | ||||
| 		tag["LENGTH"] = str(track['duration']) | ||||
| 	if save['bpm']: | ||||
| 		tag["BPM"] = str(track['bpm']) | ||||
| 	if save['label']: | ||||
| 		tag["PUBLISHER"] = track['album']['label'] | ||||
| 	if save['isrc']: | ||||
| 		tag["ISRC"] = track['ISRC'] | ||||
| 	if save['barcode']: | ||||
| 		tag["BARCODE"] = track['album']['barcode'] | ||||
| 	if save['explicit']: | ||||
| 		tag["ITUNESADVISORY"] = "1" if track['explicit'] else "0" | ||||
| 	if save['replayGain']: | ||||
| 		tag["REPLAYGAIN_TRACK_GAIN"] = track['replayGain'] | ||||
| 	if 'unsync' in track['lyrics'] and save['lyrics']: | ||||
| 		tag["LYRICS"] = track['lyrics']['unsync'] | ||||
| 	for role in track['contributors']: | ||||
| 		if role in ['author', 'engineer', 'mixer', 'producer', 'writer', 'composer']: | ||||
| 			if save['involvedPeople'] and role != 'composer' or role == 'composer' and save['composer']: | ||||
| 				tag[role] = track['contributors'][role] | ||||
| 		elif role == 'musicpublisher' and save['involvedPeople']: | ||||
| 			tag["ORGANIZATION"] = track['contributors']['musicpublisher'] | ||||
| 	if save['copyright']: | ||||
| 		tag["COPYRIGHT"] = track['copyright'] | ||||
| 	if save['savePlaylistAsCompilation']: | ||||
| 		tag["COMPILATION"] = "1" | ||||
|     if save['title']: | ||||
|         tag["TITLE"] = track['title'] | ||||
|     if save['artist']: | ||||
|         if save['multitagSeparator'] != "default": | ||||
|             tag["ARTIST"] = track['artistsString'] | ||||
|             tag["ARTISTS"] = track['artists'] | ||||
|         else: | ||||
|             tag["ARTIST"] = track['artists'] | ||||
|     if save['album']: | ||||
|         tag["ALBUM"] = track['album']['title'] | ||||
|     if save['albumArtist']: | ||||
|         tag["ALBUMARTIST"] = track['album']['artists'] | ||||
|     if save['trackNumber']: | ||||
|         tag["TRACKNUMBER"] = str(track['trackNumber']) | ||||
|     if save['trackTotal']: | ||||
|         tag["TRACKTOTAL"] = str(track['album']['trackTotal']) | ||||
|     if save['discNumber']: | ||||
|         tag["DISCNUMBER"] = str(track['discNumber']) | ||||
|     if save['discTotal']: | ||||
|         tag["DISCTOTAL"] = str(track['album']['discTotal']) | ||||
|     if save['genre']: | ||||
|         tag["GENRE"] = track['album']['genre'] | ||||
|     if save['year']: | ||||
|         tag["YEAR"] = str(track['date']['year']) | ||||
|     if save['date']: | ||||
|         tag["DATE"] = track['dateString'] | ||||
|     if save['length']: | ||||
|         tag["LENGTH"] = str(track['duration']) | ||||
|     if save['bpm']: | ||||
|         tag["BPM"] = str(track['bpm']) | ||||
|     if save['label']: | ||||
|         tag["PUBLISHER"] = track['album']['label'] | ||||
|     if save['isrc']: | ||||
|         tag["ISRC"] = track['ISRC'] | ||||
|     if save['barcode']: | ||||
|         tag["BARCODE"] = track['album']['barcode'] | ||||
|     if save['explicit']: | ||||
|         tag["ITUNESADVISORY"] = "1" if track['explicit'] else "0" | ||||
|     if save['replayGain']: | ||||
|         tag["REPLAYGAIN_TRACK_GAIN"] = track['replayGain'] | ||||
|     if 'unsync' in track['lyrics'] and save['lyrics']: | ||||
|         tag["LYRICS"] = track['lyrics']['unsync'] | ||||
|     for role in track['contributors']: | ||||
|         if role in ['author', 'engineer', 'mixer', 'producer', 'writer', 'composer']: | ||||
|             if save['involvedPeople'] and role != 'composer' or role == 'composer' and save['composer']: | ||||
|                 tag[role] = track['contributors'][role] | ||||
|         elif role == 'musicpublisher' and save['involvedPeople']: | ||||
|             tag["ORGANIZATION"] = track['contributors']['musicpublisher'] | ||||
|     if save['copyright']: | ||||
|         tag["COPYRIGHT"] = track['copyright'] | ||||
|     if save['savePlaylistAsCompilation']: | ||||
|         tag["COMPILATION"] = "1" | ||||
|  | ||||
| 	if save['cover'] and track['album']['picPath']: | ||||
| 		image = Picture() | ||||
| 		image.type = 3 | ||||
| 		image.mime = 'image/jpeg' if track['album']['picPath'].endswith('jpg') else 'image/png' | ||||
| 		with open(track['album']['picPath'], 'rb') as f: | ||||
| 			image.data = f.read() | ||||
| 		tag.add_picture(image) | ||||
|     if save['cover'] and track['album']['picPath']: | ||||
|         image = Picture() | ||||
|         image.type = 3 | ||||
|         image.mime = 'image/jpeg' if track['album']['picPath'].endswith('jpg') else 'image/png' | ||||
|         with open(track['album']['picPath'], 'rb') as f: | ||||
|             image.data = f.read() | ||||
|         tag.add_picture(image) | ||||
|  | ||||
| 	tag.save(deleteid3=True) | ||||
|     tag.save(deleteid3=True) | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 RemixDev
					RemixDev