lyrics-transcriber 0.39.0__py3-none-any.whl → 0.41.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.
@@ -68,6 +68,12 @@ def create_arg_parser() -> argparse.ArgumentParser:
68
68
  type=Path,
69
69
  help="JSON file containing output style configurations for CDG and video generation",
70
70
  )
71
+ output_group.add_argument(
72
+ "--subtitle_offset",
73
+ type=int,
74
+ default=0,
75
+ help="Offset subtitle timing by N milliseconds (positive or negative). Default: 0",
76
+ )
71
77
 
72
78
  # Feature control group
73
79
  feature_group = parser.add_argument_group("Feature Control")
@@ -143,6 +149,7 @@ def create_configs(args: argparse.Namespace, env_config: Dict[str, str]) -> tupl
143
149
  output_dir=str(args.output_dir) if args.output_dir else os.getcwd(),
144
150
  cache_dir=str(args.cache_dir),
145
151
  video_resolution=args.video_resolution,
152
+ subtitle_offset_ms=args.subtitle_offset,
146
153
  fetch_lyrics=not args.skip_lyrics_fetch,
147
154
  run_transcription=not args.skip_transcription,
148
155
  run_correction=not args.skip_correction,
@@ -43,3 +43,4 @@ class OutputConfig:
43
43
  generate_cdg: bool = True
44
44
  render_video: bool = True
45
45
  video_resolution: str = "360p"
46
+ subtitle_offset_ms: int = 0
@@ -9,6 +9,7 @@ import os
9
9
  import zipfile
10
10
  import shutil
11
11
 
12
+ from lyrics_transcriber.output.cdgmaker.cdg import CDG_VISIBLE_WIDTH
12
13
  from lyrics_transcriber.output.cdgmaker.composer import KaraokeComposer
13
14
  from lyrics_transcriber.output.cdgmaker.render import get_wrapped_text
14
15
  from lyrics_transcriber.types import LyricsSegment
@@ -110,7 +111,7 @@ class CDGGenerator:
110
111
  # Convert time from seconds to centiseconds
111
112
  timestamp = int(word.start_time * 100)
112
113
  lyrics_data.append({"timestamp": timestamp, "text": word.text.upper()}) # CDG format expects uppercase text
113
- # self.logger.debug(f"Added lyric: timestamp {timestamp}, text '{word.text}'")
114
+ self.logger.debug(f"Added lyric: timestamp {timestamp}, text '{word.text}'")
114
115
 
115
116
  # Sort by timestamp to ensure correct order
116
117
  lyrics_data.sort(key=lambda x: x["timestamp"])
@@ -125,7 +126,7 @@ class CDGGenerator:
125
126
  cdg_styles: dict,
126
127
  ) -> str:
127
128
  """Create TOML configuration file for CDG generation."""
128
- safe_filename = self._get_safe_filename(artist, title, "Karaoke CDG", "toml")
129
+ safe_filename = self._get_safe_filename(artist, title, "Karaoke", "toml")
129
130
  toml_file = os.path.join(self.output_dir, safe_filename)
130
131
  self.logger.debug(f"Generating TOML file: {toml_file}")
131
132
 
@@ -160,7 +161,7 @@ class CDGGenerator:
160
161
  title=title,
161
162
  artist=artist,
162
163
  audio_file=audio_file,
163
- output_name=f"{artist} - {title} (Karaoke CDG)",
164
+ output_name=f"{artist} - {title} (Karaoke)",
164
165
  sync_times=sync_times,
165
166
  instrumentals=instrumentals,
166
167
  formatted_lyrics=formatted_lyrics,
@@ -189,11 +190,11 @@ class CDGGenerator:
189
190
  """Compose CDG using KaraokeComposer."""
190
191
  kc = KaraokeComposer.from_file(toml_file)
191
192
  kc.compose()
192
- kc.create_mp4(height=1080, fps=30)
193
+ # kc.create_mp4(height=1080, fps=30)
193
194
 
194
195
  def _find_cdg_zip(self, artist: str, title: str) -> str:
195
196
  """Find the generated CDG ZIP file."""
196
- safe_filename = self._get_safe_filename(artist, title, "Karaoke CDG", "zip")
197
+ safe_filename = self._get_safe_filename(artist, title, "Karaoke", "zip")
197
198
  output_zip = os.path.join(self.output_dir, safe_filename)
198
199
 
199
200
  self.logger.info(f"Looking for CDG ZIP file in output directory: {output_zip}")
@@ -215,12 +216,12 @@ class CDGGenerator:
215
216
 
216
217
  def _get_cdg_path(self, artist: str, title: str) -> str:
217
218
  """Get the path to the CDG file."""
218
- safe_filename = self._get_safe_filename(artist, title, "Karaoke CDG", "cdg")
219
+ safe_filename = self._get_safe_filename(artist, title, "Karaoke", "cdg")
219
220
  return os.path.join(self.output_dir, safe_filename)
220
221
 
221
222
  def _get_mp3_path(self, artist: str, title: str) -> str:
222
223
  """Get the path to the MP3 file."""
223
- safe_filename = self._get_safe_filename(artist, title, "Karaoke CDG", "mp3")
224
+ safe_filename = self._get_safe_filename(artist, title, "Karaoke", "mp3")
224
225
  return os.path.join(self.output_dir, safe_filename)
225
226
 
226
227
  def _verify_output_files(self, cdg_file: str, mp3_file: str) -> None:
@@ -338,20 +339,20 @@ class CDGGenerator:
338
339
  formatted_lyrics = []
339
340
 
340
341
  for i, lyric in enumerate(lyrics_data):
341
- # self.logger.debug(f"Processing lyric {i}: timestamp {lyric['timestamp']}, text '{lyric['text']}'")
342
+ self.logger.debug(f"Processing lyric {i}: timestamp {lyric['timestamp']}, text '{lyric['text']}'")
342
343
 
343
344
  if i == 0 or lyric["timestamp"] - lyrics_data[i - 1]["timestamp"] >= cdg_styles["lead_in_threshold"]:
344
345
  lead_in_start = lyric["timestamp"] - cdg_styles["lead_in_total"]
345
- # self.logger.debug(f"Adding lead-in before lyric {i} at timestamp {lead_in_start}")
346
+ self.logger.debug(f"Adding lead-in before lyric {i} at timestamp {lead_in_start}")
346
347
  for j, symbol in enumerate(cdg_styles["lead_in_symbols"]):
347
348
  sync_time = lead_in_start + j * cdg_styles["lead_in_duration"]
348
349
  sync_times.append(sync_time)
349
350
  formatted_lyrics.append(symbol)
350
- # self.logger.debug(f" Added lead-in symbol {j+1}: '{symbol}' at {sync_time}")
351
+ self.logger.debug(f" Added lead-in symbol {j+1}: '{symbol}' at {sync_time}")
351
352
 
352
353
  sync_times.append(lyric["timestamp"])
353
354
  formatted_lyrics.append(lyric["text"])
354
- # self.logger.debug(f"Added lyric: '{lyric['text']}' at {lyric['timestamp']}")
355
+ self.logger.debug(f"Added lyric: '{lyric['text']}' at {lyric['timestamp']}")
355
356
 
356
357
  formatted_text = self.format_lyrics(
357
358
  formatted_lyrics,
@@ -375,7 +376,7 @@ class CDGGenerator:
375
376
  cdg_styles: dict,
376
377
  ) -> dict:
377
378
  """Create TOML data structure."""
378
- safe_output_name = self._get_safe_filename(artist, title, "Karaoke CDG")
379
+ safe_output_name = self._get_safe_filename(artist, title, "Karaoke")
379
380
  return {
380
381
  "title": title,
381
382
  "artist": artist,
@@ -473,24 +474,29 @@ class CDGGenerator:
473
474
  page_number = 1
474
475
 
475
476
  for i, text in enumerate(lyrics_data):
476
- # self.logger.debug(f"Processing text {i}: '{text}' (sync time: {sync_times[i]})")
477
+ self.logger.debug(f"format_lyrics: Processing text {i}: '{text}' (sync time: {sync_times[i]})")
477
478
 
478
479
  if text.startswith("/"):
479
480
  if current_line:
480
- wrapped_lines = get_wrapped_text(current_line.strip(), font, self.cdg_visible_width).split("\n")
481
+ wrapped_lines = get_wrapped_text(current_line.strip(), font, CDG_VISIBLE_WIDTH).split("\n")
481
482
  for wrapped_line in wrapped_lines:
482
483
  formatted_lyrics.append(wrapped_line)
483
484
  lines_on_page += 1
484
- # self.logger.debug(f"Added wrapped line: '{wrapped_line}'. Lines on page: {lines_on_page}")
485
+ self.logger.debug(f"format_lyrics: Added wrapped line: '{wrapped_line}'. Lines on page: {lines_on_page}")
486
+ # Add empty line after punctuation immediately
487
+ if wrapped_line.endswith(("!", "?", ".")) and not wrapped_line == "~":
488
+ formatted_lyrics.append("~")
489
+ lines_on_page += 1
490
+ self.logger.debug(f"format_lyrics: Added empty line after punctuation. Lines on page now: {lines_on_page}")
485
491
  if lines_on_page == 4:
486
492
  lines_on_page = 0
487
493
  page_number += 1
488
- # self.logger.debug(f"Page full. New page number: {page_number}")
494
+ self.logger.debug(f"format_lyrics: Page full. New page number: {page_number}")
489
495
  current_line = ""
490
496
  text = text[1:]
491
497
 
492
498
  current_line += text + " "
493
- # self.logger.debug(f"Current line: '{current_line}'")
499
+ self.logger.debug(f"format_lyrics: Current line: '{current_line}'")
494
500
 
495
501
  is_last_before_instrumental = any(
496
502
  inst["sync"] > sync_times[i] and (i == len(sync_times) - 1 or sync_times[i + 1] > inst["sync"]) for inst in instrumentals
@@ -498,33 +504,103 @@ class CDGGenerator:
498
504
 
499
505
  if is_last_before_instrumental or i == len(lyrics_data) - 1:
500
506
  if current_line:
501
- wrapped_lines = get_wrapped_text(current_line.strip(), font, self.cdg_visible_width).split("\n")
507
+ wrapped_lines = get_wrapped_text(current_line.strip(), font, CDG_VISIBLE_WIDTH).split("\n")
502
508
  for wrapped_line in wrapped_lines:
503
509
  formatted_lyrics.append(wrapped_line)
504
510
  lines_on_page += 1
505
- # self.logger.debug(f"Added wrapped line at end of section: '{wrapped_line}'. Lines on page: {lines_on_page}")
511
+ self.logger.debug(
512
+ f"format_lyrics: Added wrapped line at end of section: '{wrapped_line}'. Lines on page: {lines_on_page}"
513
+ )
506
514
  if lines_on_page == 4:
507
515
  lines_on_page = 0
508
516
  page_number += 1
509
- # self.logger.debug(f"Page full. New page number: {page_number}")
517
+ self.logger.debug(f"format_lyrics: Page full. New page number: {page_number}")
510
518
  current_line = ""
511
519
 
512
520
  if is_last_before_instrumental:
513
- blank_lines_needed = 4 - lines_on_page
514
- if blank_lines_needed < 4:
515
- formatted_lyrics.extend(["~"] * blank_lines_needed)
516
- # self.logger.debug(f"Added {blank_lines_needed} empty lines before instrumental. Lines on page was {lines_on_page}")
521
+ self.logger.debug(f"format_lyrics: is_last_before_instrumental: True lines_on_page: {lines_on_page}")
522
+ # Calculate remaining lines needed to reach next full page
523
+ remaining_lines = 4 - (lines_on_page % 4) if lines_on_page % 4 != 0 else 0
524
+ if remaining_lines > 0:
525
+ formatted_lyrics.extend(["~"] * remaining_lines)
526
+ self.logger.debug(f"format_lyrics: Added {remaining_lines} empty lines to complete current page")
527
+
528
+ # Reset the counter and increment page
517
529
  lines_on_page = 0
518
530
  page_number += 1
519
- # self.logger.debug(f"Reset lines_on_page to 0. New page number: {page_number}")
520
-
521
- final_lyrics = []
522
- for line in formatted_lyrics:
523
- final_lyrics.append(line)
524
- if line.endswith(("!", "?", ".")) and not line == "~":
525
- final_lyrics.append("~")
526
- # self.logger.debug("Added empty line after punctuation")
527
-
528
- result = "\n".join(final_lyrics)
529
- # self.logger.debug(f"Final formatted lyrics:\n{result}")
530
- return result
531
+ self.logger.debug(f"format_lyrics: Reset lines_on_page to 0. New page number: {page_number}")
532
+
533
+ return "\n".join(formatted_lyrics)
534
+
535
+ def generate_cdg_from_lrc(
536
+ self,
537
+ lrc_file: str,
538
+ audio_file: str,
539
+ title: str,
540
+ artist: str,
541
+ cdg_styles: dict,
542
+ ) -> Tuple[str, str, str]:
543
+ """Generate a CDG file from an LRC file and audio file.
544
+
545
+ Args:
546
+ lrc_file: Path to the LRC file
547
+ audio_file: Path to the audio file
548
+ title: Title of the song
549
+ artist: Artist name
550
+ cdg_styles: Dictionary containing CDG style parameters
551
+
552
+ Returns:
553
+ Tuple containing paths to (cdg_file, mp3_file, zip_file)
554
+ """
555
+ self._validate_and_setup_font(cdg_styles)
556
+
557
+ # Parse LRC file and convert to lyrics_data format
558
+ lyrics_data = self._parse_lrc(lrc_file)
559
+
560
+ toml_file = self._create_toml_file(
561
+ audio_file=audio_file,
562
+ title=title,
563
+ artist=artist,
564
+ lyrics_data=lyrics_data,
565
+ cdg_styles=cdg_styles,
566
+ )
567
+
568
+ try:
569
+ self._compose_cdg(toml_file)
570
+ output_zip = self._find_cdg_zip(artist, title)
571
+ self._extract_cdg_files(output_zip)
572
+
573
+ cdg_file = self._get_cdg_path(artist, title)
574
+ mp3_file = self._get_mp3_path(artist, title)
575
+
576
+ self._verify_output_files(cdg_file, mp3_file)
577
+
578
+ self.logger.info("CDG file generated successfully")
579
+ return cdg_file, mp3_file, output_zip
580
+
581
+ except Exception as e:
582
+ self.logger.error(f"Error composing CDG: {e}")
583
+ raise
584
+
585
+ def _parse_lrc(self, lrc_file: str) -> List[dict]:
586
+ """Parse LRC file and extract timestamps and lyrics."""
587
+ with open(lrc_file, "r", encoding="utf-8") as f:
588
+ content = f.read()
589
+
590
+ # Extract timestamps and lyrics
591
+ pattern = r"\[(\d{2}):(\d{2})\.(\d{3})\](\d+:)?(/?.*)"
592
+ matches = re.findall(pattern, content)
593
+
594
+ if not matches:
595
+ raise ValueError(f"No valid lyrics found in the LRC file: {lrc_file}")
596
+
597
+ lyrics = []
598
+ for match in matches:
599
+ minutes, seconds, milliseconds = map(int, match[:3])
600
+ timestamp = (minutes * 60 + seconds) * 100 + int(milliseconds / 10) # Convert to centiseconds
601
+ text = match[4].strip().upper()
602
+ if text: # Only add non-empty lyrics
603
+ lyrics.append({"timestamp": timestamp, "text": text})
604
+
605
+ self.logger.info(f"Found {len(lyrics)} lyric lines")
606
+ return lyrics
@@ -1097,27 +1097,16 @@ class KaraokeComposer:
1097
1097
  else:
1098
1098
  logger.debug("this instrumental did not wait for the previous " "line to finish")
1099
1099
 
1100
- logger.debug("purging all highlight/draw queues")
1100
+ logger.debug("_compose_lyric: Purging all highlight/draw queues")
1101
1101
  for st in lyric_states:
1102
- # If instrumental has waited for this syllable to end
1103
1102
  if instrumental.wait:
1104
- # There shouldn't be anything in the highlight queue
1105
- assert not st.highlight_queue
1106
- # If there's anything left in the draw queue
1103
+ if st.highlight_queue:
1104
+ logger.warning("_compose_lyric: Unexpected items in highlight queue when instrumental waited")
1107
1105
  if st.draw_queue:
1108
- # NOTE If the current lyric state has anything
1109
- # left in the draw queue, it should be the
1110
- # erasing of the current line.
1111
1106
  if st == state:
1112
- assert should_erase_this_line
1113
- # Queue everything left in the draw queue
1114
- # immediately
1115
- self.lyric_packet_indices.update(
1116
- range(
1117
- self.writer.packets_queued,
1118
- self.writer.packets_queued + len(st.draw_queue),
1119
- )
1120
- )
1107
+ logger.debug("_compose_lyric: Queueing remaining draw packets for current state")
1108
+ else:
1109
+ logger.warning("_compose_lyric: Unexpected items in draw queue for non-current state")
1121
1110
  self.writer.queue_packets(st.draw_queue)
1122
1111
 
1123
1112
  # Purge highlight/draw queues
@@ -78,6 +78,7 @@ class OutputGenerator:
78
78
  font_size=self.font_size,
79
79
  line_height=self.line_height,
80
80
  styles=self.config.styles,
81
+ subtitle_offset_ms=self.config.subtitle_offset_ms,
81
82
  logger=self.logger,
82
83
  )
83
84
 
@@ -161,12 +162,12 @@ class OutputGenerator:
161
162
  "720p": (1280, 720),
162
163
  "360p": (640, 360),
163
164
  }
164
-
165
+
165
166
  if resolution not in resolution_map:
166
167
  raise ValueError("Invalid video_resolution value. Must be one of: 4k, 1080p, 720p, 360p")
167
-
168
+
168
169
  resolution_dims = resolution_map[resolution]
169
-
170
+
170
171
  # Default font sizes for each resolution
171
172
  default_font_sizes = {
172
173
  "4k": 250,
@@ -174,13 +175,13 @@ class OutputGenerator:
174
175
  "720p": 100,
175
176
  "360p": 40,
176
177
  }
177
-
178
+
178
179
  # Get font size from styles if available, otherwise use default
179
180
  font_size = self.config.styles.get("karaoke", {}).get("font_size", default_font_sizes[resolution])
180
-
181
+
181
182
  # Line height matches font size for all except 360p
182
183
  line_height = 50 if resolution == "360p" else font_size
183
-
184
+
184
185
  return resolution_dims, font_size, line_height
185
186
 
186
187
  def write_corrections_data(self, correction_result: CorrectionResult, output_prefix: str) -> str:
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import logging
4
+ import argparse
5
+ import json
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ from lyrics_transcriber.output.cdg import CDGGenerator
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ def cli_main():
15
+ """Command-line interface entry point for the lrc2cdg tool."""
16
+ logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
17
+
18
+ parser = argparse.ArgumentParser(description="Convert LRC file to CDG")
19
+ parser.add_argument("lrc_file", help="Path to the LRC file")
20
+ parser.add_argument("audio_file", help="Path to the audio file")
21
+ parser.add_argument("--title", required=True, help="Title of the song")
22
+ parser.add_argument("--artist", required=True, help="Artist of the song")
23
+ parser.add_argument("--style_params_json", required=True, help="Path to JSON file containing CDG style configuration")
24
+
25
+ args = parser.parse_args()
26
+
27
+ try:
28
+ with open(args.style_params_json, "r") as f:
29
+ style_params = json.loads(f.read())
30
+ cdg_styles = style_params["cdg"]
31
+ except FileNotFoundError:
32
+ logger.error(f"Style configuration file not found: {args.style_params_json}")
33
+ sys.exit(1)
34
+ except json.JSONDecodeError as e:
35
+ logger.error(f"Invalid JSON in style configuration file: {e}")
36
+ sys.exit(1)
37
+
38
+ try:
39
+ output_dir = str(Path(args.lrc_file).parent)
40
+ generator = CDGGenerator(output_dir=output_dir, logger=logger)
41
+
42
+ cdg_file, mp3_file, zip_file = generator.generate_cdg_from_lrc(
43
+ lrc_file=args.lrc_file,
44
+ audio_file=args.audio_file,
45
+ title=args.title,
46
+ artist=args.artist,
47
+ cdg_styles=cdg_styles,
48
+ )
49
+
50
+ logger.info(f"Generated files:\nCDG: {cdg_file}\nMP3: {mp3_file}\nZIP: {zip_file}")
51
+
52
+ except ValueError as e:
53
+ logger.error(f"Invalid style configuration: {e}")
54
+ sys.exit(1)
55
+ except Exception as e:
56
+ logger.error(f"Error generating CDG: {e}")
57
+ sys.exit(1)
58
+
59
+
60
+ if __name__ == "__main__":
61
+ cli_main()
@@ -5,7 +5,7 @@ import subprocess
5
5
  import json
6
6
 
7
7
  from lyrics_transcriber.output.ass.section_screen import SectionScreen
8
- from lyrics_transcriber.types import LyricsSegment
8
+ from lyrics_transcriber.types import LyricsSegment, Word
9
9
  from lyrics_transcriber.output.ass import LyricsScreen, LyricsLine
10
10
  from lyrics_transcriber.output.ass.ass import ASS
11
11
  from lyrics_transcriber.output.ass.style import Style
@@ -25,6 +25,7 @@ class SubtitlesGenerator:
25
25
  font_size: int,
26
26
  line_height: int,
27
27
  styles: dict,
28
+ subtitle_offset_ms: int = 0,
28
29
  logger: Optional[logging.Logger] = None,
29
30
  ):
30
31
  """Initialize SubtitleGenerator.
@@ -34,12 +35,15 @@ class SubtitlesGenerator:
34
35
  video_resolution: Tuple of (width, height) for video resolution
35
36
  font_size: Font size for subtitles
36
37
  line_height: Line height for subtitle positioning
38
+ styles: Dictionary of style configurations
39
+ subtitle_offset_ms: Offset for subtitle timing in milliseconds
37
40
  logger: Optional logger instance
38
41
  """
39
42
  self.output_dir = output_dir
40
43
  self.video_resolution = video_resolution
41
44
  self.font_size = font_size
42
45
  self.styles = styles
46
+ self.subtitle_offset_ms = subtitle_offset_ms
43
47
  self.config = ScreenConfig(line_height=line_height, video_width=video_resolution[0], video_height=video_resolution[1])
44
48
  self.logger = logger or logging.getLogger(__name__)
45
49
 
@@ -91,6 +95,30 @@ class SubtitlesGenerator:
91
95
  """Create screens from segments with detailed logging."""
92
96
  self.logger.debug("Creating screens from segments")
93
97
 
98
+ # Apply timing offset to segments if needed
99
+ if self.subtitle_offset_ms != 0:
100
+ self.logger.info(f"Subtitle offset: {self.subtitle_offset_ms}ms")
101
+
102
+ offset_seconds = self.subtitle_offset_ms / 1000.0
103
+ segments = [
104
+ LyricsSegment(
105
+ text=seg.text,
106
+ words=[
107
+ Word(
108
+ text=word.text,
109
+ start_time=max(0, word.start_time + offset_seconds),
110
+ end_time=word.end_time + offset_seconds,
111
+ confidence=word.confidence,
112
+ )
113
+ for word in seg.words
114
+ ],
115
+ start_time=max(0, seg.start_time + offset_seconds),
116
+ end_time=seg.end_time + offset_seconds,
117
+ )
118
+ for seg in segments
119
+ ]
120
+ self.logger.info(f"Applied {self.subtitle_offset_ms}ms offset to segment timings")
121
+
94
122
  # Create section screens and get instrumental boundaries
95
123
  section_screens = self._create_section_screens(segments, song_duration)
96
124
  instrumental_times = self._get_instrumental_times(section_screens)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: lyrics-transcriber
3
- Version: 0.39.0
3
+ Version: 0.41.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
  License: MIT
6
6
  Author: Andrew Beveridge
@@ -1,8 +1,8 @@
1
1
  lyrics_transcriber/__init__.py,sha256=JpdjDK1MH_Be2XiSQWnb4i5Bbil1uPMA_KcuDZ3cyUI,240
2
2
  lyrics_transcriber/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- lyrics_transcriber/cli/cli_main.py,sha256=I4YXULnIA_H9ESmXdEspOcidY2n24KdUkOTDZb7r680,9967
3
+ lyrics_transcriber/cli/cli_main.py,sha256=MDgjIlgmKDfv3z6nK_j7TJKtcijfuLrB06hDMXXaZQY,10211
4
4
  lyrics_transcriber/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- lyrics_transcriber/core/config.py,sha256=yR0hG4BFEgyzd1hdb27t5B2RI7eYdAvQ9w9RUnoIpuY,1197
5
+ lyrics_transcriber/core/config.py,sha256=euwOOtuNbXy4-a1xs8QKdjcf5jXZQle0zf6X1Wthurw,1229
6
6
  lyrics_transcriber/core/controller.py,sha256=YKz-_nV-h2grXfQrNafDiK35uNitiLVA-z9BmMOkq-w,19540
7
7
  lyrics_transcriber/correction/anchor_sequence.py,sha256=YpKyY24Va5i4JgzP9ssqlOIkaYu060KaldiehbfgTdk,22200
8
8
  lyrics_transcriber/correction/corrector.py,sha256=r6AmdVl_JbOIbNgNk5aDHy-5vdw5-cayspUGht_844A,13494
@@ -81,10 +81,10 @@ lyrics_transcriber/output/ass/lyrics_screen.py,sha256=gRzUsDMLEtZZPuv77xk7M0FzCp
81
81
  lyrics_transcriber/output/ass/section_detector.py,sha256=TsSf4E0fleC-Tzd5KK6q4m-wjGiu6TvGDtHdR6sUqvc,3922
82
82
  lyrics_transcriber/output/ass/section_screen.py,sha256=QeUaIeDXs_Es33W5aqyVSaZzMwUx-b60vbAww3aQfls,4185
83
83
  lyrics_transcriber/output/ass/style.py,sha256=ty3IGorlOZ_Q-TxeA02hNb5Pb0mA755dOb8bqKr1k7U,6880
84
- lyrics_transcriber/output/cdg.py,sha256=gWyFUAsG9xNTENWQdPTZDIWrr8jNHZgH2U4bA3iN9dM,21950
84
+ lyrics_transcriber/output/cdg.py,sha256=Kx8_luGUQbrAlbzGKKNQTPTSGkN48oqOBIH6CYzNlMI,24894
85
85
  lyrics_transcriber/output/cdgmaker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
86
86
  lyrics_transcriber/output/cdgmaker/cdg.py,sha256=nBqkw0JOois-NI27CkwHblLuBaoL-sHyJb2SntX7m8s,6733
87
- lyrics_transcriber/output/cdgmaker/composer.py,sha256=l04-wnUZMo5NUhgd5Ji-QqYwy-j3esqZCT-Qohll7bk,91250
87
+ lyrics_transcriber/output/cdgmaker/composer.py,sha256=gWziaLnCo8iV7Igp7yFn5oFUVctRDtkLlcfx6O-f98Y,90780
88
88
  lyrics_transcriber/output/cdgmaker/config.py,sha256=dOsOaPg9XawR3sWdTBoQiYn7urQWafV2KzedhI6BHYU,4043
89
89
  lyrics_transcriber/output/cdgmaker/images/instrumental.png,sha256=EKUcJJGj95ceNqw7M-O9ltX4HZIaCaSKjJucKVDTSb8,14834
90
90
  lyrics_transcriber/output/cdgmaker/images/intro.png,sha256=XeN6i8aKaQObfRwcgHT8ajQAJIDVjXEd7C5tu7DtriU,17603
@@ -111,11 +111,12 @@ lyrics_transcriber/output/fonts/Zurich_Cn_BT_Bold.ttf,sha256=WNG5LOQ-uGUF_WWT5aQ
111
111
  lyrics_transcriber/output/fonts/arial.ttf,sha256=NcDzVZ2NtWnjbDEJW4pg1EFkPZX1kTneQOI_ragZuDM,275572
112
112
  lyrics_transcriber/output/fonts/georgia.ttf,sha256=fQuyDGMrtZ6BoIhfVzvSFz9x9zIE3pBY_raM4DIicHI,142964
113
113
  lyrics_transcriber/output/fonts/verdana.ttf,sha256=lu0UlJyktzks_yNbnEHVXBJTgqu-DA08K53WaJfK4Ms,139640
114
- lyrics_transcriber/output/generator.py,sha256=TzgYjppsGKev5Fg-q7JtjTAiTkuKOBE8Ssaf6ZoAwQA,8294
114
+ lyrics_transcriber/output/generator.py,sha256=FdXoyxp62jmkvOCaS1ixnRI9ek08ts1QeA8qxss_REY,8309
115
+ lyrics_transcriber/output/lrc_to_cdg.py,sha256=2pi5tvreD_ADAR4RF5yVwj7OJ4Pf5Zo_EJ7rt4iH3k0,2063
115
116
  lyrics_transcriber/output/lyrics_file.py,sha256=_KQyQjCOMIwQdQ0115uEAUIjQWTRmShkSfQuINPKxaw,3741
116
117
  lyrics_transcriber/output/plain_text.py,sha256=3mYKq0BLYz1rGBD6ROjG2dn6BPuzbn5dxIQbWZVi4ao,3689
117
118
  lyrics_transcriber/output/segment_resizer.py,sha256=b553FCdcjYAl9T1IA5K6ya0pcn1-irD5spmxSc26wnI,17143
118
- lyrics_transcriber/output/subtitles.py,sha256=BQy7N_2zdBBWEiHL0NWFz3ZgAerWqQvTLALgxxK3Etk,16920
119
+ lyrics_transcriber/output/subtitles.py,sha256=jrvg0Tn8W-lRmlGNwnrMMzJB6xdEfI0ZoNLer5zWKJk,18170
119
120
  lyrics_transcriber/output/video.py,sha256=ghb53OF6BNZy1VudKQvogPBA27eXfN8FHX9C72aGsm0,9095
120
121
  lyrics_transcriber/review/__init__.py,sha256=_3Eqw-uXZhOZwo6_sHZLhP9vxAVkLF9EBXduUvPdLjQ,57
121
122
  lyrics_transcriber/review/server.py,sha256=5Cfy3aS-h2c6bZU0wkrDZ9ATIkjndNWbxVJjQ89PFJE,5981
@@ -125,8 +126,8 @@ lyrics_transcriber/transcribers/audioshake.py,sha256=QzKGimVa6BovlvYFj35CbGpaGeP
125
126
  lyrics_transcriber/transcribers/base_transcriber.py,sha256=yPzUWPTCGmzE97H5Rz6g61e-qEGL77ZzUoiBOmswhts,5973
126
127
  lyrics_transcriber/transcribers/whisper.py,sha256=P0kas2_oX16MO1-Qy7U5gl5KQN-RuUIJZz7LsEFLUiE,12906
127
128
  lyrics_transcriber/types.py,sha256=xGf3hkTRcGZTTAjMVIev2i2DOU6co0QGpW8NxvaBQAA,16759
128
- lyrics_transcriber-0.39.0.dist-info/LICENSE,sha256=BiPihPDxhxIPEx6yAxVfAljD5Bhm_XG2teCbPEj_m0Y,1069
129
- lyrics_transcriber-0.39.0.dist-info/METADATA,sha256=9YYI3pAEcVVzJkjENCSchgEnp_M5MOUFrSjRhfpwVRk,5891
130
- lyrics_transcriber-0.39.0.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
131
- lyrics_transcriber-0.39.0.dist-info/entry_points.txt,sha256=SoGPp-kikJ9tPNF8vnjcnCyBrNWCW7AbaoZs1dIbXCo,162
132
- lyrics_transcriber-0.39.0.dist-info/RECORD,,
129
+ lyrics_transcriber-0.41.0.dist-info/LICENSE,sha256=BiPihPDxhxIPEx6yAxVfAljD5Bhm_XG2teCbPEj_m0Y,1069
130
+ lyrics_transcriber-0.41.0.dist-info/METADATA,sha256=civcml2WAL40CDoeC8zOI76uVoIGkGXe4oyvwWSEZGc,5891
131
+ lyrics_transcriber-0.41.0.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
132
+ lyrics_transcriber-0.41.0.dist-info/entry_points.txt,sha256=kcp-bSFkCACAEA0t166Kek0HpaJUXRo5SlF5tVrqNBU,216
133
+ lyrics_transcriber-0.41.0.dist-info/RECORD,,
@@ -1,5 +1,6 @@
1
1
  [console_scripts]
2
2
  cdgmaker=lyrics_transcriber.output.cdgmaker.composer:main
3
+ lrc2cdg=lyrics_transcriber.output.lrc_to_cdg:cli_main
3
4
  lyrics-transcriber=lyrics_transcriber.cli.cli_main:main
4
5
  test-cov=tests.conftest:main
5
6