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_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") |