diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml
index 83cf1ef..dff7fc9 100644
--- a/.github/workflows/python-app.yml
+++ b/.github/workflows/python-app.yml
@@ -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
diff --git a/README.md b/README.md
index ac671f8..4c21953 100644
--- a/README.md
+++ b/README.md
@@ -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
@@ -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
diff --git a/display_lyrics/DisplayLyrics.py b/display_lyrics/DisplayLyrics.py
index 2a58358..a55d290 100644
--- a/display_lyrics/DisplayLyrics.py
+++ b/display_lyrics/DisplayLyrics.py
@@ -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()
-
diff --git a/display_lyrics/Lyrics.py b/display_lyrics/Lyrics.py
index 53759ee..193ee5d 100644
--- a/display_lyrics/Lyrics.py
+++ b/display_lyrics/Lyrics.py
@@ -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")
@@ -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))
-
diff --git a/display_lyrics/__init__.py b/display_lyrics/__init__.py
index a83bb6e..97bb107 100644
--- a/display_lyrics/__init__.py
+++ b/display_lyrics/__init__.py
@@ -1,2 +1,2 @@
from .DisplayLyrics import DisplayLyrics
-from .Lyrics import Lyrics
\ No newline at end of file
+from .Lyrics import Lyrics
diff --git a/display_lyrics/test_display_lyrics.py b/display_lyrics/test_display_lyrics.py
index 3fea392..b1e4d2f 100644
--- a/display_lyrics/test_display_lyrics.py
+++ b/display_lyrics/test_display_lyrics.py
@@ -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
@@ -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
@@ -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
@@ -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)
-
diff --git a/fetch_lyrics/FetchLyrics.py b/fetch_lyrics/FetchLyrics.py
index 9985773..a6554a5 100644
--- a/fetch_lyrics/FetchLyrics.py
+++ b/fetch_lyrics/FetchLyrics.py
@@ -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
diff --git a/fetch_lyrics/PatchedGenius.py b/fetch_lyrics/PatchedGenius.py
index 8115c29..dc30467 100644
--- a/fetch_lyrics/PatchedGenius.py
+++ b/fetch_lyrics/PatchedGenius.py
@@ -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)
@@ -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('
', '\n'),
- "html.parser"
+ self._make_request(path, web=True).replace("
", "\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")
diff --git a/fetch_lyrics/__init__.py b/fetch_lyrics/__init__.py
index 1744e4b..07c8c91 100644
--- a/fetch_lyrics/__init__.py
+++ b/fetch_lyrics/__init__.py
@@ -1,2 +1,2 @@
from .FetchLyrics import FetchLyrics
-from .PatchedGenius import PatchedGenius
\ No newline at end of file
+from .PatchedGenius import PatchedGenius
diff --git a/fetch_lyrics/test_fetch_lyrics.py b/fetch_lyrics/test_fetch_lyrics.py
index e95f284..9b0d090 100644
--- a/fetch_lyrics/test_fetch_lyrics.py
+++ b/fetch_lyrics/test_fetch_lyrics.py
@@ -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
@@ -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")
-
diff --git a/requirements.txt b/requirements.txt
index 1e8521d..7c971c5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,6 +7,7 @@ side-by-side
# Unit Tests
pytest
pytest-cov
+black
# PatchedGenius
beautifulsoup4
diff --git a/songs.py b/songs.py
index f74213e..d44108d 100644
--- a/songs.py
+++ b/songs.py
@@ -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
@@ -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)
#
@@ -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
#
@@ -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"),
}
#
diff --git a/test_songs.py b/test_songs.py
index 96660eb..f2fb2b4 100644
--- a/test_songs.py
+++ b/test_songs.py
@@ -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.
"""
@@ -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
@@ -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
"""
@@ -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
diff --git a/translate_lyrics/TranslateLyrics.py b/translate_lyrics/TranslateLyrics.py
index 8572fb4..d5740c5 100644
--- a/translate_lyrics/TranslateLyrics.py
+++ b/translate_lyrics/TranslateLyrics.py
@@ -1,6 +1,7 @@
import requests, uuid
from requests.exceptions import RequestException
+
class TranslateLyrics:
def __init__(self, translator_key, region):
"""
@@ -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:
diff --git a/translate_lyrics/__init__.py b/translate_lyrics/__init__.py
index 8493be2..5277168 100644
--- a/translate_lyrics/__init__.py
+++ b/translate_lyrics/__init__.py
@@ -1 +1 @@
-from .TranslateLyrics import TranslateLyrics
\ No newline at end of file
+from .TranslateLyrics import TranslateLyrics
diff --git a/translate_lyrics/test_translate_lyrics.py b/translate_lyrics/test_translate_lyrics.py
index 1c25707..201a4e9 100644
--- a/translate_lyrics/test_translate_lyrics.py
+++ b/translate_lyrics/test_translate_lyrics.py
@@ -22,15 +22,9 @@ def mock_requests_post(*args, **kwargs):
# TODO: improve this url detection
if args[0].startswith("https://api.cognitive.microsofttranslator.com/"):
- return MockResponse([
- {
- "translations": [
- {
- "text": "mocked translated lyrics"
- }
- ]
- }
- ], 200)
+ return MockResponse(
+ [{"translations": [{"text": "mocked translated lyrics"}]}], 200
+ )
return MockResponse(None, 404)
@@ -42,11 +36,7 @@ def mock_requests_post_invalid(*args, **kwargs):
@param kwargs:
@return:
"""
- return MockResponse([
- {
- "fake_bad_data": "bad"
- }
- ], 200)
+ return MockResponse([{"fake_bad_data": "bad"}], 200)
def test_translate_lyrics_sets_translator_key():
@@ -59,7 +49,7 @@ def test_translate_lyrics_sets_translator_key():
assert translate_lyrics.subscription_key == translator_key
-@mock.patch('requests.post', side_effect=mock_requests_post)
+@mock.patch("requests.post", side_effect=mock_requests_post)
def test_translate_lyrics(mock_req):
"""
Translates lyrics
@@ -78,7 +68,7 @@ def test_translate_lyrics(mock_req):
assert english_translation == "mocked translated lyrics"
-@mock.patch('requests.post', side_effect=mock_requests_post_invalid)
+@mock.patch("requests.post", side_effect=mock_requests_post_invalid)
def test_translate_lyrics_invalid(mock_req):
"""
Translates lyrics
@@ -97,7 +87,7 @@ def test_translate_lyrics_invalid(mock_req):
assert english_translation == ""
-@mock.patch('requests.post', side_effect=mock_requests_post)
+@mock.patch("requests.post", side_effect=mock_requests_post)
def test_mock_requests_post_404(mock_req):
"""
Test 404
@@ -107,7 +97,7 @@ def test_mock_requests_post_404(mock_req):
assert resp.status_code == 404
-@mock.patch('requests.post', side_effect=mock_requests_post_invalid)
+@mock.patch("requests.post", side_effect=mock_requests_post_invalid)
def test_mock_requests_post_invalid(mock_req):
"""
Test 404