karaoke-gen 0.61.0__tar.gz → 0.62.0__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.

Potentially problematic release.


This version of karaoke-gen might be problematic. Click here for more details.

Files changed (22) hide show
  1. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/PKG-INFO +1 -1
  2. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/karaoke_gen/config.py +37 -4
  3. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/karaoke_gen/karaoke_finalise/karaoke_finalise.py +20 -9
  4. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/karaoke_gen/karaoke_gen.py +22 -2
  5. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/karaoke_gen/utils/gen_cli.py +18 -0
  6. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/pyproject.toml +1 -1
  7. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/LICENSE +0 -0
  8. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/README.md +0 -0
  9. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/karaoke_gen/__init__.py +0 -0
  10. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/karaoke_gen/audio_processor.py +0 -0
  11. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/karaoke_gen/file_handler.py +0 -0
  12. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/karaoke_gen/karaoke_finalise/__init__.py +0 -0
  13. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/karaoke_gen/lyrics_processor.py +0 -0
  14. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/karaoke_gen/metadata.py +0 -0
  15. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/karaoke_gen/resources/AvenirNext-Bold.ttf +0 -0
  16. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/karaoke_gen/resources/Montserrat-Bold.ttf +0 -0
  17. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/karaoke_gen/resources/Oswald-Bold.ttf +0 -0
  18. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/karaoke_gen/resources/Oswald-SemiBold.ttf +0 -0
  19. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/karaoke_gen/resources/Zurich_Cn_BT_Bold.ttf +0 -0
  20. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/karaoke_gen/utils/__init__.py +0 -0
  21. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/karaoke_gen/utils/bulk_cli.py +0 -0
  22. {karaoke_gen-0.61.0 → karaoke_gen-0.62.0}/karaoke_gen/video_generator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: karaoke-gen
3
- Version: 0.61.0
3
+ Version: 0.62.0
4
4
  Summary: Generate karaoke videos with synchronized lyrics. Handles the entire process from downloading audio and lyrics to creating the final video with title screens.
5
5
  License: MIT
6
6
  Author: Andrew Beveridge
@@ -46,15 +46,43 @@ DEFAULT_STYLE_PARAMS = {
46
46
  }
47
47
 
48
48
 
49
- def load_style_params(style_params_json, logger):
49
+ def apply_style_overrides(style_params, overrides, logger):
50
+ """Recursively applies overrides to the style parameters."""
51
+ for key, value in overrides.items():
52
+ keys = key.split('.')
53
+ current_level = style_params
54
+ for i, k in enumerate(keys):
55
+ if i == len(keys) - 1:
56
+ if k in current_level:
57
+ # Attempt to cast the value to the type of the existing value
58
+ try:
59
+ original_type = type(current_level[k])
60
+ if original_type == bool:
61
+ # Handle boolean conversion
62
+ value = value.lower() in ('true', '1', 't', 'y', 'yes')
63
+ else:
64
+ value = original_type(value)
65
+ except (ValueError, TypeError) as e:
66
+ logger.warning(f"Could not cast override value '{value}' for key '{key}' to original type. Using as string. Error: {e}")
67
+ current_level[k] = value
68
+ logger.info(f"Overrode style: {key} = {value}")
69
+ else:
70
+ logger.warning(f"Override key '{key}' not found in style parameters.")
71
+ elif k in current_level and isinstance(current_level[k], dict):
72
+ current_level = current_level[k]
73
+ else:
74
+ logger.warning(f"Override key part '{k}' not found or not a dictionary for key '{key}'.")
75
+ break
76
+
77
+
78
+ def load_style_params(style_params_json, style_overrides, logger):
50
79
  """Loads style parameters from a JSON file or uses defaults."""
80
+ style_params = {}
51
81
  if style_params_json:
52
82
  try:
53
83
  with open(style_params_json, "r") as f:
54
84
  style_params = json.loads(f.read())
55
85
  logger.info(f"Loaded style parameters from {style_params_json}")
56
- # You might want to add validation here to ensure the structure matches expectations
57
- return style_params
58
86
  except FileNotFoundError:
59
87
  logger.error(f"Style parameters configuration file not found: {style_params_json}")
60
88
  sys.exit(1)
@@ -66,7 +94,12 @@ def load_style_params(style_params_json, logger):
66
94
  sys.exit(1)
67
95
  else:
68
96
  logger.info("No style parameters JSON file provided. Using default styles.")
69
- return DEFAULT_STYLE_PARAMS
97
+ style_params = DEFAULT_STYLE_PARAMS
98
+
99
+ if style_overrides:
100
+ apply_style_overrides(style_params, style_overrides, logger)
101
+
102
+ return style_params
70
103
 
71
104
  def setup_title_format(style_params):
72
105
  """Sets up the title format dictionary from style parameters."""
@@ -412,26 +412,33 @@ class KaraokeFinalise:
412
412
  if "items" in response and len(response["items"]) > 0:
413
413
  for item in response["items"]:
414
414
  found_title = item["snippet"]["title"]
415
- similarity_score = fuzz.ratio(youtube_title.lower(), found_title.lower())
416
- if similarity_score >= 70: # 70% similarity
415
+
416
+ # In server-side mode, require an exact match to avoid false positives.
417
+ # Otherwise, use fuzzy matching for interactive CLI usage.
418
+ if self.server_side_mode:
419
+ is_match = youtube_title.lower() == found_title.lower()
420
+ similarity_score = 100 if is_match else 0
421
+ else:
422
+ similarity_score = fuzz.ratio(youtube_title.lower(), found_title.lower())
423
+ is_match = similarity_score >= 70
424
+
425
+ if is_match:
417
426
  found_id = item["id"]["videoId"]
418
427
  self.logger.info(
419
428
  f"Potential match found on YouTube channel with ID: {found_id} and title: {found_title} (similarity: {similarity_score}%)"
420
429
  )
421
-
422
- # In non-interactive mode, automatically confirm if similarity is high enough
430
+
431
+ # In non-interactive mode (server mode), we don't prompt. Just record the match and return.
423
432
  if self.non_interactive:
424
- self.logger.info(f"Non-interactive mode, automatically confirming match with similarity score {similarity_score}%")
433
+ self.logger.info(f"Non-interactive mode, found a match.")
425
434
  self.youtube_video_id = found_id
426
435
  self.youtube_url = f"{self.youtube_url_prefix}{self.youtube_video_id}"
427
- self.skip_notifications = True
428
436
  return True
429
-
437
+
430
438
  confirmation = input(f"Is '{found_title}' the video you are finalising? (y/n): ").strip().lower()
431
439
  if confirmation == "y":
432
440
  self.youtube_video_id = found_id
433
441
  self.youtube_url = f"{self.youtube_url_prefix}{self.youtube_video_id}"
434
- self.skip_notifications = True
435
442
  return True
436
443
 
437
444
  self.logger.info(f"No matching video found with title: {youtube_title}")
@@ -483,8 +490,12 @@ class KaraokeFinalise:
483
490
  max_length = 95
484
491
  youtube_title = self.truncate_to_nearest_word(youtube_title, max_length)
485
492
 
493
+ # In server-side mode, we should always replace videos if an exact match is found.
494
+ # Otherwise, respect the replace_existing flag from CLI.
495
+ should_replace = True if self.server_side_mode else replace_existing
496
+
486
497
  if self.check_if_video_title_exists_on_youtube_channel(youtube_title):
487
- if replace_existing:
498
+ if should_replace:
488
499
  self.logger.info(f"Video already exists on YouTube, deleting before re-upload: {self.youtube_url}")
489
500
  if self.delete_youtube_video(self.youtube_video_id):
490
501
  self.logger.info(f"Successfully deleted existing video, proceeding with upload")
@@ -67,6 +67,7 @@ class KaraokePrep:
67
67
  subtitle_offset_ms=0,
68
68
  # Style Configuration
69
69
  style_params_json=None,
70
+ style_overrides=None,
70
71
  # Add the new parameter
71
72
  skip_separation=False,
72
73
  # YouTube/Online Configuration
@@ -124,13 +125,23 @@ class KaraokePrep:
124
125
 
125
126
  # Style Config - Keep needed ones
126
127
  self.render_bounding_boxes = render_bounding_boxes # Passed to VideoGenerator
127
- self.style_params_json = style_params_json # Passed to LyricsProcessor
128
+ self.style_params_json = style_params_json
129
+ self.style_overrides = style_overrides
130
+ self.temp_style_file = None
128
131
 
129
132
  # YouTube/Online Config
130
133
  self.cookies_str = cookies_str # Passed to metadata extraction and file download
131
134
 
132
135
  # Load style parameters using the config module
133
- self.style_params = load_style_params(self.style_params_json, self.logger)
136
+ self.style_params = load_style_params(self.style_params_json, self.style_overrides, self.logger)
137
+
138
+ # If overrides were applied, write to a temp file and update the path
139
+ if self.style_overrides:
140
+ with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=".json") as temp_file:
141
+ json.dump(self.style_params, temp_file, indent=2)
142
+ self.temp_style_file = temp_file.name
143
+ self.style_params_json = self.temp_style_file
144
+ self.logger.info(f"Style overrides applied. Using temporary style file: {self.temp_style_file}")
134
145
 
135
146
  # Set up title and end formats using the config module
136
147
  self.title_format = setup_title_format(self.style_params)
@@ -199,6 +210,15 @@ class KaraokePrep:
199
210
  else:
200
211
  self.logger.debug(f"Overall output dir {self.output_dir} already exists")
201
212
 
213
+ def __del__(self):
214
+ # Cleanup the temporary style file if it was created
215
+ if self.temp_style_file and os.path.exists(self.temp_style_file):
216
+ try:
217
+ os.remove(self.temp_style_file)
218
+ self.logger.debug(f"Removed temporary style file: {self.temp_style_file}")
219
+ except OSError as e:
220
+ self.logger.warning(f"Error removing temporary style file {self.temp_style_file}: {e}")
221
+
202
222
  # Compatibility methods for tests - these call the new functions in metadata.py
203
223
  def extract_info_for_online_media(self, input_url=None, input_artist=None, input_title=None):
204
224
  """Compatibility method that calls the function in metadata.py"""
@@ -215,6 +215,11 @@ async def async_main():
215
215
  "--style_params_json",
216
216
  help="Optional: Path to JSON file containing style configuration. Example: --style_params_json='/path/to/style_params.json'",
217
217
  )
218
+ style_group.add_argument(
219
+ "--style_override",
220
+ action="append",
221
+ help="Optional: Override a style parameter. Can be used multiple times. Example: --style_override 'intro.background_image=/path/to/new_image.png'",
222
+ )
218
223
 
219
224
  # Finalisation Configuration
220
225
  finalise_group = parser.add_argument_group("Finalisation Configuration")
@@ -283,6 +288,17 @@ async def async_main():
283
288
 
284
289
  args = parser.parse_args()
285
290
 
291
+ # Process style overrides
292
+ style_overrides = {}
293
+ if args.style_override:
294
+ for override in args.style_override:
295
+ try:
296
+ key, value = override.split("=", 1)
297
+ style_overrides[key] = value
298
+ except ValueError:
299
+ logger.error(f"Invalid style override format: {override}. Must be in 'key=value' format.")
300
+ sys.exit(1)
301
+
286
302
  # Handle test email template case first
287
303
  if args.test_email_template:
288
304
  log_level = getattr(logging, args.log_level.upper())
@@ -354,6 +370,7 @@ async def async_main():
354
370
  skip_transcription_review=args.skip_transcription_review,
355
371
  subtitle_offset_ms=args.subtitle_offset_ms,
356
372
  style_params_json=args.style_params_json,
373
+ style_overrides=style_overrides,
357
374
  )
358
375
  # No await needed for constructor
359
376
  kprep = kprep_coroutine
@@ -671,6 +688,7 @@ async def async_main():
671
688
  skip_transcription_review=args.skip_transcription_review,
672
689
  subtitle_offset_ms=args.subtitle_offset_ms,
673
690
  style_params_json=args.style_params_json,
691
+ style_overrides=style_overrides,
674
692
  )
675
693
  # No await needed for constructor
676
694
  kprep = kprep_coroutine
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "karaoke-gen"
3
- version = "0.61.0"
3
+ version = "0.62.0"
4
4
  description = "Generate karaoke videos with synchronized lyrics. Handles the entire process from downloading audio and lyrics to creating the final video with title screens."
5
5
  authors = ["Andrew Beveridge <andrew@beveridge.uk>"]
6
6
  license = "MIT"
File without changes
File without changes