youngtsai commited on
Commit
ff03c35
·
1 Parent(s): e485414

fetch video title

Browse files
Files changed (2) hide show
  1. app.py +148 -47
  2. requirements.txt +2 -1
app.py CHANGED
@@ -8,6 +8,7 @@ import shutil
8
  import tempfile
9
  from google import genai
10
  from google.genai import types
 
11
 
12
  from initializer import initialize_clients, initialize_password
13
 
@@ -36,6 +37,30 @@ def mock_summary():
36
  # 假資料模擬摘要
37
  return "這份文件主要討論人工智慧在工作效率提升方面的應用,並提供了實際案例來說明其價值。"
38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  def add_to_file_list(file, file_list):
40
  if file:
41
  temp_dir = tempfile.gettempdir()
@@ -46,10 +71,25 @@ def add_to_file_list(file, file_list):
46
  return gr.update(choices=display_list), None
47
 
48
  def add_youtube_to_list(youtube_link, file_list):
49
- if youtube_link:
50
- file_list.append(youtube_link)
51
- display_list = [os.path.basename(path) if os.path.basename(path) else path for path in file_list]
52
- return gr.update(choices=display_list), ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
  def generate_transcript(youtube_link):
55
  print(f"\n開始生成 YouTube 逐字稿: {youtube_link}")
@@ -146,45 +186,100 @@ def generate_summary(transcript):
146
  raise
147
 
148
  def process_all_files(file_list):
149
- print("\n=== 開始處理檔案 ===")
150
- print(f"待處理檔案數量: {len(file_list)}")
 
 
 
 
151
 
152
- result_text = ""
153
- transcript_text = ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
 
155
- for index, file in enumerate(file_list, 1):
156
- print(f"\n處理第 {index}/{len(file_list)} 個檔案: {file}")
157
-
158
- if "youtube.com" in file or "youtu.be" in file:
159
- print(f"檢測到 YouTube 連結,開始生成逐字稿...")
160
- try:
161
- transcript = generate_transcript(file)
162
- print("✓ YouTube 逐字稿生成成功")
163
- result_text += f"🟢 YouTube 影片處理完成: {file}\n"
164
- transcript_text += f"\n=== {file} 的逐字稿 ===\n{transcript}\n"
165
- except Exception as e:
166
- print(f"✗ YouTube 逐字稿生成失敗: {str(e)}")
167
- result_text += f"🔴 YouTube 影片處理失敗: {file}\n"
168
- else:
169
- print(f"處理一般檔案: {file}")
170
- try:
171
- # 這裡可以加入其他檔案的處理邏輯
172
- print("✓ 檔案處理成功")
173
- result_text += f"🟢 檔案處理完成: {file}\n"
174
- except Exception as e:
175
- print(f"✗ 檔案處理失敗: {str(e)}")
176
- result_text += f"🔴 檔案處理失敗: {file}\n"
177
-
178
- print("\n=== 檔案處理完成 ===")
179
- return result_text, transcript_text
180
 
181
- def process_with_auth(password, file_list):
182
- """包含密碼驗證的處理函數"""
183
- if not password or password != PASSWORD:
 
 
 
184
  return "請輸入正確的密碼", "", gr.update(visible=False)
185
 
186
- result_text, transcript_text = process_all_files(file_list)
187
- return result_text, transcript_text
 
 
 
 
 
 
 
 
 
 
 
188
 
189
  def on_summary_click(transcript):
190
  if not transcript:
@@ -209,24 +304,31 @@ with gr.Blocks() as demo:
209
  gr.Markdown("### 來源選單")
210
 
211
  file_list = gr.State([])
 
212
 
213
  with gr.Tab("YouTube 連結"):
214
  youtube_link = gr.Textbox(label="輸入 YouTube 連結")
215
  add_youtube_button = gr.Button("添加到來源列表")
216
  add_youtube_button.click(add_youtube_to_list, inputs=[youtube_link, file_list], outputs=[file_list, youtube_link])
217
 
218
- with gr.Tab("上傳檔案"):
219
  upload_file = gr.File(label="從電腦添加文件", file_types=[".txt", ".pdf", ".docx"])
220
  add_file_button = gr.Button("添加到來源列表")
221
  add_file_button.click(add_to_file_list, inputs=[upload_file, file_list], outputs=[file_list, upload_file])
222
 
223
- file_display = gr.CheckboxGroup(label="已上傳的文件", interactive=True)
 
 
 
 
 
 
 
 
224
 
225
  process_files_button = gr.Button("處理檔案")
226
  rag_result = gr.Textbox(label="處理狀態", interactive=False)
227
 
228
- file_list.change(lambda x: gr.update(choices = [os.path.basename(path) if os.path.basename(path) else path for path in x]), inputs=file_list, outputs=file_display)
229
-
230
  with gr.Column(visible=True) as chat_column:
231
  gr.Markdown("### 對話區域")
232
  chatbot = gr.Chatbot(label="聊天記錄", type="messages")
@@ -247,7 +349,7 @@ with gr.Blocks() as demo:
247
  transcript_display = gr.Textbox(
248
  label="YouTube 逐字稿",
249
  interactive=False,
250
- lines=10,
251
  show_copy_button=True,
252
  placeholder="處理 YouTube 影片後,逐字稿將顯示在這裡..."
253
  )
@@ -261,10 +363,11 @@ with gr.Blocks() as demo:
261
  # 更新處理檔案按鈕的事件處理
262
  process_files_button.click(
263
  fn=process_with_auth,
264
- inputs=[password_input, file_list],
265
  outputs=[
266
  rag_result,
267
- transcript_display
 
268
  ]
269
  ).then(
270
  fn=on_summary_click,
@@ -280,6 +383,4 @@ with gr.Blocks() as demo:
280
  outputs=[summary_output]
281
  )
282
 
283
-
284
-
285
  demo.launch(share=True)
 
8
  import tempfile
9
  from google import genai
10
  from google.genai import types
11
+ import yt_dlp
12
 
13
  from initializer import initialize_clients, initialize_password
14
 
 
37
  # 假資料模擬摘要
38
  return "這份文件主要討論人工智慧在工作效率提升方面的應用,並提供了實際案例來說明其價值。"
39
 
40
+ def get_youtube_title(url):
41
+ """獲取 YouTube 影片標題"""
42
+ try:
43
+ # 確保 URL 格式完整
44
+ if not url.startswith('http'):
45
+ if 'watch?v=' in url:
46
+ url = f'https://www.youtube.com/{url}'
47
+ else:
48
+ url = f'https://www.youtube.com/watch?v={url}'
49
+
50
+ ydl_opts = {
51
+ 'quiet': True,
52
+ 'no_warnings': True,
53
+ 'extract_flat': True
54
+ }
55
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
56
+ info = ydl.extract_info(url, download=False)
57
+ title = info.get('title', url)
58
+ print(f"YouTube title: {title}")
59
+ return title
60
+ except Exception as e:
61
+ print(f"Error fetching YouTube title: {str(e)}")
62
+ return url
63
+
64
  def add_to_file_list(file, file_list):
65
  if file:
66
  temp_dir = tempfile.gettempdir()
 
71
  return gr.update(choices=display_list), None
72
 
73
  def add_youtube_to_list(youtube_link, file_list):
74
+ if not youtube_link:
75
+ return gr.update(choices=[item.split("|||")[0] if "|||" in item else os.path.basename(item) for item in file_list]), ""
76
+
77
+ # 獲取標題
78
+ title = get_youtube_title(youtube_link)
79
+
80
+ # 確保 URL 格式完整
81
+ if not youtube_link.startswith('http'):
82
+ if 'watch?v=' in youtube_link:
83
+ youtube_link = f'https://www.youtube.com/{youtube_link}'
84
+ else:
85
+ youtube_link = f'https://www.youtube.com/watch?v={youtube_link}'
86
+
87
+ # 存儲格式:[title]|||[url]
88
+ file_list.append(f"{title}|||{youtube_link}")
89
+ display_list = [item.split("|||")[0] if "|||" in item else os.path.basename(item) for item in file_list]
90
+ print(f"File list: {file_list}")
91
+ print(f"Display list: {display_list}")
92
+ return file_list, ""
93
 
94
  def generate_transcript(youtube_link):
95
  print(f"\n開始生成 YouTube 逐字稿: {youtube_link}")
 
186
  raise
187
 
188
  def process_all_files(file_list):
189
+ """處理所有選中的文件"""
190
+ if not file_list:
191
+ return "請選擇要處理的文件", ""
192
+
193
+ all_text = []
194
+ status_messages = []
195
 
196
+ for item in file_list:
197
+ try:
198
+ if "|||" in item:
199
+ # YouTube 連結
200
+ title, url = item.split("|||")
201
+ print(f"處理 YouTube: {title}")
202
+ try:
203
+ transcript = generate_transcript(url)
204
+ if transcript:
205
+ all_text.append(f"=== {title} ===\n{transcript}")
206
+ status_messages.append(f"🟢 成功處理 YouTube 影片:{title}")
207
+ else:
208
+ status_messages.append(f"🔴 無法獲取影片逐字稿:{title}")
209
+ except Exception as e:
210
+ if "無法取得影片資訊" in str(e):
211
+ # 可能是影片標題問題,但還是有內容
212
+ all_text.append(f"=== YouTube 影片 ===\n{e.transcript if hasattr(e, 'transcript') else ''}")
213
+ status_messages.append(f"🟡 影片資訊不完整,但已處理內容:{url}")
214
+ else:
215
+ status_messages.append(f"🔴 處理失敗:{title}({str(e)})")
216
+ else:
217
+ # 本地文件
218
+ filename = os.path.basename(item)
219
+ print(f"處理文件: {filename}")
220
+ try:
221
+ with open(item, 'r', encoding='utf-8') as f:
222
+ content = f.read()
223
+ try:
224
+ # 嘗試解碼文件名
225
+ decoded_name = filename.encode('latin1').decode('utf-8')
226
+ all_text.append(f"=== {decoded_name} ===\n{content}")
227
+ status_messages.append(f"🟢 成功處理文件:{decoded_name}")
228
+ except:
229
+ # 文件名有問題,但內容可用
230
+ all_text.append(f"=== 文件內容 ===\n{content}")
231
+ status_messages.append(f"🟡 文件名稱無法正確顯示,但已處理內容:{filename}")
232
+ except UnicodeDecodeError:
233
+ try:
234
+ # 嘗試其他編碼
235
+ for encoding in ['big5', 'gbk', 'shift-jis']:
236
+ try:
237
+ with open(item, 'r', encoding=encoding) as f:
238
+ content = f.read()
239
+ all_text.append(f"=== {filename} ===\n{content}")
240
+ status_messages.append(f"🟡 使用 {encoding} 編碼成功讀取文件:{filename}")
241
+ break
242
+ except:
243
+ continue
244
+ else:
245
+ status_messages.append(f"🔴 無法讀取文件內容:{filename}(編碼問題)")
246
+ except Exception as e:
247
+ status_messages.append(f"🔴 讀取文件失敗:{filename}({str(e)})")
248
+ except Exception as e:
249
+ status_messages.append(f"🔴 讀取文件失敗:{filename}({str(e)})")
250
+ except Exception as e:
251
+ status_messages.append(f"🔴 處理失敗:{item}({str(e)})")
252
+
253
+ if not all_text:
254
+ return "❌ 沒有成功處理任何文件", ""
255
+
256
+ # 合併所有文本
257
+ combined_text = "\n\n".join(all_text)
258
+ status_text = "\n".join(status_messages)
259
 
260
+ return f"處理完成\n{status_text}", combined_text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
 
262
+ def process_with_auth(password, file_list, file_display):
263
+ """帶密碼驗證的文件處理"""
264
+ if not file_display: # 使用 file_display 而不是 file_list
265
+ return "請選擇要處理的文件", "", gr.update(visible=False)
266
+
267
+ if password != PASSWORD:
268
  return "請輸入正確的密碼", "", gr.update(visible=False)
269
 
270
+ # 根據顯示的選項找到對應的完整項目
271
+ selected_files = []
272
+ for item in file_list:
273
+ if "|||" in item:
274
+ title = item.split("|||")[0]
275
+ if title in file_display:
276
+ selected_files.append(item)
277
+ else:
278
+ if os.path.basename(item) in file_display:
279
+ selected_files.append(item)
280
+
281
+ result_text, transcript_text = process_all_files(selected_files)
282
+ return result_text, transcript_text, gr.update(visible=True)
283
 
284
  def on_summary_click(transcript):
285
  if not transcript:
 
304
  gr.Markdown("### 來源選單")
305
 
306
  file_list = gr.State([])
307
+ file_display = gr.State([])
308
 
309
  with gr.Tab("YouTube 連結"):
310
  youtube_link = gr.Textbox(label="輸入 YouTube 連結")
311
  add_youtube_button = gr.Button("添加到來源列表")
312
  add_youtube_button.click(add_youtube_to_list, inputs=[youtube_link, file_list], outputs=[file_list, youtube_link])
313
 
314
+ with gr.Tab("上傳檔案(TODO)"):
315
  upload_file = gr.File(label="從電腦添加文件", file_types=[".txt", ".pdf", ".docx"])
316
  add_file_button = gr.Button("添加到來源列表")
317
  add_file_button.click(add_to_file_list, inputs=[upload_file, file_list], outputs=[file_list, upload_file])
318
 
319
+ file_display_input = gr.CheckboxGroup(label="已上傳的文件", interactive=True)
320
+
321
+ # 更新顯示邏輯
322
+ def update_display(file_list):
323
+ display_list = [item.split("|||")[0] if "|||" in item else os.path.basename(item) for item in file_list]
324
+ print(f"Updating display with: {display_list}")
325
+ return gr.update(choices=display_list, value=[])
326
+
327
+ file_list.change(update_display, inputs=file_list, outputs=file_display_input)
328
 
329
  process_files_button = gr.Button("處理檔案")
330
  rag_result = gr.Textbox(label="處理狀態", interactive=False)
331
 
 
 
332
  with gr.Column(visible=True) as chat_column:
333
  gr.Markdown("### 對話區域")
334
  chatbot = gr.Chatbot(label="聊天記錄", type="messages")
 
349
  transcript_display = gr.Textbox(
350
  label="YouTube 逐字稿",
351
  interactive=False,
352
+ lines=20,
353
  show_copy_button=True,
354
  placeholder="處理 YouTube 影片後,逐字稿將顯示在這裡..."
355
  )
 
363
  # 更新處理檔案按鈕的事件處理
364
  process_files_button.click(
365
  fn=process_with_auth,
366
+ inputs=[password_input, file_list, file_display_input],
367
  outputs=[
368
  rag_result,
369
+ transcript_display,
370
+ summary_button
371
  ]
372
  ).then(
373
  fn=on_summary_click,
 
383
  outputs=[summary_output]
384
  )
385
 
 
 
386
  demo.launch(share=True)
requirements.txt CHANGED
@@ -8,4 +8,5 @@ google-auth-oauthlib
8
  google-cloud-storage
9
  google-cloud-bigquery
10
  google-generativeai
11
- google-genai
 
 
8
  google-cloud-storage
9
  google-cloud-bigquery
10
  google-generativeai
11
+ google-genai
12
+ yt-dlp