Merge remote-tracking branch 'warry-github/master' into feature/informations-window

# Conflicts:
#	CONTRIBUTING.md
#	README.md
This commit is contained in:
Warry
2025-12-03 09:35:42 +01:00
9 changed files with 41 additions and 27 deletions
+1
View File
@@ -1,4 +1,5 @@
docs/source
vs_code/
# From https://raw.githubusercontent.com/github/gitignore/main/Python.gitignore
+1 -1
View File
@@ -29,7 +29,7 @@ Anything tagged with "enhancement" and "help wanted" is open to whoever wants to
## Write Documentation
Cookiecutter PyPackage could always use more documentation, whether as part of the official docs, in docstrings, or even on the web in blog posts, articles, and such.
The TIDAL Downloader project could always use more documentation, whether as part of the official docs, in docstrings, or even on the web in blog posts, articles, and such.
## Submit Feedback
+9 -9
View File
@@ -5,7 +5,7 @@
[![Commit activity](https://img.shields.io/github/commit-activity/m/exislow/tidal-dl-ng)](https://img.shields.io/github/commit-activity/m/exislow/tidal-dl-ng)
[![License](https://img.shields.io/github/license/exislow/tidal-dl-ng)](https://img.shields.io/github/license/exislow/tidal-dl-ng)
This tool allows to download songs and videos from TIDAL. Multithreaded and multi-chunked downloads are supported.
This tool allows you to download songs and videos from TIDAL. Multithreaded and multi-chunked downloads are supported.
⚠️ **Windows** Defender / **Anti Virus** software / web browser alerts, while you try to download the app binary: This
is a **false positive**. Please
@@ -39,7 +39,7 @@ $ tidal-dl-ng --help
╰──────────────────────────────────────────────────────────────────────────────╯
```
If you like this projects and want to support it, feel free to buy me a coffee 🙃✌️
If you like this project and want to support it, feel free to buy me a coffee 🙃✌️
<a href="https://www.buymeacoffee.com/exislow" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/arial-orange.png" alt="Buy Me A Coffee" style="height: 51px !important;width: 217px !important;" ></a>
<a href="https://ko-fi.com/exislow" target="_blank" rel="noopener noreferrer"><img src="https://help.ko-fi.com/hc/article_attachments/11833788361117" alt="61e11d430afb112ea33c3aa5_Button-1-p-500"></a>
@@ -83,8 +83,8 @@ tdng
tidal-dl-ng gui
```
If you like to have the GUI version only as a binary, have a look at the
[release page](https://github.com/exislow/tidal-dl-ng/releases) and download the correct version for your platform.
If you would like to use the GUI version as a binary, have a look at the
[release page](https://github.com/exislow/tidal-dl-ng/releases) and download the correct version for your OS.
## 🧁 Features
@@ -121,13 +121,13 @@ tidal_ng_dl/gui.py
### 📺 GUI Builder
The GUI is build with `PySide6` using the [Qt Designer](https://doc.qt.io/qt-6/qtdesigner-manual.html):
The GUI is built with `PySide6` using the [Qt Designer](https://doc.qt.io/qt-6/qtdesigner-manual.html):
```bash
PYSIDE_DESIGNER_PLUGINS=tidal_dl_ng/ui pyside6-designer
```
After all changes are saved you need to translate the Qt Designer `*.ui` file into Python code, for instance:
After all changes are saved, you need to translate the Qt Designer `*.ui` file into Python code, for instance:
```
pyside6-uic tidal_dl_ng/ui/main.ui -o tidal_dl_ng/ui/main.py
@@ -144,9 +144,9 @@ To build the project use this command:
make install
# Build macOS GUI
make gui-macos-dmg
# OR Build macOS GUI
# OR Build Windows GUI
make gui-windows
# OR Build macOS GUI
# OR Build Linux GUI
make gui-linux
# Check build output
ls dist/
@@ -206,7 +206,7 @@ sudo apt install libxcb-cursor0
### A terminal is flashing when I run this app on Windows
Please see this isse [#103](https://github.com/exislow/tidal-dl-ng/issues/103).
Please see this issue [#103](https://github.com/exislow/tidal-dl-ng/issues/103).
This is due to the Python `ffmpeg` library which is used and only happens on windows if `extract_flac` is activated.
+2 -2
View File
@@ -2,7 +2,7 @@
name = "tidal-dl-ng"
authors = [{ name = "Robert Honz", email = "<cen.sored@gmail.com>" }]
version = "0.31.4"
description = "TIDAL Medial Downloader Next Generation!"
description = "TIDAL Media Downloader Next Generation!"
readme = "README.md"
license = "AGPL-3.0-only"
license-files = ["LICENSE"]
@@ -132,7 +132,7 @@ ignore = [
# Do not use bare `except`
"E722",
# PEP 484 prohibits implicit `Optional`
# Disabled only temporarely.
# Disabled only temporarily.
"RUF013",
]
+5 -5
View File
@@ -171,7 +171,7 @@ def _process_url(
return False
if "http" not in url:
print(f"It seems like that you have supplied an invalid URL: {url}")
print(f"It seems like you have supplied an invalid URL: {url}")
return True
url_clean: str = url_ending_clean(url)
@@ -286,7 +286,7 @@ def settings_management(
Args:
names (list[str] | None, optional): None (list all options), one (list the value only for this option) or two arguments (set the value for the option). Defaults to None.
editor (bool, optional): If set, your favorite system editor will be opened. Defaults to False.
editor (bool, optional): If set, your default system editor will be opened. Defaults to False.
"""
if editor:
config_path: Path = Path(path_file_settings())
@@ -333,7 +333,7 @@ def login(ctx: typer.Context) -> bool:
Returns:
bool: True if login was successful, False otherwise.
"""
print("Let us check, if you are already logged in... ", end="")
print("Let us check if you are already logged in... ", end="")
settings = Settings()
tidal = Tidal(settings)
@@ -375,7 +375,7 @@ def download(
writable=False,
readable=True,
resolve_path=True,
help="List with URLs to download. One per line",
help="File with URLs to download. One URL per line.",
),
] = None,
) -> bool:
@@ -395,7 +395,7 @@ def download(
text: str = file_urls.read_text()
urls = text.splitlines()
else:
print("Provide either URLs, IDs or a file containing URLs (one per line).")
print("Provide either URLs or a file containing URLs (one per line).")
raise typer.Abort()
-1
View File
@@ -71,7 +71,6 @@ class BaseConfig:
# Move the invalid config file to the backup location.
shutil.move(path, path_bak)
# TODO: Implement better global logger.
print(
"Something is wrong with your config. Maybe it is not compatible anymore due to a new app version."
f" You can find a backup of your old config here: '{path_bak}'. A new default config was created."
+2 -2
View File
@@ -203,7 +203,7 @@ class DialogPreferences(QtWidgets.QDialog):
):
# If a path is set, use it otherwise the users home directory.
path_settings: str = os.path.expanduser(obj_line_edit.text()) if obj_line_edit.text() else ""
# Check if obj_line_edit is empty but path_default can be usd instead
# Check if obj_line_edit is empty but path_default can be used instead
path_settings = (
path_settings if path_settings else os.path.expanduser(path_default) if path_default else path_settings
)
@@ -223,7 +223,7 @@ class DialogPreferences(QtWidgets.QDialog):
dialog.setDirectory(dir_current)
# Execute dialog and set path is something is choosen.
# Execute dialog and set path if something is chosen.
if dialog.exec():
dir_name: str = dialog.selectedFiles()[0]
path: Path = Path(dir_name)
+17 -3
View File
@@ -411,7 +411,7 @@ class Download:
with path_file.open("wb") as f_target:
for dl_segment_result in dl_segment_results:
with dl_segment_result.path_segment.open("rb") as f_segment:
# Read and write junks, which gives better HDD write performance
# Read and write chunks, which gives better HDD write performance
while segment := f_segment.read(CHUNK_SIZE):
f_target.write(segment)
@@ -1249,7 +1249,7 @@ class Download:
response: requests.Response = requests.get(url, timeout=REQUESTS_TIMEOUT_SEC)
result = response.content
except Exception as e:
# TODO: Implement propper logging.
# TODO: Implement proper logging.
print(e)
finally:
response.close()
@@ -1258,7 +1258,7 @@ class Download:
with open(path_file, "rb") as f:
result = f.read()
except OSError as e:
# TODO: Implement propper logging.
# TODO: Implement proper logging.
print(e)
return result
@@ -1750,15 +1750,22 @@ class Download:
pathlib.Path: Path to the converted MP4 file.
"""
path_file_out: pathlib.Path = path_file.with_suffix(AudioExtensions.MP4)
self.fn_logger.debug(f"Converting video: {path_file.name} -> {path_file_out.name}")
ffmpeg = (
FFmpeg(executable=self.settings.data.path_binary_ffmpeg)
.option("y")
.option("hide_banner")
.option("nostdin")
.input(url=path_file)
.output(url=path_file_out, codec="copy", map=0, loglevel="quiet")
)
ffmpeg.execute()
self.fn_logger.debug(f"Video conversion complete: {path_file_out.name}")
return path_file_out
def _extract_flac(self, path_media_src: pathlib.Path) -> pathlib.Path:
@@ -1771,8 +1778,13 @@ class Download:
pathlib.Path: Path to the extracted FLAC file.
"""
path_media_out = path_media_src.with_suffix(AudioExtensions.FLAC)
self.fn_logger.debug(f"Extracting FLAC: {path_media_src.name} -> {path_media_out.name}")
ffmpeg = (
FFmpeg(executable=self.settings.data.path_binary_ffmpeg)
.option("hide_banner")
.option("nostdin")
.input(url=path_media_src)
.output(
url=path_media_out,
@@ -1786,6 +1798,8 @@ class Download:
ffmpeg.execute()
self.fn_logger.debug(f"FLAC extraction complete: {path_media_out.name}")
return path_media_out
def _extract_video_stream(self, m3u8_variant: m3u8.M3U8, quality: int) -> tuple[m3u8.M3U8 | bool, str]:
+3 -3
View File
@@ -69,7 +69,7 @@ class Settings:
@dataclass
class HelpSettings:
skip_existing: str = "Skip download if file already exists."
album_cover_save: str = "Safe cover to album folder."
album_cover_save: str = "Save cover to album folder."
lyrics_embed: str = "Embed lyrics in audio file, if lyrics are available."
use_primary_album_artist: str = "Use only the primary album artist for folder paths instead of track artists."
lyrics_file: str = "Save lyrics to separate *.lrc file, if lyrics are available."
@@ -97,11 +97,11 @@ class HelpSettings:
"will be converted to MP4. FFmpeg must be installed."
)
path_binary_ffmpeg: str = (
"Path to FFmpeg binary file (executable). Only necessary if FFmpeg not set in $PATH. Mandatory for Windows: "
"Path to FFmpeg binary file (executable). Only necessary if FFmpeg is not set in $PATH. Mandatory for Windows: "
"The directory of `ffmpeg.exe` must be set in %PATH%."
)
metadata_cover_dimension: str = (
"The dimensions of the cover image embedded into the track. Possible values: 320x320, 640x640x 1280x1280."
"The dimensions of the cover image embedded into the track. Possible values: 320x320, 640x640, 1280x1280."
)
metadata_cover_embed: str = "Embed album cover into file."
mark_explicit: str = "Mark explicit tracks with '🅴' in track title (only applies to metadata)."