know-flow / src /text_to_video.py
Hieucyber2208's picture
Update src/text_to_video.py
2433c72 verified
raw
history blame
9.4 kB
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_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') and file != "text.txt" and file != "requirements.txt"])
print(f"Accessing duration_time list:{duration_time} with lenght: {len(duration_time)}")
# Duyệt qua các mốc thời gian và file text
for i in range(len(duration_time) - 1):
print(f"Accessing text_list list: {text_list} with length: {len(text_list)}")
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):
"""
Concatenate all .mp3 files in a folder into a single audio file.
Parameters:
audio_folder (str): The folder containing audio files.
output_audio_path (str): The path to save the concatenated audio.
"""
print("\n[DEBUG] Function: concatenate_audio_files")
print(f"Received parameters -> audio_folder: {audio_folder}, output_audio_path: {output_audio_path}")
# Validate if the folder exists
if not os.path.isdir(audio_folder):
print(f"[ERROR] Directory does not exist: {audio_folder}")
return
audio_clips = []
audio_files = sorted([f for f in os.listdir(audio_folder) if f.endswith('.mp3')])
print(f"[DEBUG] Found audio files: {audio_files}")
if not audio_files:
print("[ERROR] No .mp3 files found in the directory!")
return
# Load audio clips
for file in audio_files:
audio_path = os.path.join(audio_folder, file)
try:
print(f"[DEBUG] Loading audio file: {audio_path}")
audio_clip = AudioFileClip(audio_path)
audio_clips.append(audio_clip)
except Exception as e:
print(f"[ERROR] Failed to load {file}: {e}")
# Ensure there are clips to concatenate
if not audio_clips:
print("[ERROR] No valid audio clips to concatenate!")
return
try:
print("[DEBUG] Concatenating audio clips...")
final_audio = concatenate_audioclips(audio_clips)
# Save the final concatenated audio
print(f"[DEBUG] Saving final audio to: {output_audio_path}")
final_audio.write_audiofile(output_audio_path, codec='libmp3lame')
print(f"✅ File audio has been saved at: {output_audio_path}")
except Exception as e:
print(f"[ERROR] Failed to concatenate and save audio: {e}")
import os
from moviepy.editor import ImageClip, AudioFileClip, concatenate_videoclips
def create_video_from_images(image_folder, audio_path, output_video_path):
"""
Create a video from images and an audio file.
Parameters:
image_folder (str): Folder containing images.
audio_path (str): Path to the background audio file.
output_video_path (str): Path to save the final video.
"""
print("\n[DEBUG] Function: create_video_from_images")
print(f"Received parameters -> image_folder: {image_folder}, audio_path: {audio_path}, output_video_path: {output_video_path}")
# Validate input folder and file existence
if not os.path.isdir(image_folder):
print(f"[ERROR] Image folder does not exist: {image_folder}")
return
if not os.path.isfile(audio_path):
print(f"[ERROR] Audio file does not exist: {audio_path}")
return
try:
# Load audio file
print(f"[DEBUG] Loading audio file: {audio_path}")
audio = AudioFileClip(audio_path)
total_duration = audio.duration
print(f"[DEBUG] Audio duration: {total_duration:.2f} seconds")
# Get image files
image_files = sorted([file for file in os.listdir(image_folder) if file.endswith(".png")])
print(f"[DEBUG] Found image files: {image_files}")
if not image_files:
print("[ERROR] No images found in the directory!")
return
# Calculate duration per image
duration_per_image = total_duration / len(image_files)
print(f"[DEBUG] Duration per image: {duration_per_image:.2f} seconds")
# Create image clips
clips = []
for img in image_files:
img_path = os.path.join(image_folder, img)
try:
print(f"[DEBUG] Loading image: {img_path}")
clip = ImageClip(img_path).with_duration(duration_per_image).resize(width=1280)
clips.append(clip)
except Exception as e:
print(f"[ERROR] Failed to load image {img}: {e}")
if not clips:
print("[ERROR] No valid image clips to create video!")
return
# Concatenate image clips
print("[DEBUG] Concatenating image clips...")
final_video = concatenate_videoclips(clips, method="chain")
# Add audio to video
print("[DEBUG] Attaching audio to video...")
final_video.audio = audio
# Export video
print(f"[DEBUG] Saving final video to: {output_video_path}")
final_video.write_videofile(output_video_path, codec="libx264", audio_codec="aac", fps=30)
print(f"✅ Video has been saved at: {output_video_path}")
except Exception as e:
print(f"[ERROR] An error occurred: {e}")
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("./")
create_srt_from_time_and_text(duration_time, './', 'subtitle.srt')
concatenate_audio_files("./","final_audio.mp3")
create_video_from_images("./","final_audio.mp3","output.mp4")
add_subtitles_to_video("output.mp4", "subtitle.srt", "final_output.mp4")