From b07c3374b24073ba3953797be9d5ecd13af3a482 Mon Sep 17 00:00:00 2001 From: Robert Honz Date: Sun, 21 Apr 2024 19:18:05 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20FLAC=20can=20be=20extracted=20from?= =?UTF-8?q?=20MP4=20containers=20(see=20options).=20Downgrade=20on=20HI=5F?= =?UTF-8?q?RES=20download=20is=20possible=20(see=20options).=20Fixes=20#93?= =?UTF-8?q?=20#86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tidal_dl_ng/dialog.py | 2 + tidal_dl_ng/download.py | 88 ++++++++++++++---- tidal_dl_ng/gui.py | 2 +- tidal_dl_ng/metadata.py | 2 +- tidal_dl_ng/model/cfg.py | 6 ++ tidal_dl_ng/ui/dialog_settings.py | 95 ++++++++++++-------- tidal_dl_ng/ui/dialog_settings.ui | 145 ++++++++++++++++++------------ 7 files changed, 226 insertions(+), 114 deletions(-) diff --git a/tidal_dl_ng/dialog.py b/tidal_dl_ng/dialog.py index f069c53..b0b9d07 100644 --- a/tidal_dl_ng/dialog.py +++ b/tidal_dl_ng/dialog.py @@ -160,6 +160,8 @@ class DialogPreferences(QtWidgets.QDialog): "video_download", "download_delay", "video_convert_mp4", + "extract_flac", + "downgrade_on_hi_res", ] def gui_populate(self): diff --git a/tidal_dl_ng/download.py b/tidal_dl_ng/download.py index da3d3a0..7f91ffc 100644 --- a/tidal_dl_ng/download.py +++ b/tidal_dl_ng/download.py @@ -12,7 +12,7 @@ import requests from requests.exceptions import HTTPError from rich.progress import Progress, TaskID from tidalapi import Album, Mix, Playlist, Session, Track, UserPlaylist, Video -from tidalapi.media import AudioExtensions, Quality, StreamManifest, VideoExtensions +from tidalapi.media import AudioExtensions, Codec, Quality, StreamManifest, VideoExtensions, VideoQuality from tidal_dl_ng.config import Settings, Tidal from tidal_dl_ng.constants import EXTENSION_LYRICS, REQUESTS_TIMEOUT_SEC, MediaType, SkipExisting @@ -70,11 +70,15 @@ class Download: self.progress = progress self.path_base = path_base - if not self.settings.data.path_binary_ffmpeg and self.settings.data.video_convert_mp4: + if not self.settings.data.path_binary_ffmpeg and ( + self.settings.data.video_convert_mp4 or self.settings.data.extract_flac + ): self.settings.data.video_convert_mp4 = False + self.settings.data.extract_flac = False + self.fn_logger.error( - "FFmpeg in path is not set. Videos can be downloaded but will not be processed. " - "Make sure FFmpeg is installed and the path to the binary is configured ('path_binary_ffmpeg')." + "FFmpeg is not set. Videos can be downloaded but will not be processed. FLAC cannot be extracted from MP4 containers. " + "Make sure FFmpeg is installed and the path to the binary is configured (`path_binary_ffmpeg`)." ) def _download( @@ -174,7 +178,8 @@ class Download: media_type: MediaType = None, video_download: bool = True, download_delay: bool = False, - quality: Quality | None = None, + quality_audio: Quality | None = None, + quality_video: VideoQuality | None = None, ) -> (bool, str): try: if media_id and media_type: @@ -203,17 +208,34 @@ class Download: return False, "" - # If a quality is explicitly set, change it. - if quality: - quality_old: Quality = self.quality_adjust(quality) - # Get extension. file_extension: str if isinstance(media, Track): + # If hi_res is forbidden in settings and highest track quality is hi_res, downgrade to high_lossless. + if self.settings.data.downgrade_on_hires and media.get_stream().audio_quality == Quality.hi_res: + quality_audio = Quality.high_lossless + + # If a quality is explicitly set, change it. + if quality_audio: + quality_audio_old: Quality = self.adjust_quality_audio(quality_audio) + file_extension = media.get_stream().get_stream_manifest().file_extension + + if self.settings.data.extract_flac: + do_flac_extract = False + + if ( + media.get_stream().get_stream_manifest().codecs.upper() == Codec.FLAC + and file_extension != AudioExtensions.FLAC + ): + file_extension = AudioExtensions.FLAC + do_flac_extract = True elif isinstance(media, Video): - file_extension = VideoExtensions.TS + if quality_video: + quality_video_old = self.adjust_quality_video(quality_video) + + file_extension = AudioExtensions.MP4 if self.settings.data.video_convert_mp4 else VideoExtensions.TS # Create file name and path file_name_relative = format_path_media(file_template, media) @@ -235,14 +257,18 @@ class Download: if not file_exists: # Create a temp directory and file. with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as tmp_path_dir: - tmp_path_file = os.path.join(tmp_path_dir, str(uuid4()) + file_extension) + tmp_path_file = os.path.join(tmp_path_dir, str(uuid4())) # Download media. tmp_path_file = self._download(media=media, path_file=tmp_path_file) + # Convert video from TS to MP4 if isinstance(media, Video) and self.settings.data.video_convert_mp4: # Convert `*.ts` file to `*.mp4` using ffmpeg tmp_path_file = self._video_convert(tmp_path_file) - path_file = os.path.splitext(path_file)[0] + ".mp4" + + # Extract FLAC from MP4 container using ffmpeg + if self.settings.data.extract_flac and do_flac_extract: + tmp_path_file = self._extract_flac(tmp_path_file, media.isrc) # Move final file to the configured destination directory. os.makedirs(os.path.dirname(path_file), exist_ok=True) @@ -256,9 +282,13 @@ class Download: status_download: bool = not file_exists - if quality: + if quality_audio: # Set quality back to the global user value - self.quality_adjust(quality_old) + self.adjust_quality_audio(quality_audio_old) + + if quality_video: + # Set quality back to the global user value + self.adjust_quality_video(quality_video_old) # Whether a file was downloaded or skipped and the download delay is enabled, wait until the next download. # Only use this, if you have a list of several Track items. @@ -270,7 +300,7 @@ class Download: return status_download, path_file - def quality_adjust(self, quality) -> Quality: + def adjust_quality_audio(self, quality) -> Quality: # Save original quality settings quality_old: Quality = self.session.audio_quality self.session.audio_quality = quality @@ -284,6 +314,13 @@ class Download: return quality_old + def adjust_quality_video(self, quality) -> VideoQuality: + quality_old: VideoQuality = self.session.video_quality + + self.session.video_quality = quality + + return quality_old + def _move_lyrics(self, file_media_dst: str, file_media_src: str): # Build tmp lyrics filename tmp_lyrics_file_path: str = file_media_src + EXTENSION_LYRICS @@ -405,7 +442,7 @@ class Download: for media in items: # Download the item. status_download, result_path_file = self.item( - media=media, file_template=file_name_relative, quality=quality + media=media, file_template=file_name_relative, quality_audio=quality ) # Advance progress bar. @@ -422,11 +459,28 @@ class Download: time.sleep(time_sleep) def _video_convert(self, path_file: str) -> str: - path_file_out = os.path.splitext(path_file)[0] + AudioExtensions.MP4 + path_file_out = path_file + AudioExtensions.MP4 result, _ = ffmpeg.input(path_file).output(path_file_out, map=0, c="copy").run() return path_file_out + def _extract_flac(self, path_file: str, isrc: str) -> str: + path_file_out = path_file + AudioExtensions.FLAC + result, _ = ( + ffmpeg.input(path_file) + .output( + path_file_out, + map=0, + movflags="use_metadata_tags", + acodec="copy", + map_metadata="0:g", + metadata=f"isrc={isrc}", + ) + .run() + ) + + return path_file_out + def _extract_video_stream(self, m3u8_variant: m3u8.M3U8, quality: int) -> (m3u8.M3U8 | bool, str): m3u8_playlist: m3u8.M3U8 | bool = False resolution_best: int = 0 diff --git a/tidal_dl_ng/gui.py b/tidal_dl_ng/gui.py index 903e8e8..ab41eb4 100644 --- a/tidal_dl_ng/gui.py +++ b/tidal_dl_ng/gui.py @@ -864,7 +864,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): if isinstance(media, Track | Video): result_dl, path_file = dl.item( - media=media, file_template=file_template, download_delay=delay_track, quality=quality + media=media, file_template=file_template, download_delay=delay_track, quality_audio=quality ) elif isinstance(media, Album | Playlist | Mix): dl.items( diff --git a/tidal_dl_ng/metadata.py b/tidal_dl_ng/metadata.py index a6ee43d..7d62ca0 100644 --- a/tidal_dl_ng/metadata.py +++ b/tidal_dl_ng/metadata.py @@ -141,7 +141,7 @@ class Metadata: self.m.tags["\xa9day"] = self.date self.m.tags["\xa9wrt"] = ", ".join(self.composer) if self.composer else "" self.m.tags["\xa9lyr"] = self.lyrics - self.m.tags["ISRC"] = self.isrc + self.m.tags["isrc"] = self.isrc @staticmethod def cover_data(url: str = None, path_file: str = None) -> str | bytes: diff --git a/tidal_dl_ng/model/cfg.py b/tidal_dl_ng/model/cfg.py index 7910644..0b24854 100644 --- a/tidal_dl_ng/model/cfg.py +++ b/tidal_dl_ng/model/cfg.py @@ -35,6 +35,8 @@ class Settings: video_convert_mp4: bool = True path_binary_ffmpeg: str = "" metadata_cover_dimension: CoverDimensions = CoverDimensions.Px320 + extract_flac: bool = True + downgrade_on_hi_res: bool = False @dataclass_json @@ -74,6 +76,10 @@ class HelpSettings: metadata_cover_dimension: str = ( "The dimensions of the cover image embedded into the track. Possible values: 320x320, 640x640x 1280x1280." ) + extract_flac: str = "Extract FLAC audio tracks from MP4 containers and save them as `*.flac` (uses FFmpeg)." + downgrade_on_hi_res: bool = ( + "Never download HI_RES (MQA) quality. Downgrade to the next best available quality instead." + ) @dataclass_json diff --git a/tidal_dl_ng/ui/dialog_settings.py b/tidal_dl_ng/ui/dialog_settings.py index b276b62..3f45cea 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.6.1 +## Created by: Qt User Interface Compiler version 6.7.0 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ @@ -27,7 +27,7 @@ class Ui_DialogSettings: if not DialogSettings.objectName(): DialogSettings.setObjectName("DialogSettings") DialogSettings.resize(640, 682) - sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) sizePolicy.setHorizontalStretch(100) sizePolicy.setVerticalStretch(100) sizePolicy.setHeightForWidth(DialogSettings.sizePolicy().hasHeightForWidth()) @@ -41,7 +41,7 @@ class Ui_DialogSettings: self.lv_main.setContentsMargins(12, 12, 12, 12) self.gb_flags = QGroupBox(DialogSettings) self.gb_flags.setObjectName("gb_flags") - sizePolicy1 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) sizePolicy1.setHorizontalStretch(100) sizePolicy1.setVerticalStretch(100) sizePolicy1.setHeightForWidth(self.gb_flags.sizePolicy().hasHeightForWidth()) @@ -52,56 +52,56 @@ class Ui_DialogSettings: self.lv_flags.setObjectName("lv_flags") self.lh_flags_1 = QHBoxLayout() self.lh_flags_1.setObjectName("lh_flags_1") - self.lv_flags_video_download = QVBoxLayout() - self.lv_flags_video_download.setObjectName("lv_flags_video_download") + self.lv_flag_video_download = QVBoxLayout() + self.lv_flag_video_download.setObjectName("lv_flag_video_download") self.cb_video_download = QCheckBox(self.gb_flags) self.cb_video_download.setObjectName("cb_video_download") sizePolicy1.setHeightForWidth(self.cb_video_download.sizePolicy().hasHeightForWidth()) self.cb_video_download.setSizePolicy(sizePolicy1) - self.lv_flags_video_download.addWidget(self.cb_video_download) + self.lv_flag_video_download.addWidget(self.cb_video_download) - self.lh_flags_1.addLayout(self.lv_flags_video_download) + self.lh_flags_1.addLayout(self.lv_flag_video_download) - self.lv_flags_video_convert = QVBoxLayout() - self.lv_flags_video_convert.setObjectName("lv_flags_video_convert") + self.lv_flag_video_convert = QVBoxLayout() + self.lv_flag_video_convert.setObjectName("lv_flag_video_convert") self.cb_video_convert_mp4 = QCheckBox(self.gb_flags) self.cb_video_convert_mp4.setObjectName("cb_video_convert_mp4") sizePolicy1.setHeightForWidth(self.cb_video_convert_mp4.sizePolicy().hasHeightForWidth()) self.cb_video_convert_mp4.setSizePolicy(sizePolicy1) - self.lv_flags_video_convert.addWidget(self.cb_video_convert_mp4) + self.lv_flag_video_convert.addWidget(self.cb_video_convert_mp4) - self.lh_flags_1.addLayout(self.lv_flags_video_convert) + self.lh_flags_1.addLayout(self.lv_flag_video_convert) self.lv_flags.addLayout(self.lh_flags_1) self.lh_flags_2 = QHBoxLayout() self.lh_flags_2.setObjectName("lh_flags_2") - self.lh_flags_lyrics_embed = QHBoxLayout() - self.lh_flags_lyrics_embed.setObjectName("lh_flags_lyrics_embed") + self.lh_flag_lyrics_embed = QHBoxLayout() + self.lh_flag_lyrics_embed.setObjectName("lh_flag_lyrics_embed") self.cb_lyrics_embed = QCheckBox(self.gb_flags) self.cb_lyrics_embed.setObjectName("cb_lyrics_embed") - sizePolicy2 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) sizePolicy2.setHorizontalStretch(0) sizePolicy2.setVerticalStretch(0) sizePolicy2.setHeightForWidth(self.cb_lyrics_embed.sizePolicy().hasHeightForWidth()) self.cb_lyrics_embed.setSizePolicy(sizePolicy2) - self.lh_flags_lyrics_embed.addWidget(self.cb_lyrics_embed) + self.lh_flag_lyrics_embed.addWidget(self.cb_lyrics_embed) - self.lh_flags_2.addLayout(self.lh_flags_lyrics_embed) + self.lh_flags_2.addLayout(self.lh_flag_lyrics_embed) - self.lh_flags_lyrics_file = QHBoxLayout() - self.lh_flags_lyrics_file.setObjectName("lh_flags_lyrics_file") + self.lh_flag_lyrics_file = QHBoxLayout() + self.lh_flag_lyrics_file.setObjectName("lh_flag_lyrics_file") self.cb_lyrics_file = QCheckBox(self.gb_flags) self.cb_lyrics_file.setObjectName("cb_lyrics_file") sizePolicy1.setHeightForWidth(self.cb_lyrics_file.sizePolicy().hasHeightForWidth()) self.cb_lyrics_file.setSizePolicy(sizePolicy1) - self.lh_flags_lyrics_file.addWidget(self.cb_lyrics_file) + self.lh_flag_lyrics_file.addWidget(self.cb_lyrics_file) - self.lh_flags_2.addLayout(self.lh_flags_lyrics_file) + self.lh_flags_2.addLayout(self.lh_flag_lyrics_file) self.lv_flags.addLayout(self.lh_flags_2) @@ -118,18 +118,37 @@ class Ui_DialogSettings: self.lh_flag_3.addLayout(self.lv_flag_download_delay) - self.verticalLayout_4 = QVBoxLayout() - self.verticalLayout_4.setObjectName("verticalLayout_4") + self.lv_flag_extract_flac = QVBoxLayout() + self.lv_flag_extract_flac.setObjectName("lv_flag_extract_flac") + self.cb_extract_flac = QCheckBox(self.gb_flags) + self.cb_extract_flac.setObjectName("cb_extract_flac") + sizePolicy2.setHeightForWidth(self.cb_extract_flac.sizePolicy().hasHeightForWidth()) + self.cb_extract_flac.setSizePolicy(sizePolicy2) - self.lh_flag_3.addLayout(self.verticalLayout_4) + self.lv_flag_extract_flac.addWidget(self.cb_extract_flac) + + self.lh_flag_3.addLayout(self.lv_flag_extract_flac) self.lv_flags.addLayout(self.lh_flag_3) + self.horizontalLayout = QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.lv_flag_downgrade_on_hi_res = QVBoxLayout() + self.lv_flag_downgrade_on_hi_res.setObjectName("lv_flag_downgrade_on_hi_res") + self.cb_downgrade_on_hi_res = QCheckBox(self.gb_flags) + self.cb_downgrade_on_hi_res.setObjectName("cb_downgrade_on_hi_res") + + self.lv_flag_downgrade_on_hi_res.addWidget(self.cb_downgrade_on_hi_res) + + self.horizontalLayout.addLayout(self.lv_flag_downgrade_on_hi_res) + + self.lv_flags.addLayout(self.horizontalLayout) + self.lv_main.addWidget(self.gb_flags) self.gb_choices = QGroupBox(DialogSettings) self.gb_choices.setObjectName("gb_choices") - sizePolicy3 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) + sizePolicy3 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed) sizePolicy3.setHorizontalStretch(0) sizePolicy3.setVerticalStretch(0) sizePolicy3.setHeightForWidth(self.gb_choices.sizePolicy().hasHeightForWidth()) @@ -140,7 +159,7 @@ class Ui_DialogSettings: self.lh_choices_skip_existing.setObjectName("lh_choices_skip_existing") self.l_icon_skip_existing = QLabel(self.gb_choices) self.l_icon_skip_existing.setObjectName("l_icon_skip_existing") - sizePolicy4 = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + sizePolicy4 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed) sizePolicy4.setHorizontalStretch(0) sizePolicy4.setVerticalStretch(0) sizePolicy4.setHeightForWidth(self.l_icon_skip_existing.sizePolicy().hasHeightForWidth()) @@ -233,7 +252,7 @@ class Ui_DialogSettings: self.c_metadata_cover_dimension = QComboBox(self.gb_choices) self.c_metadata_cover_dimension.setObjectName("c_metadata_cover_dimension") - sizePolicy5 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) + sizePolicy5 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed) sizePolicy5.setHorizontalStretch(10) sizePolicy5.setVerticalStretch(0) sizePolicy5.setHeightForWidth(self.c_metadata_cover_dimension.sizePolicy().hasHeightForWidth()) @@ -311,7 +330,7 @@ class Ui_DialogSettings: self.lh_path_fmt_album.setObjectName("lh_path_fmt_album") self.l_icon_format_album = QLabel(self.gb_path) self.l_icon_format_album.setObjectName("l_icon_format_album") - sizePolicy6 = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) + sizePolicy6 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred) sizePolicy6.setHorizontalStretch(0) sizePolicy6.setVerticalStretch(0) sizePolicy6.setHeightForWidth(self.l_icon_format_album.sizePolicy().hasHeightForWidth()) @@ -386,22 +405,22 @@ class Ui_DialogSettings: self.verticalLayout = QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") - self.horizontalLayout_3 = QHBoxLayout() - self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.horizontalLayout_10 = QHBoxLayout() + self.horizontalLayout_10.setObjectName("horizontalLayout_10") self.le_download_base_path = QLineEdit(self.gb_path) self.le_download_base_path.setObjectName("le_download_base_path") sizePolicy2.setHeightForWidth(self.le_download_base_path.sizePolicy().hasHeightForWidth()) self.le_download_base_path.setSizePolicy(sizePolicy2) self.le_download_base_path.setDragEnabled(True) - self.horizontalLayout_3.addWidget(self.le_download_base_path) + self.horizontalLayout_10.addWidget(self.le_download_base_path) self.pb_download_base_path = QPushButton(self.gb_path) self.pb_download_base_path.setObjectName("pb_download_base_path") - self.horizontalLayout_3.addWidget(self.pb_download_base_path) + self.horizontalLayout_10.addWidget(self.pb_download_base_path) - self.verticalLayout.addLayout(self.horizontalLayout_3) + self.verticalLayout.addLayout(self.horizontalLayout_10) self.horizontalLayout_7 = QHBoxLayout() self.horizontalLayout_7.setObjectName("horizontalLayout_7") @@ -450,22 +469,22 @@ class Ui_DialogSettings: self.verticalLayout.addLayout(self.horizontalLayout_8) - self.horizontalLayout_31 = QHBoxLayout() - self.horizontalLayout_31.setObjectName("horizontalLayout_31") + self.horizontalLayout_3 = QHBoxLayout() + self.horizontalLayout_3.setObjectName("horizontalLayout_3") self.le_path_binary_ffmpeg = QLineEdit(self.gb_path) self.le_path_binary_ffmpeg.setObjectName("le_path_binary_ffmpeg") sizePolicy2.setHeightForWidth(self.le_path_binary_ffmpeg.sizePolicy().hasHeightForWidth()) self.le_path_binary_ffmpeg.setSizePolicy(sizePolicy2) self.le_path_binary_ffmpeg.setDragEnabled(True) - self.horizontalLayout_31.addWidget(self.le_path_binary_ffmpeg) + self.horizontalLayout_3.addWidget(self.le_path_binary_ffmpeg) self.pb_path_binary_ffmpeg = QPushButton(self.gb_path) self.pb_path_binary_ffmpeg.setObjectName("pb_path_binary_ffmpeg") - self.horizontalLayout_31.addWidget(self.pb_path_binary_ffmpeg) + self.horizontalLayout_3.addWidget(self.pb_path_binary_ffmpeg) - self.verticalLayout.addLayout(self.horizontalLayout_31) + self.verticalLayout.addLayout(self.horizontalLayout_3) self.horizontalLayout_2.addLayout(self.verticalLayout) @@ -501,6 +520,8 @@ class Ui_DialogSettings: self.cb_lyrics_embed.setText(QCoreApplication.translate("DialogSettings", "CheckBox", None)) self.cb_lyrics_file.setText(QCoreApplication.translate("DialogSettings", "CheckBox", None)) self.cb_download_delay.setText(QCoreApplication.translate("DialogSettings", "CheckBox", None)) + self.cb_extract_flac.setText(QCoreApplication.translate("DialogSettings", "CheckBox", None)) + self.cb_downgrade_on_hi_res.setText(QCoreApplication.translate("DialogSettings", "CheckBox", None)) self.gb_choices.setTitle(QCoreApplication.translate("DialogSettings", "Choices", None)) self.l_icon_skip_existing.setText(QCoreApplication.translate("DialogSettings", "TextLabel", None)) self.l_skip_existing.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 4dfa38c..962a19f 100644 --- a/tidal_dl_ng/ui/dialog_settings.ui +++ b/tidal_dl_ng/ui/dialog_settings.ui @@ -70,7 +70,7 @@ - + @@ -87,7 +87,7 @@ - + @@ -108,7 +108,7 @@ - + @@ -128,7 +128,7 @@ - + @@ -166,7 +166,36 @@ - + + + + + + 0 + 0 + + + + CheckBox + + + + + + + + + + + + + + + CheckBox + + + + @@ -519,41 +548,41 @@ - - - - - - 0 - 0 - - - - TextLabel - - - - - - - - 0 - 0 - - - - TextLabel - - - - - + + + + + + 0 + 0 + + + + TextLabel + + + + + + + + 0 + 0 + + + + TextLabel + + + + + - + @@ -619,29 +648,29 @@ - - - - - - 0 - 0 - - - - true - - - - - - - ... - - - - - + + + + + + 0 + 0 + + + + true + + + + + + + ... + + + + +