TDN-M commited on
Commit
96b597d
·
verified ·
1 Parent(s): e7d588c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +142 -340
app.py CHANGED
@@ -8,7 +8,7 @@ from audio_processing import async_text_to_speech, text_to_speech
8
  from content_generation import create_content, CONTENT_TYPES
9
  from video_processing import create_video_func
10
  from moviepy.editor import AudioFileClip, CompositeAudioClip
11
- from utils import (combine_videos, find_matching_image, get_pexels_image,split_audio, get_bgm_file)
12
  from video_processing import create_video
13
 
14
  # Danh sách giọng đọc
@@ -18,11 +18,9 @@ VOICES = ["alloy", "echo", "fable", "onyx", "nova", "shimmer"]
18
  LANGUAGES = ["Tiếng Anh", "Tiếng Việt", "Tiếng Hindi"]
19
 
20
  # Danh sách loại nội dung và hướng dẫn mặc định cho từng loại
21
- CONTENT_TYPES = ["podcast", "giới thiệu", "triết lý sống",
22
- "Phổ biến kiến thức thống kê"]
23
  CONTENT_TYPE_INSTRUCTIONS = {
24
- "podcast":
25
- """
26
  Tone giọng: Gần gũi, thân thiện nhưng chuyên sâu, thể hiện sự am hiểu về chủ đề.
27
  Cấu trúc:
28
  - Bắt đầu bằng một câu hỏi kích thích tư duy hoặc một câu chuyện mở màn gây tò mò.
@@ -31,8 +29,7 @@ CONTENT_TYPE_INSTRUCTIONS = {
31
  - Kết thúc podcast với một thông điệp sâu sắc, để lại sự suy ngẫm cho thính giả.
32
  Mục tiêu: Mang lại kiến thức giá trị, lôi cuốn thính giả tham gia suy nghĩ và cảm nhận sâu sắc về chủ đề.
33
  """,
34
- "giới thiệu":
35
- """
36
  Tone giọng: Chuyên nghiệp, gãy gọn nhưng vẫn có sự truyền cảm.
37
  Cấu trúc:
38
  - Bắt đầu với một câu khẳng định mạnh mẽ về đối tượng được giới thiệu.
@@ -40,8 +37,7 @@ CONTENT_TYPE_INSTRUCTIONS = {
40
  - Kết thúc với một lời kêu gọi hành động, khích lệ người nghe tiếp tục lắng nghe hoặc tham gia.
41
  Mục tiêu: Đưa ra thông tin cô đọng, hấp dẫn, khiến người nghe cảm thấy bị thu hút và muốn tìm hiểu thêm.
42
  """,
43
- "triết lý sống":
44
- """
45
  Tone giọng: Sâu sắc, truyền cảm hứng, mang tính chiêm nghiệm.
46
  Cấu trúc:
47
  - Bắt đầu bằng một câu hỏi sâu sắc hoặc ẩn dụ về cuộc sống.
@@ -49,8 +45,7 @@ CONTENT_TYPE_INSTRUCTIONS = {
49
  - Kết thúc với một thông điệp sâu sắc, khơi dậy suy ngẫm cho người nghe.
50
  Mục tiêu: Khơi gợi suy nghĩ sâu sắc về cuộc sống, khiến người nghe tìm thấy ý nghĩa hoặc giá trị trong câu chuyện.
51
  """,
52
- "Phổ biến kiến thức Thống kê":
53
- """
54
  Tone giọng: Thân thiện, dễ hiểu, và mang tính giáo dục.
55
  Cấu trúc:
56
  - Bắt đầu với một câu hỏi hoặc một tình huống thực tế để thu hút sự chú ý.
@@ -62,301 +57,115 @@ CONTENT_TYPE_INSTRUCTIONS = {
62
  }
63
 
64
  def create_docx(content, output_path):
65
- """
66
- Tạo file docx từ nội dung.
67
- """
68
- doc = Document()
69
- doc.add_paragraph(content)
70
- doc.save(output_path)
71
-
72
- def process_pdf(file_path):
73
- """
74
- Xử lý file PDF và trích xuất nội dung.
75
- """
76
- doc = fitz.open(file_path)
77
- text = ""
78
- for page in doc:
79
- text += page.get_text()
80
- return text
81
-
82
- def process_docx(file_path):
83
- """
84
- Xử lý file DOCX và trích xuất nội dung.
85
- """
86
- doc = Document(file_path)
87
- text = ""
88
- for para in doc.paragraphs:
89
- text += para.text
90
- return text
91
-
92
- # Giao diện Gradio
93
- def interface():
94
- with gr.Blocks() as app:
95
- gr.Markdown("# Ứng dụng Tạo Nội dung và Video")
96
-
97
- with gr.Tab("Tạo Nội dung"):
98
- prompt = gr.Textbox(label="Nhập yêu cầu nội dung")
99
- file_upload = gr.File(label="Tải lên file kèm theo", type="filepath")
100
-
101
- # Sử dụng gr.Radio thay vì gr.CheckboxGroup
102
- content_type = gr.Radio(label="Chọn loại nội dung",
103
- choices=CONTENT_TYPES,
104
- value=None) # Giá trị mặc định là không có gì được chọn
105
-
106
- content_button = gr.Button("Tạo Nội dung")
107
- content_output = gr.Textbox(label="Nội dung tạo ra", interactive=True)
108
- confirm_button = gr.Button("Xác nhận nội dung")
109
- download_docx = gr.File(label="Tải xuống file DOCX", interactive=False)
110
- download_audio = gr.File(label="Tải xuống file âm thanh",
111
- interactive=False)
112
- status_message = gr.Label(label="Trạng thái")
113
-
114
- def generate_content(prompt, file, content_type):
115
- try:
116
- status = "Đang xử lý..."
117
- if file and os.path.exists(file):
118
- mime_type, _ = mimetypes.guess_type(file)
119
- if mime_type == "application/pdf":
120
- file_content = process_pdf(file)
121
- prompt = f"{prompt}\n\nDưới đây là nội dung của file tài liệu:\n\n{file_content}"
122
- elif mime_type in (
123
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
124
- "application/msword"):
125
- file_content = process_docx(file)
126
- prompt = f"{prompt}\n\nDưới đây là nội dung của file tài liệu:\n\n{file_content}"
127
- else:
128
- raise ValueError("Định dạng file không được hỗ trợ.")
129
-
130
- if not content_type:
131
- raise ValueError("Vui lòng chọn một loại nội dung")
132
-
133
- script_content = create_content(prompt, content_type, "Tiếng Việt")
134
- docx_path = "script.docx"
135
- create_docx(script_content, docx_path)
136
-
137
- status = "Đã tạo nội dung thành công!"
138
- return script_content, docx_path, status
139
- except Exception as e:
140
- status = f"Đã xảy ra lỗi: {str(e)}"
141
- return "", None, status
142
-
143
- async def confirm_content(content):
144
- docx_path = "script.docx"
145
- create_docx(content, docx_path)
146
-
147
- audio_path = await async_text_to_speech(content, "alloy", "Tiếng Việt")
148
- return docx_path, audio_path, "Nội dung đã được xác nhận và âm thanh đã được tạo!"
149
-
150
- content_button.click(generate_content,
151
- inputs=[prompt, file_upload, content_type],
152
- outputs=[content_output, download_docx, status_message])
153
-
154
- confirm_button.click(lambda x: asyncio.run(confirm_content(x)),
155
- inputs=[content_output],
156
- outputs=[download_docx, download_audio, status_message])
157
-
158
- with gr.Tab("Tạo Âm thanh"):
159
- text_input = gr.Textbox(label="Nhập văn bản để chuyển đổi")
160
- voice_select = gr.Dropdown(label="Chọn giọng đọc",
161
- choices=VOICES) # Dropdown cho voice_select
162
- audio_button = gr.Button("Tạo Âm thanh")
163
- audio_output = gr.Audio(label="Âm thanh tạo ra")
164
- download_audio = gr.File(label="Tải xuống file âm thanh",
165
- interactive=False)
166
-
167
- def text_to_speech_func(text, voice):
168
- audio_path = text_to_speech(text, voice, "Tiếng Việt")
169
- return audio_path, audio_path
170
-
171
- audio_button.click(text_to_speech_func,
172
- inputs=[text_input, voice_select],
173
- outputs=[audio_output, download_audio])
174
-
175
- with gr.Tab("Tạo Video"):
176
- script_input = gr.Textbox(label="Nhập kịch bản")
177
- audio_file = gr.File(label="Chọn file âm thanh", type="filepath")
178
- keywords_output = gr.Textbox(label="Từ khóa", interactive=True)
179
- max_clip_duration = gr.Slider(minimum=2, maximum=5, step=1, label="Thời lượng tối đa mỗi video (giây)")
180
- join_order = gr.Checkbox(label="Ghép ngẫu nhiên", value=True) # Mặc định là ghép ngẫu nhiên
181
- bgm_files = gr.Dropdown(choices=get_bgm_file_list(), label="Chọn nhạc nền") # Hàm get_bgm_file_list() cần được định nghĩa
182
- create_video_button = gr.Button("Tạo Video")
183
- video_output = gr.Video(label="Video tạo ra")
184
-
185
- import asyncio
186
- import mimetypes
187
- import os
188
- import tempfile
189
- import gradio as gr
190
- from docx import Document
191
- from audio_processing import async_text_to_speech, text_to_speech
192
- from content_generation import create_content, CONTENT_TYPES
193
- from video_processing import create_video_func
194
- from moviepy.editor import AudioFileClip, CompositeAudioClip
195
- from utils import (combine_videos, find_matching_image, get_pexels_image,split_audio, get_bgm_file)
196
- from video_processing import create_video
197
-
198
- # Danh sách giọng đọc
199
- VOICES = ["alloy", "echo", "fable", "onyx", "nova", "shimmer"]
200
-
201
- # Danh sách ngôn ngữ (chưa được sử dụng trong mã)
202
- LANGUAGES = ["Tiếng Anh", "Tiếng Việt", "Tiếng Hindi"]
203
-
204
- # Danh sách loại nội dung và hướng dẫn mặc định cho từng loại
205
- CONTENT_TYPES = ["podcast", "giới thiệu", "triết lý sống",
206
- "Phổ biến kiến thức thống kê"]
207
- CONTENT_TYPE_INSTRUCTIONS = {
208
- "podcast":
209
  """
210
- Tone giọng: Gần gũi, thân thiện nhưng chuyên sâu, thể hiện sự am hiểu về chủ đề.
211
- Cấu trúc:
212
- - Bắt đầu bằng một câu hỏi kích thích tư duy hoặc một câu chuyện mở màn gây tò mò.
213
- - Triển khai các luận điểm theo từng bước. Sử dụng câu từ mạnh mẽ, ví dụ điển hình hoặc những câu nói nổi tiếng.
214
- - Xây dựng các phần chuyển tiếp mượt mà giữa các ý.
215
- - Kết thúc podcast với một thông điệp sâu sắc, để lại sự suy ngẫm cho thính giả.
216
- Mục tiêu: Mang lại kiến thức giá trị, lôi cuốn thính giả tham gia suy nghĩ và cảm nhận sâu sắc về chủ đề.
217
- """,
218
- "giới thiệu":
219
- """
220
- Tone giọng: Chuyên nghiệp, gãy gọn nhưng vẫn có sự truyền cảm.
221
- Cấu trúc:
222
- - Bắt đầu với một câu khẳng định mạnh mẽ về đối tượng được giới thiệu.
223
- - Giải thích mục tiêu của phần giới thiệu, nhấn mạnh tầm quan trọng hoặc sự khác biệt.
224
- - Kết thúc với một lời kêu gọi hành động, khích lệ người nghe tiếp tục lắng nghe hoặc tham gia.
225
- Mục tiêu: Đưa ra thông tin cô đọng, hấp dẫn, khiến người nghe cảm thấy bị thu hút và muốn tìm hiểu thêm.
226
- """,
227
- "triết lý sống":
228
  """
229
- Tone giọng: Sâu sắc, truyền cảm hứng, mang tính chiêm nghiệm.
230
- Cấu trúc:
231
- - Bắt đầu bằng một câu hỏi sâu sắc hoặc ẩn dụ về cuộc sống.
232
- - Triển khai các luận điểm chặt chẽ, xen lẫn cảm xúc và những ví dụ đời thực hoặc những câu nói triết lý.
233
- - Kết thúc với một thông điệp sâu sắc, khơi dậy suy ngẫm cho người nghe.
234
- Mục tiêu: Khơi gợi suy nghĩ sâu sắc về cuộc sống, khiến người nghe tìm thấy ý nghĩa hoặc giá trị trong câu chuyện.
235
- """,
236
- "Phổ biến kiến thức Thống kê":
237
- """
238
- Tone giọng: Thân thiện, dễ hiểu, và mang tính giáo dục.
239
- Cấu trúc:
240
- - Bắt đầu với một câu hỏi hoặc một tình huống thực tế để thu hút sự chú ý.
241
- - Giải thích các khái niệm thống kê cơ bản một cách đơn giản và dễ hiểu, sử dụng ví dụ thực tế để minh họa.
242
- - Đưa ra các ứng dụng thực tế của thống kê trong đời sống hàng ngày hoặc trong các lĩnh vực cụ thể.
243
- - Kết thúc với một thông điệp khuyến khích người nghe áp dụng kiến thức thống kê vào cuộc sống.
244
- Mục tiêu: Giúp người nghe hiểu và yêu thích thống kê, thấy được giá trị và ứng dụng của nó trong cuộc sống.
245
- """
246
- }
247
-
248
- def create_docx(content, output_path):
249
- """
250
- Tạo file docx từ nội dung.
251
- """
252
- doc = Document()
253
- doc.add_paragraph(content)
254
- doc.save(output_path)
255
 
256
  def process_pdf(file_path):
257
- """
258
- Xử lý file PDF và trích xuất nội dung.
259
- """
260
- doc = fitz.open(file_path)
261
- text = ""
262
- for page in doc:
263
- text += page.get_text()
264
- return text
265
 
266
  def process_docx(file_path):
267
- """
268
- Xử lý file DOCX và trích xuất nội dung.
269
- """
270
- doc = Document(file_path)
271
- text = ""
272
- for para in doc.paragraphs:
273
- text += para.text
274
- return text
275
 
276
  # Giao diện Gradio
277
  def interface():
278
- with gr.Blocks() as app:
279
- gr.Markdown("# Ứng dụng Tạo Nội dung và Video")
280
-
281
- with gr.Tab("Tạo Nội dung"):
282
- prompt = gr.Textbox(label="Nhập yêu cầu nội dung")
283
- file_upload = gr.File(label="Tải lên file kèm theo", type="filepath")
284
-
285
- # Sử dụng gr.Radio thay vì gr.CheckboxGroup
286
- content_type = gr.Radio(label="Chọn loại nội dung",
287
- choices=CONTENT_TYPES,
288
- value=None) # Giá trị mặc định là không có gì được chọn
289
-
290
- content_button = gr.Button("Tạo Nội dung")
291
- content_output = gr.Textbox(label="Nội dung tạo ra", interactive=True)
292
- confirm_button = gr.Button("Xác nhận nội dung")
293
- download_docx = gr.File(label="Tải xuống file DOCX", interactive=False)
294
- download_audio = gr.File(label="Tải xuống file âm thanh",
295
- interactive=False)
296
- status_message = gr.Label(label="Trạng thái")
297
-
298
- def generate_content(prompt, file, content_type):
299
- try:
300
- status = "Đang xử lý..."
301
- if file and os.path.exists(file):
302
- mime_type, _ = mimetypes.guess_type(file)
303
- if mime_type == "application/pdf":
304
- file_content = process_pdf(file)
305
- prompt = f"{prompt}\n\nDưới đây là nội dung của file tài liệu:\n\n{file_content}"
306
- elif mime_type in (
307
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
308
- "application/msword"):
309
- file_content = process_docx(file)
310
- prompt = f"{prompt}\n\nDưới đây là nội dung của file tài liệu:\n\n{file_content}"
311
- else:
312
- raise ValueError("Định dạng file không được hỗ trợ.")
313
-
314
- if not content_type:
315
- raise ValueError("Vui lòng chọn một loại nội dung")
316
-
317
- script_content = create_content(prompt, content_type, "Tiếng Việt")
318
- docx_path = "script.docx"
319
- create_docx(script_content, docx_path)
320
-
321
- status = "Đã tạo nội dung thành công!"
322
- return script_content, docx_path, status
323
- except Exception as e:
324
- status = f"Đã xảy ra lỗi: {str(e)}"
325
- return "", None, status
326
-
327
- async def confirm_content(content):
328
- docx_path = "script.docx"
329
- create_docx(content, docx_path)
330
-
331
- audio_path = await async_text_to_speech(content, "alloy", "Tiếng Việt")
332
- return docx_path, audio_path, "Nội dung đã được xác nhận và âm thanh đã được tạo!"
333
-
334
- content_button.click(generate_content,
335
- inputs=[prompt, file_upload, content_type],
336
- outputs=[content_output, download_docx, status_message])
337
-
338
- confirm_button.click(lambda x: asyncio.run(confirm_content(x)),
339
- inputs=[content_output],
340
- outputs=[download_docx, download_audio, status_message])
341
-
342
- with gr.Tab("Tạo Âm thanh"):
343
- text_input = gr.Textbox(label="Nhập văn bản để chuyển đổi")
344
- voice_select = gr.Dropdown(label="Chọn giọng đọc",
345
- choices=VOICES) # Dropdown cho voice_select
346
- audio_button = gr.Button("Tạo Âm thanh")
347
- audio_output = gr.Audio(label="Âm thanh tạo ra")
348
- download_audio = gr.File(label="Tải xuống file âm thanh",
349
- interactive=False)
350
-
351
- def text_to_speech_func(text, voice):
352
- audio_path = text_to_speech(text, voice, "Tiếng Việt")
353
- return audio_path, audio_path
354
-
355
- audio_button.click(text_to_speech_func,
356
- inputs=[text_input, voice_select],
357
- outputs=[audio_output, download_audio])
358
-
359
- with gr.Tab("Tạo Video"):
360
  script_input = gr.Textbox(label="Nhập kịch bản")
361
  audio_file = gr.File(label="Chọn file âm thanh", type="filepath")
362
  keywords_output = gr.Textbox(label="Từ khóa", interactive=True)
@@ -366,51 +175,44 @@ def interface():
366
  create_video_button = gr.Button("Tạo Video")
367
  video_output = gr.Video(label="Video tạo ra")
368
 
369
- def create_video_func(script, audio_file, keywords, max_clip_duration, join_order, bgm_file):
370
- """ Tạo video từ các thông tin đầu vào. """
371
- try:
372
- # 1. Tính toán thời lượng video
373
- audio_clip = AudioFileClip(audio_file)
374
- video_duration = audio_clip.duration
375
-
376
- # 2. Tìm kiếm và tải video từ Pexels
377
- video_paths = []
378
- for keyword in keywords:
379
- for _ in range(int(video_duration // max_clip_duration)): # Chia thời lượng video cho thời lượng tối đa mỗi clip[^1^][1]
380
- image_url = get_pexels_image(keyword)
381
- if image_url:
382
- video_path = download_video_from_pexels(image_url) # Cần định nghĩa hàm download_video_from_pexels
383
- video_paths.append(video_path)
384
-
385
- # 3. Ghép video
386
- temp_dir = tempfile.mkdtemp()
387
- if join_order: # Ghép ngẫu nhiên
388
- random.shuffle(video_paths)
389
- combined_video_path = os.path.join(temp_dir, "combined_video.mp4")[^2^][2]
390
- combine_videos(combined_video_path, video_paths, audio_file, max_clip_duration)
391
-
392
- # 4. Gộp audio và nhạc nền
393
- final_video_path = "final_video.mp4"
394
- bgm_clip = AudioFileClip(bgm_file)
395
- final_audio = CompositeAudioClip([audio_clip, bgm_clip])
396
- final_video = VideoFileClip(combined_video_path).set_audio(final_audio)
397
- final_video.write_videofile(final_video_path)
398
-
399
- return final_video_path
400
- except Exception as e:
401
- print(f"Lỗi khi tạo video: {e}")
402
- return None
403
 
404
  return app
405
-
406
- # Khởi chạy ứng dụng
407
- if __name__ == "__main__":
408
- app = interface()
409
- app.launch()
410
 
411
- return app
412
-
413
  # Khởi chạy ứng dụng
414
  if __name__ == "__main__":
415
- app = interface()
416
- app.launch()
 
8
  from content_generation import create_content, CONTENT_TYPES
9
  from video_processing import create_video_func
10
  from moviepy.editor import AudioFileClip, CompositeAudioClip
11
+ from utils import (combine_videos, find_matching_image, get_pexels_image, split_audio, get_bgm_file)
12
  from video_processing import create_video
13
 
14
  # Danh sách giọng đọc
 
18
  LANGUAGES = ["Tiếng Anh", "Tiếng Việt", "Tiếng Hindi"]
19
 
20
  # Danh sách loại nội dung và hướng dẫn mặc định cho từng loại
21
+ CONTENT_TYPES = ["podcast", "giới thiệu", "triết lý sống", "Phổ biến kiến thức thống kê"]
 
22
  CONTENT_TYPE_INSTRUCTIONS = {
23
+ "podcast": """
 
24
  Tone giọng: Gần gũi, thân thiện nhưng chuyên sâu, thể hiện sự am hiểu về chủ đề.
25
  Cấu trúc:
26
  - Bắt đầu bằng một câu hỏi kích thích tư duy hoặc một câu chuyện mở màn gây tò mò.
 
29
  - Kết thúc podcast với một thông điệp sâu sắc, để lại sự suy ngẫm cho thính giả.
30
  Mục tiêu: Mang lại kiến thức giá trị, lôi cuốn thính giả tham gia suy nghĩ và cảm nhận sâu sắc về chủ đề.
31
  """,
32
+ "giới thiệu": """
 
33
  Tone giọng: Chuyên nghiệp, gãy gọn nhưng vẫn có sự truyền cảm.
34
  Cấu trúc:
35
  - Bắt đầu với một câu khẳng định mạnh mẽ về đối tượng được giới thiệu.
 
37
  - Kết thúc với một lời kêu gọi hành động, khích lệ người nghe tiếp tục lắng nghe hoặc tham gia.
38
  Mục tiêu: Đưa ra thông tin cô đọng, hấp dẫn, khiến người nghe cảm thấy bị thu hút và muốn tìm hiểu thêm.
39
  """,
40
+ "triết lý sống": """
 
41
  Tone giọng: Sâu sắc, truyền cảm hứng, mang tính chiêm nghiệm.
42
  Cấu trúc:
43
  - Bắt đầu bằng một câu hỏi sâu sắc hoặc ẩn dụ về cuộc sống.
 
45
  - Kết thúc với một thông điệp sâu sắc, khơi dậy suy ngẫm cho người nghe.
46
  Mục tiêu: Khơi gợi suy nghĩ sâu sắc về cuộc sống, khiến người nghe tìm thấy ý nghĩa hoặc giá trị trong câu chuyện.
47
  """,
48
+ "Phổ biến kiến thức Thống kê": """
 
49
  Tone giọng: Thân thiện, dễ hiểu, và mang tính giáo dục.
50
  Cấu trúc:
51
  - Bắt đầu với một câu hỏi hoặc một tình huống thực tế để thu hút sự chú ý.
 
57
  }
58
 
59
  def create_docx(content, output_path):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  """
61
+ Tạo file docx từ nội dung.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  """
63
+ doc = Document()
64
+ doc.add_paragraph(content)
65
+ doc.save(output_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
  def process_pdf(file_path):
68
+ """
69
+ Xử lý file PDF và trích xuất nội dung.
70
+ """
71
+ doc = fitz.open(file_path)
72
+ text = ""
73
+ for page in doc:
74
+ text += page.get_text()
75
+ return text
76
 
77
  def process_docx(file_path):
78
+ """
79
+ Xử lý file DOCX và trích xuất nội dung.
80
+ """
81
+ doc = Document(file_path)
82
+ text = ""
83
+ for para in doc.paragraphs:
84
+ text += para.text
85
+ return text
86
 
87
  # Giao diện Gradio
88
  def interface():
89
+ with gr.Blocks() as app:
90
+ gr.Markdown("# Ứng dụng Tạo Nội dung và Video")
91
+
92
+ with gr.Tab("Tạo Nội dung"):
93
+ prompt = gr.Textbox(label="Nhập yêu cầu nội dung")
94
+ file_upload = gr.File(label="Tải lên file kèm theo", type="filepath")
95
+
96
+ # Sử dụng gr.Radio thay vì gr.CheckboxGroup
97
+ content_type = gr.Radio(label="Chọn loại nội dung",
98
+ choices=CONTENT_TYPES,
99
+ value=None) # Giá trị mặc định là không có gì được chọn
100
+
101
+ content_button = gr.Button("Tạo Nội dung")
102
+ content_output = gr.Textbox(label="Nội dung tạo ra", interactive=True)
103
+ confirm_button = gr.Button("Xác nhận nội dung")
104
+ download_docx = gr.File(label="Tải xuống file DOCX", interactive=False)
105
+ download_audio = gr.File(label="Tải xuống file âm thanh", interactive=False)
106
+ status_message = gr.Label(label="Trạng thái")
107
+
108
+ def generate_content(prompt, file, content_type):
109
+ try:
110
+ status = "Đang xử lý..."
111
+ if file and os.path.exists(file):
112
+ mime_type, _ = mimetypes.guess_type(file)
113
+ if mime_type == "application/pdf":
114
+ file_content = process_pdf(file)
115
+ prompt = f"{prompt}\n\nDưới đây là nội dung của file tài liệu:\n\n{file_content}"
116
+ elif mime_type in (
117
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
118
+ "application/msword"):
119
+ file_content = process_docx(file)
120
+ prompt = f"{prompt}\n\nDưới đây là nội dung của file tài liệu:\n\n{file_content}"
121
+ else:
122
+ raise ValueError("Định dạng file không được hỗ trợ.")
123
+
124
+ if not content_type:
125
+ raise ValueError("Vui lòng chọn một loại nội dung")
126
+
127
+ script_content = create_content(prompt, content_type, "Tiếng Việt")
128
+ docx_path = "script.docx"
129
+ create_docx(script_content, docx_path)
130
+
131
+ status = "Đã tạo nội dung thành công!"
132
+ return script_content, docx_path, status
133
+ except Exception as e:
134
+ status = f"Đã xảy ra lỗi: {str(e)}"
135
+ return "", None, status
136
+
137
+ async def confirm_content(content):
138
+ docx_path = "script.docx"
139
+ create_docx(content, docx_path)
140
+
141
+ audio_path = await async_text_to_speech(content, "alloy", "Tiếng Việt")
142
+ return docx_path, audio_path, "Nội dung đã được xác nhận và âm thanh đã được tạo!"
143
+
144
+ content_button.click(generate_content,
145
+ inputs=[prompt, file_upload, content_type],
146
+ outputs=[content_output, download_docx, status_message])
147
+
148
+ confirm_button.click(lambda x: asyncio.run(confirm_content(x)),
149
+ inputs=[content_output],
150
+ outputs=[download_docx, download_audio, status_message])
151
+
152
+ with gr.Tab("Tạo Âm thanh"):
153
+ text_input = gr.Textbox(label="Nhập văn bản để chuyển đổi")
154
+ voice_select = gr.Dropdown(label="Chọn giọng đọc",
155
+ choices=VOICES) # Dropdown cho voice_select
156
+ audio_button = gr.Button("Tạo Âm thanh")
157
+ audio_output = gr.Audio(label="Âm thanh tạo ra")
158
+ download_audio = gr.File(label="Tải xuống file âm thanh", interactive=False)
159
+
160
+ def text_to_speech_func(text, voice):
161
+ audio_path = text_to_speech(text, voice, "Tiếng Việt")
162
+ return audio_path, audio_path
163
+
164
+ audio_button.click(text_to_speech_func,
165
+ inputs=[text_input, voice_select],
166
+ outputs=[audio_output, download_audio])
167
+
168
+ with gr.Tab("Tạo Video"):
 
 
169
  script_input = gr.Textbox(label="Nhập kịch bản")
170
  audio_file = gr.File(label="Chọn file âm thanh", type="filepath")
171
  keywords_output = gr.Textbox(label="Từ khóa", interactive=True)
 
175
  create_video_button = gr.Button("Tạo Video")
176
  video_output = gr.Video(label="Video tạo ra")
177
 
178
+ def create_video_func(script, audio_file, keywords, max_clip_duration, join_order, bgm_file):
179
+ """ Tạo video từ các thông tin đầu vào. """
180
+ try:
181
+ # 1. Tính toán thời lượng video
182
+ audio_clip = AudioFileClip(audio_file)
183
+ video_duration = audio_clip.duration
184
+
185
+ # 2. Tìm kiếm và tải video từ Pexels
186
+ video_paths = []
187
+ for keyword in keywords:
188
+ for _ in range(int(video_duration // max_clip_duration)): # Chia thời lượng video cho thời lượng tối đa mỗi clip
189
+ image_url = get_pexels_image(keyword)
190
+ if image_url:
191
+ video_path = download_video_from_pexels(image_url) # Cần định nghĩa hàm download_video_from_pexels
192
+ video_paths.append(video_path)
193
+
194
+ # 3. Ghép video
195
+ temp_dir = tempfile.mkdtemp()
196
+ if join_order: # Ghép ngẫu nhiên
197
+ random.shuffle(video_paths)
198
+ combined_video_path = os.path.join(temp_dir, "combined_video.mp4")
199
+ combine_videos(combined_video_path, video_paths, audio_file, max_clip_duration)
200
+
201
+ # 4. Gộp audio và nhạc nền
202
+ final_video_path = "final_video.mp4"
203
+ bgm_clip = AudioFileClip(bgm_file)
204
+ final_audio = CompositeAudioClip([audio_clip, bgm_clip])
205
+ final_video = VideoFileClip(combined_video_path).set_audio(final_audio)
206
+ final_video.write_videofile(final_video_path)
207
+
208
+ return final_video_path
209
+ except Exception as e:
210
+ print(f"Lỗi khi tạo video: {e}")
211
+ return None
212
 
213
  return app
 
 
 
 
 
214
 
 
 
215
  # Khởi chạy ứng dụng
216
  if __name__ == "__main__":
217
+ app = interface()
218
+ app.launch()