sreepathi-ravikumar commited on
Commit
505aba1
·
verified ·
1 Parent(s): 916cab7

Update video2.py

Browse files
Files changed (1) hide show
  1. video2.py +24 -214
video2.py CHANGED
@@ -396,228 +396,38 @@ def audio_func(id, lines, lang):
396
  return generate_tts_gtts(id, lines, lang)
397
 
398
 
399
- # Example usage
400
- if __name__ == "__main__":
401
- # Example 1: Simple English text
402
- lines = ["Hello, this is a test of the Google Text-to-Speech system."]
403
- duration, path = audio_func(0, lines, "English")
404
- print(f"Generated: {path} ({duration}s)")
405
-
406
- # Example 2: Bilingual text with custom format
407
- bilingual_text = "Hello, welcome to our service. வணக்கம், எங்கள் சேவைக்கு வரவேற்கிறோம். &&&Tamil"
408
- duration, path = audio_func(1, bilingual_text, bilingual_text)
409
- print(f"Generated: {path} ({duration}s)")
410
-
411
- # Example 3: Tamil text
412
- tamil_lines = ["வணக்கம், இது தமிழில் ஒரு சோதனை செய்தி."]
413
- duration, path = audio_func(2, tamil_lines, "Tamil")
414
- print(f"Generated: {path} ({duration}s)")
415
  #-----------------------------
416
  #---------------------------------
 
 
 
 
 
 
 
 
 
 
 
417
  def video_func(id, lines, lang):
418
  duration, audio_path = audio_func(id, lines, lang)
419
  if not duration or not audio_path:
420
  print("Failed to generate audio.")
421
  return None
 
422
  TEXT = lines[id]
423
  print("-----------------------------------------------------------------------------")
424
  print(TEXT)
425
- SKIP_SPACES = False
426
-
427
- FPS = 30 # Keep for smoothness, but can reduce to 24 if needed for speed
428
- ANIMATION_FRAMES_PER_CHAR = 2 # Reduced from 3 for faster rendering (less frames per char)
429
- WIDTH, HEIGHT = 1280, 720
430
- MARGIN_X, MARGIN_Y = 40, 60
431
- LINE_SPACING = 8
432
- FONT = cv2.FONT_HERSHEY_SIMPLEX
433
- DEFAULT_FONT_SCALE = 1.5
434
- HEADER_FONT_SCALE = 2.0 # Increased size for headers
435
- DEFAULT_THICKNESS = 2
436
- HEADER_THICKNESS = 3 # Bolder for headers
437
- DEFAULT_TEXT_COLOR = (0, 0, 0) # BGR Black
438
- HEADER_TEXT_COLOR = (255, 0, 0) # BGR Blue
439
- BG_COLOR = (255, 255, 255) # BGR White
440
- silent_video_name = f"silent_video{id}.mp4"
441
- silent_video_path = os.path.join(CLIPS_DIR, silent_video_name)
442
- FFMPEG_PRESET = "ultrafast"
443
- CRF = 28 # Increased CRF for faster encoding (lower quality, but quicker)
444
- # Pen settings
445
- PEN_COLOR = (0, 0, 255) # Red pen (BGR)
446
- PEN_TIP_RADIUS = 5
447
- PEN_LENGTH = 20
448
- PEN_THICKNESS = 2
449
- PEN_BASE_ANGLE = 45
450
- PEN_MOVEMENT_AMPLITUDE = 10
451
- # ===================================
452
-
453
- # Helper: wrap text by pixel width using cv2.getTextSize, now with per-line styles
454
- def wrap_text_cv(text, font, default_font_scale, default_thickness, max_width):
455
- wrapped_lines = []
456
- styles = [] # List of (is_header) for each wrapped line
457
- for para in text.splitlines():
458
- is_header = para.strip().startswith("###")
459
- if is_header:
460
- para = para.strip()[3:].strip() # Remove "### " or "###"
461
- font_scale = HEADER_FONT_SCALE
462
- thickness = HEADER_THICKNESS
463
- else:
464
- font_scale = default_font_scale
465
- thickness = default_thickness
466
- if para == "":
467
- wrapped_lines.append("")
468
- styles.append(False) # Not header
469
- continue
470
- words = para.split(" ")
471
- cur = ""
472
- for w in words:
473
- candidate = w if cur == "" else cur + " " + w
474
- (w_w, w_h), _ = cv2.getTextSize(candidate, font, font_scale, thickness)
475
- if w_w <= max_width:
476
- cur = candidate
477
- else:
478
- if cur != "":
479
- wrapped_lines.append(cur)
480
- styles.append(is_header)
481
- (single_w, _), _ = cv2.getTextSize(w, font, font_scale, thickness)
482
- if single_w > max_width:
483
- chunk = ""
484
- for ch in w:
485
- cand2 = chunk + ch
486
- (c_w, _), _ = cv2.getTextSize(cand2, font, font_scale, thickness)
487
- if c_w <= max_width:
488
- chunk = cand2
489
- else:
490
- wrapped_lines.append(chunk)
491
- styles.append(is_header)
492
- chunk = ch
493
- if chunk:
494
- cur = chunk
495
- else:
496
- cur = ""
497
- else:
498
- cur = w
499
- if cur != "":
500
- wrapped_lines.append(cur)
501
- styles.append(is_header)
502
- return wrapped_lines, styles
503
-
504
- # Pre-wrap text with styles
505
- text_area_width = WIDTH - 2 * MARGIN_X
506
- wrapped_lines, line_styles = wrap_text_cv(TEXT, FONT, DEFAULT_FONT_SCALE, DEFAULT_THICKNESS, text_area_width)
507
- full_text = "\n".join(wrapped_lines)
508
- if not full_text:
509
- full_text = ""
510
- # Visible indices
511
- if SKIP_SPACES:
512
- visible_indices = [i for i, ch in enumerate(full_text) if (ch != ' ' and ch != '\n' and ch != '\t')]
513
  else:
514
- visible_indices = list(range(len(full_text)))
515
-
516
- total_glyphs = len(visible_indices)
517
- print(f"Wrapped lines: {len(wrapped_lines)} lines, total glyphs (counted): {total_glyphs}")
518
- if total_glyphs == 0:
519
- print("No text to animate.")
520
- return None
521
- # Minimal frames
522
- min_frames = total_glyphs * ANIMATION_FRAMES_PER_CHAR
523
- print(f"Rendering {min_frames} minimal frames for full text animation.")
524
- # Pre-calc line heights and y_positions with per-line styles
525
- line_heights = []
526
- y_positions = []
527
- y = MARGIN_Y
528
- for i, line in enumerate(wrapped_lines):
529
- is_header = line_styles[i]
530
- font_scale = HEADER_FONT_SCALE if is_header else DEFAULT_FONT_SCALE
531
- thickness = HEADER_THICKNESS if is_header else DEFAULT_THICKNESS
532
- if line == "":
533
- (w, h), baseline = cv2.getTextSize("Ay", FONT, font_scale, thickness)
534
- else:
535
- (w, h), baseline = cv2.getTextSize(line, FONT, font_scale, thickness)
536
- lh = h + baseline + LINE_SPACING
537
- line_heights.append(lh)
538
- y_positions.append(y)
539
- y += lh
540
- # Prepare ffmpeg
541
- ffmpeg_cmd = (
542
- f'ffmpeg -y '
543
- f'-f rawvideo -pix_fmt bgr24 -s {WIDTH}x{HEIGHT} -r {FPS} -i - '
544
- f'-an '
545
- f'-c:v libx264 -preset {FFMPEG_PRESET} -crf {CRF} -pix_fmt yuv420p '
546
- f'{silent_video_path}'
547
- )
548
- print("FFMPEG CMD:", ffmpeg_cmd)
549
-
550
- proc = subprocess.Popen(shlex.split(ffmpeg_cmd), stdin=subprocess.PIPE, bufsize=10**8)
551
- # Render function, now with per-line colors and styles
552
- def render_frame(visible_text, pen_x, pen_y, anim_offset):
553
- img = np.full((HEIGHT, WIDTH, 3), BG_COLOR, dtype=np.uint8)
554
- lines = visible_text.split("\n")
555
- for idx, line in enumerate(lines):
556
- is_header = line_styles[idx]
557
- font_scale = HEADER_FONT_SCALE if is_header else DEFAULT_FONT_SCALE
558
- thickness = HEADER_THICKNESS if is_header else DEFAULT_THICKNESS
559
- color = HEADER_TEXT_COLOR if is_header else DEFAULT_TEXT_COLOR
560
- x = MARGIN_X
561
- y = y_positions[idx]
562
- (w, h), baseline = cv2.getTextSize(line, FONT, font_scale, thickness)
563
- y_draw = y + h
564
- if line != "":
565
- cv2.putText(img, line, (x, y_draw), FONT, font_scale, color, thickness, lineType=cv2.LINE_AA)
566
- if pen_x > 0:
567
- offset_y = int(PEN_MOVEMENT_AMPLITUDE * math.sin(anim_offset * math.pi))
568
- pen_tip_y = pen_y + offset_y
569
- angle_rad = math.radians(PEN_BASE_ANGLE)
570
- pen_end_x = pen_x + int(PEN_LENGTH * math.cos(angle_rad))
571
- pen_end_y = pen_tip_y - int(PEN_LENGTH * math.sin(angle_rad))
572
- cv2.line(img, (pen_x, pen_tip_y), (pen_end_x, pen_end_y), PEN_COLOR, PEN_THICKNESS)
573
- cv2.circle(img, (pen_x, pen_tip_y), PEN_TIP_RADIUS, PEN_COLOR, -1)
574
- return img
575
-
576
- t0 = time.time()
577
- frames_sent = 0
578
- prev_visible_sub = ""
579
- last_pen_x = 0
580
- last_pen_y = 0
581
- for rank, idx_in_full in enumerate(visible_indices):
582
- visible_sub = full_text[:idx_in_full + 1]
583
- if visible_sub != prev_visible_sub:
584
- lines = visible_sub.split("\n")
585
- last_line = lines[-1]
586
- line_idx = len(lines) - 1
587
- is_header = line_styles[line_idx]
588
- font_scale = HEADER_FONT_SCALE if is_header else DEFAULT_FONT_SCALE
589
- thickness = HEADER_THICKNESS if is_header else DEFAULT_THICKNESS
590
- (w, h), baseline = cv2.getTextSize(last_line, FONT, font_scale, thickness)
591
- pen_x = MARGIN_X + w + 5
592
- pen_y = y_positions[line_idx] + h // 2
593
- last_pen_x = pen_x
594
- last_pen_y = pen_y
595
- for anim_step in range(ANIMATION_FRAMES_PER_CHAR):
596
- frame_img = render_frame(visible_sub, pen_x, pen_y, anim_step / ANIMATION_FRAMES_PER_CHAR)
597
- proc.stdin.write(frame_img.tobytes())
598
- frames_sent += 1
599
- prev_visible_sub = visible_sub
600
- proc.stdin.close()
601
- proc.wait()
602
- elapsed = time.time() - t0
603
- print(f"Frames sent: {frames_sent}, elapsed time: {elapsed:.3f} seconds")
604
- if not os.path.exists(silent_video_path):
605
- print("Silent video generation failed.")
606
- return None
607
- # Combine with audio using MoviePy
608
- final_video_name = f"clip{id}.mp4"
609
- final_video_path = os.path.join(CLIPS_DIR, final_video_name)
610
- video_clip = VideoFileClip(silent_video_path)
611
- rendered_duration = video_clip.duration
612
- print(f"Rendered video duration: {rendered_duration:.3f}s, Audio duration: {duration:.3f}s")
613
- if rendered_duration > 0 and duration > 0:
614
- speed_factor = rendered_duration / duration
615
- print(f"Adjusting video speed by factor: {speed_factor:.3f}")
616
- video_clip = video_clip.fx(speedx, speed_factor)
617
- final_clip = video_clip.set_audio(AudioFileClip(audio_path))
618
- # Write final video with faster settings
619
- final_clip.write_videofile(final_video_path, codec='libx264', audio_codec='aac', preset='ultrafast', verbose=False, logger=None, threads=4) # Added threads for multi-threading
620
- print(f"Final video saved at: {final_video_path}")
621
- # Clean up
622
- os.remove(silent_video_path)
623
- return final_video_path
 
396
  return generate_tts_gtts(id, lines, lang)
397
 
398
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
  #-----------------------------
400
  #---------------------------------
401
+ import os
402
+ import subprocess
403
+ import shlex
404
+ import time
405
+ import math
406
+ import numpy as np
407
+ import cv2
408
+ from moviepy.editor import VideoFileClip, AudioFileClip
409
+ from moviepy.video.fx.speedx import speedx
410
+
411
+ # video.py
412
  def video_func(id, lines, lang):
413
  duration, audio_path = audio_func(id, lines, lang)
414
  if not duration or not audio_path:
415
  print("Failed to generate audio.")
416
  return None
417
+
418
  TEXT = lines[id]
419
  print("-----------------------------------------------------------------------------")
420
  print(TEXT)
421
+
422
+ # CREATE CLIPS DIRECTORY IF IT DOESN'T EXIST
423
+ os.makedirs(CLIPS_DIR, exist_ok=True)
424
+
425
+ # Call Rust function
426
+ final_video_path = rust_highlight.generate_video_clip(id, TEXT, audio_path, duration, CLIPS_DIR)
427
+
428
+ if final_video_path:
429
+ print(f"Final video saved at: {final_video_path}")
430
+ return final_video_path
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
  else:
432
+ print("Video generation failed.")
433
+ return None