Cleaned up downloadjob.py
This commit is contained in:
parent
66be97bec4
commit
431a558467
|
@ -1,5 +1,8 @@
|
||||||
import eventlet
|
import eventlet
|
||||||
from eventlet.green.subprocess import call as execute
|
from eventlet.green.subprocess import call as execute
|
||||||
|
requests = eventlet.import_patched('requests')
|
||||||
|
get = requests.get
|
||||||
|
request_exception = requests.exceptions
|
||||||
|
|
||||||
from os.path import sep as pathSep
|
from os.path import sep as pathSep
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -7,10 +10,6 @@ from shlex import quote
|
||||||
import re
|
import re
|
||||||
import errno
|
import errno
|
||||||
|
|
||||||
requests = eventlet.import_patched('requests')
|
|
||||||
get = requests.get
|
|
||||||
request_exception = requests.exceptions
|
|
||||||
|
|
||||||
from ssl import SSLError
|
from ssl import SSLError
|
||||||
from os import makedirs
|
from os import makedirs
|
||||||
from tempfile import gettempdir
|
from tempfile import gettempdir
|
||||||
|
@ -30,8 +29,7 @@ logging.basicConfig(level=logging.INFO)
|
||||||
logger = logging.getLogger('deemix')
|
logger = logging.getLogger('deemix')
|
||||||
|
|
||||||
TEMPDIR = Path(gettempdir()) / 'deemix-imgs'
|
TEMPDIR = Path(gettempdir()) / 'deemix-imgs'
|
||||||
if not TEMPDIR.is_dir():
|
if not TEMPDIR.is_dir(): makedirs(TEMPDIR)
|
||||||
makedirs(TEMPDIR)
|
|
||||||
|
|
||||||
extensions = {
|
extensions = {
|
||||||
9: '.flac',
|
9: '.flac',
|
||||||
|
@ -117,11 +115,13 @@ class DownloadJob:
|
||||||
self.playlistURLs = []
|
self.playlistURLs = []
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
if not self.queueItem.cancel:
|
if self.queueItem.cancel:
|
||||||
|
self.interface.send('currentItemCancelled', self.queueItem.uuid)
|
||||||
|
self.interface.send("removedFromQueue", self.queueItem.uuid)
|
||||||
|
return None
|
||||||
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: self.singleAfterDownload(result)
|
||||||
self.singleAfterDownload(result)
|
|
||||||
elif isinstance(self.queueItem, QICollection):
|
elif isinstance(self.queueItem, QICollection):
|
||||||
tracks = [None] * len(self.queueItem.collection)
|
tracks = [None] * len(self.queueItem.collection)
|
||||||
pool = eventlet.GreenPool(size=self.settings['queueConcurrency'])
|
pool = eventlet.GreenPool(size=self.settings['queueConcurrency'])
|
||||||
|
@ -129,32 +129,28 @@ class DownloadJob:
|
||||||
tracks[pos] = pool.spawn(self.downloadWrapper, track)
|
tracks[pos] = pool.spawn(self.downloadWrapper, track)
|
||||||
pool.waitall()
|
pool.waitall()
|
||||||
self.collectionAfterDownload(tracks)
|
self.collectionAfterDownload(tracks)
|
||||||
if self.interface:
|
if self.interface: self.interface.send("finishDownload", self.queueItem.uuid)
|
||||||
if self.queueItem.cancel:
|
|
||||||
self.interface.send('currentItemCancelled', self.queueItem.uuid)
|
|
||||||
self.interface.send("removedFromQueue", self.queueItem.uuid)
|
|
||||||
else:
|
|
||||||
self.interface.send("finishDownload", self.queueItem.uuid)
|
|
||||||
return self.extrasPath
|
return self.extrasPath
|
||||||
|
|
||||||
def singleAfterDownload(self, result):
|
def singleAfterDownload(self, result):
|
||||||
if not self.extrasPath:
|
if not self.extrasPath: self.extrasPath = Path(self.settings['downloadLocation'])
|
||||||
self.extrasPath = Path(self.settings['downloadLocation'])
|
|
||||||
# Save Album Cover
|
# Save Album Cover
|
||||||
if self.settings['saveArtwork'] and 'albumPath' in result:
|
if self.settings['saveArtwork'] and 'albumPath' in result:
|
||||||
for image in result['albumURLs']:
|
for image in result['albumURLs']:
|
||||||
downloadImage(image['url'], result['albumPath'] / f"{result['albumFilename']}.{image['ext']}", self.settings['overwriteFile'])
|
downloadImage(image['url'], result['albumPath'] / f"{result['albumFilename']}.{image['ext']}", self.settings['overwriteFile'])
|
||||||
|
|
||||||
# Save Artist Artwork
|
# Save Artist Artwork
|
||||||
if self.settings['saveArtworkArtist'] and 'artistPath' in result:
|
if self.settings['saveArtworkArtist'] and 'artistPath' in result:
|
||||||
for image in result['artistURLs']:
|
for image in result['artistURLs']:
|
||||||
downloadImage(image['url'], result['artistPath'] / f"{result['artistFilename']}.{image['ext']}", self.settings['overwriteFile'])
|
downloadImage(image['url'], result['artistPath'] / f"{result['artistFilename']}.{image['ext']}", self.settings['overwriteFile'])
|
||||||
|
|
||||||
# Create searched logfile
|
# Create searched logfile
|
||||||
if self.settings['logSearched'] and 'searched' in result:
|
if self.settings['logSearched'] and 'searched' in result:
|
||||||
with open(self.extrasPath / 'searched.txt', 'wb+') as f:
|
with open(self.extrasPath / 'searched.txt', 'wb+') as f:
|
||||||
orig = f.read().decode('utf-8')
|
orig = f.read().decode('utf-8')
|
||||||
if not result['searched'] in orig:
|
if not result['searched'] in orig:
|
||||||
if orig != "":
|
if orig != "": orig += "\r\n"
|
||||||
orig += "\r\n"
|
|
||||||
orig += result['searched'] + "\r\n"
|
orig += result['searched'] + "\r\n"
|
||||||
f.write(orig.encode('utf-8'))
|
f.write(orig.encode('utf-8'))
|
||||||
# Execute command after download
|
# Execute command after download
|
||||||
|
@ -162,56 +158,58 @@ class DownloadJob:
|
||||||
execute(self.settings['executeCommand'].replace("%folder%", quote(str(self.extrasPath))).replace("%filename%", quote(result['filename'])), shell=True)
|
execute(self.settings['executeCommand'].replace("%folder%", quote(str(self.extrasPath))).replace("%filename%", quote(result['filename'])), shell=True)
|
||||||
|
|
||||||
def collectionAfterDownload(self, tracks):
|
def collectionAfterDownload(self, tracks):
|
||||||
if not self.extrasPath:
|
if not self.extrasPath: self.extrasPath = Path(self.settings['downloadLocation'])
|
||||||
self.extrasPath = Path(self.settings['downloadLocation'])
|
|
||||||
playlist = [None] * len(tracks)
|
playlist = [None] * len(tracks)
|
||||||
errors = ""
|
errors = ""
|
||||||
searched = ""
|
searched = ""
|
||||||
|
|
||||||
for index in range(len(tracks)):
|
for i in range(len(tracks)):
|
||||||
result = tracks[index].wait()
|
result = tracks[i].wait()
|
||||||
# Check if queue is cancelled
|
if not result: return None # Check if item is cancelled
|
||||||
if not result:
|
|
||||||
return None
|
|
||||||
# Log errors to file
|
# Log errors to file
|
||||||
if 'error' in result:
|
if result.get('error'):
|
||||||
if not 'data' in result['error']:
|
if not result['error'].get('data'): result['error']['data'] = {'id': "0", 'title': 'Unknown', 'artist': 'Unknown'}
|
||||||
result['error']['data'] = {'id': "0", 'title': 'Unknown', 'artist': 'Unknown'}
|
|
||||||
errors += f"{result['error']['data']['id']} | {result['error']['data']['artist']} - {result['error']['data']['title']} | {result['error']['message']}\r\n"
|
errors += f"{result['error']['data']['id']} | {result['error']['data']['artist']} - {result['error']['data']['title']} | {result['error']['message']}\r\n"
|
||||||
|
|
||||||
# Log searched to file
|
# Log searched to file
|
||||||
if 'searched' in result:
|
if 'searched' in result: searched += result['searched'] + "\r\n"
|
||||||
searched += result['searched'] + "\r\n"
|
|
||||||
# Save Album Cover
|
# Save Album Cover
|
||||||
if self.settings['saveArtwork'] and 'albumPath' in result:
|
if self.settings['saveArtwork'] and 'albumPath' in result:
|
||||||
for image in result['albumURLs']:
|
for image in result['albumURLs']:
|
||||||
downloadImage(image['url'], result['albumPath'] / f"{result['albumFilename']}.{image['ext']}", self.settings['overwriteFile'])
|
downloadImage(image['url'], result['albumPath'] / f"{result['albumFilename']}.{image['ext']}", self.settings['overwriteFile'])
|
||||||
|
|
||||||
# Save Artist Artwork
|
# Save Artist Artwork
|
||||||
if self.settings['saveArtworkArtist'] and 'artistPath' in result:
|
if self.settings['saveArtworkArtist'] and 'artistPath' in result:
|
||||||
for image in result['artistURLs']:
|
for image in result['artistURLs']:
|
||||||
downloadImage(image['url'], result['artistPath'] / f"{result['artistFilename']}.{image['ext']}", self.settings['overwriteFile'])
|
downloadImage(image['url'], result['artistPath'] / f"{result['artistFilename']}.{image['ext']}", self.settings['overwriteFile'])
|
||||||
|
|
||||||
# Save filename for playlist file
|
# Save filename for playlist file
|
||||||
playlist[index] = ""
|
playlist[i] = result.get('filename', "")
|
||||||
if 'filename' in result:
|
|
||||||
playlist[index] = result['filename']
|
|
||||||
|
|
||||||
# Create errors logfile
|
# Create errors logfile
|
||||||
if self.settings['logErrors'] and errors != "":
|
if self.settings['logErrors'] and errors != "":
|
||||||
with open(self.extrasPath / 'errors.txt', 'wb') as f:
|
with open(self.extrasPath / 'errors.txt', 'wb') as f:
|
||||||
f.write(errors.encode('utf-8'))
|
f.write(errors.encode('utf-8'))
|
||||||
|
|
||||||
# Create searched logfile
|
# Create searched logfile
|
||||||
if self.settings['logSearched'] and searched != "":
|
if self.settings['logSearched'] and searched != "":
|
||||||
with open(self.extrasPath / 'searched.txt', 'wb') as f:
|
with open(self.extrasPath / 'searched.txt', 'wb') as f:
|
||||||
f.write(searched.encode('utf-8'))
|
f.write(searched.encode('utf-8'))
|
||||||
|
|
||||||
# Save Playlist Artwork
|
# Save Playlist Artwork
|
||||||
if self.settings['saveArtwork'] and self.playlistCoverName and not self.settings['tags']['savePlaylistAsCompilation']:
|
if self.settings['saveArtwork'] and self.playlistCoverName and not self.settings['tags']['savePlaylistAsCompilation']:
|
||||||
for image in self.playlistURLs:
|
for image in self.playlistURLs:
|
||||||
downloadImage(image['url'], self.extrasPath / f"{self.playlistCoverName}.{image['ext']}", self.settings['overwriteFile'])
|
downloadImage(image['url'], self.extrasPath / f"{self.playlistCoverName}.{image['ext']}", self.settings['overwriteFile'])
|
||||||
|
|
||||||
# Create M3U8 File
|
# Create M3U8 File
|
||||||
if self.settings['createM3U8File']:
|
if self.settings['createM3U8File']:
|
||||||
filename = settingsRegexPlaylistFile(self.settings['playlistFilenameTemplate'], self.queueItem, self.settings) or "playlist"
|
filename = settingsRegexPlaylistFile(self.settings['playlistFilenameTemplate'], self.queueItem, self.settings) or "playlist"
|
||||||
with open(self.extrasPath / f'{filename}.m3u8', 'wb') as f:
|
with open(self.extrasPath / f'{filename}.m3u8', 'wb') as f:
|
||||||
for line in playlist:
|
for line in playlist:
|
||||||
f.write((line + "\n").encode('utf-8'))
|
f.write((line + "\n").encode('utf-8'))
|
||||||
|
|
||||||
# Execute command after download
|
# Execute command after download
|
||||||
if self.settings['executeCommand'] != "":
|
if self.settings['executeCommand'] != "":
|
||||||
execute(self.settings['executeCommand'].replace("%folder%", quote(str(self.extrasPath))), shell=True)
|
execute(self.settings['executeCommand'].replace("%folder%", quote(str(self.extrasPath))), shell=True)
|
||||||
|
@ -219,9 +217,7 @@ class DownloadJob:
|
||||||
def download(self, trackAPI_gw, track=None):
|
def download(self, trackAPI_gw, track=None):
|
||||||
result = {}
|
result = {}
|
||||||
if self.queueItem.cancel: raise DownloadCancelled
|
if self.queueItem.cancel: raise DownloadCancelled
|
||||||
|
if trackAPI_gw['SNG_ID'] == "0": raise DownloadFailed("notOnDeezer")
|
||||||
if trackAPI_gw['SNG_ID'] == "0":
|
|
||||||
raise DownloadFailed("notOnDeezer")
|
|
||||||
|
|
||||||
# Create Track object
|
# Create Track object
|
||||||
if not track:
|
if not track:
|
||||||
|
@ -237,6 +233,7 @@ class DownloadJob:
|
||||||
raise DownloadError('albumDoesntExsists')
|
raise DownloadError('albumDoesntExsists')
|
||||||
if self.queueItem.cancel: raise DownloadCancelled
|
if self.queueItem.cancel: raise DownloadCancelled
|
||||||
|
|
||||||
|
# Check if track not yet encoded
|
||||||
if track.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")
|
||||||
|
@ -266,8 +263,9 @@ class DownloadJob:
|
||||||
else:
|
else:
|
||||||
raise DownloadFailed("notEncoded")
|
raise DownloadFailed("notEncoded")
|
||||||
|
|
||||||
|
try:
|
||||||
selectedFormat = self.getPreferredBitrate(track)
|
selectedFormat = self.getPreferredBitrate(track)
|
||||||
if selectedFormat == -100:
|
except PreferredBitrateNotFound:
|
||||||
if track.fallbackId != "0":
|
if track.fallbackId != "0":
|
||||||
logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not found at desired bitrate, using fallback id")
|
logger.warn(f"[{track.mainArtist['name']} - {track.title}] Track not found at desired bitrate, using fallback id")
|
||||||
newTrack = self.dz.get_track_gw(track.fallbackId)
|
newTrack = self.dz.get_track_gw(track.fallbackId)
|
||||||
|
@ -295,10 +293,13 @@ class DownloadJob:
|
||||||
raise DownloadFailed("wrongBitrateNoAlternative")
|
raise DownloadFailed("wrongBitrateNoAlternative")
|
||||||
else:
|
else:
|
||||||
raise DownloadFailed("wrongBitrate")
|
raise DownloadFailed("wrongBitrate")
|
||||||
elif selectedFormat == -200:
|
except TrackNot360:
|
||||||
raise DownloadFailed("no360RA")
|
raise DownloadFailed("no360RA")
|
||||||
track.selectedFormat = selectedFormat
|
track.selectedFormat = selectedFormat
|
||||||
|
|
||||||
|
imageQuality = f'000000-{self.settings["jpegImageQuality"]}-0-0.jpg'
|
||||||
|
if self.settings['embeddedArtworkPNG']: imageQuality = 'none-100-0-0.png'
|
||||||
|
|
||||||
if self.settings['tags']['savePlaylistAsCompilation'] and track.playlist:
|
if self.settings['tags']['savePlaylistAsCompilation'] and track.playlist:
|
||||||
track.trackNumber = track.position
|
track.trackNumber = track.position
|
||||||
track.discNumber = "1"
|
track.discNumber = "1"
|
||||||
|
@ -308,23 +309,25 @@ class DownloadJob:
|
||||||
track.playlist['picType'],
|
track.playlist['picType'],
|
||||||
track.playlist['pic'],
|
track.playlist['pic'],
|
||||||
self.settings['embeddedArtworkSize'], self.settings['embeddedArtworkSize'],
|
self.settings['embeddedArtworkSize'], self.settings['embeddedArtworkSize'],
|
||||||
'none-100-0-0.png' if self.settings['embeddedArtworkPNG'] else f'000000-{self.settings["jpegImageQuality"]}-0-0.jpg'
|
imageQuality
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
track.playlist['picUrl'] = track.playlist['pic']
|
track.playlist['picUrl'] = track.playlist['pic']
|
||||||
|
|
||||||
ext = track.playlist['picUrl'][-4:]
|
ext = track.playlist['picUrl'][-4:]
|
||||||
if ext[0] != ".":
|
if ext[0] != ".": ext = ".jpg"
|
||||||
ext = ".jpg"
|
|
||||||
track.album['picPath'] = TEMPDIR / f"pl{trackAPI_gw['_EXTRA_PLAYLIST']['id']}_{self.settings['embeddedArtworkSize']}{ext}"
|
track.album['picPath'] = TEMPDIR / f"pl{trackAPI_gw['_EXTRA_PLAYLIST']['id']}_{self.settings['embeddedArtworkSize']}{ext}"
|
||||||
else:
|
else:
|
||||||
if track.album['date']:
|
if track.album['date']: track.date = track.album['date']
|
||||||
track.date = track.album['date']
|
|
||||||
track.album['picUrl'] = "https://e-cdns-images.dzcdn.net/images/cover/{}/{}x{}-{}".format(
|
track.album['picUrl'] = "https://e-cdns-images.dzcdn.net/images/cover/{}/{}x{}-{}".format(
|
||||||
track.album['pic'],
|
track.album['pic'],
|
||||||
self.settings['embeddedArtworkSize'], self.settings['embeddedArtworkSize'],
|
self.settings['embeddedArtworkSize'], self.settings['embeddedArtworkSize'],
|
||||||
'none-100-0-0.png' if self.settings['embeddedArtworkPNG'] else f'000000-{self.settings["jpegImageQuality"]}-0-0.jpg'
|
imageQuality
|
||||||
)
|
)
|
||||||
|
|
||||||
track.album['picPath'] = TEMPDIR / f"alb{track.album['id']}_{self.settings['embeddedArtworkSize']}{track.album['picUrl'][-4:]}"
|
track.album['picPath'] = TEMPDIR / f"alb{track.album['id']}_{self.settings['embeddedArtworkSize']}{track.album['picUrl'][-4:]}"
|
||||||
|
|
||||||
track.album['bitrate'] = selectedFormat
|
track.album['bitrate'] = selectedFormat
|
||||||
|
|
||||||
track.dateString = formatDate(track.date, self.settings['dateFormat'])
|
track.dateString = formatDate(track.date, self.settings['dateFormat'])
|
||||||
|
@ -393,17 +396,16 @@ class DownloadJob:
|
||||||
track.album['picType'],
|
track.album['picType'],
|
||||||
track.album['pic'],
|
track.album['pic'],
|
||||||
self.settings['localArtworkSize'], self.settings['localArtworkSize'],
|
self.settings['localArtworkSize'], self.settings['localArtworkSize'],
|
||||||
'none-100-0-0.png' if format == "png" else f'000000-{self.settings["jpegImageQuality"]}-0-0.jpg'
|
imageQuality
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
url = track.album['pic']
|
url = track.album['pic']
|
||||||
if format != "jpg":
|
if format != "jpg": continue
|
||||||
continue
|
|
||||||
else:
|
else:
|
||||||
url = "https://e-cdns-images.dzcdn.net/images/cover/{}/{}x{}-{}".format(
|
url = "https://e-cdns-images.dzcdn.net/images/cover/{}/{}x{}-{}".format(
|
||||||
track.album['pic'],
|
track.album['pic'],
|
||||||
self.settings['localArtworkSize'], self.settings['localArtworkSize'],
|
self.settings['localArtworkSize'], self.settings['localArtworkSize'],
|
||||||
'none-100-0-0.png' if format == "png" else f'000000-{self.settings["jpegImageQuality"]}-0-0.jpg'
|
imageQuality
|
||||||
)
|
)
|
||||||
result['albumURLs'].append({'url': url, 'ext': format})
|
result['albumURLs'].append({'url': url, 'ext': format})
|
||||||
result['albumPath'] = coverPath
|
result['albumPath'] = coverPath
|
||||||
|
@ -418,12 +420,14 @@ class DownloadJob:
|
||||||
if track.album['mainArtist']['pic'] != "":
|
if track.album['mainArtist']['pic'] != "":
|
||||||
url = "https://e-cdns-images.dzcdn.net/images/artist/{}/{}x{}-{}".format(
|
url = "https://e-cdns-images.dzcdn.net/images/artist/{}/{}x{}-{}".format(
|
||||||
track.album['mainArtist']['pic'], self.settings['localArtworkSize'], self.settings['localArtworkSize'],
|
track.album['mainArtist']['pic'], self.settings['localArtworkSize'], self.settings['localArtworkSize'],
|
||||||
'none-100-0-0.png' if format == "png" else f'000000-{self.settings["jpegImageQuality"]}-0-0.jpg')
|
imageQuality
|
||||||
elif format == "jpg":
|
)
|
||||||
|
elif format == "jpg": # Blank artist image is not available in PNG
|
||||||
url = "https://e-cdns-images.dzcdn.net/images/artist//{}x{}-{}".format(
|
url = "https://e-cdns-images.dzcdn.net/images/artist//{}x{}-{}".format(
|
||||||
self.settings['localArtworkSize'], self.settings['localArtworkSize'], f'000000-{self.settings["jpegImageQuality"]}-0-0.jpg')
|
self.settings['localArtworkSize'], self.settings['localArtworkSize'],
|
||||||
if url:
|
f'000000-{self.settings["jpegImageQuality"]}-0-0.jpg'
|
||||||
result['artistURLs'].append({'url': url, 'ext': format})
|
)
|
||||||
|
if url: result['artistURLs'].append({'url': url, 'ext': format})
|
||||||
result['artistPath'] = artistPath
|
result['artistPath'] = artistPath
|
||||||
result['artistFilename'] = f"{settingsRegexArtist(self.settings['artistImageTemplate'], track.album['mainArtist'], self.settings)}"
|
result['artistFilename'] = f"{settingsRegexArtist(self.settings['artistImageTemplate'], track.album['mainArtist'], self.settings)}"
|
||||||
|
|
||||||
|
@ -444,13 +448,16 @@ class DownloadJob:
|
||||||
f.write(track.lyrics['sync'].encode('utf-8'))
|
f.write(track.lyrics['sync'].encode('utf-8'))
|
||||||
|
|
||||||
trackAlreadyDownloaded = writepath.is_file()
|
trackAlreadyDownloaded = writepath.is_file()
|
||||||
|
|
||||||
|
# Don't overwrite and don't mind extension
|
||||||
if not trackAlreadyDownloaded and self.settings['overwriteFile'] == 'e':
|
if not trackAlreadyDownloaded and self.settings['overwriteFile'] == 'e':
|
||||||
exts = ['.mp3', '.flac', '.opus', '.m4a']
|
exts = ['.mp3', '.flac', '.opus', '.m4a']
|
||||||
baseFilename = str(filepath / filename)
|
baseFilename = str(filepath / filename)
|
||||||
for ext in exts:
|
for ext in exts:
|
||||||
trackAlreadyDownloaded = Path(baseFilename+ext).is_file()
|
trackAlreadyDownloaded = Path(baseFilename+ext).is_file()
|
||||||
if trackAlreadyDownloaded:
|
if trackAlreadyDownloaded: break
|
||||||
break
|
|
||||||
|
# Don't overwrite and keep both files
|
||||||
if trackAlreadyDownloaded and self.settings['overwriteFile'] == 'b':
|
if trackAlreadyDownloaded and self.settings['overwriteFile'] == 'b':
|
||||||
baseFilename = str(filepath / filename)
|
baseFilename = str(filepath / filename)
|
||||||
i = 1
|
i = 1
|
||||||
|
@ -461,7 +468,6 @@ class DownloadJob:
|
||||||
trackAlreadyDownloaded = False
|
trackAlreadyDownloaded = False
|
||||||
writepath = Path(currentFilename)
|
writepath = Path(currentFilename)
|
||||||
|
|
||||||
|
|
||||||
if extrasPath:
|
if extrasPath:
|
||||||
if not self.extrasPath: self.extrasPath = extrasPath
|
if not self.extrasPath: self.extrasPath = extrasPath
|
||||||
result['filename'] = str(writepath)[len(str(extrasPath))+ len(pathSep):]
|
result['filename'] = str(writepath)[len(str(extrasPath))+ len(pathSep):]
|
||||||
|
@ -476,7 +482,7 @@ class DownloadJob:
|
||||||
track.playlist['picType'],
|
track.playlist['picType'],
|
||||||
track.playlist['pic'],
|
track.playlist['pic'],
|
||||||
self.settings['localArtworkSize'], self.settings['localArtworkSize'],
|
self.settings['localArtworkSize'], self.settings['localArtworkSize'],
|
||||||
'none-100-0-0.png' if format == "png" else f'000000-{self.settings["jpegImageQuality"]}-0-0.jpg'
|
imageQuality
|
||||||
)
|
)
|
||||||
self.playlistURLs.append({'url': url, 'ext': format})
|
self.playlistURLs.append({'url': url, 'ext': format})
|
||||||
else:
|
else:
|
||||||
|
@ -548,13 +554,10 @@ class DownloadJob:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
trackDownloaded = downloadMusic(track, trackAPI_gw)
|
trackDownloaded = downloadMusic(track, trackAPI_gw)
|
||||||
except DownloadFailed as e:
|
|
||||||
raise e
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
if not trackDownloaded:
|
if not trackDownloaded: 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")
|
||||||
self.completeTrackPercentage()
|
self.completeTrackPercentage()
|
||||||
|
@ -574,9 +577,8 @@ class DownloadJob:
|
||||||
track.filesizes['FILESIZE_FLAC'] = "0"
|
track.filesizes['FILESIZE_FLAC'] = "0"
|
||||||
track.filesizes['FILESIZE_FLAC_TESTED'] = True
|
track.filesizes['FILESIZE_FLAC_TESTED'] = True
|
||||||
return self.download(trackAPI_gw, track)
|
return self.download(trackAPI_gw, track)
|
||||||
if track.searched:
|
|
||||||
result['searched'] = f"{track.mainArtist['name']} - {track.title}"
|
|
||||||
|
|
||||||
|
if track.searched: result['searched'] = f"{track.mainArtist['name']} - {track.title}"
|
||||||
logger.info(f"[{track.mainArtist['name']} - {track.title}] Track download completed\n{str(writepath)}")
|
logger.info(f"[{track.mainArtist['name']} - {track.title}] Track download completed\n{str(writepath)}")
|
||||||
self.queueItem.downloaded += 1
|
self.queueItem.downloaded += 1
|
||||||
self.queueItem.files.append(str(writepath))
|
self.queueItem.files.append(str(writepath))
|
||||||
|
@ -586,10 +588,9 @@ class DownloadJob:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def getPreferredBitrate(self, track):
|
def getPreferredBitrate(self, track):
|
||||||
if track.localTrack:
|
if track.localTrack: return 0
|
||||||
return 0
|
|
||||||
|
|
||||||
fallback = self.settings['fallbackBitrate']
|
shouldFallback = self.settings['fallbackBitrate']
|
||||||
falledBack = False
|
falledBack = False
|
||||||
|
|
||||||
formats_non_360 = {
|
formats_non_360 = {
|
||||||
|
@ -603,30 +604,34 @@ class DownloadJob:
|
||||||
13: "MP4_RA1",
|
13: "MP4_RA1",
|
||||||
}
|
}
|
||||||
|
|
||||||
if not fallback:
|
is360format = int(self.bitrate) in formats_360
|
||||||
error_num = -100
|
|
||||||
|
if not shouldFallback:
|
||||||
formats = formats_360
|
formats = formats_360
|
||||||
formats.update(formats_non_360)
|
formats.update(formats_non_360)
|
||||||
elif int(self.bitrate) in formats_360:
|
elif is360format:
|
||||||
error_num = -200
|
|
||||||
formats = formats_360
|
formats = formats_360
|
||||||
else:
|
else:
|
||||||
error_num = 8
|
|
||||||
formats = formats_non_360
|
formats = formats_non_360
|
||||||
|
|
||||||
for format_num, format in formats.items():
|
for formatNumber, formatName in formats.items():
|
||||||
if format_num <= int(self.bitrate):
|
if formatNumber <= int(self.bitrate):
|
||||||
if f"FILESIZE_{format}" in track.filesizes:
|
if f"FILESIZE_{formatName}" in track.filesizes:
|
||||||
if int(track.filesizes[f"FILESIZE_{format}"]) != 0:
|
if int(track.filesizes[f"FILESIZE_{formatName}"]) != 0: return formatNumber
|
||||||
return format_num
|
if not track.filesizes[f"FILESIZE_{formatName}_TESTED"]:
|
||||||
elif not track.filesizes[f"FILESIZE_{format}_TESTED"]:
|
request = requests.head(
|
||||||
request = requests.head(self.dz.get_track_stream_url(track.id, track.MD5, track.mediaVersion, format_num), headers={'User-Agent': USER_AGENT_HEADER}, timeout=30)
|
self.dz.get_track_stream_url(track.id, track.MD5, track.mediaVersion, formatNumber),
|
||||||
|
headers={'User-Agent': USER_AGENT_HEADER},
|
||||||
|
timeout=30
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
request.raise_for_status()
|
request.raise_for_status()
|
||||||
return format_num
|
return formatNumber
|
||||||
except request_exception.HTTPError: # if the format is not available, Deezer returns a 403 error
|
except request_exception.HTTPError: # if the format is not available, Deezer returns a 403 error
|
||||||
pass
|
pass
|
||||||
if fallback:
|
if not shouldFallback:
|
||||||
|
raise PreferredBitrateNotFound
|
||||||
|
else:
|
||||||
if not falledBack:
|
if not falledBack:
|
||||||
falledBack = True
|
falledBack = True
|
||||||
logger.info(f"[{track.mainArtist['name']} - {track.title}] Fallback to lower bitrate")
|
logger.info(f"[{track.mainArtist['name']} - {track.title}] Fallback to lower bitrate")
|
||||||
|
@ -640,18 +645,14 @@ class DownloadJob:
|
||||||
'artist': track.mainArtist['name']
|
'artist': track.mainArtist['name']
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
continue
|
if is360format: raise TrackNot360
|
||||||
else:
|
return 8
|
||||||
return error_num
|
|
||||||
|
|
||||||
return error_num # fallback is enabled and loop went through all formats
|
|
||||||
|
|
||||||
def streamTrack(self, stream, track, start=0):
|
def streamTrack(self, stream, track, start=0):
|
||||||
if self.queueItem.cancel: raise DownloadCancelled
|
if self.queueItem.cancel: raise DownloadCancelled
|
||||||
|
|
||||||
headers=dict(self.dz.http_headers)
|
headers=dict(self.dz.http_headers)
|
||||||
if range != 0:
|
if range != 0: headers['Range'] = f'bytes={start}-'
|
||||||
headers['Range'] = f'bytes={start}-'
|
|
||||||
chunkLength = start
|
chunkLength = start
|
||||||
percentage = 0
|
percentage = 0
|
||||||
|
|
||||||
|
@ -664,8 +665,7 @@ class DownloadJob:
|
||||||
blowfish_key = str.encode(self.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"])
|
||||||
if complete == 0:
|
if complete == 0: raise DownloadEmpty
|
||||||
raise DownloadEmpty
|
|
||||||
if start != 0:
|
if start != 0:
|
||||||
responseRange = request.headers["Content-Range"]
|
responseRange = request.headers["Content-Range"]
|
||||||
logger.info(f'{itemName} downloading range {responseRange}')
|
logger.info(f'{itemName} downloading range {responseRange}')
|
||||||
|
@ -687,7 +687,6 @@ class DownloadJob:
|
||||||
else:
|
else:
|
||||||
chunkProgres = (len(chunk) / (complete + start)) / self.queueItem.size * 100
|
chunkProgres = (len(chunk) / (complete + start)) / self.queueItem.size * 100
|
||||||
self.downloadPercentage += chunkProgres
|
self.downloadPercentage += chunkProgres
|
||||||
|
|
||||||
self.updatePercentage()
|
self.updatePercentage()
|
||||||
|
|
||||||
except SSLError as e:
|
except SSLError as e:
|
||||||
|
@ -701,8 +700,7 @@ class DownloadJob:
|
||||||
if round(self.downloadPercentage) != self.lastPercentage and round(self.downloadPercentage) % 2 == 0:
|
if round(self.downloadPercentage) != self.lastPercentage and round(self.downloadPercentage) % 2 == 0:
|
||||||
self.lastPercentage = round(self.downloadPercentage)
|
self.lastPercentage = round(self.downloadPercentage)
|
||||||
self.queueItem.progress = self.lastPercentage
|
self.queueItem.progress = self.lastPercentage
|
||||||
if self.interface:
|
if self.interface: self.interface.send("updateQueue", {'uuid': self.queueItem.uuid, 'progress': self.lastPercentage})
|
||||||
self.interface.send("updateQueue", {'uuid': self.queueItem.uuid, 'progress': self.lastPercentage})
|
|
||||||
|
|
||||||
def completeTrackPercentage(self):
|
def completeTrackPercentage(self):
|
||||||
if isinstance(self.queueItem, QISingle):
|
if isinstance(self.queueItem, QISingle):
|
||||||
|
@ -721,9 +719,11 @@ class DownloadJob:
|
||||||
def downloadWrapper(self, trackAPI_gw):
|
def downloadWrapper(self, trackAPI_gw):
|
||||||
track = {
|
track = {
|
||||||
'id': trackAPI_gw['SNG_ID'],
|
'id': trackAPI_gw['SNG_ID'],
|
||||||
'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 ""),
|
'title': trackAPI_gw['SNG_TITLE'].strip(),
|
||||||
'artist': trackAPI_gw['ART_NAME']
|
'artist': trackAPI_gw['ART_NAME']
|
||||||
}
|
}
|
||||||
|
if trackAPI_gw.get('VERSION') and trackAPI_gw['VERSION'] not in trackAPI_gw['SNG_TITLE']:
|
||||||
|
track['title'] += f" {trackAPI_gw['VERSION']}".strip()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = self.download(trackAPI_gw)
|
result = self.download(trackAPI_gw)
|
||||||
|
@ -772,3 +772,9 @@ class DownloadCancelled(DownloadError):
|
||||||
|
|
||||||
class DownloadEmpty(DownloadError):
|
class DownloadEmpty(DownloadError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class PreferredBitrateNotFound(DownloadError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TrackNot360(DownloadError):
|
||||||
|
pass
|
||||||
|
|
|
@ -53,7 +53,9 @@ class QueueManager:
|
||||||
|
|
||||||
trackAPI_gw['SINGLE_TRACK'] = True
|
trackAPI_gw['SINGLE_TRACK'] = True
|
||||||
|
|
||||||
title = f"{trackAPI_gw['SNG_TITLE']} {trackAPI_gw.get('VERSION', '')}".strip()
|
title = trackAPI_gw['SNG_TITLE'].strip()
|
||||||
|
if trackAPI_gw.get('VERSION') and trackAPI_gw['VERSION'] not in trackAPI_gw['SNG_TITLE']:
|
||||||
|
title += f" {trackAPI_gw['VERSION']}".strip()
|
||||||
explicit = bool(int(trackAPI_gw.get('EXPLICIT_LYRICS', 0)))
|
explicit = bool(int(trackAPI_gw.get('EXPLICIT_LYRICS', 0)))
|
||||||
|
|
||||||
return QISingle(
|
return QISingle(
|
||||||
|
|
Loading…
Reference in New Issue