streamlit / app.py
zainmushtaq54's picture
Update app.py
9d06b5b verified
import streamlit as st
from PIL import Image
import numpy as np
import io
import base64
import time
import os
# Disable Streamlit config file creation - Critical for Hugging Face Spaces
os.environ["STREAMLIT_GLOBAL_DEVELOPMENT_MODE"] = "false"
os.environ["STREAMLIT_GLOBAL_RECURSION"] = "false"
# Page setup - Vertical layout for 9:16 aspect
st.set_page_config(
page_title="Free Hedra Clone",
page_icon="🤖",
layout="centered",
initial_sidebar_state="expanded"
)
st.title("🎭 Free AI Avatar Generator")
st.subheader("Audio-Driven Speaking Avatar (9:16 Portrait)")
# Initialize session state
if "frame" not in st.session_state:
st.session_state.frame = 0
if "avatar_img" not in st.session_state:
st.session_state.avatar_img = None
if "audio_data" not in st.session_state:
st.session_state.audio_data = None
if "audio_duration" not in st.session_state:
st.session_state.audio_duration = 5
if "mime_type" not in st.session_state:
st.session_state.mime_type = "audio/mp3"
# Create default 9:16 avatar
def create_default_avatar():
width, height = 720, 1280
img = Image.new('RGB', (width, height), color=(73, 109, 137))
return img
# Sidebar configuration
with st.sidebar:
st.header("🎛️ Configuration")
# Audio file upload
audio_file = st.file_uploader(
"Upload Audio File:",
type=["mp3", "wav"],
help="Upload an audio file (MP3 or WAV)"
)
# Process audio immediately after upload
if audio_file:
st.session_state.audio_data = audio_file.read()
if audio_file.name.endswith('.wav'):
st.session_state.mime_type = "audio/wav"
else:
st.session_state.mime_type = "audio/mp3"
# Set fixed duration since we can't reliably calculate it
st.session_state.audio_duration = 5
st.info("Audio duration set to 5 seconds (fixed)")
# Avatar image upload
uploaded_image = st.file_uploader(
"Upload Avatar Image:",
type=["png", "jpg", "jpeg"]
)
# Process image immediately after upload
if uploaded_image:
try:
img_data = uploaded_image.read()
st.session_state.avatar_img = Image.open(io.BytesIO(img_data))
except:
st.error("Error processing image")
# Animation options
st.markdown("---")
st.markdown("### Animation Options")
pulse_intensity = st.slider("Pulse Intensity", 0.1, 1.0, 0.3, 0.1)
animation_speed = st.slider("Animation Speed", 0.1, 2.0, 0.5, 0.1)
mouth_intensity = st.slider("Mouth Movement", 0.5, 2.0, 1.2, 0.1)
custom_duration = st.slider("Animation Duration (seconds)", 1, 30, 5, 1)
# Create speaking animation
def animate_avatar(image, intensity=0.3, anim_speed=0.5, mouth_factor=1.2):
try:
# Convert to RGB if needed
if image.mode != 'RGB':
image = image.convert('RGB')
img_array = np.array(image)
height, width, _ = img_array.shape
# Create pulsation effect
pulse = 1.0 + intensity * np.sin(st.session_state.frame * anim_speed)
# Apply stronger effect to bottom half
for y in range(height):
position_factor = y / height
if position_factor > 0.6: # Mouth region
region_intensity = intensity * mouth_factor
region_pulse = 1.0 + region_intensity * np.sin(st.session_state.frame * anim_speed * 1.5)
else:
region_pulse = pulse
img_array[y] = np.clip(img_array[y] * region_pulse, 0, 255).astype(np.uint8)
return Image.fromarray(img_array)
except Exception as e:
st.error(f"Animation error: {str(e)}")
return image
# Audio player component
def audio_player(audio_bytes, mime_type="audio/mp3"):
audio_base64 = base64.b64encode(audio_bytes).decode()
audio_html = f"""
<audio controls autoplay style="width: 100%">
<source src="data:{mime_type};base64,{audio_base64}" type="{mime_type}">
</audio>
"""
st.markdown(audio_html, unsafe_allow_html=True)
# Create default avatar if needed
if st.session_state.avatar_img is None:
st.session_state.avatar_img = create_default_avatar()
# Display avatar preview
st.subheader("Avatar Preview")
st.image(
st.session_state.avatar_img,
caption="Your Avatar",
use_column_width=True
)
# Generate button
if st.session_state.audio_data:
if st.button("Generate Speaking Avatar", type="primary", use_container_width=True):
# Create animation container
st.subheader("Speaking Avatar")
animation_placeholder = st.empty()
# Play audio
st.subheader("Audio Player")
audio_player(st.session_state.audio_data, st.session_state.mime_type)
# Create animation
st.subheader("Avatar Animation")
# Animate for custom duration
start_time = time.time()
frames = 0
while time.time() - start_time < custom_duration:
try:
animated_img = animate_avatar(
st.session_state.avatar_img,
pulse_intensity,
animation_speed,
mouth_intensity
)
animation_placeholder.image(animated_img, use_column_width=True)
st.session_state.frame += 1
frames += 1
time.sleep(0.1)
except Exception as e:
st.error(f"Error during animation: {str(e)}")
break
# Download audio button
st.subheader("Download Options")
st.download_button(
label="Download Audio",
data=st.session_state.audio_data,
file_name=f"avatar_audio.{'mp3' if st.session_state.mime_type == 'audio/mp3' else 'wav'}",
mime=st.session_state.mime_type,
use_container_width=True
)
else:
st.warning("Please upload an audio file to generate the speaking avatar")
# Footer
st.markdown("---")
st.caption("""
**Free Hedra Clone** - Audio-driven avatar animation
All processing done in memory - No file system writes
""")