|
import os |
|
import uuid |
|
import moviepy.editor as mp |
|
from PIL import Image |
|
import gradio as gr |
|
|
|
|
|
|
|
|
|
global_logs = [] |
|
|
|
def add_log(message: str): |
|
""" |
|
๋ก๊ทธ ๋ฉ์์ง๋ฅผ ์ ์ฅํ๋ ํจ์ |
|
""" |
|
global_logs.append(message) |
|
print(message) |
|
|
|
def parse_time_to_seconds(time_str: str): |
|
""" |
|
์:๋ถ:์ด ํํ์ ๋ฌธ์์ด(์: '00:00:10' ๋๋ '00:01:02')์ ์ด ๋จ์(float)๋ก ๋ณํ |
|
""" |
|
try: |
|
h, m, s = time_str.split(":") |
|
total_seconds = int(h) * 3600 + int(m) * 60 + float(s) |
|
return total_seconds |
|
except Exception: |
|
return 0.0 |
|
|
|
def generate_thumbnail(video_clip, time_point): |
|
""" |
|
ํน์ ์์ ์ ํ๋ ์์ ์ธ๋ค์ผ๋ก ์์ฑํ์ฌ PIL.Image๋ก ๋ฐํ |
|
""" |
|
try: |
|
frame = video_clip.get_frame(time_point) |
|
thumbnail_img = Image.fromarray(frame) |
|
return thumbnail_img |
|
except Exception as e: |
|
add_log(f"[ERROR] ์ธ๋ค์ผ ์์ฑ ์คํจ: {e}") |
|
frame = video_clip.get_frame(0) |
|
thumbnail_img = Image.fromarray(frame) |
|
return thumbnail_img |
|
|
|
def process_video(video, |
|
start_time_str, |
|
end_time_str, |
|
resolution_factor, |
|
frame_rate_factor, |
|
speed_factor, |
|
repeat_count): |
|
""" |
|
๋์์์ GIF๋ก ๋ณํํ๋ ํจ์ |
|
""" |
|
global global_logs |
|
global_logs = [] |
|
|
|
add_log("[LOG 1] ๋น๋์ค ์
๋ก๋ ๋ฐ ์ฒ๋ฆฌ ์์") |
|
|
|
|
|
video_path = video if isinstance(video, str) else video.name |
|
|
|
add_log("[LOG 2] ๋น๋์ค ๋ก๋ ์ค...") |
|
try: |
|
input_video = mp.VideoFileClip(video_path) |
|
except Exception as e: |
|
add_log(f"[ERROR] ๋น๋์ค ๋ก๋ ์คํจ: {e}") |
|
return None, None, "\n".join(global_logs) |
|
|
|
duration = input_video.duration |
|
add_log(f"[LOG 3] ์
๋ก๋๋ ์์์ ์ฌ์์๊ฐ: {duration:.2f}์ด") |
|
|
|
add_log("[LOG 4] ์์/๋ ์๊ฐ ํ์ฑ ์ค...") |
|
start_sec = parse_time_to_seconds(start_time_str) |
|
end_sec = parse_time_to_seconds(end_time_str) |
|
|
|
|
|
if start_sec < 0: |
|
start_sec = 0 |
|
if end_sec <= 0 or end_sec > duration: |
|
end_sec = duration |
|
if start_sec >= end_sec: |
|
start_sec = 0 |
|
end_sec = duration |
|
|
|
add_log(f"[LOG 5] ์ ์ฉ๋ ์์ ์๊ฐ: {start_sec}์ด, ์ข
๋ฃ ์๊ฐ: {end_sec}์ด") |
|
|
|
add_log("[LOG 6] ์์ ์๋ฅด๊ธฐ ์์
์งํ...") |
|
clip = input_video.subclip(start_sec, end_sec) |
|
|
|
|
|
if abs(resolution_factor - 1.0) > 1e-3: |
|
add_log(f"[LOG 7] ํด์๋ {resolution_factor*100:.1f}%๋ก ์กฐ์ ์ค...") |
|
clip = clip.resize(resolution_factor) |
|
|
|
|
|
if abs(speed_factor - 1.0) > 1e-3: |
|
add_log(f"[LOG 8] ์ฌ์์๋ {speed_factor}๋ฐฐ๋ก ์กฐ์ ์ค...") |
|
clip = clip.fx(mp.vfx.speedx, speed_factor) |
|
|
|
|
|
original_fps = clip.fps |
|
target_fps = original_fps * speed_factor * frame_rate_factor |
|
add_log(f"[LOG 9] ์ต์ข
์ถ๋ ฅ FPS: {target_fps:.2f} (์๋ณธ FPS: {original_fps})") |
|
clip = clip.set_fps(target_fps) |
|
|
|
|
|
add_log(f"[LOG 10] GIF ๋ฐ๋ณต ํ์ ์ค์ : {repeat_count} (0์ด๋ฉด ๋ฌดํ๋ฐ๋ณต)") |
|
final_clip = clip |
|
|
|
add_log("[LOG 11] GIF ์์ฑ ์ค...") |
|
output_filename = f"temp_{uuid.uuid4().hex}.gif" |
|
try: |
|
|
|
loop_param = 0 if int(repeat_count) == 0 else int(repeat_count) |
|
final_clip.write_gif(output_filename, fps=target_fps, program='ffmpeg', loop=loop_param) |
|
add_log("[LOG 12] GIF ์์ฑ ์๋ฃ! ํ์ผ๋ช
: " + output_filename) |
|
except Exception as e: |
|
add_log(f"[ERROR] GIF ์์ฑ ์คํจ: {e}") |
|
return None, None, "\n".join(global_logs) |
|
|
|
|
|
return output_filename, output_filename, "\n".join(global_logs) |
|
|
|
def update_thumbnails(video, start_time_str, end_time_str): |
|
""" |
|
์์/๋ ์ธ๋ค์ผ์ ์
๋ฐ์ดํธํ๋ ํจ์ |
|
""" |
|
video_path = video if isinstance(video, str) else video.name |
|
|
|
try: |
|
input_video = mp.VideoFileClip(video_path) |
|
except Exception as e: |
|
add_log(f"[ERROR] ๋น๋์ค ๋ก๋ ์คํจ: {e}") |
|
return None, None |
|
|
|
duration = input_video.duration |
|
|
|
start_sec = parse_time_to_seconds(start_time_str) |
|
end_sec = parse_time_to_seconds(end_time_str) |
|
if start_sec < 0: |
|
start_sec = 0 |
|
if end_sec <= 0 or end_sec > duration: |
|
end_sec = duration |
|
if start_sec >= end_sec: |
|
start_sec = 0 |
|
end_sec = duration |
|
|
|
start_thumb = generate_thumbnail(input_video, start_sec) |
|
end_thumb = generate_thumbnail(input_video, end_sec) |
|
|
|
return start_thumb, end_thumb |
|
|
|
|
|
|
|
|
|
with gr.Blocks() as demo: |
|
gr.Markdown("## ๋์์์ GIF๋ก ๋ณํํ๊ธฐ ๋ฐ๋ชจ") |
|
|
|
with gr.Tab("GIF ๋ณํ"): |
|
|
|
video_input = gr.Video(label="๋์์ ์
๋ก๋") |
|
|
|
start_time = gr.Textbox(label="์์ ์๊ฐ (์: 00:00:05)", value="00:00:00") |
|
end_time = gr.Textbox(label="์ข
๋ฃ ์๊ฐ (์: 00:00:10)", value="00:00:05") |
|
|
|
start_thumb_output = gr.Image(label="์์ ์ธ๋ค์ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ") |
|
end_thumb_output = gr.Image(label="์ข
๋ฃ ์ธ๋ค์ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ") |
|
|
|
resolution_slider = gr.Slider(label="ํด์๋ ๋น์จ ์กฐ์ (0.1 ~ 1.0)", minimum=0.1, maximum=1.0, step=0.1, value=0.5) |
|
fps_slider = gr.Slider(label="ํ๋ ์ ๋ ์ดํธ ๋ฐฐ์จ ์กฐ์ (0.1 ~ 2.0)", minimum=0.1, maximum=2.0, step=0.1, value=0.5) |
|
speed_slider = gr.Slider(label="์ฌ์ ์๋ ์กฐ์ (0.5 ~ 2.0)", minimum=0.5, maximum=2.0, step=0.1, value=1.0) |
|
repeat_slider = gr.Slider(label="GIF ๋ฐ๋ณต ํ์ (0: ๋ฌดํ๋ฐ๋ณต, 1~10: ๋ฐ๋ณต ํ์)", minimum=0, maximum=10, step=1, value=0) |
|
|
|
generate_button = gr.Button("GIF ์์ฑํ๊ธฐ") |
|
|
|
gif_preview_output = gr.Image(label="์์ฑ๋ GIF ๋ฏธ๋ฆฌ๋ณด๊ธฐ") |
|
download_output = gr.File(label="GIF ๋ค์ด๋ก๋ ๋งํฌ") |
|
logs_output = gr.Textbox(label="๋ก๊ทธ ์ถ๋ ฅ", lines=10) |
|
|
|
|
|
start_time.change(fn=update_thumbnails, |
|
inputs=[video_input, start_time, end_time], |
|
outputs=[start_thumb_output, end_thumb_output]) |
|
end_time.change(fn=update_thumbnails, |
|
inputs=[video_input, start_time, end_time], |
|
outputs=[start_thumb_output, end_thumb_output]) |
|
video_input.change(fn=update_thumbnails, |
|
inputs=[video_input, start_time, end_time], |
|
outputs=[start_thumb_output, end_thumb_output]) |
|
|
|
generate_button.click(fn=process_video, |
|
inputs=[video_input, start_time, end_time, |
|
resolution_slider, fps_slider, speed_slider, repeat_slider], |
|
outputs=[gif_preview_output, download_output, logs_output]) |
|
|
|
gr.Markdown( |
|
"### [์ฌ์ฉ ๊ฐ์ด๋]\n" |
|
"1. ๋์์์ ์
๋ก๋ํ์ธ์.\n" |
|
"2. ์์/๋ ์๊ฐ์ ์ ์ ํ ์
๋ ฅํ์ธ์.\n" |
|
"3. ํด์๋, ํ๋ ์ ๋ ์ดํธ ๋ฐฐ์จ, ์ฌ์ ์๋, ๋ฐ๋ณต ํ์๋ฅผ ์กฐ์ ํ ๋ค `GIF ์์ฑํ๊ธฐ` ๋ฒํผ์ ๋๋ฅด๋ฉด GIF๊ฐ ์์ฑ๋ฉ๋๋ค.\n" |
|
" - ๋ฐ๋ณต ํ์: 0์ ์ ํํ๋ฉด ๋ฌดํ๋ฐ๋ณต, 1~10์ ํด๋น ํ์๋งํผ ๋ฐ๋ณต๋ฉ๋๋ค.\n" |
|
"4. ๊ฒฐ๊ณผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์ ๋ค์ด๋ก๋ ๋งํฌ๋ฅผ ํตํด GIF๋ฅผ ํ์ธํ ์ ์์ต๋๋ค." |
|
) |
|
|
|
if __name__ == "__main__": |
|
demo.launch() |
|
|