Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| import json | |
| import requests | |
| import random | |
| import subprocess | |
| import os | |
| 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='pt', slow=False) | |
| tts.save(filename) | |
| # Function to generate video content and create data.json | |
| def generate_video_content(topic): | |
| api_key = "AIzaSyAtVhAjcUi7tHYnYZTWA4_L2ExvsAeupQY" | |
| 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 upto 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("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): | |
| # Write scene_videos to temporary files | |
| temp_filenames = [] | |
| for i, video_clip in enumerate(scene_videos): | |
| temp_filename = 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 | |
| with open('video_list.txt', '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.txt', '-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.txt') | |
| for temp_filename in temp_filenames: | |
| os.remove(temp_filename) | |
| def process_video(topic): | |
| generate_video_content(topic) | |
| with open('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 = 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']}) | |
| # Step 3: Fetch videos from Pexels based on portrait orientation | |
| 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 = 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 | |
| # Step 4: Create Scene Videos without resizing | |
| 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) | |
| # Trim the concatenated clip to match the voiceover duration | |
| concatenated_clip = concatenated_clip.subclip(0, scene['voiceover_duration']) | |
| # Add audio to video clip | |
| concatenated_clip = concatenated_clip.set_audio(AudioFileClip(scene['voiceover_filename'])) | |
| scene_videos.append(concatenated_clip) | |
| final_filename = title + '.mp4' | |
| concatenate_videos_ffmpeg(scene_videos, final_filename) | |
| # Clean up downloaded files | |
| for scene in scene_info: | |
| os.remove(scene['voiceover_filename']) | |
| for video_filename in scene['video_filenames']: | |
| os.remove(video_filename) | |
| return final_filename, title, description, tags | |
| iface = gr.Interface( | |
| fn=process_video, | |
| inputs="text", | |
| outputs=["video", "text", "text", "text"], | |
| css="footer {visibility: hidden}", | |
| 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)", | |
| title="Text to Short Video - Invideo AI Clone" | |
| ) | |
| iface.launch() |