164 lines
7.8 KiB
Python
164 lines
7.8 KiB
Python
import tkinter as tk
|
|
from tkinter import ttk, filedialog, simpledialog, Label
|
|
from gtts import gTTS
|
|
from moviepy.editor import *
|
|
from PIL import Image, ImageTk
|
|
import os
|
|
from datetime import datetime
|
|
from tkinter import messagebox
|
|
from moviepy.config import change_settings
|
|
change_settings({"IMAGEMAGICK_BINARY": r"C:\Program Files\ImageMagick-7.1.1-Q16-HDRI\magick.exe"})
|
|
|
|
class VideoCreatorApp:
|
|
def __init__(self, root):
|
|
self.root = root
|
|
self.setup_ui()
|
|
self.text_image_pairs = []
|
|
self.bgm_path = ''
|
|
self.save_path = "C:/Users/thdtl/Desktop/python/AutoYoutubeProject/Result" # 초기 저장 경로는 비워둡니다.
|
|
self.selected_item_index = None
|
|
|
|
def setup_ui(self):
|
|
self.root.title("YouTube Shorts Video Creator")
|
|
self.root.geometry("800x600") # Adjust the size of the window
|
|
self.listbox = tk.Listbox(self.root, width=70, height=20)
|
|
self.listbox.pack()
|
|
tk.Button(self.root, text="Add Text and Image", command=self.add_text_image_sequence).pack()
|
|
tk.Button(self.root, text="Edit Selected Text and Image", command=self.edit_selected_sequence).pack()
|
|
tk.Button(self.root, text="Delete Selected Text and Image", command=self.delete_selected_sequence).pack()
|
|
tk.Button(self.root, text="Set BGM", command=self.set_bgm).pack()
|
|
tk.Button(self.root, text="Remove BGM", command=self.remove_bgm).pack()
|
|
tk.Button(self.root, text="Set Save Path", command=self.set_save_path).pack()
|
|
tk.Button(self.root, text="Create Video", command=self.create_video).pack()
|
|
|
|
self.save_path_label = tk.Label(self.root, text="No path set")
|
|
self.save_path_label.pack()
|
|
|
|
self.image_label = Label(self.root)
|
|
self.image_label.pack()
|
|
|
|
self.progress = ttk.Progressbar(self.root, length=300, mode='determinate')
|
|
self.progress.pack()
|
|
|
|
self.listbox.bind('<<ListboxSelect>>', self.on_select)
|
|
|
|
def set_save_path(self):
|
|
# 사용자가 비디오를 저장할 경로를 선택할 수 있게 합니다.
|
|
path = filedialog.askdirectory() # 폴더 선택 다이얼로그를 엽니다.
|
|
if path:
|
|
self.save_path = path
|
|
self.save_path_label.config(text=path) # 선택된 경로를 레이블에 표시합니다.
|
|
|
|
def remove_bgm(self):
|
|
self.bgm_path = '' # 배경음악 경로를 빈 문자열로 설정
|
|
print("Background music removed") # 콘솔에 배경음악 제거 메시지 출력
|
|
|
|
def add_text_image_sequence(self):
|
|
text = simpledialog.askstring("Input", "Enter your text:", parent=self.root)
|
|
if text: # 텍스트가 입력되었다면
|
|
image_path = filedialog.askopenfilename(
|
|
title="Select related image",
|
|
filetypes=[("Image files", "*.jpg *.jpeg *.png *.gif")], # 이미지 파일 형식을 모두 허용
|
|
parent=self.root
|
|
)
|
|
# 이미지 선택을 취소하거나 비워둔 경우 None 또는 빈 문자열('')이 될 수 있습니다.
|
|
# 이 경우에도 텍스트는 저장되지만, 이미지 경로는 빈 문자열로 처리됩니다.
|
|
self.text_image_pairs.append((text, image_path if image_path else ""))
|
|
self.listbox.insert(tk.END, text)
|
|
else:
|
|
messagebox.showwarning("Warning", "No text was entered.")
|
|
|
|
|
|
def edit_selected_sequence(self):
|
|
if self.selected_item_index is not None:
|
|
new_text = simpledialog.askstring("Edit text", "Enter new text:", parent=self.root, initialvalue=self.text_image_pairs[self.selected_item_index][0])
|
|
if new_text:
|
|
new_image_path = filedialog.askopenfilename(title="Select new related image", filetypes=[("Image files", "*.jpg *.jpeg *.png")])
|
|
if new_image_path:
|
|
self.text_image_pairs[self.selected_item_index] = (new_text, new_image_path)
|
|
self.listbox.delete(self.selected_item_index)
|
|
self.listbox.insert(self.selected_item_index, new_text)
|
|
|
|
def delete_selected_sequence(self):
|
|
if self.selected_item_index is not None:
|
|
del self.text_image_pairs[self.selected_item_index]
|
|
self.listbox.delete(self.selected_item_index)
|
|
|
|
def on_select(self, event):
|
|
widget = event.widget
|
|
if widget.curselection():
|
|
index = int(widget.curselection()[0])
|
|
self.selected_item_index = index
|
|
# 선택된 항목에 대한 이미지 미리보기 표시
|
|
img_path = self.text_image_pairs[index][1]
|
|
self.display_image(img_path)
|
|
|
|
def set_bgm(self):
|
|
self.bgm_path = filedialog.askopenfilename(title="Select BGM file", filetypes=[("Audio files", "*.mp3 *.wav")])
|
|
|
|
def create_video(self):
|
|
self.progress['value'] = 0
|
|
total_steps = len(self.text_image_pairs)
|
|
current_step = 0
|
|
|
|
video_clips = [] # 최종 비디오 클립들을 저장할 리스트
|
|
|
|
for text, img_path in self.text_image_pairs:
|
|
# TTS 생성 및 오디오 클립 저장
|
|
tts = gTTS(text=text, lang='ko')
|
|
tts_file = f"temp_audio_{current_step}.mp3"
|
|
tts.save(tts_file)
|
|
tts_audio_clip = AudioFileClip(tts_file).volumex(2.0)
|
|
|
|
# 이미지 경로가 없는 경우, 투명한 배경의 클립 생성
|
|
if not img_path:
|
|
img_clip = ColorClip(size=(1080, 1920), color=(0, 0, 0, 0), duration=tts_audio_clip.duration)
|
|
else:
|
|
img_clip = ImageClip(img_path).set_duration(tts_audio_clip.duration).resize(newsize=(1080, 1920)).set_position('center')
|
|
|
|
# 자막 클립 생성
|
|
txt_clip = TextClip(text, font='Arial', fontsize=24, color='white', bg_color=None).set_pos('bottom').set_duration(tts_audio_clip.duration)
|
|
|
|
# 이미지 클립과 자막 클립을 합성
|
|
video_clip = CompositeVideoClip([img_clip, txt_clip], size=(1080, 1920)).set_duration(tts_audio_clip.duration).set_audio(tts_audio_clip)
|
|
video_clips.append(video_clip)
|
|
|
|
os.remove(tts_file)
|
|
current_step += 1
|
|
self.progress['value'] = (current_step / total_steps) * 100
|
|
self.root.update_idletasks()
|
|
|
|
# 모든 비디오 클립을 순차적으로 결합
|
|
final_video_clip = concatenate_videoclips(video_clips, method="compose")
|
|
|
|
# 배경음악 설정
|
|
if self.bgm_path:
|
|
bgm = AudioFileClip(self.bgm_path).volumex(0.5)
|
|
final_video_clip = final_video_clip.set_audio(CompositeAudioClip([final_video_clip.audio, bgm.set_duration(final_video_clip.duration)]))
|
|
|
|
# 최종 비디오 저장 경로 설정
|
|
if not self.save_path:
|
|
messagebox.showerror("Error", "Please set a save path first.")
|
|
return
|
|
|
|
if not os.path.exists("Result"):
|
|
os.makedirs("Result")
|
|
current_time = datetime.now().strftime("%Y%m%d%H%M%S")
|
|
final_video_file = os.path.join(self.save_path, f"{current_time}.mp4")
|
|
final_video_clip.write_videofile(final_video_file, fps=24, codec="libx264", audio_codec="aac")
|
|
|
|
self.progress['value'] = 100
|
|
messagebox.showinfo("Video Creator", f"Video creation completed successfully! Saved to {final_video_file}")
|
|
|
|
def display_image(self, img_path):
|
|
img = Image.open(img_path)
|
|
img = img.resize((200, 200), Image.Resampling.LANCZOS) # 이미지 크기 조정
|
|
img_tk = ImageTk.PhotoImage(img)
|
|
self.image_label.configure(image=img_tk)
|
|
self.image_label.image = img_tk # 참조 추가
|
|
|
|
|
|
if __name__ == "__main__":
|
|
root = tk.Tk()
|
|
app = VideoCreatorApp(root)
|
|
root.mainloop() |