Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IMPROVEMENT] added Musixmatch lyrics source #2683

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions swaglyrics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def user_data_dir(file_name):
backend_url = 'https://api.swaglyrics.dev'
api_timeout = 10
genius_timeout = 20
musixmatch_timeout = 20
unsupported_txt = user_data_dir("unsupported.txt")

# create unsupported.txt if it doesn't exist
Expand Down
64 changes: 62 additions & 2 deletions swaglyrics/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from html import unescape
from unidecode import unidecode

from swaglyrics import __version__, unsupported_txt, backend_url, api_timeout, genius_timeout
from swaglyrics import __version__, unsupported_txt, backend_url, api_timeout, genius_timeout, musixmatch_timeout


def clear() -> None:
Expand Down Expand Up @@ -61,7 +61,30 @@ def stripper(song: str, artist: str) -> str:
return url_data


def get_lyrics(song: str, artist: str) -> Optional[str]:
def get_lyrics(song: str, artist: str, sources=["Genius", "Musixmatch"]) -> Optional[str]:
"""
Get lyrics from given the song and artist.
Default lyrics source is Genius
:param song: currently playing song
:param artist: song artist
:param sources: ordered list of lyrics sources to attempt to query
:return: song lyrics or None if lyrics unavailable
"""
get_lyrics_from = {
"Genius": get_lyrics_from_genius,
"Musixmatch": get_lyrics_from_musixmatch,
}
for source in sources:
try:
lyrics = get_lyrics_from[source](song, artist)
if lyrics:
return lyrics
except KeyError:
raise ValueError(f'"{source}" is invalid source')
return None


def get_lyrics_from_genius(song: str, artist: str) -> Optional[str]:
"""
Get lyrics from Genius given the song and artist.
Formats the URL with the stripped url path to fetch the lyrics.
Expand Down Expand Up @@ -100,6 +123,43 @@ def get_lyrics(song: str, artist: str) -> Optional[str]:
return lyrics


def get_lyrics_from_musixmatch(song: str, artist: str) -> Optional[str]:
"""
Get lyrics from Musixmatch given the song and artist.
Formats the URL with the stripped url path to fetch the lyrics.
:param song: currently playing song
:param artist: song artist
:return: song lyrics or None if lyrics unavailable
"""
# fake legitimate browser lookup with custom header
headers = {'User-Agent': 'Mozilla/5.0 \
(Macintosh; Intel Mac OS X 10_10_1) \
AppleWebKit/537.36 \
(KHTML, like Gecko) \
Chrome/39.0.2171.95 \
Safari/537.36'}

# get search page from Musixmatch
search_url = f'https://www.musixmatch.com/search/{artist}%20{song}/tracks'
search_page = requests.get(search_url, headers=headers, timeout=musixmatch_timeout)

# get first track page from search page
try:
html = BeautifulSoup(search_page.content, "html.parser")
track_url = 'https://www.musixmatch.com' + html.find("a", {'class': "title"})['href']
except TypeError:
return None
first_track_page = requests.get(track_url, headers=headers, timeout=musixmatch_timeout)

# get lyrics from paragraphs
html = BeautifulSoup(first_track_page.content, "html.parser")
lyrics = ""
for p in html.find_all("p", {'class': "mxm-lyrics__content"}):
lyrics += p.text

return lyrics


def lyrics(song: str, artist: str, make_issue: bool = True) -> str:
"""
Displays the fetched lyrics if song playing and handles if lyrics unavailable.
Expand Down