Skip to content

Commit 9b62ee5

Browse files
committed
translations: make sure we do not re-translate
1 parent cacfcb2 commit 9b62ee5

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
@@ -747,6 +747,7 @@ def scrape(cls, html: str) -> str | None:
747747
class Translator(RequestHandler):
748748
TRANSLATE_URL = "https://api.cognitive.microsofttranslator.com/translate"
749749
LINE_PARTS_RE = re.compile(r"^(\[\d\d:\d\d.\d\d\]|) *(.*)$")
750+
remove_translations = partial(re.compile(r" / [^\n]+").sub, "")
750751

751752
_log: Logger
752753
api_key: str
@@ -814,23 +815,45 @@ def append_translations(self, lines: Iterable[str]) -> list[str]:
814815
# only add the space between non-empty timestamps and texts
815816
return [" ".join(filter(None, p)) for p in zip(timestamps, texts)]
816817

817-
def translate(self, lyrics: str) -> str:
818+
def translate(self, new_lyrics: str, old_lyrics: str) -> str:
818819
"""Translate the given lyrics to the target language.
819820
821+
Check old lyrics for existing translations and return them if their
822+
original text matches the new lyrics. This is to avoid translating
823+
the same lyrics multiple times.
824+
820825
If the lyrics are already in the target language or not in any of
821826
of the source languages (if configured), they are returned as is.
822827
823828
The footer with the source URL is preserved, if present.
824829
"""
825-
lyrics_language = langdetect.detect(lyrics).upper()
826-
if lyrics_language == self.to_language or (
827-
self.from_languages and lyrics_language not in self.from_languages
830+
if (
831+
" / " in old_lyrics
832+
and self.remove_translations(old_lyrics) == new_lyrics
828833
):
829-
return lyrics
834+
self.info("🔵 Translations already exist")
835+
return old_lyrics
836+
837+
lyrics_language = langdetect.detect(new_lyrics).upper()
838+
if lyrics_language == self.to_language:
839+
self.info(
840+
"🔵 Lyrics are already in the target language {}",
841+
self.to_language,
842+
)
843+
return new_lyrics
844+
845+
if self.from_languages and lyrics_language not in self.from_languages:
846+
self.info(
847+
"🔵 Configuration {} does not permit translating from {}",
848+
self.from_languages,
849+
lyrics_language,
850+
)
851+
return new_lyrics
830852

831-
lyrics, *url = lyrics.split("\n\nSource: ")
853+
lyrics, *url = new_lyrics.split("\n\nSource: ")
832854
with self.handle_request():
833855
translated_lines = self.append_translations(lyrics.splitlines())
856+
self.info("🟢 Translated lyrics to {}", self.to_language)
834857
return "\n\nSource: ".join(["\n".join(translated_lines), *url])
835858

836859

@@ -1068,12 +1091,7 @@ def add_item_lyrics(self, item: Item, write: bool) -> None:
10681091
if lyrics := self.find_lyrics(item):
10691092
self.info("🟢 Found lyrics: {0}", item)
10701093
if translator := self.translator:
1071-
initial_lyrics = lyrics
1072-
if (lyrics := translator.translate(lyrics)) != initial_lyrics:
1073-
self.info(
1074-
"🟢 Added translation to {}",
1075-
self.config["translate_to"].get().upper(),
1076-
)
1094+
lyrics = translator.translate(lyrics, item.lyrics)
10771095
else:
10781096
self.info("🔴 Lyrics not found: {}", item)
10791097
lyrics = self.config["fallback"].get()

test/plugins/test_lyrics.py

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

579579
@pytest.mark.parametrize(
580-
"initial_lyrics, expected",
580+
"new_lyrics, old_lyrics, expected",
581581
[
582582
pytest.param(
583583
"""
@@ -586,6 +586,7 @@ def callback(request, _):
586586
My body wouldn't let me hide it (Hide it)
587587
No matter what, I wouldn't fold (Wouldn't fold, wouldn't fold)
588588
Ridin' through the thunder, lightnin'""",
589+
"",
589590
"""
590591
[Refrain: Doja Cat] / [Refrain : Doja Cat]
591592
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)
@@ -601,6 +602,7 @@ def callback(request, _):
601602
[00:01.00] Some more synced lyrics
602603
603604
Source: https://lrclib.net/api/123""",
605+
"",
604606
"""
605607
[00:00.00] Some synced lyrics / Quelques paroles synchronisées
606608
[00:00:50]
@@ -611,17 +613,24 @@ def callback(request, _):
611613
),
612614
pytest.param(
613615
"Quelques paroles",
616+
"",
614617
"Quelques paroles",
615618
id="already in the target language",
616619
),
620+
pytest.param(
621+
"Some lyrics",
622+
"Some lyrics / Some translation",
623+
"Some lyrics / Some translation",
624+
id="already translated",
625+
),
617626
],
618627
)
619-
def test_translate(self, initial_lyrics, expected):
628+
def test_translate(self, new_lyrics, old_lyrics, expected):
620629
plugin = lyrics.LyricsPlugin()
621630
bing = lyrics.Translator(plugin._log, "123", "FR", ["EN"])
622631

623632
assert bing.translate(
624-
textwrap.dedent(initial_lyrics)
633+
textwrap.dedent(new_lyrics), old_lyrics
625634
) == textwrap.dedent(expected)
626635

627636

0 commit comments

Comments
 (0)