gif-test / app.py
taeyeol's picture
Update app.py
3f777d6 verified
import os
import tempfile
import gradio as gr
import numpy as np # (ν•„μš”ν•˜λ‹€λ©΄)
import imageio
from PIL import Image
import moviepy.editor as me # MoviePy 전체 μž„ν¬νŠΈ
LOGS = []
def log_step(message: str):
LOGS.append(message)
print(message)
return "\n".join(LOGS)
def parse_time_str(time_str: str):
try:
parts = time_str.strip().split(":")
if len(parts) == 3:
h, m, s = parts
return int(h)*3600 + int(m)*60 + float(s)
elif len(parts) == 2:
m, s = parts
return int(m)*60 + float(s)
elif len(parts) == 1:
return float(parts[0])
else:
return 0.0
except:
return 0.0
def upload_video(video_file):
if video_file is None:
return None, "μ—…λ‘œλ“œλœ 파일이 μ—†μŠ΅λ‹ˆλ‹€.", None, log_step("[단계 1] μ—…λ‘œλ“œλœ 파일이 μ—†μŠ΅λ‹ˆλ‹€.")
temp_dir = tempfile.mkdtemp()
temp_video_path = os.path.join(temp_dir, video_file.name)
with open(temp_video_path, "wb") as f:
f.write(video_file.read())
try:
clip = me.VideoFileClip(temp_video_path)
duration_sec = clip.duration
msg = f"μž¬μƒ μ‹œκ°„: {duration_sec:.2f}초"
log_out = log_step("[단계 1] λΉ„λ””μ˜€ μ—…λ‘œλ“œ 및 μž¬μƒ 확인 μ™„λ£Œ")
return temp_video_path, msg, duration_sec, log_out
except Exception as e:
err_log = log_step(f"[단계 1] μ—…λ‘œλ“œ μ—λŸ¬: {e}")
return None, f"μ˜μƒ 처리 μ—λŸ¬: {e}", None, err_log
def generate_thumbnails(video_path, start_time_str, end_time_str, log_history):
log_step("[단계 4] μ‹œμž‘/끝 μ‹œκ°„ μž…λ ₯ μ™„λ£Œ")
start_s = parse_time_str(start_time_str)
end_s = parse_time_str(end_time_str)
if not video_path:
return None, None, log_step("[단계 5] λΉ„λ””μ˜€ λ―Έμ—…λ‘œλ“œ β†’ 썸넀일 λΆˆκ°€")
try:
clip = me.VideoFileClip(video_path)
duration = clip.duration
if start_s < 0: start_s = 0
if end_s > duration: end_s = duration
if start_s > end_s: start_s, end_s = end_s, start_s
thumb1 = clip.get_frame(start_s)
thumb2 = clip.get_frame(end_s)
thumb1_img = Image.fromarray(thumb1)
thumb2_img = Image.fromarray(thumb2)
log_out = log_step("[단계 5] 썸넀일 생성 μ™„λ£Œ")
return thumb1_img, thumb2_img, log_out
except Exception as e:
err_log = log_step(f"[단계 5] 썸넀일 생성 μ—λŸ¬: {e}")
return None, None, err_log
def create_gif(video_path, start_time_str, end_time_str, resolution, fps, speed, loop_count, log_history):
if not video_path:
return None, None, log_step("[단계 10] GIF 생성 μ‹€νŒ¨: λΉ„λ””μ˜€κ°€ μ—…λ‘œλ“œλ˜μ§€ μ•ŠμŒ.")
start_s = parse_time_str(start_time_str)
end_s = parse_time_str(end_time_str)
try:
clip = me.VideoFileClip(video_path)
duration = clip.duration
if start_s < 0: start_s = 0
if end_s > duration: end_s = duration
if start_s > end_s: start_s, end_s = end_s, start_s
subclip = clip.subclip(start_s, end_s)
if speed != 1.0:
subclip = subclip.fx(me.vfx.speedx, speed)
# 해상도 쑰절(κΈ°λ³Έ 100%)
w, h = subclip.size
if resolution < 100:
scale = resolution / 100.0
subclip = subclip.resize((int(w * scale), int(h * scale)))
# FPS
fps = int(fps) if fps > 0 else None
# 반볡 횟수
loop = loop_count # 0=λ¬΄ν•œ
temp_dir = tempfile.mkdtemp()
gif_path = os.path.join(temp_dir, "output.gif")
subclip.write_gif(gif_path, fps=fps, loop=loop)
log_out1 = log_step("[단계 10] 'GIF 생성' λ²„νŠΌ 클릭됨")
log_out2 = log_step("[단계 11] GIF 미리보기 생성 μ™„λ£Œ")
log_out3 = log_step("[단계 12] GIF λ‹€μš΄λ‘œλ“œ μ€€λΉ„ μ™„λ£Œ")
preview_img = Image.open(gif_path)
return preview_img, gif_path, "\n".join([log_out1, log_out2, log_out3])
except Exception as e:
err_log = log_step(f"[단계 10~12] GIF 생성 μ—λŸ¬: {e}")
return None, None, err_log
def build_interface():
with gr.Blocks() as demo:
gr.Markdown("## λ™μ˜μƒμ„ GIF둜 λ³€ν™˜ν•˜κΈ°")
video_upload = gr.File(label="λΉ„λ””μ˜€ μ—…λ‘œλ“œ", file_types=["video"])
video_player = gr.Video(label="μ—…λ‘œλ“œλœ μ˜μƒ 미리보기")
video_info = gr.Textbox(label="μ—…λ‘œλ“œλœ μ˜μƒ 정보", interactive=False)
video_path_state = gr.State()
video_duration_state = gr.State()
log_box = gr.Textbox(label="둜그", interactive=False, lines=10)
with gr.Row():
start_time = gr.Textbox(label="μ‹œμž‘ μ‹œκ°„ (HH:MM:SS/ MM:SS)", value="0:00")
end_time = gr.Textbox(label="끝 μ‹œκ°„ (HH:MM:SS/ MM:SS)", value="0:10")
with gr.Row():
thumb1 = gr.Image(label="μ‹œμž‘ 지점 썸넀일")
thumb2 = gr.Image(label="끝 지점 썸넀일")
resolution_slider = gr.Slider(1, 100, 100, step=1, label="해상도(%)")
fps_slider = gr.Slider(1, 60, 30, step=1, label="FPS")
speed_slider = gr.Slider(0.1, 3.0, 1.0, step=0.1, label="μž¬μƒ 속도(λ°°)")
loop_slider = gr.Slider(0, 10, 0, step=1, label="GIF 반볡 횟수 (0=λ¬΄ν•œ)")
generate_gif_btn = gr.Button("GIF 생성")
gif_preview = gr.Image(label="GIF 미리보기")
gif_download = gr.File(label="GIF λ‹€μš΄λ‘œλ“œ(ν΄λ¦­ν•˜μ—¬ μ €μž₯)")
# 이벀트 바인딩
video_upload.change(
fn=upload_video,
inputs=video_upload,
outputs=[video_path_state, video_info, video_duration_state, log_box]
)
start_time.change(
fn=generate_thumbnails,
inputs=[video_path_state, start_time, end_time, log_box],
outputs=[thumb1, thumb2, log_box]
)
end_time.change(
fn=generate_thumbnails,
inputs=[video_path_state, start_time, end_time, log_box],
outputs=[thumb1, thumb2, log_box]
)
generate_gif_btn.click(
fn=create_gif,
inputs=[video_path_state, start_time, end_time, resolution_slider, fps_slider, speed_slider, loop_slider, log_box],
outputs=[gif_preview, gif_download, log_box]
)
return demo
if __name__ == "__main__":
interface = build_interface()
interface.launch()