Merge pull request #68 from exislow/45-support-for-artist-download
45 support for artist download
This commit is contained in:
@@ -105,10 +105,9 @@ class DialogPreferences(QtWidgets.QDialog):
|
|||||||
def _init_signals(self):
|
def _init_signals(self):
|
||||||
self.ui.cb_video_convert_mp4.stateChanged.connect(self.on_cb_video_convert_mp4)
|
self.ui.cb_video_convert_mp4.stateChanged.connect(self.on_cb_video_convert_mp4)
|
||||||
|
|
||||||
def on_cb_video_convert_mp4(self, int):
|
def on_cb_video_convert_mp4(self, change_status: int):
|
||||||
if self.ui.cb_video_convert_mp4.isChecked():
|
|
||||||
# Check if ffmpeg is in PATH otherwise show error message.
|
# Check if ffmpeg is in PATH otherwise show error message.
|
||||||
if not is_installed_ffmpeg():
|
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.setChecked(False)
|
||||||
self.ui.cb_video_convert_mp4.setCheckState(QtCore.Qt.CheckState.Unchecked)
|
self.ui.cb_video_convert_mp4.setCheckState(QtCore.Qt.CheckState.Unchecked)
|
||||||
QtWidgets.QMessageBox.critical(
|
QtWidgets.QMessageBox.critical(
|
||||||
|
|||||||
+46
-8
@@ -30,6 +30,7 @@ except ImportError as e:
|
|||||||
import coloredlogs.converter
|
import coloredlogs.converter
|
||||||
from rich.progress import Progress
|
from rich.progress import Progress
|
||||||
from tidalapi import Album, Mix, Playlist, Quality, Track, UserPlaylist, Video
|
from tidalapi import Album, Mix, Playlist, Quality, Track, UserPlaylist, Video
|
||||||
|
from tidalapi.artist import Artist
|
||||||
from tidalapi.session import SearchTypes
|
from tidalapi.session import SearchTypes
|
||||||
|
|
||||||
from tidal_dl_ng.config import Settings, Tidal
|
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):
|
def _populate_search_types(self, ui_target: QtWidgets.QComboBox, options: SearchTypes):
|
||||||
for item in options:
|
for item in options:
|
||||||
if item and item.__name__ != "Artist":
|
if item:
|
||||||
ui_target.addItem(item.__name__, 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):
|
def _init_tree_results(self, tree: QtWidgets.QTableWidget):
|
||||||
tree.setColumnHidden(5, True)
|
tree.setColumnHidden(5, True)
|
||||||
@@ -321,10 +322,14 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
self.s_tr_results_add_top_level_item.emit(child)
|
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):
|
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.
|
# TODO: Duration needs to be calculated later to properly fill with zeros.
|
||||||
|
if item.duration_sec > -1:
|
||||||
# Format seconds to mm:ss.
|
# Format seconds to mm:ss.
|
||||||
m, s = divmod(item.duration_sec, 60)
|
m, s = divmod(item.duration_sec, 60)
|
||||||
duration: str = f"{m:02d}:{s:02d}"
|
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)
|
# 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)
|
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.setText(4, duration)
|
||||||
child.setData(5, QtCore.Qt.ItemDataRole.UserRole, item.obj)
|
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.
|
# Add a disabled dummy child, so expansion arrow will appear. This Child will be replaced on expansion.
|
||||||
child_dummy: QtWidgets.QTreeWidgetItem = QtWidgets.QTreeWidgetItem()
|
child_dummy: QtWidgets.QTreeWidgetItem = QtWidgets.QTreeWidgetItem()
|
||||||
|
|
||||||
@@ -442,6 +447,17 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
obj=item,
|
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)
|
result.append(result_item)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@@ -521,7 +537,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
|
|
||||||
def list_items_show_result(
|
def list_items_show_result(
|
||||||
self,
|
self,
|
||||||
media_list: Album | Playlist | Mix | None = None,
|
media_list: Album | Playlist | Mix | Artist | None = None,
|
||||||
point: QtCore.QPoint | None = None,
|
point: QtCore.QPoint | None = None,
|
||||||
parent: QtWidgets.QTreeWidgetItem = None,
|
parent: QtWidgets.QTreeWidgetItem = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -530,7 +546,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
media_list = item.data(3, QtCore.Qt.ItemDataRole.UserRole)
|
media_list = item.data(3, QtCore.Qt.ItemDataRole.UserRole)
|
||||||
|
|
||||||
# Get all results
|
# 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)
|
result: [ResultItem] = self.search_result_to_model(media_items)
|
||||||
|
|
||||||
self.populate_tree_results(result, parent=parent)
|
self.populate_tree_results(result, parent=parent)
|
||||||
@@ -551,10 +567,30 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
if len(items) == 0:
|
if len(items) == 0:
|
||||||
logger_gui.error("Please select a row first.")
|
logger_gui.error("Please select a row first.")
|
||||||
else:
|
else:
|
||||||
|
# If it is an artist resolve it with all available albums of him
|
||||||
|
if len(items) == 1:
|
||||||
|
tmp_media: QtWidgets.QTreeWidgetItem = items[0].data(5, QtCore.Qt.ItemDataRole.UserRole)
|
||||||
|
|
||||||
|
if isinstance(tmp_media, Artist):
|
||||||
|
tmp_children: [QtWidgets.QTreeWidgetItem] = []
|
||||||
|
is_dummy_child = not bool(items[0].child(0).data(5, QtCore.Qt.ItemDataRole.UserRole))
|
||||||
|
|
||||||
|
# Use the expand function to retrieve all albums.
|
||||||
|
if is_dummy_child:
|
||||||
|
self.on_tr_results_expanded(items[0])
|
||||||
|
|
||||||
|
count_children: int = items[0].childCount()
|
||||||
|
|
||||||
|
# Get all children.
|
||||||
|
for idx in range(count_children):
|
||||||
|
tmp_children.append(items[0].child(idx))
|
||||||
|
|
||||||
|
items: [Album] = tmp_children
|
||||||
|
|
||||||
items_pos_last = len(items) - 1
|
items_pos_last = len(items) - 1
|
||||||
|
|
||||||
for item in items:
|
for item in items:
|
||||||
media: Track | Album | Playlist | Video = item.data(5, QtCore.Qt.ItemDataRole.UserRole)
|
media: Track | Album | Playlist | Video | Artist = item.data(5, QtCore.Qt.ItemDataRole.UserRole)
|
||||||
# Skip only if Track item, skip option set and the item is not the last in the list.
|
# Skip only if Track item, skip option set and the item is not the last in the list.
|
||||||
download_delay: bool = bool(
|
download_delay: bool = bool(
|
||||||
isinstance(media, Track | Video)
|
isinstance(media, Track | Video)
|
||||||
@@ -567,7 +603,9 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
self.pb_download.setText("Download")
|
self.pb_download.setText("Download")
|
||||||
self.pb_download.setEnabled(True)
|
self.pb_download.setEnabled(True)
|
||||||
|
|
||||||
def download(self, media: Track | Album | Playlist | Video | Mix, dl: Download, delay_track: bool = False) -> None:
|
def download(
|
||||||
|
self, media: Track | Album | Playlist | Video | Mix | Artist, dl: Download, delay_track: bool = False
|
||||||
|
) -> None:
|
||||||
self.s_pb_reset.emit()
|
self.s_pb_reset.emit()
|
||||||
self.s_statusbar_message.emit(StatusbarMessage(message="Download started..."))
|
self.s_statusbar_message.emit(StatusbarMessage(message="Download started..."))
|
||||||
|
|
||||||
@@ -597,7 +635,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
|
|
||||||
if load_children:
|
if load_children:
|
||||||
list_item.removeChild(list_item.child(0))
|
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)
|
self.list_items_show_result(media_list=media_list, parent=list_item)
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
from collections.abc import Callable
|
||||||
|
|
||||||
from tidalapi import Album, Mix, Playlist, Session, Track, UserPlaylist, Video
|
from tidalapi import Album, Mix, Playlist, Session, Track, UserPlaylist, Video
|
||||||
from tidalapi.artist import Artist, Role
|
from tidalapi.artist import Artist, Role
|
||||||
from tidalapi.session import SearchTypes
|
from tidalapi.session import SearchTypes
|
||||||
@@ -87,21 +89,25 @@ def search_results_all(session: Session, needle: str, types_media: SearchTypes =
|
|||||||
return result
|
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
|
limit: int = 100
|
||||||
offset: int = 0
|
offset: int = 0
|
||||||
done: bool = False
|
done: bool = False
|
||||||
result: [Track | Video] = []
|
result: [Track | Video | Album] = []
|
||||||
|
|
||||||
if isinstance(media_list, Mix):
|
if isinstance(media_list, Mix):
|
||||||
result = media_list.items()
|
result = media_list.items()
|
||||||
else:
|
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:
|
while not done:
|
||||||
tmp_result: [Track | Video] = (
|
tmp_result: [Track | Video] = func_get_items_media(limit=limit, offset=offset)
|
||||||
media_list.items(limit=limit, offset=offset)
|
|
||||||
if videos_include
|
|
||||||
else media_list.tracks(limit=limit, offset=offset)
|
|
||||||
)
|
|
||||||
|
|
||||||
if bool(tmp_result):
|
if bool(tmp_result):
|
||||||
result += tmp_result
|
result += tmp_result
|
||||||
|
|||||||
Reference in New Issue
Block a user