From 69a7ff681374a6d7b7cc8d600d07068a93ed61cc Mon Sep 17 00:00:00 2001 From: Yaronzz Date: Tue, 18 Jan 2022 11:59:37 +0800 Subject: [PATCH] update gui --- TIDALDL-PY/tidal_dl/util.py | 11 +-- TIDALDL-PY/tidal_gui/control/listWidget.py | 3 + TIDALDL-PY/tidal_gui/control/scrollWidget.py | 15 +++- .../tidal_gui/resource/themeDefault.qss | 25 +++++++ TIDALDL-PY/tidal_gui/style.py | 2 + TIDALDL-PY/tidal_gui/view/downloadItemView.py | 52 +++++++++----- TIDALDL-PY/tidal_gui/view/searchView.py | 2 +- TIDALDL-PY/tidal_gui/view/taskItemView.py | 18 +++-- .../tidal_gui/viewModel/downloadItemModel.py | 69 ++++++++++++------- .../tidal_gui/viewModel/taskItemModel.py | 66 ++++++++++++++++-- 10 files changed, 201 insertions(+), 62 deletions(-) diff --git a/TIDALDL-PY/tidal_dl/util.py b/TIDALDL-PY/tidal_dl/util.py index 884fdf6..8420ecd 100644 --- a/TIDALDL-PY/tidal_dl/util.py +++ b/TIDALDL-PY/tidal_dl/util.py @@ -12,7 +12,7 @@ import logging import os import time -from urllib import request +import requests import aigpy import lyricsgenius @@ -318,7 +318,7 @@ def convert(srcPath, stream): return srcPath -def downloadTrack(track: Track, album=None, playlist=None, userProgress=None): +def downloadTrack(track: Track, album=None, playlist=None, userProgress=None, partSize=1048576): try: msg, stream = API.getStreamUrl(track.id, CONF.audioQuality) if not aigpy.string.isNull(msg) or stream is None: @@ -326,6 +326,8 @@ def downloadTrack(track: Track, album=None, playlist=None, userProgress=None): return False, msg if CONF.showTrackInfo: Printf.track(track, stream) + if userProgress is not None: + userProgress.updateStream(stream) path = getTrackPath(CONF, track, stream, album, playlist) # check exist @@ -337,6 +339,7 @@ def downloadTrack(track: Track, album=None, playlist=None, userProgress=None): logging.info("[DL Track] name=" + aigpy.path.getFileName(path) + "\nurl=" + stream.url) tool = aigpy.download.DownloadTool(path + '.part', [stream.url]) tool.setUserProgress(userProgress) + tool.setPartSize(partSize) check, err = tool.start(CONF.showProgress) if not check: Printf.err("Download failed! " + aigpy.path.getFileName(path) + ' (' + str(err) + ')') @@ -377,7 +380,7 @@ def downloadVideo(video: Video, album=None, playlist=None): path = getVideoPath(CONF, video, album, playlist) logging.info("[DL Video] name=" + aigpy.path.getFileName(path) + "\nurl=" + stream.m3u8Url) - m3u8content = request.get(stream.m3u8Url).content + m3u8content = requests.get(stream.m3u8Url).content if m3u8content is None: Printf.err(video.title + ' get m3u8 content failed.') return False, "Get m3u8 content failed" @@ -488,7 +491,7 @@ def getBasePath(model): if isinstance(model, tidal_dl.model.Playlist): return getPlaylistPath(CONF, model) if isinstance(model, tidal_dl.model.Track): - return getAlbumPath(CONF, model) + return getAlbumPath(CONF, model.album) if isinstance(model, tidal_dl.model.Video): filePath = getVideoPath(CONF, model, model.album, model.playlist) return aigpy.pathHelper.getDirName(filePath) diff --git a/TIDALDL-PY/tidal_gui/control/listWidget.py b/TIDALDL-PY/tidal_gui/control/listWidget.py index 3e8f6c5..2a2099b 100644 --- a/TIDALDL-PY/tidal_gui/control/listWidget.py +++ b/TIDALDL-PY/tidal_gui/control/listWidget.py @@ -27,3 +27,6 @@ class ListWidget(QListWidget): # item.setSizeHint(QSize(widget.width(), widget.height())) self.addItem(item) self.setItemWidget(item, widget) + + def setAdjustMode(self): + self.setResizeMode(QListWidget.Adjust) diff --git a/TIDALDL-PY/tidal_gui/control/scrollWidget.py b/TIDALDL-PY/tidal_gui/control/scrollWidget.py index 8daa827..19deb2c 100644 --- a/TIDALDL-PY/tidal_gui/control/scrollWidget.py +++ b/TIDALDL-PY/tidal_gui/control/scrollWidget.py @@ -8,14 +8,15 @@ @Contact : yaronhuang@foxmail.com @Desc : """ - +from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QWidget, QScrollArea, QVBoxLayout +from PyQt5.QtGui import QResizeEvent class ScrollWidget(QScrollArea): def __init__(self): super(ScrollWidget, self).__init__() - + self._numWidget = 0 self._layout = QVBoxLayout() self._layout.addStretch(1) @@ -26,4 +27,12 @@ class ScrollWidget(QScrollArea): self.setWidgetResizable(True) def addWidgetItem(self, widget: QWidget): - self._layout.insertWidget(0, widget) + self._layout.insertWidget(self._numWidget, widget) + self._numWidget += 1 + + def resizeEvent(self, e: QResizeEvent): + super().resizeEvent(e) + width = e.size().width() + if width > 0: + self._mainW.setMaximumWidth(width) + diff --git a/TIDALDL-PY/tidal_gui/resource/themeDefault.qss b/TIDALDL-PY/tidal_gui/resource/themeDefault.qss index c470a20..a58f642 100644 --- a/TIDALDL-PY/tidal_gui/resource/themeDefault.qss +++ b/TIDALDL-PY/tidal_gui/resource/themeDefault.qss @@ -82,6 +82,8 @@ QWidget#BaseWidget { background: var(--Color_Default); } + + QWidget#TaskItemView { background: var(--Color_Default); border-style: solid; @@ -543,6 +545,15 @@ QLabel#BoldLabel { font-weight: bold; } +QLabel#ItalicLabel { + font-style: italic; +} + +QLabel#TagLabel { + background: var(--Color_DangerPressed); +} + + /* QFrame */ QFrame#VLineQFrame { @@ -678,6 +689,20 @@ QListWidget#TaskContentListWidget::item:selected background: var(--Color_Default); } +QWidget#DownloadItemsWidget { + border-left: none; + border-right: none; + border-top: 1px solid var(--Color_Border); + border-bottom: none; + background: var(--Color_Default); + outline:none; +} + +QWidget#DownloadItemView { + border-style: none; + margin-top: 2px; + background: var(--Color_Default); +} QListWidget#DownloadItemsListWidget { diff --git a/TIDALDL-PY/tidal_gui/style.py b/TIDALDL-PY/tidal_gui/style.py index 27c5284..1ce6f91 100644 --- a/TIDALDL-PY/tidal_gui/style.py +++ b/TIDALDL-PY/tidal_gui/style.py @@ -50,6 +50,8 @@ class LabelStyle(Enum): SearchErr = 5, Icon = 6, Bold = 7, + Italic = 9, + Tag = 10 class ThemeStyle(Enum): diff --git a/TIDALDL-PY/tidal_gui/view/downloadItemView.py b/TIDALDL-PY/tidal_gui/view/downloadItemView.py index 46ffd83..53dd878 100644 --- a/TIDALDL-PY/tidal_gui/view/downloadItemView.py +++ b/TIDALDL-PY/tidal_gui/view/downloadItemView.py @@ -19,15 +19,21 @@ class DownloadItemView(QWidget): def __init__(self): super(DownloadItemView, self).__init__() self.__initView__() + self.setObjectName('DownloadItemView') + self.setAttribute(Qt.WA_StyledBackground) def __initView__(self): self._indexLabel = Label('1') + self._codecLabel = Label('', LabelStyle.Tag) self._titleLabel = Label('title', LabelStyle.Bold) - self._ownLabel = Label('own') + self._ownLabel = Label('own', LabelStyle.Italic) self._ownLabel.setMaximumWidth(200) - self._actionLabel = Label('') + self._actionLabel = Label('', LabelStyle.Italic) self._actionLabel.setFixedWidth(80) + self._errLabel = Label('') + self._errLabel.setVisible(False) + self._sizeLabel = Label('/') self._speedLabel = Label('') @@ -37,22 +43,28 @@ class DownloadItemView(QWidget): self._progress.setFixedWidth(300) self._progress.setRange(0, 100) + titleLayout = QHBoxLayout() + titleLayout.setSpacing(3) + titleLayout.setContentsMargins(0, 0, 0, 0) + titleLayout.addWidget(self._indexLabel, Qt.AlignLeft) + titleLayout.addWidget(self._titleLabel, Qt.AlignLeft) + titleLayout.addStretch(50) + titleLayout.addWidget(self._codecLabel, Qt.AlignRight) + titleLayout.addWidget(self._ownLabel, Qt.AlignRight) + + speedLayout = QHBoxLayout() + speedLayout.setSpacing(30) + speedLayout.addWidget(self._sizeLabel) + speedLayout.addWidget(self._speedLabel) + grid = QGridLayout(self) - grid.addWidget(self._indexLabel, 0, 0, Qt.AlignLeft | Qt.AlignVCenter) - grid.addWidget(self._titleLabel, 0, 1, Qt.AlignLeft | Qt.AlignVCenter) - grid.addWidget(self._ownLabel, 0, 2, Qt.AlignRight | Qt.AlignVCenter) - 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) + grid.setContentsMargins(0,0,0,0) + grid.setSpacing(2) + grid.addLayout(titleLayout, 0, 0, Qt.AlignLeft | Qt.AlignVCenter) + grid.addWidget(self._progress, 0, 1, Qt.AlignRight | Qt.AlignVCenter) + grid.addWidget(self._actionLabel, 0, 2, Qt.AlignRight | Qt.AlignVCenter) + grid.addWidget(self._errLabel, 1, 0, Qt.AlignLeft | Qt.AlignVCenter) + grid.addLayout(speedLayout, 1, 1, Qt.AlignLeft | Qt.AlignVCenter) def setLabel(self, index, title, own): self._indexLabel.setText(str(index)) @@ -61,6 +73,7 @@ class DownloadItemView(QWidget): def setErrmsg(self, msg): self._errLabel.setText(msg) + self._errLabel.setVisible(len(msg) > 0) def setAction(self, msg): self._actionLabel.setText(msg) @@ -70,7 +83,10 @@ class DownloadItemView(QWidget): pass def setSize(self, curSize: str, totalSize: str): - self._sizeLabel.setText(f'{curSize}/{totalSize}') + self._sizeLabel.setText(f'{curSize} / {totalSize}') def setSpeed(self, speed: str): self._speedLabel.setText(speed) + + def setCodec(self, codec: str): + self._codecLabel.setText(codec) diff --git a/TIDALDL-PY/tidal_gui/view/searchView.py b/TIDALDL-PY/tidal_gui/view/searchView.py index 36080f9..c6d2391 100644 --- a/TIDALDL-PY/tidal_gui/view/searchView.py +++ b/TIDALDL-PY/tidal_gui/view/searchView.py @@ -127,7 +127,7 @@ class SearchView(QWidget): item.title, item.artists[0].name, str(item.releaseDate), - API.getDurationString(item.duration)] + getDurationString(item.duration)] datas.append(rowData) elif stype == Type.Track: diff --git a/TIDALDL-PY/tidal_gui/view/taskItemView.py b/TIDALDL-PY/tidal_gui/view/taskItemView.py index 489b7d2..6dd31aa 100644 --- a/TIDALDL-PY/tidal_gui/view/taskItemView.py +++ b/TIDALDL-PY/tidal_gui/view/taskItemView.py @@ -29,7 +29,7 @@ class TaskItemView(QWidget): def __initView__(self): layout = QVBoxLayout() layout.addLayout(self.__initHead__(), Qt.AlignTop) - layout.addWidget(self.__initList__(), Qt.AlignTop) + layout.addWidget(self.__initList__()) self.setLayout(layout) @@ -40,8 +40,10 @@ class TaskItemView(QWidget): self._btnOpen = PushButton('', ButtonStyle.TaskOpen) self._btnExpand = PushButton('', ButtonStyle.TaskExpand) self._btnExpand.clicked.connect(self.__expandClick__) - btnLayout = createHBoxLayout([self._btnRetry, self._btnCancel, - self._btnDelete, self._btnOpen, + btnLayout = createHBoxLayout([self._btnRetry, + self._btnCancel, + #self._btnDelete, + self._btnOpen, self._btnExpand]) self._titleLabel = Label('', LabelStyle.PageTitle) @@ -49,6 +51,8 @@ class TaskItemView(QWidget): self._errLabel = Label() self._errLabel.hide() labelLayout = createVBoxLayout([self._titleLabel, self._descLabel, self._errLabel]) + labelLayout.insertStretch(0, 1) + labelLayout.addStretch(1) self._picLabel = Label('', LabelStyle.Icon) self._picLabel.setMinimumHeight(64) @@ -61,8 +65,10 @@ class TaskItemView(QWidget): return headLayout def __initList__(self): - self._list = ListWidget(ListWidgetStyle.DownloadItems) - self._list.setMinimumHeight(200) + self._list = QWidget() + self._list.setObjectName("DownloadItemsWidget") + self._listLayout = QVBoxLayout(self._list) + self._listLayout.setSpacing(0) return self._list def setLabel(self, title, desc): @@ -78,7 +84,7 @@ class TaskItemView(QWidget): self._picLabel.setPixmap(pic.scaled(64, 64)) def addListItem(self, view): - self._list.addWidgetItem(view) + self._listLayout.addWidget(view) def __expandClick__(self): if self._list.isHidden(): diff --git a/TIDALDL-PY/tidal_gui/viewModel/downloadItemModel.py b/TIDALDL-PY/tidal_gui/viewModel/downloadItemModel.py index 365cf4f..ac0cce7 100644 --- a/TIDALDL-PY/tidal_gui/viewModel/downloadItemModel.py +++ b/TIDALDL-PY/tidal_gui/viewModel/downloadItemModel.py @@ -10,6 +10,7 @@ """ import os +import aigpy from abc import ABC, ABCMeta from enum import Enum from pickle import FALSE @@ -23,26 +24,39 @@ from tidal_gui.viewModel.viewModel import ViewModel class DownloadStatus(Enum): - Wait = 0, - Running = 1, - Finish = 2, - Error = 3, - Cancel = 4, + WAIT = 0, + RUNNING = 1, + SUCCESS = 2, + ERROR = 3, + CANCEL = 4, -_endStatus_ = [DownloadStatus.Finish, DownloadStatus.Error, DownloadStatus.Cancel] +_endStatus_ = [DownloadStatus.SUCCESS, DownloadStatus.ERROR, DownloadStatus.CANCEL] class Progress(UserProgress): def __init__(self, model): super().__init__() self.model = model + self.curStr = '' + self.maxStr = '' + + def __toMBStr__(self, num): + size = aigpy.memory.convert(num, aigpy.memory.Unit.BYTE, aigpy.memory.Unit.MB) + return str(round(size, 2)) + ' MB' def updateCurNum(self): - self.model.update(self.curNum, self.maxNum) + per = self.curNum * 100 / self.maxNum + self.curStr = self.__toMBStr__(self.curNum) + self.model.SIGNAL_REFRESH_VIEW.emit('updateCurNum', {'per': per, + 'curStr': self.curStr, + 'maxStr': self.maxStr}) def updateMaxNum(self): - pass + self.maxStr = self.__toMBStr__(self.maxNum) + + def updateStream(self, stream): + self.model.SIGNAL_REFRESH_VIEW.emit('updateStream', {'stream': stream}) class DownloadItemModel(ViewModel): @@ -53,13 +67,27 @@ class DownloadItemModel(ViewModel): self.basePath = basePath self.isTrack = isinstance(data, Track) self.progress = Progress(self) - self.__setStatus__(DownloadStatus.Wait) + self.__setStatus__(DownloadStatus.WAIT) if self.isTrack: self.__initTrack__(index) else: self.__initVideo__(index) + self.SIGNAL_REFRESH_VIEW.connect(self.__refresh__) + + def __refresh__(self, stype: str, object): + if stype == "updateCurNum": + per = object['per'] + curStr = object['curStr'] + maxStr = object['maxStr'] + self.view.setSize(curStr, maxStr) + self.view.setProgress(per) + elif stype == "updateStream": + codec = object['stream'].codec + self.view.setCodec(codec) + + def __setStatus__(self, status: DownloadStatus, desc: str = ''): self.status = status if desc == '': @@ -68,7 +96,7 @@ class DownloadItemModel(ViewModel): self.view.setAction(status.name + '-' + desc) def __setErrStatus__(self, errmsg: str): - self.status = DownloadStatus.Error + self.status = DownloadStatus.ERROR self.view.setAction(self.status.name) self.view.setErrmsg(errmsg) @@ -82,33 +110,28 @@ class DownloadItemModel(ViewModel): 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 + return self.status == DownloadStatus.WAIT def stopDownload(self): if self.status not in _endStatus_: - self.__setStatus__(DownloadStatus.Cancel) + self.__setStatus__(DownloadStatus.CANCEL) def retry(self): - self.__setStatus__(DownloadStatus.Wait) + if self.status in [DownloadStatus.ERROR, DownloadStatus.CANCEL]: + self.__setStatus__(DownloadStatus.WAIT) def download(self): - self.__setStatus__(DownloadStatus.Running) + self.__setStatus__(DownloadStatus.RUNNING) if self.isTrack: check, msg = downloadTrack(self.data, self.data.album, self.data.playlist, self.progress) else: check, msg = downloadVideo(self.data) - + if check is False: self.__setErrStatus__(msg) - else: - self.__setStatus__(DownloadStatus.Finish) + return self.view.setProgress(100) - self.__setStatus__(DownloadStatus.Finish) - + self.__setStatus__(DownloadStatus.SUCCESS) diff --git a/TIDALDL-PY/tidal_gui/viewModel/taskItemModel.py b/TIDALDL-PY/tidal_gui/viewModel/taskItemModel.py index 623a6e8..66756ff 100644 --- a/TIDALDL-PY/tidal_gui/viewModel/taskItemModel.py +++ b/TIDALDL-PY/tidal_gui/viewModel/taskItemModel.py @@ -49,7 +49,7 @@ class TaskItemModel(ViewModel): def __refresh__(self, stype: str, obj): if stype == "setPic": self.view.setPic(obj) - elif stype == "addListItem": + elif stype == "addListItems": for index, item in enumerate(obj): downItem = DownloadItemModel(index + 1, item, self.path) self.view.addListItem(downItem.view) @@ -84,7 +84,7 @@ class TaskItemModel(ViewModel): self.view.setLabel(title, desc) def __thread_func__(model: TaskItemModel, album: Album): - cover = API.getCoverData(album.cover, '1280', '1280') + cover = API.getCoverData(album.cover) model.SIGNAL_REFRESH_VIEW.emit('setPic', cover) msg, tracks, videos = API.getItems(album.id, Type.Album) @@ -97,17 +97,69 @@ class TaskItemModel(ViewModel): for item in videos: item.album = album - model.SIGNAL_REFRESH_VIEW.emit('addListItem', tracks + videos) - print('__initAlbum__') + model.SIGNAL_REFRESH_VIEW.emit('addListItems', tracks + videos) time.sleep(1) _thread.start_new_thread(__thread_func__, (self, data)) def __initTrack__(self, data: Track): - pass + title = data.title + desc = f"by {getArtistsNames(data.artists)} " \ + f"{getDurationString(data.duration)} " + self.view.setLabel(title, desc) + + def __thread_func__(model: TaskItemModel, track: Track): + mag, track.album = API.getAlbum(track.album.id) + model.path = getBasePath(track) + cover = API.getCoverData(track.album.cover) + model.SIGNAL_REFRESH_VIEW.emit('setPic', cover) + model.SIGNAL_REFRESH_VIEW.emit('addListItems', [track]) + time.sleep(1) + + _thread.start_new_thread(__thread_func__, (self, data)) def __initVideo__(self, data: Video): - pass + self.path = getBasePath(data) + + title = data.title + desc = f"by {getArtistsNames(data.artists)} " \ + f"{getDurationString(data.duration)} " + self.view.setLabel(title, desc) + + def __thread_func__(model: TaskItemModel, video: Video): + cover = API.getCoverData(video.imageID) + model.SIGNAL_REFRESH_VIEW.emit('setPic', cover) + model.SIGNAL_REFRESH_VIEW.emit('addListItems', [video]) + time.sleep(1) + + _thread.start_new_thread(__thread_func__, (self, data)) def __initPlaylist__(self, data: Playlist): - pass + self.path = getBasePath(data) + + title = data.title + desc = f"{getDurationString(data.duration)} " \ + f"Track-{data.numberOfTracks} " \ + f"Video-{data.numberOfVideos}" + self.view.setLabel(title, desc) + + def __thread_func__(model: TaskItemModel, playlist: Playlist): + cover = API.getCoverData(playlist.squareImage) + model.SIGNAL_REFRESH_VIEW.emit('setPic', cover) + + msg, tracks, videos = API.getItems(playlist.uuid, Type.Playlist) + if not aigpy.stringHelper.isNull(msg): + model.view.setErrmsg(msg) + return + + for item in tracks: + mag, album = API.getAlbum(item.album.id) + item.playlist = playlist + item.album = album + for item in videos: + item.playlist = playlist + + model.SIGNAL_REFRESH_VIEW.emit('addListItems', tracks + videos) + time.sleep(1) + + _thread.start_new_thread(__thread_func__, (self, data))