Seedance-Free / app.py
ginipick's picture
Update app.py
dc72519 verified
raw
history blame
11.1 kB
#!/usr/bin/env python3
"""
AI Video Generator with Gradio
Single file application - app.py
"""
import os
import gradio as gr
import replicate
import base64
from PIL import Image
import io
import requests
from datetime import datetime
import tempfile
# API 토큰 설정
api_token = os.getenv("RAPI_TOKEN")
if api_token:
os.environ["REPLICATE_API_TOKEN"] = api_token
# 화면 비율 옵션
ASPECT_RATIOS = {
"16:9": "16:9 (YouTube, 일반 동영상)",
"4:3": "4:3 (전통적인 TV 형식)",
"1:1": "1:1 (Instagram 피드)",
"3:4": "3:4 (Instagram 포트레이트)",
"9:16": "9:16 (Instagram 릴스, TikTok)",
"21:9": "21:9 (시네마틱 와이드)",
"9:21": "9:21 (울트라 세로형)"
}
def update_prompt_placeholder(mode):
"""모드에 따라 프롬프트 플레이스홀더 업데이트"""
if mode == "텍스트 to 비디오":
return gr.update(placeholder="생성할 비디오를 설명해주세요.\n예: The sun rises slowly between tall buildings. [Ground-level follow shot] Bicycle tires roll over a dew-covered street at dawn.")
else:
return gr.update(placeholder="이미지를 어떻게 움직이게 할지 설명해주세요.\n예: Camera slowly zooms in while clouds move across the sky. The subject's hair gently moves in the wind.")
def update_image_input(mode):
"""모드에 따라 이미지 입력 표시/숨김"""
if mode == "이미지 to 비디오":
return gr.update(visible=True)
else:
return gr.update(visible=False)
def generate_video(mode, prompt, image, aspect_ratio, seed, api_key_input, progress=gr.Progress()):
"""비디오 생성 메인 함수"""
# API 토큰 확인
token = api_key_input or api_token
if not token:
return None, "❌ API 토큰이 필요합니다. 환경변수 RAPI_TOKEN을 설정하거나 API 키를 입력하세요."
os.environ["REPLICATE_API_TOKEN"] = token
# 입력 검증
if not prompt:
return None, "❌ 프롬프트를 입력해주세요."
if mode == "이미지 to 비디오" and image is None:
return None, "❌ 이미지를 업로드해주세요."
try:
progress(0, desc="비디오 생성 준비 중...")
# 입력 파라미터 설정
input_params = {
"prompt": prompt,
"duration": 5,
"resolution": "480p",
"aspect_ratio": aspect_ratio,
"seed": seed
}
# 이미지 to 비디오 모드
if mode == "이미지 to 비디오" and image is not None:
progress(0.1, desc="이미지 처리 중...")
# PIL Image를 base64로 변환
if isinstance(image, str): # 파일 경로인 경우
with Image.open(image) as img:
buffered = io.BytesIO()
img.save(buffered, format="PNG")
image_base64 = base64.b64encode(buffered.getvalue()).decode()
else: # PIL Image 객체인 경우
buffered = io.BytesIO()
image.save(buffered, format="PNG")
image_base64 = base64.b64encode(buffered.getvalue()).decode()
input_params["image"] = f"data:image/png;base64,{image_base64}"
progress(0.3, desc="Replicate API 호출 중...")
# Replicate 실행
output = replicate.run(
"bytedance/seedance-1-lite",
input=input_params
)
progress(0.7, desc="비디오 다운로드 중...")
# 비디오 데이터 가져오기
if hasattr(output, 'read'):
video_data = output.read()
else:
# URL인 경우 다운로드
response = requests.get(output)
video_data = response.content
# 임시 파일로 저장
with tempfile.NamedTemporaryFile(delete=False, suffix='.mp4') as tmp_file:
tmp_file.write(video_data)
video_path = tmp_file.name
# output.mp4로도 저장
with open("output.mp4", "wb") as file:
file.write(video_data)
progress(1.0, desc="완료!")
# 생성 정보
info = f"""✅ 비디오가 성공적으로 생성되었습니다!
📊 생성 정보:
- 모드: {mode}
- 화면 비율: {aspect_ratio}
- Seed: {seed}
- 재생 시간: 5초
- 해상도: 480p
- 파일: output.mp4"""
return video_path, info
except Exception as e:
error_msg = f"❌ 오류가 발생했습니다: {str(e)}"
return None, error_msg
# Gradio 인터페이스 생성
with gr.Blocks(title="AI Video Generator", theme=gr.themes.Soft()) as app:
gr.Markdown("""
# 🎬 AI Video Generator
**Replicate API**를 사용하여 텍스트나 이미지로부터 비디오를 생성합니다.
[![Powered by Replicate](https://img.shields.io/badge/Powered%20by-Replicate-blue)](https://replicate.com/)
""")
with gr.Row():
with gr.Column(scale=1):
# API 설정
with gr.Accordion("⚙️ API 설정", open=not bool(api_token)):
if api_token:
gr.Markdown("✅ API 토큰이 환경변수에서 로드되었습니다.")
api_key_input = gr.Textbox(
label="Replicate API Token (선택사항)",
type="password",
placeholder="환경변수를 덮어쓰려면 여기에 입력",
value=""
)
else:
gr.Markdown("⚠️ 환경변수 RAPI_TOKEN이 설정되지 않았습니다.")
api_key_input = gr.Textbox(
label="Replicate API Token (필수)",
type="password",
placeholder="Replicate API 토큰을 입력하세요",
value=""
)
# 생성 모드
mode = gr.Radio(
label="🎯 생성 모드",
choices=["텍스트 to 비디오", "이미지 to 비디오"],
value="텍스트 to 비디오"
)
# 이미지 업로드
image_input = gr.Image(
label="📷 이미지 업로드",
type="pil",
visible=False
)
# 화면 비율
aspect_ratio = gr.Dropdown(
label="📐 화면 비율",
choices=list(ASPECT_RATIOS.keys()),
value="16:9",
info="SNS 플랫폼에 최적화된 비율을 선택하세요"
)
# 비율 설명 표시
ratio_info = gr.Markdown(value=f"선택된 비율: {ASPECT_RATIOS['16:9']}")
# Seed 설정
seed = gr.Number(
label="🎲 랜덤 시드",
value=42,
precision=0,
info="동일한 시드값으로 동일한 결과를 재현할 수 있습니다"
)
# 고정 설정 표시
gr.Markdown("""
### 📋 고정 설정
- **재생 시간**: 5초
- **해상도**: 480p
""")
with gr.Column(scale=2):
# 프롬프트 입력
prompt = gr.Textbox(
label="✍️ 프롬프트",
lines=5,
placeholder="생성할 비디오를 설명해주세요.\n예: The sun rises slowly between tall buildings. [Ground-level follow shot] Bicycle tires roll over a dew-covered street at dawn."
)
# 생성 버튼
generate_btn = gr.Button("🎬 비디오 생성", variant="primary", size="lg")
# 결과 표시
with gr.Column():
output_video = gr.Video(
label="📹 생성된 비디오",
autoplay=True
)
output_info = gr.Textbox(
label="정보",
lines=8,
interactive=False
)
# 사용 방법
with gr.Accordion("📖 사용 방법", open=False):
gr.Markdown("""
### 설치 방법
1. **필요한 패키지 설치**:
```bash
pip install gradio replicate pillow requests
```
2. **환경변수 설정** (선택사항):
```bash
export RAPI_TOKEN="your-replicate-api-token"
```
3. **실행**:
```bash
python app.py
```
### 기능 설명
- **텍스트 to 비디오**: 텍스트 설명만으로 비디오를 생성합니다.
- **이미지 to 비디오**: 업로드한 이미지를 움직이는 비디오로 변환합니다.
- **화면 비율**: 다양한 SNS 플랫폼에 최적화된 비율을 선택할 수 있습니다.
- **Seed 값**: 동일한 시드값으로 동일한 결과를 재현할 수 있습니다.
### 프롬프트 작성 팁
- 구체적이고 상세한 설명을 사용하세요
- 카메라 움직임을 명시하세요 (예: zoom in, pan left, tracking shot)
- 조명과 분위기를 설명하세요 (예: golden hour, dramatic lighting)
- 움직임의 속도를 지정하세요 (예: slowly, rapidly, gently)
""")
# 예시
gr.Examples(
examples=[
["텍스트 to 비디오", "A serene lake at sunrise with mist rolling over the water. Camera slowly pans across the landscape as birds fly overhead.", None, "16:9", 42],
["텍스트 to 비디오", "Urban street scene at night with neon lights reflecting on wet pavement. People walking with umbrellas, camera tracking forward.", None, "9:16", 123],
["텍스트 to 비디오", "Close-up of a flower blooming in time-lapse, soft natural lighting, shallow depth of field.", None, "1:1", 789],
],
inputs=[mode, prompt, image_input, aspect_ratio, seed],
label="예시 프롬프트"
)
# 이벤트 핸들러
mode.change(
fn=update_prompt_placeholder,
inputs=[mode],
outputs=[prompt]
)
mode.change(
fn=update_image_input,
inputs=[mode],
outputs=[image_input]
)
aspect_ratio.change(
fn=lambda x: f"선택된 비율: {ASPECT_RATIOS[x]}",
inputs=[aspect_ratio],
outputs=[ratio_info]
)
generate_btn.click(
fn=generate_video,
inputs=[mode, prompt, image_input, aspect_ratio, seed, api_key_input],
outputs=[output_video, output_info]
)
# 앱 실행
if __name__ == "__main__":
app.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
inbrowser=True
)