Skip to content

Commit 1bf3e9f

Browse files
committed
translations: make sure we do not re-translate
1 parent f09f01b commit 1bf3e9f

File tree

2 files changed

+42
-15
lines changed

2 files changed

+42
-15
lines changed

beetsplug/lyrics.py

+30-12
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,7 @@ def scrape(cls, html: str) -> str | None:
733733
class Translator(RequestHandler):
734734
TRANSLATE_URL = "https://api.cognitive.microsofttranslator.com/translate"
735735
LINE_PARTS_RE = re.compile(r"^(\[\d\d:\d\d.\d\d\]|) *(.*)$")
736+
remove_translations = partial(re.compile(r" / [^\n]+").sub, "")
736737

737738
_log: beets.logging.Logger
738739
api_key: str
@@ -800,23 +801,45 @@ def append_translations(self, lines: Iterable[str]) -> list[str]:
800801
# only add the space between non-empty timestamps and texts
801802
return [" ".join(filter(None, p)) for p in zip(timestamps, texts)]
802803

803-
def translate(self, lyrics: str) -> str:
804+
def translate(self, new_lyrics: str, old_lyrics: str) -> str:
804805
"""Translate the given lyrics to the target language.
805806
807+
Check old lyrics for existing translations and return them if their
808+
original text matches the new lyrics. This is to avoid translating
809+
the same lyrics multiple times.
810+
806811
If the lyrics are already in the target language or not in any of
807812
of the source languages (if configured), they are returned as is.
808813
809814
The footer with the source URL is preserved, if present.
810815
"""
811-
lyrics_language = langdetect.detect(lyrics).upper()
812-
if lyrics_language == self.to_language or (
813-
self.from_languages and lyrics_language not in self.from_languages
816+
if (
817+
" / " in old_lyrics
818+
and self.remove_translations(old_lyrics) == new_lyrics
814819
):
815-
return lyrics
820+
self.info("🔵 Translations already exist")
821+
return old_lyrics
822+
823+
lyrics_language = langdetect.detect(new_lyrics).upper()
824+
if lyrics_language == self.to_language:
825+
self.info(
826+
"🔵 Lyrics are already in the target language {}",
827+
self.to_language,
828+
)
829+
return new_lyrics
830+
831+
if self.from_languages and lyrics_language not in self.from_languages:
832+
self.info(
833+
"🔵 Configuration {} does not permit translating from {}",
834+
self.from_languages,
835+
lyrics_language,
836+
)
837+
return new_lyrics
816838

817-
lyrics, *url = lyrics.split("\n\nSource: ")
839+
lyrics, *url = new_lyrics.split("\n\nSource: ")
818840
with self.handle_request():
819841
translated_lines = self.append_translations(lyrics.splitlines())
842+
self.info("🟢 Translated lyrics to {}", self.to_language)
820843
return "\n\nSource: ".join(["\n".join(translated_lines), *url])
821844

822845

@@ -1054,12 +1077,7 @@ def add_item_lyrics(self, item: Item, write: bool) -> None:
10541077
if lyrics := self.find_lyrics(item):
10551078
self.info("🟢 Found lyrics: {0}", item)
10561079
if translator := self.translator:
1057-
initial_lyrics = lyrics
1058-
if (lyrics := translator.translate(lyrics)) != initial_lyrics:
1059-
self.info(
1060-
"🟢 Added translation to {}",
1061-
self.config["translate_to"].get().upper(),
1062-
)
1080+
lyrics = translator.translate(lyrics, item.lyrics)
10631081
else:
10641082
self.info("🔴 Lyrics not found: {}", item)
10651083
lyrics = self.config["fallback"].get()

test/plugins/test_lyrics.py

+12-3
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,7 @@ def callback(request, _):
560560
requests_mock.post(lyrics.Translator.TRANSLATE_URL, json=callback)
561561

562562
@pytest.mark.parametrize(
563-
"initial_lyrics, expected",
563+
"new_lyrics, old_lyrics, expected",
564564
[
565565
pytest.param(
566566
"""
@@ -569,6 +569,7 @@ def callback(request, _):
569569
My body wouldn't let me hide it (Hide it)
570570
No matter what, I wouldn't fold (Wouldn't fold, wouldn't fold)
571571
Ridin' through the thunder, lightnin'""",
572+
"",
572573
"""
573574
[Refrain: Doja Cat] / [Refrain : Doja Cat]
574575
Hard for me to let you go (Let you go, let you go) / Difficile pour moi de te laisser partir (Te laisser partir, te laisser partir)
@@ -584,6 +585,7 @@ def callback(request, _):
584585
[00:01.00] Some more synced lyrics
585586
586587
Source: https://lrclib.net/api/123""",
588+
"",
587589
"""
588590
[00:00.00] Some synced lyrics / Quelques paroles synchronisées
589591
[00:00:50]
@@ -594,17 +596,24 @@ def callback(request, _):
594596
),
595597
pytest.param(
596598
"Quelques paroles",
599+
"",
597600
"Quelques paroles",
598601
id="already in the target language",
599602
),
603+
pytest.param(
604+
"Some lyrics",
605+
"Some lyrics / Some translation",
606+
"Some lyrics / Some translation",
607+
id="already translated",
608+
),
600609
],
601610
)
602-
def test_translate(self, initial_lyrics, expected):
611+
def test_translate(self, new_lyrics, old_lyrics, expected):
603612
plugin = lyrics.LyricsPlugin()
604613
bing = lyrics.Translator(plugin._log, "123", "FR", ["EN"])
605614

606615
assert bing.translate(
607-
textwrap.dedent(initial_lyrics)
616+
textwrap.dedent(new_lyrics), old_lyrics
608617
) == textwrap.dedent(expected)
609618

610619

0 commit comments

Comments
 (0)