Delete app.py
Browse files
app.py
DELETED
@@ -1,110 +0,0 @@
|
|
1 |
-
import gradio as gr
|
2 |
-
from moviepy.editor import VideoFileClip
|
3 |
-
import cv2
|
4 |
-
import base64
|
5 |
-
from openai import OpenAI
|
6 |
-
import os
|
7 |
-
|
8 |
-
# 這個函數負責處理影片,將影片按指定的秒數提取幀圖片,並提取音訊
|
9 |
-
def process_video(video_path, seconds_per_frame=2):
|
10 |
-
print(f"Debug: 開始處理影片 {video_path}") # Debug訊息
|
11 |
-
base64Frames = [] # 用於儲存幀圖片的Base64編碼列表
|
12 |
-
base_video_path, _ = os.path.splitext(video_path) # 獲取影片文件的基礎路徑(不含副檔名)
|
13 |
-
video = cv2.VideoCapture(video_path) # 使用OpenCV打開影片
|
14 |
-
total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT)) # 獲取影片總幀數
|
15 |
-
fps = video.get(cv2.CAP_PROP_FPS) # 獲取影片的幀率(每秒幀數)
|
16 |
-
frames_to_skip = int(fps * seconds_per_frame) # 計算要跳過的幀數
|
17 |
-
curr_frame = 0 # 初始化當前幀的指標
|
18 |
-
|
19 |
-
print(f"Debug: 總幀數 {total_frames}, 幀率 {fps}, 每 {seconds_per_frame} 秒處理一幀") # Debug訊息
|
20 |
-
|
21 |
-
# 逐幀處理影片
|
22 |
-
while curr_frame < total_frames - 1:
|
23 |
-
video.set(cv2.CAP_PROP_POS_FRAMES, curr_frame) # 設置要讀取的幀
|
24 |
-
success, frame = video.read() # 讀取當前幀
|
25 |
-
if not success:
|
26 |
-
print(f"Debug: 讀取幀失敗,當前幀 {curr_frame}") # Debug訊息
|
27 |
-
break
|
28 |
-
_, buffer = cv2.imencode(".jpg", frame) # 將幀編碼為JPEG格式
|
29 |
-
base64Frames.append(base64.b64encode(buffer).decode("utf-8")) # 將JPEG格式的幀轉為Base64編碼並儲存
|
30 |
-
curr_frame += frames_to_skip # 更新當前幀的指標
|
31 |
-
|
32 |
-
video.release() # 釋放影片資源
|
33 |
-
print(f"Debug: 完成幀的提取,共提取了 {len(base64Frames)} 幀") # Debug訊息
|
34 |
-
|
35 |
-
# 提取音訊
|
36 |
-
audio_path = f"{base_video_path}.mp3" # 定義音訊輸出的路徑
|
37 |
-
print(f"Debug: 開始提取音訊到 {audio_path}") # Debug訊息
|
38 |
-
clip = VideoFileClip(video_path) # 使用moviepy打開影片
|
39 |
-
clip.audio.write_audiofile(audio_path, bitrate="32k") # 將音訊寫入MP3文件
|
40 |
-
clip.audio.close()
|
41 |
-
clip.close()
|
42 |
-
print(f"Debug: 音訊提取完成") # Debug訊息
|
43 |
-
|
44 |
-
return base64Frames, audio_path
|
45 |
-
|
46 |
-
# 影片摘要生成函數
|
47 |
-
def summarize_video(file_path):
|
48 |
-
api_key = os.getenv("OPENAI_API_KEY")
|
49 |
-
print(f"Debug: 使用的API Key為 {api_key}") # Debug訊息
|
50 |
-
client = OpenAI(api_key=api_key)
|
51 |
-
|
52 |
-
# 提取影片幀和音訊(每0.5秒提取一幀)
|
53 |
-
print(f"Debug: 開始提取影片幀和音訊") # Debug訊息
|
54 |
-
base64Frames, audio_path = process_video(file_path, seconds_per_frame=0.5)
|
55 |
-
|
56 |
-
# 使用 Whisper 進行音訊轉文字
|
57 |
-
print(f"Debug: 開始進行音訊轉文字") # Debug訊息
|
58 |
-
transcription = client.audio.transcriptions.create(
|
59 |
-
model="whisper-1", file=open(audio_path, "rb")
|
60 |
-
)
|
61 |
-
print(f"Debug: 音訊轉文字完成,轉錄結果長度 {len(transcription.text)} 字元") # Debug訊息
|
62 |
-
|
63 |
-
# 使用 GPT-4o 生成摘要
|
64 |
-
print(f"Debug: 開始生成GPT-4o摘要") # Debug訊息
|
65 |
-
response = client.chat.completions.create(
|
66 |
-
model="gpt-4o",
|
67 |
-
messages=[
|
68 |
-
{
|
69 |
-
"role": "system",
|
70 |
-
"content": """你是一位優秀的摘要撰寫者。請根據提供的影片及其文字轉錄內容撰寫一份 Markdown 格式的摘要。""",
|
71 |
-
},
|
72 |
-
{
|
73 |
-
"role": "user",
|
74 |
-
"content": [
|
75 |
-
"這些是從影片中獲取的幀圖片",
|
76 |
-
*map(
|
77 |
-
lambda x: {
|
78 |
-
"type": "image_url",
|
79 |
-
"image_url": {
|
80 |
-
"url": f"data:image/jpg;base64,{x}",
|
81 |
-
"detail": "low",
|
82 |
-
},
|
83 |
-
},
|
84 |
-
base64Frames,
|
85 |
-
),
|
86 |
-
{
|
87 |
-
"type": "text",
|
88 |
-
"text": f"這是影片的文字轉錄內容: {transcription.text}",
|
89 |
-
},
|
90 |
-
],
|
91 |
-
},
|
92 |
-
],
|
93 |
-
temperature=0,
|
94 |
-
)
|
95 |
-
print(f"Debug: GPT-4o摘要生成完成") # Debug訊息
|
96 |
-
|
97 |
-
return response.choices[0].message.content
|
98 |
-
|
99 |
-
# 定義Gradio界面
|
100 |
-
demo = gr.Interface(
|
101 |
-
fn=summarize_video,
|
102 |
-
inputs=[gr.File(label="上傳影片 (mp4)")],
|
103 |
-
outputs="markdown",
|
104 |
-
title="影片摘要生成器",
|
105 |
-
description="上傳影片並將會生成摘要。",
|
106 |
-
)
|
107 |
-
|
108 |
-
if __name__ == "__main__":
|
109 |
-
print("Debug: 啟動Gradio介面") # Debug訊息
|
110 |
-
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|