diff --git a/tidal_dl_ng/dialog.py b/tidal_dl_ng/dialog.py index 21d32d3..5a063d2 100644 --- a/tidal_dl_ng/dialog.py +++ b/tidal_dl_ng/dialog.py @@ -105,19 +105,18 @@ class DialogPreferences(QtWidgets.QDialog): def _init_signals(self): self.ui.cb_video_convert_mp4.stateChanged.connect(self.on_cb_video_convert_mp4) - def on_cb_video_convert_mp4(self, int): - if self.ui.cb_video_convert_mp4.isChecked(): - # Check if ffmpeg is in PATH otherwise show error message. - if not is_installed_ffmpeg(): - self.ui.cb_video_convert_mp4.setChecked(False) - self.ui.cb_video_convert_mp4.setCheckState(QtCore.Qt.CheckState.Unchecked) - QtWidgets.QMessageBox.critical( - self, - "FFmpeg not found!", - "Either FFmpeg is not installed on your computer or not set within " - "your PATH variable. You cannot activate this option until FFmpeg " - "is correctly installed and set to your environmental PATH variable.", - ) + def on_cb_video_convert_mp4(self, change_status: int): + # Check if ffmpeg is in PATH otherwise show error message. + if self.ui.cb_video_convert_mp4.isChecked() and not is_installed_ffmpeg(): + self.ui.cb_video_convert_mp4.setChecked(False) + self.ui.cb_video_convert_mp4.setCheckState(QtCore.Qt.CheckState.Unchecked) + QtWidgets.QMessageBox.critical( + self, + "FFmpeg not found!", + "Either FFmpeg is not installed on your computer or not set within " + "your PATH variable. You cannot activate this option until FFmpeg " + "is correctly installed and set to your environmental PATH variable.", + ) def _init_line_edit(self): self.parameters_line_edit = [ diff --git a/tidal_dl_ng/gui.py b/tidal_dl_ng/gui.py index 9ca72e1..e1ca19d 100644 --- a/tidal_dl_ng/gui.py +++ b/tidal_dl_ng/gui.py @@ -30,6 +30,7 @@ except ImportError as e: import coloredlogs.converter from rich.progress import Progress from tidalapi import Album, Mix, Playlist, Quality, Track, UserPlaylist, Video +from tidalapi.artist import Artist from tidalapi.session import SearchTypes from tidal_dl_ng.config import Settings, Tidal @@ -178,10 +179,10 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): def _populate_search_types(self, ui_target: QtWidgets.QComboBox, options: SearchTypes): for item in options: - if item and item.__name__ != "Artist": + if item: ui_target.addItem(item.__name__, item) - self.cb_search_type.setCurrentIndex(1) + self.cb_search_type.setCurrentIndex(2) def _init_tree_results(self, tree: QtWidgets.QTableWidget): tree.setColumnHidden(5, True) @@ -321,10 +322,14 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): self.s_tr_results_add_top_level_item.emit(child) def populate_tree_result_child(self, item: [Track | Video | Mix | Album | Playlist], index_count_digits: int): + duration: str = "" + # TODO: Duration needs to be calculated later to properly fill with zeros. - # Format seconds to mm:ss. - m, s = divmod(item.duration_sec, 60) - duration: str = f"{m:02d}:{s:02d}" + if item.duration_sec > -1: + # Format seconds to mm:ss. + m, s = divmod(item.duration_sec, 60) + duration: str = f"{m:02d}:{s:02d}" + # Since sorting happens only by string, we need to pad the index and add 1 (to avoid start at 0) index: str = str(item.position + 1).zfill(index_count_digits) @@ -337,7 +342,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): child.setText(4, duration) child.setData(5, QtCore.Qt.ItemDataRole.UserRole, item.obj) - if isinstance(item.obj, Mix | Playlist | Album): + if isinstance(item.obj, Mix | Playlist | Album | Artist): # Add a disabled dummy child, so expansion arrow will appear. This Child will be replaced on expansion. child_dummy: QtWidgets.QTreeWidgetItem = QtWidgets.QTreeWidgetItem() @@ -442,6 +447,17 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): obj=item, ) + result.append(result_item) + elif isinstance(item, Artist): + result_item: ResultItem = ResultItem( + position=idx, + artist=item.name, + title="", + album="", + duration_sec=-1, + obj=item, + ) + result.append(result_item) return result @@ -521,7 +537,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): def list_items_show_result( self, - media_list: Album | Playlist | Mix | None = None, + media_list: Album | Playlist | Mix | Artist | None = None, point: QtCore.QPoint | None = None, parent: QtWidgets.QTreeWidgetItem = None, ) -> None: @@ -530,7 +546,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): media_list = item.data(3, QtCore.Qt.ItemDataRole.UserRole) # Get all results - media_items: [Track | Video] = items_results_all(media_list) + media_items: [Track | Video | Album] = items_results_all(media_list) result: [ResultItem] = self.search_result_to_model(media_items) self.populate_tree_results(result, parent=parent) @@ -597,7 +613,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): if load_children: list_item.removeChild(list_item.child(0)) - media_list: [Mix | Album | Playlist] = list_item.data(5, QtCore.Qt.ItemDataRole.UserRole) + media_list: [Mix | Album | Playlist | Artist] = list_item.data(5, QtCore.Qt.ItemDataRole.UserRole) self.list_items_show_result(media_list=media_list, parent=list_item) diff --git a/tidal_dl_ng/helper/tidal.py b/tidal_dl_ng/helper/tidal.py index 2dde6b2..93c9f76 100644 --- a/tidal_dl_ng/helper/tidal.py +++ b/tidal_dl_ng/helper/tidal.py @@ -1,3 +1,5 @@ +from collections.abc import Callable + from tidalapi import Album, Mix, Playlist, Session, Track, UserPlaylist, Video from tidalapi.artist import Artist, Role from tidalapi.session import SearchTypes @@ -87,21 +89,25 @@ def search_results_all(session: Session, needle: str, types_media: SearchTypes = return result -def items_results_all(media_list: [Mix | Playlist | Album], videos_include: bool = True) -> [Track | Video]: +def items_results_all(media_list: [Mix | Playlist | Album], videos_include: bool = True) -> [Track | Video | Album]: limit: int = 100 offset: int = 0 done: bool = False - result: [Track | Video] = [] + result: [Track | Video | Album] = [] if isinstance(media_list, Mix): result = media_list.items() else: + if isinstance(media_list, Playlist | Album): + if videos_include: + func_get_items_media: Callable = media_list.items + else: + func_get_items_media: Callable = media_list.tracks + else: + func_get_items_media: Callable = media_list.get_albums + while not done: - tmp_result: [Track | Video] = ( - media_list.items(limit=limit, offset=offset) - if videos_include - else media_list.tracks(limit=limit, offset=offset) - ) + tmp_result: [Track | Video] = func_get_items_media(limit=limit, offset=offset) if bool(tmp_result): result += tmp_result