lyrics-transcriber 0.40.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
@@ -126,7 +126,7 @@ class CDGGenerator:
126
126
  cdg_styles: dict,
127
127
  ) -> str:
128
128
  """Create TOML configuration file for CDG generation."""
129
- safe_filename = self._get_safe_filename(artist, title, "Karaoke CDG", "toml")
129
+ safe_filename = self._get_safe_filename(artist, title, "Karaoke", "toml")
130
130
  toml_file = os.path.join(self.output_dir, safe_filename)
131
131
  self.logger.debug(f"Generating TOML file: {toml_file}")
132
132
 
@@ -161,7 +161,7 @@ class CDGGenerator:
161
161
  title=title,
162
162
  artist=artist,
163
163
  audio_file=audio_file,
164
- output_name=f"{artist} - {title} (Karaoke CDG)",
164
+ output_name=f"{artist} - {title} (Karaoke)",
165
165
  sync_times=sync_times,
166
166
  instrumentals=instrumentals,
167
167
  formatted_lyrics=formatted_lyrics,
@@ -190,11 +190,11 @@ class CDGGenerator:
190
190
  """Compose CDG using KaraokeComposer."""
191
191
  kc = KaraokeComposer.from_file(toml_file)
192
192
  kc.compose()
193
- kc.create_mp4(height=1080, fps=30)
193
+ # kc.create_mp4(height=1080, fps=30)
194
194
 
195
195
  def _find_cdg_zip(self, artist: str, title: str) -> str:
196
196
  """Find the generated CDG ZIP file."""
197
- safe_filename = self._get_safe_filename(artist, title, "Karaoke CDG", "zip")
197
+ safe_filename = self._get_safe_filename(artist, title, "Karaoke", "zip")
198
198
  output_zip = os.path.join(self.output_dir, safe_filename)
199
199
 
200
200
  self.logger.info(f"Looking for CDG ZIP file in output directory: {output_zip}")
@@ -216,12 +216,12 @@ class CDGGenerator:
216
216
 
217
217
  def _get_cdg_path(self, artist: str, title: str) -> str:
218
218
  """Get the path to the CDG file."""
219
- safe_filename = self._get_safe_filename(artist, title, "Karaoke CDG", "cdg")
219
+ safe_filename = self._get_safe_filename(artist, title, "Karaoke", "cdg")
220
220
  return os.path.join(self.output_dir, safe_filename)
221
221
 
222
222
  def _get_mp3_path(self, artist: str, title: str) -> str:
223
223
  """Get the path to the MP3 file."""
224
- safe_filename = self._get_safe_filename(artist, title, "Karaoke CDG", "mp3")
224
+ safe_filename = self._get_safe_filename(artist, title, "Karaoke", "mp3")
225
225
  return os.path.join(self.output_dir, safe_filename)
226
226
 
227
227
  def _verify_output_files(self, cdg_file: str, mp3_file: str) -> None:
@@ -376,7 +376,7 @@ class CDGGenerator:
376
376
  cdg_styles: dict,
377
377
  ) -> dict:
378
378
  """Create TOML data structure."""
379
- safe_output_name = self._get_safe_filename(artist, title, "Karaoke CDG")
379
+ safe_output_name = self._get_safe_filename(artist, title, "Karaoke")
380
380
  return {
381
381
  "title": title,
382
382
  "artist": artist,
@@ -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:
@@ -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.40.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,7 +81,7 @@ 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=YKGqljyX4L7j3mg2Y3YfATIPSOszErVtdmyp8SjYnQk,24916
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
87
  lyrics_transcriber/output/cdgmaker/composer.py,sha256=gWziaLnCo8iV7Igp7yFn5oFUVctRDtkLlcfx6O-f98Y,90780
@@ -111,12 +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
115
  lyrics_transcriber/output/lrc_to_cdg.py,sha256=2pi5tvreD_ADAR4RF5yVwj7OJ4Pf5Zo_EJ7rt4iH3k0,2063
116
116
  lyrics_transcriber/output/lyrics_file.py,sha256=_KQyQjCOMIwQdQ0115uEAUIjQWTRmShkSfQuINPKxaw,3741
117
117
  lyrics_transcriber/output/plain_text.py,sha256=3mYKq0BLYz1rGBD6ROjG2dn6BPuzbn5dxIQbWZVi4ao,3689
118
118
  lyrics_transcriber/output/segment_resizer.py,sha256=b553FCdcjYAl9T1IA5K6ya0pcn1-irD5spmxSc26wnI,17143
119
- lyrics_transcriber/output/subtitles.py,sha256=BQy7N_2zdBBWEiHL0NWFz3ZgAerWqQvTLALgxxK3Etk,16920
119
+ lyrics_transcriber/output/subtitles.py,sha256=jrvg0Tn8W-lRmlGNwnrMMzJB6xdEfI0ZoNLer5zWKJk,18170
120
120
  lyrics_transcriber/output/video.py,sha256=ghb53OF6BNZy1VudKQvogPBA27eXfN8FHX9C72aGsm0,9095
121
121
  lyrics_transcriber/review/__init__.py,sha256=_3Eqw-uXZhOZwo6_sHZLhP9vxAVkLF9EBXduUvPdLjQ,57
122
122
  lyrics_transcriber/review/server.py,sha256=5Cfy3aS-h2c6bZU0wkrDZ9ATIkjndNWbxVJjQ89PFJE,5981
@@ -126,8 +126,8 @@ lyrics_transcriber/transcribers/audioshake.py,sha256=QzKGimVa6BovlvYFj35CbGpaGeP
126
126
  lyrics_transcriber/transcribers/base_transcriber.py,sha256=yPzUWPTCGmzE97H5Rz6g61e-qEGL77ZzUoiBOmswhts,5973
127
127
  lyrics_transcriber/transcribers/whisper.py,sha256=P0kas2_oX16MO1-Qy7U5gl5KQN-RuUIJZz7LsEFLUiE,12906
128
128
  lyrics_transcriber/types.py,sha256=xGf3hkTRcGZTTAjMVIev2i2DOU6co0QGpW8NxvaBQAA,16759
129
- lyrics_transcriber-0.40.0.dist-info/LICENSE,sha256=BiPihPDxhxIPEx6yAxVfAljD5Bhm_XG2teCbPEj_m0Y,1069
130
- lyrics_transcriber-0.40.0.dist-info/METADATA,sha256=hj-_pxAxvpcNC7XBPLbEvpPQYEDfHazvIOHrlXsWUOk,5891
131
- lyrics_transcriber-0.40.0.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
132
- lyrics_transcriber-0.40.0.dist-info/entry_points.txt,sha256=kcp-bSFkCACAEA0t166Kek0HpaJUXRo5SlF5tVrqNBU,216
133
- lyrics_transcriber-0.40.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,,