lyrics-transcriber 0.12.8__tar.gz → 0.13.1__tar.gz

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.
Files changed (17) hide show
  1. {lyrics_transcriber-0.12.8 → lyrics_transcriber-0.13.1}/PKG-INFO +16 -15
  2. lyrics_transcriber-0.13.1/lyrics_transcriber/llm_prompts/promptfooconfig.yaml +61 -0
  3. lyrics_transcriber-0.13.1/lyrics_transcriber/llm_prompts/test_data/ABBA-UnderAttack-Genius.txt +48 -0
  4. {lyrics_transcriber-0.12.8 → lyrics_transcriber-0.13.1}/lyrics_transcriber/transcriber.py +17 -15
  5. {lyrics_transcriber-0.12.8 → lyrics_transcriber-0.13.1}/lyrics_transcriber/utils/cli.py +3 -1
  6. {lyrics_transcriber-0.12.8 → lyrics_transcriber-0.13.1}/pyproject.toml +16 -15
  7. lyrics_transcriber-0.12.8/lyrics_transcriber/llm_prompts/promptfooconfig.yaml +0 -39
  8. {lyrics_transcriber-0.12.8 → lyrics_transcriber-0.13.1}/LICENSE +0 -0
  9. {lyrics_transcriber-0.12.8 → lyrics_transcriber-0.13.1}/README.md +0 -0
  10. {lyrics_transcriber-0.12.8 → lyrics_transcriber-0.13.1}/lyrics_transcriber/__init__.py +0 -0
  11. {lyrics_transcriber-0.12.8 → lyrics_transcriber-0.13.1}/lyrics_transcriber/llm_prompts/README.md +0 -0
  12. {lyrics_transcriber-0.12.8 → lyrics_transcriber-0.13.1}/lyrics_transcriber/llm_prompts/llm_prompt_lyrics_correction_andrew_handwritten_20231118.txt +0 -0
  13. {lyrics_transcriber-0.12.8 → lyrics_transcriber-0.13.1}/lyrics_transcriber/llm_prompts/llm_prompt_lyrics_correction_gpt_optimised_20231119.txt +0 -0
  14. {lyrics_transcriber-0.12.8 → lyrics_transcriber-0.13.1}/lyrics_transcriber/llm_prompts/llm_prompt_lyrics_matching_andrew_handwritten_20231118.txt +0 -0
  15. {lyrics_transcriber-0.12.8 → lyrics_transcriber-0.13.1}/lyrics_transcriber/utils/__init__.py +0 -0
  16. {lyrics_transcriber-0.12.8 → lyrics_transcriber-0.13.1}/lyrics_transcriber/utils/ass.py +0 -0
  17. {lyrics_transcriber-0.12.8 → lyrics_transcriber-0.13.1}/lyrics_transcriber/utils/subtitles.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lyrics-transcriber
3
- Version: 0.12.8
3
+ Version: 0.13.1
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
@@ -13,21 +13,22 @@ Classifier: Programming Language :: Python :: 3.9
13
13
  Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
- Requires-Dist: Cython (>=0,<1)
17
- Requires-Dist: dtw-python (>=1,<2)
18
- Requires-Dist: llvmlite (>=0,<1)
19
- Requires-Dist: lyricsgenius (>=3,<4)
20
- Requires-Dist: numba (>=0.57,<0.58)
21
- Requires-Dist: numpy (>=1,<2)
22
- Requires-Dist: onnx (>=1,<2)
23
- Requires-Dist: onnxruntime (>=1,<2)
16
+ Requires-Dist: Cython (>=0)
17
+ Requires-Dist: dtw-python (>=1)
18
+ Requires-Dist: llvmlite (>=0)
19
+ Requires-Dist: lyricsgenius (>=3)
20
+ Requires-Dist: numba (>=0.57)
21
+ Requires-Dist: numpy (>=1)
22
+ Requires-Dist: onnx (>=1)
23
+ Requires-Dist: onnxruntime (>=1)
24
24
  Requires-Dist: openai (>=1,<2)
25
- Requires-Dist: openai-whisper (==20231117)
26
- Requires-Dist: python-slugify (>=8,<9)
27
- Requires-Dist: syrics (>=0,<1)
28
- Requires-Dist: torch (>1)
29
- Requires-Dist: tqdm (>=4,<5)
30
- Requires-Dist: whisper-timestamped (>=1,<2)
25
+ Requires-Dist: openai-whisper (>=20231117)
26
+ Requires-Dist: python-slugify (>=8)
27
+ Requires-Dist: syrics (>=0)
28
+ Requires-Dist: torch (>=1)
29
+ Requires-Dist: tqdm (>=4)
30
+ Requires-Dist: transformers (>=4)
31
+ Requires-Dist: whisper-timestamped (>=1)
31
32
  Project-URL: Documentation, https://github.com/karaokenerds/python-lyrics-transcriber/blob/main/README.md
32
33
  Project-URL: Repository, https://github.com/karaokenerds/python-lyrics-transcriber
33
34
  Description-Content-Type: text/markdown
@@ -0,0 +1,61 @@
1
+ # This configuration runs each prompt through a series of example inputs and checks if they meet requirements.
2
+ # Learn more: https://promptfoo.dev/docs/configuration/guide
3
+
4
+ description: Song lyric corrector for a karaoke video studio, responsible for reading lyrics inputs, correcting them and generating JSON-based responses containing the corrected lyrics according to predefined criteria.
5
+ providers:
6
+ - id: openai:gpt-3.5-turbo-1106
7
+ config:
8
+ temperature: 0
9
+ # - id: openai:gpt-4-1106-preview
10
+ # config:
11
+ # temperature: 0
12
+ prompts:
13
+ - file://llm_prompt_lyrics_correction_andrew_handwritten_20231118.txt
14
+
15
+ defaultTest:
16
+ assert:
17
+ - type: is-json
18
+ value:
19
+ required: [id, text, words]
20
+ type: object
21
+ properties:
22
+ id:
23
+ type: number
24
+ text:
25
+ type: string
26
+ words:
27
+ type: array
28
+ items:
29
+ type: object
30
+ properties:
31
+ text:
32
+ type: string
33
+ start:
34
+ type: number
35
+ end:
36
+ type: number
37
+ confidence:
38
+ type: number
39
+
40
+ tests:
41
+ - description: ABBA - Under Attack (segment 0)
42
+ vars:
43
+ reference_lyrics: file://test_data/ABBA-UnderAttack-Genius.txt
44
+ previous_two_corrected_lines:
45
+ upcoming_two_uncorrected_lines:
46
+ segment_input: |
47
+ {"id": 0, "start": 17.46, "end": 21.3, "confidence": 0.792, "text": " Don't know how to take it, don't know where to go", "words": [{"text": "Don't", "start": 17.46, "end": 18.2, "confidence": 0.278}, {"text": "know", "start": 18.2, "end": 18.42, "confidence": 0.965}, {"text": "how", "start": 18.42, "end": 18.66, "confidence": 0.865}, {"text": "to", "start": 18.66, "end": 18.88, "confidence": 0.994}, {"text": "take", "start": 18.88, "end": 19.2, "confidence": 0.992}, {"text": "it,", "start": 19.2, "end": 19.44, "confidence": 0.974}, {"text": "don't", "start": 19.56, "end": 19.8, "confidence": 0.917}, {"text": "know", "start": 19.8, "end": 20.02, "confidence": 0.989}, {"text": "where", "start": 20.02, "end": 20.46, "confidence": 0.963}, {"text": "to", "start": 20.46, "end": 20.76, "confidence": 0.983}, {"text": "go", "start": 20.76, "end": 21.3, "confidence": 0.982}]}
48
+ assert:
49
+ - type: contains
50
+ value: "Don't know how to take it, don't know where to go"
51
+
52
+ - description: ABBA - Under Attack (segment 1)
53
+ vars:
54
+ reference_lyrics: file://test_data/ABBA-UnderAttack-Genius.txt
55
+ previous_two_corrected_lines:
56
+ upcoming_two_uncorrected_lines:
57
+ segment_input: |
58
+ {"id": 1, "start": 22.04, "end": 27.84, "confidence": 0.763, "text": " My resistance running low And every day the hole is getting tighter", "words": [{"text": "My", "start": 22.04, "end": 22.32, "confidence": 0.535}, {"text": "resistance", "start": 22.32, "end": 22.94, "confidence": 0.936}, {"text": "running", "start": 22.94, "end": 23.66, "confidence": 0.89}, {"text": "low", "start": 23.66, "end": 24.36, "confidence": 0.999}, {"text": "And", "start": 24.36, "end": 25.14, "confidence": 0.485}, {"text": "every", "start": 25.14, "end": 25.56, "confidence": 0.568}, {"text": "day", "start": 25.56, "end": 25.88, "confidence": 0.997}, {"text": "the", "start": 25.88, "end": 26.1, "confidence": 0.959}, {"text": "hole", "start": 26.1, "end": 26.48, "confidence": 0.361}, {"text": "is", "start": 26.48, "end": 26.68, "confidence": 0.947}, {"text": "getting", "start": 26.68, "end": 27.08, "confidence": 0.996}, {"text": "tighter", "start": 27.08, "end": 27.84, "confidence": 0.975}]}
59
+ assert:
60
+ - type: contains
61
+ value: "My resistance running low And every day the hold is getting tighter"
@@ -0,0 +1,48 @@
1
+ Don't know how to take it, don't know where to go
2
+ My resistance running low
3
+ And every day the hold is getting tighter and it troubles me so
4
+ (You know that I'm nobody's fool)
5
+ I'm nobody's fool and yet it's clear to me
6
+ I don't have a strategy
7
+ It's just like taking candy from a baby and I think I must be
8
+
9
+ Under attack, I'm being taken
10
+ About to crack, defences breaking
11
+ Won't somebody please have a heart
12
+ Come and rescue me now 'cause I'm falling apart
13
+ Under attack, I'm taking cover
14
+ He's on my track, my chasing lover
15
+ Thinking nothing can stop him now
16
+ Should I want to, I'm not sure I would know how
17
+
18
+ This is getting crazy, I should tell him so
19
+ Really let my anger show
20
+ Persuade him that the answer to his questions is a definite no
21
+ (I'm kind of flattered I suppose)
22
+ Guess I'm kind of flattered but I'm scared as well
23
+ Something like a magic spell
24
+ I hardly dare to think of what would happen, where I'd be if I fell
25
+
26
+ Under attack, I'm being taken
27
+ About to crack, defences breaking
28
+ Won't somebody please have a heart
29
+ Come and rescue me now 'cause I'm falling apart
30
+ Under attack, I'm taking cover
31
+ He's on my track, my chasing lover
32
+ Thinking nothing's gonna stop him now
33
+ Should I want to, I'm not sure I won't know how
34
+
35
+ Under attack, I'm being taken
36
+ About to crack, defences breaking
37
+ Won't somebody see and save a heart
38
+ Come and rescue me now 'cause I'm falling apart
39
+ Under attack, I'm taking cover
40
+ He's on my track, my chasing lover
41
+ Thinking nothing can stop him now
42
+ Should I want to, I'm not sure I would know how
43
+
44
+ Under attack, I'm being taken
45
+ About to crack, defences breaking
46
+ Won't somebody please have a heart
47
+ Come and rescue me now 'cause I'm falling apart
48
+ Under attack, I'm taking cover
@@ -190,6 +190,9 @@ class LyricsTranscriber:
190
190
  online_lyrics_text_key = f"{online_lyrics_source}_lyrics_text"
191
191
  online_lyrics_filepath_key = f"{online_lyrics_source}_lyrics_filepath"
192
192
 
193
+ if online_lyrics_text_key not in self.outputs:
194
+ continue
195
+
193
196
  data_input_str = (
194
197
  f'Data input 1:\n{self.outputs["transcribed_lyrics_text"]}\nData input 2:\n{self.outputs[online_lyrics_text_key]}\n'
195
198
  )
@@ -274,7 +277,7 @@ class LyricsTranscriber:
274
277
  total_segments = len(self.outputs["transcription_data_dict"]["segments"])
275
278
  self.logger.info(f"Beginning correction using LLM, total segments: {total_segments}")
276
279
 
277
- with open(self.outputs["llm_transcript_filepath"], "a", buffering=1) as llm_transcript_file:
280
+ with open(self.outputs["llm_transcript_filepath"], "a", buffering=1, encoding="utf-8") as llm_transcript_file:
278
281
  self.logger.debug(f"writing LLM chat instructions: {self.outputs['llm_transcript_filepath']}")
279
282
 
280
283
  llm_transcript_header = f"--- SYSTEM instructions passed in for all segments ---:\n\n{system_prompt}\n"
@@ -303,14 +306,13 @@ class LyricsTranscriber:
303
306
  previous_two_corrected_lines = ""
304
307
  upcoming_two_uncorrected_lines = ""
305
308
 
306
- if segment["id"] > 2:
307
- for previous_segment in corrected_lyrics_dict["segments"]:
308
- if previous_segment["id"] in (segment["id"] - 2, segment["id"] - 1):
309
- previous_two_corrected_lines += previous_segment["text"].strip() + "\n"
309
+ for previous_segment in corrected_lyrics_dict["segments"]:
310
+ if previous_segment["id"] in (segment["id"] - 2, segment["id"] - 1):
311
+ previous_two_corrected_lines += previous_segment["text"].strip() + "\n"
310
312
 
311
- for next_segment in self.outputs["transcription_data_dict"]["segments"]:
312
- if next_segment["id"] in (segment["id"] + 1, segment["id"] + 2):
313
- upcoming_two_uncorrected_lines += next_segment["text"].strip() + "\n"
313
+ for next_segment in self.outputs["transcription_data_dict"]["segments"]:
314
+ if next_segment["id"] in (segment["id"] + 1, segment["id"] + 2):
315
+ upcoming_two_uncorrected_lines += next_segment["text"].strip() + "\n"
314
316
 
315
317
  llm_transcript_segment += f"--- Segment {segment['id']} / {total_segments} ---\n"
316
318
  llm_transcript_segment += f"Previous two corrected lines:\n\n{previous_two_corrected_lines}\nUpcoming two uncorrected lines:\n\n{upcoming_two_uncorrected_lines}\nData input:\n\n{segment_input}\n"
@@ -371,7 +373,7 @@ class LyricsTranscriber:
371
373
  self.logger.info(f'Successfully processed correction for all {len(corrected_lyrics_dict["segments"])} lyrics segments')
372
374
 
373
375
  self.logger.debug(f"writing corrected lyrics data JSON filepath: {corrected_lyrics_data_json_cache_filepath}")
374
- with open(corrected_lyrics_data_json_cache_filepath, "w") as corrected_lyrics_data_json_cache_file:
376
+ with open(corrected_lyrics_data_json_cache_filepath, "w", encoding="utf-8") as corrected_lyrics_data_json_cache_file:
375
377
  corrected_lyrics_data_json_cache_file.write(json.dumps(corrected_lyrics_dict, indent=4))
376
378
 
377
379
  self.outputs["corrected_lyrics_data_filepath"] = corrected_lyrics_data_json_cache_filepath
@@ -406,7 +408,7 @@ class LyricsTranscriber:
406
408
  self.outputs["corrected_lyrics_text"] = ""
407
409
 
408
410
  self.logger.debug(f"writing lyrics plain text to corrected_lyrics_text_filepath: {corrected_lyrics_text_filepath}")
409
- with open(corrected_lyrics_text_filepath, "w") as f:
411
+ with open(corrected_lyrics_text_filepath, "w", encoding="utf-8") as f:
410
412
  for corrected_segment in self.outputs["corrected_lyrics_data_dict"]["segments"]:
411
413
  self.outputs["corrected_lyrics_text"] += corrected_segment["text"].strip() + "\n"
412
414
  f.write(corrected_segment["text".strip()] + "\n")
@@ -453,7 +455,7 @@ class LyricsTranscriber:
453
455
  self.logger.debug(
454
456
  f"writing lyrics data JSON to spotify_lyrics_data_json_cache_filepath: {spotify_lyrics_data_json_cache_filepath}"
455
457
  )
456
- with open(spotify_lyrics_data_json_cache_filepath, "w") as f:
458
+ with open(spotify_lyrics_data_json_cache_filepath, "w", encoding="utf-8") as f:
457
459
  f.write(spotify_lyrics_json)
458
460
  except Exception as e:
459
461
  self.logger.warn(f"caught exception while attempting to fetch from spotify: ", e)
@@ -473,7 +475,7 @@ class LyricsTranscriber:
473
475
  self.outputs["spotify_lyrics_text"] = ""
474
476
 
475
477
  self.logger.debug(f"writing lyrics plain text to spotify_lyrics_text_filepath: {spotify_lyrics_text_filepath}")
476
- with open(spotify_lyrics_text_filepath, "w") as f:
478
+ with open(spotify_lyrics_text_filepath, "w", encoding="utf-8") as f:
477
479
  for line in lines:
478
480
  self.outputs["spotify_lyrics_text"] += line["words"].strip() + "\n"
479
481
  f.write(line["words"].strip() + "\n")
@@ -505,7 +507,7 @@ class LyricsTranscriber:
505
507
  lyrics = self.clean_genius_lyrics(song.lyrics)
506
508
 
507
509
  self.logger.debug(f"writing clean lyrics to genius_lyrics_cache_filepath: {genius_lyrics_cache_filepath}")
508
- with open(genius_lyrics_cache_filepath, "w") as f:
510
+ with open(genius_lyrics_cache_filepath, "w", encoding="utf-8") as f:
509
511
  f.write(lyrics)
510
512
 
511
513
  self.outputs["genius_lyrics_filepath"] = genius_lyrics_cache_filepath
@@ -559,7 +561,7 @@ class LyricsTranscriber:
559
561
 
560
562
  lrc_filename = self.outputs["midico_lrc_filepath"]
561
563
  self.logger.debug(f"writing midico formatted word timestamps to LRC file: {lrc_filename}")
562
- with open(lrc_filename, "w") as f:
564
+ with open(lrc_filename, "w", encoding="utf-8") as f:
563
565
  f.write("[re:MidiCo]\n")
564
566
  for segment in self.outputs["corrected_lyrics_data_dict"]["segments"]:
565
567
  for i, word in enumerate(segment["words"]):
@@ -753,7 +755,7 @@ class LyricsTranscriber:
753
755
  self.outputs["transcribed_lyrics_text"] = ""
754
756
 
755
757
  self.logger.debug(f"writing lyrics plain text to transcribed_lyrics_text_filepath: {transcribed_lyrics_text_filepath}")
756
- with open(transcribed_lyrics_text_filepath, "w") as f:
758
+ with open(transcribed_lyrics_text_filepath, "w", encoding="utf-8") as f:
757
759
  for segment in self.outputs["transcription_data_dict"]["segments"]:
758
760
  self.outputs["transcribed_lyrics_text"] += segment["text"] + "\n"
759
761
  f.write(segment["text"].strip() + "\n")
@@ -2,7 +2,6 @@
2
2
  import argparse
3
3
  import logging
4
4
  import pkg_resources
5
- from lyrics_transcriber import LyricsTranscriber
6
5
 
7
6
 
8
7
  def main():
@@ -110,6 +109,9 @@ def main():
110
109
 
111
110
  logger.debug("Loading LyricsTranscriber class")
112
111
 
112
+ # Lazy load this class so help output is printed quickly rather than waiting for heavy libraries to load
113
+ from lyrics_transcriber import LyricsTranscriber
114
+
113
115
  transcriber = LyricsTranscriber(
114
116
  args.audio_filepath,
115
117
  genius_api_token=args.genius_api_token,
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "lyrics-transcriber"
3
- version = "0.12.8"
3
+ version = "0.13.1"
4
4
  description = "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
  authors = ["Andrew Beveridge <andrew@beveridge.uk>"]
6
6
  license = "MIT"
@@ -13,21 +13,22 @@ documentation = "https://github.com/karaokenerds/python-lyrics-transcriber/blob/
13
13
 
14
14
  [tool.poetry.dependencies]
15
15
  python = ">=3.9"
16
- Cython = "^0"
17
- dtw-python = "^1"
18
- llvmlite = "^0"
19
- numba = "^0.57"
20
- numpy = "^1"
21
- onnx = "^1"
22
- onnxruntime = "^1"
23
- torch = ">1"
24
- tqdm = "^4"
25
- lyricsgenius = "^3"
26
- python-slugify = "^8"
27
- syrics = "^0"
16
+ Cython = ">=0"
17
+ dtw-python = ">=1"
18
+ llvmlite = ">=0"
19
+ numba = ">=0.57"
20
+ numpy = ">=1"
21
+ onnx = ">=1"
22
+ onnxruntime = ">=1"
23
+ torch = ">=1"
24
+ tqdm = ">=4"
25
+ lyricsgenius = ">=3"
26
+ python-slugify = ">=8"
27
+ syrics = ">=0"
28
28
  openai = "^1"
29
- openai-whisper = "20231117"
30
- whisper-timestamped = "^1"
29
+ openai-whisper = ">=20231117"
30
+ transformers = ">=4"
31
+ whisper-timestamped = ">=1"
31
32
  # Note: after adding openai-whisper and whisper-timestamped with poetry lock, I then removed all traces of triton
32
33
  # from poetry.lock before running poetry install, as triton doesn't support macOS but isn't actually needed for whisper.
33
34
  # This was the only way I was able to get a working cross-platform build published to PyPI.
@@ -1,39 +0,0 @@
1
- # This configuration runs each prompt through a series of example inputs and checks if they meet requirements.
2
- # Learn more: https://promptfoo.dev/docs/configuration/guide
3
-
4
- prompts:
5
- - file://llm_prompt_lyrics_correction_*.txt
6
- providers: [openai:gpt-3.5-turbo-0613, openai:gpt-4-1106-preview]
7
- tests:
8
- - description: First test case - automatic review
9
- vars:
10
- var1: first variable's value
11
- var2: another value
12
- var3: some other value
13
- # For more information on assertions, see https://promptfoo.dev/docs/configuration/expected-outputs
14
- assert:
15
- - type: equals
16
- value: expected LLM output goes here
17
- - type: contains
18
- value: some text
19
- - type: javascript
20
- value: 1 / (output.length + 1) # prefer shorter outputs
21
-
22
- - description: Second test case - manual review
23
- # Test cases don't need assertions if you prefer to manually review the output
24
- vars:
25
- var1: new value
26
- var2: another value
27
- var3: third value
28
-
29
- - description: Third test case - other types of automatic review
30
- vars:
31
- var1: yet another value
32
- var2: and another
33
- var3: dear llm, please output your response in json format
34
- assert:
35
- - type: contains-json
36
- - type: similar
37
- value: ensures that output is semantically similar to this text
38
- - type: model-graded-closedqa
39
- value: ensure that output contains a reference to X