Skip to content

Commit

Permalink
Release v4.0.5
Browse files Browse the repository at this point in the history
  • Loading branch information
xnetcat authored Nov 12, 2022
2 parents 32289fd + e083dfc commit 6fc73ed
Show file tree
Hide file tree
Showing 33 changed files with 33,352 additions and 22,551 deletions.
28 changes: 27 additions & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,30 @@ spotdl download 'The Weeknd - Blinding Lights'
```
````

??? Search info To search for and download an album, run, with quotation marks

```bash
spotdl download 'album:[albumName]'
```

example:

```bash
spotdl download 'album:After Hours'
```

You can include the artist tag to narrow down a search

```bash
spotdl download 'artist:[artistName] album:[albumName]'
```

example:

```bash
spotdl download 'artist:The Weeknd album:After Hours'
```

??? info "YouTube link with Spotify metadata" To download YouTube video with metadata from
Spotify, run > Noting the quote `"` are required

Expand Down Expand Up @@ -238,7 +262,8 @@ false
"print_errors": false,
"sponsor_block": false,
"preload": false,
"archive": null
"archive": null,
"playlist_numbering": false
}
```

Expand Down Expand Up @@ -308,6 +333,7 @@ Output options:
--print-errors Print errors (wrong songs, failed downloads etc) on exit, useful for long playlist
--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 When downloading a playlist, convert all tracks into a single album, using the playlist icon as the album art.
Web options:
--host HOST The host to use for the web server.
Expand Down
5 changes: 2 additions & 3 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ theme:
name: material
features:
- content.code.annotate
- navigation.tabs
- navigation.top
palette:
- media: "(prefers-color-scheme: light)"
scheme: default
Expand All @@ -36,9 +38,6 @@ theme:
toggle:
icon: material/weather-night
name: Switch to light mode
features:
- navigation.tabs
- navigation.top

plugins:
- search
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "spotdl"
version = "4.0.4"
version = "4.0.5"
description = "Download your Spotify playlists and songs along with album art and metadata"
license = "MIT"
authors = ["spotDL Team <spotdladmins@googlegroups.com>"]
Expand Down
2 changes: 2 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# pytest.ini
[pytest]
asyncio_mode = auto
markers =
vcr
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.0.4"
__version__ = "4.0.5"
1 change: 1 addition & 0 deletions spotdl/console/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def download(
- downloader: Already initialized downloader instance.
- save_path: Path to save the songs to or None.
- m3u_file: Path to the m3u file to save the songs to.
- archive: Path to the archive file to save the songs to.
"""

# Parse the query
Expand Down
1 change: 1 addition & 0 deletions spotdl/console/entry_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ def entry_point():
restrict=settings["restrict"],
print_errors=settings["print_errors"],
sponsor_block=settings["sponsor_block"],
playlist_numbering=settings["playlist_numbering"],
)

def graceful_exit(_signal, _frame):
Expand Down
2 changes: 1 addition & 1 deletion spotdl/console/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def sync(
# Create m3u file
if m3u_file:
gen_m3u_files(
query,
sync_data["query"],
m3u_file,
new_songs,
downloader.output,
Expand Down
5 changes: 4 additions & 1 deletion spotdl/download/downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ def __init__(
print_errors: bool = False,
sponsor_block: bool = False,
loop: Optional[asyncio.AbstractEventLoop] = None,
playlist_numbering: bool = False,
):
"""
Initialize the Downloader class.
Expand All @@ -109,6 +110,7 @@ def __init__(
- restrict: Whether to restrict the filename to ASCII characters.
- print_errors: Whether to print errors on exit.
- sponsor_block: Whether to remove sponsor segments using sponsor block postprocessor.
- playlist_numbering: Whether to convert tracks in a playlist into an album
### Notes
- `search-query` uses the same format as `output`.
Expand Down Expand Up @@ -187,6 +189,7 @@ def __init__(
self.sponsor_block = sponsor_block
self.audio_providers_classes = audio_providers_classes
self.progress_handler = ProgressHandler(NAME_TO_LEVEL[log_level], simple_tui)
self.playlist_numbering = playlist_numbering

self.lyrics_providers: List[LyricsProvider] = []
for lyrics_provider_class in lyrics_providers_classes:
Expand Down Expand Up @@ -344,7 +347,7 @@ def search_and_download(self, song: Song) -> Tuple[Song, Optional[Path]]:
# If it's None extract the current metadata
# And reinitialize the song object
if song.name is None and song.url:
song = reinit_song(song)
song = reinit_song(song, self.playlist_numbering)

# Find song lyrics and add them to the song object
lyrics = self.search_lyrics(song)
Expand Down
14 changes: 8 additions & 6 deletions spotdl/providers/audio/youtube.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Youtube module for downloading and searching songs.
"""

from typing import Any, Dict, List, Optional
from typing import Dict, List, Optional, Tuple

from pytube import YouTube as PyTube, Search
from rapidfuzz import fuzz
Expand Down Expand Up @@ -38,10 +38,10 @@ def search(self, song: Song) -> Optional[str]:
if song.isrc:
isrc_results = self.get_results(song.isrc)

if isrc_results and len(isrc_results) == 1:
if isrc_results:
isrc_result = self.order_results(isrc_results, song)
if len(isrc_result) == 1:
isrc_link, isrc_score = isrc_result.popitem()
isrc_link, (isrc_score, _) = isrc_result.popitem()

if isrc_score > 90:
# print(f"# RETURN URL - {isrc_link} - isrc score")
Expand All @@ -63,7 +63,7 @@ def search(self, song: Song) -> Optional[str]:
# Order results
ordered_results = self.order_results(results, song)
else:
ordered_results = {results[0].watch_url: 100}
ordered_results = {results[0].watch_url: (100.0, 9_000_000_000)}

# No matches found
if len(ordered_results) == 0:
Expand Down Expand Up @@ -123,7 +123,9 @@ def get_results(

return Search(search_term).results

def order_results(self, results: List[PyTube], song: Song) -> Dict[str, Any]:
def order_results(
self, results: List[PyTube], song: Song
) -> Dict[str, Tuple[float, int]]:
"""
Filter results based on the song's metadata.
Expand All @@ -136,7 +138,7 @@ def order_results(self, results: List[PyTube], song: Song) -> Dict[str, Any]:
"""

# Assign an overall avg match value to each result
links_with_match_value = {}
links_with_match_value: Dict[str, Tuple[float, int]] = {}

# Slugify song title
slug_song_name = slugify(song.name)
Expand Down
2 changes: 1 addition & 1 deletion spotdl/providers/audio/ytmusic.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ def get_results(self, search_term: str, **kwargs) -> List[Dict[str, Any]]:

def order_results(
self, results: List[Dict[str, Any]], song: Song, is_isrc: bool = False
) -> Dict[str, Any]:
) -> Dict[str, float]:
"""
Filter results based on the song's metadata.
Expand Down
7 changes: 5 additions & 2 deletions spotdl/providers/lyrics/genius.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,23 @@ def get_lyrics(self, name: str, artists: List[str], **_) -> Optional[str]:
"https://api.genius.com/search",
params={"q": f"{name} {artist_str}"},
headers=headers,
timeout=10,
)

song_id = search_response.json()["response"]["hits"][0]["result"]["id"]

song_response = requests.get(
f"https://api.genius.com/songs/{song_id}", headers=headers
f"https://api.genius.com/songs/{song_id}", headers=headers, timeout=10
)

song_url = song_response.json()["response"]["song"]["url"]

counter = 0
soup = None
while counter < 4:
genius_page_response = requests.get(song_url, headers=self.headers)
genius_page_response = requests.get(
song_url, headers=self.headers, timeout=10
)

if not genius_page_response.ok:
counter += 1
Expand Down
4 changes: 2 additions & 2 deletions spotdl/providers/lyrics/musixmatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def get_lyrics(self, name: str, artists: List[str], **kwargs) -> Optional[str]:
query += "/tracks"

search_url = f"https://www.musixmatch.com/search/{query}"
search_resp = requests.get(search_url, headers=self.headers)
search_resp = requests.get(search_url, headers=self.headers, timeout=10)

search_soup = BeautifulSoup(search_resp.text, "html.parser")
song_url_tag = search_soup.select_one("a[href^='/lyrics/']")
Expand All @@ -63,7 +63,7 @@ def get_lyrics(self, name: str, artists: List[str], **kwargs) -> Optional[str]:
return lyrics

song_url = "https://www.musixmatch.com" + str(song_url_tag.get("href", ""))
lyrics_resp = requests.get(song_url, headers=self.headers)
lyrics_resp = requests.get(song_url, headers=self.headers, timeout=10)

lyrics_soup = BeautifulSoup(lyrics_resp.text, "html.parser")
lyrics_paragraphs = lyrics_soup.select("p.mxm-lyrics__content")
Expand Down
23 changes: 23 additions & 0 deletions spotdl/types/album.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,29 @@ class Album(SongList):

artist: Dict[str, Any]

@classmethod
def search(cls, search_term: str):
"""
Searches for Album from a search term.
### Arguments
- search_term: The search term to use.
### Returns
- The raw search results
"""

spotify_client = SpotifyClient()
raw_search_results = spotify_client.search(search_term, type="album")

if (
raw_search_results is None
or len(raw_search_results.get("albums", {}).get("items", [])) == 0
):
raise AlbumError("No album matches found on spotify")

return raw_search_results

@staticmethod
def get_urls(url: str) -> List[str]:
"""
Expand Down
23 changes: 23 additions & 0 deletions spotdl/types/artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,29 @@ def from_url(cls, url: str) -> "Artist":
urls=urls,
)

@classmethod
def search(cls, search_term: str):
"""
Searches for Artist from a search term.
### Arguments
- search_term: The search term to use.
### Returns
- The raw search results
"""

spotify_client = SpotifyClient()
raw_search_results = spotify_client.search(search_term, type="artist")

if (
raw_search_results is None
or len(raw_search_results.get("artists", {}).get("items", [])) == 0
):
raise ArtistError("No artist matches found on spotify")

return raw_search_results

@staticmethod
def get_urls(url: str) -> List[str]:
"""
Expand Down
30 changes: 30 additions & 0 deletions spotdl/types/playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,30 @@ class Playlist(SongList):
description: str
author_url: str
author_name: str
cover_url: str

@classmethod
def search(cls, search_term: str):
"""
Searches for Playlist from a search term.
### Arguments
- search_term: The search term to use.
### Returns
- The raw search results
"""

spotify_client = SpotifyClient()
raw_search_results = spotify_client.search(search_term, type="playlist")

if (
raw_search_results is None
or len(raw_search_results.get("playlists", {}).get("items", [])) == 0
):
raise PlaylistError("No playlist matches found on spotify")

return raw_search_results

@staticmethod
def get_urls(url: str) -> List[str]:
Expand Down Expand Up @@ -89,4 +113,10 @@ def get_metadata(url: str) -> Dict[str, Any]:
"description": playlist["description"],
"author_url": playlist["external_urls"]["spotify"],
"author_name": playlist["owner"]["display_name"],
"cover_url": max(
playlist["images"],
key=lambda i: 0
if i["width"] is None or i["height"] is None
else i["width"] * i["height"],
)["url"],
}
Loading

0 comments on commit 6fc73ed

Please sign in to comment.