diff --git a/README.md b/README.md index 984cd051f..d2922db5b 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,8 @@ Refer to our [Installation Guide](https://spotdl.rtfd.io/en/latest/installation/ ### Python (Recommended Method) - _spotDL_ can be installed by running `pip install spotdl`. + - To update spotDL run `pip install --upgrade spotdl` + > On some systems you might have to change `pip` to `pip3`.
@@ -115,7 +117,7 @@ For a list of all **options** use ```spotdl -h``` - `url`: Get direct download link for each song from the query. - Usage: - `spotdl web [query]` + `spotdl url [query]` - `sync`: Updates directories. Compares the directory with the current state of the playlist. Newly added songs will be downloaded and removed songs will be deleted. No other songs will be downloaded and no other files will be deleted. diff --git a/docs/index.md b/docs/index.md index d766c3eb3..b0a07d983 100644 --- a/docs/index.md +++ b/docs/index.md @@ -30,6 +30,7 @@ Refer to our [Installation Guide](https://spotdl.rtfd.io/en/latest/installation/ ### Python (Recommended Method) - _spotDL_ can be installed by running `pip install spotdl`. + - To update spotDL run `pip install --upgrade spotdl` > On some systems you might have to change `pip` to `pip3`. @@ -94,6 +95,7 @@ General usage: ```sh spotdl [operation] [options] QUERY ``` +When downloading songs, the song's Spotify Popularity variable [(described here)](https://developer.spotify.com/documentation/web-api/reference/get-track) is stored in metadata as a comment, so you can sort your downloaded songs by popularity. There are different **operations** spotDL can perform. The *default* is `download`, which simply downloads the songs from YouTube and embeds metadata. @@ -135,11 +137,11 @@ spotDL uses YouTube as a source for music downloads. This method is used to avoi spotDL downloads music from YouTube and is designed to always download the highest possible bitrate; which is 128 kbps for regular users and 256 kbps for YouTube Music premium users. -Check the [Audio Formats](docs/USAGE.md#audio-formats-and-quality) page for more info. +Check the [Audio Formats](USAGE#audio-formats-and-quality) page for more info. ## Contributing -Interested in contributing? Check out our [CONTRIBUTING.md](docs/CONTRIBUTING.md) to find +Interested in contributing? Check out our [CONTRIBUTING.md](CONTRIBUTING) to find resources around contributing along with a guide on how to set up a development environment. ## Donate diff --git a/pyproject.toml b/pyproject.toml index c9e854b33..7557efbce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "spotdl" -version = "4.1.10" +version = "4.1.11" 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 8164b3862..f62a6a079 100644 --- a/spotdl/_version.py +++ b/spotdl/_version.py @@ -2,4 +2,4 @@ Version module for spotdl. """ -__version__ = "4.1.10" +__version__ = "4.1.11" diff --git a/spotdl/download/downloader.py b/spotdl/download/downloader.py index 3b5f03222..b858249c7 100644 --- a/spotdl/download/downloader.py +++ b/spotdl/download/downloader.py @@ -604,7 +604,7 @@ def search_and_download(self, song: Song) -> Tuple[Song, Optional[Path]]: self.settings["bitrate"] in ["auto", "disable", None] and temp_file.suffix == output_file.suffix ): - shutil.move(temp_file, output_file) + shutil.move(str(temp_file), output_file) success = True result = None else: diff --git a/spotdl/types/song.py b/spotdl/types/song.py index 4d04288b1..3857e89b2 100644 --- a/spotdl/types/song.py +++ b/spotdl/types/song.py @@ -51,6 +51,7 @@ class Song: copyright_text: Optional[str] download_url: Optional[str] = None lyrics: Optional[str] = None + popularity: Optional[int] = None album_id: Optional[str] = None list_name: Optional[str] = None list_url: Optional[str] = None @@ -118,6 +119,7 @@ def from_url(cls, url: str) -> "Song": explicit=raw_track_meta["explicit"], publisher=raw_album_meta["label"], url=raw_track_meta["external_urls"]["spotify"], + popularity=raw_track_meta["popularity"], cover_url=max( raw_album_meta["images"], key=lambda i: i["width"] * i["height"] )["url"] diff --git a/spotdl/utils/formatter.py b/spotdl/utils/formatter.py index 1f258cd4b..b9ca978c9 100644 --- a/spotdl/utils/formatter.py +++ b/spotdl/utils/formatter.py @@ -371,7 +371,12 @@ def create_file_name( long_title = len(song.name) > (length_limit * 0.50) path_separator = "/" if "/" in template else "\\" - name_template = template.rsplit(path_separator, 1)[1] + name_template_parts = template.rsplit(path_separator, 1) + name_template = ( + name_template_parts[1] + if len(name_template_parts) == 1 + else name_template_parts[0] + ) if long_artist: logger.warning( diff --git a/spotdl/utils/metadata.py b/spotdl/utils/metadata.py index f4b533559..5486c3bae 100644 --- a/spotdl/utils/metadata.py +++ b/spotdl/utils/metadata.py @@ -56,7 +56,6 @@ class MetadataError(Exception): "date": "\xa9day", "title": "\xa9nam", "year": "\xa9day", - "originaldate": "purd", "comment": "\xa9cmt", "group": "\xa9grp", "writer": "\xa9wrt", @@ -80,7 +79,6 @@ class MetadataError(Exception): "date": "TDRC", "title": "TIT2", "year": "TDRC", - "originaldate": "TDOR", "comment": "COMM::XXX", "group": "TIT1", "writer": "TEXT", @@ -163,7 +161,6 @@ def embed_metadata(output_file: Path, song: Song, id3_separator: str = "/"): ) audio_file[tag_preset["title"]] = song.name audio_file[tag_preset["date"]] = song.date - audio_file[tag_preset["originaldate"]] = song.date audio_file[tag_preset["encodedby"]] = song.publisher # Embed metadata that isn't always present @@ -212,6 +209,15 @@ def embed_metadata(output_file: Path, song: Song, id3_separator: str = "/"): if song.download_url: audio_file.add(COMM(encoding=3, text=song.download_url)) + if song.popularity: + audio_file.add( + COMM( + encoding=3, + lang="eng", + text="Spotify Popularity: " + str(song.popularity), + ) + ) + # Embed album art audio_file = embed_cover(audio_file, song, encoding) @@ -469,8 +475,6 @@ def get_file_metadata(path: Path, id3_separator: str = "/") -> Optional[Dict[str else: if key == "artist": song_meta["artists"] = val - if key == "originaldate": - song_meta["year"] = int(str(val[0])[:4]) elif key == "tracknumber": song_meta["track_number"] = int(val[0]) elif key == "discnumber": diff --git a/spotdl/utils/search.py b/spotdl/utils/search.py index 0fe11285d..c1e38a9ab 100644 --- a/spotdl/utils/search.py +++ b/spotdl/utils/search.py @@ -7,8 +7,8 @@ import concurrent.futures import json -import re import logging +import re from pathlib import Path from typing import Dict, List, Optional diff --git a/tests/types/test_song.py b/tests/types/test_song.py index 0ca363f76..2bb2c8533 100644 --- a/tests/types/test_song.py +++ b/tests/types/test_song.py @@ -32,6 +32,7 @@ def test_song_init(): disc_count=1, publisher="test", url="test", + popularity=1, ) assert song.name == "test" @@ -51,6 +52,7 @@ def test_song_init(): assert song.cover_url == "test" assert song.explicit is True assert song.download_url == "test" + assert song.popularity == 1 def test_song_wrong_init(): @@ -99,6 +101,7 @@ def test_song_from_url(): ) assert song.explicit == False assert song.download_url == None + assert song.popularity == 0 @pytest.mark.vcr() @@ -122,8 +125,9 @@ def test_song_from_search_term(): assert song.tracks_count == 1 assert song.isrc == "GB2LD2110301" assert song.song_id == "4SN9kQlguIcjPtMNQJwD30" - assert song.explicit == False - assert song.download_url == None + assert song.explicit is False + assert song.download_url is None + assert song.popularity is not None and song.popularity >= 0 def test_song_from_data_dump(): @@ -156,7 +160,8 @@ def test_song_from_data_dump(): "disc_count": 1, "copyright_text": "", "publisher": "", - "url": "https://open.spotify.com/track/1t2qKa8K72IBC8yQlhD9bU" + "url": "https://open.spotify.com/track/1t2qKa8K72IBC8yQlhD9bU", + "popularity": 0 } """ ) @@ -178,8 +183,9 @@ def test_song_from_data_dump(): song.cover_url == "https://i.scdn.co/image/ab67616d0000b273fe2cb38e4d2412dbb0e54332" ) - assert song.explicit == False - assert song.download_url == None + assert song.explicit is False + assert song.download_url is None + assert song.popularity == 0 def test_song_from_data_dump_wrong_type(): @@ -220,6 +226,7 @@ def test_song_from_dict(): "copyright_text": "", "publisher": "", "url": "https://open.spotify.com/track/1t2qKa8K72IBC8yQlhD9bU", + "popularity": 0, } ) @@ -241,3 +248,4 @@ def test_song_from_dict(): == "https://i.scdn.co/image/ab67616d0000b273fe2cb38e4d2412dbb0e54332" ) assert song.explicit == False + assert song.popularity == 0 diff --git a/tests/utils/test_metadata.py b/tests/utils/test_metadata.py index c9ea50fab..801d020a8 100644 --- a/tests/utils/test_metadata.py +++ b/tests/utils/test_metadata.py @@ -62,6 +62,7 @@ def test_embed_metadata(tmpdir, monkeypatch, output_format): "copyright_text": "", "publisher": "", "url": "https://open.spotify.com/track/1t2qKa8K72IBC8yQlhD9bU", + "popularity": 0, } song = Song.from_dict(song_obj)