Made the refactoring work

This commit is contained in:
RemixDev 2020-08-15 23:03:05 +02:00
parent 4cfdc4872d
commit 34263c150f
14 changed files with 171 additions and 190 deletions

View File

@ -1,36 +1,25 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import click import click
import deemix.app.cli as app from deemix.app.cli import cli
from deemix.app.settings import initSettings
from os.path import isfile from os.path import isfile
import random
import string
def randomString(stringLength=8):
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for i in range(stringLength))
@click.command() @click.command()
@click.option('-b', '--bitrate', default=None, help='Overwrites the default bitrate selected') @click.option('-b', '--bitrate', default=None, help='Overwrites the default bitrate selected')
@click.option('-l', '--local', is_flag=True, help='Downloads in a local folder insted of using the default') @click.option('-l', '--local', is_flag=True, help='Downloads in a local folder insted of using the default')
@click.argument('url', nargs=-1, required=True) @click.argument('url', nargs=-1, required=True)
def download(bitrate, local, url): def download(bitrate, local, url):
settings = initSettings() app = cli(local)
if local:
settings['downloadLocation'] = randomString(12)
click.echo("Using a local download folder: "+settings['downloadLocation'])
app.login() app.login()
url = list(url) url = list(url)
if isfile(url[0]): if isfile(url[0]):
filename = url[0] filename = url[0]
with open(filename) as f: with open(filename) as f:
url = f.readlines() url = f.readlines()
app.downloadLink(url, settings, bitrate) app.downloadLink(url, bitrate)
click.echo("All done!") click.echo("All done!")
if local: if local:
click.echo(settings['downloadLocation']) #folder name output click.echo(app.set.settings['downloadLocation']) #folder name output
if __name__ == '__main__': if __name__ == '__main__':
download() download()

View File

@ -477,37 +477,37 @@ class Deezer:
for track in data: for track in data:
item = { item = {
'id': track['SNG_ID'], 'id': track['SNG_ID'],
'title': track['SNG_TITLE'], 'title': track['SNG_TITLE'],
'link': 'https://www.deezer.com/track/'+str(track['SNG_ID']), 'link': 'https://www.deezer.com/track/'+str(track['SNG_ID']),
'duration': track['DURATION'], 'duration': track['DURATION'],
'rank': track['RANK_SNG'], 'rank': track['RANK_SNG'],
'explicit_lyrics': int(track['EXPLICIT_LYRICS']) > 0, 'explicit_lyrics': int(track['EXPLICIT_LYRICS']) > 0,
'explicit_content_lyrics': track['EXPLICIT_TRACK_CONTENT']['EXPLICIT_COVER_STATUS'], 'explicit_content_lyrics': track['EXPLICIT_TRACK_CONTENT']['EXPLICIT_COVER_STATUS'],
'explicit_content_cover': track['EXPLICIT_TRACK_CONTENT']['EXPLICIT_LYRICS_STATUS'], 'explicit_content_cover': track['EXPLICIT_TRACK_CONTENT']['EXPLICIT_LYRICS_STATUS'],
'time_add': track['DATE_ADD'], 'time_add': track['DATE_ADD'],
'album': { 'album': {
'id': track['ALB_ID'], 'id': track['ALB_ID'],
'title': track['ALB_TITLE'], 'title': track['ALB_TITLE'],
'cover': 'https://api.deezer.com/album/'+str(track['ALB_ID'])+'/image', 'cover': 'https://api.deezer.com/album/'+str(track['ALB_ID'])+'/image',
'cover_small': 'https://e-cdns-images.dzcdn.net/images/cover/'+str(track['ALB_PICTURE'])+'/56x56-000000-80-0-0.jpg', 'cover_small': 'https://e-cdns-images.dzcdn.net/images/cover/'+str(track['ALB_PICTURE'])+'/56x56-000000-80-0-0.jpg',
'cover_medium': 'https://e-cdns-images.dzcdn.net/images/cover/'+str(track['ALB_PICTURE'])+'/250x250-000000-80-0-0.jpg', 'cover_medium': 'https://e-cdns-images.dzcdn.net/images/cover/'+str(track['ALB_PICTURE'])+'/250x250-000000-80-0-0.jpg',
'cover_big': 'https://e-cdns-images.dzcdn.net/images/cover/'+str(track['ALB_PICTURE'])+'/500x500-000000-80-0-0.jpg', 'cover_big': 'https://e-cdns-images.dzcdn.net/images/cover/'+str(track['ALB_PICTURE'])+'/500x500-000000-80-0-0.jpg',
'cover_xl': 'https://e-cdns-images.dzcdn.net/images/cover/'+str(track['ALB_PICTURE'])+'/1000x1000-000000-80-0-0.jpg', 'cover_xl': 'https://e-cdns-images.dzcdn.net/images/cover/'+str(track['ALB_PICTURE'])+'/1000x1000-000000-80-0-0.jpg',
'tracklist': 'https://api.deezer.com/album/'+str(track['ALB_ID'])+'/tracks', 'tracklist': 'https://api.deezer.com/album/'+str(track['ALB_ID'])+'/tracks',
'type': 'album' 'type': 'album'
}, },
'artist': { 'artist': {
'id': track['ART_ID'], 'id': track['ART_ID'],
'name': track['ART_NAME'], 'name': track['ART_NAME'],
'picture': 'https://api.deezer.com/artist/'+str(track['ART_ID'])+'/image', 'picture': 'https://api.deezer.com/artist/'+str(track['ART_ID'])+'/image',
'picture_small': 'https://e-cdns-images.dzcdn.net/images/artist/'+str(track['ART_PICTURE'])+'/56x56-000000-80-0-0.jpg', 'picture_small': 'https://e-cdns-images.dzcdn.net/images/artist/'+str(track['ART_PICTURE'])+'/56x56-000000-80-0-0.jpg',
'picture_medium': 'https://e-cdns-images.dzcdn.net/images/artist/'+str(track['ART_PICTURE'])+'/250x250-000000-80-0-0.jpg', 'picture_medium': 'https://e-cdns-images.dzcdn.net/images/artist/'+str(track['ART_PICTURE'])+'/250x250-000000-80-0-0.jpg',
'picture_big': 'https://e-cdns-images.dzcdn.net/images/artist/'+str(track['ART_PICTURE'])+'/500x500-000000-80-0-0.jpg', 'picture_big': 'https://e-cdns-images.dzcdn.net/images/artist/'+str(track['ART_PICTURE'])+'/500x500-000000-80-0-0.jpg',
'picture_xl': 'https://e-cdns-images.dzcdn.net/images/artist/'+str(track['ART_PICTURE'])+'/1000x1000-000000-80-0-0.jpg', 'picture_xl': 'https://e-cdns-images.dzcdn.net/images/artist/'+str(track['ART_PICTURE'])+'/1000x1000-000000-80-0-0.jpg',
'tracklist': 'https://api.deezer.com/artist/'+str(track['ART_ID'])+'/top?limit=50', 'tracklist': 'https://api.deezer.com/artist/'+str(track['ART_ID'])+'/top?limit=50',
'type': 'artist' 'type': 'artist'
}, },
'type': 'track' 'type': 'track'
} }
result.append(item) result.append(item)
return result return result

View File

@ -2,11 +2,11 @@
from deemix.api.deezer import Deezer from deemix.api.deezer import Deezer
from deemix.app.settings import Settings from deemix.app.settings import Settings
from deemix.app.queuemanager import QueueManager from deemix.app.queuemanager import QueueManager
from deemix.app.spotify import SpotifyHelper from deemix.app.spotifyhelper import SpotifyHelper
class deemix: class deemix:
def __init__(self): def __init__(self, configFolder=None):
self.set = Settings() self.set = Settings(configFolder)
self.dz = Deezer() self.dz = Deezer()
self.sp = SpotifyHelper() self.sp = SpotifyHelper(configFolder)
self.qm = QueueManager() self.qm = QueueManager()

View File

@ -1,43 +1,47 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os.path as path import os.path as path
import string
import random
from os import mkdir from os import mkdir
from deemix.utils import localpaths from deemix.app import deemix
from deemix.api.deezer import Deezer
from deemix.app.queuemanager import addToQueue
from deemix.app.spotify import SpotifyHelper
dz = Deezer() def randomString(stringLength=8):
sp = SpotifyHelper() letters = string.ascii_lowercase
return ''.join(random.choice(letters) for i in range(stringLength))
class cli(deemix):
def __init__(self, local, configFolder=None):
super().__init__(configFolder)
if local:
self.set.settings['downloadLocation'] = randomString(12)
print("Using a local download folder: "+settings['downloadLocation'])
def requestValidArl(): def downloadLink(self, url, bitrate=None):
while True: for link in url:
arl = input("Paste here your arl:") if ';' in link:
if dz.login_via_arl(arl): for l in link.split(";"):
break self.qm.addToQueue(self.dz, self.sp, l, self.set.settings, bitrate)
return arl else:
self.qm.addToQueue(self.dz, self.sp, link, self.set.settings, bitrate)
def requestValidArl(self):
while True:
arl = input("Paste here your arl:")
if self.dz.login_via_arl(arl):
break
return arl
def login(): def login(self):
configFolder = localpaths.getConfigFolder() configFolder = self.set.configFolder
if not path.isdir(configFolder): if not path.isdir(configFolder):
mkdir(configFolder) mkdir(configFolder)
if path.isfile(path.join(configFolder, '.arl')): if path.isfile(path.join(configFolder, '.arl')):
with open(path.join(configFolder, '.arl'), 'r') as f: with open(path.join(configFolder, '.arl'), 'r') as f:
arl = f.readline().rstrip("\n") arl = f.readline().rstrip("\n")
if not dz.login_via_arl(arl): if not self.dz.login_via_arl(arl):
arl = requestValidArl() arl = self.requestValidArl()
else:
arl = requestValidArl()
with open(path.join(configFolder, '.arl'), 'w') as f:
f.write(arl)
def downloadLink(url, settings, bitrate=None):
for link in url:
if ';' in link:
for l in link.split(";"):
addToQueue(dz, sp, l, settings, bitrate)
else: else:
addToQueue(dz, sp, link, settings, bitrate) arl = self.requestValidArl()
with open(path.join(configFolder, '.arl'), 'w') as f:
f.write(arl)

View File

@ -11,7 +11,7 @@ from tempfile import gettempdir
from time import sleep from time import sleep
from deemix.app.queueitem import QIConvertable, QISingle, QICollection from deemix.app.queueitem import QIConvertable, QISingle, QICollection
from deemix.app.Track import Track from deemix.app.track import Track
from deemix.utils.misc import changeCase from deemix.utils.misc import changeCase
from deemix.utils.pathtemplates import generateFilename, generateFilepath, settingsRegexAlbum, settingsRegexArtist, settingsRegexPlaylistFile from deemix.utils.pathtemplates import generateFilename, generateFilepath, settingsRegexAlbum, settingsRegexArtist, settingsRegexPlaylistFile
from deemix.api.deezer import USER_AGENT_HEADER from deemix.api.deezer import USER_AGENT_HEADER
@ -46,8 +46,8 @@ errorMessages = {
'wrongBitrate': "Track not found at desired bitrate.", 'wrongBitrate': "Track not found at desired bitrate.",
'wrongBitrateNoAlternative': "Track not found at desired bitrate and no alternative found!", 'wrongBitrateNoAlternative': "Track not found at desired bitrate and no alternative found!",
'no360RA': "Track is not available in Reality Audio 360.", 'no360RA': "Track is not available in Reality Audio 360.",
'notAvailable': "Track not available on deezer's servers!" 'notAvailable': "Track not available on deezer's servers!",
'notAvailableNoAlternative': "Track not available on deezer's servers and no alternative found!", 'notAvailableNoAlternative': "Track not available on deezer's servers and no alternative found!"
} }
def after_download(tracks, settings, queueItem): def after_download(tracks, settings, queueItem):
@ -168,8 +168,10 @@ class DownloadJob:
def __init__(self, dz, sp, queueItem, interface=None): def __init__(self, dz, sp, queueItem, interface=None):
self.dz = dz self.dz = dz
self.sp = sp self.sp = sp
self.queueItem = queueItem
self.interface = interface self.interface = interface
if isinstance(queueItem, QIConvertable):
self.sp.convert_spotify_playlist(self.dz, queueItem, interface=self.interface)
self.queueItem = queueItem
self.settings = queueItem.settings self.settings = queueItem.settings
self.bitrate = queueItem.bitrate self.bitrate = queueItem.bitrate
self.downloadPercentage = 0 self.downloadPercentage = 0
@ -177,8 +179,6 @@ class DownloadJob:
self.extrasPath = self.settings['downloadLocation'] self.extrasPath = self.settings['downloadLocation']
def start(self): def start(self):
if isinstance(self.queueItem, QIConvertable):
self.sp.convert_spotify_playlist(self.dz, self.queueItem, self.settings, interface=self.interface)
if isinstance(self.queueItem, QISingle): if isinstance(self.queueItem, QISingle):
result = self.downloadWrapper(self.queueItem.single) result = self.downloadWrapper(self.queueItem.single)
if result: if result:
@ -215,15 +215,15 @@ class DownloadJob:
) )
if self.queueItem.cancel: raise DownloadCancelled if self.queueItem.cancel: raise DownloadCancelled
if self.MD5 == '': if track.MD5 == '':
if track.fallbackId != "0": if track.fallbackId != "0":
logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not yet encoded, using fallback id") logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not yet encoded, using fallback id")
newTrack = self.dz.get_track_gw(track.fallbackId) newTrack = self.dz.get_track_gw(track.fallbackId)
track.parseEssentialData(self.dz, newTrack) track.parseEssentialData(self.dz, newTrack)
return self.download(trackAPI_gw, track) return self.download(trackAPI_gw, track)
elif not track.searched and self.settings['fallbackSearch']: elif not track.searched and self.settings['fallbackSearch']:
logger.warn(f"[{self.mainArtist['name']} - {self.title}] Track not yet encoded, searching for alternative") logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not yet encoded, searching for alternative")
searchedId = self.dz.get_track_from_metadata(self.mainArtist['name'], self.title, self.album['title']) searchedId = self.dz.get_track_from_metadata(track.mainArtist['name'], track.title, track.album['title'])
if searchedId != 0: if searchedId != 0:
newTrack = self.dz.get_track_gw(searchedId) newTrack = self.dz.get_track_gw(searchedId)
track.parseEssentialData(self.dz, newTrack) track.parseEssentialData(self.dz, newTrack)
@ -242,8 +242,8 @@ class DownloadJob:
track.parseEssentialData(self.dz, newTrack) track.parseEssentialData(self.dz, newTrack)
return self.download(trackAPI_gw, track) return self.download(trackAPI_gw, track)
elif not track.searched and self.settings['fallbackSearch']: elif not track.searched and self.settings['fallbackSearch']:
logger.warn(f"[{self.mainArtist['name']} - {self.title}] Track not found at desired bitrate, searching for alternative") logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not found at desired bitrate, searching for alternative")
searchedId = self.dz.get_track_from_metadata(self.mainArtist['name'], self.title, self.album['title']) searchedId = self.dz.get_track_from_metadata(track.mainArtist['name'], track.title, track.album['title'])
if searchedId != 0: if searchedId != 0:
newTrack = self.dz.get_track_gw(searchedId) newTrack = self.dz.get_track_gw(searchedId)
track.parseEssentialData(self.dz, newTrack) track.parseEssentialData(self.dz, newTrack)
@ -261,8 +261,7 @@ class DownloadJob:
track.trackNumber = track.position track.trackNumber = track.position
track.discNumber = "1" track.discNumber = "1"
track.album = {**track.album, **track.playlist} track.album = {**track.album, **track.playlist}
track.album['picPath'] = os.path.join(TEMPDIR, track.album['picPath'] = os.path.join(TEMPDIR, f"pl{trackAPI_gw['_EXTRA_PLAYLIST']['id']}_{self.settings['embeddedArtworkSize']}.jpg")
f"pl{trackAPI_gw['_EXTRA_PLAYLIST']['id']}_{settings['embeddedArtworkSize']}.jpg")
else: else:
if track.album['date']: if track.album['date']:
track.date = track.album['date'] track.date = track.album['date']
@ -271,10 +270,11 @@ class DownloadJob:
self.settings['embeddedArtworkSize'], self.settings['embeddedArtworkSize'], self.settings['embeddedArtworkSize'], self.settings['embeddedArtworkSize'],
f'000000-{self.settings["jpegImageQuality"]}-0-0.jpg' f'000000-{self.settings["jpegImageQuality"]}-0-0.jpg'
) )
track.album['picPath'] = os.path.join(TEMPDIR, f"alb{track.album['id']}_{self.settings['embeddedArtworkSize']}.jpg")
track.album['bitrate'] = selectedFormat track.album['bitrate'] = selectedFormat
track.dateString = formatDate(track.date, settings['dateFormat']) track.dateString = formatDate(track.date, self.settings['dateFormat'])
track.album['dateString'] = formatDate(track.album['date'], settings['dateFormat']) track.album['dateString'] = formatDate(track.album['date'], self.settings['dateFormat'])
# Check if user wants the feat in the title # Check if user wants the feat in the title
# 0 => do not change # 0 => do not change
@ -320,11 +320,6 @@ class DownloadJob:
if self.queueItem.cancel: raise DownloadCancelled if self.queueItem.cancel: raise DownloadCancelled
# Download and cache coverart # Download and cache coverart
if self.settings['tags']['savePlaylistAsCompilation'] and track.playlist:
else:
track.album['picPath'] = os.path.join(TEMPDIR,
f"alb{track.album['id']}_{settings['embeddedArtworkSize']}.jpg")
logger.info(f"[{track.mainArtist['name']} - {track.title}] Getting the album cover") logger.info(f"[{track.mainArtist['name']} - {track.title}] Getting the album cover")
track.album['picPath'] = downloadImage(track.album['picUrl'], track.album['picPath']) track.album['picPath'] = downloadImage(track.album['picUrl'], track.album['picPath'])
@ -418,7 +413,7 @@ class DownloadJob:
if not trackAlreadyDownloaded or self.settings['overwriteFile'] == 'y': if not trackAlreadyDownloaded or self.settings['overwriteFile'] == 'y':
logger.info(f"[{track.mainArtist['name']} - {track.title}] Downloading the track") logger.info(f"[{track.mainArtist['name']} - {track.title}] Downloading the track")
track.downloadUrl = dz.get_track_stream_url(track.id, track.MD5, track.mediaVersion, track.selectedFormat) track.downloadUrl = self.dz.get_track_stream_url(track.id, track.MD5, track.mediaVersion, track.selectedFormat)
def downloadMusic(track, trackAPI_gw): def downloadMusic(track, trackAPI_gw):
try: try:
@ -435,8 +430,8 @@ class DownloadJob:
track.parseEssentialData(self.dz, newTrack) track.parseEssentialData(self.dz, newTrack)
return False return False
elif not track.searched and self.settings['fallbackSearch']: elif not track.searched and self.settings['fallbackSearch']:
logger.warn(f"[{self.mainArtist['name']} - {self.title}] Track not available, searching for alternative") logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not available, searching for alternative")
searchedId = self.dz.get_track_from_metadata(self.mainArtist['name'], self.title, self.album['title']) searchedId = self.dz.get_track_from_metadata(track.mainArtist['name'], track.title, track.album['title'])
if searchedId != 0: if searchedId != 0:
newTrack = self.dz.get_track_gw(searchedId) newTrack = self.dz.get_track_gw(searchedId)
track.parseEssentialData(self.dz, newTrack) track.parseEssentialData(self.dz, newTrack)
@ -460,7 +455,7 @@ class DownloadJob:
try: try:
trackDownloaded = downloadMusic(track, trackAPI_gw) trackDownloaded = downloadMusic(track, trackAPI_gw)
except DownloadFailed as e: except DownloadFailed as e:
raise DownloadFailed raise e
except Exception as e: except Exception as e:
raise e raise e
@ -468,7 +463,7 @@ class DownloadJob:
return self.download(trackAPI_gw, track) return self.download(trackAPI_gw, track)
else: else:
logger.info(f"[{track.mainArtist['name']} - {track.title}] Skipping track as it's already downloaded") logger.info(f"[{track.mainArtist['name']} - {track.title}] Skipping track as it's already downloaded")
trackCompletePercentage(trackAPI, queueItem, interface) self.completeTrackPercentage()
# Adding tags # Adding tags
if (not trackAlreadyDownloaded or self.settings['overwriteFile'] in ['t', 'y']) and not track.localTrack: if (not trackAlreadyDownloaded or self.settings['overwriteFile'] in ['t', 'y']) and not track.localTrack:
@ -482,10 +477,10 @@ class DownloadJob:
remove(writepath) remove(writepath)
logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not available in FLAC, falling back if necessary") logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not available in FLAC, falling back if necessary")
self.removeTrackPercentage(trackAPI, queueItem, interface) self.removeTrackPercentage(trackAPI, queueItem, interface)
track.formats['FILESIZE_FLAC'] = "0" track.filesizes['FILESIZE_FLAC'] = "0"
return self.download(trackAPI_gw, track) return self.download(trackAPI_gw, track)
if track.searched: if track.searched:
result['searched'] = f'{track.mainArtist['name']} - {track.title}' result['searched'] = f"{track.mainArtist['name']} - {track.title}"
logger.info(f"[{track.mainArtist['name']} - {track.title}] Track download completed") logger.info(f"[{track.mainArtist['name']} - {track.title}] Track download completed")
self.queueItem.downloaded += 1 self.queueItem.downloaded += 1
@ -533,16 +528,16 @@ class DownloadJob:
return error_num # fallback is enabled and loop went through all formats return error_num # fallback is enabled and loop went through all formats
def stream_track(self, stream, track, trackAPI): def streamTrack(self, stream, track, trackAPI):
if self.queueItem.cancel: raise DownloadCancelled if self.queueItem.cancel: raise DownloadCancelled
try: try:
request = get(track.downloadUrl, headers=dz.http_headers, stream=True, timeout=30) request = get(track.downloadUrl, headers=self.dz.http_headers, stream=True, timeout=30)
except ConnectionError: except ConnectionError:
sleep(2) sleep(2)
return stream_track(dz, track, stream, trackAPI, queueItem, interface) return self.streamTrack(stream, track, trackAPI)
request.raise_for_status() request.raise_for_status()
blowfish_key = str.encode(dz._get_blowfish_key(str(track.id))) blowfish_key = str.encode(self.dz._get_blowfish_key(str(track.id)))
complete = int(request.headers["Content-Length"]) complete = int(request.headers["Content-Length"])
chunkLength = 0 chunkLength = 0
percentage = 0 percentage = 0
@ -585,9 +580,9 @@ class DownloadJob:
def downloadWrapper(self, trackAPI_gw): def downloadWrapper(self, trackAPI_gw):
track = { track = {
'id': queueItem.single['SNG_ID'], 'id': trackAPI_gw['SNG_ID'],
'title': queueItem.single['SNG_TITLE'] + (queueItem.single['VERSION'] if 'VERSION' in queueItem.single and queueItem.single['VERSION'] and not queueItem.single['VERSION'] in queueItem.single['SNG_TITLE'] else ""), 'title': trackAPI_gw['SNG_TITLE'] + (trackAPI_gw['VERSION'] if 'VERSION' in trackAPI_gw and trackAPI_gw['VERSION'] and not trackAPI_gw['VERSION'] in trackAPI_gw['SNG_TITLE'] else ""),
'mainArtist': {'name': queueItem.single['ART_NAME']} 'artist': trackAPI_gw['ART_NAME']
} }
try: try:
@ -595,14 +590,14 @@ class DownloadJob:
except DownloadCancelled: except DownloadCancelled:
return None return None
except DownloadFailed as error: except DownloadFailed as error:
logger.error(f"[{track['mainArtist']['name']} - {track['title']}] {error.message}") logger.error(f"[{track['artist']} - {track['title']}] {error.message}")
result = {'error': { result = {'error': {
'message': error.message, 'message': error.message,
'errid': error.errid, 'errid': error.errid,
'data': track 'data': track
}} }}
except Exception as e: except Exception as e:
logger.exception(str(e)) logger.exception(f"[{track['artist']} - {track['title']}] {str(e)}")
result = {'error': { result = {'error': {
'message': str(e), 'message': str(e),
'data': track 'data': track
@ -611,10 +606,10 @@ class DownloadJob:
if 'error' in result: if 'error' in result:
self.completeTrackPercentage() self.completeTrackPercentage()
self.queueItem.failed += 1 self.queueItem.failed += 1
self.queueItem.errors.append(error.message) self.queueItem.errors.append(result['error']['message'])
if interface: if self.interface:
error = result['error'] error = result['error']
interface.send("updateQueue", { self.interface.send("updateQueue", {
'uuid': self.queueItem.uuid, 'uuid': self.queueItem.uuid,
'failed': True, 'failed': True,
'data': error['data'], 'data': error['data'],

View File

@ -4,43 +4,43 @@ class QueueItem:
def __init__(self, id=None, bitrate=None, title=None, artist=None, cover=None, size=None, type=None, settings=None, queueItemList=None): def __init__(self, id=None, bitrate=None, title=None, artist=None, cover=None, size=None, type=None, settings=None, queueItemList=None):
if queueItemList: if queueItemList:
self.title = queueItemList['title'] self.title = queueItemList['title']
self.artist = queueItemList['artist'] self.artist = queueItemList['artist']
self.cover = queueItemList['cover'] self.cover = queueItemList['cover']
self.size = queueItemList['size'] self.size = queueItemList['size']
self.type = queueItemList['type'] self.type = queueItemList['type']
self.id = queueItemList['id'] self.id = queueItemList['id']
self.bitrate = queueItemList['bitrate'] self.bitrate = queueItemList['bitrate']
self.settings = queueItemList['settings'] self.settings = queueItemList['settings']
else: else:
self.title = title self.title = title
self.artist = artist self.artist = artist
self.cover = cover self.cover = cover
self.size = size self.size = size
self.type = type self.type = type
self.id = id self.id = id
self.bitrate = bitrate self.bitrate = bitrate
self.settings = settings self.settings = settings
self.downloaded = 0 self.downloaded = 0
self.failed = 0 self.failed = 0
self.errors = [] self.errors = []
self.progress = 0 self.progress = 0
self.uuid = f"{self.type}_{self.id}_{self.bitrate}" self.uuid = f"{self.type}_{self.id}_{self.bitrate}"
self.cancel = False self.cancel = False
def toDict(self): def toDict(self):
return { return {
'title': self.title, 'title': self.title,
'artist': self.artist, 'artist': self.artist,
'cover': self.cover, 'cover': self.cover,
'size': self.size, 'size': self.size,
'downloaded': self.downloaded, 'downloaded': self.downloaded,
'failed': self.failed, 'failed': self.failed,
'errors': self.errors, 'errors': self.errors,
'progress': self.progress, 'progress': self.progress,
'type': self.type, 'type': self.type,
'id': self.id, 'id': self.id,
'bitrate': self.bitrate, 'bitrate': self.bitrate,
'uuid': self.uuid 'uuid': self.uuid
} }
def getResettedItem(self): def getResettedItem(self):
@ -87,13 +87,13 @@ class QICollection(QueueItem):
queueItem['collection'] = self.collection queueItem['collection'] = self.collection
return queueItem return queueItem
class QIConvertable(QueueItem): class QIConvertable(QICollection):
def __init__(self, id=None, bitrate=None, title=None, artist=None, cover=None, size=None, type=None, settings=None, extra=None, queueItemList=None): def __init__(self, id=None, bitrate=None, title=None, artist=None, cover=None, size=None, type=None, settings=None, extra=None, queueItemList=None):
if queueItemList: if queueItemList:
super().__init__(queueItemList=queueItemList) super().__init__(queueItemList=queueItemList)
self.extra = queueItemList['_EXTRA'] self.extra = queueItemList['_EXTRA']
else: else:
super().__init__(id, bitrate, title, artist, cover, size, type, settings) super().__init__(id, bitrate, title, artist, cover, size, type, settings, [])
self.extra = extra self.extra = extra
def toDict(self): def toDict(self):

View File

@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from deemix.app.downloader import download from deemix.app.downloadjob import DownloadJob
from deemix.utils.misc import getIDFromLink, getTypeFromLink, getBitrateInt from deemix.utils.misc import getIDFromLink, getTypeFromLink, getBitrateInt
from deemix.api.deezer import APIError from deemix.api.deezer import APIError
from spotipy.exceptions import SpotifyException from spotipy.exceptions import SpotifyException
@ -35,7 +35,7 @@ class QueueManager:
if 'id' in trackAPI and 'title' in trackAPI: if 'id' in trackAPI and 'title' in trackAPI:
id = trackAPI['id'] id = trackAPI['id']
else: else:
return QueueError(url, "Track ISRC is not available on deezer", "ISRCnotOnDeezer")
except APIError as e: except APIError as e:
e = json.loads(str(e)) e = json.loads(str(e))
return QueueError(url, f"Wrong URL: {e['type']+': ' if 'type' in e else ''}{e['message'] if 'message' in e else ''}") return QueueError(url, f"Wrong URL: {e['type']+': ' if 'type' in e else ''}{e['message'] if 'message' in e else ''}")
@ -303,7 +303,7 @@ class QueueManager:
try: try:
playlist = sp.generate_playlist_queueitem(dz, id, settings) playlist = sp.generate_playlist_queueitem(dz, id, settings)
playlist['bitrate'] = bitrate playlist.bitrate = bitrate
return playlist return playlist
except SpotifyException as e: except SpotifyException as e:
return QueueError(url, "Wrong URL: "+e.msg[e.msg.find('\n')+2:]) return QueueError(url, "Wrong URL: "+e.msg[e.msg.find('\n')+2:])
@ -317,12 +317,14 @@ class QueueManager:
if interface: if interface:
interface.send("loginNeededToDownload") interface.send("loginNeededToDownload")
return False return False
def parseLink(link): def parseLink(link):
link = link.strip() link = link.strip()
if link == "": if link == "":
return False return False
logger.info("Generating queue item for: "+link) logger.info("Generating queue item for: "+link)
return self.generateQueueItem(dz, sp, link, settings, bitrate, interface=interface) return self.generateQueueItem(dz, sp, link, settings, bitrate, interface=interface)
if type(url) is list: if type(url) is list:
queueItem = [] queueItem = []
for link in url: for link in url:
@ -339,6 +341,7 @@ class QueueManager:
queueItem = parseLink(url) queueItem = parseLink(url)
if not queueItem: if not queueItem:
return False return False
if type(queueItem) is list: if type(queueItem) is list:
ogLen = len(self.queue) ogLen = len(self.queue)
for x in queueItem: for x in queueItem:
@ -369,6 +372,7 @@ class QueueManager:
logger.info(f"[{queueItem.uuid}] Added to queue.") logger.info(f"[{queueItem.uuid}] Added to queue.")
self.queue.append(queueItem.uuid) self.queue.append(queueItem.uuid)
self.queueList[queueItem.uuid] = queueItem self.queueList[queueItem.uuid] = queueItem
self.nextItem(dz, sp, interface) self.nextItem(dz, sp, interface)
return True return True
@ -383,7 +387,7 @@ class QueueManager:
if interface: if interface:
interface.send("startDownload", self.currentItem) interface.send("startDownload", self.currentItem)
logger.info(f"[{self.currentItem}] Started downloading.") logger.info(f"[{self.currentItem}] Started downloading.")
download(dz, sp, self.queueList[self.currentItem], interface) DownloadJob(dz, sp, self.queueList[self.currentItem]).start()
self.afterDownload(dz, sp, interface) self.afterDownload(dz, sp, interface)
def afterDownload(self, dz, sp, interface): def afterDownload(self, dz, sp, interface):

View File

@ -213,7 +213,7 @@ class SpotifyHelper:
if not 'explicit' in playlistAPI: if not 'explicit' in playlistAPI:
playlistAPI['explicit'] = False playlistAPI['explicit'] = False
extra['playlistAPI'] = playlistAPI extra['playlistAPI'] = playlistAPI
return QICollection( return QIConvertable(
playlist_id, playlist_id,
0, 0,
spotify_playlist['name'], spotify_playlist['name'],
@ -225,7 +225,7 @@ class SpotifyHelper:
extra, extra,
) )
def convert_spotify_playlist(self, dz, item, settings, interface=None): def convert_spotify_playlist(self, dz, queueItem, interface=None):
convertPercentage = 0 convertPercentage = 0
lastPercentage = 0 lastPercentage = 0
if path.isfile(path.join(self.configFolder, 'spotifyCache.json')): if path.isfile(path.join(self.configFolder, 'spotifyCache.json')):
@ -234,13 +234,13 @@ class SpotifyHelper:
else: else:
cache = {'tracks': {}, 'albums': {}} cache = {'tracks': {}, 'albums': {}}
if interface: if interface:
interface.send("startConversion", item.uuid) interface.send("startConversion", queueItem.uuid)
collection = [] collection = []
for pos, track in enumerate(item.extra['unconverted'], start=1): for pos, track in enumerate(queueItem.extra['unconverted'], start=1):
if str(track['id']) in cache['tracks']: if str(track['id']) in cache['tracks']:
trackID = cache['tracks'][str(track['id'])] trackID = cache['tracks'][str(track['id'])]
else: else:
trackID = self.get_trackid_spotify(dz, 0, settings['fallbackSearch'], track) trackID = self.get_trackid_spotify(dz, 0, queueItem.settings['fallbackSearch'], track)
cache['tracks'][str(track['id'])] = trackID cache['tracks'][str(track['id'])] = trackID
if trackID == 0: if trackID == 0:
deezerTrack = { deezerTrack = {
@ -257,35 +257,25 @@ class SpotifyHelper:
} }
else: else:
deezerTrack = dz.get_track_gw(trackID) deezerTrack = dz.get_track_gw(trackID)
deezerTrack['_EXTRA_PLAYLIST'] = item.extra['playlistAPI'] deezerTrack['_EXTRA_PLAYLIST'] = queueItem.extra['playlistAPI']
deezerTrack['POSITION'] = pos deezerTrack['POSITION'] = pos
deezerTrack['SIZE'] = item.size deezerTrack['SIZE'] = queueItem.size
deezerTrack['FILENAME_TEMPLATE'] = settings['playlistTracknameTemplate'] deezerTrack['FILENAME_TEMPLATE'] = queueItem.settings['playlistTracknameTemplate']
collection.append(deezerTrack) collection.append(deezerTrack)
convertPercentage = (pos / item.size) * 100 convertPercentage = (pos / queueItem.size) * 100
print(convertPercentage)
if round(convertPercentage) != lastPercentage and round(convertPercentage) % 2 == 0: if round(convertPercentage) != lastPercentage and round(convertPercentage) % 2 == 0:
lastPercentage = round(convertPercentage) lastPercentage = round(convertPercentage)
if interface: if interface:
interface.send("updateQueue", {'uuid': item.uuid, 'conversion': lastPercentage}) interface.send("updateQueue", {'uuid': queueItem.uuid, 'conversion': lastPercentage})
queueItem.extra = None
queueItem.collection = collection
item = QICollection(
item.id,
item.bitrate,
item.title,
item.artist,
item.cover,
item.size,
item.type,
item.settings,
collection,
)
with open(path.join(self.configFolder, 'spotifyCache.json'), 'w') as spotifyCache: with open(path.join(self.configFolder, 'spotifyCache.json'), 'w') as spotifyCache:
json.dump(cache, spotifyCache) json.dump(cache, spotifyCache)
if interface: if interface:
interface.send("startDownload", item['uuid']) interface.send("startDownload", queueItem.uuid)
def get_user_playlists(self, user): def get_user_playlists(self, user):
if not self.spotifyEnabled: if not self.spotifyEnabled:

View File

@ -13,7 +13,7 @@ class Track:
self.title = trackAPI_gw['SNG_TITLE'].strip() self.title = trackAPI_gw['SNG_TITLE'].strip()
if 'VERSION' in trackAPI_gw and trackAPI_gw['VERSION'] and not trackAPI_gw['VERSION'] in trackAPI_gw['SNG_TITLE']: if 'VERSION' in trackAPI_gw and trackAPI_gw['VERSION'] and not trackAPI_gw['VERSION'] in trackAPI_gw['SNG_TITLE']:
track.title += " " + trackAPI_gw['VERSION'].strip() self.title += " " + trackAPI_gw['VERSION'].strip()
self.position = None self.position = None
if 'POSITION' in trackAPI_gw: if 'POSITION' in trackAPI_gw:
@ -87,7 +87,7 @@ class Track:
self.album['bitrate'] = 0 self.album['bitrate'] = 0
self.album['dateString'] = None self.album['dateString'] = None
self.artistsString self.artistsString = ""
def parseEssentialData(self, dz, trackAPI_gw): def parseEssentialData(self, dz, trackAPI_gw):
self.id = trackAPI_gw['SNG_ID'] self.id = trackAPI_gw['SNG_ID']
@ -97,7 +97,7 @@ class Track:
self.fallbackId = "0" self.fallbackId = "0"
if 'FALLBACK' in trackAPI_gw: if 'FALLBACK' in trackAPI_gw:
self.fallbackId = trackAPI_gw['FALLBACK']['SNG_ID'] self.fallbackId = trackAPI_gw['FALLBACK']['SNG_ID']
self.formats = dz.get_track_filesizes(track["id"]) self.filesizes = dz.get_track_filesizes(self.id)
def parseLocalTrackData(self, trackAPI_gw): def parseLocalTrackData(self, trackAPI_gw):
self.album = { self.album = {
@ -160,7 +160,7 @@ class Track:
self.trackNumber = trackAPI_gw['TRACK_NUMBER'] self.trackNumber = trackAPI_gw['TRACK_NUMBER']
self.contributors = trackAPI_gw['SNG_CONTRIBUTORS'] self.contributors = trackAPI_gw['SNG_CONTRIBUTORS']
track.lyrics = { self.lyrics = {
'id': None, 'id': None,
'unsync': None, 'unsync': None,
'sync': None 'sync': None
@ -184,13 +184,13 @@ class Track:
self.lyrics['sync'] += lastTimestamp self.lyrics['sync'] += lastTimestamp
self.lyrics['sync'] += trackAPI_gw["LYRICS"]["LYRICS_SYNC_JSON"][i]["line"] + "\r\n" self.lyrics['sync'] += trackAPI_gw["LYRICS"]["LYRICS_SYNC_JSON"][i]["line"] + "\r\n"
track.mainArtist = { self.mainArtist = {
'id': trackAPI_gw['ART_ID'], 'id': trackAPI_gw['ART_ID'],
'name': trackAPI_gw['ART_NAME'], 'name': trackAPI_gw['ART_NAME'],
'pic': None 'pic': None
} }
if 'ART_PICTURE' in trackAPI_gw: if 'ART_PICTURE' in trackAPI_gw:
track.mainArtist['pic'] = trackAPI_gw['ART_PICTURE'] self.mainArtist['pic'] = trackAPI_gw['ART_PICTURE']
self.date = None self.date = None
if 'PHYSICAL_RELEASE_DATE' in trackAPI_gw: if 'PHYSICAL_RELEASE_DATE' in trackAPI_gw:
@ -261,7 +261,7 @@ class Track:
if 'copyright' in albumAPI: if 'copyright' in albumAPI:
self.copyright = albumAPI['copyright'] self.copyright = albumAPI['copyright']
if not track.album['pic']: if not self.album['pic']:
self.album['pic'] = albumAPI['cover_small'][albumAPI['cover_small'].find('cover/') + 6:-24] self.album['pic'] = albumAPI['cover_small'][albumAPI['cover_small'].find('cover/') + 6:-24]
if 'genres' in albumAPI and 'data' in albumAPI['genres'] and len(albumAPI['genres']['data']) > 0: if 'genres' in albumAPI and 'data' in albumAPI['genres'] and len(albumAPI['genres']['data']) > 0:

View File

@ -50,7 +50,6 @@ def andCommaConcat(lst):
result = "" result = ""
for i, art in enumerate(lst): for i, art in enumerate(lst):
result += art result += art
track['commaArtistsString'] += art
if tot != i + 1: if tot != i + 1:
if tot - 1 == i + 1: if tot - 1 == i + 1:
result += " & " result += " & "

View File

@ -130,10 +130,10 @@ def generateFilepath(track, trackAPI, settings):
def settingsRegex(filename, track, settings, playlist=None): def settingsRegex(filename, track, settings, playlist=None):
filename = filename.replace("%title%", fixName(track.title, settings['illegalCharacterReplacer'])) filename = filename.replace("%title%", fixName(track.title, settings['illegalCharacterReplacer']))
filename = filename.replace("%artist%", fixName(track.mainArtist['name'], settings['illegalCharacterReplacer'])) filename = filename.replace("%artist%", fixName(track.mainArtist['name'], settings['illegalCharacterReplacer']))
filename = filename.replace("%artists%", fixName(track.commaArtistsString, settings['illegalCharacterReplacer'])) filename = filename.replace("%artists%", fixName(", ".join(track.artists), settings['illegalCharacterReplacer']))
filename = filename.replace("%allartists%", fixName(track.artistsString, settings['illegalCharacterReplacer'])) filename = filename.replace("%allartists%", fixName(track.artistsString, settings['illegalCharacterReplacer']))
filename = filename.replace("%mainartists%", fixName(track.mainArtistsString, settings['illegalCharacterReplacer'])) filename = filename.replace("%mainartists%", fixName(track.mainArtistsString, settings['illegalCharacterReplacer']))
filename = filename.replace("%featartists%", fixName('('+track.featArtistsString+')', settings['illegalCharacterReplacer']) if 'featArtistsString' in track else "") filename = filename.replace("%featartists%", fixName('('+track.featArtistsString+')', settings['illegalCharacterReplacer']) if track.featArtistsString else "")
filename = filename.replace("%album%", fixName(track.album['title'], settings['illegalCharacterReplacer'])) filename = filename.replace("%album%", fixName(track.album['title'], settings['illegalCharacterReplacer']))
filename = filename.replace("%albumartist%", filename = filename.replace("%albumartist%",
fixName(track.album['mainArtist']['name'], settings['illegalCharacterReplacer'])) fixName(track.album['mainArtist']['name'], settings['illegalCharacterReplacer']))

View File

@ -75,7 +75,7 @@ def tagID3(stream, track, save):
if save['copyright']: if save['copyright']:
tag.add(TCOP(text=track.copyright)) tag.add(TCOP(text=track.copyright))
if save['savePlaylistAsCompilation'] and "playlist" in track: if save['savePlaylistAsCompilation'] and track.playlist:
tag.add(TCMP(text="1")) tag.add(TCMP(text="1"))
if save['cover'] and track.album['picPath']: if save['cover'] and track.album['picPath']:
@ -155,7 +155,7 @@ def tagFLAC(stream, track, save):
if save['copyright']: if save['copyright']:
tag["COPYRIGHT"] = track.copyright tag["COPYRIGHT"] = track.copyright
if save['savePlaylistAsCompilation'] and "playlist" in track: if save['savePlaylistAsCompilation'] and track.playlist:
tag["COMPILATION"] = "1" tag["COMPILATION"] = "1"
if save['cover'] and track.album['picPath']: if save['cover'] and track.album['picPath']: