diff --git a/pyproject.toml b/pyproject.toml index 7473fa4..5fe8012 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -170,7 +170,7 @@ profile = "black" multi_line_output = 3 indent = 4 color_output = true -known_third_party = ["Crypto", "PySide6", "coloredlogs", "dataclasses_json", "ffmpeg", "m3u8", "mpegdash", "mutagen", "pathvalidate", "requests", "rich", "tidalapi", "typer"] +known_third_party = ["Crypto", "PySide6", "coloredlogs", "dataclasses_json", "ffmpeg", "helper", "m3u8", "mpegdash", "mutagen", "pathvalidate", "requests", "rich", "tidalapi", "typer"] [tool.mypy] # https://mypy.readthedocs.io/en/latest/config_file.html#using-a-pyproject-toml-file diff --git a/tidal_dl_ng/dialog.py b/tidal_dl_ng/dialog.py index b1901fd..34735a6 100644 --- a/tidal_dl_ng/dialog.py +++ b/tidal_dl_ng/dialog.py @@ -2,6 +2,7 @@ import os.path from enum import Enum from pathlib import Path +from helper.path import is_installed_ffmpeg from PySide6 import QtCore, QtGui, QtWidgets from tidalapi import Quality as QualityAudio @@ -86,19 +87,39 @@ class DialogPreferences(QtWidgets.QDialog): pixmapi: QtWidgets.QStyle.StandardPixmap = QtWidgets.QStyle.SP_MessageBoxQuestion self.icon = self.style().standardIcon(pixmapi) - self.parameters_checkboxes = [ - "lyrics_embed", - "lyrics_file", - "video_download", - "download_delay", - "video_convert_mp4", - ] - self.parameters_combo = [ - ("skip_existing", SkipExisting), - ("quality_audio", QualityAudio), - ("quality_video", QualityVideo), - ("metadata_cover_dimension", CoverDimensions), - ] + self._init_checkboxes() + self._init_comboboxes() + self._init_line_edit() + + # Create an instance of the GUI + self.ui = Ui_DialogSettings() + + # Run the .setupUi() method to show the GUI + self.ui.setupUi(self) + # Set data. + self.gui_populate() + self._init_signals() + + self.exec() + + def _init_signals(self): + self.ui.cb_video_convert_mp4.stateChanged.connect(self.on_cb_video_convert_mp4) + + def on_cb_video_convert_mp4(self, int): + if self.ui.cb_video_convert_mp4.isChecked(): + # Check if ffmpeg is in PATH otherwise show error message. + if not is_installed_ffmpeg(): + self.ui.cb_video_convert_mp4.setChecked(False) + self.ui.cb_video_convert_mp4.setCheckState(QtCore.Qt.CheckState.Unchecked) + QtWidgets.QMessageBox.critical( + self, + "FFmpeg not found!", + "Either FFmpeg is not installed on your computer or not set within " + "your PATH variable. You cannot activate this option until FFmpeg " + "is correctly installed and set to your environmental PATH variable.", + ) + + def _init_line_edit(self): self.parameters_line_edit = [ "download_base_path", "format_album", @@ -108,15 +129,22 @@ class DialogPreferences(QtWidgets.QDialog): "format_video", ] - # Create an instance of the GUI - self.ui = Ui_DialogSettings() + def _init_comboboxes(self): + self.parameters_combo = [ + ("skip_existing", SkipExisting), + ("quality_audio", QualityAudio), + ("quality_video", QualityVideo), + ("metadata_cover_dimension", CoverDimensions), + ] - # Run the .setupUi() method to show the GUI - self.ui.setupUi(self) - # Set data. - self.gui_populate() - - self.exec() + def _init_checkboxes(self): + self.parameters_checkboxes = [ + "lyrics_embed", + "lyrics_file", + "video_download", + "download_delay", + "video_convert_mp4", + ] def gui_populate(self): self.populate_checkboxes() @@ -190,7 +218,7 @@ class DialogPreferences(QtWidgets.QDialog): checkbox.setChecked(getattr(self.data, pn)) def accept(self): - # Get settings. TODO + # Get settings. self.to_settings() self.done(1) diff --git a/tidal_dl_ng/download.py b/tidal_dl_ng/download.py index 7cdbcca..2afdf20 100644 --- a/tidal_dl_ng/download.py +++ b/tidal_dl_ng/download.py @@ -29,7 +29,7 @@ from tidal_dl_ng.constants import ( ) from tidal_dl_ng.helper.decryption import decrypt_file, decrypt_security_token from tidal_dl_ng.helper.exceptions import MediaMissing, UnknownManifestFormat -from tidal_dl_ng.helper.path import check_file_exists, format_path_media, path_file_sanitize +from tidal_dl_ng.helper.path import check_file_exists, format_path_media, is_installed_ffmpeg, path_file_sanitize from tidal_dl_ng.helper.tidal import ( instantiate_media, items_results_all, @@ -82,6 +82,13 @@ class Download: self.progress = progress self.path_base = path_base + if not is_installed_ffmpeg() and self.settings.data.video_convert_mp4: + self.settings.data.video_convert_mp4 = False + self.fn_logger.error( + "Cannot find FFmpeg in PATH. Videos can be downloaded but will not be processed. " + "Make sure FFmpeg is installed correctly and present within your environmental PATH variable." + ) + def _download( self, media: Track | Video, @@ -284,7 +291,9 @@ class Download: def metadata_write(self, track: Track, path_file: str): result: bool = False release_date: str = ( - track.album.available_release_date.strftime("%Y-%m-%d") if track.album.available_release_date else track.album.release_date.strftime("%Y-%m-%d") if track.album.release_date else "" + track.album.available_release_date.strftime("%Y-%m-%d") + if track.album.available_release_date + else track.album.release_date.strftime("%Y-%m-%d") if track.album.release_date else "" ) 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 "" diff --git a/tidal_dl_ng/gui.py b/tidal_dl_ng/gui.py index 532dd02..38d9ccf 100644 --- a/tidal_dl_ng/gui.py +++ b/tidal_dl_ng/gui.py @@ -287,16 +287,16 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): self.thread_it(self.list_items_show_result, point=point) def on_download_list_media(self, point: QtCore.QPoint): - self.b_download.setEnabled(False) - self.b_download.setText("Downloading...") + self.pb_download.setEnabled(False) + self.pb_download.setText("Downloading...") item = self.tr_lists_user.itemAt(point) media = item.data(3, QtCore.Qt.ItemDataRole.UserRole) self.download(media, self.dl) - self.b_download.setText("Download") - self.b_download.setEnabled(True) + self.pb_download.setText("Download") + self.pb_download.setEnabled(True) def search_populate_results(self, query: str, type_media: SearchTypes): self.tr_results.clear() @@ -447,11 +447,11 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): return result def _init_signals(self): - self.b_download.clicked.connect(lambda: self.thread_it(self.on_download_results)) + self.pb_download.clicked.connect(lambda: self.thread_it(self.on_download_results)) self.l_search.returnPressed.connect( lambda: self.search_populate_results(self.l_search.text(), self.cb_search_type.currentData()) ) - self.b_search.clicked.connect( + self.pb_search.clicked.connect( lambda: self.search_populate_results(self.l_search.text(), self.cb_search_type.currentData()) ) self.cb_quality_audio.currentIndexChanged.connect(self.on_quality_set_audio) @@ -543,8 +543,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): self.threadpool.start(worker) def on_download_results(self): - self.b_download.setEnabled(False) - self.b_download.setText("Downloading...") + self.pb_download.setEnabled(False) + self.pb_download.setText("Downloading...") items: [QtWidgets.QTreeWidgetItem] = self.tr_results.selectedItems() @@ -564,8 +564,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): self.download(media, self.dl, delay_track=download_delay) - self.b_download.setText("Download") - self.b_download.setEnabled(True) + self.pb_download.setText("Download") + self.pb_download.setEnabled(True) def download(self, media: Track | Album | Playlist | Video | Mix, dl: Download, delay_track: bool = False) -> None: self.s_pb_reset.emit() diff --git a/tidal_dl_ng/helper/path.py b/tidal_dl_ng/helper/path.py index 2fc7d16..2b739d6 100644 --- a/tidal_dl_ng/helper/path.py +++ b/tidal_dl_ng/helper/path.py @@ -2,6 +2,7 @@ import glob import math import os import re +import shutil from pathlib import Path, PosixPath from pathvalidate import sanitize_filename, sanitize_filepath @@ -10,7 +11,7 @@ from tidalapi import Album, Mix, Playlist, Track, UserPlaylist, Video from tidal_dl_ng import __name_display__ from tidal_dl_ng.constants import FILENAME_SANITIZE_PLACEHOLDER, UNIQUIFY_THRESHOLD, AudioExtensions, MediaType -from tidal_dl_ng.helper.tidal import name_builder_artist, name_builder_title, name_builder_album_artist +from tidal_dl_ng.helper.tidal import name_builder_album_artist, name_builder_artist, name_builder_title def path_home() -> str: @@ -261,3 +262,10 @@ def check_file_exists(path_file: str, extension_ignore: bool = False) -> bool: result = bool(sum([glob.glob(_file) for _file in path_files], [])) return result + + +def is_installed_ffmpeg() -> bool: + # Checks if ffmpeg is found in PATH. + result: bool = bool(shutil.which("ffmpeg")) + + return result diff --git a/tidal_dl_ng/helper/tidal.py b/tidal_dl_ng/helper/tidal.py index 77dd72d..2dde6b2 100644 --- a/tidal_dl_ng/helper/tidal.py +++ b/tidal_dl_ng/helper/tidal.py @@ -1,5 +1,5 @@ from tidalapi import Album, Mix, Playlist, Session, Track, UserPlaylist, Video -from tidalapi.artist import Role, Artist +from tidalapi.artist import Artist, Role from tidalapi.session import SearchTypes from tidal_dl_ng.constants import MediaType diff --git a/tidal_dl_ng/ui/main.py b/tidal_dl_ng/ui/main.py index 8ee8f4c..5f0ef0a 100644 --- a/tidal_dl_ng/ui/main.py +++ b/tidal_dl_ng/ui/main.py @@ -187,26 +187,26 @@ class Ui_MainWindow: self.lh_search.addWidget(self.cb_search_type) - self.b_search = QPushButton(self.w_central) - self.b_search.setObjectName("b_search") + self.pb_search = QPushButton(self.w_central) + self.pb_search.setObjectName("pb_search") # if QT_CONFIG(statustip) - self.b_search.setStatusTip("") + self.pb_search.setStatusTip("") # endif // QT_CONFIG(statustip) # if QT_CONFIG(whatsthis) - self.b_search.setWhatsThis("") + self.pb_search.setWhatsThis("") # endif // QT_CONFIG(whatsthis) # if QT_CONFIG(accessibility) - self.b_search.setAccessibleName("") + self.pb_search.setAccessibleName("") # endif // QT_CONFIG(accessibility) # if QT_CONFIG(accessibility) - self.b_search.setAccessibleDescription("") + self.pb_search.setAccessibleDescription("") # endif // QT_CONFIG(accessibility) - self.b_search.setText("Search") + self.pb_search.setText("Search") # if QT_CONFIG(shortcut) - self.b_search.setShortcut("") + self.pb_search.setShortcut("") # endif // QT_CONFIG(shortcut) - self.lh_search.addWidget(self.b_search) + self.lh_search.addWidget(self.pb_search) self.lv_search_result.addLayout(self.lh_search) @@ -315,29 +315,29 @@ class Ui_MainWindow: self.lh_download.addWidget(self.cb_quality_video) - self.b_download = QPushButton(self.w_central) - self.b_download.setObjectName("b_download") + self.pb_download = QPushButton(self.w_central) + self.pb_download.setObjectName("pb_download") # if QT_CONFIG(tooltip) - self.b_download.setToolTip("") + self.pb_download.setToolTip("") # endif // QT_CONFIG(tooltip) # if QT_CONFIG(statustip) - self.b_download.setStatusTip("") + self.pb_download.setStatusTip("") # endif // QT_CONFIG(statustip) # if QT_CONFIG(whatsthis) - self.b_download.setWhatsThis("") + self.pb_download.setWhatsThis("") # endif // QT_CONFIG(whatsthis) # if QT_CONFIG(accessibility) - self.b_download.setAccessibleName("") + self.pb_download.setAccessibleName("") # endif // QT_CONFIG(accessibility) # if QT_CONFIG(accessibility) - self.b_download.setAccessibleDescription("") + self.pb_download.setAccessibleDescription("") # endif // QT_CONFIG(accessibility) - self.b_download.setText("Download") + self.pb_download.setText("Download") # if QT_CONFIG(shortcut) - self.b_download.setShortcut("") + self.pb_download.setShortcut("") # endif // QT_CONFIG(shortcut) - self.lh_download.addWidget(self.b_download) + self.lh_download.addWidget(self.pb_download) self.lh_download.setStretch(0, 5) self.lh_download.setStretch(2, 5) diff --git a/tidal_dl_ng/ui/main.ui b/tidal_dl_ng/ui/main.ui index ea018c6..26b7270 100644 --- a/tidal_dl_ng/ui/main.ui +++ b/tidal_dl_ng/ui/main.ui @@ -246,7 +246,7 @@ - + @@ -436,7 +436,7 @@ - +