TDN-M commited on
Commit
b6fccee
·
verified ·
1 Parent(s): d76c658

Upload 6 files

Browse files
Files changed (3) hide show
  1. app.py +188 -234
  2. content_generation.py +1 -10
  3. video_processing.py +0 -1
app.py CHANGED
@@ -1,235 +1,189 @@
1
- import asyncio
2
- import mimetypes
3
- import os
4
- import tempfile
5
- import glob
6
- import random
7
- import gradio as gr
8
- from docx import Document
9
- from audio_processing import async_text_to_speech, text_to_speech
10
- from content_generation import create_content, CONTENT_TYPES
11
- from video_processing import create_video_func
12
- from moviepy.editor import AudioFileClip, CompositeAudioClip
13
- from utils import (combine_videos, get_pexels_image, get_bgm_file)
14
- from video_processing import create_video
15
-
16
- # Danh sách giọng đọc
17
- VOICES = ["alloy", "echo", "fable", "onyx", "nova", "shimmer"]
18
-
19
- # Danh sách ngôn ngữ (chưa được sử dụng trong mã)
20
- LANGUAGES = ["Tiếng Anh", "Tiếng Việt", "Tiếng Hindi"]
21
-
22
- # Danh sách loại nội dung và hướng dẫn mặc định cho từng loại
23
- CONTENT_TYPES = ["podcast", "giới thiệu", "triết lý sống", "Phổ biến kiến thức thống kê"]
24
- CONTENT_TYPE_INSTRUCTIONS = {
25
- "podcast": """
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ò.
29
- - 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.
30
- - Xây dựng các phần chuyển tiếp mượt mà giữa các ý.
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
- Tone giọng: Chuyên nghiệp, gãy gọn nhưng vẫn có sự truyền cảm.
36
- Cấu trúc:
37
- - Bắt đầu với một câu khẳng định mạnh mẽ về đối tượng được giới thiệu.
38
- - 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.
39
- - 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.
40
- 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.
41
- """,
42
- "triết lý sống": """
43
- Tone giọng: Sâu sắc, truyền cảm hứng, mang tính chiêm nghiệm.
44
- Cấu trúc:
45
- - Bắt đầu bằng một câu hỏi sâu sắc hoặc ẩn dụ về cuộc sống.
46
- - 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ý.
47
- - 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.
48
- 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.
49
- """,
50
- "Phổ biến kiến thức Thống kê": """
51
- Tone giọng: Thân thiện, dễ hiểu, và mang tính giáo dục.
52
- Cấu trúc:
53
- - 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ú ý.
54
- - 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.
55
- - Đư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ể.
56
- - 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.
57
- 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.
58
- """
59
- }
60
-
61
- def create_docx(content, output_path):
62
- """
63
- Tạo file docx từ nội dung.
64
- """
65
- doc = Document()
66
- doc.add_paragraph(content)
67
- doc.save(output_path)
68
-
69
- def process_pdf(file_path):
70
- """
71
- Xử file PDF và trích xuất nội dung.
72
- """
73
- doc = fitz.open(file_path)
74
- text = ""
75
- for page in doc:
76
- text += page.get_text()
77
- return text
78
-
79
- def process_docx(file_path):
80
- """
81
- Xử file DOCX và trích xuất nội dung.
82
- """
83
- doc = Document(file_path)
84
- text = ""
85
- for para in doc.paragraphs:
86
- text += para.text
87
- return text
88
-
89
- def get_bgm_file_list():
90
- """
91
- Trả về danh sách các tệp nhạc nền.
92
- """
93
- # Giả sử bạn có một thư mục chứa các tệp nhạc nền
94
- song_dir = "/data/bg-music"
95
- return [os.path.basename(file) for file in glob.glob(os.path.join(song_dir, "*.mp3"))]
96
-
97
- # Giao diện Gradio
98
- def interface():
99
- with gr.Blocks() as app:
100
- gr.Markdown("# Ứng dụng Tạo Nội dung và Video")
101
-
102
- with gr.Tab("Tạo Nội dung"):
103
- prompt = gr.Textbox(label="Nhập yêu cầu nội dung")
104
- file_upload = gr.File(label="Tải lên file kèm theo", type="filepath")
105
-
106
- # Sử dụng gr.Radio thay gr.CheckboxGroup
107
- content_type = gr.Radio(label="Chọn loại nội dung",
108
- choices=CONTENT_TYPES,
109
- value=None) # Giá trị mặc định là không có gì được chọn
110
-
111
- content_button = gr.Button("Tạo Nội dung")
112
- content_output = gr.Textbox(label="Nội dung tạo ra", interactive=True)
113
- confirm_button = gr.Button("Xác nhận nội dung")
114
- download_docx = gr.File(label="Tải xuống file DOCX", interactive=False)
115
- download_audio = gr.File(label="Tải xuống file âm thanh", interactive=False)
116
- status_message = gr.Label(label="Trạng thái")
117
-
118
- def generate_content(prompt, file, content_type):
119
- try:
120
- status = "Đang xử lý..."
121
- if file and os.path.exists(file):
122
- mime_type, _ = mimetypes.guess_type(file)
123
- if mime_type == "application/pdf":
124
- file_content = process_pdf(file)
125
- prompt = f"{prompt}\n\nDưới đây là nội dung của file tài liệu:\n\n{file_content}"
126
- elif mime_type in (
127
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
128
- "application/msword"):
129
- file_content = process_docx(file)
130
- prompt = f"{prompt}\n\nDưới đây là nội dung của file tài liệu:\n\n{file_content}"
131
- else:
132
- raise ValueError("Định dạng file không được hỗ trợ.")
133
-
134
- if not content_type:
135
- raise ValueError("Vui lòng chọn một loại nội dung")
136
-
137
- script_content = create_content(prompt, content_type, "Tiếng Việt")
138
- docx_path = "script.docx"
139
- create_docx(script_content, docx_path)
140
-
141
- status = "Đã tạo nội dung thành công!"
142
- return script_content, docx_path, status
143
- except Exception as e:
144
- status = f"Đã xảy ra lỗi: {str(e)}"
145
- return "", None, status
146
-
147
- async def confirm_content(content):
148
- docx_path = "script.docx"
149
- create_docx(content, docx_path)
150
-
151
- audio_path = await async_text_to_speech(content, "alloy", "Tiếng Việt")
152
- return docx_path, audio_path, "Nội dung đã được xác nhận âm thanh đã được tạo!"
153
-
154
- content_button.click(generate_content,
155
- inputs=[prompt, file_upload, content_type],
156
- outputs=[content_output, download_docx, status_message])
157
-
158
- confirm_button.click(lambda x: asyncio.run(confirm_content(x)),
159
- inputs=[content_output],
160
- outputs=[download_docx, download_audio, status_message])
161
-
162
- with gr.Tab("Tạo Âm thanh"):
163
- text_input = gr.Textbox(label="Nhập văn bản để chuyển đổi")
164
- voice_select = gr.Dropdown(label="Chọn giọng đọc",
165
- choices=VOICES) # Dropdown cho voice_select
166
- audio_button = gr.Button("Tạo Âm thanh")
167
- audio_output = gr.Audio(label="Âm thanh tạo ra")
168
- download_audio = gr.File(label="Tải xuống file âm thanh", interactive=False)
169
-
170
- def text_to_speech_func(text, voice):
171
- audio_path = text_to_speech(text, voice, "Tiếng Việt")
172
- return audio_path, audio_path
173
-
174
- audio_button.click(text_to_speech_func,
175
- inputs=[text_input, voice_select],
176
- outputs=[audio_output, download_audio])
177
-
178
- with gr.Tab("Tạo Video"):
179
- script_input = gr.Textbox(label="Nhập kịch bản")
180
- audio_file = gr.File(label="Chọn file âm thanh", type="filepath")
181
- keywords_output = gr.Textbox(label="Từ khóa", interactive=True)
182
- max_clip_duration = gr.Slider(minimum=2, maximum=5, step=1, label="Thời lượng tối đa mỗi video (giây)")
183
- join_order = gr.Checkbox(label="Ghép ngẫu nhiên", value=True) # Mặc định là ghép ngẫu nhiên
184
- bgm_files = gr.Dropdown(choices=get_bgm_file_list(), label="Chọn nhạc nền")
185
- video_output = gr.Video(label="Video tạo ra")
186
-
187
- # Thêm nút để tạo video
188
- video_button = gr.Button("Tạo Video")
189
-
190
- def create_video_func(script, audio_file, keywords, max_clip_duration, join_order, bgm_file):
191
- """ Tạo video từ các thông tin đầu vào. """
192
- try:
193
- # 1. Tính toán thời lượng video
194
- audio_clip = AudioFileClip(audio_file)
195
- video_duration = audio_clip.duration
196
-
197
- # 2. Tìm kiếm và tải video từ Pexels
198
- video_paths = []
199
- for keyword in keywords:
200
- 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
201
- image_url = get_pexels_image(keyword)
202
- if image_url:
203
- video_path = download_video_from_pexels(image_url) # Cần định nghĩa hàm download_video_from_pexels
204
- video_paths.append(video_path)
205
-
206
- # 3. Ghép video
207
- temp_dir = tempfile.mkdtemp()
208
- if join_order: # Ghép ngẫu nhiên
209
- random.shuffle(video_paths)
210
- combined_video_path = os.path.join(temp_dir, "combined_video.mp4")
211
- combine_videos(combined_video_path, video_paths, audio_file, max_clip_duration)
212
-
213
- # 4. Gộp audio và nhạc nền
214
- final_video_path = "final_video.mp4"
215
- bgm_clip = AudioFileClip(bgm_file)
216
- final_audio = CompositeAudioClip([audio_clip, bgm_clip])
217
- final_video = VideoFileClip(combined_video_path).set_audio(final_audio)
218
- final_video.write_videofile(final_video_path)
219
-
220
- return final_video_path
221
- except Exception as e:
222
- print(f"Lỗi khi tạo video: {e}")
223
- return None
224
-
225
- # Liên kết nút với hàm xử lý video
226
- video_button.click(create_video_func,
227
- inputs=[script_input, audio_file, keywords_output, max_clip_duration, join_order, bgm_files],
228
- outputs=video_output)
229
-
230
- return app
231
-
232
- # Khởi chạy ứng dụng
233
- if __name__ == "__main__":
234
- app = interface()
235
  app.launch()
 
1
+ import asyncio
2
+ import mimetypes
3
+ import os
4
+ import tempfile
5
+ import glob
6
+ import gradio as gr
7
+ from docx import Document
8
+ from audio_processing import async_text_to_speech, text_to_speech
9
+ from content_generation import create_content, CONTENT_TYPES
10
+ from video_processing import create_video_func
11
+ from moviepy.editor import AudioFileClip, CompositeAudioClip
12
+ from utils import (combine_videos, get_pexels_image, get_bgm_file)
13
+ from video_processing import create_video
14
+ from content_generation import create_content, CONTENT_TYPES
15
+
16
+ def create_docx(content, output_path):
17
+ """
18
+ Tạo file docx từ nội dung.
19
+ """
20
+ doc = Document()
21
+ doc.add_paragraph(content)
22
+ doc.save(output_path)
23
+
24
+ def process_pdf(file_path):
25
+ """
26
+ Xử file PDF trích xuất nội dung.
27
+ """
28
+ doc = fitz.open(file_path)
29
+ text = ""
30
+ for page in doc:
31
+ text += page.get_text()
32
+ return text
33
+
34
+ def process_docx(file_path):
35
+ """
36
+ Xử lý file DOCX và trích xuất nội dung.
37
+ """
38
+ doc = Document(file_path)
39
+ text = ""
40
+ for para in doc.paragraphs:
41
+ text += para.text
42
+ return text
43
+
44
+ def get_bgm_file_list():
45
+ """
46
+ Trả về danh sách các tệp nhạc nền.
47
+ """
48
+ # Giả sử bạn một thư mục chứa các tệp nhạc nền
49
+ song_dir = "/data/bg-music"
50
+ return [os.path.basename(file) for file in glob.glob(os.path.join(song_dir, "*.mp3"))]
51
+
52
+ # Giao diện Gradio
53
+ def interface():
54
+ with gr.Blocks() as app:
55
+ gr.Markdown("# Ứng dụng Tạo Nội dung Video")
56
+
57
+ with gr.Tab("Tạo Nội dung"):
58
+ prompt = gr.Textbox(label="Nhập yêu cầu nội dung")
59
+ file_upload = gr.File(label="Tải lên file kèm theo", type="filepath")
60
+
61
+ # Sử dụng gr.Radio thay vì gr.CheckboxGroup
62
+ content_type = gr.Radio(label="Chọn loại nội dung",
63
+ choices=CONTENT_TYPES,
64
+ value=None) # Giá trị mặc định là không có gì được chọn
65
+
66
+ content_button = gr.Button("Tạo Nội dung")
67
+ content_output = gr.Textbox(label="Nội dung tạo ra", interactive=True)
68
+ confirm_button = gr.Button("Xác nhận nội dung")
69
+ download_docx = gr.File(label="Tải xuống file DOCX", interactive=False)
70
+ download_audio = gr.File(label="Tải xuống file âm thanh", interactive=False)
71
+ status_message = gr.Label(label="Trạng thái")
72
+
73
+ def generate_content(prompt, file, content_type):
74
+ try:
75
+ status = "Đang xử lý..."
76
+ if file and os.path.exists(file):
77
+ mime_type, _ = mimetypes.guess_type(file)
78
+ if mime_type == "application/pdf":
79
+ file_content = process_pdf(file)
80
+ prompt = f"{prompt}\n\nDưới đây là nội dung của file tài liệu:\n\n{file_content}"
81
+ elif mime_type in (
82
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
83
+ "application/msword"):
84
+ file_content = process_docx(file)
85
+ prompt = f"{prompt}\n\nDưới đây là nội dung của file tài liệu:\n\n{file_content}"
86
+ else:
87
+ raise ValueError("Định dạng file không được hỗ trợ.")
88
+
89
+ if not content_type:
90
+ raise ValueError("Vui lòng chọn một loại nội dung")
91
+
92
+ script_content = create_content(prompt, content_type, "Tiếng Việt")
93
+ docx_path = "script.docx"
94
+ create_docx(script_content, docx_path)
95
+
96
+ status = "Đã tạo nội dung thành công!"
97
+ return script_content, docx_path, status
98
+ except Exception as e:
99
+ status = f"Đã xảy ra lỗi: {str(e)}"
100
+ return "", None, status
101
+
102
+ async def confirm_content(content):
103
+ docx_path = "script.docx"
104
+ create_docx(content, docx_path)
105
+
106
+ audio_path = await async_text_to_speech(content, "alloy", "Tiếng Việt")
107
+ return docx_path, audio_path, "Nội dung đã được xác nhận và âm thanh đã được tạo!"
108
+
109
+ content_button.click(generate_content,
110
+ inputs=[prompt, file_upload, content_type],
111
+ outputs=[content_output, download_docx, status_message])
112
+
113
+ confirm_button.click(lambda x: asyncio.run(confirm_content(x)),
114
+ inputs=[content_output],
115
+ outputs=[download_docx, download_audio, status_message])
116
+
117
+ with gr.Tab("Tạo Âm thanh"):
118
+ text_input = gr.Textbox(label="Nhập văn bản để chuyển đổi")
119
+ voice_select = gr.Dropdown(label="Chọn giọng đọc",
120
+ choices=VOICES) # Dropdown cho voice_select
121
+ audio_button = gr.Button("Tạo Âm thanh")
122
+ audio_output = gr.Audio(label="Âm thanh tạo ra")
123
+ download_audio = gr.File(label="Tải xuống file âm thanh", interactive=False)
124
+
125
+ def text_to_speech_func(text, voice):
126
+ audio_path = text_to_speech(text, voice, "Tiếng Việt")
127
+ return audio_path, audio_path
128
+
129
+ audio_button.click(text_to_speech_func,
130
+ inputs=[text_input, voice_select],
131
+ outputs=[audio_output, download_audio])
132
+
133
+ with gr.Tab("Tạo Video"):
134
+ script_input = gr.Textbox(label="Nhập kịch bản")
135
+ audio_file = gr.File(label="Chọn file âm thanh", type="filepath")
136
+ keywords_output = gr.Textbox(label="Từ khóa", interactive=True)
137
+ max_clip_duration = gr.Slider(minimum=2, maximum=5, step=1, label="Thời lượng tối đa mỗi video (giây)")
138
+ join_order = gr.Checkbox(label="Ghép ngẫu nhiên", value=True) # Mặc định là ghép ngẫu nhiên
139
+ bgm_files = gr.Dropdown(choices=get_bgm_file_list(), label="Chọn nhạc nền")
140
+ video_output = gr.Video(label="Video tạo ra")
141
+
142
+ # Thêm nút để tạo video
143
+ video_button = gr.Button("Tạo Video")
144
+
145
+ def create_video_func(script, audio_file, keywords, max_clip_duration, join_order, bgm_file):
146
+ """ Tạo video từ các thông tin đầu vào. """
147
+ try:
148
+ # 1. Tính toán thời lượng video
149
+ audio_clip = AudioFileClip(audio_file)
150
+ video_duration = audio_clip.duration
151
+
152
+ # 2. Tìm kiếmtải video từ Pexels
153
+ video_paths = []
154
+ for keyword in keywords.split(','):
155
+ video_url = get_pexels_video(keyword.strip())
156
+ if video_url:
157
+ video_path = download_video(video_url) # Sử dụng hàm download_video
158
+ video_paths.append(video_path)
159
+
160
+ # 3. Ghép video
161
+ temp_dir = tempfile.mkdtemp()
162
+ if join_order: # Ghép ngẫu nhiên
163
+ random.shuffle(video_paths)
164
+ combined_video_path = os.path.join(temp_dir, "combined_video.mp4")
165
+ combine_videos(combined_video_path, video_paths, audio_file, max_clip_duration)
166
+
167
+ # 4. Gộp audio và nhạc nền
168
+ final_video_path = "final_video.mp4"
169
+ bgm_clip = AudioFileClip(bgm_file)
170
+ final_audio = CompositeAudioClip([audio_clip, bgm_clip])
171
+ final_video = VideoFileClip(combined_video_path).set_audio(final_audio)
172
+ final_video.write_videofile(final_video_path)
173
+
174
+ return final_video_path
175
+ except Exception as e:
176
+ print(f"Lỗi khi tạo video: {e}")
177
+ return None
178
+
179
+ # Liên kết nút với hàm xử lý video
180
+ video_button.click(create_video_func,
181
+ inputs=[script_input, audio_file, keywords_output, max_clip_duration, join_order, bgm_files],
182
+ outputs=video_output)
183
+
184
+ return app
185
+
186
+ # Khởi chạy ứng dụng
187
+ if __name__ == "__main__":
188
+ app = interface()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  app.launch()
content_generation.py CHANGED
@@ -50,18 +50,9 @@ CONTENT_TYPE_INSTRUCTIONS = {
50
  }
51
 
52
  def create_content(prompt, content_type, language):
53
- """
54
- Tạo nội dung dựa trên prompt, loại nội dung và ngôn ngữ.
55
- """
56
  content_type_instructions = CONTENT_TYPE_INSTRUCTIONS.get(content_type, "")
57
  general_instructions = f"""
58
- Viết một kịch bản dựa trên các ý chính và ý tưởng sáng tạo từ yêu cầu của người dùng. Sử dụng giọng điệu trò chuyện và bao gồm bất kỳ bối cảnh hoặc giải thích cần thiết nào để làm cho nội dung dễ tiếp cận với khán giả.
59
- Bắt đầu kịch bản bằng cách nêu rõ đây là một bài tóm tắt, tham chiếu đến tiêu đề hoặc đề mục trong văn bản đầu vào. Nếu văn bản đầu vào không có tiêu đề, hãy đưa ra một tóm tắt ngắn gọn về nội dung được đề cập để mở đầu.
60
- Bao gồm các định nghĩa và thuật ngữ rõ ràng, cùng với ví dụ cho tất cả các vấn đề chính.
61
- Không bao gồm bất kỳ placeholder nào trong ngoặc vuông như [Host] hoặc [Guest]. Thiết kế đầu ra của bạn để được đọc to - nó sẽ được chuyển đổi trực tiếp thành âm thanh.
62
- Chỉ có một người nói, bạn. Giữ đúng chủ đề và duy trì một luồng hấp dẫn.
63
- Tóm tắt một cách tự nhiên những hiểu biết và bài học chính từ bài tóm tắt. Điều này nên diễn ra một cách tự nhiên từ cuộc trò chuyện, nhắc lại các điểm chính một cách thân mật, như trong một cuộc trò chuyện.
64
- Bài tóm tắt nên có khoảng 3000 từ.
65
  Hãy tuân theo những hướng dẫn cụ thể sau cho thể loại {content_type}:
66
  {content_type_instructions}
67
  Ngôn ngữ sử dụng: {language}
 
50
  }
51
 
52
  def create_content(prompt, content_type, language):
 
 
 
53
  content_type_instructions = CONTENT_TYPE_INSTRUCTIONS.get(content_type, "")
54
  general_instructions = f"""
55
+ Viết một kịch bản dựa trên các ý chính và ý tưởng sáng tạo từ yêu cầu của người dùng...
 
 
 
 
 
 
56
  Hãy tuân theo những hướng dẫn cụ thể sau cho thể loại {content_type}:
57
  {content_type_instructions}
58
  Ngôn ngữ sử dụng: {language}
video_processing.py CHANGED
@@ -2,7 +2,6 @@ import os
2
  import random
3
  import shutil
4
  import tempfile
5
- from content_generation import extract_key_contents
6
  from concurrent.futures import ThreadPoolExecutor
7
  from moviepy.editor import (
8
  AudioFileClip,
 
2
  import random
3
  import shutil
4
  import tempfile
 
5
  from concurrent.futures import ThreadPoolExecutor
6
  from moviepy.editor import (
7
  AudioFileClip,