Snehnasu's picture
Update app.py
594b0f1 verified
Raw
History Blame Contribute Delete
7.41 kB
import os
import uuid
import tempfile
import gradio as gr
from gtts import gTTS
from PIL import Image
import pytesseract
from moviepy import ImageClip, AudioFileClip, concatenate_videoclips
def set_clip_duration(clip, duration):
if hasattr(clip, "with_duration"):
return clip.with_duration(duration)
return clip.set_duration(duration)
def set_clip_fps(clip, fps):
if hasattr(clip, "with_fps"):
return clip.with_fps(fps)
return clip.set_fps(fps)
def resize_clip_height(clip, height):
if hasattr(clip, "resized"):
return clip.resized(height=height)
return clip.resize(height=height)
def attach_audio(video_clip, audio_clip):
if hasattr(video_clip, "with_audio"):
return video_clip.with_audio(audio_clip)
return video_clip.set_audio(audio_clip)
def extract_text_from_image(image_path):
"""
Reads visible text from a screenshot using OCR.
Works best when screenshots contain dashboard labels, KPI names, chart titles, or table text.
"""
image = Image.open(image_path)
text = pytesseract.image_to_string(image)
cleaned_lines = []
for line in text.splitlines():
line = line.strip()
if line:
cleaned_lines.append(line)
cleaned_text = " ".join(cleaned_lines)
return cleaned_text
def create_ai_narration(slide_number, total_slides, extracted_text):
"""
Creates a simple narration script from OCR text.
This is intentionally lightweight for Hugging Face Free CPU.
"""
if extracted_text.strip() == "":
return (
f"This is slide {slide_number} of {total_slides}. "
"This screen appears to show a visual dashboard or report. "
"The key takeaway should be reviewed based on the visible charts, metrics, and layout."
)
# Limit text so narration does not become too long
max_chars = 450
short_text = extracted_text[:max_chars]
if slide_number == 1:
intro = "This first screen introduces the main dashboard view."
elif slide_number == total_slides:
intro = "This final screen summarizes the key information shown in the report."
else:
intro = f"This is slide {slide_number} of {total_slides}."
narration = (
f"{intro} "
f"The visible information includes: {short_text}. "
"The main point is to review the key metrics, compare the important values, "
"and identify where business attention may be required."
)
return narration
def create_slide_video(image_path, narration_text, output_height, temp_dir, run_id, slide_number):
"""
Creates one slide video:
screenshot + AI-generated narration audio.
"""
audio_path = os.path.join(
temp_dir,
f"slide_audio_{run_id}_{slide_number}.mp3"
)
tts = gTTS(text=narration_text, lang="en")
tts.save(audio_path)
audio = AudioFileClip(audio_path)
audio_duration = audio.duration
slide_duration = audio_duration + 0.5
image_clip = ImageClip(image_path)
image_clip = set_clip_duration(image_clip, slide_duration)
image_clip = resize_clip_height(image_clip, output_height)
image_clip = set_clip_fps(image_clip, 24)
slide_clip = attach_audio(image_clip, audio)
return slide_clip, audio, audio_path, narration_text
def create_video_from_screenshots(images, output_height):
if not images:
return None, None, "Please upload at least one screenshot or image.", ""
try:
output_height = int(output_height)
if output_height < 240:
return None, None, "Output height should be at least 240.", ""
run_id = str(uuid.uuid4())
temp_dir = tempfile.gettempdir()
final_video_path = os.path.join(
temp_dir,
f"ai_narrated_video_{run_id}.mp4"
)
total_slides = len(images)
slide_clips = []
audio_clips = []
audio_paths = []
generated_narrations = []
for index, image_path in enumerate(images):
slide_number = index + 1
extracted_text = extract_text_from_image(image_path)
narration_text = create_ai_narration(
slide_number=slide_number,
total_slides=total_slides,
extracted_text=extracted_text
)
slide_clip, audio_clip, audio_path, final_narration = create_slide_video(
image_path=image_path,
narration_text=narration_text,
output_height=output_height,
temp_dir=temp_dir,
run_id=run_id,
slide_number=slide_number
)
slide_clips.append(slide_clip)
audio_clips.append(audio_clip)
audio_paths.append(audio_path)
generated_narrations.append(
f"Slide {slide_number}:\n{final_narration}"
)
final_video = concatenate_videoclips(
slide_clips,
method="compose"
)
final_video.write_videofile(
final_video_path,
codec="libx264",
audio_codec="aac",
fps=24,
preset="ultrafast",
threads=2
)
final_duration = final_video.duration
final_video.close()
for clip in slide_clips:
clip.close()
for audio in audio_clips:
audio.close()
narration_preview = "\n\n".join(generated_narrations)
return (
final_video_path,
audio_paths[0],
f"Success: AI-generated narrated video created. Slides: {total_slides}. Duration: {final_duration:.1f} seconds.",
narration_preview
)
except Exception as e:
return None, None, f"Error: {str(e)}", ""
with gr.Blocks(title="AI Screenshot to Narrated Video Generator") as demo:
gr.Markdown(
"""
# AI Screenshot to Narrated Video Generator
Upload screenshots and the app will automatically:
1. Read text from each screenshot
2. Generate narration for each slide
3. Convert narration to audio
4. Create a narrated video
No manual narration text is required.
"""
)
with gr.Row():
with gr.Column():
images_input = gr.Files(
label="Upload Screenshots / Images",
file_types=["image"],
type="filepath"
)
height_input = gr.Dropdown(
label="Output Video Height",
choices=[360, 480, 720],
value=480
)
generate_button = gr.Button("Generate AI Narrated Video")
with gr.Column():
video_output = gr.Video(label="Generated Video")
audio_output = gr.Audio(label="First Slide Audio Preview")
status_output = gr.Textbox(label="Status")
narration_output = gr.Textbox(
label="Generated Narration Preview",
lines=12
)
generate_button.click(
fn=create_video_from_screenshots,
inputs=[
images_input,
height_input
],
outputs=[
video_output,
audio_output,
status_output,
narration_output
]
)
demo.queue()
demo.launch()