summaryrefslogtreecommitdiff
path: root/tidal_dl_ng
diff options
context:
space:
mode:
authorRobert Honz <exislow+github@gmail.com>2024-12-15 23:35:10 +0100
committerRobert Honz <exislow+github@gmail.com>2024-12-15 23:35:10 +0100
commit5768edacc57fd29bf2ef182cb9db312d4aebb468 (patch)
treecb5ec1765386c3e08bf90fa82103341c1c8bb1d3 /tidal_dl_ng
parent62c32f19a4bbf708e6330f3c89813de1c1883ad1 (diff)
downloadtidal-dl-ng-5768edacc57fd29bf2ef182cb9db312d4aebb468.tar.gz
✨ Multithreaded downloads implemented (for Playlists, Albums etc.). Fixes #180
Diffstat (limited to 'tidal_dl_ng')
-rw-r--r--tidal_dl_ng/dialog.py2
-rw-r--r--tidal_dl_ng/download.py36
-rw-r--r--tidal_dl_ng/model/cfg.py6
-rw-r--r--tidal_dl_ng/ui/dialog_settings.py25
-rw-r--r--tidal_dl_ng/ui/dialog_settings.ui30
5 files changed, 80 insertions, 19 deletions
diff --git a/tidal_dl_ng/dialog.py b/tidal_dl_ng/dialog.py
index 073e016..4e36d8a 100644
--- a/tidal_dl_ng/dialog.py
+++ b/tidal_dl_ng/dialog.py
@@ -162,7 +162,7 @@ class DialogPreferences(QtWidgets.QDialog):
]
def _init_spin_box(self):
- self.parameters_spin_box = ["album_track_num_pad_min"]
+ self.parameters_spin_box = ["album_track_num_pad_min", "downloads_concurrent_max"]
def _init_comboboxes(self):
self.parameters_combo = [
diff --git a/tidal_dl_ng/download.py b/tidal_dl_ng/download.py
index 83d8743..af00497 100644
--- a/tidal_dl_ng/download.py
+++ b/tidal_dl_ng/download.py
@@ -621,22 +621,30 @@ class Download:
# Iterate through list items
while not self.progress.finished:
- for item_media in items:
- # Download the item.
- status, result_path_file = self.item(
- media=item_media,
- file_template=file_name_relative,
- quality_audio=quality_audio,
- quality_video=quality_video,
- download_delay=download_delay,
- is_parent_album=is_album,
- )
+ with futures.ThreadPoolExecutor(max_workers=self.settings.data.downloads_concurrent_max) as executor:
+ # Dispatch all download tasks to worker threads
+ l_futures: [any] = [
+ executor.submit(
+ self.item,
+ media=item_media,
+ file_template=file_name_relative,
+ quality_audio=quality_audio,
+ quality_video=quality_video,
+ download_delay=download_delay,
+ is_parent_album=is_album,
+ )
+ for item_media in items
+ ]
+ # Report results as they become available
+ for future in futures.as_completed(l_futures):
+ # Retrieve result
+ status, result_path_file = future.result()
- # Advance progress bar.
- self.progress.advance(p_task1)
+ # Advance progress bar.
+ self.progress.advance(p_task1)
- if not progress_stdout:
- self.progress_gui.list_item.emit(self.progress.tasks[p_task1].percentage)
+ if not progress_stdout:
+ self.progress_gui.list_item.emit(self.progress.tasks[p_task1].percentage)
self.fn_logger.info(f"Finished list '{list_media_name}'.")
diff --git a/tidal_dl_ng/model/cfg.py b/tidal_dl_ng/model/cfg.py
index bf01814..88636f1 100644
--- a/tidal_dl_ng/model/cfg.py
+++ b/tidal_dl_ng/model/cfg.py
@@ -40,7 +40,8 @@ class Settings:
downloads_simultaneous_per_track_max: int = 20
download_delay_sec_min: float = 3.0
download_delay_sec_max: float = 5.0
- album_track_num_pad_min: int = 0
+ album_track_num_pad_min: int = 1
+ downloads_concurrent_max: int = 3
@dataclass_json
@@ -86,8 +87,9 @@ class HelpSettings:
download_delay_sec_min: str = "Lower boundary for the calculation of the download delay in seconds."
download_delay_sec_max: str = "Upper boundary for the calculation of the download delay in seconds."
album_track_num_pad_min: str = (
- "Minimum length of the album track count, will be padded with zeroes (0). To disable " "padding set this to 0."
+ "Minimum length of the album track count, will be padded with zeroes (0). To disable padding set this to 1."
)
+ downloads_concurrent_max: str = "Maximum concurrent number of downloads (threads)."
@dataclass_json
diff --git a/tidal_dl_ng/ui/dialog_settings.py b/tidal_dl_ng/ui/dialog_settings.py
index d3d2401..945af92 100644
--- a/tidal_dl_ng/ui/dialog_settings.py
+++ b/tidal_dl_ng/ui/dialog_settings.py
@@ -27,7 +27,7 @@ class Ui_DialogSettings:
def setupUi(self, DialogSettings):
if not DialogSettings.objectName():
DialogSettings.setObjectName("DialogSettings")
- DialogSettings.resize(640, 705)
+ DialogSettings.resize(640, 767)
sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(100)
sizePolicy.setVerticalStretch(100)
@@ -293,6 +293,27 @@ class Ui_DialogSettings:
self.verticalLayout_8.addLayout(self.horizontalLayout_9)
+ self.horizontalLayout_11 = QHBoxLayout()
+ self.horizontalLayout_11.setObjectName("horizontalLayout_11")
+ self.l_downloads_concurrent_max = QLabel(self.gb_numbers)
+ self.l_downloads_concurrent_max.setObjectName("l_downloads_concurrent_max")
+
+ self.horizontalLayout_11.addWidget(self.l_downloads_concurrent_max)
+
+ self.l_icon_downloads_concurrent_max = QLabel(self.gb_numbers)
+ self.l_icon_downloads_concurrent_max.setObjectName("l_icon_downloads_concurrent_max")
+
+ self.horizontalLayout_11.addWidget(self.l_icon_downloads_concurrent_max)
+
+ self.sb_downloads_concurrent_max = QSpinBox(self.gb_numbers)
+ self.sb_downloads_concurrent_max.setObjectName("sb_downloads_concurrent_max")
+ self.sb_downloads_concurrent_max.setMinimum(1)
+ self.sb_downloads_concurrent_max.setMaximum(5)
+
+ self.horizontalLayout_11.addWidget(self.sb_downloads_concurrent_max)
+
+ self.verticalLayout_8.addLayout(self.horizontalLayout_11)
+
self.lv_main.addWidget(self.gb_numbers)
self.gb_path = QGroupBox(DialogSettings)
@@ -563,6 +584,8 @@ class Ui_DialogSettings:
self.gb_numbers.setTitle(QCoreApplication.translate("DialogSettings", "Numbers", None))
self.l_album_track_num_pad_min.setText(QCoreApplication.translate("DialogSettings", "TextLabel", None))
self.l_icon_album_track_num_pad_min.setText(QCoreApplication.translate("DialogSettings", "TextLabel", None))
+ self.l_downloads_concurrent_max.setText(QCoreApplication.translate("DialogSettings", "TextLabel", None))
+ self.l_icon_downloads_concurrent_max.setText(QCoreApplication.translate("DialogSettings", "TextLabel", None))
self.gb_path.setTitle(QCoreApplication.translate("DialogSettings", "Path", None))
self.l_icon_download_base_path.setText(QCoreApplication.translate("DialogSettings", "TextLabel", None))
self.l_download_base_path.setText(QCoreApplication.translate("DialogSettings", "TextLabel", None))
diff --git a/tidal_dl_ng/ui/dialog_settings.ui b/tidal_dl_ng/ui/dialog_settings.ui
index bff04c7..d81701a 100644
--- a/tidal_dl_ng/ui/dialog_settings.ui
+++ b/tidal_dl_ng/ui/dialog_settings.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>640</width>
- <height>705</height>
+ <height>767</height>
</rect>
</property>
<property name="sizePolicy">
@@ -386,6 +386,34 @@
</item>
</layout>
</item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_11">
+ <item>
+ <widget class="QLabel" name="l_downloads_concurrent_max">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="l_icon_downloads_concurrent_max">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="sb_downloads_concurrent_max">
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
</layout>
</widget>
</item>