diff --git a/TIDALDL-PY/tidal_dl/__init__.py b/TIDALDL-PY/tidal_dl/__init__.py
index 23daf92..4adc183 100644
--- a/TIDALDL-PY/tidal_dl/__init__.py
+++ b/TIDALDL-PY/tidal_dl/__init__.py
@@ -26,44 +26,11 @@ from tidal_dl.lang.language import setLang, initLang, getLangChoicePrint
from tidal_dl.printf import Printf, VERSION
from tidal_dl.settings import Settings, TokenSettings, getLogPath
from tidal_dl.tidal import TidalAPI
-from tidal_dl.util import API
+from tidal_dl.util import API, CONF, TOKEN, LANG, displayTime, loginByConfig, loginByWeb
import tidal_dl.apiKey as apiKey
ssl._create_default_https_context = ssl._create_unverified_context
-TOKEN = TokenSettings.read()
-CONF = Settings.read()
-LANG = initLang(CONF.language)
-API.apiKey = apiKey.getItem(CONF.apiKeyIndex)
-
-logging.basicConfig(filename=getLogPath(),
- level=logging.INFO,
- format='%(asctime)s - %(levelname)s: %(message)s')
-
-
-def displayTime(seconds, granularity=2):
- if seconds <= 0:
- return "unknown"
-
- result = []
- intervals = (
- ('weeks', 604800),
- ('days', 86400),
- ('hours', 3600),
- ('minutes', 60),
- ('seconds', 1),
- )
-
- for name, count in intervals:
- value = seconds // count
- if value:
- seconds -= value * count
- if value == 1:
- name = name.rstrip('s')
- result.append("{} {}".format(value, name))
- return ', '.join(result[:granularity])
-
-
def login():
print(LANG.AUTH_START_LOGIN)
msg, check = API.getDeviceCode()
@@ -74,29 +41,7 @@ def login():
# print(LANG.AUTH_LOGIN_CODE.format(green(API.key.userCode)))
print(LANG.AUTH_NEXT_STEP.format(green("http://" + API.key.verificationUrl + "/" + API.key.userCode), yellow(displayTime(API.key.authCheckTimeout))))
print(LANG.AUTH_WAITING)
- start = time.time()
- elapsed = 0
- while elapsed < API.key.authCheckTimeout:
- elapsed = time.time() - start
- # print("Check auth status...")
- msg, check = API.checkAuthStatus()
- if not check:
- if msg == "pending":
- time.sleep(API.key.authCheckInterval + 1)
- continue
- Printf.err(msg)
- break
- if check:
- Printf.success(LANG.MSG_VALID_ACCESSTOKEN.format(displayTime(int(API.key.expiresIn))))
- TOKEN.userid = API.key.userId
- TOKEN.countryCode = API.key.countryCode
- TOKEN.accessToken = API.key.accessToken
- TOKEN.refreshToken = API.key.refreshToken
- TOKEN.expiresAfter = time.time() + int(API.key.expiresIn)
- TokenSettings.save(TOKEN)
- break
- if elapsed >= API.key.authCheckTimeout:
- Printf.err(LANG.AUTH_TIMEOUT)
+ loginByWeb()
return
@@ -139,33 +84,13 @@ def setAPIKey():
def checkLogin():
- if not isNull(TOKEN.accessToken):
- # print('Checking Access Token...') #add to translations
- msg, check = API.verifyAccessToken(TOKEN.accessToken)
- if check:
- Printf.info(LANG.MSG_VALID_ACCESSTOKEN.format(displayTime(int(TOKEN.expiresAfter - time.time()))))
- return
- else:
- Printf.info(LANG.MSG_INVAILD_ACCESSTOKEN)
- msg, check = API.refreshAccessToken(TOKEN.refreshToken)
- if check:
- Printf.success(LANG.MSG_VALID_ACCESSTOKEN.format(displayTime(int(API.key.expiresIn))))
- TOKEN.userid = API.key.userId
- TOKEN.countryCode = API.key.countryCode
- TOKEN.accessToken = API.key.accessToken
- TOKEN.expiresAfter = time.time() + int(API.key.expiresIn)
- TokenSettings.save(TOKEN)
- return
- else:
- Printf.err(msg)
- tmp = TokenSettings() # clears saved tokens
- TokenSettings.save(tmp)
+ if loginByConfig():
+ return
login()
return
def checkLogout():
- global LANG
login()
return
diff --git a/TIDALDL-PY/tidal_dl/download.py b/TIDALDL-PY/tidal_dl/download.py
index 61d64dd..7a284cb 100644
--- a/TIDALDL-PY/tidal_dl/download.py
+++ b/TIDALDL-PY/tidal_dl/download.py
@@ -22,396 +22,13 @@ from tidal_dl.model import Track, Video, Lyrics, Mix
from tidal_dl.printf import Printf
from tidal_dl.settings import Settings
from tidal_dl.tidal import TidalAPI
-from tidal_dl.util import getVideoPath, getTrackPath, getAlbumPath, getArtistsName, convertToM4a, setMetaData, \
- isNeedDownload, API, getLyricsFromGemius
+from tidal_dl.util import convert, downloadTrack, downloadVideo, encrypted, getVideoPath, getTrackPath, getAlbumPath, API
def __loadAPI__(user):
API.key.accessToken = user.accessToken
API.key.userId = user.userid
API.key.countryCode = user.countryCode
- # API.key.sessionId = user.sessionid1
-
-
-def __loadVideoAPI__(user):
- API.key.accessToken = user.accessToken
- API.key.userId = user.userid
- API.key.countryCode = user.countryCode
- # API.key.sessionId = user.sessionid2 if not aigpy.string.isNull(user.sessionid2) else user.sessionid1
-
-
-# def __getIndexStr__(index):
-# pre = "0"
-# if index < 10:
-# return pre + str(index)
-# if index < 99:
-# return str(index)
-# return str(index)
-
-
-# def __getExtension__(url):
-# if '.flac' in url:
-# return '.flac'
-# if '.mp4' in url:
-# return '.mp4'
-# return '.m4a'
-
-
-# def __getArtists__(array):
-# ret = []
-# for item in array:
-# ret.append(item.name)
-# return ret
-
-
-# def __getArtistsString__(artists):
-# return ", ".join(map(lambda artist: artist.name, artists))
-
-
-# def __parseContributors__(roleType, Contributors):
-# if Contributors is None:
-# return None
-# try:
-# ret = []
-# for item in Contributors['items']:
-# if item['role'] == roleType:
-# ret.append(item['name'])
-# return ret
-# except:
-# return None
-
-
-# GEMIUS = lyricsgenius.Genius('vNKbAWAE3rVY_48nRaiOrDcWNLvsxS-Z8qyG5XfEzTOtZvkTfg6P3pxOVlA2BjaW')
-#
-#
-# def __getLyrics__(trackName, artistName, proxy):
-# try:
-# if not aigpy.string.isNull(proxy):
-# GEMIUS._session.proxies = {
-# 'http': f'http://{proxy}',
-# 'https': f'http://{proxy}',
-# }
-#
-# song = GEMIUS.search_song(trackName, artistName)
-# return song.lyrics
-# except:
-# return ""
-
-#
-# def __setMetaData__(track, album, filepath, contributors, lyrics):
-# obj = aigpy.tag.TagTool(filepath)
-# obj.album = track.album.title
-# obj.title = track.title
-# if not aigpy.string.isNull(track.version):
-# obj.title += ' (' + track.version + ')'
-#
-# obj.artist = map(lambda artist: artist.name, track.artists) # __getArtists__(track.artists)
-# obj.copyright = track.copyRight
-# obj.tracknumber = track.trackNumber
-# obj.discnumber = track.volumeNumber
-# obj.composer = __parseContributors__('Composer', contributors)
-# obj.isrc = track.isrc
-#
-# obj.albumartist = map(lambda artist: artist.name, album.artists) #__getArtists__(album.artists)
-# obj.date = album.releaseDate
-# obj.totaldisc = album.numberOfVolumes
-# obj.lyrics = lyrics
-# if obj.totaldisc <= 1:
-# obj.totaltrack = album.numberOfTracks
-# coverpath = API.getCoverUrl(album.cover, "1280", "1280")
-# obj.save(coverpath)
-# return
-
-
-# def __convertToM4a__(filepath, codec):
-# if 'ac4' in codec or 'mha1' in codec:
-# return filepath
-# if '.mp4' not in filepath:
-# return filepath
-# newpath = filepath.replace('.mp4', '.m4a')
-# aigpy.path.remove(newpath)
-# os.rename(filepath, newpath)
-# return newpath
-
-
-# def __stripPathParts__(stripped_path, separator):
-# result = ""
-# stripped_path = stripped_path.split(separator)
-# for stripped_path_part in stripped_path:
-# result += stripped_path_part.strip()
-# if not stripped_path.index(stripped_path_part) == len(stripped_path) - 1:
-# result += separator
-# return result.strip()
-#
-#
-# def __stripPath__(path):
-# result = __stripPathParts__(path, "/")
-# result = __stripPathParts__(result, "\\")
-# return result.strip()
-
-# "{ArtistName}/{Flag} [{AlbumID}] [{AlbumYear}] {AlbumTitle}"
-# def getAlbumPath(conf: Settings, album):
-# base = conf.downloadPath + '/Album/'
-# artist = aigpy.path.replaceLimitChar(__getArtistsString__(album.artists), '-')
-# # album folder pre: [ME][ID]
-# flag = API.getFlag(album, Type.Album, True, "")
-# if conf.audioQuality != AudioQuality.Master:
-# flag = flag.replace("M", "")
-# if not conf.addExplicitTag:
-# flag = flag.replace("E", "")
-# if not aigpy.string.isNull(flag):
-# flag = "[" + flag + "] "
-#
-# sid = str(album.id)
-# # album and addyear
-# albumname = aigpy.path.replaceLimitChar(album.title, '-')
-# year = ""
-# if album.releaseDate is not None:
-# year = aigpy.string.getSubOnlyEnd(album.releaseDate, '-')
-# # retpath
-# retpath = conf.albumFolderFormat
-# if retpath is None or len(retpath) <= 0:
-# retpath = Settings.getDefaultAlbumFolderFormat()
-# retpath = retpath.replace(R"{ArtistName}", artist.strip())
-# retpath = retpath.replace(R"{Flag}", flag)
-# retpath = retpath.replace(R"{AlbumID}", sid)
-# retpath = retpath.replace(R"{AlbumYear}", year)
-# retpath = retpath.replace(R"{AlbumTitle}", albumname.strip())
-# retpath = __stripPath__(retpath.strip())
-# return base + retpath
-#
-#
-# def __getAlbumPath2__(conf, album):
-# # outputdir/Album/artist/
-# artist = aigpy.path.replaceLimitChar(__getArtistsString__(album.artists), '-').strip()
-# base = conf.downloadPath + '/Album/' + artist + '/'
-#
-# # album folder pre: [ME][ID]
-# flag = API.getFlag(album, Type.Album, True, "")
-# if conf.audioQuality != AudioQuality.Master:
-# flag = flag.replace("M", "")
-# if not conf.addExplicitTag:
-# flag = flag.replace("E", "")
-# if not aigpy.string.isNull(flag):
-# flag = "[" + flag + "] "
-#
-# sid = "[" + str(album.id) + "] " if conf.addAlbumIDBeforeFolder else ""
-#
-# # album and addyear
-# albumname = aigpy.path.replaceLimitChar(album.title, '-').strip()
-# year = ""
-# if conf.addYear and album.releaseDate is not None:
-# year = "[" + aigpy.string.getSubOnlyEnd(album.releaseDate, '-') + "] "
-# return base + flag + sid + year + albumname + '/'
-#
-#
-# def getPlaylistPath(conf, playlist):
-# # outputdir/Playlist/
-# base = conf.downloadPath + '/Playlist/'
-# # name
-# name = aigpy.path.replaceLimitChar(playlist.title, '-')
-# return base + name + '/'
-#
-#
-# # "{TrackNumber} - {ArtistName} - {TrackTitle}{ExplicitFlag}"
-#
-#
-# def getTrackPath(conf: Settings, track, stream, album=None, playlist=None):
-# if album is not None:
-# base = getAlbumPath(conf, album) + '/'
-# if album.numberOfVolumes > 1:
-# base += 'CD' + str(track.volumeNumber) + '/'
-# if playlist is not None and conf.usePlaylistFolder:
-# base = getPlaylistPath(conf, playlist)
-# # number
-# number = __getIndexStr__(track.trackNumber)
-# if playlist is not None and conf.usePlaylistFolder:
-# number = __getIndexStr__(track.trackNumberOnPlaylist)
-# # artist
-# artist = aigpy.path.replaceLimitChar(__getArtistsString__(track.artists), '-')
-# # title
-# title = track.title
-# if not aigpy.string.isNull(track.version):
-# title += ' (' + track.version + ')'
-# title = aigpy.path.replaceLimitChar(title, '-')
-# # get explicit
-# explicit = "(Explicit)" if conf.addExplicitTag and track.explicit else ''
-# # album and addyear
-# albumname = aigpy.path.replaceLimitChar(album.title, '-')
-# year = ""
-# if album.releaseDate is not None:
-# year = aigpy.string.getSubOnlyEnd(album.releaseDate, '-')
-# # extension
-# extension = __getExtension__(stream.url)
-# retpath = conf.trackFileFormat
-# if retpath is None or len(retpath) <= 0:
-# retpath = Settings.getDefaultTrackFileFormat()
-# retpath = retpath.replace(R"{TrackNumber}", number)
-# retpath = retpath.replace(R"{ArtistName}", artist.strip())
-# retpath = retpath.replace(R"{TrackTitle}", title)
-# retpath = retpath.replace(R"{ExplicitFlag}", explicit)
-# retpath = retpath.replace(R"{AlbumYear}", year)
-# retpath = retpath.replace(R"{AlbumTitle}", albumname.strip())
-# retpath = retpath.strip()
-# return base + retpath + extension
-#
-#
-# def __getTrackPath2__(conf, track, stream, album=None, playlist=None):
-# if album is not None:
-# base = getAlbumPath(conf, album)
-# if album.numberOfVolumes > 1:
-# base += 'CD' + str(track.volumeNumber) + '/'
-# if playlist is not None and conf.usePlaylistFolder:
-# base = getPlaylistPath(conf, playlist)
-#
-# # hyphen
-# hyphen = ' - ' if conf.addHyphen else ' '
-# # get number
-# number = ''
-# if conf.useTrackNumber:
-# number = __getIndexStr__(track.trackNumber) + hyphen
-# if playlist is not None:
-# number = __getIndexStr__(track.trackNumberOnPlaylist) + hyphen
-# # get artist
-# artist = ''
-# if conf.artistBeforeTitle:
-# artist = aigpy.path.replaceLimitChar(__getArtistsString__(track.artists), '-') + hyphen
-# # get explicit
-# explicit = "(Explicit)" if conf.addExplicitTag and track.explicit else ''
-# # title
-# title = track.title
-# if not aigpy.string.isNull(track.version):
-# title += ' - ' + track.version
-# title = aigpy.path.replaceLimitChar(title, '-')
-# # extension
-# extension = __getExtension__(stream.url)
-# return base + number + artist.strip() + title + explicit + extension
-#
-#
-# def getVideoPath(conf, video, album=None, playlist=None):
-# if album is not None and album.title is not None:
-# base = getAlbumPath(conf, album)
-# elif playlist is not None and conf.usePlaylistFolder:
-# base = getPlaylistPath(conf, playlist)
-# else:
-# base = conf.downloadPath + '/Video/'
-#
-# # hyphen
-# hyphen = ' - ' if conf.addHyphen else ' '
-# # get number
-# number = ''
-# if conf.useTrackNumber:
-# number = __getIndexStr__(video.trackNumber) + hyphen
-# # get artist
-# artist = ''
-# if conf.artistBeforeTitle:
-# artist = aigpy.path.replaceLimitChar(__getArtistsString__(video.artists), '-') + hyphen
-# # get explicit
-# explicit = "(Explicit)" if conf.addExplicitTag and video.explicit else ''
-# # title
-# title = aigpy.path.replaceLimitChar(video.title, '-')
-# # extension
-# extension = ".mp4"
-# return base + number + artist.strip() + title + explicit + extension
-
-
-# def __isNeedDownload__(path, url):
-# curSize = aigpy.file.getSize(path)
-# if curSize <= 0:
-# return True
-# netSize = aigpy.net.getSize(url)
-# if curSize >= netSize:
-# return False
-# return True
-
-
-
-def __downloadVideo__(conf, video: Video, album=None, playlist=None):
- if video.allowStreaming is False:
- Printf.err("Download failed! " + video.title + ' not allow streaming.')
- return
-
- msg, stream = API.getVideoStreamUrl(video.id, conf.videoQuality)
- Printf.video(video, stream)
- if not aigpy.string.isNull(msg):
- Printf.err(video.title + "." + msg)
- return
- path = getVideoPath(conf, video, album, playlist)
-
- logging.info("[DL Video] name=" + aigpy.path.getFileName(path) + "\nurl=" + stream.m3u8Url)
- m3u8content = requests.get(stream.m3u8Url).content
- if m3u8content is None:
- Printf.err(video.title + ' get m3u8 content failed.')
- return
-
- urls = aigpy.m3u8.parseTsUrls(m3u8content)
- if len(urls) <= 0:
- Printf.err(video.title + ' parse ts urls failed.')
- logging.info("[DL Video] title=" + video.title + "\m3u8Content=" + str(m3u8content))
- return
-
- check, msg = aigpy.m3u8.downloadByTsUrls(urls, path)
- # check, msg = aigpy.m3u8.download(stream.m3u8Url, path)
- if check is True:
- Printf.success(aigpy.path.getFileName(path))
- else:
- Printf.err("\nDownload failed!" + msg + '(' + aigpy.path.getFileName(path) + ')')
-
-
-def __downloadTrack__(conf: Settings, track: Track, album=None, playlist=None):
- try:
- # if track.allowStreaming is False:
- # Printf.err("Download failed! " + track.title + ' not allow streaming.')
- # return
-
- msg, stream = API.getStreamUrl(track.id, conf.audioQuality)
- if conf.showTrackInfo:
- Printf.track(track, stream)
- if not aigpy.string.isNull(msg) or stream is None:
- Printf.err(track.title + "." + msg)
- return
- path = getTrackPath(conf, track, stream, album, playlist)
-
- # check exist
- if conf.checkExist and isNeedDownload(path, stream.url) == False:
- Printf.success(aigpy.path.getFileName(path) + " (skip:already exists!)")
- return
- logging.info("[DL Track] name=" + aigpy.path.getFileName(path) + "\nurl=" + stream.url)
- tool = aigpy.download.DownloadTool(path + '.part', [stream.url])
- check, err = tool.start(conf.showProgress)
-
- if not check:
- Printf.err("Download failed! " + aigpy.path.getFileName(path) + ' (' + str(err) + ')')
- return
- # encrypted -> decrypt and remove encrypted file
- if aigpy.string.isNull(stream.encryptionKey):
- os.replace(path + '.part', path)
- else:
- key, nonce = decrypt_security_token(stream.encryptionKey)
- decrypt_file(path + '.part', path, key, nonce)
- os.remove(path + '.part')
-
- path = convertToM4a(path, stream.codec)
-
- # contributors
- msg, contributors = API.getTrackContributors(track.id)
- msg, tidalLyrics = API.getLyrics(track.id)
-
- lyrics = '' if tidalLyrics is None else tidalLyrics.subtitles
- if conf.addLyrics and lyrics == '':
- lyrics = getLyricsFromGemius(track.title, getArtistsName(track.artists), conf.lyricsServerProxy)
-
- if conf.lyricFile:
- if tidalLyrics is None:
- Printf.info(f'Failed to get lyrics from tidal!"{track.title}"')
- else:
- lrcPath = path.rsplit(".", 1)[0] + '.lrc'
- aigpy.fileHelper.write(lrcPath, tidalLyrics.subtitles, 'w')
-
- setMetaData(track, album, path, contributors, lyrics)
- Printf.success(aigpy.path.getFileName(path))
- except Exception as e:
- Printf.err("Download failed! " + track.title + ' (' + str(e) + ')')
def __downloadCover__(conf, album):
@@ -462,21 +79,21 @@ def __album__(conf, obj):
if conf.saveCovers:
__downloadCover__(conf, obj)
for item in tracks:
- __downloadTrack__(conf, item, obj)
+ downloadTrack(item, obj)
for item in videos:
- __downloadVideo__(conf, item, obj)
+ downloadVideo(item, obj)
def __track__(conf, obj):
msg, album = API.getAlbum(obj.album.id)
if conf.saveCovers:
__downloadCover__(conf, album)
- __downloadTrack__(conf, obj, album)
+ downloadTrack(obj, album)
def __video__(conf, obj):
# Printf.video(obj)
- __downloadVideo__(conf, obj, obj.album)
+ downloadVideo(obj, obj.album)
def __artist__(conf, obj):
@@ -499,11 +116,11 @@ def __playlist__(conf, obj):
for index, item in enumerate(tracks):
mag, album = API.getAlbum(item.album.id)
item.trackNumberOnPlaylist = index + 1
- __downloadTrack__(conf, item, album, obj)
+ downloadTrack(item, album, obj)
if conf.saveCovers and not conf.usePlaylistFolder:
__downloadCover__(conf, album)
for item in videos:
- __downloadVideo__(conf, item, None)
+ downloadVideo(item, None)
def __mix__(conf, obj: Mix):
@@ -511,11 +128,11 @@ def __mix__(conf, obj: Mix):
for index, item in enumerate(obj.tracks):
mag, album = API.getAlbum(item.album.id)
item.trackNumberOnPlaylist = index + 1
- __downloadTrack__(conf, item, album)
+ downloadTrack(item, album)
if conf.saveCovers and not conf.usePlaylistFolder:
__downloadCover__(conf, album)
for item in obj.videos:
- __downloadVideo__(conf, item, None)
+ downloadVideo(item, None)
def file(user, conf, string):
@@ -558,7 +175,6 @@ def start(user, conf, string):
if etype == Type.Track:
__track__(conf, obj)
if etype == Type.Video:
- __loadVideoAPI__(user)
__video__(conf, obj)
if etype == Type.Artist:
__artist__(conf, obj)
diff --git a/TIDALDL-PY/tidal_dl/model.py b/TIDALDL-PY/tidal_dl/model.py
index 35de80b..1b568f1 100644
--- a/TIDALDL-PY/tidal_dl/model.py
+++ b/TIDALDL-PY/tidal_dl/model.py
@@ -51,6 +51,17 @@ class Album(ModelBase):
artists = Artist()
+class Playlist(ModelBase):
+ uuid = None
+ title = None
+ numberOfTracks = 0
+ numberOfVideos = 0
+ description = None
+ duration = 0
+ image = None
+ squareImage = None
+
+
class Track(ModelBase):
id = None
title = None
@@ -67,6 +78,7 @@ class Track(ModelBase):
artists = Artist()
album = Album()
allowStreaming = False
+ playlist = None
class Video(ModelBase):
@@ -83,17 +95,7 @@ class Video(ModelBase):
artists = Artist()
album = Album()
allowStreaming = False
-
-
-class Playlist(ModelBase):
- uuid = None
- title = None
- numberOfTracks = 0
- numberOfVideos = 0
- description = None
- duration = 0
- image = None
- squareImage = None
+ playlist = None
class Mix(ModelBase):
diff --git a/TIDALDL-PY/tidal_dl/tidal.py b/TIDALDL-PY/tidal_dl/tidal.py
index 124e2cf..600d3af 100644
--- a/TIDALDL-PY/tidal_dl/tidal.py
+++ b/TIDALDL-PY/tidal_dl/tidal.py
@@ -397,6 +397,14 @@ class TidalAPI(object):
if sid is None or sid == "":
return None
return "https://resources.tidal.com/images/" + sid.replace("-", "/") + "/" + width + "x" + height + ".jpg"
+
+ def getCoverData(self, sid, width="320", height="320"):
+ url = self.getCoverUrl(sid, width, height)
+ try:
+ respond = requests.get(url)
+ return respond.content
+ except:
+ return ''
def getArtistsName(self, artists=[]):
array = []
diff --git a/TIDALDL-PY/tidal_dl/util.py b/TIDALDL-PY/tidal_dl/util.py
index 65737db..884fdf6 100644
--- a/TIDALDL-PY/tidal_dl/util.py
+++ b/TIDALDL-PY/tidal_dl/util.py
@@ -11,19 +11,32 @@
import logging
import os
+import time
+from urllib import request
import aigpy
import lyricsgenius
import datetime
+from tidal_dl import apiKey
+import tidal_dl
from tidal_dl.decryption import decrypt_file
from tidal_dl.decryption import decrypt_security_token
from tidal_dl.enums import Type, AudioQuality
+from tidal_dl.lang.language import initLang
from tidal_dl.model import Track, Video, Lyrics, Mix, Album
from tidal_dl.printf import Printf
-from tidal_dl.settings import Settings
+from tidal_dl.settings import Settings, TokenSettings, getLogPath
from tidal_dl.tidal import TidalAPI
+TOKEN = TokenSettings.read()
+CONF = Settings.read()
+LANG = initLang(CONF.language)
API = TidalAPI()
+API.apiKey = apiKey.getItem(CONF.apiKeyIndex)
+
+logging.basicConfig(filename=getLogPath(),
+ level=logging.INFO,
+ format='%(asctime)s - %(levelname)s: %(message)s')
def __getIndexStr__(index):
@@ -49,6 +62,7 @@ def __secondsToTimeStr__(seconds):
time_string = time_string[2:]
return time_string
+
def __parseContributors__(roleType, Contributors):
if Contributors is None:
return None
@@ -273,3 +287,216 @@ def isNeedDownload(path, url):
if curSize >= netSize:
return False
return True
+
+
+def encrypted(stream, srcPath, descPath):
+ if aigpy.string.isNull(stream.encryptionKey):
+ os.replace(srcPath, descPath)
+ else:
+ key, nonce = decrypt_security_token(stream.encryptionKey)
+ decrypt_file(srcPath, descPath, key, nonce)
+ os.remove(srcPath)
+
+
+def getAudioQualityList():
+ return map(lambda quality: quality.name, tidal_dl.enums.AudioQuality)
+
+
+def getVideoQualityList():
+ return map(lambda quality: quality.name, tidal_dl.enums.VideoQuality)
+
+
+def skip(path, url):
+ if CONF.checkExist and isNeedDownload(path, url) is False:
+ return True
+ return False
+
+
+def convert(srcPath, stream):
+ if CONF.onlyM4a:
+ return convertToM4a(srcPath, stream.codec)
+ return srcPath
+
+
+def downloadTrack(track: Track, album=None, playlist=None, userProgress=None):
+ try:
+ msg, stream = API.getStreamUrl(track.id, CONF.audioQuality)
+ if not aigpy.string.isNull(msg) or stream is None:
+ Printf.err(track.title + "." + msg)
+ return False, msg
+ if CONF.showTrackInfo:
+ Printf.track(track, stream)
+ path = getTrackPath(CONF, track, stream, album, playlist)
+
+ # check exist
+ if skip(path, stream.url):
+ Printf.success(aigpy.path.getFileName(path) + " (skip:already exists!)")
+ return True, ""
+
+ # download
+ logging.info("[DL Track] name=" + aigpy.path.getFileName(path) + "\nurl=" + stream.url)
+ tool = aigpy.download.DownloadTool(path + '.part', [stream.url])
+ tool.setUserProgress(userProgress)
+ check, err = tool.start(CONF.showProgress)
+ if not check:
+ Printf.err("Download failed! " + aigpy.path.getFileName(path) + ' (' + str(err) + ')')
+ return False, str(err)
+
+ # encrypted -> decrypt and remove encrypted file
+ encrypted(stream, path + '.part', path)
+
+ # convert
+ path = convert(path, stream)
+
+ # contributors
+ msg, contributors = API.getTrackContributors(track.id)
+ msg, tidalLyrics = API.getLyrics(track.id)
+
+ lyrics = '' if tidalLyrics is None else tidalLyrics.subtitles
+ if CONF.lyricFile:
+ if tidalLyrics is None:
+ Printf.info(f'Failed to get lyrics from tidal!"{track.title}"')
+ else:
+ lrcPath = path.rsplit(".", 1)[0] + '.lrc'
+ aigpy.fileHelper.write(lrcPath, tidalLyrics.subtitles, 'w')
+
+ setMetaData(track, album, path, contributors, lyrics)
+ Printf.success(aigpy.path.getFileName(path))
+ return True, ""
+ except Exception as e:
+ Printf.err("Download failed! " + track.title + ' (' + str(e) + ')')
+ return False, str(e)
+
+
+def downloadVideo(video: Video, album=None, playlist=None):
+ msg, stream = API.getVideoStreamUrl(video.id, CONF.videoQuality)
+ Printf.video(video, stream)
+ if not aigpy.string.isNull(msg):
+ Printf.err(video.title + "." + msg)
+ return False, msg
+ path = getVideoPath(CONF, video, album, playlist)
+
+ logging.info("[DL Video] name=" + aigpy.path.getFileName(path) + "\nurl=" + stream.m3u8Url)
+ m3u8content = request.get(stream.m3u8Url).content
+ if m3u8content is None:
+ Printf.err(video.title + ' get m3u8 content failed.')
+ return False, "Get m3u8 content failed"
+
+ urls = aigpy.m3u8.parseTsUrls(m3u8content)
+ if len(urls) <= 0:
+ Printf.err(video.title + ' parse ts urls failed.')
+ logging.info("[DL Video] title=" + video.title + "\m3u8Content=" + str(m3u8content))
+ return False, 'Parse ts urls failed.'
+
+ check, msg = aigpy.m3u8.downloadByTsUrls(urls, path)
+ # check, msg = aigpy.m3u8.download(stream.m3u8Url, path)
+ if check is True:
+ Printf.success(aigpy.path.getFileName(path))
+ return True, ''
+ else:
+ Printf.err("\nDownload failed!" + msg + '(' + aigpy.path.getFileName(path) + ')')
+ return False, msg
+
+
+def displayTime(seconds, granularity=2):
+ if seconds <= 0:
+ return "unknown"
+
+ result = []
+ intervals = (
+ ('weeks', 604800),
+ ('days', 86400),
+ ('hours', 3600),
+ ('minutes', 60),
+ ('seconds', 1),
+ )
+
+ for name, count in intervals:
+ value = seconds // count
+ if value:
+ seconds -= value * count
+ if value == 1:
+ name = name.rstrip('s')
+ result.append("{} {}".format(value, name))
+ return ', '.join(result[:granularity])
+
+def loginByConfig():
+ if aigpy.stringHelper.isNull(TOKEN.accessToken):
+ return False
+
+ msg, check = API.verifyAccessToken(TOKEN.accessToken)
+ if check:
+ Printf.info(LANG.MSG_VALID_ACCESSTOKEN.format(displayTime(int(TOKEN.expiresAfter - time.time()))))
+ API.key.countryCode = TOKEN.countryCode
+ API.key.userId = TOKEN.userid
+ API.key.accessToken = TOKEN.accessToken
+ return True
+
+ Printf.info(LANG.MSG_INVAILD_ACCESSTOKEN)
+ msg, check = API.refreshAccessToken(TOKEN.refreshToken)
+ if check:
+ Printf.success(LANG.MSG_VALID_ACCESSTOKEN.format(displayTime(int(API.key.expiresIn))))
+ TOKEN.userid = API.key.userId
+ TOKEN.countryCode = API.key.countryCode
+ TOKEN.accessToken = API.key.accessToken
+ TOKEN.expiresAfter = time.time() + int(API.key.expiresIn)
+ TokenSettings.save(TOKEN)
+ return True
+ else:
+ tmp = TokenSettings() # clears saved tokens
+ TokenSettings.save(tmp)
+ return False
+
+def loginByWeb():
+ start = time.time()
+ elapsed = 0
+ while elapsed < API.key.authCheckTimeout:
+ elapsed = time.time() - start
+ msg, check = API.checkAuthStatus()
+ if not check:
+ if msg == "pending":
+ time.sleep(API.key.authCheckInterval + 1)
+ continue
+ return False
+ if check:
+ Printf.success(LANG.MSG_VALID_ACCESSTOKEN.format(displayTime(int(API.key.expiresIn))))
+ TOKEN.userid = API.key.userId
+ TOKEN.countryCode = API.key.countryCode
+ TOKEN.accessToken = API.key.accessToken
+ TOKEN.refreshToken = API.key.refreshToken
+ TOKEN.expiresAfter = time.time() + int(API.key.expiresIn)
+ TokenSettings.save(TOKEN)
+ return True
+
+ Printf.err(LANG.AUTH_TIMEOUT)
+ return False
+
+def getArtistsNames(artists): # : list[tidal_dl.model.Artist]
+ ret = []
+ for item in artists:
+ ret.append(item.name)
+ return ','.join(ret)
+
+def getDurationString(seconds: int):
+ m, s = divmod(seconds, 60)
+ h, m = divmod(m, 60)
+ return "%02d:%02d:%02d" % (h, m, s)
+
+def getBasePath(model):
+ if isinstance(model, tidal_dl.model.Album):
+ return getAlbumPath(CONF, model)
+ if isinstance(model, tidal_dl.model.Playlist):
+ return getPlaylistPath(CONF, model)
+ if isinstance(model, tidal_dl.model.Track):
+ return getAlbumPath(CONF, model)
+ if isinstance(model, tidal_dl.model.Video):
+ filePath = getVideoPath(CONF, model, model.album, model.playlist)
+ return aigpy.pathHelper.getDirName(filePath)
+ return './'
+
+def getFilePath(model, stream=None):
+ if isinstance(model, tidal_dl.model.Track):
+ return getTrackPath(CONF, model, stream, model.album, model.playlist)
+ if isinstance(model, tidal_dl.model.Video):
+ return getVideoPath(CONF, model, model.album, model.playlist)
+ return './'
diff --git a/TIDALDL-PY/tidal_gui/__init__.py b/TIDALDL-PY/tidal_gui/__init__.py
index 014e003..edf7452 100644
--- a/TIDALDL-PY/tidal_gui/__init__.py
+++ b/TIDALDL-PY/tidal_gui/__init__.py
@@ -23,11 +23,12 @@ def main():
mainView = MainModel()
mainView.show()
-
+
app.exec_()
mainView.uninit()
sys.exit()
+
if __name__ == '__main__':
main()
diff --git a/TIDALDL-PY/tidal_gui/control/comboBox.py b/TIDALDL-PY/tidal_gui/control/comboBox.py
index 793c66b..a2cb882 100644
--- a/TIDALDL-PY/tidal_gui/control/comboBox.py
+++ b/TIDALDL-PY/tidal_gui/control/comboBox.py
@@ -8,12 +8,12 @@
@Contact : yaronhuang@foxmail.com
@Desc :
"""
-from PyQt5.QtWidgets import QComboBox, QListView
from PyQt5.QtCore import Qt
+from PyQt5.QtWidgets import QComboBox, QListView
class ComboBox(QComboBox):
- def __init__(self, items: list, width: int=200):
+ def __init__(self, items: list, width: int = 200):
super(ComboBox, self).__init__()
self.setItems(items)
self.setFixedWidth(width)
diff --git a/TIDALDL-PY/tidal_gui/control/framelessWidget.py b/TIDALDL-PY/tidal_gui/control/framelessWidget.py
index 5ede2a5..6e0118b 100644
--- a/TIDALDL-PY/tidal_gui/control/framelessWidget.py
+++ b/TIDALDL-PY/tidal_gui/control/framelessWidget.py
@@ -8,13 +8,7 @@
@Contact : yaronhuang@foxmail.com
@Desc :
"""
-import ctypes.wintypes
-from ctypes.wintypes import POINT
-import typing
-import PyQt5
-import win32api
-import win32con
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtGui import QMouseEvent
from PyQt5.QtWidgets import QWidget, QGridLayout, QHBoxLayout
diff --git a/TIDALDL-PY/tidal_gui/control/listWidget.py b/TIDALDL-PY/tidal_gui/control/listWidget.py
index 4ad62c1..3e8f6c5 100644
--- a/TIDALDL-PY/tidal_gui/control/listWidget.py
+++ b/TIDALDL-PY/tidal_gui/control/listWidget.py
@@ -8,7 +8,6 @@
@Contact : yaronhuang@foxmail.com
@Desc :
"""
-from PyQt5.QtCore import QSize
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QListWidget, QListWidgetItem, QWidget
diff --git a/TIDALDL-PY/tidal_gui/control/pushButton.py b/TIDALDL-PY/tidal_gui/control/pushButton.py
index 475a48c..01fea3a 100644
--- a/TIDALDL-PY/tidal_gui/control/pushButton.py
+++ b/TIDALDL-PY/tidal_gui/control/pushButton.py
@@ -29,4 +29,3 @@ class PushButton(QPushButton):
if iconUrl != '':
self.setIcon(QIcon(iconUrl))
-
diff --git a/TIDALDL-PY/tidal_gui/control/scrollWidget.py b/TIDALDL-PY/tidal_gui/control/scrollWidget.py
index fff07a7..8daa827 100644
--- a/TIDALDL-PY/tidal_gui/control/scrollWidget.py
+++ b/TIDALDL-PY/tidal_gui/control/scrollWidget.py
@@ -9,21 +9,19 @@
@Desc :
"""
-from PyQt5.QtCore import QSize, Qt
-from PyQt5.QtGui import QIcon
-from PyQt5.QtWidgets import QListWidget, QListWidgetItem, QWidget, QScrollArea, QVBoxLayout, QPushButton, QBoxLayout
+from PyQt5.QtWidgets import QWidget, QScrollArea, QVBoxLayout
class ScrollWidget(QScrollArea):
def __init__(self):
super(ScrollWidget, self).__init__()
-
+
self._layout = QVBoxLayout()
self._layout.addStretch(1)
-
+
self._mainW = QWidget()
self._mainW.setLayout(self._layout)
-
+
self.setWidget(self._mainW)
self.setWidgetResizable(True)
diff --git a/TIDALDL-PY/tidal_gui/control/tableView.py b/TIDALDL-PY/tidal_gui/control/tableView.py
index 05f9349..6f14a86 100644
--- a/TIDALDL-PY/tidal_gui/control/tableView.py
+++ b/TIDALDL-PY/tidal_gui/control/tableView.py
@@ -10,7 +10,7 @@
"""
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QStandardItemModel, QStandardItem
-from PyQt5.QtWidgets import QTableView, QTableWidgetItem, QAbstractItemView
+from PyQt5.QtWidgets import QTableView, QAbstractItemView
class TableView(QTableView):
@@ -25,7 +25,7 @@ class TableView(QTableView):
self._model.setHeaderData(index, Qt.Horizontal, name)
self.setModel(self._model)
- # self.setHorizontalHeaderItem(index, QTableWidgetItem(name))
+ # self.setHorizontalHeaderItem(index, QTableWidgetItem(name))
# for index in range(0, rowCount):
# self.setRowHeight(index, 50)
diff --git a/TIDALDL-PY/tidal_gui/control/tableWidget.py b/TIDALDL-PY/tidal_gui/control/tableWidget.py
index ed17a43..51ced47 100644
--- a/TIDALDL-PY/tidal_gui/control/tableWidget.py
+++ b/TIDALDL-PY/tidal_gui/control/tableWidget.py
@@ -8,13 +8,13 @@
@Contact : yaronhuang@foxmail.com
@Desc :
"""
-import aigpy
-import requests
import threading
+
from PyQt5.QtCore import Qt, QUrl
-from PyQt5.QtGui import QIcon, QPixmap
-from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
-from PyQt5.QtWidgets import QTableWidget, QTableWidgetItem, QAbstractItemView, QLabel
+from PyQt5.QtGui import QPixmap
+from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
+from PyQt5.QtWidgets import QTableWidget, QTableWidgetItem, QAbstractItemView
+
from tidal_gui.control.label import Label, LabelStyle
diff --git a/TIDALDL-PY/tidal_gui/downloader.py b/TIDALDL-PY/tidal_gui/downloader.py
index 3613520..031b859 100644
--- a/TIDALDL-PY/tidal_gui/downloader.py
+++ b/TIDALDL-PY/tidal_gui/downloader.py
@@ -8,16 +8,18 @@
@Contact : yaronhuang@foxmail.com
@Desc :
"""
-from PyQt5.Qt import QThread
-from queue import Queue
-from tidal_gui.viewModel.taskModel import TaskModel
import time
+from PyQt5.Qt import QThread
+
+from tidal_gui.viewModel.taskModel import TaskModel
+
+
class DownloaderImp(QThread):
def __init__(self):
super(DownloaderImp, self).__init__()
self._taskModel = None
-
+
def run(self):
print('DownloadImp start...')
while not self.isInterruptionRequested():
@@ -27,12 +29,13 @@ class DownloaderImp(QThread):
item.download()
time.sleep(1)
print('DownloadImp stop...')
-
+
def setTaskModel(self, model: TaskModel):
self._taskModel = model
-
+
def stop(self):
self.requestInterruption()
self.wait()
-
+
+
downloadImp = DownloaderImp()
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/V.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/V.svg
index 0a1576c..5bdc765 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/V.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/V.svg
@@ -1 +1,12 @@
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/buymeacoffee.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/buymeacoffee.svg
index 9a8bd88..f231fb3 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/buymeacoffee.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/buymeacoffee.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/down.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/down.svg
index a171c81..aaedf37 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/down.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/down.svg
@@ -3,4 +3,8 @@
-->
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/downHover.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/downHover.svg
index 24cbb35..2fc7c26 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/downHover.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/downHover.svg
@@ -3,4 +3,8 @@
-->
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/github.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/github.svg
index 2d1cea6..d176dd1 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/github.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/github.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/left.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/left.svg
index e931f47..cb55090 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/left.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/left.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/download.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/download.svg
index be5235f..7e1ccad 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/download.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/download.svg
@@ -1 +1,10 @@
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/downloadHover.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/downloadHover.svg
index 3eeb232..a0718a6 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/downloadHover.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/downloadHover.svg
@@ -1 +1,10 @@
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/info.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/info.svg
index b428605..78fb6e5 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/info.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/info.svg
@@ -1 +1,10 @@
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/search.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/search.svg
index 0c1900f..9cf2416 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/search.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/search.svg
@@ -1 +1,10 @@
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/settings.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/settings.svg
index dee79cf..ff71fe2 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/settings.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/leftTab/settings.svg
@@ -1 +1,10 @@
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/paypal.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/paypal.svg
index a46caba..e91041d 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/paypal.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/paypal.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/right.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/right.svg
index 96bf9b1..352e296 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/right.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/right.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/search.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/search.svg
index a1cffbe..fe03660 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/search.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/search.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/search2.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/search2.svg
index 2235c0d..b801741 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/search2.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/search2.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/cancel.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/cancel.svg
index 2f1c840..3696dee 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/cancel.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/cancel.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/delete.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/delete.svg
index 45176f6..7019a8d 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/delete.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/delete.svg
@@ -1 +1,9 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/expand.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/expand.svg
index 922d4eb..3b7a650 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/expand.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/expand.svg
@@ -1 +1,4 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/open.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/open.svg
index 6a01693..3396540 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/open.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/open.svg
@@ -1 +1,6 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/retry.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/retry.svg
index 42d25cd..174202b 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/retry.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskItem/retry.svg
@@ -1 +1,8 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/complete.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/complete.svg
index 9397de9..ca50052 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/complete.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/complete.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/download.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/download.svg
index 0e9d285..96a2ae2 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/download.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/download.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/error.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/error.svg
index a2bc642..2065ba1 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/error.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/taskTab/error.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg - 原/upHover.svg b/TIDALDL-PY/tidal_gui/resource/svg - 原/upHover.svg
index 9fff595..d7e66be 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg - 原/upHover.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg - 原/upHover.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/V.svg b/TIDALDL-PY/tidal_gui/resource/svg/V.svg
index 0a1576c..5bdc765 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/V.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/V.svg
@@ -1 +1,12 @@
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/buymeacoffee.svg b/TIDALDL-PY/tidal_gui/resource/svg/buymeacoffee.svg
index 9a8bd88..f231fb3 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/buymeacoffee.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/buymeacoffee.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/down.svg b/TIDALDL-PY/tidal_gui/resource/svg/down.svg
index a171c81..aaedf37 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/down.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/down.svg
@@ -3,4 +3,8 @@
-->
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/downHover.svg b/TIDALDL-PY/tidal_gui/resource/svg/downHover.svg
index 24cbb35..2fc7c26 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/downHover.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/downHover.svg
@@ -3,4 +3,8 @@
-->
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/github.svg b/TIDALDL-PY/tidal_gui/resource/svg/github.svg
index 2d1cea6..d176dd1 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/github.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/github.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/left.svg b/TIDALDL-PY/tidal_gui/resource/svg/left.svg
index e931f47..cb55090 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/left.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/left.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/leftTab/download.svg b/TIDALDL-PY/tidal_gui/resource/svg/leftTab/download.svg
index be5235f..7e1ccad 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/leftTab/download.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/leftTab/download.svg
@@ -1 +1,10 @@
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/leftTab/downloadHover.svg b/TIDALDL-PY/tidal_gui/resource/svg/leftTab/downloadHover.svg
index 3eeb232..a0718a6 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/leftTab/downloadHover.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/leftTab/downloadHover.svg
@@ -1 +1,10 @@
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/leftTab/info.svg b/TIDALDL-PY/tidal_gui/resource/svg/leftTab/info.svg
index b428605..78fb6e5 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/leftTab/info.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/leftTab/info.svg
@@ -1 +1,10 @@
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/leftTab/search.svg b/TIDALDL-PY/tidal_gui/resource/svg/leftTab/search.svg
index 0c1900f..9cf2416 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/leftTab/search.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/leftTab/search.svg
@@ -1 +1,10 @@
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/leftTab/settings.svg b/TIDALDL-PY/tidal_gui/resource/svg/leftTab/settings.svg
index dee79cf..ff71fe2 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/leftTab/settings.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/leftTab/settings.svg
@@ -1 +1,10 @@
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/paypal.svg b/TIDALDL-PY/tidal_gui/resource/svg/paypal.svg
index a46caba..e91041d 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/paypal.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/paypal.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/right.svg b/TIDALDL-PY/tidal_gui/resource/svg/right.svg
index 96bf9b1..352e296 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/right.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/right.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/search.svg b/TIDALDL-PY/tidal_gui/resource/svg/search.svg
index a1cffbe..fe03660 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/search.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/search.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/search2.svg b/TIDALDL-PY/tidal_gui/resource/svg/search2.svg
index 2235c0d..b801741 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/search2.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/search2.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/taskItem/cancel.svg b/TIDALDL-PY/tidal_gui/resource/svg/taskItem/cancel.svg
index 2f1c840..3696dee 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/taskItem/cancel.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/taskItem/cancel.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/taskItem/delete.svg b/TIDALDL-PY/tidal_gui/resource/svg/taskItem/delete.svg
index 45176f6..7019a8d 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/taskItem/delete.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/taskItem/delete.svg
@@ -1 +1,9 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/taskItem/expand.svg b/TIDALDL-PY/tidal_gui/resource/svg/taskItem/expand.svg
index 922d4eb..3b7a650 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/taskItem/expand.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/taskItem/expand.svg
@@ -1 +1,4 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/taskItem/open.svg b/TIDALDL-PY/tidal_gui/resource/svg/taskItem/open.svg
index 6a01693..3396540 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/taskItem/open.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/taskItem/open.svg
@@ -1 +1,6 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/taskItem/retry.svg b/TIDALDL-PY/tidal_gui/resource/svg/taskItem/retry.svg
index 42d25cd..174202b 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/taskItem/retry.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/taskItem/retry.svg
@@ -1 +1,8 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/taskTab/complete.svg b/TIDALDL-PY/tidal_gui/resource/svg/taskTab/complete.svg
index 9397de9..ca50052 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/taskTab/complete.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/taskTab/complete.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/taskTab/download.svg b/TIDALDL-PY/tidal_gui/resource/svg/taskTab/download.svg
index 0e9d285..96a2ae2 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/taskTab/download.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/taskTab/download.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/taskTab/error.svg b/TIDALDL-PY/tidal_gui/resource/svg/taskTab/error.svg
index a2bc642..2065ba1 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/taskTab/error.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/taskTab/error.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/svg/upHover.svg b/TIDALDL-PY/tidal_gui/resource/svg/upHover.svg
index 9fff595..d7e66be 100644
--- a/TIDALDL-PY/tidal_gui/resource/svg/upHover.svg
+++ b/TIDALDL-PY/tidal_gui/resource/svg/upHover.svg
@@ -1 +1,5 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/TIDALDL-PY/tidal_gui/resource/themeDefault.qss b/TIDALDL-PY/tidal_gui/resource/themeDefault.qss
index 6784334..c470a20 100644
--- a/TIDALDL-PY/tidal_gui/resource/themeDefault.qss
+++ b/TIDALDL-PY/tidal_gui/resource/themeDefault.qss
@@ -94,7 +94,18 @@ QWidget#AboutWidget {
}
-
+QScrollBar::vertical{
+ background:transparent;
+ width: 4px;
+ border-radius:6px;
+}
+QScrollBar::handle{
+ background: lightgray;
+ border-radius:6px;
+}
+QScrollBar::handle:hover{background:gray;}
+QScrollBar::sub-line{background:transparent;}
+QScrollBar::add-line{background:transparent;}
/* PushButton Style */
diff --git a/TIDALDL-PY/tidal_gui/style.py b/TIDALDL-PY/tidal_gui/style.py
index 50475d2..27c5284 100644
--- a/TIDALDL-PY/tidal_gui/style.py
+++ b/TIDALDL-PY/tidal_gui/style.py
@@ -33,13 +33,13 @@ class ButtonStyle(Enum):
PrePage = 14
NextPage = 15
-
+
TaskRetry = 16,
TaskCancel = 17,
TaskDelete = 18,
TaskOpen = 19,
TaskExpand = 20,
-
+
class LabelStyle(Enum):
Default = 0,
diff --git a/TIDALDL-PY/tidal_gui/theme.py b/TIDALDL-PY/tidal_gui/theme.py
index 0aa09b2..a6c4e19 100644
--- a/TIDALDL-PY/tidal_gui/theme.py
+++ b/TIDALDL-PY/tidal_gui/theme.py
@@ -10,7 +10,9 @@
"""
import os
+
import aigpy
+
from tidal_gui.style import ThemeStyle
_RESOURCE_PATH = './resource'
@@ -20,7 +22,6 @@ else:
_RESOURCE_PATH = aigpy.path.getDirName(__file__).replace('\\', '/') + "resource"
-
def __getParam__(line: str):
key = aigpy.string.getSub(line, "--", ":")
value = aigpy.string.getSub(line, ":", ";")
@@ -55,9 +56,11 @@ def __getQss__(filePath: str) -> str:
qss = __parseQss__(content, params)
return qss
+
def getResourcePath():
return _RESOURCE_PATH
+
def getThemeQssContent(style: ThemeStyle = ThemeStyle.Default):
name = "theme" + style.name + ".qss"
return __getQss__(_RESOURCE_PATH + '/' + name)
diff --git a/TIDALDL-PY/tidal_gui/tidalImp.py b/TIDALDL-PY/tidal_gui/tidalImp.py
deleted file mode 100644
index b706f04..0000000
--- a/TIDALDL-PY/tidal_gui/tidalImp.py
+++ /dev/null
@@ -1,165 +0,0 @@
-#!/usr/bin/env python
-# -*- encoding: utf-8 -*-
-"""
-@File : tidalImp.py
-@Date : 2021/9/2
-@Author : Yaronzz
-@Version : 1.0
-@Contact : yaronhuang@foxmail.com
-@Desc :
-"""
-import time
-
-import requests
-import tidal_dl.model
-import tidal_dl.enums
-from aigpy.stringHelper import isNull
-from tidal_dl import TokenSettings, TOKEN, TidalAPI, CONF
-from tidal_dl.util import getAlbumPath, getPlaylistPath, getTrackPath
-
-
-class TidalImp(TidalAPI):
- def __init__(self):
- super(TidalImp, self).__init__()
-
- def loginByConfig(self):
- if isNull(TOKEN.accessToken):
- return False
-
- msg, check = self.verifyAccessToken(TOKEN.accessToken)
- if check:
- self.key.countryCode = TOKEN.countryCode
- self.key.userId = TOKEN.userid
- self.key.accessToken = TOKEN.accessToken
- return True
-
- msg, check = self.refreshAccessToken(TOKEN.refreshToken)
- if check:
- TOKEN.userid = self.key.userId
- TOKEN.countryCode = self.key.countryCode
- TOKEN.accessToken = self.key.accessToken
- TOKEN.expiresAfter = time.time() + int(self.key.expiresIn)
- TokenSettings.save(TOKEN)
- return True
- else:
- tmp = TokenSettings() # clears saved tokens
- TokenSettings.save(tmp)
- return False
-
- def loginByWeb(self):
- start = time.time()
- elapsed = 0
- while elapsed < self.key.authCheckTimeout:
- elapsed = time.time() - start
- msg, check = self.checkAuthStatus()
- if not check:
- if msg == "pending":
- time.sleep(self.key.authCheckInterval + 1)
- continue
- return False
- if check:
- TOKEN.userid = self.key.userId
- TOKEN.countryCode = self.key.countryCode
- TOKEN.accessToken = self.key.accessToken
- TOKEN.refreshToken = self.key.refreshToken
- TOKEN.expiresAfter = time.time() + int(self.key.expiresIn)
- TokenSettings.save(TOKEN)
- return True
- return False
-
- @staticmethod
- def getArtistsNames(artists): # : list[tidal_dl.model.Artist]
- ret = []
- for item in artists:
- ret.append(item.name)
- return ','.join(ret)
-
- @staticmethod
- def getDurationString(seconds: int):
- m, s = divmod(seconds, 60)
- h, m = divmod(m, 60)
- return "%02d:%02d:%02d" % (h, m, s)
-
- def getCoverData(self, sid, width="320", height="320"):
- url = self.getCoverUrl(sid, width, height)
- try:
- respond = requests.get(url)
- return respond.content
- except:
- return ''
-
- @staticmethod
- def getAudioQualityList():
- return map(lambda quality: quality.name, tidal_dl.enums.AudioQuality)
-
- @staticmethod
- def getVideoQualityList():
- return map(lambda quality: quality.name, tidal_dl.enums.VideoQuality)
-
- def getBasePath(self, model):
- if isinstance(model, tidal_dl.model.Album):
- return getAlbumPath(CONF, model)
- if isinstance(model, tidal_dl.model.Playlist):
- return getPlaylistPath(CONF, model)
- if isinstance(model, tidal_dl.model.Track):
- return getAlbumPath(CONF, model.album)
- if isinstance(model, tidal_dl.model.Video):
- return CONF.downloadPath + '/Video/'
- return './'
-
- def getConfig(self):
- return CONF
-
- # def getTackPath(self, basePath, track, stream, album=None, playlist=None):
- # # number
- # number = __getIndexStr__(track.trackNumber)
- # if playlist is not None and CONF.usePlaylistFolder:
- # number = __getIndexStr__(track.trackNumberOnPlaylist)
- # # artist
- # artist = aigpy.path.replaceLimitChar(__getArtistsString__(track.artists), '-')
- # # title
- # title = track.title
- # if not aigpy.string.isNull(track.version):
- # title += ' (' + track.version + ')'
- # title = aigpy.path.replaceLimitChar(title, '-')
- # # get explicit
- # explicit = "(Explicit)" if CONF.addExplicitTag and track.explicit else ''
- # # album and addyear
- # albumname = aigpy.path.replaceLimitChar(album.title, '-')
- # year = ""
- # if album.releaseDate is not None:
- # year = aigpy.string.getSubOnlyEnd(album.releaseDate, '-')
- # # extension
- # extension = __getExtension__(stream.url)
- # retpath = CONF.trackFileFormat
- # if retpath is None or len(retpath) <= 0:
- # retpath = Settings.getDefaultTrackFileFormat()
- # retpath = retpath.replace(R"{TrackNumber}", number)
- # retpath = retpath.replace(R"{ArtistName}", artist.strip())
- # retpath = retpath.replace(R"{TrackTitle}", title)
- # retpath = retpath.replace(R"{ExplicitFlag}", explicit)
- # retpath = retpath.replace(R"{AlbumYear}", year)
- # retpath = retpath.replace(R"{AlbumTitle}", albumname.strip())
- # retpath = retpath.strip()
- # return basePath + retpath + extension
-
- # def getVideoPath(self, basePath, video):
- # # hyphen
- # hyphen = ' - ' if CONF.addHyphen else ' '
- # # get number
- # number = ''
- # if CONF.useTrackNumber:
- # number = __getIndexStr__(video.trackNumber) + hyphen
- # # get artist
- # artist = ''
- # if CONF.artistBeforeTitle:
- # artist = aigpy.path.replaceLimitChar(__getArtistsString__(video.artists), '-') + hyphen
- # # get explicit
- # explicit = "(Explicit)" if CONF.addExplicitTag and video.explicit else ''
- # # title
- # title = aigpy.path.replaceLimitChar(video.title, '-')
- # # extension
- # extension = ".mp4"
- # return basePath + number + artist.strip() + title + explicit + extension
-
-tidalImp = TidalImp()
diff --git a/TIDALDL-PY/tidal_gui/view/downloadItemView.py b/TIDALDL-PY/tidal_gui/view/downloadItemView.py
index 3b492df..46ffd83 100644
--- a/TIDALDL-PY/tidal_gui/view/downloadItemView.py
+++ b/TIDALDL-PY/tidal_gui/view/downloadItemView.py
@@ -8,14 +8,11 @@
@Contact : yaronhuang@foxmail.com
@Desc :
"""
-from PyQt5.QtGui import QPixmap
-from PyQt5.QtWidgets import QWidget, QHBoxLayout, QTableWidget, QVBoxLayout, QGridLayout, QProgressBar
from PyQt5.QtCore import Qt
+from PyQt5.QtWidgets import QWidget, QHBoxLayout, QGridLayout, QProgressBar
from tidal_gui.control.label import Label
-from tidal_gui.control.layout import createHBoxLayout, createVBoxLayout
-from tidal_gui.control.pushButton import PushButton
-from tidal_gui.style import LabelStyle, ButtonStyle
+from tidal_gui.style import LabelStyle
class DownloadItemView(QWidget):
@@ -38,6 +35,7 @@ class DownloadItemView(QWidget):
self._progress.setTextVisible(False)
self._progress.setFixedHeight(3)
self._progress.setFixedWidth(300)
+ self._progress.setRange(0, 100)
grid = QGridLayout(self)
grid.addWidget(self._indexLabel, 0, 0, Qt.AlignLeft | Qt.AlignVCenter)
@@ -46,14 +44,14 @@ class DownloadItemView(QWidget):
grid.addWidget(self._progress, 0, 3, Qt.AlignRight | Qt.AlignVCenter)
grid.addWidget(self._actionLabel, 0, 4, Qt.AlignRight | Qt.AlignVCenter)
grid.addWidget(self._errLabel, 1, 1, Qt.AlignLeft | Qt.AlignVCenter)
-
+
grid.setColumnStretch(1, 1)
-
+
layout = QHBoxLayout()
layout.setSpacing(30)
layout.addWidget(self._sizeLabel)
layout.addWidget(self._speedLabel)
-
+
grid.addLayout(layout, 1, 3, Qt.AlignLeft | Qt.AlignVCenter)
def setLabel(self, index, title, own):
@@ -70,9 +68,9 @@ class DownloadItemView(QWidget):
def setProgress(self, value):
self._progress.setValue(value)
pass
-
+
def setSize(self, curSize: str, totalSize: str):
self._sizeLabel.setText(f'{curSize}/{totalSize}')
-
+
def setSpeed(self, speed: str):
self._speedLabel.setText(speed)
diff --git a/TIDALDL-PY/tidal_gui/view/mainView.py b/TIDALDL-PY/tidal_gui/view/mainView.py
index 0c6e9c4..94eee89 100644
--- a/TIDALDL-PY/tidal_gui/view/mainView.py
+++ b/TIDALDL-PY/tidal_gui/view/mainView.py
@@ -87,7 +87,7 @@ class MainView(FramelessWidget):
self._moveWidget.setFixedHeight(30)
self._moveWidget.setObjectName("BaseWidget")
return self._moveWidget
-
+
def __initContent__(self) -> QGridLayout:
self._searchView = None
self._settingsView = None
diff --git a/TIDALDL-PY/tidal_gui/view/searchView.py b/TIDALDL-PY/tidal_gui/view/searchView.py
index 64a12a1..36080f9 100644
--- a/TIDALDL-PY/tidal_gui/view/searchView.py
+++ b/TIDALDL-PY/tidal_gui/view/searchView.py
@@ -9,21 +9,20 @@
@Desc :
"""
import threading
-import tidal_dl.model
+
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QTabWidget
-from tidal_dl import Type
+import tidal_dl.model
+from tidal_dl import Type
+from tidal_dl.util import API, getDurationString
from tidal_gui.control.comboBox import ComboBox
from tidal_gui.control.label import Label
-from tidal_gui.control.layout import createHBoxLayout, createVBoxLayout
from tidal_gui.control.lineEdit import LineEdit
from tidal_gui.control.pushButton import PushButton
-from tidal_gui.control.tableView import TableView
from tidal_gui.control.tableWidget import TableWidget
from tidal_gui.style import ButtonStyle, LabelStyle
from tidal_gui.theme import getResourcePath
-from tidal_gui.tidalImp import tidalImp
class SearchView(QWidget):
@@ -57,7 +56,7 @@ class SearchView(QWidget):
self._prePageBtn = PushButton('', ButtonStyle.PrePage)
self._nextPageBtn = PushButton('', ButtonStyle.NextPage)
-
+
self._pageIndexEdit = LineEdit('')
self._pageIndexEdit.setAlignment(Qt.AlignCenter)
self._pageIndexEdit.setEnabled(False)
@@ -123,12 +122,12 @@ class SearchView(QWidget):
datas = []
for index, item in enumerate(items):
rowData = [str(index + 1 + indexOffset),
- QUrl(tidalImp.getCoverUrl(item.cover)),
- tidalImp.getFlag(item, Type.Album, True),
+ QUrl(API.getCoverUrl(item.cover)),
+ API.getFlag(item, Type.Album, True),
item.title,
item.artists[0].name,
str(item.releaseDate),
- tidalImp.getDurationString(item.duration)]
+ API.getDurationString(item.duration)]
datas.append(rowData)
elif stype == Type.Track:
@@ -136,11 +135,11 @@ class SearchView(QWidget):
datas = []
for index, item in enumerate(items):
rowData = [str(index + 1 + indexOffset),
- tidalImp.getFlag(item, Type.Track, True),
+ API.getFlag(item, Type.Track, True),
item.title,
item.album.title,
item.artists[0].name,
- tidalImp.getDurationString(item.duration)]
+ getDurationString(item.duration)]
datas.append(rowData)
elif stype == Type.Video:
@@ -148,11 +147,11 @@ class SearchView(QWidget):
datas = []
for index, item in enumerate(items):
rowData = [str(index + 1 + indexOffset),
- QUrl(tidalImp.getCoverUrl(item.imageID)),
- tidalImp.getFlag(item, Type.Video, True),
+ QUrl(API.getCoverUrl(item.imageID)),
+ API.getFlag(item, Type.Video, True),
item.title,
item.artists[0].name,
- tidalImp.getDurationString(item.duration)]
+ getDurationString(item.duration)]
datas.append(rowData)
elif stype == Type.Playlist:
@@ -160,10 +159,10 @@ class SearchView(QWidget):
datas = []
for index, item in enumerate(items):
rowData = [str(index + 1 + indexOffset),
- QUrl(tidalImp.getCoverUrl(item.squareImage)),
+ QUrl(API.getCoverUrl(item.squareImage)),
item.title,
'',
- tidalImp.getDurationString(item.duration)]
+ getDurationString(item.duration)]
datas.append(rowData)
for index, rowData in enumerate(datas):
diff --git a/TIDALDL-PY/tidal_gui/view/taskItemView.py b/TIDALDL-PY/tidal_gui/view/taskItemView.py
index 91b8ef6..489b7d2 100644
--- a/TIDALDL-PY/tidal_gui/view/taskItemView.py
+++ b/TIDALDL-PY/tidal_gui/view/taskItemView.py
@@ -8,14 +8,14 @@
@Contact : yaronhuang@foxmail.com
@Desc :
"""
+from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap
-from PyQt5.QtWidgets import QWidget, QHBoxLayout, QTableWidget, QVBoxLayout, QListView, QGridLayout
-from PyQt5.QtCore import Qt, QPoint
+from PyQt5.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout
from tidal_gui.control.label import Label
from tidal_gui.control.layout import createHBoxLayout, createVBoxLayout
-from tidal_gui.control.pushButton import PushButton
from tidal_gui.control.listWidget import ListWidget
+from tidal_gui.control.pushButton import PushButton
from tidal_gui.style import LabelStyle, ButtonStyle, ListWidgetStyle
@@ -25,12 +25,12 @@ class TaskItemView(QWidget):
self.__initView__()
self.setObjectName('TaskItemView')
self.setAttribute(Qt.WA_StyledBackground)
-
+
def __initView__(self):
layout = QVBoxLayout()
layout.addLayout(self.__initHead__(), Qt.AlignTop)
layout.addWidget(self.__initList__(), Qt.AlignTop)
-
+
self.setLayout(layout)
def __initHead__(self):
@@ -95,4 +95,3 @@ class TaskItemView(QWidget):
self._btnDelete.clicked.connect(func)
elif name == 'open':
self._btnOpen.clicked.connect(func)
-
diff --git a/TIDALDL-PY/tidal_gui/view/taskView.py b/TIDALDL-PY/tidal_gui/view/taskView.py
index 6fc9eb6..e4723bf 100644
--- a/TIDALDL-PY/tidal_gui/view/taskView.py
+++ b/TIDALDL-PY/tidal_gui/view/taskView.py
@@ -9,8 +9,9 @@
@Desc :
"""
from enum import Enum
+
from PyQt5.QtCore import Qt, QSize
-from PyQt5.QtWidgets import QWidget, QGridLayout, QListWidgetItem, QListView
+from PyQt5.QtWidgets import QWidget, QGridLayout, QListWidgetItem
from tidal_gui.control.label import Label
from tidal_gui.control.line import Line
@@ -18,7 +19,6 @@ from tidal_gui.control.listWidget import ListWidget
from tidal_gui.control.scrollWidget import ScrollWidget
from tidal_gui.style import LabelStyle, ListWidgetStyle
from tidal_gui.theme import getResourcePath
-from tidal_gui.view.taskItemView import TaskItemView
class TaskListType(Enum):
diff --git a/TIDALDL-PY/tidal_gui/viewModel/__init__.py b/TIDALDL-PY/tidal_gui/viewModel/__init__.py
index a4ecc95..77ced6e 100644
--- a/TIDALDL-PY/tidal_gui/viewModel/__init__.py
+++ b/TIDALDL-PY/tidal_gui/viewModel/__init__.py
@@ -8,4 +8,3 @@
@Contact : yaronhuang@foxmail.com
@Desc :
"""
-
diff --git a/TIDALDL-PY/tidal_gui/viewModel/downloadItemModel.py b/TIDALDL-PY/tidal_gui/viewModel/downloadItemModel.py
index 48b3c0b..365cf4f 100644
--- a/TIDALDL-PY/tidal_gui/viewModel/downloadItemModel.py
+++ b/TIDALDL-PY/tidal_gui/viewModel/downloadItemModel.py
@@ -9,27 +9,42 @@
@Desc :
"""
-import _thread
import os
-import time
+from abc import ABC, ABCMeta
from enum import Enum
+from pickle import FALSE
+from aigpy.downloadHelper import UserProgress
import aigpy.stringHelper
-from tidal_dl import Type
-from tidal_dl.model import Album, Track, Video, Playlist
-from tidal_gui.tidalImp import tidalImp
+from tidal_dl.model import Track
+from tidal_dl.util import downloadTrack, downloadVideo, getArtistsNames, setMetaData
from tidal_gui.view.downloadItemView import DownloadItemView
from tidal_gui.viewModel.viewModel import ViewModel
+
class DownloadStatus(Enum):
Wait = 0,
Running = 1,
Finish = 2,
Error = 3,
Cancel = 4,
-
+
+
_endStatus_ = [DownloadStatus.Finish, DownloadStatus.Error, DownloadStatus.Cancel]
+
+class Progress(UserProgress):
+ def __init__(self, model):
+ super().__init__()
+ self.model = model
+
+ def updateCurNum(self):
+ self.model.update(self.curNum, self.maxNum)
+
+ def updateMaxNum(self):
+ pass
+
+
class DownloadItemModel(ViewModel):
def __init__(self, index, data, basePath):
super(DownloadItemModel, self).__init__()
@@ -37,8 +52,9 @@ class DownloadItemModel(ViewModel):
self.data = data
self.basePath = basePath
self.isTrack = isinstance(data, Track)
+ self.progress = Progress(self)
self.__setStatus__(DownloadStatus.Wait)
-
+
if self.isTrack:
self.__initTrack__(index)
else:
@@ -50,7 +66,12 @@ class DownloadItemModel(ViewModel):
self.view.setAction(status.name)
else:
self.view.setAction(status.name + '-' + desc)
-
+
+ def __setErrStatus__(self, errmsg: str):
+ self.status = DownloadStatus.Error
+ self.view.setAction(self.status.name)
+ self.view.setErrmsg(errmsg)
+
def __initTrack__(self, index):
title = self.data.title
own = self.data.album.title
@@ -58,84 +79,36 @@ class DownloadItemModel(ViewModel):
def __initVideo__(self, index):
title = self.data.title
- own = tidalImp.getArtistsNames(self.data.artists)
+ own = getArtistsNames(self.data.artists)
self.view.setLabel(index, title, own)
+ def update(self, curNum, maxNum):
+ per = curNum * 100 / maxNum
+ self.view.setProgress(per)
+
def isInWait(self):
return self.status == DownloadStatus.Wait
-
+
def stopDownload(self):
if self.status not in _endStatus_:
self.__setStatus__(DownloadStatus.Cancel)
def retry(self):
self.__setStatus__(DownloadStatus.Wait)
-
+
def download(self):
self.__setStatus__(DownloadStatus.Running)
-
+
if self.isTrack:
- if not self.__dlTrack__():
- return
+ check, msg = downloadTrack(self.data, self.data.album, self.data.playlist, self.progress)
else:
- if not self.__dlVideo__():
- return
+ check, msg = downloadVideo(self.data)
+ if check is False:
+ self.__setErrStatus__(msg)
+ else:
+ self.__setStatus__(DownloadStatus.Finish)
+
self.view.setProgress(100)
self.__setStatus__(DownloadStatus.Finish)
-
- def __dlTrack__(self):
- try:
- track = self.data
- conf = tidalImp.getConfig()
-
- msg, stream = tidalImp.getStreamUrl(track.id, conf.audioQuality)
- if not aigpy.string.isNull(msg) or stream is None:
- self.__setStatus__(DownloadStatus.Error)
- return False
-
- tidalImp.getBasePath(track)
- path = tidalImp.getTackPath(self.basePath, track, stream)
- # # check exist
- # if conf.checkExist and tidalImp.__isNeedDownload__(path, stream.url) == False:
- # return True
-
- tool = aigpy.download.DownloadTool(path + '.part', [stream.url])
- check, err = tool.start(conf.showProgress)
-
- if not check:
- Printf.err("Download failed! " + aigpy.path.getFileName(path) + ' (' + str(err) + ')')
- return
- # encrypted -> decrypt and remove encrypted file
- if aigpy.string.isNull(stream.encryptionKey):
- os.replace(path + '.part', path)
- else:
- key, nonce = decrypt_security_token(stream.encryptionKey)
- decrypt_file(path + '.part', path, key, nonce)
- os.remove(path + '.part')
-
- path = __convertToM4a__(path, stream.codec)
-
- # contributors
- msg, contributors = API.getTrackContributors(track.id)
- msg, tidalLyrics = API.getLyrics(track.id)
-
- lyrics = '' if tidalLyrics is None else tidalLyrics.subtitles
- if conf.addLyrics and lyrics == '':
- lyrics = __getLyrics__(track.title, __getArtistsString__(track.artists), conf.lyricsServerProxy)
-
- if conf.lyricFile:
- if tidalLyrics is None:
- Printf.info(f'Failed to get lyrics from tidal!"{track.title}"')
- else:
- lrcPath = path.rsplit(".", 1)[0] + '.lrc'
- aigpy.fileHelper.write(lrcPath, tidalLyrics.subtitles, 'w')
-
- __setMetaData__(track, album, path, contributors, lyrics)
- Printf.success(aigpy.path.getFileName(path))
- except Exception as e:
- Printf.err("Download failed! " + track.title + ' (' + str(e) + ')')
-
- def __dlVideo__(self):
- pass
diff --git a/TIDALDL-PY/tidal_gui/viewModel/loginModel.py b/TIDALDL-PY/tidal_gui/viewModel/loginModel.py
index 9e6058a..300a42b 100644
--- a/TIDALDL-PY/tidal_gui/viewModel/loginModel.py
+++ b/TIDALDL-PY/tidal_gui/viewModel/loginModel.py
@@ -9,15 +9,15 @@
@Desc :
"""
import _thread
-import time
import webbrowser
-from PyQt5.QtCore import QTimer, pyqtSignal, QObject
+from PyQt5.QtCore import pyqtSignal
+from tidal_dl.util import API, loginByConfig, loginByWeb
-from tidal_gui.tidalImp import tidalImp
from tidal_gui.view.loginView import LoginView
from tidal_gui.viewModel.viewModel import ViewModel
+
class LoginModel(ViewModel):
SIGNAL_LOGIN_SUCCESS = pyqtSignal()
@@ -26,7 +26,7 @@ class LoginModel(ViewModel):
self.view = LoginView()
self.view.connectConfirmButton(self.__openWeb__)
self.SIGNAL_REFRESH_VIEW.connect(self.__refresh__)
-
+
def __refresh__(self, stype: str, text: str):
if stype == "userCode":
self.view.setDeviceCode(text)
@@ -37,11 +37,11 @@ class LoginModel(ViewModel):
self.view.hideEnterView()
self.view.setMsg(text)
- def login(self, useConfig = True):
+ def login(self, useConfig=True):
self.SIGNAL_REFRESH_VIEW.emit('showMsg', "LOGIN...")
def __thread_login__(model: LoginModel, useConfig: bool):
- if useConfig and tidalImp.loginByConfig():
+ if useConfig and loginByConfig():
model.SIGNAL_LOGIN_SUCCESS.emit()
return
model.getDeviceCode()
@@ -52,9 +52,9 @@ class LoginModel(ViewModel):
self.SIGNAL_REFRESH_VIEW.emit('showMsg', "GET DEVICE-CODE...")
def __thread_getCode__(model: LoginModel):
- msg, check = tidalImp.getDeviceCode()
+ msg, check = API.getDeviceCode()
if check:
- model.SIGNAL_REFRESH_VIEW.emit('userCode', tidalImp.key.userCode)
+ model.SIGNAL_REFRESH_VIEW.emit('userCode', API.key.userCode)
else:
model.SIGNAL_REFRESH_VIEW.emit('showMsg', msg)
@@ -62,10 +62,10 @@ class LoginModel(ViewModel):
def __openWeb__(self):
self.view.enableConfirmButton(False)
- webbrowser.open('http://link.tidal.com/' + tidalImp.key.userCode, new=0, autoraise=True)
+ webbrowser.open('http://link.tidal.com/' + API.key.userCode, new=0, autoraise=True)
def __thread_waitLogin__(model: LoginModel):
- if tidalImp.loginByWeb():
+ if loginByWeb():
model.SIGNAL_LOGIN_SUCCESS.emit()
else:
model.getDeviceCode()
diff --git a/TIDALDL-PY/tidal_gui/viewModel/mainModel.py b/TIDALDL-PY/tidal_gui/viewModel/mainModel.py
index e6bd947..53a437e 100644
--- a/TIDALDL-PY/tidal_gui/viewModel/mainModel.py
+++ b/TIDALDL-PY/tidal_gui/viewModel/mainModel.py
@@ -37,17 +37,17 @@ class MainModel(ViewModel):
self.view.setAboutView(self.aboutModel.view)
self.view.showPage()
-
+
downloadImp.setTaskModel(self.taskModel)
downloadImp.start()
-
+
def uninit(self):
self.taskModel.stopDownloadItem()
downloadImp.stop()
- def show(self, relogin:bool = False):
+ def show(self, relogin: bool = False):
self.view.hide()
- self.loginModel.login(bool(1-relogin))
+ self.loginModel.login(bool(1 - relogin))
self.loginModel.show()
def __loginSuccess__(self):
diff --git a/TIDALDL-PY/tidal_gui/viewModel/searchModel.py b/TIDALDL-PY/tidal_gui/viewModel/searchModel.py
index 9e35ee0..939ba06 100644
--- a/TIDALDL-PY/tidal_gui/viewModel/searchModel.py
+++ b/TIDALDL-PY/tidal_gui/viewModel/searchModel.py
@@ -12,13 +12,12 @@ import _thread
import threading
import aigpy.stringHelper
-import tidal_dl
from PyQt5.QtCore import pyqtSignal
from aigpy.modelHelper import ModelBase
-from tidal_dl import Type
-from tidal_dl.model import Album, SearchResult
-from tidal_gui.tidalImp import tidalImp
+import tidal_dl
+from tidal_dl import Type
+from tidal_dl.util import API, getAudioQualityList, getVideoQualityList
from tidal_gui.view.searchView import SearchView
from tidal_gui.viewModel.viewModel import ViewModel
@@ -33,8 +32,8 @@ class SearchModel(ViewModel):
self.view = SearchView()
self.view.setPageIndex(1, 1)
- self.view.setTrackQualityItems(tidalImp.getAudioQualityList())
- self.view.setVideoQualityItems(tidalImp.getVideoQualityList())
+ self.view.setTrackQualityItems(getAudioQualityList())
+ self.view.setVideoQualityItems(getVideoQualityList())
self.view.connectButton('search', self.__search__)
self.view.connectButton('prePage', self.__searchPre__)
self.view.connectButton('nextPage', self.__searchNext__)
@@ -76,7 +75,7 @@ class SearchModel(ViewModel):
limit = 20
offset = (index - 1) * limit
stype = tidal_dl.Type(typeIndex)
- msg, model._resultData = tidalImp.search(searchText, stype, offset, limit)
+ msg, model._resultData = API.search(searchText, stype, offset, limit)
if not aigpy.stringHelper.isNull(msg):
model.SIGNAL_REFRESH_VIEW.emit('setSearchErrmsg', msg)
diff --git a/TIDALDL-PY/tidal_gui/viewModel/settingsModel.py b/TIDALDL-PY/tidal_gui/viewModel/settingsModel.py
index 6e7d912..c6c3440 100644
--- a/TIDALDL-PY/tidal_gui/viewModel/settingsModel.py
+++ b/TIDALDL-PY/tidal_gui/viewModel/settingsModel.py
@@ -20,10 +20,10 @@ class SettingsModel(ViewModel):
self.view.connectButton('Logout', self.__logout__)
self.view.connectButton('Cancel', self.__cancel__)
self.view.connectButton('OK', self.__ok__)
-
+
def __logout__(self):
self._parent.show(True)
-
+
def __cancel__(self):
pass
diff --git a/TIDALDL-PY/tidal_gui/viewModel/taskItemModel.py b/TIDALDL-PY/tidal_gui/viewModel/taskItemModel.py
index a383929..623a6e8 100644
--- a/TIDALDL-PY/tidal_gui/viewModel/taskItemModel.py
+++ b/TIDALDL-PY/tidal_gui/viewModel/taskItemModel.py
@@ -10,17 +10,16 @@
"""
import _thread
import os
+import time
import aigpy.stringHelper
+
from tidal_dl import Type
from tidal_dl.model import Album, Track, Video, Playlist
-
-from tidal_gui.tidalImp import tidalImp
+from tidal_dl.util import API, getArtistsNames, getBasePath, getDurationString
from tidal_gui.view.taskItemView import TaskItemView
-from tidal_gui.viewModel.viewModel import ViewModel
from tidal_gui.viewModel.downloadItemModel import DownloadItemModel
-from PyQt5.QtWidgets import QListWidget, QListWidgetItem, QWidget, QScrollArea, QVBoxLayout, QPushButton
-import time
+from tidal_gui.viewModel.viewModel import ViewModel
class TaskItemModel(ViewModel):
@@ -47,11 +46,11 @@ class TaskItemModel(ViewModel):
self.SIGNAL_REFRESH_VIEW.connect(self.__refresh__)
- def __refresh__(self, stype: str, text: str):
+ def __refresh__(self, stype: str, obj):
if stype == "setPic":
- self.view.setPic(text)
+ self.view.setPic(obj)
elif stype == "addListItem":
- for index, item in enumerate(text):
+ for index, item in enumerate(obj):
downItem = DownloadItemModel(index + 1, item, self.path)
self.view.addListItem(downItem.view)
self.downloadModelList.append(downItem)
@@ -75,24 +74,29 @@ class TaskItemModel(ViewModel):
os.startfile(self.path)
def __initAlbum__(self, data: Album):
- self.path = tidalImp.getBasePath(data)
-
+ self.path = getBasePath(data)
+
title = data.title
- desc = f"by {tidalImp.getArtistsNames(data.artists)} " \
- f"{tidalImp.getDurationString(data.duration)} " \
+ desc = f"by {getArtistsNames(data.artists)} " \
+ f"{getDurationString(data.duration)} " \
f"Track-{data.numberOfTracks} " \
f"Video-{data.numberOfVideos}"
self.view.setLabel(title, desc)
def __thread_func__(model: TaskItemModel, album: Album):
- cover = tidalImp.getCoverData(album.cover, '1280', '1280')
+ cover = API.getCoverData(album.cover, '1280', '1280')
model.SIGNAL_REFRESH_VIEW.emit('setPic', cover)
- msg, tracks, videos = tidalImp.getItems(album.id, Type.Album)
+ msg, tracks, videos = API.getItems(album.id, Type.Album)
if not aigpy.stringHelper.isNull(msg):
model.view.setErrmsg(msg)
return
+ for item in tracks:
+ item.album = album
+ for item in videos:
+ item.album = album
+
model.SIGNAL_REFRESH_VIEW.emit('addListItem', tracks + videos)
print('__initAlbum__')
time.sleep(1)
diff --git a/TIDALDL-PY/tidal_gui/viewModel/taskModel.py b/TIDALDL-PY/tidal_gui/viewModel/taskModel.py
index e83e3c3..13bc217 100644
--- a/TIDALDL-PY/tidal_gui/viewModel/taskModel.py
+++ b/TIDALDL-PY/tidal_gui/viewModel/taskModel.py
@@ -8,40 +8,36 @@
@Contact : yaronhuang@foxmail.com
@Desc :
"""
-from PyQt5.QtWidgets import QPushButton, QCheckBox, QWidget
-from tidal_dl import Type
from tidal_dl.model import Album, Artist
-
-from tidal_gui.control.layout import createHBoxLayout
from tidal_gui.view.taskView import TaskView, TaskListType
+from tidal_gui.viewModel.downloadItemModel import DownloadItemModel
from tidal_gui.viewModel.taskItemModel import TaskItemModel
from tidal_gui.viewModel.viewModel import ViewModel
-from tidal_gui.viewModel.downloadItemModel import DownloadItemModel
class TaskModel(ViewModel):
def __init__(self):
super(TaskModel, self).__init__()
self.view = TaskView()
-
+
self._listMap = {}
for item in map(lambda typeItem: typeItem.name, TaskListType):
self._listMap[item] = []
-
+
self.test()
-
+
def addTaskItem(self, data):
item = TaskItemModel(data)
self._listMap[TaskListType.Download.name].append(item)
self.view.addItemView(TaskListType.Download, item.view)
-
+
def getWaitDownloadItem(self) -> DownloadItemModel:
for item in self._listMap[TaskListType.Download.name]:
for downItem in item.downloadModelList:
if downItem.isInWait():
return downItem
return None
-
+
def stopDownloadItem(self):
for item in self._listMap[TaskListType.Download.name]:
for downItem in item.downloadModelList:
diff --git a/TIDALDL-PY/tidal_gui/viewModel/viewModel.py b/TIDALDL-PY/tidal_gui/viewModel/viewModel.py
index 356d6e9..7197534 100644
--- a/TIDALDL-PY/tidal_gui/viewModel/viewModel.py
+++ b/TIDALDL-PY/tidal_gui/viewModel/viewModel.py
@@ -9,12 +9,12 @@
@Desc :
"""
from PyQt5.QtCore import QObject
-from PyQt5.QtCore import QTimer, pyqtSignal
+from PyQt5.QtCore import pyqtSignal
class ViewModel(QObject):
SIGNAL_REFRESH_VIEW = pyqtSignal(str, object)
-
+
def __init__(self):
super(ViewModel, self).__init__()
self.view = None