import whisper

import os
import subprocess
from bs4 import BeautifulSoup

################----------------------------------------------Video generation-----------------------------


# ==========================================
# CONFIGURATION & PATHS
# ==========================================
output_dir = r"C:\Users\Bot\Documents\output_sections"
html_file = r"C:\Users\Bot\Documents\html_data.txt"

# Make sure this click sound exists on your PC
click_sound = r"F:\localsend\click2.wav"
thumbnail_img = os.path.join(output_dir, "genereted_image.jpg")

# Font setup exactly from your old code
font_path = "C:/Users/Bot/Documents/Baloo-Regular.ttf"
font_path_clean = font_path.replace("\\", "/")
safe_font = font_path_clean.replace(":", "\\:")

def escape_text(t):
    return t.replace("'", r"\'").replace(":", r"\:").replace(",", r"\,")

def get_video_duration(filepath):
    """Uses ffprobe to get the exact length of a video in seconds."""
    cmd = [
        "ffprobe", "-v", "error", "-show_entries", "format=duration",
        "-of", "default=noprint_wrappers=1:nokey=1", filepath
    ]
    try:
        result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        return float(result.stdout.strip())
    except (ValueError, Exception):
        return 0.0

# ==========================================
# PART 1: EXTRACT HEADINGS FROM HTML
# ==========================================
print("--- Extracting Headings from HTML ---")
section_headings = {}

if os.path.exists(html_file):
    with open(html_file, 'r', encoding='utf-8') as f:
        soup = BeautifulSoup(f.read(), 'html.parser')

    for div in soup.find_all('div', class_=lambda c: c and c.startswith('section_')):
        sec_class = next((c for c in div.get('class', []) if c.startswith('section_') and c != 'section_heading'), None)
        if sec_class:
            try:
                sec_num = int(sec_class.split('_')[1])
                heading_div = div.find('div', class_='section_heading')
                if heading_div:
                    section_headings[sec_num] = heading_div.get_text(strip=True)
            except ValueError:
                continue

# ==========================================
# PART 2: PROCESS OVERLAYS PER SECTION
# ==========================================
processed_videos = []
cumulative_video_time = 0.0  # Tracks the total timeline to fix cumulative text files

for i in range(1, 100):  
    base_vid = os.path.join(output_dir, f"section_{i}.mp4")
    if not os.path.exists(base_vid):
        continue
       
    print(f"\n--- Processing Section {i} ---")
   
    # Get the duration of this specific base video to add to our tracker later
    current_vid_duration = get_video_duration(base_vid)
   
    inputs = [f"-i \"{base_vid}\""]
    time_ranges = []
   
    # Find all image lines for THIS specific section
    for file in sorted(os.listdir(output_dir)):
        if file.startswith(f"section_{i}_line_") and file.endswith(".jpg"):
            base_name = file.replace(".jpg", "")
            start_file = os.path.join(output_dir, f"{base_name}_start.txt")
            end_file = os.path.join(output_dir, f"{base_name}_end.txt")
            image_file = os.path.join(output_dir, file)
           
            if not (os.path.exists(start_file) and os.path.exists(end_file)):
                continue
               
            with open(start_file, "r") as sf:
                raw_start = float(sf.read().strip())
            with open(end_file, "r") as ef:
                raw_end = float(ef.read().strip())
               
            # --- THE MAGIC FIX: Convert Cumulative Time to Relative Time ---
            # If the text file says 150s, but we are at 148s in the global timeline,
            # it converts it to 2s into the current clip.
            start_time = raw_start - cumulative_video_time if raw_start >= cumulative_video_time else raw_start
            end_time = raw_end - cumulative_video_time if raw_end >= cumulative_video_time else raw_end
           
            # Ensure no negative times from slight overlapping
            start_time = max(0.0, start_time)
            end_time = max(start_time + 1.0, end_time)

            inputs.append(f"-i \"{image_file.replace(os.sep, '/')}\"")
            time_ranges.append((start_time, end_time))
           
    filter_complex_parts = []
    last_video = "[0:v]"
   
    # --- 1. BUILD IMAGE OVERLAYS ---
    for idx, (start_time, end_time) in enumerate(time_ranges, start=1):
        out_label = f"[v{idx}]"
        overlay_cmd = (
            f"[{idx}:v] scale=800:-1[scaled_{idx}];"
            f"{last_video}[scaled_{idx}] overlay=enable='between(t,{start_time},{end_time})':x=1000:y=(H-h)/2+50 {out_label}"
        )
        filter_complex_parts.append(overlay_cmd)
        last_video = out_label
       
    # --- 2. ADD HEADING TEXT (First 5 seconds) ---
    if i in section_headings:
        safe_title = escape_text(section_headings[i])
       
        # Lower third settings
        LT_FONT_SIZE = 50
        LT_BG_COLOR = "yellow"
        LT_TEXT_COLOR = "black"
        LT_PADDING_LEFT = 50
        LT_PADDING_BOTTOM = 200
        LT_BOX_PADDING = 20
       
        text_out = "[vout_text]"
        part = (
            f"drawtext=text='{safe_title}':"
            f"fontfile='{safe_font}':"
            f"fontcolor={LT_TEXT_COLOR}:"
            f"fontsize={LT_FONT_SIZE}:"
            f"box=1:boxcolor={LT_BG_COLOR}:boxborderw={LT_BOX_PADDING}:"
            f"x={LT_PADDING_LEFT}:"
            f"y=h-text_h-{LT_PADDING_BOTTOM}:"
            f"enable='between(t,0,5)'"
        )
        filter_complex_parts.append(f"{last_video} {part} {text_out}")
        last_video = text_out

    # --- 3. ADD CLICK SOUNDS & AUDIO MIX ---
    use_clicks = os.path.exists(click_sound)
    if use_clicks and time_ranges:
        for _ in time_ranges:
            inputs.append(f"-i \"{click_sound.replace(os.sep, '/')}\"")
           
        audio_mix_parts = ["[0:a]"]
        click_audio_index = len(time_ranges) + 1 # first click input
       
        for idx, (start_time, _) in enumerate(time_ranges, start=1):
            audio_in = f"[{click_audio_index}:a]"
            delayed = f"[click{idx}]"
            delay_ms = int(float(start_time) * 1000)
            filter_complex_parts.append(f"{audio_in} adelay={delay_ms}|{delay_ms} {delayed}")
            audio_mix_parts.append(delayed)
            click_audio_index += 1
           
        filter_complex_parts.append(f"{''.join(audio_mix_parts)} amix=inputs={len(audio_mix_parts)}:normalize=0[audio_out]")
        audio_map = "-map \"[audio_out]\""
    else:
        audio_map = "-map 0:a"
       
    # --- 4. RUN FFMPEG ---
    output_path = os.path.join(output_dir, f"section_{i}_final.mp4").replace("\\", "/")
   
    if filter_complex_parts:
        filter_complex = "; ".join(filter_complex_parts)
        cmd = (
            f"ffmpeg -hwaccel cuda {' '.join(inputs)} -filter_complex \"{filter_complex}\" "
            f"-map \"{last_video}\" {audio_map} -c:v h264_nvenc -preset fast -b:v 5M "
            f"-c:a aac -b:a 192k \"{output_path}\" -y"
        )
        print(f"🔹 Running FFmpeg for Section {i}...")
        subprocess.run(cmd, shell=True)
    else:
        print(f"🔹 No images/text for Section {i}, copying original video...")
        cmd = f"ffmpeg -i \"{base_vid.replace(os.sep, '/')}\" -c copy \"{output_path}\" -y"
        subprocess.run(cmd, shell=True)
       
    processed_videos.append(output_path)
   
    # Update cumulative time tracker to auto-correct the next section's text files
    cumulative_video_time += current_vid_duration


# ==========================================
# PART 3: MERGE ALL CLIPS & THUMBNAIL
# ==========================================
print("\n--- Merging all section clips into one ---")
if processed_videos:
    concat_list = os.path.join(output_dir, "concat_list.txt")
    with open(concat_list, "w", encoding="utf-8") as f:
        for vid in processed_videos:
            f.write(f"file '{vid}'\n")
           
    merged_sections_vid = os.path.join(output_dir, "merged_base_video.mp4").replace("\\", "/")
    final_output = os.path.join(output_dir, "FINAL_YOUTUBE_VIDEO.mp4").replace("\\", "/")
    thumb_video_path = os.path.join(output_dir, "thumb_clip.mp4").replace("\\", "/")
   
    # Merge the processed sections
    concat_cmd = f"ffmpeg -f concat -safe 0 -i \"{concat_list.replace(os.sep, '/')}\" -c copy \"{merged_sections_vid}\" -y"
    subprocess.run(concat_cmd, shell=True)
   
    # Append the thumbnail to the end (using your old code exactly)
    if os.path.exists(thumbnail_img):
        cmd_thumb = (
            f"ffmpeg -loop 1 -i \"{thumbnail_img.replace(os.sep, '/')}\" "
            f"-f lavfi -t 1 -i anullsrc=r=44100:cl=stereo "
            f"-shortest -vf scale=1920:1080,fps=30,format=yuv420p "
            f"-c:v h264_nvenc -preset fast -b:v 5M -pix_fmt yuv420p "
            f"-c:a aac -b:a 192k \"{thumb_video_path}\" -y"
        )
        print(f"🔹 Creating thumbnail clip...")
        subprocess.run(cmd_thumb, shell=True)

        cmd_concat_thumb = (
            f"ffmpeg -i \"{merged_sections_vid}\" -i \"{thumb_video_path}\" "
            f"-filter_complex \"[0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1[outv][outa]\" "
            f"-map \"[outv]\" -map \"[outa]\" -c:v h264_nvenc -preset fast -b:v 5M "
            f"-c:a aac -b:a 192k \"{final_output}\" -y"
        )
        print(f"🔹 Concatenating Final Video + Thumbnail...")
        subprocess.run(cmd_concat_thumb, shell=True)
        print(f"✅ Final video with thumbnail saved: {final_output}")
    else:
        print("⚠️ Thumbnail not found, outputting merged video as final.")
        os.rename(merged_sections_vid, final_output)
else:
    print("❌ No videos were generated. Check your paths.")



0 comments:

Post a Comment

 
Top