diff --git a/TIDALDL-PY/exe/tidal-dl.exe b/TIDALDL-PY/exe/tidal-dl.exe index e3e56a7..2d30df3 100644 Binary files a/TIDALDL-PY/exe/tidal-dl.exe and b/TIDALDL-PY/exe/tidal-dl.exe differ diff --git a/TIDALDL-PY/guiStatic.in b/TIDALDL-PY/guiStatic.in new file mode 100644 index 0000000..116c12d --- /dev/null +++ b/TIDALDL-PY/guiStatic.in @@ -0,0 +1,3 @@ +include tidal_gui/resource/themeDefault.qss +include tidal_gui/resource/svg/*.svg +include tidal_gui/resource/svg/*/*.svg diff --git a/TIDALDL-PY/requirements.txt b/TIDALDL-PY/requirements.txt index 72d4061..455272c 100644 --- a/TIDALDL-PY/requirements.txt +++ b/TIDALDL-PY/requirements.txt @@ -4,5 +4,5 @@ prettytable==0.7.2 mutagen==1.45.1 psutil==5.7.3 pycryptodome==3.9.9 -aigpy==2021.9.10.3 +aigpy==2022.01.18.2 lyricsgenius==3.0.1 diff --git a/TIDALDL-PY/setup-gui.py b/TIDALDL-PY/setup-gui.py index 60d14b3..d5b55c4 100644 --- a/TIDALDL-PY/setup-gui.py +++ b/TIDALDL-PY/setup-gui.py @@ -1,16 +1,16 @@ from setuptools import setup, find_packages setup( name = 'tidal-gui', - version = '1.0.0.4', + version = '2022.01.18.3', license = "Apache2", - description = "Tidal Music Downloader.", + description = "Tidal Music Downloader-GUI.", author = 'YaronH', author_email = "yaronhuang@foxmail.com", - packages = find_packages(), + packages=find_packages(exclude=['tidal_dl*']), include_package_data = True, platforms = "any", - install_requires=["aigpy", "PyQt5", "requests>=2.22.0", "pycryptodome", "pydub", "prettytable", "lyricsgenius"], + install_requires=["tidal-dl", "PyQt5"], entry_points={'console_scripts': [ 'tidal-gui = tidal_gui:main', ]} ) diff --git a/TIDALDL-PY/setup.py b/TIDALDL-PY/setup.py index 32e87eb..54ff200 100644 --- a/TIDALDL-PY/setup.py +++ b/TIDALDL-PY/setup.py @@ -10,9 +10,10 @@ setup( author='YaronH', author_email="yaronhuang@foxmail.com", - packages=find_packages(), - include_package_data=True, + packages=find_packages(exclude=['tidal_gui*']), + include_package_data=False, platforms="any", - install_requires=["aigpy>=2021.12.10.1", "requests>=2.22.0", "pycryptodome", "pydub", "prettytable", "lyricsgenius"], + install_requires=["aigpy>=2022.01.18.2", "requests>=2.22.0", + "pycryptodome", "pydub", "prettytable", "lyricsgenius"], entry_points={'console_scripts': ['tidal-dl = tidal_dl:main', ]} ) diff --git a/TIDALDL-PY/tidal_dl/printf.py b/TIDALDL-PY/tidal_dl/printf.py index b34327e..e0a3212 100644 --- a/TIDALDL-PY/tidal_dl/printf.py +++ b/TIDALDL-PY/tidal_dl/printf.py @@ -30,7 +30,7 @@ __LOGO__ = ''' https://github.com/yaronzz/Tidal-Media-Downloader ''' -VERSION = '2022.01.11.1' +VERSION = '2022.01.18.2' class Printf(object): diff --git a/TIDALDL-PY/tidal_dl/util.py b/TIDALDL-PY/tidal_dl/util.py index 8420ecd..c4e70fe 100644 --- a/TIDALDL-PY/tidal_dl/util.py +++ b/TIDALDL-PY/tidal_dl/util.py @@ -301,10 +301,32 @@ def encrypted(stream, srcPath, descPath): def getAudioQualityList(): return map(lambda quality: quality.name, tidal_dl.enums.AudioQuality) +def getCurAudioQuality(): + return CONF.audioQuality.name + +def setCurAudioQuality(text): + if CONF.audioQuality.name == text: + return + for item in tidal_dl.enums.AudioQuality: + if item.name == text: + CONF.audioQuality = item + break + Settings.save(CONF) def getVideoQualityList(): return map(lambda quality: quality.name, tidal_dl.enums.VideoQuality) +def getCurVideoQuality(): + return CONF.videoQuality.name + +def setCurVideoQuality(text): + if CONF.videoQuality.name == text: + return + for item in tidal_dl.enums.VideoQuality: + if item.name == text: + CONF.videoQuality = item + break + Settings.save(CONF) def skip(path, url): if CONF.checkExist and isNeedDownload(path, url) is False: diff --git a/TIDALDL-PY/tidal_gui/control/framelessWidget.py b/TIDALDL-PY/tidal_gui/control/framelessWidget.py index 6e0118b..b71fc34 100644 --- a/TIDALDL-PY/tidal_gui/control/framelessWidget.py +++ b/TIDALDL-PY/tidal_gui/control/framelessWidget.py @@ -66,7 +66,10 @@ class FramelessWidget(QWidget): def __clickInValidMoveWidget__(self, x=-1, y=-1) -> bool: if self.validMoveWidget is None: - return True + return False + if self.clickPos is None: + return False + if x == -1 and y == -1: x = self.clickPos.x() y = self.clickPos.y() diff --git a/TIDALDL-PY/tidal_gui/control/scrollWidget.py b/TIDALDL-PY/tidal_gui/control/scrollWidget.py index 19deb2c..ac2f538 100644 --- a/TIDALDL-PY/tidal_gui/control/scrollWidget.py +++ b/TIDALDL-PY/tidal_gui/control/scrollWidget.py @@ -29,6 +29,10 @@ class ScrollWidget(QScrollArea): def addWidgetItem(self, widget: QWidget): self._layout.insertWidget(self._numWidget, widget) self._numWidget += 1 + + def delWidgetItem(self, widget: QWidget): + self._layout.removeWidget(widget) + self._numWidget -= 1 def resizeEvent(self, e: QResizeEvent): super().resizeEvent(e) diff --git a/TIDALDL-PY/tidal_gui/view/mainView.py b/TIDALDL-PY/tidal_gui/view/mainView.py index 94eee89..bdd0161 100644 --- a/TIDALDL-PY/tidal_gui/view/mainView.py +++ b/TIDALDL-PY/tidal_gui/view/mainView.py @@ -74,8 +74,8 @@ class MainView(FramelessWidget): layout.addWidget(self._searchBtn) layout.addWidget(self._taskBtn) layout.addStretch(1) - layout.addWidget(self._settingsBtn) - layout.addWidget(self._aboutBtn) + # layout.addWidget(self._settingsBtn) + # layout.addWidget(self._aboutBtn) widget = QWidget() widget.setLayout(layout) @@ -119,6 +119,5 @@ class MainView(FramelessWidget): self.__setContentPage__(view, PageType.Settings) def setAboutView(self, view: QWidget): - # self.__setContentPage__(view, PageType.About) self._pages[PageType.About] = view self._pages[PageType.About].hide() diff --git a/TIDALDL-PY/tidal_gui/view/searchView.py b/TIDALDL-PY/tidal_gui/view/searchView.py index c6d2391..c4a44ef 100644 --- a/TIDALDL-PY/tidal_gui/view/searchView.py +++ b/TIDALDL-PY/tidal_gui/view/searchView.py @@ -198,11 +198,15 @@ class SearchView(QWidget): return -1 return array[0].row() - def setTrackQualityItems(self, items: list): + def setTrackQualityItems(self, items: list, curItem = None): self._trackQualityComboBox.setItems(items) + if curItem is not None: + self._trackQualityComboBox.setCurrentText(curItem) - def setVideoQualityItems(self, items: list): + def setVideoQualityItems(self, items: list, curItem=None): self._videoQualityComboBox.setItems(items) + if curItem is not None: + self._videoQualityComboBox.setCurrentText(curItem) def getTrackQualityText(self): return self._trackQualityComboBox.currentText() @@ -222,3 +226,9 @@ class SearchView(QWidget): def connectTab(self, func): self._tabWidget.currentChanged.connect(func) + + def connectQualityComboBox(self, name: str, func): + if name == 'track': + self._trackQualityComboBox.currentIndexChanged.connect(func) + else: + self._videoQualityComboBox.currentIndexChanged.connect(func) diff --git a/TIDALDL-PY/tidal_gui/view/taskView.py b/TIDALDL-PY/tidal_gui/view/taskView.py index e4723bf..7e7cbc4 100644 --- a/TIDALDL-PY/tidal_gui/view/taskView.py +++ b/TIDALDL-PY/tidal_gui/view/taskView.py @@ -21,7 +21,7 @@ from tidal_gui.style import LabelStyle, ListWidgetStyle from tidal_gui.theme import getResourcePath -class TaskListType(Enum): +class TaskStatus(Enum): Download = 0, Complete = 1, Error = 2, @@ -33,18 +33,18 @@ class TaskView(QWidget): self._listMap = {} self._pageMap = {} - for item in map(lambda typeItem: typeItem.name, TaskListType): + for item in map(lambda typeItem: typeItem.name, TaskStatus): self._listMap[item] = ScrollWidget() self._pageMap[item] = QWidget() self.__initView__() self._listTab.setCurrentRow(0) - self._pageMap[TaskListType.Download.name].show() + self._pageMap[TaskStatus.Download.name].show() def __initView__(self): grid = QGridLayout(self) grid.addLayout(self.__initLefTab__(), 0, 0, Qt.AlignLeft) - for item in map(lambda typeItem: typeItem.name, TaskListType): + for item in map(lambda typeItem: typeItem.name, TaskStatus): grid.addWidget(self.__createContent__(item), 0, 1) def __initLefTab__(self): @@ -52,9 +52,9 @@ class TaskView(QWidget): self._listTab.setIconSize(QSize(20, 20)) iconPath = getResourcePath() + "/svg/taskTab/" - self._listTab.addIConTextItem(iconPath + 'download.svg', TaskListType.Download.name) - self._listTab.addIConTextItem(iconPath + 'complete.svg', TaskListType.Complete.name) - self._listTab.addIConTextItem(iconPath + 'error.svg', TaskListType.Error.name) + self._listTab.addIConTextItem(iconPath + 'download.svg', TaskStatus.Download.name) + self._listTab.addIConTextItem(iconPath + 'complete.svg', TaskStatus.Complete.name) + self._listTab.addIConTextItem(iconPath + 'error.svg', TaskStatus.Error.name) self._listTab.itemClicked.connect(self.__tabItemChanged__) @@ -83,5 +83,8 @@ class TaskView(QWidget): else: self._pageMap[name].hide() - def addItemView(self, stype: TaskListType, view): + def addItemView(self, stype: TaskStatus, view): self._listMap[stype.name].addWidgetItem(view) + + def delItemView(self, stype: TaskStatus, view): + self._listMap[stype.name].delWidgetItem(view) diff --git a/TIDALDL-PY/tidal_gui/viewModel/downloadItemModel.py b/TIDALDL-PY/tidal_gui/viewModel/downloadItemModel.py index ac0cce7..ca604ae 100644 --- a/TIDALDL-PY/tidal_gui/viewModel/downloadItemModel.py +++ b/TIDALDL-PY/tidal_gui/viewModel/downloadItemModel.py @@ -16,6 +16,7 @@ from enum import Enum from pickle import FALSE from aigpy.downloadHelper import UserProgress import aigpy.stringHelper +from PyQt5.QtCore import pyqtSignal from tidal_dl.model import Track from tidal_dl.util import downloadTrack, downloadVideo, getArtistsNames, setMetaData @@ -60,6 +61,8 @@ class Progress(UserProgress): class DownloadItemModel(ViewModel): + SIGNAL_END = pyqtSignal(DownloadStatus) + def __init__(self, index, data, basePath): super(DownloadItemModel, self).__init__() self.view = DownloadItemView() @@ -94,11 +97,13 @@ class DownloadItemModel(ViewModel): self.view.setAction(status.name) else: self.view.setAction(status.name + '-' + desc) + if status in _endStatus_: + self.SIGNAL_END.emit(status) + def __setErrStatus__(self, errmsg: str): - self.status = DownloadStatus.ERROR - self.view.setAction(self.status.name) self.view.setErrmsg(errmsg) + self.__setStatus__(DownloadStatus.ERROR) def __initTrack__(self, index): title = self.data.title diff --git a/TIDALDL-PY/tidal_gui/viewModel/mainModel.py b/TIDALDL-PY/tidal_gui/viewModel/mainModel.py index 53a437e..4c9b63d 100644 --- a/TIDALDL-PY/tidal_gui/viewModel/mainModel.py +++ b/TIDALDL-PY/tidal_gui/viewModel/mainModel.py @@ -42,7 +42,7 @@ class MainModel(ViewModel): downloadImp.start() def uninit(self): - self.taskModel.stopDownloadItem() + self.taskModel.uninit() downloadImp.stop() def show(self, relogin: bool = False): diff --git a/TIDALDL-PY/tidal_gui/viewModel/searchModel.py b/TIDALDL-PY/tidal_gui/viewModel/searchModel.py index 939ba06..4f8c615 100644 --- a/TIDALDL-PY/tidal_gui/viewModel/searchModel.py +++ b/TIDALDL-PY/tidal_gui/viewModel/searchModel.py @@ -17,7 +17,7 @@ from aigpy.modelHelper import ModelBase import tidal_dl from tidal_dl import Type -from tidal_dl.util import API, getAudioQualityList, getVideoQualityList +from tidal_dl.util import API, getAudioQualityList, getCurAudioQuality, getCurVideoQuality, getVideoQualityList, setCurAudioQuality, setCurVideoQuality from tidal_gui.view.searchView import SearchView from tidal_gui.viewModel.viewModel import ViewModel @@ -32,12 +32,14 @@ class SearchModel(ViewModel): self.view = SearchView() self.view.setPageIndex(1, 1) - self.view.setTrackQualityItems(getAudioQualityList()) - self.view.setVideoQualityItems(getVideoQualityList()) + self.view.setTrackQualityItems(getAudioQualityList(), getCurAudioQuality()) + self.view.setVideoQualityItems(getVideoQualityList(), getCurVideoQuality()) self.view.connectButton('search', self.__search__) self.view.connectButton('prePage', self.__searchPre__) self.view.connectButton('nextPage', self.__searchNext__) self.view.connectButton('download', self.__download__) + self.view.connectQualityComboBox('track', self.__changeAudioQuality__) + self.view.connectQualityComboBox('video', self.__changeVideoQuality__) self.view.connectTab(lambda: self.__search__(0)) self.SIGNAL_REFRESH_VIEW.connect(self.__refresh__) @@ -149,3 +151,9 @@ class SearchModel(ViewModel): return self.SIGNAL_ADD_TASKITEM.emit(items[index]) + + def __changeAudioQuality__(self): + setCurAudioQuality(self.view.getTrackQualityText()) + + def __changeVideoQuality__(self): + setCurVideoQuality(self.view.getVideoQualityText()) diff --git a/TIDALDL-PY/tidal_gui/viewModel/taskItemModel.py b/TIDALDL-PY/tidal_gui/viewModel/taskItemModel.py index 66756ff..45f7db6 100644 --- a/TIDALDL-PY/tidal_gui/viewModel/taskItemModel.py +++ b/TIDALDL-PY/tidal_gui/viewModel/taskItemModel.py @@ -9,6 +9,8 @@ @Desc : """ import _thread +from asyncio import tasks +from enum import Enum import os import time @@ -18,7 +20,8 @@ from tidal_dl import Type from tidal_dl.model import Album, Track, Video, Playlist from tidal_dl.util import API, getArtistsNames, getBasePath, getDurationString from tidal_gui.view.taskItemView import TaskItemView -from tidal_gui.viewModel.downloadItemModel import DownloadItemModel +from tidal_gui.view.taskView import TaskStatus +from tidal_gui.viewModel.downloadItemModel import DownloadItemModel, DownloadStatus from tidal_gui.viewModel.viewModel import ViewModel @@ -46,6 +49,21 @@ class TaskItemModel(ViewModel): self.SIGNAL_REFRESH_VIEW.connect(self.__refresh__) + def getTaskStatus(self) -> TaskStatus: + if len(self.downloadModelList) <= 0: + return TaskStatus.Download + + errorNum = 0 + for item in self.downloadModelList: + if item.status in [DownloadStatus.WAIT, DownloadStatus.RUNNING, DownloadStatus.CANCEL]: + return TaskStatus.Download + elif item.status == DownloadStatus.ERROR: + errorNum += 1 + + if errorNum > 0: + return TaskStatus.Error + return TaskStatus.Complete + def __refresh__(self, stype: str, obj): if stype == "setPic": self.view.setPic(obj) diff --git a/TIDALDL-PY/tidal_gui/viewModel/taskModel.py b/TIDALDL-PY/tidal_gui/viewModel/taskModel.py index 13bc217..e0ee047 100644 --- a/TIDALDL-PY/tidal_gui/viewModel/taskModel.py +++ b/TIDALDL-PY/tidal_gui/viewModel/taskModel.py @@ -8,10 +8,12 @@ @Contact : yaronhuang@foxmail.com @Desc : """ +import threading +from PyQt5.QtCore import QTimer from tidal_dl.model import Album, Artist -from tidal_gui.view.taskView import TaskView, TaskListType +from tidal_gui.view.taskView import TaskView, TaskStatus from tidal_gui.viewModel.downloadItemModel import DownloadItemModel -from tidal_gui.viewModel.taskItemModel import TaskItemModel +from tidal_gui.viewModel.taskItemModel import TaskItemModel, TaskStatus from tidal_gui.viewModel.viewModel import ViewModel @@ -21,28 +23,45 @@ class TaskModel(ViewModel): self.view = TaskView() self._listMap = {} - for item in map(lambda typeItem: typeItem.name, TaskListType): + for item in map(lambda typeItem: typeItem.name, TaskStatus): self._listMap[item] = [] - self.test() + self._timer = QTimer(self) + self._timer.timeout.connect(self.__checkTaskStatus__) + self._timer.start(3000) + + # self.test() + + def __checkTaskStatus__(self): + for item in self._listMap[TaskStatus.Download.name][:]: + status = item.getTaskStatus() + if status == TaskStatus.Download: + continue + + self._listMap[TaskStatus.Download.name].remove(item) + self.view.delItemView(TaskStatus.Download, item.view) + + self._listMap[status.name].append(item) + self.view.addItemView(status, item.view) + + def uninit(self): + self._timer.stop() + for item in self._listMap[TaskStatus.Download.name]: + for downItem in item.downloadModelList: + downItem.stopDownload() def addTaskItem(self, data): item = TaskItemModel(data) - self._listMap[TaskListType.Download.name].append(item) - self.view.addItemView(TaskListType.Download, item.view) + self._listMap[TaskStatus.Download.name].append(item) + self.view.addItemView(TaskStatus.Download, item.view) def getWaitDownloadItem(self) -> DownloadItemModel: - for item in self._listMap[TaskListType.Download.name]: + for item in self._listMap[TaskStatus.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: - downItem.stopDownload() - def test(self): ar = Artist() ar.name = 'yaron' diff --git a/TIDALDL-PY/upload.sh b/TIDALDL-PY/upload.sh index 2995098..dfcc211 100644 --- a/TIDALDL-PY/upload.sh +++ b/TIDALDL-PY/upload.sh @@ -1,10 +1,11 @@ cd TIDALDL-PY rm -rf dist rm -rf build -rm -rf exe +rm -rf exe/tidal-dl.exe +rm MANIFEST.in rm -rf tidal_dl.egg-info +rm -rf tidal_gui.egg-info rm -rf tidal_dl_test.egg-info -mkdir exe # pack python setup.py sdist bdist_wheel @@ -28,13 +29,26 @@ twine upload dist/* + + + +cd TIDALDL-PY rm -rf dist -rm -rf build -rm -rf exe +rm -rf build +rm -rf exe/tidal-gui.exe +cp -rf guiStatic.in MANIFEST.in rm -rf tidal_dl.egg-info -python setup.py sdist bdist_wheel -pip uninstall -y tidal-dl -python setup.py install +rm -rf tidal_gui.egg-info +rm -rf tidal_dl_test.egg-info +# pack +python setup-gui.py sdist bdist_wheel +# creat exe file +pyinstaller -F tidal_gui/__init__.py +# rename exe name +mv dist/__init__.exe exe/tidal-gui.exe +pip uninstall -y tidal-gui +# upload +twine upload dist/*