Skip to content

Commit

Permalink
v4.2.9
Browse files Browse the repository at this point in the history
  • Loading branch information
xnetcat authored Nov 15, 2024
2 parents 866dadd + 79d63cc commit d1a80b7
Show file tree
Hide file tree
Showing 24 changed files with 1,375 additions and 797 deletions.
6 changes: 1 addition & 5 deletions .github/workflows/standard-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,9 @@ jobs:
poetry env use "3.11"
poetry install
- name: Check for docstring's
run: |
poetry run pylint --limit-inference-results 0 --enable missing-function-docstring missing-module-docstring missing-class-docstring empty-docstring --disable=all ./spotdl
- name: Run Pylint check
run: |
poetry run pylint --fail-under 10 --limit-inference-results 0 ./spotdl
poetry run pylint --fail-under 10 --limit-inference-results 0 --disable=R0917,W0511 ./spotdl
- name: Run MyPy check
run: |
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,6 @@ temp/
# VS Code
.vscode
*.txt

# Output Folder
output/
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Refer to our [Installation Guide](https://spotdl.rtfd.io/en/latest/installation/
- On Termux
- `curl -L https://raw.githubusercontent.com/spotDL/spotify-downloader/master/scripts/termux.sh | sh`
- Arch
- There is an [Arch User Repository (AUR) package](https://aur.archlinux.org/packages/python-spotdl/) for
- There is an [Arch User Repository (AUR) package](https://aur.archlinux.org/packages/spotdl/) for
spotDL.
- Docker
- Build image:
Expand Down
3 changes: 3 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ If you don't want config to load automatically change `load_config` option in co
"scan_for_songs": false,
"m3u": null,
"output": "{artists} - {title}.{output-ext}",
"m3u_output": "#EXTINF:{duration}, {artists} - {title}.{output-ext}",
"overwrite": "skip",
"search_query": null,
"ffmpeg": "ffmpeg",
Expand Down Expand Up @@ -463,6 +464,8 @@ Output options:
--sponsor-block Use the sponsor block to download songs from yt/ytm.
--archive ARCHIVE Specify the file name for an archive of already downloaded songs
--playlist-numbering Sets each track in a playlist to have the playlist's name as its album, and album art as the playlist's icon
--playlist-retain-track-cover
Sets each track in a playlist to have the playlist's name as its album, while retaining album art of each track
--scan-for-songs Scan the output directory for existing files. This option should be combined with the --overwrite option to control how existing files are handled. (Output
directory is the last directory that is not a template variable in the output template)
--fetch-albums Fetch all albums from songs in query
Expand Down
1,965 changes: 1,227 additions & 738 deletions poetry.lock

Large diffs are not rendered by default.

56 changes: 37 additions & 19 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "spotdl"
version = "4.2.8"
version = "4.2.9"
description = "Download your Spotify playlists and songs along with album art and metadata"
license = "MIT"
authors = ["spotDL Team <[email protected]>"]
Expand All @@ -27,7 +27,7 @@ classifiers = [
]

[tool.poetry.dependencies]
python = ">=3.8,<3.13"
python = ">=3.8,<3.14"

spotipy = [
{version = "^2.23.0", python = "<=3.8"},
Expand All @@ -38,50 +38,68 @@ ytmusicapi = [
{version = "^1.4.1", python = ">=3.10"},
]
pytube = "^15.0.0"
yt-dlp = "^2024.8.6"
yt-dlp = [
{version = "^2024.10.7", python = "<3.9"},
{version = "^2024.11.4", python = ">=3.9"},
]
mutagen = "^1.47.0"
rich = "^13.8.0"
rich = "^13.9.4"
beautifulsoup4 = "^4.12.3"
requests = "^2.32.3"
rapidfuzz = "^3.9.7"
rapidfuzz = [
{version = "^3.9.7", python = "<3.9"},
{version = "^3.10.0", python = ">=3.9"},
]
python-slugify = {extras = ["unidecode"], version = "^8.0.4"}
uvicorn = "^0.23.2"
pydantic = "^2.9.0"
pydantic = "^2.9.2"
fastapi = "^0.103.0"
platformdirs = "^4.2.2"
platformdirs = "^4.3.6"
pykakasi = "^2.3.0"
syncedlyrics = "^1.0.1"
soundcloud-v2 = "^1.6.0"

[tool.poetry.group.dev.dependencies]
pytest = "^8.3.2"
pytest = "^8.3.3"
pytest-mock = "^3.14.0"
pyfakefs = "^5.6.0"
pytest-cov = "^5.0.0"
pyfakefs = "^5.7.1"
pytest-cov = [
{version = "^5.0.0", python = "<3.9"},
{version = "^6.0.0", python = ">=3.9"},
]
pytest-subprocess = "^1.5.2"
pytest-asyncio = "^0.21.1"
mypy = "^1.11.2"
pylint = "^3.2.7"
black = "^24.8.0"
mypy = "^1.13.0"
pylint = [
{version = "^3.2.7", python = "<3.9"},
{version = "^3.3.1", python = ">=3.9"},
]
black = [
{version = "^24.8.0", python = "<3.9"},
{version = "^24.10.0", python = ">=3.9"},
]
mdformat-gfm = "^0.3.5"
types-orjson = "^3.6.2"
types-python-slugify = "^8.0.2.20240310"
types-requests = "==2.31.0.6"
types-setuptools = "^74.1.0.20240906"
types-setuptools = "^75.3.0.20241112"
types-toml = "^0.10.8.7"
types-ujson = "^5.10.0.20240515"
pyinstaller = "^6.10.0"
pyinstaller = "^6.11.1"
mkdocs = "^1.6.1"
isort = "^5.13.2"
dill = "^0.3.7"
mkdocs-material = "^9.5.34"
mkdocs-material = "^9.5.44"
mkdocstrings = "^0.26.0"
mkdocstrings-python = "^1.11.1"
pymdown-extensions = "^10.9"
mkdocstrings-python = [
{version = "^1.11.1", python = "<3.9"},
{version = "^1.12.0", python = ">=3.9"},
]
pymdown-extensions = "^10.12"
mkdocs-gen-files = "^0.5.0"
mkdocs-literate-nav = "^0.6.0"
mkdocs-section-index = "^0.3.5"
vcrpy = "^6.0.1"
vcrpy = "^6.0.2"
pytest-recording = "^0.13.1"

[tool.poetry.scripts]
Expand Down
3 changes: 3 additions & 0 deletions spotdl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ def search(self, query: List[str]) -> List[Song]:
use_ytm_data=self.downloader.settings["ytm_data"],
playlist_numbering=self.downloader.settings["playlist_numbering"],
album_type=self.downloader.settings["album_type"],
playlist_retain_track_cover=self.downloader.settings[
"playlist_retain_track_cover"
],
)

def get_download_urls(self, songs: List[Song]) -> List[Optional[str]]:
Expand Down
2 changes: 1 addition & 1 deletion spotdl/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
Version module for spotdl.
"""

__version__ = "4.2.8"
__version__ = "4.2.9"
1 change: 1 addition & 0 deletions spotdl/console/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def download(
playlist_numbering=downloader.settings["playlist_numbering"],
albums_to_ignore=downloader.settings["ignore_albums"],
album_type=downloader.settings["album_type"],
playlist_retain_track_cover=downloader.settings["playlist_retain_track_cover"],
)

# Download the songs
Expand Down
3 changes: 3 additions & 0 deletions spotdl/console/meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ async def pool_worker(file_path: Path) -> None:
use_ytm_data=downloader.settings["ytm_data"],
playlist_numbering=downloader.settings["playlist_numbering"],
album_type=downloader.settings["album_type"],
playlist_retain_track_cover=downloader.settings[
"playlist_retain_track_cover"
],
)

downloader.download_multiple_songs(songs_list)
46 changes: 31 additions & 15 deletions spotdl/console/save.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,24 +47,41 @@ def save(
use_ytm_data=downloader.settings["ytm_data"],
playlist_numbering=downloader.settings["playlist_numbering"],
album_type=downloader.settings["album_type"],
playlist_retain_track_cover=downloader.settings["playlist_retain_track_cover"],
)
save_data = [song.json for song in songs]

def process_song(song: Song):
try:
data = downloader.search(song)
if data is None:
logger.error("Could not find a match for %s", song.display_name)

download_url = None
if downloader.settings["preload"]:
try:
download_url = downloader.search(song)
if download_url is None:
logger.error("Could not find a match for %s", song.display_name)
return None

logger.info("Found url for %s: %s", song.display_name, download_url)
except Exception as exception:
logger.error(
"%s generated an exception: %s", song.display_name, exception
)
return None

logger.info("Found url for %s: %s", song.display_name, data)

return {**song.json, "download_url": data}
lyrics = None
try:
lyrics = downloader.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 downloader.lyrics_providers]
),
)
except Exception as exception:
logger.error("%s generated an exception: %s", song.display_name, exception)
logger.debug("Could not search for lyrics: %s", exception)

return None
return {**song.json, "download_url": download_url, "lyrics": lyrics}

async def pool_worker(song: Song):
async with downloader.semaphore:
Expand All @@ -74,11 +91,10 @@ async def pool_worker(song: Song):
# hurt performance.
return await downloader.loop.run_in_executor(None, process_song, song)

if downloader.settings["preload"]:
tasks = [pool_worker(song) for song in songs]
tasks = [pool_worker(song) for song in songs]

# call all task asynchronously, and wait until all are finished
save_data = list(downloader.loop.run_until_complete(asyncio.gather(*tasks)))
# call all task asynchronously, and wait until all are finished
save_data = list(downloader.loop.run_until_complete(asyncio.gather(*tasks)))

if to_stdout:
# Print the songs to stdout
Expand All @@ -92,7 +108,7 @@ async def pool_worker(song: Song):
gen_m3u_files(
songs,
m3u_file,
downloader.settings["output"],
downloader.settings["m3u_output"],
downloader.settings["format"],
downloader.settings["restrict"],
False,
Expand Down
14 changes: 10 additions & 4 deletions spotdl/console/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

import json
import logging
from typing import List, Tuple
from pathlib import Path
from typing import List, Tuple

from spotdl.download.downloader import Downloader
from spotdl.types.song import Song
Expand Down Expand Up @@ -56,6 +56,9 @@ def sync(
use_ytm_data=downloader.settings["ytm_data"],
playlist_numbering=downloader.settings["playlist_numbering"],
album_type=downloader.settings["album_type"],
playlist_retain_track_cover=downloader.settings[
"playlist_retain_track_cover"
],
)

# Create sync file
Expand All @@ -79,7 +82,7 @@ def sync(
gen_m3u_files(
songs_list,
m3u_file,
downloader.settings["output"],
downloader.settings["m3u_output"],
downloader.settings["format"],
downloader.settings["restrict"],
False,
Expand Down Expand Up @@ -112,6 +115,9 @@ def sync(
use_ytm_data=downloader.settings["ytm_data"],
playlist_numbering=downloader.settings["playlist_numbering"],
album_type=downloader.settings["album_type"],
playlist_retain_track_cover=downloader.settings[
"playlist_retain_track_cover"
],
)

# Get the names and URLs of previously downloaded songs from the sync file
Expand Down Expand Up @@ -151,7 +157,7 @@ def sync(
if path != new_path:
to_rename.append((path, new_path))

# TODO: Downloading duplicate songs in the same playlist
# fix later Downloading duplicate songs in the same playlist
# will trigger a re-download of the song. To fix this we have to copy the song
# to the new location without removing the old one.
for old_path, new_path in to_rename:
Expand Down Expand Up @@ -226,7 +232,7 @@ def sync(
gen_m3u_files(
songs_playlist,
m3u_file,
downloader.settings["output"],
downloader.settings["m3u_output"],
downloader.settings["format"],
downloader.settings["restrict"],
False,
Expand Down
1 change: 1 addition & 0 deletions spotdl/console/url.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def url(
use_ytm_data=downloader.settings["ytm_data"],
playlist_numbering=downloader.settings["playlist_numbering"],
album_type=downloader.settings["album_type"],
playlist_retain_track_cover=downloader.settings["playlist_retain_track_cover"],
)

def process_song(song: Song):
Expand Down
19 changes: 12 additions & 7 deletions spotdl/download/downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
AudioProvider,
BandCamp,
Piped,
SliderKZ,
SoundCloud,
YouTube,
YouTubeMusic,
Expand Down Expand Up @@ -57,7 +56,6 @@
AUDIO_PROVIDERS: Dict[str, Type[AudioProvider]] = {
"youtube": YouTube,
"youtube-music": YouTubeMusic,
"slider-kz": SliderKZ,
"soundcloud": SoundCloud,
"bandcamp": BandCamp,
"piped": Piped,
Expand Down Expand Up @@ -306,9 +304,14 @@ def download_multiple_songs(

if self.settings["save_errors"]:
with open(
self.settings["save_errors"], "w", encoding="utf-8"
self.settings["save_errors"], "a", encoding="utf-8"
) as error_file:
error_file.write("\n".join(self.errors))
if len(self.errors) > 0:
error_file.write(
f"{datetime.datetime.now().strftime('%Y-%m-%d-%H-%M-%S')}\n"
)
for error in self.errors:
error_file.write(f"{error}\n")

logger.info("Saved errors to %s", self.settings["save_errors"])

Expand Down Expand Up @@ -336,7 +339,7 @@ def download_multiple_songs(
gen_m3u_files(
song_list,
self.settings["m3u"],
self.settings["output"],
self.settings["m3u_output"],
self.settings["format"],
self.settings["restrict"],
False,
Expand Down Expand Up @@ -454,6 +457,7 @@ def search_and_download( # pylint: disable=R0911
restrict=self.settings["restrict"],
file_name_length=self.settings["max_filename_length"],
)

except Exception:
song = reinit_song(song)

Expand Down Expand Up @@ -490,7 +494,8 @@ def search_and_download( # pylint: disable=R0911
and dup_song_path.exists()
]

file_exists = output_file.exists() or dup_song_paths
# Checking if file already exists in all subfolders of output directory
file_exists = file_exists = output_file.exists() or dup_song_paths
if not self.settings["scan_for_songs"]:
for file_extension in self.scan_formats:
ext_path = output_file.with_suffix(f".{file_extension}")
Expand Down Expand Up @@ -566,7 +571,7 @@ def search_and_download( # pylint: disable=R0911
logger.info("Removing duplicate file: %s", dup_song_path)

dup_song_path.unlink()
except (PermissionError, OSError) as exc:
except (PermissionError, OSError, Exception) as exc:
logger.debug(
"Could not remove duplicate file: %s, error: %s",
dup_song_path,
Expand Down
Loading

0 comments on commit d1a80b7

Please sign in to comment.