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.
- lyrics_transcriber/cli/cli_main.py +7 -0
- lyrics_transcriber/core/config.py +1 -0
- lyrics_transcriber/output/cdg.py +7 -7
- lyrics_transcriber/output/generator.py +7 -6
- lyrics_transcriber/output/subtitles.py +29 -1
- {lyrics_transcriber-0.40.0.dist-info → lyrics_transcriber-0.41.0.dist-info}/METADATA +1 -1
- {lyrics_transcriber-0.40.0.dist-info → lyrics_transcriber-0.41.0.dist-info}/RECORD +10 -10
- {lyrics_transcriber-0.40.0.dist-info → lyrics_transcriber-0.41.0.dist-info}/LICENSE +0 -0
- {lyrics_transcriber-0.40.0.dist-info → lyrics_transcriber-0.41.0.dist-info}/WHEEL +0 -0
- {lyrics_transcriber-0.40.0.dist-info → lyrics_transcriber-0.41.0.dist-info}/entry_points.txt +0 -0
@@ -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,
|
lyrics_transcriber/output/cdg.py
CHANGED
@@ -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
|
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
|
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
|
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
|
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
|
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
|
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.
|
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=
|
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=
|
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=
|
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=
|
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=
|
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.
|
130
|
-
lyrics_transcriber-0.
|
131
|
-
lyrics_transcriber-0.
|
132
|
-
lyrics_transcriber-0.
|
133
|
-
lyrics_transcriber-0.
|
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,,
|
File without changes
|
File without changes
|
{lyrics_transcriber-0.40.0.dist-info → lyrics_transcriber-0.41.0.dist-info}/entry_points.txt
RENAMED
File without changes
|