Skip to content

Commit

Permalink
lint with black
Browse files Browse the repository at this point in the history
  • Loading branch information
patkub committed Oct 28, 2024
1 parent e90e4b6 commit c551b5f
Show file tree
Hide file tree
Showing 16 changed files with 105 additions and 82 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Lint with black
run: |
black . --check
- name: Test with pytest
run: |
pytest
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
[![Python package](https://github.com/patkub/console-songs/actions/workflows/python-app.yml/badge.svg)](https://github.com/patkub/console-songs/actions/workflows/python-app.yml)
![Python 3.11+ Required](https://img.shields.io/badge/python-3.11+-brightgreen.svg)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)

### Idea
1. Fetch song lyrics
Expand Down Expand Up @@ -37,6 +38,11 @@ pip3 install -r requirements.txt

You should now be able to run `python3 songs.py --help` and see usage.

When making code changes, remember to format code with black:
```
black .
```

### Usage

Provide the song and optionally the artist's name
Expand Down
13 changes: 9 additions & 4 deletions display_lyrics/DisplayLyrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,22 @@ def display_lyrics(song_info, original_lyrics, english_lyrics):

# Display portion that has both original and english translation
for i in range(min_stanzas):
side_by_side.print_side_by_side(split_original_lyrics[i], split_english_lyrics[i])
side_by_side.print_side_by_side(
split_original_lyrics[i], split_english_lyrics[i]
)
print()

if len_original_lyrics > len_english_lyrics:
# original lyrics have more than english translation
for i in range(len_original_lyrics - min_stanzas):
side_by_side.print_side_by_side(split_original_lyrics[min_stanzas - 1 + i], "")
side_by_side.print_side_by_side(
split_original_lyrics[min_stanzas - 1 + i], ""
)
print()
elif len_english_lyrics > len_original_lyrics:
# english translated lyrics have more than original
for i in range(len_english_lyrics - min_stanzas):
side_by_side.print_side_by_side("", split_english_lyrics[min_stanzas - 1 + i])
side_by_side.print_side_by_side(
"", split_english_lyrics[min_stanzas - 1 + i]
)
print()

3 changes: 1 addition & 2 deletions display_lyrics/Lyrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def __init__(self, lyrics):

def normalize_lyrics(self, lyrics):
# normalize line endings
self.lyrics = re.sub(r'[\r\n][\r\n]{2,}', '\n\n', lyrics)
self.lyrics = re.sub(r"[\r\n][\r\n]{2,}", "\n\n", lyrics)
# split into stanzas
self.stanzas = self.lyrics.split("\n\n")

Expand Down Expand Up @@ -44,4 +44,3 @@ def get_min_stanzas(self, other):
Get the minimum number of stanzas between two Lyrics
"""
return min(len(self.stanzas), len(other.stanzas))

2 changes: 1 addition & 1 deletion display_lyrics/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from .DisplayLyrics import DisplayLyrics
from .Lyrics import Lyrics
from .Lyrics import Lyrics
15 changes: 7 additions & 8 deletions display_lyrics/test_display_lyrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ def get_expected_print_calls(song_info):
call("\n{}".format(song_info.full_title)),
call("{}\n".format(song_info.url)),
call(),
call()
call(),
]
return expected_print_calls


@patch('builtins.print')
@patch('side_by_side.print_side_by_side', fake_print_side_by_side)
@patch("builtins.print")
@patch("side_by_side.print_side_by_side", fake_print_side_by_side)
def test_display_lyrics_method(mocked_print):
"""
Displays original and English translated lyrics side-by-side
Expand All @@ -55,8 +55,8 @@ class FakeSongInfo:
mocked_print.assert_has_calls(expected_print_calls)


@patch('builtins.print')
@patch('side_by_side.print_side_by_side', fake_print_side_by_side)
@patch("builtins.print")
@patch("side_by_side.print_side_by_side", fake_print_side_by_side)
def test_display_lyrics_method_original_longer(mocked_print):
"""
Displays original and English translated lyrics side-by-side
Expand All @@ -82,8 +82,8 @@ class FakeSongInfo:
mocked_print.assert_has_calls(expected_print_calls)


@patch('builtins.print')
@patch('side_by_side.print_side_by_side', fake_print_side_by_side)
@patch("builtins.print")
@patch("side_by_side.print_side_by_side", fake_print_side_by_side)
def test_display_lyrics_method_original_shorter(mocked_print):
"""
Displays original and English translated lyrics side-by-side
Expand All @@ -107,4 +107,3 @@ class FakeSongInfo:
# expect to print: title, url, and two blank lines
expected_print_calls = get_expected_print_calls(song_info)
mocked_print.assert_has_calls(expected_print_calls)

2 changes: 1 addition & 1 deletion fetch_lyrics/FetchLyrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def fetch_lyrics(self, song, artist):
@return: object: song info
"""

#print("Looking for song {} by artist {}".format(song, artist))
# print("Looking for song {} by artist {}".format(song, artist))
# https://genius.com/Mihail-ma-ucide-ea-lyrics
song = self.genius.search_song(song, artist)
return song
24 changes: 13 additions & 11 deletions fetch_lyrics/PatchedGenius.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class PatchedGenius(Genius): # pragma: no cover
A patched version of Genius with @xanthon's fixes to remove ads and unwanted content in lyrics
Credit: @xanthon from https://github.com/johnwmillr/LyricsGenius/pull/272 . Thanks!
"""

def __init__(self, *args, **kwargs):
super(PatchedGenius, self).__init__(*args, **kwargs)

Expand Down Expand Up @@ -37,35 +38,36 @@ def lyrics(self, song_id=None, song_url=None, remove_section_headers=False):
if song_url:
path = song_url.replace("https://genius.com/", "")
else:
path = self.song(song_id)['song']['path'][1:]
path = self.song(song_id)["song"]["path"][1:]
# Scrape the song lyrics from the HTML
html = BeautifulSoup(
self._make_request(path, web=True).replace('<br/>', '\n'),
"html.parser"
self._make_request(path, web=True).replace("<br/>", "\n"), "html.parser"
)
# Determine the class of the div
divs = html.find_all("div", class_=re.compile("^lyrics$|Lyrics__Container"))
if divs is None or len(divs) <= 0:
if self.verbose:
print("Couldn't find the lyrics section. "
"Please report this if the song has lyrics.\n"
"Song URL: https://genius.com/{}".format(path))
print(
"Couldn't find the lyrics section. "
"Please report this if the song has lyrics.\n"
"Song URL: https://genius.com/{}".format(path)
)
return None

# remove ads from div
ads = html.find("div", {'class': re.compile("RightSidebar__Container")})
ads = html.find("div", {"class": re.compile("RightSidebar__Container")})
ads.decompose()
# remove header
header = html.find("div", {'class': re.compile("LyricsHeader__Container")})
header = html.find("div", {"class": re.compile("LyricsHeader__Container")})
header.decompose()
# remove embed note / footer
footer = html.find("div", {'class': re.compile("LyricsFooter__Container")})
footer = html.find("div", {"class": re.compile("LyricsFooter__Container")})
footer.decompose()

lyrics = "\n".join([div.get_text() for div in divs])

# Remove [Verse], [Bridge], etc.
if self.remove_section_headers or remove_section_headers:
lyrics = re.sub(r'(\[.*?\])*', '', lyrics)
lyrics = re.sub('\n{2}', '\n', lyrics) # Gaps between verses
lyrics = re.sub(r"(\[.*?\])*", "", lyrics)
lyrics = re.sub("\n{2}", "\n", lyrics) # Gaps between verses
return lyrics.strip("\n")
2 changes: 1 addition & 1 deletion fetch_lyrics/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from .FetchLyrics import FetchLyrics
from .PatchedGenius import PatchedGenius
from .PatchedGenius import PatchedGenius
3 changes: 1 addition & 2 deletions fetch_lyrics/test_fetch_lyrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from unittest.mock import patch


@patch('lyricsgenius.Genius.search_song')
@patch("lyricsgenius.Genius.search_song")
def test_fetch_lyrics_calls_genius_api(mock_search_song):
"""
Fetches lyrics for a song given its name and artist by calling Genius API
Expand All @@ -19,4 +19,3 @@ def test_fetch_lyrics_calls_genius_api(mock_search_song):

# Assert underlying mocked lyricsgenius.Genius.search_song was called
mock_search_song.assert_called_with("fake_song", "fake_artist")

1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ side-by-side
# Unit Tests
pytest
pytest-cov
black

# PatchedGenius
beautifulsoup4
24 changes: 15 additions & 9 deletions songs.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@

# Class to get song lyrics from Genius
from fetch_lyrics import FetchLyrics, PatchedGenius

# Class to translate lyrics using Microsoft Azure AI Translator
from translate_lyrics import TranslateLyrics

# Display output
from display_lyrics import DisplayLyrics

Expand Down Expand Up @@ -44,7 +46,9 @@ def process_song(song, artist, access_keys, genius_patch):
#
# Translate lyrics to English using Microsoft Azure AI Translator
#
lyrics_translator = TranslateLyrics(access_keys["MS_TRANSLATOR_KEY"], access_keys["MS_TRANSLATOR_REGION"])
lyrics_translator = TranslateLyrics(
access_keys["MS_TRANSLATOR_KEY"], access_keys["MS_TRANSLATOR_REGION"]
)
english_translation = lyrics_translator.translate_lyrics(song_lyrics)

#
Expand All @@ -53,7 +57,7 @@ def process_song(song, artist, access_keys, genius_patch):
DisplayLyrics.display_lyrics(song_info, song_lyrics, english_translation)


if __name__ == '__main__': # pragma: no cover
if __name__ == "__main__": # pragma: no cover
#
# Parse arguments
#
Expand All @@ -62,19 +66,21 @@ def process_song(song, artist, access_keys, genius_patch):
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)
parser.add_argument("song", nargs="+")
parser.add_argument('--genius-patch',
action=argparse.BooleanOptionalAction,
default=True,
help='Use patched version of Genius API')
parser.add_argument(
"--genius-patch",
action=argparse.BooleanOptionalAction,
default=True,
help="Use patched version of Genius API",
)
args = parser.parse_args()

song = args.song[0]
artist = args.song[1] if len(args.song) > 1 else None

access_keys = {
'GENIUS_ACCESS_TOKEN': os.getenv("GENIUS_ACCESS_TOKEN"),
'MS_TRANSLATOR_KEY': os.getenv("MS_TRANSLATOR_KEY"),
'MS_TRANSLATOR_REGION': os.getenv("MS_TRANSLATOR_REGION")
"GENIUS_ACCESS_TOKEN": os.getenv("GENIUS_ACCESS_TOKEN"),
"MS_TRANSLATOR_KEY": os.getenv("MS_TRANSLATOR_KEY"),
"MS_TRANSLATOR_REGION": os.getenv("MS_TRANSLATOR_REGION"),
}

#
Expand Down
44 changes: 29 additions & 15 deletions test_songs.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,17 @@
mockedFetchLyrics.lyrics = "mocked_lyrics"


@patch('fetch_lyrics.FetchLyrics.FetchLyrics.fetch_lyrics', return_value=mockedFetchLyrics)
@patch('translate_lyrics.TranslateLyrics.TranslateLyrics.translate_lyrics', return_value="english translation")
@patch('display_lyrics.DisplayLyrics.DisplayLyrics.display_lyrics')
def test_process_song(mocked_display_lyrics, mocked_translate_lyrics, mocked_fetch_lyrics):
@patch(
"fetch_lyrics.FetchLyrics.FetchLyrics.fetch_lyrics", return_value=mockedFetchLyrics
)
@patch(
"translate_lyrics.TranslateLyrics.TranslateLyrics.translate_lyrics",
return_value="english translation",
)
@patch("display_lyrics.DisplayLyrics.DisplayLyrics.display_lyrics")
def test_process_song(
mocked_display_lyrics, mocked_translate_lyrics, mocked_fetch_lyrics
):
"""
Fetch song lyrics, translate to English, and display original and English side-by-side lyrics.
"""
Expand All @@ -18,9 +25,9 @@ def test_process_song(mocked_display_lyrics, mocked_translate_lyrics, mocked_fet
song = "test_song_name"
artist = "test_artist_name"
access_keys = {
'GENIUS_ACCESS_TOKEN': "fake_token_1",
'MS_TRANSLATOR_KEY': "fake_token_2",
'MS_TRANSLATOR_REGION': "fake_token_3"
"GENIUS_ACCESS_TOKEN": "fake_token_1",
"MS_TRANSLATOR_KEY": "fake_token_2",
"MS_TRANSLATOR_REGION": "fake_token_3",
}
experimental = False

Expand All @@ -33,13 +40,20 @@ def test_process_song(mocked_display_lyrics, mocked_translate_lyrics, mocked_fet
# mocked lyrics get translated
mocked_translate_lyrics.assert_called_with("mocked_lyrics")
# display is called with song info, original and translated lyrics
mocked_display_lyrics.assert_called_with(ANY, "mocked_lyrics", "english translation")
mocked_display_lyrics.assert_called_with(
ANY, "mocked_lyrics", "english translation"
)


@patch('fetch_lyrics.FetchLyrics.FetchLyrics.fetch_lyrics', return_value=None)
@patch('translate_lyrics.TranslateLyrics.TranslateLyrics.translate_lyrics', return_value="english translation")
@patch('display_lyrics.DisplayLyrics.DisplayLyrics.display_lyrics')
def test_process_song_null(mocked_display_lyrics, mocked_translate_lyrics, mocked_fetch_lyrics):
@patch("fetch_lyrics.FetchLyrics.FetchLyrics.fetch_lyrics", return_value=None)
@patch(
"translate_lyrics.TranslateLyrics.TranslateLyrics.translate_lyrics",
return_value="english translation",
)
@patch("display_lyrics.DisplayLyrics.DisplayLyrics.display_lyrics")
def test_process_song_null(
mocked_display_lyrics, mocked_translate_lyrics, mocked_fetch_lyrics
):
"""
Null song fetched, translation and display are not called
"""
Expand All @@ -48,9 +62,9 @@ def test_process_song_null(mocked_display_lyrics, mocked_translate_lyrics, mocke
song = "test_song_name"
artist = "test_artist_name"
access_keys = {
'GENIUS_ACCESS_TOKEN': "fake_token_1",
'MS_TRANSLATOR_KEY': "fake_token_2",
'MS_TRANSLATOR_REGION': "fake_token_3"
"GENIUS_ACCESS_TOKEN": "fake_token_1",
"MS_TRANSLATOR_KEY": "fake_token_2",
"MS_TRANSLATOR_REGION": "fake_token_3",
}
experimental = False

Expand Down
17 changes: 8 additions & 9 deletions translate_lyrics/TranslateLyrics.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import requests, uuid
from requests.exceptions import RequestException


class TranslateLyrics:
def __init__(self, translator_key, region):
"""
Expand All @@ -20,24 +21,22 @@ def translate_lyrics(self, lyrics):
# If you encounter any issues with the base_url or path, make sure
# that you are using the latest endpoint: https://docs.microsoft.com/azure/cognitive-services/translator/reference/v3-0-translate
endpoint = "https://api.cognitive.microsofttranslator.com/"
path = '/translate?api-version=3.0'
path = "/translate?api-version=3.0"
# from romanian to english
# params = '&from=ro&to=en'
# or detect original language, and translate to english
params = '&to=en'
params = "&to=en"
constructed_url = endpoint + path + params

headers = {
'Ocp-Apim-Subscription-Key': self.subscription_key,
'Ocp-Apim-Subscription-Region': self.region,
'Content-type': 'application/json',
'X-ClientTraceId': str(uuid.uuid4())
"Ocp-Apim-Subscription-Key": self.subscription_key,
"Ocp-Apim-Subscription-Region": self.region,
"Content-type": "application/json",
"X-ClientTraceId": str(uuid.uuid4()),
}

# You can pass more than one object in body.
body = [{
'text': lyrics
}]
body = [{"text": lyrics}]

english_translation = ""
try:
Expand Down
2 changes: 1 addition & 1 deletion translate_lyrics/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .TranslateLyrics import TranslateLyrics
from .TranslateLyrics import TranslateLyrics
Loading

0 comments on commit c551b5f

Please sign in to comment.