File size: 6,147 Bytes
ac5e4c8
 
 
 
fe72b7c
 
ac5e4c8
 
 
 
 
 
fe72b7c
 
ac5e4c8
60ab7b2
 
ac5e4c8
 
60ab7b2
 
 
 
ac5e4c8
 
 
60ab7b2
ac5e4c8
60ab7b2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ac5e4c8
fe72b7c
 
 
 
 
 
 
ac5e4c8
 
 
 
 
 
 
 
fe72b7c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ac5e4c8
3bce222
 
fe72b7c
ac5e4c8
fe72b7c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ac5e4c8
 
fe72b7c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ac5e4c8
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
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()