Spaces:
Runtime error
Runtime error
Martin Tomov
commited on
Commit
โข
663e3d1
1
Parent(s):
e3b186c
Update app.py
Browse files
app.py
CHANGED
@@ -1,4 +1,3 @@
|
|
1 |
-
from dotenv import load_dotenv
|
2 |
from IPython.display import display, Image, Audio
|
3 |
from moviepy.editor import VideoFileClip, AudioFileClip
|
4 |
from moviepy.audio.io.AudioFileClip import AudioFileClip
|
@@ -12,9 +11,6 @@ import requests
|
|
12 |
import streamlit as st
|
13 |
import tempfile
|
14 |
|
15 |
-
# Load environment variables from .env.local
|
16 |
-
load_dotenv('.env.local')
|
17 |
-
|
18 |
## 1. Turn video into frames
|
19 |
def video_to_frames(video_file):
|
20 |
# Save the uploaded video file to a temporary file
|
@@ -73,20 +69,14 @@ def text_to_audio(text, api_key, voice):
|
|
73 |
},
|
74 |
)
|
75 |
|
76 |
-
# Check if the request was successful
|
77 |
if response.status_code != 200:
|
78 |
raise Exception("Request failed with status code")
|
79 |
|
80 |
-
# Create an in-memory bytes buffer
|
81 |
audio_bytes_io = io.BytesIO()
|
82 |
-
# Write audio data to the in-memory bytes buffer
|
83 |
for chunk in response.iter_content(chunk_size=1024*1024):
|
84 |
audio_bytes_io.write(chunk)
|
85 |
-
|
86 |
-
# Important: Seek to the start of the BytesIO buffer before returning
|
87 |
audio_bytes_io.seek(0)
|
88 |
|
89 |
-
# Save audio to a temporary file
|
90 |
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmpfile:
|
91 |
for chunk in response.iter_content(chunk_size=1024*1024):
|
92 |
tmpfile.write(chunk)
|
@@ -97,73 +87,51 @@ def text_to_audio(text, api_key, voice):
|
|
97 |
## 4. Merge videos & audio
|
98 |
def merge_audio_video(video_filename, audio_filename, output_filename):
|
99 |
print("Merging audio and video ...")
|
100 |
-
# Load the video file
|
101 |
video_clip = VideoFileClip(video_filename)
|
102 |
-
# Load the audio file
|
103 |
audio_clip = AudioFileClip(audio_filename)
|
104 |
-
# Set the audio of the video clip as the audio file
|
105 |
final_clip = video_clip.set_audio(audio_clip)
|
106 |
-
# Write the result to a file (without audio)
|
107 |
final_clip.write_videofile(output_filename, codec='libx264', audio_codec="aac")
|
108 |
-
# Close the clips
|
109 |
video_clip.close()
|
110 |
audio_clip.close()
|
111 |
|
112 |
-
# Return the path to the new video file
|
113 |
return output_filename
|
114 |
|
115 |
## 5. Streamlit UI
|
116 |
def main():
|
117 |
st.set_page_config(page_title="AI Voiceover", page_icon="๐ฎ")
|
118 |
st.title("GPT4V AI Voiceover ๐ฅ๐ฎ")
|
119 |
-
st.
|
120 |
|
121 |
-
# Retrieve the OpenAI API key from environment
|
122 |
-
openai_key = os.getenv('OPENAI_API_KEY')
|
123 |
if not openai_key:
|
124 |
-
st.error("OpenAI API key
|
125 |
-
return
|
126 |
-
|
127 |
uploaded_file = st.file_uploader("Select a video file", type=["mp4", "avi"])
|
128 |
|
129 |
option = st.selectbox(
|
130 |
'Choose the voice you want',
|
131 |
('Female Voice', 'Male Voice'))
|
132 |
-
classify = ''
|
133 |
-
if option == 'Male Voice':
|
134 |
-
classify = 'alloy'
|
135 |
-
elif option == 'Female Voice':
|
136 |
-
classify = 'nova'
|
137 |
|
138 |
if uploaded_file is not None:
|
139 |
st.video(uploaded_file)
|
140 |
p = 'Generate a short voiceover script for the video, matching the content with the video scenes. The style should be...'
|
141 |
-
|
142 |
-
prompt = st.text_area(
|
143 |
-
"Prompt", value=p
|
144 |
-
)
|
145 |
|
146 |
if st.button("START PROCESSING", type="primary") and uploaded_file is not None:
|
147 |
with st.spinner("Video is being processed..."):
|
148 |
base64Frame, video_filename, video_duration = video_to_frames(uploaded_file)
|
149 |
est_word_count = video_duration * 4
|
150 |
-
final_prompt =
|
151 |
text = frames_to_story(base64Frame, final_prompt, openai_key)
|
152 |
st.write(text)
|
153 |
-
# Generate audio from text
|
154 |
audio_filename, audio_bytes_io = text_to_audio(text, openai_key, classify)
|
155 |
-
# Merge audio and video
|
156 |
output_video_filename = os.path.splitext(video_filename)[0] + "_output.mp4"
|
157 |
-
|
158 |
final_video_filename = merge_audio_video(video_filename, audio_filename, output_video_filename)
|
159 |
-
|
160 |
-
# Display the result
|
161 |
st.video(final_video_filename)
|
162 |
-
|
163 |
-
# Clean up the temporary files
|
164 |
os.unlink(video_filename)
|
165 |
os.unlink(audio_filename)
|
166 |
os.unlink(final_video_filename)
|
167 |
|
168 |
if __name__ == "__main__":
|
169 |
-
main()
|
|
|
|
|
1 |
from IPython.display import display, Image, Audio
|
2 |
from moviepy.editor import VideoFileClip, AudioFileClip
|
3 |
from moviepy.audio.io.AudioFileClip import AudioFileClip
|
|
|
11 |
import streamlit as st
|
12 |
import tempfile
|
13 |
|
|
|
|
|
|
|
14 |
## 1. Turn video into frames
|
15 |
def video_to_frames(video_file):
|
16 |
# Save the uploaded video file to a temporary file
|
|
|
69 |
},
|
70 |
)
|
71 |
|
|
|
72 |
if response.status_code != 200:
|
73 |
raise Exception("Request failed with status code")
|
74 |
|
|
|
75 |
audio_bytes_io = io.BytesIO()
|
|
|
76 |
for chunk in response.iter_content(chunk_size=1024*1024):
|
77 |
audio_bytes_io.write(chunk)
|
|
|
|
|
78 |
audio_bytes_io.seek(0)
|
79 |
|
|
|
80 |
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmpfile:
|
81 |
for chunk in response.iter_content(chunk_size=1024*1024):
|
82 |
tmpfile.write(chunk)
|
|
|
87 |
## 4. Merge videos & audio
|
88 |
def merge_audio_video(video_filename, audio_filename, output_filename):
|
89 |
print("Merging audio and video ...")
|
|
|
90 |
video_clip = VideoFileClip(video_filename)
|
|
|
91 |
audio_clip = AudioFileClip(audio_filename)
|
|
|
92 |
final_clip = video_clip.set_audio(audio_clip)
|
|
|
93 |
final_clip.write_videofile(output_filename, codec='libx264', audio_codec="aac")
|
|
|
94 |
video_clip.close()
|
95 |
audio_clip.close()
|
96 |
|
|
|
97 |
return output_filename
|
98 |
|
99 |
## 5. Streamlit UI
|
100 |
def main():
|
101 |
st.set_page_config(page_title="AI Voiceover", page_icon="๐ฎ")
|
102 |
st.title("GPT4V AI Voiceover ๐ฅ๐ฎ")
|
103 |
+
openai_key = st.text_input("Enter your OpenAI API key")
|
104 |
|
|
|
|
|
105 |
if not openai_key:
|
106 |
+
st.error("Please enter your OpenAI API key.")
|
107 |
+
return
|
108 |
+
|
109 |
uploaded_file = st.file_uploader("Select a video file", type=["mp4", "avi"])
|
110 |
|
111 |
option = st.selectbox(
|
112 |
'Choose the voice you want',
|
113 |
('Female Voice', 'Male Voice'))
|
114 |
+
classify = 'alloy' if option == 'Male Voice' else 'nova'
|
|
|
|
|
|
|
|
|
115 |
|
116 |
if uploaded_file is not None:
|
117 |
st.video(uploaded_file)
|
118 |
p = 'Generate a short voiceover script for the video, matching the content with the video scenes. The style should be...'
|
119 |
+
prompt = st.text_area("Prompt", value=p)
|
|
|
|
|
|
|
120 |
|
121 |
if st.button("START PROCESSING", type="primary") and uploaded_file is not None:
|
122 |
with st.spinner("Video is being processed..."):
|
123 |
base64Frame, video_filename, video_duration = video_to_frames(uploaded_file)
|
124 |
est_word_count = video_duration * 4
|
125 |
+
final_prompt = f"{prompt}(This video is ONLY {video_duration} seconds long. So make sure the voiceover MUST be able to be explained in less than {est_word_count} words. Ignore and don't generate anything else than the script that you'll use to voice over the video.)"
|
126 |
text = frames_to_story(base64Frame, final_prompt, openai_key)
|
127 |
st.write(text)
|
|
|
128 |
audio_filename, audio_bytes_io = text_to_audio(text, openai_key, classify)
|
|
|
129 |
output_video_filename = os.path.splitext(video_filename)[0] + "_output.mp4"
|
|
|
130 |
final_video_filename = merge_audio_video(video_filename, audio_filename, output_video_filename)
|
|
|
|
|
131 |
st.video(final_video_filename)
|
|
|
|
|
132 |
os.unlink(video_filename)
|
133 |
os.unlink(audio_filename)
|
134 |
os.unlink(final_video_filename)
|
135 |
|
136 |
if __name__ == "__main__":
|
137 |
+
main()
|