Spaces:
Sleeping
Sleeping
from moviepy.video.io.VideoFileClip import VideoFileClip, AudioFileClip | |
from moviepy.video.VideoClip import TextClip, ImageClip | |
from moviepy.video.compositing.CompositeVideoClip import concatenate_videoclips, CompositeVideoClip | |
from moviepy.audio.AudioClip import concatenate_audioclips | |
from moviepy.video.tools.subtitles import SubtitlesClip | |
from moviepy.video.VideoClip import ColorClip | |
import os | |
from itertools import accumulate | |
import pysrt | |
def format_time(seconds): | |
"""Chuyển đổi thời gian (giây) thành định dạng SRT hh:mm:ss,ms""" | |
mins, sec = divmod(seconds, 60) | |
hours, mins = divmod(mins, 60) | |
return f"{int(hours):02}:{int(mins):02}:{int(sec):02},{int((sec % 1) * 1000):03}" | |
def get_audio_duration(audio_path): | |
# Lọc các file có đuôi .mp3 | |
audio_paths = os.listdir(audio_path) | |
audio_list = [file for file in audio_paths if file.endswith(".mp3")] | |
# Khởi tạo danh sách audio duration | |
duration_list = [] | |
for audio_path in audio_list: | |
# Mở file âm thanh và lấy thời gian | |
with AudioFileClip(f"./audio/{audio_path}") as audio: | |
duration_list.append(audio.duration) | |
# Tính tổng tích lũy thời gian | |
duration_list = [format_time(time) for time in list(accumulate(duration_list))] | |
return [format_time(0.0)] + duration_list | |
def create_srt_from_time_and_text(duration_time, text_folder, output_srt): | |
subtitle = "" | |
subtitle_index = 1 | |
text_list = sorted([file for file in os.listdir(text_folder) if file.endswith('txt')]) | |
# Duyệt qua các mốc thời gian và file text | |
for i in range(len(duration_time) - 1): | |
start_time = duration_time[i] | |
end_time = duration_time[i + 1] | |
# Lấy tên file text tương ứng | |
text_file = text_list[i] | |
text_path = os.path.join(text_folder, text_file) | |
if os.path.exists(text_path): | |
with open(text_path, 'r', encoding='utf-8') as f: | |
text = f.read().strip() | |
# Thêm phần subtitle vào chuỗi kết quả | |
subtitle += f"{subtitle_index}\n{start_time} --> {end_time}\n{text}\n\n" | |
subtitle_index += 1 | |
else: | |
print(f"File {text_file} không tồn tại!") | |
# Lưu vào file SRT | |
with open(output_srt, 'w', encoding='utf-8') as f: | |
f.write(subtitle) | |
def concatenate_audio_files(audio_folder, output_audio_path): | |
# Lọc tất cả các file âm thanh .mp3 trong thư mục | |
audio_clips = [] | |
for file in sorted(os.listdir(audio_folder)): | |
if file.endswith('.mp3'): | |
audio_path = os.path.join(audio_folder, file) | |
audio_clip = AudioFileClip(audio_path) | |
audio_clips.append(audio_clip) | |
# Ghép tất cả các audio clip lại với nhau | |
final_audio = concatenate_audioclips(audio_clips) | |
# Lưu kết quả vào file output | |
final_audio.write_audiofile(output_audio_path, codec = 'libmp3lame') | |
print(f"File audio đã được lưu tại: {output_audio_path}") | |
def create_video_from_images(image_folder, audio_path, output_video_path): | |
# Đọc file âm thanh để lấy thời lượng | |
audio = AudioFileClip(audio_path) | |
total_duration = audio.duration # Tổng thời lượng video bằng thời lượng audio | |
# Đọc tất cả các file ảnh trong thư mục và sắp xếp theo tên | |
image_files = [file for file in sorted(os.listdir(image_folder)) if file.endswith("png")] | |
if not image_files: | |
raise ValueError("Không tìm thấy ảnh nào trong thư mục!") | |
# Tính thời lượng hiển thị cho mỗi ảnh | |
duration_per_image = total_duration / len(image_files) | |
# Tạo danh sách các clip ảnh | |
clips = [ImageClip(f"./image/{img}").with_duration(duration_per_image).resized(width=1280) for img in image_files] | |
# Ghép các clip ảnh lại với nhau | |
final_video = concatenate_videoclips(clips, method="chain") | |
# Gán âm thanh vào video | |
final_video .audio = audio | |
# Xuất video | |
final_video.write_videofile(output_video_path, codec="libx264", audio_codec="aac", fps=30) | |
print(f"Video đã được lưu tại: {output_video_path}") | |
def wrap_text(text, max_width): | |
""" | |
Tự động xuống dòng để vừa với chiều rộng max_width. | |
""" | |
import textwrap | |
return "\n".join(textwrap.wrap(text, width=max_width)) | |
def add_subtitles_to_video(video_path, subtitle_path, output_video_path): | |
""" | |
Thêm phụ đề từ file .srt trực tiếp vào video. | |
:param video_path: Đường dẫn video gốc | |
:param subtitle_path: Đường dẫn file .srt | |
:param output_video_path: Đường dẫn lưu video đầu ra | |
""" | |
# Đọc file video | |
video = VideoFileClip(video_path) | |
# Đọc file .srt | |
subs = pysrt.open(subtitle_path) | |
subtitle_clips = [] # Danh sách các đoạn phụ đề | |
# Xử lý từng dòng phụ đề | |
for sub in subs: | |
# Chuyển thời gian thành giây | |
start_time = sub.start.ordinal / 1000 # Chuyển từ milliseconds sang giây | |
end_time = sub.end.ordinal / 1000 | |
font = "./BeVietnamPro-Light.ttf" | |
# Tạo clip phụ đề | |
txt_clip = TextClip(font=font, text=wrap_text(sub.text, max_width=85), font_size=30, stroke_color="black", stroke_width=3, color="#fff") | |
# Đặt vị trí hiển thị (giữa phía dưới video) | |
txt_clip = txt_clip.with_position(('center', 'bottom')).with_duration(end_time - start_time).with_start(start_time) | |
subtitle_clips.append(txt_clip) | |
# Ghép phụ đề vào video | |
final_video = CompositeVideoClip([video] + subtitle_clips) | |
# Xuất video với phụ đề | |
final_video.write_videofile(output_video_path, fps=video.fps, codec='libx264', threads=4) | |
print(f"Video với phụ đề đã được lưu tại: {output_video_path}") | |
def text_to_video(): | |
duration_time = get_audio_duration("./audio") | |
create_srt_from_time_and_text(duration_time, './text', './output/subtitle.srt') | |
concatenate_audio_files("./audio","./output/final_audio.mp3") | |
create_video_from_images("./image","./output/final_audio.mp3","./output/output.mp4") | |
add_subtitles_to_video("./output/output.mp4", "./output/subtitle.srt", "./output/final_output.mp4") | |
if __name__ == "__main__": | |
text_to_video() |