lyrics-transcriber 0.18.0__py3-none-any.whl → 0.19.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,10 +5,11 @@ import json
5
5
 
6
6
 
7
7
  class AudioShakeTranscriber:
8
- def __init__(self, api_token, logger):
8
+ def __init__(self, api_token, logger, output_prefix):
9
9
  self.api_token = api_token
10
10
  self.base_url = "https://groovy.audioshake.ai"
11
11
  self.logger = logger
12
+ self.output_prefix = output_prefix
12
13
 
13
14
  def transcribe(self, audio_filepath):
14
15
  self.logger.info(f"Transcribing {audio_filepath} using AudioShake API")
@@ -103,4 +104,10 @@ class AudioShakeTranscriber:
103
104
  if "text" not in segment:
104
105
  segment["text"] = " ".join(word["text"] for word in segment["words"])
105
106
 
107
+ transcription_data["output_filename"] = self.get_output_filename(" (AudioShake)")
108
+
106
109
  return transcription_data
110
+
111
+ def get_output_filename(self, suffix):
112
+ """Generate consistent filename with (Purpose) suffix pattern"""
113
+ return f"{self.output_prefix}{suffix}"
@@ -166,6 +166,8 @@ class LyricsTranscriber:
166
166
 
167
167
  self.create_folders()
168
168
 
169
+ self.output_prefix = f"{artist} - {title}"
170
+
169
171
  def generate(self):
170
172
  self.logger.debug(f"audio_filepath is set: {self.audio_filepath}, beginning initial whisper transcription")
171
173
 
@@ -294,7 +296,7 @@ class LyricsTranscriber:
294
296
 
295
297
  self.logger.debug("write_corrected_lyrics_data_file initiating OpenAI client")
296
298
 
297
- corrected_lyrics_data_json_cache_filepath = os.path.join(self.cache_dir, "lyrics-" + self.get_song_slug() + "-corrected.json")
299
+ corrected_lyrics_data_json_cache_filepath = os.path.join(self.cache_dir, self.get_output_filename(" (Lyrics Corrected).json"))
298
300
 
299
301
  if os.path.isfile(corrected_lyrics_data_json_cache_filepath):
300
302
  self.logger.debug(
@@ -331,9 +333,7 @@ class LyricsTranscriber:
331
333
  # TODO: Possibly add a step after segment-based correct to get the LLM to self-analyse the diff
332
334
 
333
335
  self.outputs["llm_transcript"] = ""
334
- self.outputs["llm_transcript_filepath"] = os.path.join(
335
- self.cache_dir, "lyrics-" + self.get_song_slug() + "-llm-correction-transcript.txt"
336
- )
336
+ self.outputs["llm_transcript_filepath"] = os.path.join(self.cache_dir, self.get_output_filename(" (LLM Transcript).txt"))
337
337
 
338
338
  total_segments = len(self.outputs["transcription_data_dict"]["segments"])
339
339
  self.logger.info(f"Beginning correction using LLM, total segments: {total_segments}")
@@ -466,7 +466,9 @@ class LyricsTranscriber:
466
466
  if self.outputs["corrected_lyrics_data_dict"]:
467
467
  self.logger.debug(f"corrected_lyrics_data_dict exists, writing plain text lyrics file")
468
468
 
469
- corrected_lyrics_text_filepath = os.path.join(self.cache_dir, "lyrics-" + self.get_song_slug() + "-corrected.txt")
469
+ corrected_lyrics_text_filepath = os.path.join(
470
+ self.cache_dir, self.get_output_filename(" (Lyrics Corrected).txt") # Updated to use consistent naming
471
+ )
470
472
  self.outputs["corrected_lyrics_text_filepath"] = corrected_lyrics_text_filepath
471
473
 
472
474
  self.outputs["corrected_lyrics_text"] = ""
@@ -475,7 +477,7 @@ class LyricsTranscriber:
475
477
  with open(corrected_lyrics_text_filepath, "w", encoding="utf-8") as f:
476
478
  for corrected_segment in self.outputs["corrected_lyrics_data_dict"]["segments"]:
477
479
  self.outputs["corrected_lyrics_text"] += corrected_segment["text"].strip() + "\n"
478
- f.write(corrected_segment["text".strip()] + "\n")
480
+ f.write(corrected_segment["text"].strip() + "\n")
479
481
 
480
482
  def write_spotify_lyrics_data_file(self):
481
483
  if self.spotify_cookie and self.song_known:
@@ -484,7 +486,9 @@ class LyricsTranscriber:
484
486
  self.logger.warning(f"skipping spotify fetch as not all spotify params were set")
485
487
  return
486
488
 
487
- spotify_lyrics_data_json_cache_filepath = os.path.join(self.cache_dir, "lyrics-" + self.get_song_slug() + "-spotify.json")
489
+ spotify_lyrics_data_json_cache_filepath = os.path.join(
490
+ self.cache_dir, self.get_output_filename(" (Lyrics Spotify).json") # Updated to use consistent naming
491
+ )
488
492
 
489
493
  if os.path.isfile(spotify_lyrics_data_json_cache_filepath):
490
494
  self.logger.debug(
@@ -531,7 +535,9 @@ class LyricsTranscriber:
531
535
  if self.outputs["spotify_lyrics_data_dict"]:
532
536
  self.logger.debug(f"spotify_lyrics data found, checking/writing plain text lyrics file")
533
537
 
534
- spotify_lyrics_text_filepath = os.path.join(self.cache_dir, "lyrics-" + self.get_song_slug() + "-spotify.txt")
538
+ spotify_lyrics_text_filepath = os.path.join(
539
+ self.cache_dir, self.get_output_filename(" (Lyrics Spotify).txt") # Updated to use consistent naming
540
+ )
535
541
  self.outputs["spotify_lyrics_text_filepath"] = spotify_lyrics_text_filepath
536
542
 
537
543
  lines = self.outputs["spotify_lyrics_data_dict"]["lyrics"]["lines"]
@@ -561,7 +567,7 @@ class LyricsTranscriber:
561
567
  self.logger.warning(f"skipping genius fetch as not all genius params were set")
562
568
  return
563
569
 
564
- genius_lyrics_cache_filepath = os.path.join(self.cache_dir, "lyrics-" + self.get_song_slug() + "-genius.txt")
570
+ genius_lyrics_cache_filepath = os.path.join(self.cache_dir, self.get_output_filename(" (Lyrics Genius).txt"))
565
571
 
566
572
  if os.path.isfile(genius_lyrics_cache_filepath):
567
573
  self.logger.debug(f"found existing file at genius_lyrics_cache_filepath, reading: {genius_lyrics_cache_filepath}")
@@ -635,7 +641,9 @@ class LyricsTranscriber:
635
641
  # then loops over each word and writes all words with MidiCo segment start/end formatting
636
642
  # and word-level timestamps to a MidiCo-compatible LRC file
637
643
  def write_midico_lrc_file(self):
638
- self.outputs["midico_lrc_filepath"] = self.get_cache_filepath(".lrc")
644
+ self.outputs["midico_lrc_filepath"] = os.path.join(
645
+ self.cache_dir, self.get_output_filename(" (Lyrics Corrected).lrc") # Updated suffix
646
+ )
639
647
 
640
648
  lrc_filename = self.outputs["midico_lrc_filepath"]
641
649
  self.logger.debug(f"writing midico formatted word timestamps to LRC file: {lrc_filename}")
@@ -692,9 +700,15 @@ class LyricsTranscriber:
692
700
  self.logger.debug("Reset current line")
693
701
 
694
702
  current_line_text += (" " if current_line_text else "") + word["text"]
703
+
704
+ # fmt: off
695
705
  lyric_segment = subtitles.LyricSegment(
696
- text=word["text"], ts=timedelta(seconds=word["start"]), end_ts=timedelta(seconds=word["end"])
706
+ text=word["text"],
707
+ ts=timedelta(seconds=word["start"]),
708
+ end_ts=timedelta(seconds=word["end"])
697
709
  )
710
+ # fmt: on
711
+
698
712
  current_line.segments.append(lyric_segment)
699
713
  self.logger.debug(f"Added word to current line. Current line: '{current_line_text}'")
700
714
 
@@ -706,7 +720,7 @@ class LyricsTranscriber:
706
720
  return screens
707
721
 
708
722
  def write_ass_file(self):
709
- self.outputs["ass_subtitles_filepath"] = self.get_cache_filepath(".ass")
723
+ self.outputs["ass_subtitles_filepath"] = os.path.join(self.cache_dir, self.get_output_filename(" (Lyrics Corrected).ass"))
710
724
 
711
725
  ass_filepath = self.outputs["ass_subtitles_filepath"]
712
726
  self.logger.debug(f"writing ASS formatted subtitle file: {ass_filepath}")
@@ -832,10 +846,10 @@ class LyricsTranscriber:
832
846
 
833
847
  def write_transcribed_lyrics_plain_text(self):
834
848
  if self.outputs["transcription_data_dict"]:
835
- transcription_cache_suffix = "-audioshake-transcribed.txt" if self.audioshake_api_token else "-whisper-transcribed.txt"
849
+ transcription_cache_suffix = " (Lyrics AudioShake).txt" if self.audioshake_api_token else " (Lyrics Whisper).txt"
836
850
  self.logger.debug(f"transcription_cache_suffix: {transcription_cache_suffix}")
837
851
 
838
- transcribed_lyrics_text_filepath = os.path.join(self.cache_dir, "lyrics-" + self.get_song_slug() + transcription_cache_suffix)
852
+ transcribed_lyrics_text_filepath = os.path.join(self.cache_dir, self.get_output_filename(transcription_cache_suffix))
839
853
  self.outputs["transcribed_lyrics_text_filepath"] = transcribed_lyrics_text_filepath
840
854
 
841
855
  self.outputs["transcribed_lyrics_text"] = ""
@@ -949,8 +963,8 @@ class LyricsTranscriber:
949
963
  return new_segments
950
964
 
951
965
  def transcribe(self):
952
- transcription_cache_suffix = "-audioshake" if self.audioshake_api_token else "-whisper"
953
- self.outputs["transcription_data_filepath"] = self.get_cache_filepath(f"{transcription_cache_suffix}.json")
966
+ transcription_cache_suffix = " (AudioShake).json" if self.audioshake_api_token else " (Whisper).json"
967
+ self.outputs["transcription_data_filepath"] = self.get_cache_filepath(transcription_cache_suffix)
954
968
 
955
969
  transcription_cache_filepath = self.outputs["transcription_data_filepath"]
956
970
  if os.path.isfile(transcription_cache_filepath):
@@ -963,14 +977,14 @@ class LyricsTranscriber:
963
977
  self.logger.debug(f"Using AudioShake API for transcription")
964
978
  from .audioshake_transcriber import AudioShakeTranscriber
965
979
 
966
- audioshake = AudioShakeTranscriber(self.audioshake_api_token, logger=self.logger)
980
+ audioshake = AudioShakeTranscriber(api_token=self.audioshake_api_token, logger=self.logger, output_prefix=self.output_prefix)
967
981
  transcription_data = audioshake.transcribe(self.audio_filepath)
968
982
  else:
969
983
  self.logger.debug(f"Using Whisper for transcription with model: {self.transcription_model}")
970
984
  audio = whisper.load_audio(self.audio_filepath)
971
985
  model = whisper.load_model(self.transcription_model, device="cpu")
972
986
  transcription_data = whisper.transcribe(model, audio, language="en", beam_size=5, temperature=0.2, best_of=5)
973
-
987
+
974
988
  # auditok is needed for voice activity detection, but it has OS package dependencies that are hard to install on some platforms
975
989
  # transcription_data = whisper.transcribe(model, audio, language="en", vad="auditok", beam_size=5, temperature=0.2, best_of=5)
976
990
 
@@ -990,10 +1004,8 @@ class LyricsTranscriber:
990
1004
  self.outputs["transcription_data_dict"] = transcription_data
991
1005
 
992
1006
  def get_cache_filepath(self, extension):
993
- filename = os.path.split(self.audio_filepath)[1]
994
- filename_slug = slugify.slugify(filename, lowercase=False)
995
- hash_value = self.get_file_hash(self.audio_filepath)
996
- cache_filepath = os.path.join(self.cache_dir, filename_slug + "_" + hash_value + extension)
1007
+ # Instead of using slugify and hash, use the consistent naming pattern
1008
+ cache_filepath = os.path.join(self.cache_dir, self.get_output_filename(extension))
997
1009
  self.logger.debug(f"get_cache_filepath returning cache_filepath: {cache_filepath}")
998
1010
  return cache_filepath
999
1011
 
@@ -1014,3 +1026,7 @@ class LyricsTranscriber:
1014
1026
 
1015
1027
  if self.output_dir is not None:
1016
1028
  os.makedirs(self.output_dir, exist_ok=True)
1029
+
1030
+ def get_output_filename(self, suffix):
1031
+ """Generate consistent filename with (Purpose) suffix pattern"""
1032
+ return f"{self.output_prefix}{suffix}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lyrics-transcriber
3
- Version: 0.18.0
3
+ Version: 0.19.0
4
4
  Summary: Automatically create synchronised lyrics files in ASS and MidiCo LRC formats with word-level timestamps, using Whisper and lyrics from Genius and Spotify
5
5
  Home-page: https://github.com/karaokenerds/python-lyrics-transcriber
6
6
  License: MIT
@@ -1,18 +1,18 @@
1
1
  lyrics_transcriber/__init__.py,sha256=bIRjsXAzlghS1rQxWNLU0wppZy0T_iciN9EclHLwNrQ,94
2
- lyrics_transcriber/audioshake_transcriber.py,sha256=rfbBS7K99hYLVyOqTuhK0eigopSqXsc2Zfgg4lZz41A,4647
2
+ lyrics_transcriber/audioshake_transcriber.py,sha256=MdlDv58-l5yL1QPuToc6pxaW7TXHVip1GxbPgrXTk9c,4960
3
3
  lyrics_transcriber/llm_prompts/README.md,sha256=DPAGRDVGt9ZNcQAAoQGFhwesLY3D6hD8apL71yHP4yo,196
4
4
  lyrics_transcriber/llm_prompts/llm_prompt_lyrics_correction_andrew_handwritten_20231118.txt,sha256=a3XjAYfyhWt1uCKKqm_n2Pc0STdmBdiHHtJ7ODP99Nk,4046
5
5
  lyrics_transcriber/llm_prompts/llm_prompt_lyrics_correction_gpt_optimised_20231119.txt,sha256=r6HN3DD_3gwh3B_JPd2R0I4lDXuB5iy7B90J9agOxbQ,2369
6
6
  lyrics_transcriber/llm_prompts/llm_prompt_lyrics_matching_andrew_handwritten_20231118.txt,sha256=hvk2Vs3M3Q4zGQsiQnXvnpd8wXWfwsudYeqN5qFyNWs,1754
7
7
  lyrics_transcriber/llm_prompts/promptfooconfig.yaml,sha256=O4YxlLV7XSUiSw_1Q9G7ELC2VAbrYUV_N5QxrPbd1jE,3735
8
8
  lyrics_transcriber/llm_prompts/test_data/ABBA-UnderAttack-Genius.txt,sha256=8d-RvZtyINKUlpQLwMi-VD--Y59J-epPt7SZSqjFbPI,1690
9
- lyrics_transcriber/transcriber.py,sha256=W-XXNDVgS25JLvfZL8bx9kRtdVD3ZpNqyt-1Qp4eCak,50681
9
+ lyrics_transcriber/transcriber.py,sha256=4Z9ugLG_LmQ3kw_GZMYeA4TVrZjPuCI8yru44iFUOyQ,51190
10
10
  lyrics_transcriber/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  lyrics_transcriber/utils/ass.py,sha256=b8lnjgXGD1OD1ld_b1xxUmSOf4nSEfz9BpgSkh16R4g,90291
12
12
  lyrics_transcriber/utils/cli.py,sha256=8Poba_9wQw0VmOK73vuK-w-abR9QmO4y4FYDHiAQbc0,6972
13
13
  lyrics_transcriber/utils/subtitles.py,sha256=_WG0pFoZMXcrGe6gbARkC9KrWzFNTMOsiqQwNL-H2lU,11812
14
- lyrics_transcriber-0.18.0.dist-info/LICENSE,sha256=BiPihPDxhxIPEx6yAxVfAljD5Bhm_XG2teCbPEj_m0Y,1069
15
- lyrics_transcriber-0.18.0.dist-info/METADATA,sha256=K8IY-6Vy5Wa6X5VKCg_sDgjvzfyiiyOBOo8mbyOUNi0,5825
16
- lyrics_transcriber-0.18.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
17
- lyrics_transcriber-0.18.0.dist-info/entry_points.txt,sha256=lh6L-iR5CGELaNcouDK94X78eS5Ua_tK9lI4UEkza-k,72
18
- lyrics_transcriber-0.18.0.dist-info/RECORD,,
14
+ lyrics_transcriber-0.19.0.dist-info/LICENSE,sha256=BiPihPDxhxIPEx6yAxVfAljD5Bhm_XG2teCbPEj_m0Y,1069
15
+ lyrics_transcriber-0.19.0.dist-info/METADATA,sha256=IrVopVhJauL3M2GDjBtXq3dPjBakkJ_l_u6V5T0GCwY,5825
16
+ lyrics_transcriber-0.19.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
17
+ lyrics_transcriber-0.19.0.dist-info/entry_points.txt,sha256=lh6L-iR5CGELaNcouDK94X78eS5Ua_tK9lI4UEkza-k,72
18
+ lyrics_transcriber-0.19.0.dist-info/RECORD,,