From b8091e233b666b9920e333918e465a25db64de7b Mon Sep 17 00:00:00 2001 From: Robert Honz Date: Sun, 15 Dec 2024 20:37:06 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Album=20track=20number=20padding=20?= =?UTF-8?q?can=20be=20set.=20Fixes=20#142?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tidal_dl_ng/dialog.py | 24 +++++++++++++++++++++- tidal_dl_ng/download.py | 4 ++-- tidal_dl_ng/helper/path.py | 19 +++++++++++------ tidal_dl_ng/model/cfg.py | 8 ++++++-- tidal_dl_ng/ui/dialog_settings.py | 32 ++++++++++++++++++++++++++++- tidal_dl_ng/ui/dialog_settings.ui | 34 +++++++++++++++++++++++++++++++ 6 files changed, 109 insertions(+), 12 deletions(-) diff --git a/tidal_dl_ng/dialog.py b/tidal_dl_ng/dialog.py index 928f6c1..073e016 100644 --- a/tidal_dl_ng/dialog.py +++ b/tidal_dl_ng/dialog.py @@ -116,11 +116,13 @@ class DialogPreferences(QtWidgets.QDialog): parameters_checkboxes: [str] parameters_combo: [(str, StrEnum)] parameters_line_edit: [str] + parameters_spin_box: [str] prefix_checkbox: str = "cb_" prefix_label: str = "l_" prefix_icon: str = "icon_" prefix_line_edit: str = "le_" prefix_combo: str = "c_" + prefix_spin_box: str = "sb_" def __init__(self, settings: Settings, settings_save: QtCore.Signal, parent=None): super().__init__(parent) @@ -135,6 +137,7 @@ class DialogPreferences(QtWidgets.QDialog): self._init_checkboxes() self._init_comboboxes() self._init_line_edit() + self._init_spin_box() # Create an instance of the GUI self.ui = Ui_DialogSettings() @@ -143,6 +146,7 @@ class DialogPreferences(QtWidgets.QDialog): self.ui.setupUi(self) # Set data. self.gui_populate() + # Post setup self.exec() @@ -157,6 +161,9 @@ class DialogPreferences(QtWidgets.QDialog): "path_binary_ffmpeg", ] + def _init_spin_box(self): + self.parameters_spin_box = ["album_track_num_pad_min"] + def _init_comboboxes(self): self.parameters_combo = [ ("quality_audio", QualityAudio), @@ -181,6 +188,7 @@ class DialogPreferences(QtWidgets.QDialog): self.populate_checkboxes() self.populate_combo() self.populate_line_edit() + self.populate_spin_box() def dialog_chose_file( self, @@ -225,7 +233,7 @@ class DialogPreferences(QtWidgets.QDialog): label_icon.setPixmap(QtGui.QPixmap(self.icon.pixmap(QtCore.QSize(16, 16)))) label_icon.setToolTip(getattr(self.help_settings, pn)) label.setText(pn) - line_edit.setText(getattr(self.data, pn)) + line_edit.setText(str(getattr(self.data, pn))) # Base Path File Dialog self.ui.pb_download_base_path.clicked.connect(lambda x: self.dialog_chose_file(self.ui.le_download_base_path)) @@ -265,6 +273,17 @@ class DialogPreferences(QtWidgets.QDialog): checkbox.setIcon(self.icon) checkbox.setChecked(getattr(self.data, pn)) + def populate_spin_box(self): + for pn in self.parameters_spin_box: + label_icon: QtWidgets.QLabel = getattr(self.ui, self.prefix_label + self.prefix_icon + pn) + label: QtWidgets.QLabel = getattr(self.ui, self.prefix_label + pn) + spin_box: QtWidgets.QSpinBox = getattr(self.ui, self.prefix_spin_box + pn) + + label_icon.setPixmap(QtGui.QPixmap(self.icon.pixmap(QtCore.QSize(16, 16)))) + label_icon.setToolTip(getattr(self.help_settings, pn)) + label.setText(pn) + spin_box.setValue(getattr(self.data, pn)) + def accept(self): # Get settings. self.to_settings() @@ -280,4 +299,7 @@ class DialogPreferences(QtWidgets.QDialog): for item in self.parameters_combo: setattr(self.settings.data, item[0], getattr(self.ui, self.prefix_combo + item[0]).currentData()) + for item in self.parameters_spin_box: + setattr(self.settings.data, item, getattr(self.ui, self.prefix_spin_box + item).value()) + self.s_settings_save.emit() diff --git a/tidal_dl_ng/download.py b/tidal_dl_ng/download.py index 9098ed1..83d8743 100644 --- a/tidal_dl_ng/download.py +++ b/tidal_dl_ng/download.py @@ -299,7 +299,7 @@ class Download: # Create file name and path file_extension_dummy: str = AudioExtensions.FLAC - file_name_relative = format_path_media(file_template, media) + file_name_relative = format_path_media(file_template, media, self.settings.data.album_track_num_pad_min) path_media_dst: pathlib.Path = ( (pathlib.Path(self.path_base).expanduser() / (file_name_relative + file_extension_dummy)) .resolve() @@ -596,7 +596,7 @@ class Download: raise MediaMissing # Create file name and path - file_name_relative = format_path_media(file_template, media) + file_name_relative = format_path_media(file_template, media, self.settings.data.album_track_num_pad_min) # Get the name of the list and check, if videos should be included. list_media_name: str = name_builder_title(media) diff --git a/tidal_dl_ng/helper/path.py b/tidal_dl_ng/helper/path.py index d9ff35c..a275cca 100644 --- a/tidal_dl_ng/helper/path.py +++ b/tidal_dl_ng/helper/path.py @@ -49,7 +49,9 @@ def path_file_settings() -> str: return os.path.join(path_config_base(), "settings.json") -def format_path_media(fmt_template: str, media: Track | Album | Playlist | UserPlaylist | Video | Mix) -> str: +def format_path_media( + fmt_template: str, media: Track | Album | Playlist | UserPlaylist | Video | Mix, album_track_num_pad_min: int = 0 +) -> str: result = fmt_template # Search track format template for placeholder. @@ -58,7 +60,7 @@ def format_path_media(fmt_template: str, media: Track | Album | Playlist | UserP for _matchNum, match in enumerate(matches, start=1): template_str = match.group() - result_fmt = format_str_media(match.group(1), media) + result_fmt = format_str_media(match.group(1), media, album_track_num_pad_min) if result_fmt != match.group(1): value = sanitize_filename(result_fmt) @@ -67,7 +69,9 @@ def format_path_media(fmt_template: str, media: Track | Album | Playlist | UserP return result -def format_str_media(name: str, media: Track | Album | Playlist | UserPlaylist | Video | Mix) -> str: # noqa: C901 +def format_str_media( + name: str, media: Track | Album | Playlist | UserPlaylist | Video | Mix, album_track_num_pad_min: int = 0 +) -> str: result: str = name try: @@ -98,7 +102,10 @@ def format_str_media(name: str, media: Track | Album | Playlist | UserPlaylist | if isinstance(media, Track | Video): num_tracks: int = media.album.num_tracks if hasattr(media, "album") else 1 count_digits: int = int(math.log10(num_tracks)) + 1 - result = str(media.track_num).zfill(count_digits) + count_digits_computed: int = ( + count_digits if count_digits > album_track_num_pad_min else album_track_num_pad_min + ) + result = str(media.track_num).zfill(count_digits_computed) case "album_num_tracks": if isinstance(media, Track | Video): result = str(media.album.num_tracks if hasattr(media, "album") else 1) @@ -160,11 +167,11 @@ def format_str_media(name: str, media: Track | Album | Playlist | UserPlaylist | case "track_volume_num_optional": if isinstance(media, Track | Video): num_volumes: int = media.album.num_volumes if hasattr(media, "album") else 1 - result = "" if num_volumes is 1 else str(media.volume_num) + result = "" if num_volumes == 1 else str(media.volume_num) case "track_volume_num_optional_CD": if isinstance(media, Track | Video): num_volumes: int = media.album.num_volumes if hasattr(media, "album") else 1 - result = "" if num_volumes is 1 else f"CD{media.volume_num!s}" + result = "" if num_volumes == 1 else f"CD{media.volume_num!s}" except Exception as e: # TODO: Implement better exception logging. print(e) diff --git a/tidal_dl_ng/model/cfg.py b/tidal_dl_ng/model/cfg.py index 06614f4..bf01814 100644 --- a/tidal_dl_ng/model/cfg.py +++ b/tidal_dl_ng/model/cfg.py @@ -40,6 +40,7 @@ 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 @dataclass_json @@ -82,8 +83,11 @@ class HelpSettings: cover_album_file: str = "Save cover to 'cover.jpg', if an album is downloaded." extract_flac: str = "Extract FLAC audio tracks from MP4 containers and save them as `*.flac` (uses FFmpeg)." downloads_simultaneous_per_track_max: str = "Maximum number of simultaneous chunk downloads per track." - download_delay_sec_min: float = "Lower boundary for the calculation of the download delay in seconds." - download_delay_sec_max: float = "Upper boundary for the calculation of the download delay in seconds." + 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." + ) @dataclass_json diff --git a/tidal_dl_ng/ui/dialog_settings.py b/tidal_dl_ng/ui/dialog_settings.py index a624e34..d3d2401 100644 --- a/tidal_dl_ng/ui/dialog_settings.py +++ b/tidal_dl_ng/ui/dialog_settings.py @@ -1,7 +1,7 @@ ################################################################################ ## Form generated from reading UI file 'dialog_settings.ui' ## -## Created by: Qt User Interface Compiler version 6.8.0 +## Created by: Qt User Interface Compiler version 6.8.1 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ @@ -18,6 +18,7 @@ from PySide6.QtWidgets import ( QLineEdit, QPushButton, QSizePolicy, + QSpinBox, QVBoxLayout, ) @@ -268,6 +269,32 @@ class Ui_DialogSettings: self.lv_main.addWidget(self.gb_choices) + self.gb_numbers = QGroupBox(DialogSettings) + self.gb_numbers.setObjectName("gb_numbers") + self.verticalLayout_8 = QVBoxLayout(self.gb_numbers) + self.verticalLayout_8.setObjectName("verticalLayout_8") + self.horizontalLayout_9 = QHBoxLayout() + self.horizontalLayout_9.setObjectName("horizontalLayout_9") + self.l_album_track_num_pad_min = QLabel(self.gb_numbers) + self.l_album_track_num_pad_min.setObjectName("l_album_track_num_pad_min") + + self.horizontalLayout_9.addWidget(self.l_album_track_num_pad_min) + + self.l_icon_album_track_num_pad_min = QLabel(self.gb_numbers) + self.l_icon_album_track_num_pad_min.setObjectName("l_icon_album_track_num_pad_min") + + self.horizontalLayout_9.addWidget(self.l_icon_album_track_num_pad_min) + + self.sb_album_track_num_pad_min = QSpinBox(self.gb_numbers) + self.sb_album_track_num_pad_min.setObjectName("sb_album_track_num_pad_min") + self.sb_album_track_num_pad_min.setMaximum(4) + + self.horizontalLayout_9.addWidget(self.sb_album_track_num_pad_min) + + self.verticalLayout_8.addLayout(self.horizontalLayout_9) + + self.lv_main.addWidget(self.gb_numbers) + self.gb_path = QGroupBox(DialogSettings) self.gb_path.setObjectName("gb_path") self.horizontalLayout_2 = QHBoxLayout(self.gb_path) @@ -533,6 +560,9 @@ class Ui_DialogSettings: self.l_quality_video.setText(QCoreApplication.translate("DialogSettings", "TextLabel", None)) self.l_icon_metadata_cover_dimension.setText(QCoreApplication.translate("DialogSettings", "TextLabel", None)) self.l_metadata_cover_dimension.setText(QCoreApplication.translate("DialogSettings", "TextLabel", None)) + 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.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 4fa07cf..bff04c7 100644 --- a/tidal_dl_ng/ui/dialog_settings.ui +++ b/tidal_dl_ng/ui/dialog_settings.ui @@ -355,6 +355,40 @@ + + + + Numbers + + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + 4 + + + + + + + +