shorts-maker / app.py
aheedsajid's picture
Update app.py
3c1c436 verified
raw
history blame contribute delete
No virus
8.49 kB
import gradio as gr
import json
import requests
import random
import subprocess
import os
import shutil
from uuid import uuid4
from gtts import gTTS
from moviepy.editor import VideoFileClip, AudioFileClip, concatenate_videoclips
# Function to fetch portrait videos from Pexels
def get_pexels_video(keyword):
headers = {"Authorization": "P310n0wKAlr05spr9UrtHeQXqouRlCL7r1jT6RQD55I1FIqyZt4fx7SL"}
params = {
"query": keyword,
"per_page": 10,
"orientation": "portrait",
"size": "large",
"min_width": 1080,
"min_height": 1920,
}
response = requests.get("https://api.pexels.com/videos/search", headers=headers, params=params)
if response.status_code == 200:
videos = response.json()['videos']
portrait_videos = [video for video in videos if video['width'] < video['height']] # Check for portrait videos
if portrait_videos:
selected_video = random.choice(portrait_videos)
return selected_video['video_files'][0]['link']
else:
print(f"No portrait video found on Pexels for {keyword}")
return None
else:
print("Failed to fetch video from Pexels")
return None
# Function to generate voiceover using gTTS
def generate_voiceover(text, filename, speed=1.0):
tts = gTTS(text=text, lang='en', slow=False)
tts.save(filename)
# Function to generate video content and create data.json
def generate_video_content(topic, session_dir):
api_key = "AIzaSyDXMC0Sq9PmdgBgqupwin39IW0taR9aB34"
url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-1.0-pro:generateContent?key={api_key}"
payload = {
"contents": [
{
"role": "user",
"parts": [
{
"text": f"Create a short one-minute video on {topic} and the keyword under each scene will be used for pexels search query, so give a precise keyword for each scene and create very short scenes. Give up to 15 related tags. Give response in this json format and give just the json: {{ \"title\": \"\", \"description\": \"\", \"video\": [ {{ \"scene\": \"\", \"keyword\": \"\", \"voiceover\": \"\" }} ], \"tags\": [\"\", \"\", \"\"] }}"
}
]
}
],
"generationConfig": {
"temperature": 0.9,
"topK": 1,
"topP": 1,
"maxOutputTokens": 2048,
"stopSequences": []
},
"safetySettings": [
{
"category": "HARM_CATEGORY_HARASSMENT",
"threshold": "BLOCK_NONE"
},
{
"category": "HARM_CATEGORY_HATE_SPEECH",
"threshold": "BLOCK_NONE"
},
{
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
"threshold": "BLOCK_NONE"
},
{
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
"threshold": "BLOCK_NONE"
}
]
}
headers = {
'Content-Type': 'application/json'
}
response = requests.post(url, headers=headers, json=payload)
if response.status_code == 200:
try:
with open(os.path.join(session_dir, "data.json"), "w") as f:
f.write(response.json()["candidates"][0]["content"]["parts"][0]["text"])
print("Data saved successfully as data.json")
except KeyError:
print("Error: Could not find the desired data in the response")
else:
print("Error occurred while fetching data")
# Function to concatenate videos using FFmpeg
def concatenate_videos_ffmpeg(scene_videos, output_filename, session_dir):
# Write scene_videos to temporary files
temp_filenames = []
for i, video_clip in enumerate(scene_videos):
temp_filename = os.path.join(session_dir, f'temp_video_{i}.mp4')
video_clip.write_videofile(temp_filename, codec="libx264", audio_codec="aac", temp_audiofile="temp-audio.m4a", remove_temp=True, verbose=False)
temp_filenames.append(temp_filename)
# Create a text file containing the list of videos to concatenate
video_list_path = os.path.join(session_dir, 'video_list.txt')
with open(video_list_path, 'w') as f:
for temp_filename in temp_filenames:
f.write(f"file '{temp_filename}'\n")
# Use FFmpeg to concatenate the videos without resizing
subprocess.run(['ffmpeg', '-f', 'concat', '-safe', '0', '-i', video_list_path, '-vf', 'scale=1080:1920', '-c:a', 'aac', '-b:a', '256k', '-c:v', 'libx264', '-preset', 'medium', output_filename])
# Clean up the video list file and temporary video files
os.remove(video_list_path)
for temp_filename in temp_filenames:
os.remove(temp_filename)
def process_video(topic, session_id):
session_dir = os.path.join("sessions", session_id)
os.makedirs(session_dir, exist_ok=True)
try:
generate_video_content(topic, session_dir)
with open(os.path.join(session_dir, 'data.json'), 'r') as f:
data = json.load(f)
title = data.get('title', '')
description = data.get('description', '')
tags = ", ".join(data.get('tags', []))
scene_info = []
for scene in data['video']:
voiceover_text = scene['voiceover']
voiceover_filename = os.path.join(session_dir, f"{scene['scene']}_voiceover.mp3")
generate_voiceover(voiceover_text, voiceover_filename)
voiceover_duration = AudioFileClip(voiceover_filename).duration
scene_info.append({'scene': scene['scene'], 'voiceover_filename': voiceover_filename, 'voiceover_duration': voiceover_duration, 'keyword': scene['keyword']})
for scene in scene_info:
video_urls = [get_pexels_video(scene['keyword']) for _ in range(3)] # Fetch 3 videos for each scene
video_filenames = []
for video_url in video_urls:
if video_url:
# Download video file locally
video_filename = os.path.join(session_dir, f"{scene['scene']}_video_{len(video_filenames)}.mp4")
with open(video_filename, 'wb') as f:
f.write(requests.get(video_url).content)
video_filenames.append(video_filename)
scene['video_filenames'] = video_filenames
scene_videos = []
for scene in scene_info:
video_clips = []
for video_filename in scene['video_filenames']:
video_clip = VideoFileClip(video_filename)
video_clips.append(video_clip)
concatenated_clip = concatenate_videoclips(video_clips)
concatenated_clip = concatenated_clip.subclip(0, scene['voiceover_duration']) # Trim the concatenated clip to match the voiceover duration
concatenated_clip = concatenated_clip.set_audio(AudioFileClip(scene['voiceover_filename'])) # Add audio to video clip
scene_videos.append(concatenated_clip)
final_filename = os.path.join(session_dir, title + '.mp4')
concatenate_videos_ffmpeg(scene_videos, final_filename, session_dir)
return final_filename, title, description, tags, session_id # Return session ID as output
finally:
# Clean up downloaded files and temporary directory
shutil.rmtree(session_dir, ignore_errors=True)
def generate_session_id():
return str(uuid4())
with gr.Blocks() as iface:
gr.Interface(
fn=process_video,
inputs=[
gr.Textbox(lines=2, label="Enter your topic", placeholder="Enter your topic here..."),
gr.State(generate_session_id) # State input for unique session ID
],
outputs=[
"video", "text", "text", "text", gr.State() # State output for unique session ID
],
title="Text to Short Video - Invideo AI Clone",
description="Generate a free short video just with a word or topic. Best for YouTube, Reels and TikTok. This is a prototype. If you want better software, please inbox or email me at aheedsajid@gmail.com and do like this space. [Click here to Donate](https://nowpayments.io/donation/aheed) <b>Want to make long landscape videos?</b> [Click here](https://huggingface.co/spaces/aheedsajid/invideo-clone)"
)
iface.launch()