diff --git a/pyproject.toml b/pyproject.toml index 74fe3ea..dc89604 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -171,7 +171,7 @@ profile = "black" multi_line_output = 3 indent = 4 color_output = true -known_third_party = ["Crypto", "PySide6", "coloredlogs", "dataclasses_json", "ffmpeg", "m3u8", "mutagen", "pathvalidate", "requests", "rich", "tidalapi", "toml", "typer"] +known_third_party = ["Crypto", "PySide6", "coloredlogs", "constants", "dataclasses_json", "ffmpeg", "m3u8", "mutagen", "pathvalidate", "requests", "rich", "tidalapi", "toml", "typer"] [tool.mypy] # https://mypy.readthedocs.io/en/latest/config_file.html#using-a-pyproject-toml-file diff --git a/tidal_dl_ng/config.py b/tidal_dl_ng/config.py index a3ab809..1d17953 100644 --- a/tidal_dl_ng/config.py +++ b/tidal_dl_ng/config.py @@ -1,3 +1,4 @@ +import json import os import shutil from collections.abc import Callable @@ -31,7 +32,9 @@ class BaseConfig: os.makedirs(self.path_base, exist_ok=True) with open(self.file_path, encoding="utf-8", mode="w") as f: - f.write(data_json) + # Save it in a pretty format + obj_json_config = json.loads(data_json) + json.dump(obj_json_config, f, indent=4) def set_option(self, key: str, value: Any) -> None: value_old: Any = getattr(self.data, key) diff --git a/tidal_dl_ng/constants.py b/tidal_dl_ng/constants.py index 979b786..d3ea2b8 100644 --- a/tidal_dl_ng/constants.py +++ b/tidal_dl_ng/constants.py @@ -5,6 +5,7 @@ REQUESTS_TIMEOUT_SEC: int = 45 EXTENSION_LYRICS: str = ".lrc" UNIQUIFY_THRESHOLD: int = 99 FILENAME_SANITIZE_PLACEHOLDER: str = "_" +COVER_NAME: str = "cover.jpg" class QualityVideo(StrEnum): diff --git a/tidal_dl_ng/dialog.py b/tidal_dl_ng/dialog.py index 23f1dc8..c035867 100644 --- a/tidal_dl_ng/dialog.py +++ b/tidal_dl_ng/dialog.py @@ -171,6 +171,8 @@ class DialogPreferences(QtWidgets.QDialog): "download_delay", "video_convert_mp4", "extract_flac", + "metadata_cover_embed", + "cover_album_file", ] def gui_populate(self): diff --git a/tidal_dl_ng/download.py b/tidal_dl_ng/download.py index 9dbd46d..b6d5f13 100644 --- a/tidal_dl_ng/download.py +++ b/tidal_dl_ng/download.py @@ -10,6 +10,7 @@ from uuid import uuid4 import ffmpeg import m3u8 import requests +from constants import COVER_NAME from requests.exceptions import HTTPError from rich.progress import Progress, TaskID from tidalapi import Album, Mix, Playlist, Session, Track, UserPlaylist, Video @@ -189,6 +190,7 @@ class Download: download_delay: bool = False, quality_audio: Quality | None = None, quality_video: QualityVideo | None = None, + is_parent_album: bool = False, ) -> (bool, str): try: if media_id and media_type: @@ -278,10 +280,13 @@ class Download: tmp_path_file = self._extract_flac(tmp_path_file) tmp_path_lyrics: pathlib.Path | None = None + tmp_path_cover: pathlib.Path | None = None # Write metadata to file. if not isinstance(media, Video): - result_metadata, tmp_path_lyrics = self.metadata_write(media, tmp_path_file) + result_metadata, tmp_path_lyrics, tmp_path_cover = self.metadata_write( + media, tmp_path_file, is_parent_album + ) # Move final file to the configured destination directory. os.makedirs(os.path.dirname(path_media_dst), exist_ok=True) @@ -289,7 +294,11 @@ class Download: # Move lyrics file if self.settings.data.lyrics_file and not isinstance(media, Video): - self._move_lyrics(path_media_dst, tmp_path_lyrics) + self._move_lyrics(tmp_path_lyrics, path_media_dst) + + # Move cover file + if self.settings.data.cover_album_file: + self._move_cover(tmp_path_cover, path_media_dst) else: self.fn_logger.debug(f"Download skipped, since file exists: '{path_media_dst}'") @@ -327,13 +336,13 @@ class Download: return quality_old - def _move_lyrics(self, file_media_dst: str, path_lyrics: pathlib.Path) -> bool: + def _move_file(self, path_file_source: pathlib.Path, path_file_destination: str | pathlib.Path) -> bool: result: bool - # Build tmp lyrics filename + # Check if the file was downloaded - if path_lyrics and os.path.isfile(path_lyrics): + if path_file_source and os.path.isfile(path_file_source): # Move it. - shutil.move(path_lyrics, os.path.splitext(file_media_dst)[0] + EXTENSION_LYRICS) + shutil.move(path_file_source, path_file_destination) result = True else: @@ -341,20 +350,64 @@ class Download: return result - def lyrics_write_file(self, dir_destination: pathlib.Path, lyrics: str) -> str: + def _move_lyrics(self, path_lyrics: pathlib.Path, file_media_dst: str) -> bool: + # Build tmp lyrics filename + path_file_lyrics: str = os.path.splitext(file_media_dst)[0] + EXTENSION_LYRICS + result: bool = self._move_file(path_lyrics, path_file_lyrics) + + return result + + def _move_cover(self, path_cover: pathlib.Path, file_media_dst: str) -> bool: + # Build tmp lyrics filename + path_file_cover: pathlib.Path = pathlib.Path(file_media_dst).parent.absolute() / COVER_NAME + result: bool = self._move_file(path_cover, path_file_cover) + + return result + + def lyrics_to_file(self, dir_destination: pathlib.Path, lyrics: str) -> str: + return self.write_to_tmp_file(dir_destination, mode="x", content=lyrics) + + def cover_to_file(self, dir_destination: pathlib.Path, image: bytes) -> str: + return self.write_to_tmp_file(dir_destination, mode="xb", content=image) + + def write_to_tmp_file(self, dir_destination: pathlib.Path, mode: str, content: str | bytes) -> str: result: str = dir_destination / str(uuid4()) + encoding: str | None = "utf-8" if isinstance(content, str) else None try: - with open(result, "x", encoding="utf-8") as f: - f.write(lyrics) + with open(result, mode=mode, encoding=encoding) as f: + f.write(content) except: result = "" return result - def metadata_write(self, track: Track, path_media: str) -> (bool, pathlib.Path | None): + @staticmethod + def cover_data(url: str = None, path_file: str = None) -> str | bytes: + result: str | bytes = "" + + if url: + try: + result = requests.get(url, timeout=REQUESTS_TIMEOUT_SEC).content + except Exception as e: + # TODO: Implement propper logging. + print(e) + elif path_file: + try: + with open(path_file, "rb") as f: + result = f.read() + except OSError as e: + # TODO: Implement propper logging. + print(e) + + return result + + def metadata_write( + self, track: Track, path_media: str, is_parent_album: str + ) -> (bool, pathlib.Path | None, pathlib.Path | None): result: bool = False path_lyrics: pathlib.Path | None = None + path_cover: pathlib.Path | None = None release_date: str = ( track.album.available_release_date.strftime("%Y-%m-%d") if track.album.available_release_date @@ -363,6 +416,7 @@ class Download: copy_right: str = track.copyright if hasattr(track, "copyright") and track.copyright else "" isrc: str = track.isrc if hasattr(track, "isrc") and track.isrc else "" lyrics: str = "" + cover_data: bytes = None if self.settings.data.lyrics_embed or self.settings.data.lyrics_file: # Try to retrieve lyrics. @@ -379,7 +433,14 @@ class Download: print(f"Could not retrieve lyrics for `{name_builder_item(track)}`.") if lyrics and self.settings.data.lyrics_file: - path_lyrics = self.lyrics_write_file(pathlib.Path(path_media).parent, lyrics) + path_lyrics = self.lyrics_to_file(pathlib.Path(path_media).parent, lyrics) + + if self.settings.data.metadata_cover_embed or (self.settings.data.cover_album_file and is_parent_album): + url_cover = track.album.image(int(self.settings.data.metadata_cover_dimension)) + cover_data = self.cover_data(url=url_cover) + + if cover_data and self.settings.data.cover_album_file and is_parent_album: + path_cover = self.cover_to_file(pathlib.Path(path_media).parent, cover_data) # `None` values are not allowed. m: Metadata = Metadata( @@ -396,14 +457,14 @@ class Download: totaltrack=track.album.num_tracks if track.album and track.album.num_tracks else 1, totaldisc=track.album.num_volumes if track.album and track.album.num_volumes else 1, discnumber=track.volume_num if track.volume_num else 1, - url_cover=track.album.image(int(self.settings.data.metadata_cover_dimension)), + cover_data=cover_data if self.settings.data.metadata_cover_embed else None, ) m.save() result = True - return result, path_lyrics + return result, path_lyrics, path_cover def items( self, @@ -451,16 +512,19 @@ class Download: f"[green]List '{list_media_name}'", total=len(items), visible=progress_stdout ) + is_album: bool = isinstance(media, Album) + # Iterate through list items while not self.progress.finished: - for media in items: + for item_media in items: # Download the item. status_download, result_path_file = self.item( - media=media, + 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, ) # Advance progress bar. diff --git a/tidal_dl_ng/gui.py b/tidal_dl_ng/gui.py index 55e2b97..7aa2150 100644 --- a/tidal_dl_ng/gui.py +++ b/tidal_dl_ng/gui.py @@ -31,7 +31,6 @@ from tidal_dl_ng.helper.tidal import ( search_results_all, user_media_lists, ) -from tidal_dl_ng.metadata import Metadata try: import qdarktheme @@ -249,11 +248,16 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): tree.setColumnWidth(3, 150) tree.setColumnWidth(4, 150) header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents) + # Connect the contextmenu + tree.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + tree.customContextMenuRequested.connect(self.menu_context_tree_results) def _init_tree_results_model(self, model: QtGui.QStandardItemModel) -> None: - model.setColumnCount(7) + labels_column: [str] = ["#", "obj", "Artist", "Title", "Album", "Duration", "Quality", "Date Added"] + + model.setColumnCount(len(labels_column)) model.setRowCount(0) - model.setHorizontalHeaderLabels(["#", "obj", "Artist", "Title", "Album", "Duration", "Quality"]) + model.setHorizontalHeaderLabels(labels_column) def _init_tree_queue(self, tree: QtWidgets.QTableWidget): tree.setColumnHidden(1, True) @@ -359,7 +363,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): self.spinner.stop() self.spinner = None - def menu_context_tree_lists(self, point): + def menu_context_tree_lists(self, point: QtCore.QPoint): # Infos about the node selected. index = self.tr_lists_user.indexAt(point) @@ -370,12 +374,46 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): # We build the menu. menu = QtWidgets.QMenu() menu.addAction("Download Playlist", lambda: self.thread_download_list_media(point)) + menu.addAction("Copy Share URL", lambda: self.thread_copy_url_share(self.tr_lists_user, point)) menu.exec(self.tr_lists_user.mapToGlobal(point)) - def thread_download_list_media(self, point): + def menu_context_tree_results(self, point: QtCore.QPoint): + # Infos about the node selected. + index = self.tr_results.indexAt(point) + + # Do not open menu if something went wrong or a parent node is clicked. + if not index.isValid(): + return + + # We build the menu. + menu = QtWidgets.QMenu() + menu.addAction("Copy Share URL", lambda: self.thread_copy_url_share(self.tr_results, point)) + + menu.exec(self.tr_results.mapToGlobal(point)) + + def thread_download_list_media(self, point: QtCore.QPoint): self.thread_it(self.on_download_list_media, point) - self.thread_it(self.list_items_show_result, point=point) + + def thread_copy_url_share(self, tree_target: QtWidgets.QTreeWidget, point: QtCore.QPoint): + self.thread_it(self.on_copy_url_share, tree_target, point) + + def on_copy_url_share(self, tree_target: QtWidgets.QTreeWidget | QtWidgets.QTreeView, point: QtCore.QPoint = None): + if isinstance(tree_target, QtWidgets.QTreeWidget): + + item: QtWidgets.QTreeWidgetItem = tree_target.itemAt(point) + media: Album | Artist | Mix | Playlist = get_user_list_media_item(item) + else: + index: QtCore.QModelIndex = tree_target.indexAt(point) + media: Track | Video | Album | Artist | Mix | Playlist = get_results_media_item( + index, self.proxy_tr_results, self.model_tr_results + ) + + clipboard = QtWidgets.QApplication.clipboard() + url_share = media.share_url if hasattr(media, "share_url") else "No share URL available." + + clipboard.clear() + clipboard.setText(url_share) def on_download_list_media(self, point: QtCore.QPoint = None): items: [QtWidgets.QTreeWidgetItem] @@ -444,6 +482,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): child_album: QtGui.QStandardItem = QtGui.QStandardItem(item.album) child_duration: QtGui.QStandardItem = QtGui.QStandardItem(duration) child_quality: QtGui.QStandardItem = QtGui.QStandardItem(item.quality) + child_date_added: QtGui.QStandardItem = QtGui.QStandardItem(item.date_user_added) 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. @@ -452,7 +491,16 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): child_dummy.setEnabled(False) child_index.appendRow(child_dummy) - return child_index, child_obj, child_artist, child_title, child_album, child_duration, child_quality + return ( + child_index, + child_obj, + child_artist, + child_title, + child_album, + child_duration, + child_quality, + child_date_added, + ) def on_tr_results_add_top_level_item(self, item_child: Sequence[QtGui.QStandardItem]): self.model_tr_results.appendRow(item_child) @@ -501,6 +549,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): if isinstance(item, Track | Video | Album): explicit = " 🅴" if item.explicit else "" + date_user_added: str = item.user_date_added.strftime("%Y-%m-%d_%H:%M") if item.user_date_added else "" + if isinstance(item, Track): result_item: ResultItem = ResultItem( position=idx, @@ -511,6 +561,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): obj=item, quality=quality_audio_highest(item), explicit=bool(item.explicit), + date_user_added=date_user_added, ) result.append(result_item) @@ -524,6 +575,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): obj=item, quality=item.video_quality, explicit=bool(item.explicit), + date_user_added=date_user_added, ) result.append(result_item) @@ -537,6 +589,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): obj=item, quality="", explicit=False, + date_user_added=date_user_added, ) result.append(result_item) @@ -550,6 +603,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): obj=item, quality=quality_audio_highest(item), explicit=bool(item.explicit), + date_user_added=date_user_added, ) result.append(result_item) @@ -564,6 +618,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): obj=item, quality="", explicit=False, + date_user_added=date_user_added, ) result.append(result_item) @@ -577,6 +632,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): obj=item, quality="", explicit=False, + date_user_added=date_user_added, ) result.append(result_item) @@ -757,7 +813,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): if cover_url and self.cover_url_current != cover_url: self.cover_url_current = cover_url - data_cover: bytes = Metadata.cover_data(cover_url) + data_cover: bytes = Download.cover_data(cover_url) pixmap: QtGui.QPixmap = QtGui.QPixmap() pixmap.loadFromData(data_cover) self.l_pm_cover.setPixmap(pixmap) diff --git a/tidal_dl_ng/metadata.py b/tidal_dl_ng/metadata.py index b70b4c8..d9ecb8c 100644 --- a/tidal_dl_ng/metadata.py +++ b/tidal_dl_ng/metadata.py @@ -1,10 +1,7 @@ import mutagen -import requests from mutagen import flac, id3, mp4 from mutagen.id3 import APIC, TALB, TCOM, TCOP, TDRC, TIT2, TOPE, TPE1, TRCK, TSRC, USLT -from tidal_dl_ng.constants import REQUESTS_TIMEOUT_SEC - class Metadata: path_file: str @@ -22,7 +19,7 @@ class Metadata: isrc: str lyrics: str path_cover: str - url_cover: str + cover_data: bytes m: mutagen.mp4.MP4 | mutagen.mp4.MP4 | mutagen.flac.FLAC def __init__( @@ -41,8 +38,7 @@ class Metadata: albumartist: str = "", date: str = "", lyrics: str = "", - path_cover: str = "", - url_cover: str = "", + cover_data: bytes = None, ): self.path_file = path_file self.title = title @@ -58,27 +54,25 @@ class Metadata: self.composer = composer self.isrc = isrc self.lyrics = lyrics - self.path_cover = path_cover - self.url_cover = url_cover + self.cover_data = cover_data self.m: mutagen.mp4.MP4 | mutagen.flac.FLAC | mutagen.mp3.MP3 = mutagen.File(self.path_file) def _cover(self) -> bool: result: bool = False - data_cover: str | bytes = self.cover_data(url=self.url_cover, path_file=self.path_cover) - if data_cover: + if self.cover_data: if isinstance(self.m, mutagen.flac.FLAC): flac_cover = flac.Picture() flac_cover.type = id3.PictureType.COVER_FRONT - flac_cover.data = data_cover + flac_cover.data = self.cover_data flac_cover.mime = "image/jpeg" self.m.clear_pictures() self.m.add_picture(flac_cover) elif isinstance(self.m, mutagen.mp3.MP3): - self.m.tags.add(APIC(encoding=3, data=data_cover)) + self.m.tags.add(APIC(encoding=3, data=self.cover_data)) elif isinstance(self.m, mutagen.mp4.MP4): - cover_mp4 = mp4.MP4Cover(data_cover) + cover_mp4 = mp4.MP4Cover(self.cover_data) self.m.tags["covr"] = [cover_mp4] result = True @@ -142,23 +136,3 @@ class Metadata: self.m.tags["\xa9wrt"] = ", ".join(self.composer) if self.composer else "" self.m.tags["\xa9lyr"] = self.lyrics self.m.tags["isrc"] = self.isrc - - @staticmethod - def cover_data(url: str = None, path_file: str = None) -> str | bytes: - result: str | bytes = "" - - if url: - try: - result = requests.get(url, timeout=REQUESTS_TIMEOUT_SEC).content - except Exception as e: - # TODO: Implement propper logging. - print(e) - elif path_file: - try: - with open(path_file, "rb") as f: - result = f.read() - except OSError as e: - # TODO: Implement propper logging. - print(e) - - return result diff --git a/tidal_dl_ng/model/cfg.py b/tidal_dl_ng/model/cfg.py index 2c00438..fae3590 100644 --- a/tidal_dl_ng/model/cfg.py +++ b/tidal_dl_ng/model/cfg.py @@ -10,8 +10,6 @@ from tidal_dl_ng.constants import CoverDimensions, QualityVideo, SkipExisting @dataclass class Settings: skip_existing: SkipExisting = SkipExisting.Disabled - # TODO: Implement cover download to a separate file. - # album_cover_save: bool = True lyrics_embed: bool = False lyrics_file: bool = False # TODO: Implement API KEY selection. @@ -36,6 +34,8 @@ class Settings: video_convert_mp4: bool = True path_binary_ffmpeg: str = "" metadata_cover_dimension: CoverDimensions = CoverDimensions.Px320 + metadata_cover_embed: bool = True + cover_album_file: bool = True extract_flac: bool = True @@ -79,6 +79,8 @@ class HelpSettings: metadata_cover_dimension: str = ( "The dimensions of the cover image embedded into the track. Possible values: 320x320, 640x640x 1280x1280." ) + metadata_cover_embed: str = "Embed album cover into file." + 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)." diff --git a/tidal_dl_ng/model/gui_data.py b/tidal_dl_ng/model/gui_data.py index 8c7c30e..186ed98 100644 --- a/tidal_dl_ng/model/gui_data.py +++ b/tidal_dl_ng/model/gui_data.py @@ -28,6 +28,7 @@ class ResultItem: obj: object quality: str explicit: bool + date_user_added: str @dataclass diff --git a/tidal_dl_ng/ui/dialog_settings.py b/tidal_dl_ng/ui/dialog_settings.py index 15ac0fe..ad45f1d 100644 --- a/tidal_dl_ng/ui/dialog_settings.py +++ b/tidal_dl_ng/ui/dialog_settings.py @@ -78,8 +78,8 @@ class Ui_DialogSettings: self.lh_flags_2 = QHBoxLayout() self.lh_flags_2.setObjectName("lh_flags_2") - self.lh_flag_lyrics_embed = QHBoxLayout() - self.lh_flag_lyrics_embed.setObjectName("lh_flag_lyrics_embed") + self.lv_flag_lyrics_embed = QVBoxLayout() + self.lv_flag_lyrics_embed.setObjectName("lv_flag_lyrics_embed") self.cb_lyrics_embed = QCheckBox(self.gb_flags) self.cb_lyrics_embed.setObjectName("cb_lyrics_embed") sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) @@ -88,20 +88,20 @@ class Ui_DialogSettings: sizePolicy2.setHeightForWidth(self.cb_lyrics_embed.sizePolicy().hasHeightForWidth()) self.cb_lyrics_embed.setSizePolicy(sizePolicy2) - self.lh_flag_lyrics_embed.addWidget(self.cb_lyrics_embed) + self.lv_flag_lyrics_embed.addWidget(self.cb_lyrics_embed) - self.lh_flags_2.addLayout(self.lh_flag_lyrics_embed) + self.lh_flags_2.addLayout(self.lv_flag_lyrics_embed) - self.lh_flag_lyrics_file = QHBoxLayout() - self.lh_flag_lyrics_file.setObjectName("lh_flag_lyrics_file") + self.lv_flag_lyrics_file = QVBoxLayout() + self.lv_flag_lyrics_file.setObjectName("lv_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_flag_lyrics_file.addWidget(self.cb_lyrics_file) + self.lv_flag_lyrics_file.addWidget(self.cb_lyrics_file) - self.lh_flags_2.addLayout(self.lh_flag_lyrics_file) + self.lh_flags_2.addLayout(self.lv_flag_lyrics_file) self.lv_flags.addLayout(self.lh_flags_2) @@ -131,6 +131,28 @@ class Ui_DialogSettings: self.lv_flags.addLayout(self.lh_flag_3) + self.lh_flags_4 = QHBoxLayout() + self.lh_flags_4.setObjectName("lh_flags_4") + self.lv_flag_metadata_cover_embed = QVBoxLayout() + self.lv_flag_metadata_cover_embed.setObjectName("lv_flag_metadata_cover_embed") + self.cb_metadata_cover_embed = QCheckBox(self.gb_flags) + self.cb_metadata_cover_embed.setObjectName("cb_metadata_cover_embed") + + self.lv_flag_metadata_cover_embed.addWidget(self.cb_metadata_cover_embed) + + self.lh_flags_4.addLayout(self.lv_flag_metadata_cover_embed) + + self.lv_flag_cover_album_file = QVBoxLayout() + self.lv_flag_cover_album_file.setObjectName("lv_flag_cover_album_file") + self.cb_cover_album_file = QCheckBox(self.gb_flags) + self.cb_cover_album_file.setObjectName("cb_cover_album_file") + + self.lv_flag_cover_album_file.addWidget(self.cb_cover_album_file) + + self.lh_flags_4.addLayout(self.lv_flag_cover_album_file) + + self.lv_flags.addLayout(self.lh_flags_4) + self.lv_main.addWidget(self.gb_flags) self.gb_choices = QGroupBox(DialogSettings) @@ -508,6 +530,8 @@ class Ui_DialogSettings: 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_metadata_cover_embed.setText(QCoreApplication.translate("DialogSettings", "CheckBox", None)) + self.cb_cover_album_file.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 da0fc4a..9bc894d 100644 --- a/tidal_dl_ng/ui/dialog_settings.ui +++ b/tidal_dl_ng/ui/dialog_settings.ui @@ -108,7 +108,7 @@ - + @@ -128,7 +128,7 @@ - + @@ -184,6 +184,32 @@ + + + + + + + + CheckBox + + + + + + + + + + + CheckBox + + + + + + +