karaoke-gen 0.75.16__py3-none-any.whl → 0.75.53__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.
@@ -1,6 +1,6 @@
1
1
  karaoke_gen/__init__.py,sha256=wHpDbURJxmJAMNZ0uQjISv5MIT7KD9RWYi15xlYgEhU,1351
2
- karaoke_gen/audio_fetcher.py,sha256=9ByJRTExJRjZ6tx2n3bUQf4N35WIZv8woH4VJHIGWtk,33599
3
- karaoke_gen/audio_processor.py,sha256=0Ud2JVQ4cBSQwlKfJXIr2KkZr-8xF7gB4eVqP_elxQ8,39711
2
+ karaoke_gen/audio_fetcher.py,sha256=A1c7HxUbV4inhGuj9SySr3zkOSGKfAFovXu2TKzodSA,63811
3
+ karaoke_gen/audio_processor.py,sha256=YCMsxh-OSzC6h1Oqht8gh48j4KaRDuRIMUUsNNQxJeY,39913
4
4
  karaoke_gen/config.py,sha256=LBZKpvwSgta8YoVX2GFFW-4CP22AyRtqsBn-KCeh8eg,2499
5
5
  karaoke_gen/file_handler.py,sha256=jnPc4kFtG-PX-IVPHYWa7maXd4lNmbkKd1HogDNzgN8,16674
6
6
  karaoke_gen/instrumental_review/__init__.py,sha256=91K9wPWfQnOqbINuhxtErPnXyY0gijdiF-69n-p3334,1382
@@ -8,12 +8,12 @@ karaoke_gen/instrumental_review/analyzer.py,sha256=Heg8TbrwM4g5IV7bavmO6EfVD4M0U
8
8
  karaoke_gen/instrumental_review/editor.py,sha256=_DGTjKMk5WhoGtLGtTvHzU522LJyQQ_DSY1r8fULuiA,11568
9
9
  karaoke_gen/instrumental_review/models.py,sha256=cUSb_JheJK0cGdKx9f59-9sRvRrhrgdTdKBzQN3lHto,5226
10
10
  karaoke_gen/instrumental_review/server.py,sha256=Ick90X77t2EeMRwtx2U08sSybadQyWH7G0tDG-4JqP4,19377
11
- karaoke_gen/instrumental_review/static/index.html,sha256=ZLxIohX0skbvvNa-pkhIPksHRDMyM6EPI_MFFATC_Bs,58223
11
+ karaoke_gen/instrumental_review/static/index.html,sha256=EjMFxCQJOUSrsgwIXAW3R4bN6hYxDLmL4MHzoXxI4f0,59362
12
12
  karaoke_gen/instrumental_review/waveform.py,sha256=Q6LBPZrJAD6mzZ7TmRf3Tf4gwYhUYTHumJKytLs3hSg,12940
13
13
  karaoke_gen/karaoke_finalise/__init__.py,sha256=HqZ7TIhgt_tYZ-nb_NNCaejWAcF_aK-7wJY5TaW_keM,46
14
- karaoke_gen/karaoke_finalise/karaoke_finalise.py,sha256=jZtnvP_SMdE9e5TpotTu9WPTPEfnZVRTyO9heylzMXs,92080
15
- karaoke_gen/karaoke_gen.py,sha256=GB-TKoM99S4k_JIDbsiqOqOhZC5QJv1BqTgzQlF0oNw,56513
16
- karaoke_gen/lyrics_processor.py,sha256=9SIYGgQLF_KQi_tKxQmO80p_S63-L7FIlu7L46Q5nQQ,18726
14
+ karaoke_gen/karaoke_finalise/karaoke_finalise.py,sha256=Wn1KcdRyINT63UxKUPT9uB-bsrFVih0Im_cjXtequS0,93534
15
+ karaoke_gen/karaoke_gen.py,sha256=tfoywlSEI9qOi1krHTBafumXl3bcWxn6w0AN2vQ1nHs,57048
16
+ karaoke_gen/lyrics_processor.py,sha256=JrbTLmMR_jvcpuxsPihLXlCBKkFNKqYHvraw3_3KCPI,23263
17
17
  karaoke_gen/metadata.py,sha256=SZW6TuUpkGGU98gRdjPfrR8F4vWXjnfCSGry2XD5_A4,6689
18
18
  karaoke_gen/pipeline/__init__.py,sha256=-MZnba4qobr1qGDamG9CieLl2pWCZMEB5_Yur62RKeM,2106
19
19
  karaoke_gen/pipeline/base.py,sha256=yg4LIm7Mc9ER0zCmZcUv4huEkotSSXK_0OAFio-TSNI,6235
@@ -35,9 +35,9 @@ karaoke_gen/resources/Zurich_Cn_BT_Bold.ttf,sha256=WNG5LOQ-uGUF_WWT5aQHzVbyWvQqG
35
35
  karaoke_gen/style_loader.py,sha256=13010BVxwVgamRn8K8Z9fNgLBdPs9LFmWMRMLyBdits,17908
36
36
  karaoke_gen/utils/__init__.py,sha256=FpOHyeBRB06f3zMoLBUJHTDZACrabg-DoyBTxNKYyNY,722
37
37
  karaoke_gen/utils/bulk_cli.py,sha256=s2SXTnnQf7TM8Fk8yz9Cfd3Xl08uHBnve_rTkf4wE5g,19337
38
- karaoke_gen/utils/cli_args.py,sha256=TusVLY74vtOmBVjkltmASTGkHybgVlB2_8J1zHdslC8,18049
39
- karaoke_gen/utils/gen_cli.py,sha256=9_4oFgaRXjISCTov0X319a97NqF4IFhQn_r_ThBKv3o,42509
40
- karaoke_gen/utils/remote_cli.py,sha256=QH27bIFIrOMaySeBpk6cF9IGQqdhKnN2BllJk8WoeUg,132290
38
+ karaoke_gen/utils/cli_args.py,sha256=Td8fE1FisY1fcQuOoKlAQ6B3NF4bxGvSPDaQlpfY3T0,18172
39
+ karaoke_gen/utils/gen_cli.py,sha256=ahoVp_n298O8yUmFOBDsm4mLXgTuEb80mBnz4_mo4SM,44137
40
+ karaoke_gen/utils/remote_cli.py,sha256=l1iHUz_hrsbAfTvGDhA4gzr2g1CFBFc9np0nQ1jk1tQ,144629
41
41
  karaoke_gen/video_background_processor.py,sha256=p3sryMxmkori4Uy2MYgmlk5_QQ7Uh9IoVJLAdkdLIUI,15124
42
42
  karaoke_gen/video_generator.py,sha256=B7BQBrjkyvk3L3sctnPXnvr1rzkw0NYx5UCAl0ZiVx0,18464
43
43
  lyrics_transcriber/__init__.py,sha256=g9ZbJg9U1qo7XzrC25J3bTKcNzzwUJWDVdi_7-hjcM4,412
@@ -45,7 +45,7 @@ lyrics_transcriber/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
45
45
  lyrics_transcriber/cli/cli_main.py,sha256=F72ENLTj934bXjHAUbRm0toCK73qnuJhwEm9agBVKHQ,11596
46
46
  lyrics_transcriber/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
47
  lyrics_transcriber/core/config.py,sha256=AM6RZKll8tzdZtMLgvHRQb1SxiXVPek0q4vmSWVUvEo,1368
48
- lyrics_transcriber/core/controller.py,sha256=jPoJ-r1jZ94Xp8ARN9lpSjsMAqPXuGHhwnQy_FmkBvI,25216
48
+ lyrics_transcriber/core/controller.py,sha256=laeUakqT-0CoIyoBWYvv7kWxX-_wefWRwg2xrz84gRg,29432
49
49
  lyrics_transcriber/correction/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
50
  lyrics_transcriber/correction/agentic/__init__.py,sha256=p7PHiebuvRs8RDlPDs-9gLZKzXG5KfWg3fFCdDhY6pE,222
51
51
  lyrics_transcriber/correction/agentic/adapter.py,sha256=Z0JBTAA7xlSdctCHqO9nBMl78C4XmqsLKKtS6BvNZNI,2912
@@ -125,7 +125,7 @@ lyrics_transcriber/frontend/REPLACE_ALL_FUNCTIONALITY.md,sha256=iRZbicW5satHel9g
125
125
  lyrics_transcriber/frontend/__init__.py,sha256=nW8acRSWTjXoRwGqcTU4w-__X7tMAE0iXL0uihBN3CU,836
126
126
  lyrics_transcriber/frontend/eslint.config.js,sha256=3ADH23ANA4NNBKFy6nCVk65e8bx1DrVd_FIaYNnhuqA,734
127
127
  lyrics_transcriber/frontend/index.html,sha256=u1m7042a1kLWZVIElYQ9y-lzfIAdYJGtQE-i4Zjc3xY,806
128
- lyrics_transcriber/frontend/package.json,sha256=AlMkVDBqguyQxZNrNdpqkMCbnz1dl5Uu2YTIjrnAYkg,1182
128
+ lyrics_transcriber/frontend/package.json,sha256=5krvXBc9deVYXREN9Pxw5N6ohiAsSCnPXnXlcb6p-U0,1182
129
129
  lyrics_transcriber/frontend/public/android-chrome-192x192.png,sha256=lg-6aPF5mGLiuG7LyftZk_0RI41srmpA8wj-NkaaQms,17632
130
130
  lyrics_transcriber/frontend/public/android-chrome-512x512.png,sha256=x-zuKT3NYsTqAWzhKRTZeD4-0uYoUjqMPZpKTChqNJ8,123447
131
131
  lyrics_transcriber/frontend/public/apple-touch-icon.png,sha256=6y5vGra54w5oc8VP6sn2JjoQtN9hWTKn0YPhmdlmfU0,16188
@@ -133,8 +133,8 @@ lyrics_transcriber/frontend/public/favicon-16x16.png,sha256=2wy_7ZmVS4d7umByJVHQ
133
133
  lyrics_transcriber/frontend/public/favicon-32x32.png,sha256=6TyrRMIw6G8dpG4_QWVTY8DMxo0bIGzc_9aOJrkiJKM,1297
134
134
  lyrics_transcriber/frontend/public/favicon.ico,sha256=ZK7QvdBuZp0QxPkluCW4IKxfleFmFLp1KkG_d5xmx7E,15406
135
135
  lyrics_transcriber/frontend/public/nomad-karaoke-logo.png,sha256=jTTBFXV6hGJGolZYQ-dIjgQQbMsehk5XGtsllhLrdzg,212641
136
- lyrics_transcriber/frontend/src/App.tsx,sha256=f1-dp-MU8vap18eAXacwVDO5P4eE2iG9zSvjau-7NJs,6533
137
- lyrics_transcriber/frontend/src/api.ts,sha256=qYpH_KRkco1pYU4oJ8XgV4Y6xrTOPGngxwxB0NwlKro,8163
136
+ lyrics_transcriber/frontend/src/App.tsx,sha256=XI93-pX2p76ID45XUQTitDclUZHZ1kxhZZB2VriMV8k,6747
137
+ lyrics_transcriber/frontend/src/api.ts,sha256=GcjbOrlU7EdUpZ7MUPFqE1rtH-ckdw8wHtgyQxWateY,8648
138
138
  lyrics_transcriber/frontend/src/components/AIFeedbackModal.tsx,sha256=N-M8LNKE3nCjrSkWTf8ObWSdBqD0Cv3Xiz_wVv9OtFo,3133
139
139
  lyrics_transcriber/frontend/src/components/AddLyricsModal.tsx,sha256=ubJwQewryjUrXwpBkITQNu4POhoUtDbNA93cqa-yJKY,3416
140
140
  lyrics_transcriber/frontend/src/components/AgenticCorrectionMetrics.tsx,sha256=Yg6FG0LtrneRfAYeBu3crt_RdN-_o7FojtYhDMDKi0o,8595
@@ -202,12 +202,12 @@ lyrics_transcriber/frontend/vite.config.ts,sha256=8FdW0dN8zDFqfhQSxX5h7sIu72X2pi
202
202
  lyrics_transcriber/frontend/web_assets/android-chrome-192x192.png,sha256=lg-6aPF5mGLiuG7LyftZk_0RI41srmpA8wj-NkaaQms,17632
203
203
  lyrics_transcriber/frontend/web_assets/android-chrome-512x512.png,sha256=x-zuKT3NYsTqAWzhKRTZeD4-0uYoUjqMPZpKTChqNJ8,123447
204
204
  lyrics_transcriber/frontend/web_assets/apple-touch-icon.png,sha256=6y5vGra54w5oc8VP6sn2JjoQtN9hWTKn0YPhmdlmfU0,16188
205
- lyrics_transcriber/frontend/web_assets/assets/index-COYImAcx.js,sha256=FskPrshJHw2Q2TPCSaeY1KYWTjG-pR7pVTSUzTOEx7g,1421207
206
- lyrics_transcriber/frontend/web_assets/assets/index-COYImAcx.js.map,sha256=iiWHTy0GY5J7H-gMKprSTZ2LJvqEp61bv7CmIDc8fKo,3003447
205
+ lyrics_transcriber/frontend/web_assets/assets/index-BECn1o8Q.js,sha256=yNW-EWYuyaj_16fGDyvCGkb2BGoCufHlpKquqOxzQac,1421945
206
+ lyrics_transcriber/frontend/web_assets/assets/index-BECn1o8Q.js.map,sha256=aY17CfmBvSG6FgnuQLzgc9V02xfaOimIIAo8l4fiAJk,3004853
207
207
  lyrics_transcriber/frontend/web_assets/favicon-16x16.png,sha256=2wy_7ZmVS4d7umByJVHQ37DBB_8xrU9xfT1_konSXQI,604
208
208
  lyrics_transcriber/frontend/web_assets/favicon-32x32.png,sha256=6TyrRMIw6G8dpG4_QWVTY8DMxo0bIGzc_9aOJrkiJKM,1297
209
209
  lyrics_transcriber/frontend/web_assets/favicon.ico,sha256=ZK7QvdBuZp0QxPkluCW4IKxfleFmFLp1KkG_d5xmx7E,15406
210
- lyrics_transcriber/frontend/web_assets/index.html,sha256=KGxgVv7MBXJ1fZwq5PVmLrW887hRLmtmywj0xcsq-t0,830
210
+ lyrics_transcriber/frontend/web_assets/index.html,sha256=qqHYlM3VAy_3YJn3QgETGjR94XTWL3Ci_S-5_lHYgxw,830
211
211
  lyrics_transcriber/frontend/web_assets/nomad-karaoke-logo.png,sha256=jTTBFXV6hGJGolZYQ-dIjgQQbMsehk5XGtsllhLrdzg,212641
212
212
  lyrics_transcriber/frontend/yarn.lock,sha256=wtImLsCO1P1Lpkhc1jAN6IiHQ0As4xn39n0cwKoh4LM,131996
213
213
  lyrics_transcriber/lyrics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -253,7 +253,7 @@ lyrics_transcriber/output/cdgmaker/transitions/wipeleft.png,sha256=7jY6Z-mdY94PE
253
253
  lyrics_transcriber/output/cdgmaker/transitions/wipeout.png,sha256=YSLdv2RCGeSilOGt7PYTLAOlBfL4bofu8Vpj-PcskZo,9995
254
254
  lyrics_transcriber/output/cdgmaker/transitions/wiperight.png,sha256=Dw3N6cqJaSwL3owDqyAM4W573EmdNnkjdiXhoxWC4yk,8443
255
255
  lyrics_transcriber/output/cdgmaker/utils.py,sha256=TNcOnccegpxT3OogWQCexy8BrDqfihLAShMvqeM81cw,2945
256
- lyrics_transcriber/output/countdown_processor.py,sha256=i2j0gSxAfgGPxdKnr3d_Vkzt-CXW-BALJDmOQ13dfBY,9457
256
+ lyrics_transcriber/output/countdown_processor.py,sha256=EsUmnamNiG3SRfqPsZjSH3BH81a9AUg7wIdtfDkW57s,10848
257
257
  lyrics_transcriber/output/fonts/AvenirNext-Bold.ttf,sha256=YxgKz2OP46lwLPCpIZhVa8COi_9KRDSXw4n8dIHHQSs,327048
258
258
  "lyrics_transcriber/output/fonts/DMSans-VariableFont_opsz,wght.ttf",sha256=7uav75vmxRukpMx8wqtPeNvaxqOzlBljO400geBzYYI,238984
259
259
  lyrics_transcriber/output/fonts/DMSerifDisplay-Regular.ttf,sha256=mMHMLr3CMjEQgJ5cKlYEn8YSsHwSnDtxT-Qjn_n8ffM,72220
@@ -274,14 +274,14 @@ lyrics_transcriber/review/server.py,sha256=_05ul0MsddKf8iTIg-NASVYkl9kBRo0M3WhWB
274
274
  lyrics_transcriber/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
275
275
  lyrics_transcriber/storage/dropbox.py,sha256=Dyam1ULTkoxD1X5trkZ5dGp5XhBGCn998moC8IS9-68,9804
276
276
  lyrics_transcriber/transcribers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
277
- lyrics_transcriber/transcribers/audioshake.py,sha256=4VQdm-r3Pe0nIlJzqgOyOSp7amnWnU05VhFxBidY_WU,11831
277
+ lyrics_transcriber/transcribers/audioshake.py,sha256=RihuLKzhhHfX7m5cjKISwIuTQkGWapCS29D6Qk3hR4U,15869
278
278
  lyrics_transcriber/transcribers/base_transcriber.py,sha256=T3m4ZCwZ9Bpv6Jvb2hNcnllk-lmeNmADDJlSySBtP1Q,6480
279
279
  lyrics_transcriber/transcribers/whisper.py,sha256=YcCB1ic9H6zL1GS0jD0emu8-qlcH0QVEjjjYB4aLlIQ,13260
280
- lyrics_transcriber/types.py,sha256=Y7WUx8PAOBYWCIZgw4ndeHfPH8Gg--O3OYYQgMpJ2iI,27728
280
+ lyrics_transcriber/types.py,sha256=UJjaxhVd2o14AG4G8ToU598p0JeYdiTFjpG38jGCoYQ,27917
281
281
  lyrics_transcriber/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
282
282
  lyrics_transcriber/utils/word_utils.py,sha256=-cMGpj9UV4F6IsoDKAV2i1aiqSO8eI91HMAm_igtVMk,958
283
- karaoke_gen-0.75.16.dist-info/METADATA,sha256=vDrn5nCYEnReERLgY8XaVRTimrTZm15rq9YPN8PzP1k,17279
284
- karaoke_gen-0.75.16.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
285
- karaoke_gen-0.75.16.dist-info/entry_points.txt,sha256=xIyLe7K84ZyjO8L0_AmNectz93QjGSs5AkApMtlAd4g,160
286
- karaoke_gen-0.75.16.dist-info/licenses/LICENSE,sha256=81R_4XwMZDODHD7JcZeUR8IiCU8AD7Ajl6bmwR9tYDk,1074
287
- karaoke_gen-0.75.16.dist-info/RECORD,,
283
+ karaoke_gen-0.75.53.dist-info/METADATA,sha256=DR2eCSIs1DM9Xudkimpy66AgxZRfYSYyzpBX-sxkhgE,20662
284
+ karaoke_gen-0.75.53.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
285
+ karaoke_gen-0.75.53.dist-info/entry_points.txt,sha256=xIyLe7K84ZyjO8L0_AmNectz93QjGSs5AkApMtlAd4g,160
286
+ karaoke_gen-0.75.53.dist-info/licenses/LICENSE,sha256=81R_4XwMZDODHD7JcZeUR8IiCU8AD7Ajl6bmwR9tYDk,1074
287
+ karaoke_gen-0.75.53.dist-info/RECORD,,
@@ -307,6 +307,37 @@ class LyricsTranscriber:
307
307
  self.results.transcription_corrected = CorrectionResult.from_dict(corrections_data)
308
308
  self.logger.info("Successfully loaded existing corrections data")
309
309
 
310
+ # Check if the loaded corrections have countdown padding applied
311
+ # This is important because the video needs to use padded audio to sync
312
+ # with the countdown-adjusted timestamps in the ASS subtitles
313
+ if self.output_config.add_countdown:
314
+ from lyrics_transcriber.output.countdown_processor import CountdownProcessor
315
+
316
+ countdown_processor = CountdownProcessor(
317
+ cache_dir=self.output_config.cache_dir,
318
+ logger=self.logger,
319
+ )
320
+
321
+ if countdown_processor.has_countdown(self.results.transcription_corrected):
322
+ self.logger.info(
323
+ "Loaded corrections have countdown - creating padded audio for video sync"
324
+ )
325
+ # Create padded audio file to match the countdown-adjusted timestamps
326
+ padded_audio_path = countdown_processor.create_padded_audio_only(self.audio_filepath)
327
+ self.audio_filepath = padded_audio_path
328
+
329
+ # Set countdown padding attributes on results
330
+ self.results.countdown_padding_added = True
331
+ self.results.countdown_padding_seconds = countdown_processor.COUNTDOWN_PADDING_SECONDS
332
+ self.results.padded_audio_filepath = padded_audio_path
333
+
334
+ self.logger.info(
335
+ f"Countdown padding applied: {countdown_processor.COUNTDOWN_PADDING_SECONDS}s. "
336
+ f"Using padded audio: {padded_audio_path}"
337
+ )
338
+ else:
339
+ self.logger.info("Loaded corrections do not have countdown - no padding needed")
340
+
310
341
  # Skip to output generation
311
342
  self.generate_outputs()
312
343
  self.logger.info("Processing completed successfully using existing corrections")
@@ -363,7 +394,24 @@ class LyricsTranscriber:
363
394
 
364
395
  def transcribe(self) -> None:
365
396
  """Run transcription using all available transcribers."""
366
- self.logger.info(f"Starting transcription with providers: {list(self.transcribers.keys())}")
397
+ provider_names = list(self.transcribers.keys())
398
+
399
+ if not provider_names:
400
+ self.logger.warning(
401
+ "Starting transcription with providers: [] - NO TRANSCRIPTION PROVIDERS CONFIGURED!\n"
402
+ "\n"
403
+ "This means no word-level timing data will be generated, and synchronized karaoke "
404
+ "lyrics cannot be created. The output will lack the '(With Vocals).mkv' video file.\n"
405
+ "\n"
406
+ "To enable transcription, configure at least one provider:\n"
407
+ " - AudioShake: Set AUDIOSHAKE_API_TOKEN environment variable\n"
408
+ " - Whisper/RunPod: Set RUNPOD_API_KEY and WHISPER_RUNPOD_ID environment variables\n"
409
+ "\n"
410
+ "See README.md 'Transcription Providers' section for detailed setup instructions."
411
+ )
412
+ else:
413
+ self.logger.info(f"Starting transcription with providers: {provider_names}")
414
+ self._log_provider_configuration_status()
367
415
 
368
416
  for name, transcriber_info in self.transcribers.items():
369
417
  self.logger.info(f"Running transcription with {name}")
@@ -376,7 +424,33 @@ class LyricsTranscriber:
376
424
  self.logger.debug(f"Transcription completed for {name}")
377
425
 
378
426
  if not self.results.transcription_results:
379
- self.logger.warning("No successful transcriptions from any provider")
427
+ self.logger.warning(
428
+ "No successful transcriptions from any provider. "
429
+ "Check that your API tokens are valid and the services are accessible."
430
+ )
431
+
432
+ def _log_provider_configuration_status(self) -> None:
433
+ """Log detailed configuration status for each potential transcription provider."""
434
+ self.logger.debug("Transcription provider configuration status:")
435
+
436
+ # AudioShake status
437
+ if self.transcriber_config.audioshake_api_token:
438
+ self.logger.debug(" - AudioShake: CONFIGURED (API token provided)")
439
+ else:
440
+ self.logger.debug(" - AudioShake: NOT CONFIGURED (missing AUDIOSHAKE_API_TOKEN)")
441
+
442
+ # Whisper/RunPod status
443
+ has_runpod_key = bool(self.transcriber_config.runpod_api_key)
444
+ has_whisper_id = bool(self.transcriber_config.whisper_runpod_id)
445
+
446
+ if has_runpod_key and has_whisper_id:
447
+ self.logger.debug(" - Whisper (RunPod): CONFIGURED (API key and endpoint ID provided)")
448
+ elif has_runpod_key:
449
+ self.logger.debug(" - Whisper (RunPod): PARTIALLY CONFIGURED (missing WHISPER_RUNPOD_ID)")
450
+ elif has_whisper_id:
451
+ self.logger.debug(" - Whisper (RunPod): PARTIALLY CONFIGURED (missing RUNPOD_API_KEY)")
452
+ else:
453
+ self.logger.debug(" - Whisper (RunPod): NOT CONFIGURED (missing RUNPOD_API_KEY and WHISPER_RUNPOD_ID)")
380
454
 
381
455
  def correct_lyrics(self) -> None:
382
456
  """Run lyrics correction using transcription and internet lyrics."""
@@ -2,7 +2,7 @@
2
2
  "name": "lyrics-transcriber-frontend",
3
3
  "private": true,
4
4
  "homepage": "https://nomadkaraoke.github.io/lyrics-transcriber-frontend",
5
- "version": "0.82.0",
5
+ "version": "0.83.0",
6
6
  "type": "module",
7
7
  "scripts": {
8
8
  "dev": "vite",
@@ -19,25 +19,27 @@ export default function App() {
19
19
  const params = new URLSearchParams(window.location.search)
20
20
  const encodedApiUrl = params.get('baseApiUrl')
21
21
  const audioHashParam = params.get('audioHash')
22
+ const reviewTokenParam = params.get('reviewToken')
22
23
 
23
24
  if (encodedApiUrl) {
24
25
  const baseApiUrl = decodeURIComponent(encodedApiUrl)
25
- setApiClient(new LiveApiClient(baseApiUrl))
26
+ // Pass reviewToken to LiveApiClient for authentication
27
+ setApiClient(new LiveApiClient(baseApiUrl, reviewTokenParam || undefined))
26
28
  setIsReadOnly(false)
27
29
  if (audioHashParam) {
28
30
  setAudioHash(audioHashParam)
29
31
  }
30
32
  // Fetch initial data
31
- fetchData(baseApiUrl)
33
+ fetchData(baseApiUrl, reviewTokenParam || undefined)
32
34
  } else {
33
35
  setApiClient(new FileOnlyClient())
34
36
  setIsReadOnly(true)
35
37
  }
36
38
  }, [])
37
39
 
38
- const fetchData = async (baseUrl: string) => {
40
+ const fetchData = async (baseUrl: string, reviewToken?: string) => {
39
41
  try {
40
- const client = new LiveApiClient(baseUrl)
42
+ const client = new LiveApiClient(baseUrl, reviewToken)
41
43
  const data = await client.getCorrectionData()
42
44
  // console.log('Full correction data from API:', data)
43
45
  setData(data)
@@ -35,14 +35,29 @@ interface AddLyricsRequest {
35
35
  }
36
36
 
37
37
  export class LiveApiClient implements ApiClient {
38
- constructor(private baseUrl: string) {
38
+ private reviewToken?: string;
39
+
40
+ constructor(private baseUrl: string, reviewToken?: string) {
39
41
  this.baseUrl = baseUrl.replace(/\/$/, '')
42
+ this.reviewToken = reviewToken
40
43
  }
41
44
 
42
45
  public isUpdatingHandlers = false;
43
46
 
47
+ /**
48
+ * Build URL with reviewToken query parameter if available
49
+ */
50
+ private buildUrl(path: string): string {
51
+ const url = `${this.baseUrl}${path}`
52
+ if (this.reviewToken) {
53
+ const separator = url.includes('?') ? '&' : '?'
54
+ return `${url}${separator}review_token=${encodeURIComponent(this.reviewToken)}`
55
+ }
56
+ return url
57
+ }
58
+
44
59
  async getCorrectionData(): Promise<CorrectionData> {
45
- const response = await fetch(`${this.baseUrl}/correction-data`);
60
+ const response = await fetch(this.buildUrl('/correction-data'));
46
61
  if (!response.ok) {
47
62
  throw new Error(`API error: ${response.statusText}`);
48
63
  }
@@ -64,7 +79,7 @@ export class LiveApiClient implements ApiClient {
64
79
  corrected_segments: data.corrected_segments
65
80
  };
66
81
 
67
- const response = await fetch(`${this.baseUrl}/complete`, {
82
+ const response = await fetch(this.buildUrl('/complete'), {
68
83
  method: 'POST',
69
84
  headers: {
70
85
  'Content-Type': 'application/json',
@@ -78,7 +93,7 @@ export class LiveApiClient implements ApiClient {
78
93
  }
79
94
 
80
95
  getAudioUrl(audioHash: string): string {
81
- return `${this.baseUrl}/audio/${audioHash}`
96
+ return this.buildUrl(`/audio/${audioHash}`)
82
97
  }
83
98
 
84
99
  async generatePreviewVideo(data: CorrectionData): Promise<PreviewVideoResponse> {
@@ -88,7 +103,7 @@ export class LiveApiClient implements ApiClient {
88
103
  corrected_segments: data.corrected_segments
89
104
  };
90
105
 
91
- const response = await fetch(`${this.baseUrl}/preview-video`, {
106
+ const response = await fetch(this.buildUrl('/preview-video'), {
92
107
  method: 'POST',
93
108
  headers: {
94
109
  'Content-Type': 'application/json',
@@ -107,7 +122,7 @@ export class LiveApiClient implements ApiClient {
107
122
  }
108
123
 
109
124
  getPreviewVideoUrl(previewHash: string): string {
110
- return `${this.baseUrl}/preview-video/${previewHash}`;
125
+ return this.buildUrl(`/preview-video/${previewHash}`);
111
126
  }
112
127
 
113
128
  async updateHandlers(enabledHandlers: string[]): Promise<CorrectionData> {
@@ -116,7 +131,7 @@ export class LiveApiClient implements ApiClient {
116
131
  console.log('API: Set isUpdatingHandlers to', this.isUpdatingHandlers);
117
132
 
118
133
  try {
119
- const response = await fetch(`${this.baseUrl}/handlers`, {
134
+ const response = await fetch(this.buildUrl('/handlers'), {
120
135
  method: 'POST',
121
136
  headers: {
122
137
  'Content-Type': 'application/json',
@@ -147,7 +162,7 @@ export class LiveApiClient implements ApiClient {
147
162
  lyrics
148
163
  };
149
164
 
150
- const response = await fetch(`${this.baseUrl}/add-lyrics`, {
165
+ const response = await fetch(this.buildUrl('/add-lyrics'), {
151
166
  method: 'POST',
152
167
  headers: {
153
168
  'Content-Type': 'application/json',
@@ -170,7 +185,7 @@ export class LiveApiClient implements ApiClient {
170
185
  async submitAnnotations(annotations: Omit<CorrectionAnnotation, 'annotation_id' | 'timestamp'>[]): Promise<void> {
171
186
  // Submit each annotation to the backend
172
187
  for (const annotation of annotations) {
173
- const response = await fetch(`${this.baseUrl}/v1/annotations`, {
188
+ const response = await fetch(this.buildUrl('/v1/annotations'), {
174
189
  method: 'POST',
175
190
  headers: {
176
191
  'Content-Type': 'application/json',
@@ -186,7 +201,7 @@ export class LiveApiClient implements ApiClient {
186
201
  }
187
202
 
188
203
  async getAnnotationStats(): Promise<any> {
189
- const response = await fetch(`${this.baseUrl}/v1/annotations/stats`);
204
+ const response = await fetch(this.buildUrl('/v1/annotations/stats'));
190
205
  if (!response.ok) {
191
206
  throw new Error(`API error: ${response.statusText}`);
192
207
  }
@@ -34056,13 +34056,26 @@ function validateCorrectionData(data) {
34056
34056
  return CorrectionDataSchema.parse(data);
34057
34057
  }
34058
34058
  class LiveApiClient {
34059
- constructor(baseUrl) {
34059
+ constructor(baseUrl, reviewToken) {
34060
+ __publicField(this, "reviewToken");
34060
34061
  __publicField(this, "isUpdatingHandlers", false);
34061
34062
  this.baseUrl = baseUrl;
34062
34063
  this.baseUrl = baseUrl.replace(/\/$/, "");
34064
+ this.reviewToken = reviewToken;
34065
+ }
34066
+ /**
34067
+ * Build URL with reviewToken query parameter if available
34068
+ */
34069
+ buildUrl(path) {
34070
+ const url = `${this.baseUrl}${path}`;
34071
+ if (this.reviewToken) {
34072
+ const separator = url.includes("?") ? "&" : "?";
34073
+ return `${url}${separator}review_token=${encodeURIComponent(this.reviewToken)}`;
34074
+ }
34075
+ return url;
34063
34076
  }
34064
34077
  async getCorrectionData() {
34065
- const response = await fetch(`${this.baseUrl}/correction-data`);
34078
+ const response = await fetch(this.buildUrl("/correction-data"));
34066
34079
  if (!response.ok) {
34067
34080
  throw new Error(`API error: ${response.statusText}`);
34068
34081
  }
@@ -34079,7 +34092,7 @@ class LiveApiClient {
34079
34092
  corrections: data.corrections,
34080
34093
  corrected_segments: data.corrected_segments
34081
34094
  };
34082
- const response = await fetch(`${this.baseUrl}/complete`, {
34095
+ const response = await fetch(this.buildUrl("/complete"), {
34083
34096
  method: "POST",
34084
34097
  headers: {
34085
34098
  "Content-Type": "application/json"
@@ -34091,14 +34104,14 @@ class LiveApiClient {
34091
34104
  }
34092
34105
  }
34093
34106
  getAudioUrl(audioHash) {
34094
- return `${this.baseUrl}/audio/${audioHash}`;
34107
+ return this.buildUrl(`/audio/${audioHash}`);
34095
34108
  }
34096
34109
  async generatePreviewVideo(data) {
34097
34110
  const updatePayload = {
34098
34111
  corrections: data.corrections,
34099
34112
  corrected_segments: data.corrected_segments
34100
34113
  };
34101
- const response = await fetch(`${this.baseUrl}/preview-video`, {
34114
+ const response = await fetch(this.buildUrl("/preview-video"), {
34102
34115
  method: "POST",
34103
34116
  headers: {
34104
34117
  "Content-Type": "application/json"
@@ -34114,14 +34127,14 @@ class LiveApiClient {
34114
34127
  return await response.json();
34115
34128
  }
34116
34129
  getPreviewVideoUrl(previewHash) {
34117
- return `${this.baseUrl}/preview-video/${previewHash}`;
34130
+ return this.buildUrl(`/preview-video/${previewHash}`);
34118
34131
  }
34119
34132
  async updateHandlers(enabledHandlers) {
34120
34133
  console.log("API: Starting handler update...");
34121
34134
  this.isUpdatingHandlers = true;
34122
34135
  console.log("API: Set isUpdatingHandlers to", this.isUpdatingHandlers);
34123
34136
  try {
34124
- const response = await fetch(`${this.baseUrl}/handlers`, {
34137
+ const response = await fetch(this.buildUrl("/handlers"), {
34125
34138
  method: "POST",
34126
34139
  headers: {
34127
34140
  "Content-Type": "application/json"
@@ -34147,7 +34160,7 @@ class LiveApiClient {
34147
34160
  source,
34148
34161
  lyrics
34149
34162
  };
34150
- const response = await fetch(`${this.baseUrl}/add-lyrics`, {
34163
+ const response = await fetch(this.buildUrl("/add-lyrics"), {
34151
34164
  method: "POST",
34152
34165
  headers: {
34153
34166
  "Content-Type": "application/json"
@@ -34165,7 +34178,7 @@ class LiveApiClient {
34165
34178
  }
34166
34179
  async submitAnnotations(annotations) {
34167
34180
  for (const annotation of annotations) {
34168
- const response = await fetch(`${this.baseUrl}/v1/annotations`, {
34181
+ const response = await fetch(this.buildUrl("/v1/annotations"), {
34169
34182
  method: "POST",
34170
34183
  headers: {
34171
34184
  "Content-Type": "application/json"
@@ -34178,7 +34191,7 @@ class LiveApiClient {
34178
34191
  }
34179
34192
  }
34180
34193
  async getAnnotationStats() {
34181
- const response = await fetch(`${this.baseUrl}/v1/annotations/stats`);
34194
+ const response = await fetch(this.buildUrl("/v1/annotations/stats"));
34182
34195
  if (!response.ok) {
34183
34196
  throw new Error(`API error: ${response.statusText}`);
34184
34197
  }
@@ -38943,10 +38956,12 @@ const TimelineCanvas = reactExports.memo(function TimelineCanvas2({
38943
38956
  const deltaTime = time - dragStartRef.current.time;
38944
38957
  const updates = [];
38945
38958
  for (const [wordId, originalTimes] of dragOriginalTimesRef.current) {
38959
+ const newStartTime = Math.max(0, originalTimes.start + deltaTime);
38960
+ const newEndTime = Math.max(newStartTime + 0.05, originalTimes.end + deltaTime);
38946
38961
  updates.push({
38947
38962
  wordId,
38948
- newStartTime: Math.max(0, originalTimes.start + deltaTime),
38949
- newEndTime: Math.max(0.05, originalTimes.end + deltaTime)
38963
+ newStartTime,
38964
+ newEndTime
38950
38965
  });
38951
38966
  }
38952
38967
  if (updates.length > 0) {
@@ -39275,7 +39290,7 @@ const LyricsSynchronizer = reactExports.memo(function LyricsSynchronizer2({
39275
39290
  );
39276
39291
  const allWords = reactExports.useMemo(() => getAllWords(workingSegments), [workingSegments]);
39277
39292
  const audioDuration = reactExports.useMemo(() => {
39278
- if (window.getAudioDuration) {
39293
+ if (typeof window.getAudioDuration === "function") {
39279
39294
  const duration2 = window.getAudioDuration();
39280
39295
  return duration2 > 0 ? duration2 : 300;
39281
39296
  }
@@ -39347,7 +39362,7 @@ const LyricsSynchronizer = reactExports.memo(function LyricsSynchronizer2({
39347
39362
  const [isPlaying, setIsPlaying] = reactExports.useState(false);
39348
39363
  reactExports.useEffect(() => {
39349
39364
  const checkPlaying = () => {
39350
- setIsPlaying(window.isAudioPlaying || false);
39365
+ setIsPlaying(typeof window.isAudioPlaying === "boolean" ? window.isAudioPlaying : false);
39351
39366
  };
39352
39367
  checkPlaying();
39353
39368
  const interval = setInterval(checkPlaying, 100);
@@ -39359,7 +39374,7 @@ const LyricsSynchronizer = reactExports.memo(function LyricsSynchronizer2({
39359
39374
  }
39360
39375
  }, [onPlaySegment]);
39361
39376
  const handleStopAudio = reactExports.useCallback(() => {
39362
- if (window.toggleAudioPlayback && window.isAudioPlaying) {
39377
+ if (typeof window.toggleAudioPlayback === "function" && window.isAudioPlaying) {
39363
39378
  window.toggleAudioPlayback();
39364
39379
  }
39365
39380
  if (isManualSyncing) {
@@ -39616,7 +39631,7 @@ const LyricsSynchronizer = reactExports.memo(function LyricsSynchronizer2({
39616
39631
  if (onPlaySegment) {
39617
39632
  onPlaySegment(time);
39618
39633
  setTimeout(() => {
39619
- if (window.toggleAudioPlayback && window.isAudioPlaying) {
39634
+ if (typeof window.toggleAudioPlayback === "function" && window.isAudioPlaying) {
39620
39635
  window.toggleAudioPlayback();
39621
39636
  }
39622
39637
  }, 50);
@@ -42923,22 +42938,23 @@ function App() {
42923
42938
  const params = new URLSearchParams(window.location.search);
42924
42939
  const encodedApiUrl = params.get("baseApiUrl");
42925
42940
  const audioHashParam = params.get("audioHash");
42941
+ const reviewTokenParam = params.get("reviewToken");
42926
42942
  if (encodedApiUrl) {
42927
42943
  const baseApiUrl = decodeURIComponent(encodedApiUrl);
42928
- setApiClient(new LiveApiClient(baseApiUrl));
42944
+ setApiClient(new LiveApiClient(baseApiUrl, reviewTokenParam || void 0));
42929
42945
  setIsReadOnly(false);
42930
42946
  if (audioHashParam) {
42931
42947
  setAudioHash(audioHashParam);
42932
42948
  }
42933
- fetchData(baseApiUrl);
42949
+ fetchData(baseApiUrl, reviewTokenParam || void 0);
42934
42950
  } else {
42935
42951
  setApiClient(new FileOnlyClient());
42936
42952
  setIsReadOnly(true);
42937
42953
  }
42938
42954
  }, []);
42939
- const fetchData = async (baseUrl) => {
42955
+ const fetchData = async (baseUrl, reviewToken) => {
42940
42956
  try {
42941
- const client2 = new LiveApiClient(baseUrl);
42957
+ const client2 = new LiveApiClient(baseUrl, reviewToken);
42942
42958
  const data2 = await client2.getCorrectionData();
42943
42959
  setData(data2);
42944
42960
  } catch (err) {
@@ -43258,7 +43274,7 @@ const theme = createTheme({
43258
43274
  spacing: (factor) => `${0.6 * factor}rem`
43259
43275
  // Further reduced from 0.8 * factor
43260
43276
  });
43261
- const version = "0.82.0";
43277
+ const version = "0.83.0";
43262
43278
  const packageJson = {
43263
43279
  version
43264
43280
  };
@@ -43269,4 +43285,4 @@ ReactDOM$1.createRoot(document.getElementById("root")).render(
43269
43285
  /* @__PURE__ */ jsxRuntimeExports.jsx(App, {})
43270
43286
  ] })
43271
43287
  );
43272
- //# sourceMappingURL=index-COYImAcx.js.map
43288
+ //# sourceMappingURL=index-BECn1o8Q.js.map