Skip to content

lyrics

Lyrics providers for spotdl.

AzLyrics() ¤

Bases: LyricsProvider

AZLyrics lyrics provider class.

Source code in spotdl/providers/lyrics/azlyrics.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def __init__(self):
    super().__init__()

    self.session = requests.Session()
    self.session.headers.update(self.headers)

    self.session.get("https://www.azlyrics.com/")

    resp = self.session.get("https://www.azlyrics.com/geo.js")

    # extract value from js code
    js_code = resp.text
    start_index = js_code.find('value"') + 9
    end_index = js_code[start_index:].find('");')

    self.x_code = js_code[start_index : start_index + end_index]

extract_lyrics(url, **_) ¤

Extracts the lyrics from the given url.

Arguments¤
  • url: The url to extract the lyrics from.
  • kwargs: Additional arguments.
Returns¤
  • The lyrics of the song or None if no lyrics were found.
Source code in spotdl/providers/lyrics/azlyrics.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
def extract_lyrics(self, url: str, **_) -> Optional[str]:
    """
    Extracts the lyrics from the given url.

    ### Arguments
    - url: The url to extract the lyrics from.
    - kwargs: Additional arguments.

    ### Returns
    - The lyrics of the song or None if no lyrics were found.
    """

    response = self.session.get(url)
    soup = BeautifulSoup(response.content, "html.parser")

    # Find all divs that don't have a class
    div_tags = soup.find_all("div", class_=False, id_=False)

    # Find the div with the longest text
    lyrics_div = sorted(div_tags, key=lambda x: len(x.text))[-1]

    # extract lyrics from div and clean it up
    lyrics = lyrics_div.get_text().strip()

    return lyrics

get_results(name, artists, **kwargs) ¤

Returns the results for the given song.

Arguments¤
  • name: The name of the song.
  • artists: The artists of the song.
  • kwargs: Additional arguments.
Returns¤
  • A dictionary with the results. (The key is the title and the value is the url.)
Source code in spotdl/providers/lyrics/azlyrics.py
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
def get_results(self, name: str, artists: List[str], **kwargs) -> Dict[str, str]:
    """
    Returns the results for the given song.

    ### Arguments
    - name: The name of the song.
    - artists: The artists of the song.
    - kwargs: Additional arguments.

    ### Returns
    - A dictionary with the results. (The key is the title and the value is the url.)
    """

    # Join every artist by comma in artists
    artist_str = ", ".join(artist for artist in artists if artist)

    params = {
        "q": f"{artist_str} - {name}",
        "x": self.x_code,
    }

    counter = 0
    soup = None
    while counter < 4:
        try:
            response = self.session.get(
                "https://search.azlyrics.com/search.php", params=params
            )
        except requests.ConnectionError:
            continue

        if not response.ok:
            counter += 1
            continue

        soup = BeautifulSoup(response.content, "html.parser")
        break

    if soup is None:
        return {}

    td_tags = soup.find_all("td")
    if len(td_tags) == 0:
        return {}

    results = {}
    for td_tag in td_tags:
        a_tags = td_tag.find_all("a", href=True)
        if len(a_tags) == 0:
            continue

        a_tag = a_tags[0]
        url = a_tag["href"].strip()
        if url == "":
            continue

        title = td_tag.find("span").get_text().strip()
        artist = td_tag.find("b").get_text().strip()

        results[f"{artist} - {title}"] = url

    return results

Genius() ¤

Bases: LyricsProvider

Genius lyrics provider class.

Source code in spotdl/providers/lyrics/genius.py
20
21
22
23
24
25
26
27
28
29
30
31
32
def __init__(self):
    """
    Init the lyrics provider search and set headers.
    """

    super().__init__()

    self.headers.update(
        {
            "Authorization": "Bearer "
            "alXXDbPZtK1m2RrZ8I4k2Hn8Ahsd0Gh_o076HYvcdlBvmc0ULL1H8Z8xRlew5qaG",
        }
    )

extract_lyrics(url, **_) ¤

Extracts the lyrics from the given url.

Arguments¤
  • url: The url to extract the lyrics from.
  • kwargs: Additional arguments.
Returns¤
  • The lyrics of the song or None if no lyrics were found.
Source code in spotdl/providers/lyrics/genius.py
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
def extract_lyrics(self, url: str, **_) -> Optional[str]:
    """
    Extracts the lyrics from the given url.

    ### Arguments
    - url: The url to extract the lyrics from.
    - kwargs: Additional arguments.

    ### Returns
    - The lyrics of the song or None if no lyrics were found.
    """

    url = f"https://api.genius.com/songs/{url}"
    song_response = requests.get(url, headers=self.headers, timeout=10)
    url = song_response.json()["response"]["song"]["url"]

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

        if not genius_page_response.ok:
            counter += 1
            continue

        soup = BeautifulSoup(
            genius_page_response.text.replace("<br/>", "\n"), "html.parser"
        )

        break

    if soup is None:
        return None

    lyrics_div = soup.select_one("div.lyrics")
    lyrics_containers = soup.select("div[class^=Lyrics__Container]")

    # Get lyrics
    if lyrics_div:
        lyrics = lyrics_div.get_text()
    elif lyrics_containers:
        lyrics = "\n".join(con.get_text() for con in lyrics_containers)
    else:
        return None

    if not lyrics:
        return None

    # Clean lyrics
    lyrics = lyrics.strip()

    # Remove desc at the beginning if it exists
    for to_remove in ["desc", "Desc"]:
        lyrics.replace(to_remove, "", 1)

    return lyrics

get_results(name, artists, **_) ¤

Returns the results for the given song.

Arguments¤
  • name: The name of the song.
  • artists: The artists of the song.
  • kwargs: Additional arguments.
Returns¤
  • A dictionary with the results. (The key is the title and the value is the url.)
Source code in spotdl/providers/lyrics/genius.py
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
def get_results(self, name: str, artists: List[str], **_) -> Dict[str, str]:
    """
    Returns the results for the given song.

    ### Arguments
    - name: The name of the song.
    - artists: The artists of the song.
    - kwargs: Additional arguments.

    ### Returns
    - A dictionary with the results. (The key is the title and the value is the url.)
    """

    artists_str = ", ".join(artists)
    title = f"{name} - {artists_str}"

    search_response = requests.get(
        "https://api.genius.com/search",
        params={"q": title},
        headers=self.headers,
        timeout=10,
    )

    results: Dict[str, str] = {}
    for hit in search_response.json()["response"]["hits"]:
        results[hit["result"]["full_title"]] = hit["result"]["id"]

    return results

LyricsProvider() ¤

Base class for all other lyrics providers.

Source code in spotdl/providers/lyrics/base.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
def __init__(self):
    """
    Init the lyrics provider searchand set headers.
    """

    self.headers = {
        "Connection": "keep-alive",
        "Pragma": "no-cache",
        "Cache-Control": "no-cache",
        "sec-ch-ua": '"Chromium";v="104", " Not A;Brand";v="99", "Google Chrome";v="104"',
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
        "(KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36",
        "Accept": "*/*",
        "Sec-Fetch-Site": "same-origin",
        "Sec-Fetch-Mode": "cors",
        "Sec-Fetch-Dest": "empty",
        "Accept-Language": "en-US;q=0.8,en;q=0.7",
    }

name: str property ¤

Returns the name of the lyrics provider.

extract_lyrics(url, **kwargs) ¤

Extracts the lyrics from the given url.

Arguments¤
  • url: The url to extract the lyrics from.
  • kwargs: Additional arguments.
Returns¤
  • The lyrics of the song or None if no lyrics were found.
Source code in spotdl/providers/lyrics/base.py
53
54
55
56
57
58
59
60
61
62
63
64
65
def extract_lyrics(self, url: str, **kwargs) -> Optional[str]:
    """
    Extracts the lyrics from the given url.

    ### Arguments
    - url: The url to extract the lyrics from.
    - kwargs: Additional arguments.

    ### Returns
    - The lyrics of the song or None if no lyrics were found.
    """

    raise NotImplementedError

get_lyrics(name, artists, **kwargs) ¤

Returns the lyrics for the given song.

Arguments¤
  • name: The name of the song.
  • artists: The artists of the song.
  • kwargs: Additional arguments.
Returns¤
  • The lyrics of the song or None if no lyrics were found.
Source code in spotdl/providers/lyrics/base.py
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
def get_lyrics(self, name: str, artists: List[str], **kwargs) -> Optional[str]:
    """
    Returns the lyrics for the given song.

    ### Arguments
    - name: The name of the song.
    - artists: The artists of the song.
    - kwargs: Additional arguments.

    ### Returns
    - The lyrics of the song or None if no lyrics were found.
    """
    try:
        results = self.get_results(name, artists, **kwargs)
    except Exception as exc:
        logger.debug(
            "%s: Failed to get results for %s - %s: %s",
            self.name,
            name,
            ", ".join(artists),
            exc,
        )
        return None

    if not results:
        return None

    results_with_score = {}
    for title, url in results.items():
        score = ratio(slugify(title), slugify(f"{name} - {', '.join(artists)}"))
        results_with_score[score] = url

    if not results_with_score:
        return None

    # Get song url with highest title match
    score, url = max(results_with_score.items(), key=lambda x: x[0])

    # Only return lyrics if the title match is at least 55%
    if score < 55:
        return None

    try:
        return self.extract_lyrics(url, **kwargs)
    except Exception as exc:
        logger.debug(
            "%s: Failed to extract lyrics from %s: %s", self.name, url, exc
        )
        return None

get_results(name, artists, **kwargs) ¤

Returns the results for the given song.

Arguments¤
  • name: The name of the song.
  • artists: The artists of the song.
  • kwargs: Additional arguments.
Returns¤
  • A dictionary with the results. (The key is the title and the value is the url.)
Source code in spotdl/providers/lyrics/base.py
38
39
40
41
42
43
44
45
46
47
48
49
50
51
def get_results(self, name: str, artists: List[str], **kwargs) -> Dict[str, str]:
    """
    Returns the results for the given song.

    ### Arguments
    - name: The name of the song.
    - artists: The artists of the song.
    - kwargs: Additional arguments.

    ### Returns
    - A dictionary with the results. (The key is the title and the value is the url.)
    """

    raise NotImplementedError

MusixMatch ¤

Bases: LyricsProvider

MusixMatch lyrics provider class.

extract_lyrics(url, **kwargs) ¤

Extracts the lyrics from the given url.

Arguments¤
  • url: The url to extract the lyrics from.
  • kwargs: Additional arguments.
Returns¤
  • The lyrics of the song or None if no lyrics were found.
Source code in spotdl/providers/lyrics/musixmatch.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def extract_lyrics(self, url: str, **kwargs) -> Optional[str]:
    """
    Extracts the lyrics from the given url.

    ### Arguments
    - url: The url to extract the lyrics from.
    - kwargs: Additional arguments.

    ### Returns
    - The lyrics of the song or None if no lyrics were found.
    """

    lyrics_resp = requests.get(url, headers=self.headers, timeout=10)

    lyrics_soup = BeautifulSoup(lyrics_resp.text, "html.parser")
    lyrics_paragraphs = lyrics_soup.select("p.mxm-lyrics__content")
    lyrics = "\n".join(i.get_text() for i in lyrics_paragraphs)

    return lyrics

get_results(name, artists, **kwargs) ¤

Returns the results for the given song.

Arguments¤
  • name: The name of the song.
  • artists: The artists of the song.
  • kwargs: Additional arguments.
Returns¤
  • A dictionary with the results. (The key is the title and the value is the url.)
Source code in spotdl/providers/lyrics/musixmatch.py
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
def get_results(self, name: str, artists: List[str], **kwargs) -> Dict[str, str]:
    """
    Returns the results for the given song.

    ### Arguments
    - name: The name of the song.
    - artists: The artists of the song.
    - kwargs: Additional arguments.

    ### Returns
    - A dictionary with the results. (The key is the title and the value is the url.)
    """

    track_search = kwargs.get("track_search", False)
    artists_str = ", ".join(
        artist for artist in artists if artist.lower() not in name.lower()
    )

    # quote the query so that it's safe to use in a url
    # e.g "Au/Ra" -> "Au%2FRa"
    query = quote(f"{name} - {artists_str}", safe="")

    # search the `tracks page` if track_search is True
    if track_search:
        query += "/tracks"

    search_url = f"https://www.musixmatch.com/search/{query}"
    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("a[href^='/lyrics/']")

    if not song_url_tag:
        # song_url_tag being None means no results were found on the
        # All Results page, therefore, we use `track_search` to
        # search the tracks page.

        # track_serach being True means we are already searching the tracks page.
        if track_search:
            return {}

        return self.get_results(name, artists, track_search=True)

    results: Dict[str, str] = {}
    for tag in song_url_tag:
        results[tag.get_text()] = "https://www.musixmatch.com" + str(
            tag.get("href", "")
        )

    return results

Synced ¤

Bases: LyricsProvider

Lyrics provider for synced lyrics using the syncedlyrics library Currently supported websites: Deezer, NetEase

extract_lyrics(url, **kwargs) ¤

Extracts the lyrics from the given url.

Arguments¤
  • url: The url to extract the lyrics from.
  • kwargs: Additional arguments.
Returns¤
  • The lyrics of the song or None if no lyrics were found.
Source code in spotdl/providers/lyrics/synced.py
35
36
37
38
39
40
41
42
43
44
45
46
47
def extract_lyrics(self, url: str, **kwargs) -> Optional[str]:
    """
    Extracts the lyrics from the given url.

    ### Arguments
    - url: The url to extract the lyrics from.
    - kwargs: Additional arguments.

    ### Returns
    - The lyrics of the song or None if no lyrics were found.
    """

    raise NotImplementedError

get_lyrics(name, artists, **_) ¤

Try to get lyrics using syncedlyrics

Arguments¤
  • name: The name of the song.
  • artists: The artists of the song.
  • kwargs: Additional arguments.
Returns¤
  • The lyrics of the song or None if no lyrics were found.
Source code in spotdl/providers/lyrics/synced.py
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
def get_lyrics(self, name: str, artists: List[str], **_) -> Optional[str]:
    """
    Try to get lyrics using syncedlyrics

    ### Arguments
    - name: The name of the song.
    - artists: The artists of the song.
    - kwargs: Additional arguments.

    ### Returns
    - The lyrics of the song or None if no lyrics were found.
    """

    lyrics = syncedlyrics.search(f"{name} - {artists[0]}", allow_plain_format=True)

    return lyrics

get_results(name, artists, **kwargs) ¤

Returns the results for the given song.

Arguments¤
  • name: The name of the song.
  • artists: The artists of the song.
  • kwargs: Additional arguments.
Returns¤
  • A dictionary with the results. (The key is the title and the value is the url.)
Source code in spotdl/providers/lyrics/synced.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def get_results(self, name: str, artists: List[str], **kwargs) -> Dict[str, str]:
    """
    Returns the results for the given song.

    ### Arguments
    - name: The name of the song.
    - artists: The artists of the song.
    - kwargs: Additional arguments.

    ### Returns
    - A dictionary with the results. (The key is the title and the value is the url.)
    """

    raise NotImplementedError