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
def __init__(self):
    super().__init__()

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

    self.x_code = self.get_x_code()

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
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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, **_) ¤

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
28
29
30
31
32
33
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
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
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.)
    """

    if self.x_code is None:
        self.x_code = self.get_x_code()

    if self.x_code is None:
        return {}

    # 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

get_x_code() ¤

Returns the x_code used by AZLyrics.

Returns¤
  • The x_code used by AZLyrics or None if it couldn't be retrieved.
Source code in spotdl/providers/lyrics/azlyrics.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
def get_x_code(self) -> Optional[str]:
    """
    Returns the x_code used by AZLyrics.

    ### Returns
    - The x_code used by AZLyrics or None if it couldn't be retrieved.
    """

    x_code = None

    try:
        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('");')

        x_code = js_code[start_index : start_index + end_index]
    except requests.ConnectionError:
        pass

    return x_code

Genius() ¤

Bases: LyricsProvider

Genius lyrics provider class.

Source code in spotdl/providers/lyrics/genius.py
21
22
23
24
25
26
27
28
29
30
31
32
33
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
 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
119
120
121
122
123
124
125
126
127
128
129
130
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,
        proxies=GlobalConfig.get_parameter("proxies"),
    )
    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,
            proxies=GlobalConfig.get_parameter("proxies"),
        )

        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
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
62
63
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,
        proxies=GlobalConfig.get_parameter("proxies"),
    )

    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
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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
54
55
56
57
58
59
60
61
62
63
64
65
66
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
 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
119
120
121
122
123
124
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():
        result_title = slugify(title)
        match_title = slugify(f"{name} - {', '.join(artists)}")

        res_list, song_list = based_sort(
            result_title.split("-"), match_title.split("-")
        )
        result_title, match_title = "-".join(res_list), "-".join(song_list)

        score = ratio(result_title, match_title)
        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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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.

Source code in spotdl/providers/lyrics/base.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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",
    }

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/musixmatch.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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.
    """

    lyrics_resp = requests.get(
        url,
        headers=self.headers,
        timeout=10,
        proxies=GlobalConfig.get_parameter("proxies"),
    )

    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
 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
 99
100
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,
        proxies=GlobalConfig.get_parameter("proxies"),
    )
    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

Source code in spotdl/providers/lyrics/base.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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",
    }

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
36
37
38
39
40
41
42
43
44
45
46
47
48
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) ¤

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
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
def get_lyrics(self, name: str, artists: List[str], **kwargs) -> 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.
    """

    try:
        lyrics = syncedlyrics.search(
            f"{name} - {artists[0]}",
            allow_plain_format=kwargs.get("allow_plain_format", True),
        )
        return lyrics
    except requests.exceptions.SSLError:
        # Max retries reached
        return None
    except TypeError:
        # Error at syncedlyrics.providers.musixmatch L89 -
        #   Because `body` is occasionally an empty list instead of a dictionary.
        # We get this error when allow_plain_format is set to True,
        #   and there are no synced lyrics present
        # Because its empty, we know there are no lyrics
        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/synced.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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