MPEGEndFrame / app.py
dseditor's picture
Upload app.py
3bce222 verified
import gradio as gr
import subprocess
import os
import tempfile
import shutil
from video_merger import merge_videos
def extract_last_frame(video_file):
if video_file is None:
return None, "請上傳影片檔案"
try:
# 創建固定名稱的輸出檔案
temp_output = tempfile.mktemp(suffix='.jpg')
# 獲取影片總幀數
frame_count_cmd = [
'ffprobe',
'-v', 'error',
'-select_streams', 'v:0',
'-count_frames',
'-show_entries', 'stream=nb_read_frames',
'-of', 'default=nokey=1:noprint_wrappers=1',
video_file
]
frame_result = subprocess.run(frame_count_cmd, capture_output=True, text=True)
# 如果無法獲取幀數,使用反向讀取方法
if not frame_result.stdout.strip() or frame_result.returncode != 0:
# 方法2: 使用 select 過濾器選擇最後一幀
extract_cmd = [
'ffmpeg',
'-i', video_file,
'-vf', 'select=eq(n\\,0)',
'-vsync', '0',
'-q:v', '2',
'-frames:v', '1',
temp_output,
'-y'
]
# 先嘗試從尾部讀取
extract_cmd_reverse = [
'ffmpeg',
'-sseof', '-3',
'-i', video_file,
'-vframes', '1',
'-vf', 'select=eq(pict_type\\,I)',
'-vsync', '0',
'-q:v', '2',
temp_output,
'-y'
]
result = subprocess.run(extract_cmd_reverse, capture_output=True, text=True)
# 如果失敗,使用標準方法
if result.returncode != 0 or not os.path.exists(temp_output):
subprocess.run(extract_cmd, capture_output=True, text=True)
else:
# 方法1: 使用幀數選擇最後一幀
total_frames = int(frame_result.stdout.strip())
last_frame = total_frames - 1
extract_cmd = [
'ffmpeg',
'-i', video_file,
'-vf', f'select=eq(n\\,{last_frame})',
'-vsync', '0',
'-q:v', '2',
'-frames:v', '1',
temp_output,
'-y'
]
subprocess.run(extract_cmd, check=True, capture_output=True)
if os.path.exists(temp_output) and os.path.getsize(temp_output) > 0:
# 重新命名為 lastframe.jpg
output_dir = tempfile.gettempdir()
final_output = os.path.join(output_dir, 'lastframe.jpg')
shutil.copy2(temp_output, final_output)
os.remove(temp_output)
return final_output, "成功提取最後一幀!"
else:
return None, "提取失敗,請確認影片格式正確"
except subprocess.CalledProcessError as e:
return None, f"處理錯誤: {str(e)}"
except Exception as e:
return None, f"發生錯誤: {str(e)}"
def merge_videos_wrapper(video_files):
"""包裝合併函數以處理檔名"""
if not video_files:
return None, "請上傳影片檔案"
temp_output, message = merge_videos(video_files)
if temp_output and os.path.exists(temp_output):
# 重新命名為 final.mp4
output_dir = tempfile.gettempdir()
final_output = os.path.join(output_dir, 'final.mp4')
shutil.copy2(temp_output, final_output)
os.remove(temp_output)
return final_output, message
else:
return None, message
# 創建 Gradio 界面
with gr.Blocks(title="Grok影片工具箱") as demo:
gr.Markdown("# Grok影片工具箱")
gr.Markdown("提供影片最後一幀提取和多段影片合併功能")
with gr.Tabs():
# Tab 1: 提取最後一幀
with gr.Tab("提取最後一幀"):
gr.Markdown("上傳影片(最大 100MB),自動提取最後一幀為 JPEG 圖片")
with gr.Row():
with gr.Column():
video_input = gr.Video(
label="上傳影片",
max_length=None,
height=400
)
extract_btn = gr.Button("提取最後一幀", variant="primary")
with gr.Column():
image_output = gr.Image(
label="最後一幀 (lastframe.jpg)",
type="filepath",
height=400
)
status_output = gr.Textbox(label="狀態", interactive=False)
extract_btn.click(
fn=extract_last_frame,
inputs=[video_input],
outputs=[image_output, status_output]
)
# Tab 2: 合併影片
with gr.Tab("合併影片"):
gr.Markdown("上傳多個 MP4 影片,按照上傳順序合併為一個影片")
with gr.Row():
with gr.Column():
videos_input = gr.File(
label="上傳影片檔案(可多選)",
file_count="multiple",
file_types=["video"],
height=300
)
merge_btn = gr.Button("合併影片", variant="primary")
with gr.Column():
merged_video_output = gr.Video(
label="合併後的影片 (final.mp4)",
height=400
)
merge_status_output = gr.Textbox(label="狀態", interactive=False)
merge_btn.click(
fn=merge_videos_wrapper,
inputs=[videos_input],
outputs=[merged_video_output, merge_status_output]
)
if __name__ == "__main__":
demo.launch()