|
import streamlit as st |
|
import requests |
|
import time |
|
|
|
|
|
GENERATE_VIDEO_URL = "https://api.heygen.com/v2/video/generate" |
|
VIDEO_STATUS_URL = "https://api.heygen.com/v1/video_status.get" |
|
|
|
|
|
HEADERS = { |
|
"accept": "application/json", |
|
"content-type": "application/json", |
|
"x-api-key": "MWE1NDAyOWU1ZDkyNDhlZmIzM2MzYzY3ZmU1MGNiZDQtMTc0NDAxMjI0MA==" |
|
} |
|
|
|
st.title("HeyGen Video Generator π¬") |
|
|
|
|
|
st.subheader("Follow these simple steps:") |
|
|
|
st.caption("""1οΈβ£ Enter Video Details β Provide a title.\n |
|
2οΈβ£ Add Scenes β Click "β Add Scene" to add multiple scenes, each with:\n |
|
|
|
Avatar ID & Style\n |
|
Voice ID & Script Input\n |
|
Emotion & Positioning\n |
|
3οΈβ£ Generate Video β Click "π Generate Video" to start processing.\n |
|
4οΈβ£ Track Progress β The app will automatically check the status every half minute.\n |
|
5οΈβ£ Watch & Reset β Once the video is ready, it will be displayed. Click "π Reset All" to start over.\n\n""") |
|
|
|
|
|
|
|
if "scenes" not in st.session_state: |
|
st.session_state.scenes = [] |
|
if "video_id" not in st.session_state: |
|
st.session_state.video_id = None |
|
if "video_status" not in st.session_state: |
|
st.session_state.video_status = None |
|
|
|
|
|
def reset_all(): |
|
st.session_state.scenes = [] |
|
st.session_state.video_id = None |
|
st.session_state.video_status = None |
|
st.rerun() |
|
|
|
|
|
if st.button("β Add Scene",type="primary"): |
|
st.session_state.scenes.append({ |
|
"avatar_id": "", |
|
"avatar_style": "normal", |
|
"offset_x": 0, |
|
"offset_y": 0, |
|
"voice_id": "", |
|
"input_text": "", |
|
"emotion": "Friendly" |
|
}) |
|
|
|
|
|
title = st.text_input("Title", placeholder="Enter video title") |
|
|
|
|
|
for i, scene in enumerate(st.session_state.scenes): |
|
st.subheader(f"π¬ Scene {i+1}") |
|
|
|
|
|
scene["avatar_id"] = st.text_input(f"Avatar ID (Scene {i+1})", scene["avatar_id"], key=f"avatar_id_{i}") |
|
scene["avatar_style"] = st.selectbox(f"Avatar Style (Scene {i+1})", ["normal", "circle", "closeUp"], index=0, key=f"avatar_style_{i}") |
|
scene["offset_x"] = st.number_input(f"Offset X (Scene {i+1})", value=scene["offset_x"], key=f"offset_x_{i}") |
|
scene["offset_y"] = st.number_input(f"Offset Y (Scene {i+1})", value=scene["offset_y"], key=f"offset_y_{i}") |
|
scene["voice_id"] = st.text_input(f"Voice ID (Scene {i+1})", scene["voice_id"], key=f"voice_id_{i}") |
|
scene["input_script"] = st.text_area(f"Input script (Scene {i+1})", scene["input_text"], key=f"input_text_{i}") |
|
scene["emotion"] = st.selectbox(f"Voice emotion (Scene {i+1})", ["Friendly", "Serious", "Excited", "Soothing", "Broadcaster"], index=0, key=f"emotion_{i}") |
|
|
|
|
|
if st.button("π Generate Video"): |
|
if not title or not st.session_state.scenes: |
|
st.error("β Please fill in all required fields and add at least one scene!") |
|
else: |
|
|
|
video_inputs = [ |
|
{ |
|
"character": { |
|
"type": "avatar", |
|
"avatar_id": scene["avatar_id"], |
|
"scale": 1, |
|
"avatar_style": scene["avatar_style"], |
|
"offset": {"x": scene["offset_x"], "y": scene["offset_y"]}, |
|
}, |
|
"voice": { |
|
"type": "text", |
|
"voice_id": scene["voice_id"], |
|
"input_text": scene["input_script"], |
|
"speed": 1, |
|
"emotion": scene["emotion"], |
|
}, |
|
} |
|
for scene in st.session_state.scenes |
|
] |
|
|
|
|
|
payload = { |
|
"caption": False, |
|
"title": title, |
|
"dimension": {"width": 1280, "height": 720}, |
|
"video_inputs": video_inputs, |
|
} |
|
|
|
|
|
response = requests.post(GENERATE_VIDEO_URL, json=payload, headers=HEADERS) |
|
|
|
|
|
if response.status_code == 200: |
|
data = response.json() |
|
if data.get("error") is None: |
|
st.session_state.video_id = data["data"]["video_id"] |
|
st.success(f"β
Video generation started! Video ID: {st.session_state.video_id}") |
|
else: |
|
st.error(f"β API Error: {data['error']}") |
|
else: |
|
st.error(f"β Request Failed! Status Code: {response.status_code}") |
|
|
|
|
|
def check_video_status(video_id): |
|
while True: |
|
params = {"video_id": video_id} |
|
response = requests.get(VIDEO_STATUS_URL, headers=HEADERS, params=params) |
|
|
|
if response.status_code == 200: |
|
data = response.json() |
|
status = data["data"]["status"] |
|
st.session_state.video_status = status |
|
|
|
|
|
if status in ["processing", "waiting", "pending"]: |
|
st.info(f"β³ Video is still {status}. Please wait...") |
|
elif status == "failed": |
|
error_info = data["data"].get("error", {}) |
|
error_message = error_info.get("message", "An unknown error occurred.") |
|
st.error(f"β Video generation failed: {error_message}") |
|
return |
|
elif status == "completed": |
|
video_url = data["data"].get("video_url") |
|
if video_url: |
|
st.success("π Video is ready! Watch it below:") |
|
st.video(video_url) |
|
else: |
|
st.warning("β οΈ Video completed but URL is missing.") |
|
return |
|
else: |
|
st.error(f"β Request Failed! Status Code: {response.status_code}") |
|
return |
|
|
|
time.sleep(30) |
|
|
|
|
|
if st.session_state.video_id: |
|
check_video_status(st.session_state.video_id) |
|
|
|
|
|
if st.session_state.video_status == "completed": |
|
if st.button("π Reset All"): |
|
reset_all() |
|
|