From 6f075b6e96d6db338869789320f9fbee43e54837 Mon Sep 17 00:00:00 2001 From: kuba Date: Sun, 23 Apr 2023 10:29:39 +0200 Subject: [PATCH 1/8] check if time tags are actually time signatures --- spotdl/utils/metadata.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spotdl/utils/metadata.py b/spotdl/utils/metadata.py index 050eb4853..f4b533559 100644 --- a/spotdl/utils/metadata.py +++ b/spotdl/utils/metadata.py @@ -318,7 +318,9 @@ def embed_lyrics(audio_file, song: Song, encoding: str): time_tag = time_tag.replace("]", "") time_tag = time_tag.replace(".", ":") time_tag_vals = time_tag.split(":") - if len(time_tag_vals) != 3: + if len(time_tag_vals) != 3 or any( + not isinstance(tag, int) for tag in time_tag_vals + ): continue minute, sec, millisecond = time_tag_vals From 45ad5035743050301e6d7f6c49cbc34a67366eeb Mon Sep 17 00:00:00 2001 From: kuba Date: Sun, 23 Apr 2023 10:35:21 +0200 Subject: [PATCH 2/8] catch unexpected exceptions when getting lyrics --- spotdl/download/downloader.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/spotdl/download/downloader.py b/spotdl/download/downloader.py index 59b03aba0..3b5f03222 100644 --- a/spotdl/download/downloader.py +++ b/spotdl/download/downloader.py @@ -492,15 +492,20 @@ def search_and_download(self, song: Song) -> Tuple[Song, Optional[Path]]: ) # Find song lyrics and add them to the song object - lyrics = self.search_lyrics(song) - if lyrics is None: - logger.debug( - "No lyrics found for %s, lyrics providers: %s", - song.display_name, - ", ".join([lprovider.name for lprovider in self.lyrics_providers]), - ) - else: - song.lyrics = lyrics + try: + lyrics = self.search_lyrics(song) + if lyrics is None: + logger.debug( + "No lyrics found for %s, lyrics providers: %s", + song.display_name, + ", ".join( + [lprovider.name for lprovider in self.lyrics_providers] + ), + ) + else: + song.lyrics = lyrics + except Exception as exc: + logger.debug("Could not search for lyrics: %s", exc) # If the file already exists and we want to overwrite the metadata, # we can skip the download @@ -695,7 +700,7 @@ def search_and_download(self, song: Song) -> Tuple[Song, Optional[Path]]: # Initialize the modify chapters post processor modify_chapters = ModifyChaptersPP( - audio_downloader.audio_handler, + downloader=audio_downloader.audio_handler, remove_sponsor_segments=SPONSOR_BLOCK_CATEGORIES, ) From 47832339274ff8229eb64d956071b26a3fd96786 Mon Sep 17 00:00:00 2001 From: kuba Date: Sun, 23 Apr 2023 10:35:31 +0200 Subject: [PATCH 3/8] bump version to v4.1.8 --- pyproject.toml | 2 +- spotdl/_version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index cfb4b8355..5aa909ecf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "spotdl" -version = "4.1.7" +version = "4.1.8" description = "Download your Spotify playlists and songs along with album art and metadata" license = "MIT" authors = ["spotDL Team "] diff --git a/spotdl/_version.py b/spotdl/_version.py index 0ca972c39..e8216808c 100644 --- a/spotdl/_version.py +++ b/spotdl/_version.py @@ -2,4 +2,4 @@ Version module for spotdl. """ -__version__ = "4.1.7" +__version__ = "4.1.8" From ed6e8faec43111d0447830fe913ba32f5bb38f54 Mon Sep 17 00:00:00 2001 From: kuba Date: Sun, 23 Apr 2023 12:06:22 +0200 Subject: [PATCH 4/8] rewritten file name creation logic for long file names --- spotdl/utils/formatter.py | 94 ++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 42 deletions(-) diff --git a/spotdl/utils/formatter.py b/spotdl/utils/formatter.py index df329ae05..35627525b 100644 --- a/spotdl/utils/formatter.py +++ b/spotdl/utils/formatter.py @@ -352,48 +352,17 @@ def create_file_name( return file - # If the file name length is greater than , - # and we are already using the short version of the template, - # fallback to default template - if short is True: - # Path template is already short, but we still can't create a file - # so we reduce it even further - if template == "{artist} - {title}.{output-ext}": - if len(song.name) > (length_limit * 0.80): - logger.warning( - "%s: File name is too long. Using only part of the song title.", - song.display_name, - ) - - name_parts = song.name.split(" ") - new_name = "" - for part in name_parts: - if len(new_name) + len(part) < (length_limit * 0.80): - new_name += part + " " - else: - break - - song.name = new_name.strip() - else: - logger.warning( - "%s: File name is too long. Using only song title.", - song.display_name, - ) - - return create_file_name( - song=song, - template="{title}.{output-ext}", - file_extension=file_extension, - restrict=restrict, - short=short, - ) - - # This will probably never occur, but just in case - if template == "{title}.{output-ext}": - raise RecursionError( - f'"{song.display_name} is too long to be shortened. File a bug report on GitHub' - ) + if short is False: + return create_file_name( + song, + template, + file_extension, + restrict=restrict, + short=True, + file_name_length=length_limit, + ) + if template != "{artist} - {title}.{output-ext}": logger.warning( "%s: File name is too long. Using the default template.", song.display_name, @@ -405,10 +374,51 @@ def create_file_name( file_extension=file_extension, restrict=restrict, short=short, + file_name_length=length_limit, + ) + + # Path template is already short, but we still can't create a file + # so we reduce it even further + long_artist = len(song.artist) > (length_limit * 0.50) + long_title = len(song.name) > (length_limit * 0.50) + + if long_artist: + logger.warning( + "%s: File name is too long. Using only part of song artist.", + song.display_name, ) + short_artist = song.artist.split(",")[0] + song.artist = short_artist + if len(song.artist) > (length_limit * 0.50): + song.artist = song.artist.split(" ")[0] + + if long_title: + logger.warning( + "%s: File name is too long. Using only part of the song title.", + song.display_name, + ) + + name_parts = song.name.split(" ") + new_name = "" + for part in name_parts: + if ( + len(f"{song.artist} - {new_name + part}.{file_extension}") + 1 + < length_limit + ): + new_name += part + " " + else: + break + + song.name = new_name.strip() + return create_file_name( - song, template, file_extension, restrict=restrict, short=True + song=song, + template="{artist} - {title}.{output-ext}", + file_extension=file_extension, + restrict=restrict, + short=short, + file_name_length=length_limit, ) From 7d6160b489f7954bc649eaa9957658bd5b75fe58 Mon Sep 17 00:00:00 2001 From: kuba Date: Sun, 23 Apr 2023 14:26:00 +0200 Subject: [PATCH 5/8] keep the parent path when creating short path name --- spotdl/utils/formatter.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/spotdl/utils/formatter.py b/spotdl/utils/formatter.py index 35627525b..5c362cb58 100644 --- a/spotdl/utils/formatter.py +++ b/spotdl/utils/formatter.py @@ -362,7 +362,9 @@ def create_file_name( file_name_length=length_limit, ) - if template != "{artist} - {title}.{output-ext}": + parent_dir = template.rsplit("/" if "/" in template else "\\", 1)[0] + default_file_name = parent_dir + "/{artist} - {title}.{output-ext}" + if template.endswith("/{artist} - {title}.{output-ext}"): logger.warning( "%s: File name is too long. Using the default template.", song.display_name, @@ -370,7 +372,7 @@ def create_file_name( return create_file_name( song=song, - template="{artist} - {title}.{output-ext}", + template=default_file_name, file_extension=file_extension, restrict=restrict, short=short, @@ -414,7 +416,7 @@ def create_file_name( return create_file_name( song=song, - template="{artist} - {title}.{output-ext}", + template=default_file_name, file_extension=file_extension, restrict=restrict, short=short, From 841f95ab6d9b93527feba1652873865b19104885 Mon Sep 17 00:00:00 2001 From: kuba Date: Sun, 23 Apr 2023 16:40:21 +0200 Subject: [PATCH 6/8] don't use default template when formatting template --- spotdl/utils/formatter.py | 66 ++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/spotdl/utils/formatter.py b/spotdl/utils/formatter.py index 5c362cb58..1f258cd4b 100644 --- a/spotdl/utils/formatter.py +++ b/spotdl/utils/formatter.py @@ -4,6 +4,7 @@ and file names. """ +import copy import logging import re from functools import lru_cache @@ -303,6 +304,8 @@ def create_file_name( - the formatted string as a Path object """ + temp_song = copy.deepcopy(song) + # If template does not contain any of the keys, # append {artists} - {title}.{output-ext} to it if not any(key in template for key in VARS) and template != "": @@ -362,61 +365,54 @@ def create_file_name( file_name_length=length_limit, ) - parent_dir = template.rsplit("/" if "/" in template else "\\", 1)[0] - default_file_name = parent_dir + "/{artist} - {title}.{output-ext}" - if template.endswith("/{artist} - {title}.{output-ext}"): - logger.warning( - "%s: File name is too long. Using the default template.", - song.display_name, - ) - - return create_file_name( - song=song, - template=default_file_name, - file_extension=file_extension, - restrict=restrict, - short=short, - file_name_length=length_limit, - ) - # Path template is already short, but we still can't create a file # so we reduce it even further long_artist = len(song.artist) > (length_limit * 0.50) long_title = len(song.name) > (length_limit * 0.50) + path_separator = "/" if "/" in template else "\\" + name_template = template.rsplit(path_separator, 1)[1] + if long_artist: logger.warning( "%s: File name is too long. Using only part of song artist.", - song.display_name, + temp_song.display_name, ) - short_artist = song.artist.split(",")[0] - song.artist = short_artist - if len(song.artist) > (length_limit * 0.50): - song.artist = song.artist.split(" ")[0] + short_artist = temp_song.artist.split(",")[0] + temp_song.artist = short_artist + if len(temp_song.artist) > (length_limit * 0.50): + temp_song.artist = temp_song.artist.split(" ")[0] if long_title: logger.warning( "%s: File name is too long. Using only part of the song title.", - song.display_name, + temp_song.display_name, ) - name_parts = song.name.split(" ") - new_name = "" + name_parts = temp_song.name.split(" ") + + old_name = temp_song.name + temp_song.name = "" for part in name_parts: - if ( - len(f"{song.artist} - {new_name + part}.{file_extension}") + 1 - < length_limit - ): - new_name += part + " " - else: - break + old_name = temp_song.name + temp_song.name += part + " " + + formatted_name = format_query( + song=temp_song, + template=name_template, + santitize=True, + file_extension=file_extension, + short=True, + ) - song.name = new_name.strip() + if len(formatted_name.strip()) > length_limit: + temp_song.name = old_name.strip() + break return create_file_name( - song=song, - template=default_file_name, + song=temp_song, + template=template, file_extension=file_extension, restrict=restrict, short=short, From 3293c8596e8f0453f9a50ac6a9f375fb2964d6b4 Mon Sep 17 00:00:00 2001 From: kuba Date: Sun, 23 Apr 2023 16:47:12 +0200 Subject: [PATCH 7/8] added support for spotify.link --- spotdl/utils/search.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spotdl/utils/search.py b/spotdl/utils/search.py index a637c37c2..239beec91 100644 --- a/spotdl/utils/search.py +++ b/spotdl/utils/search.py @@ -11,6 +11,7 @@ from pathlib import Path from typing import Dict, List, Optional +import requests from ytmusicapi import YTMusic from spotdl.types.album import Album @@ -197,6 +198,9 @@ def get_simple_songs( lists.append(spot_list) elif "open.spotify.com" in request and "track" in request: songs.append(Song.from_url(url=request)) + elif "https://spotify.link/" in request: + resp = requests.head(request, allow_redirects=True, timeout=10) + songs.append(Song.from_url(url=resp.url)) elif "open.spotify.com" in request and "playlist" in request: lists.append(Playlist.from_url(request, fetch_songs=False)) elif "open.spotify.com" in request and "album" in request: From 1173b62d0a9e5823f6e5d7d106f12d840e360e86 Mon Sep 17 00:00:00 2001 From: kuba Date: Sun, 23 Apr 2023 17:51:20 +0200 Subject: [PATCH 8/8] updated syncedlyrics to v0.5.0 --- poetry.lock | 14 +++++++------- pyproject.toml | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/poetry.lock b/poetry.lock index 7134c0205..f80db4932 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2336,14 +2336,14 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyam [[package]] name = "syncedlyrics" -version = "0.4.0" +version = "0.5.0" description = "Get an LRC format (synchronized) lyrics for your music" category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ - {file = "syncedlyrics-0.4.0-py3-none-any.whl", hash = "sha256:be2f77e4185ae95162048564bf747b2c9cb82e0e3b9635e877fbcb903c4c7d69"}, - {file = "syncedlyrics-0.4.0.tar.gz", hash = "sha256:d4a4e3207053b367e5d51ec9f8583886a2395796562c6fa4d75de12ef9decba7"}, + {file = "syncedlyrics-0.5.0-py3-none-any.whl", hash = "sha256:2edfae34bdc80c9dca4336a81a49f6591a8e469597fc14e9a89e1a3d297dbcfb"}, + {file = "syncedlyrics-0.5.0.tar.gz", hash = "sha256:767c84abf4ae7ba423a0a224c9b2284a8d9c34f00f2e596748707655a760018e"}, ] [package.dependencies] @@ -2462,14 +2462,14 @@ types-urllib3 = "<1.27" [[package]] name = "types-setuptools" -version = "67.6.0.8" +version = "67.7.0.0" description = "Typing stubs for setuptools" category = "dev" optional = false python-versions = "*" files = [ - {file = "types-setuptools-67.6.0.8.tar.gz", hash = "sha256:df3e084c3b5af782cb81bd516f40213b572d932fa2f1272aef307d15643736f8"}, - {file = "types_setuptools-67.6.0.8-py3-none-any.whl", hash = "sha256:0b178cab1d5a7487cd410d1b5448d1a85d9da1bf44aa7be7c0d4f33d65041e5a"}, + {file = "types-setuptools-67.7.0.0.tar.gz", hash = "sha256:f59b03c786510176c7601578d84d5e6c58e985de331f08036b2fe008408a63c1"}, + {file = "types_setuptools-67.7.0.0-py3-none-any.whl", hash = "sha256:02ee794d41c282ecfc99e09d8115ca4c3c4395a9747d1a35fa4a6f243d7585e6"}, ] [[package]] @@ -2971,4 +2971,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = ">=3.7.2,<3.12" -content-hash = "d751958d06617f68de9dde9af8978b7ba6d1a2dd701f7aa6e4156acd931c4ca1" +content-hash = "2db91f5ae019bd242f1ea3a3e71e94e146d1c9d909ebf6132c67a164033153ff" diff --git a/pyproject.toml b/pyproject.toml index 5aa909ecf..615f77222 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ pydantic = "^1.10.7" fastapi = "^0.95.1" platformdirs = "^3.2.0" pykakasi = "^2.2.1" -syncedlyrics = "0.4.0" +syncedlyrics = "^0.5.0" typing-extensions = "^4.5.0" [tool.poetry.dev-dependencies] @@ -64,7 +64,7 @@ mdformat-gfm = "^0.3.5" types-orjson = "^3.6.2" types-python-slugify = "^8.0.0.2" types-requests = "^2.28.11.17" -types-setuptools = "^67.6.0.8" +types-setuptools = "^67.7.0.0" types-toml = "^0.10.8.6" types-ujson = "^5.7.0.3" pyinstaller = "^5.10.1"