first commit

dd
This commit is contained in:
thdtl5487@gmail.com 2024-03-04 23:55:32 +09:00
parent 3b77e41747
commit 6400f9d348
140 changed files with 986 additions and 0 deletions

Binary file not shown.

94
GPTRequest.py Normal file
View File

@ -0,0 +1,94 @@
# 코드를 작성하기 전에 전체적인 구조를 계획해 보겠습니다.
# 1. 필요한 라이브러리를 불러옵니다.
import os
import datetime
import openai # OpenAI API를 사용하기 위한 라이브러리
# 2. ChatGPT와의 소통을 담당할 함수를 정의합니다.
def ask_chatgpt(question, api_key):
openai.api_key = api_key
response = openai.Completion.create(
model="gpt-3.5-turbo", # 이 모델은 최신 버전에서 사용됩니다.
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": question}
]
)
# 응답 형식에 따라 적절한 필드를 반환해야 합니다.
# 새로운 API에 따라, 응답 형식이 달라질 수 있으므로, 정확한 필드를 확인해야 합니다.
return response.choices[0].text.strip() # 'message' 대신 'text'를 사용
# 3. 응답을 분석하여 파일 제목을 결정하는 함수를 정의합니다.
def determine_filename(response):
"""
응답 텍스트를 분석하여 파일 제목을 결정하는 함수.
:param response: ChatGPT로부터 받은 응답 텍스트.
:return: 결정된 파일 제목.
"""
# "제목"으로 시작하는 문장을 찾습니다.
for sentence in response.split('.'):
if sentence.strip().lower().startswith("제목"):
return sentence.strip() + ".txt" # 제목이 있는 경우 파일 이름을 결정합니다.
# 제목이 없는 경우 현재 날짜로 파일 이름을 결정합니다.
current_date = datetime.datetime.now().strftime("%Y%m%d")
return f"{current_date}-Response.txt"
# 4. 응답과 이미지(있는 경우)를 저장하는 함수를 정의합니다.
def save_response_and_images(response, folder_path, filename):
"""
응답 텍스트와 이미지(있는 경우) 파일에 저장하는 함수.
:param response: ChatGPT로부터 받은 응답 텍스트.
:param folder_path: 저장할 폴더의 경로.
:param filename: 파일 제목.
"""
# GPT_Response 폴더가 없으면 생성합니다.
if not os.path.exists(folder_path):
os.makedirs(folder_path)
# 응답을 텍스트 파일로 저장합니다.
file_path = os.path.join(folder_path, filename)
with open(file_path, 'w') as file:
file.write(response)
# TODO: 이미지 처리 로직은 사용자의 추가 요구 사항에 따라 결정되어야 합니다.
# 주석 처리된 함수 호출들을 제외하고, 이 구조를 기반으로 구체적인 구현을 진행하겠습니다.
# 참고: 실제 코드 실행은 주석 처리되어 있으므로, 여기에서는 실행되지 않습니다.
# 파이썬 코드 수정: 사용자로부터 질문을 입력 받는 기능 추가
# 함수 정의 부분은 그대로 둡니다. 주요 변경은 스크립트 실행 부분에 있습니다.
# 사용자로부터 질문을 입력 받는 코드를 추가합니다.
def main(api_key):
# 사용자로부터 질문을 입력 받습니다.
question = input("ChatGPT에게 물어볼 질문을 입력하세요: ")
# ChatGPT에게 질문을 하고 응답을 받습니다.
response = ask_chatgpt(question, api_key)
# 응답으로부터 파일 제목을 결정합니다.
filename = determine_filename(response)
# 응답과 이미지(있는 경우)를 저장합니다.
folder_path = os.path.join(os.getcwd(), "GPT_Response")
save_response_and_images(response, folder_path, filename)
# 사용자에게 응답이 저장된 위치를 알려줍니다.
print(f"응답이 {folder_path} 폴더에 저장되었습니다, 파일명: {filename}")
# 이제 main 함수를 실행할 준비가 되었습니다.
# 참고: 실제 환경에서 API 키와 함께 main 함수를 호출해야 합니다.
# 예: main('your_openai_api_key_here')
# 주석 처리된 함수 호출들을 제외하고, 이 구조를 기반으로 구체적인 구현을 진행하겠습니다.
# 참고: 실제 코드 실행은 주석 처리되어 있으므로, 여기에서는 실행되지 않습니다.
if __name__ == "__main__":
main("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")

164
MakeVideo.py Normal file
View File

@ -0,0 +1,164 @@
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()

115
MakeVideo2.py Normal file
View File

@ -0,0 +1,115 @@
from tkinter import Tk, Label, Button, Entry, filedialog, messagebox, Listbox, simpledialog, OptionMenu, StringVar
import os
from gtts import gTTS
from moviepy.editor import concatenate_videoclips, AudioFileClip, ImageClip, CompositeVideoClip, TextClip
import tempfile
from datetime import datetime
class VideoProject:
def __init__(self):
self.texts = []
self.images = []
self.background_music = None
self.video_type = "참교육"
def add_text(self, text):
self.texts.append(text)
def add_image(self, image_path):
self.images.append(image_path)
def set_background_music(self, music_path):
self.background_music = music_path
def set_video_type(self, video_type):
self.video_type = video_type
class VideoMakerApp:
def __init__(self, master):
self.master = master
master.title("유튜브 쇼츠 제작기")
self.project = VideoProject()
self.label = Label(master, text="유튜브 쇼츠 제작기에 오신 것을 환영합니다!")
self.label.pack()
self.text_entry = Entry(master)
self.text_entry.pack()
self.add_text_button = Button(master, text="텍스트 추가", command=self.add_text)
self.add_text_button.pack()
self.add_image_button = Button(master, text="이미지 추가", command=self.add_image)
self.add_image_button.pack()
self.add_music_button = Button(master, text="배경음 추가", command=self.add_background_music)
self.add_music_button.pack()
self.type_var = StringVar(master)
self.type_var.set("참교육")
self.type_dropdown = OptionMenu(master, self.type_var, "참교육", "야썰", "어이없음", "감동", command=self.project.set_video_type)
self.type_dropdown.pack()
self.create_video_button = Button(master, text="동영상 만들기", command=self.create_video_with_speech)
self.create_video_button.pack()
self.item_listbox = Listbox(master)
self.item_listbox.pack()
def add_text(self):
text = self.text_entry.get()
if text:
self.project.add_text(text)
self.item_listbox.insert('end', f"텍스트: {text}")
messagebox.showinfo("성공", "텍스트가 추가되었습니다.")
def add_image(self):
file_path = filedialog.askopenfilename()
if file_path:
self.project.add_image(file_path)
self.item_listbox.insert('end', f"이미지: {os.path.basename(file_path)}")
messagebox.showinfo("성공", "이미지가 추가되었습니다.")
def add_background_music(self):
file_path = filedialog.askopenfilename()
if file_path:
self.project.set_background_music(file_path)
messagebox.showinfo("성공", "배경음이 설정되었습니다.")
def create_video_with_speech(self):
clips = []
for text, image_path in zip(self.project.texts, self.project.images):
tts = gTTS(text=text, lang='ko')
temp_file = tempfile.NamedTemporaryFile(delete=False)
tts.save(temp_file.name)
temp_file.close()
audio_clip = AudioFileClip(temp_file.name)
image_clip = ImageClip(image_path).set_duration(audio_clip.duration).set_fps(24)
text_clip = TextClip(text, fontsize=24, color='white').set_position('bottom').set_duration(audio_clip.duration)
composite_clip = CompositeVideoClip([image_clip, text_clip]).set_duration(audio_clip.duration).set_fps(24).set_audio(audio_clip)
clips.append(composite_clip)
os.unlink(temp_file.name)
final_clip = concatenate_videoclips(clips)
if self.project.background_music:
background_audio = AudioFileClip(self.project.background_music).set_duration(final_clip.duration).set_fps(24)
final_clip = final_clip.set_audio(background_audio)
result_dir = "Result"
if not os.path.exists(result_dir):
os.makedirs(result_dir)
today = datetime.now().strftime("%Y%m%d")
file_name = f"{today}_{self.project.video_type}.mp4"
final_path = os.path.join(result_dir, file_name)
final_clip.write_videofile(final_path, fps=24)
messagebox.showinfo("완료", "동영상이 성공적으로 생성되었습니다.")
root = Tk()
my_gui = VideoMakerApp(root)
root.mainloop()

127
MakeVideo3.py Normal file
View File

@ -0,0 +1,127 @@
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, simpledialog
from gtts import gTTS
from moviepy.editor import *
import os
from datetime import datetime
from PIL import Image
class VideoCreatorApp:
def __init__(self, master):
self.master = master
self.master.title('Video Creator')
self.init_ui()
# 데이터 관리를 위한 리스트
self.scripts = []
self.images = []
self.actions = []
self.background_music = None
# 임시 및 결과 폴더 확인
self.temp_folder = "temp"
self.result_folder = "Result"
for folder in [self.temp_folder, self.result_folder]:
if not os.path.exists(folder):
os.makedirs(folder)
def init_ui(self):
# UI 구성 요소
self.main_frame = ttk.Frame(self.master)
self.main_frame.pack(fill=tk.BOTH, expand=True)
# 스크립트, 이미지, 액션 목록 및 추가 버튼
self.list_frame = ttk.Frame(self.main_frame)
self.list_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
self.script_listbox = tk.Listbox(self.list_frame)
self.script_listbox.pack(fill=tk.X)
ttk.Button(self.list_frame, text='Add Script', command=self.add_script).pack(fill=tk.X)
self.image_listbox = tk.Listbox(self.list_frame)
self.image_listbox.pack(fill=tk.X)
ttk.Button(self.list_frame, text='Add Image', command=self.add_image).pack(fill=tk.X)
# 액션 추가 예시는 생략
# self.action_listbox = tk.Listbox(self.list_frame)
# self.action_listbox.pack(fill=tk.X)
# ttk.Button(self.list_frame, text='Add Action', command=self.add_action).pack(fill=tk.X)
self.preview_frame = ttk.Frame(self.main_frame, width=200)
self.preview_frame.pack(side=tk.RIGHT, fill=tk.Y)
self.preview_frame.pack_propagate(False)
ttk.Button(self.preview_frame, text='Set Background Music', command=self.set_background_music).pack(fill=tk.X)
ttk.Button(self.preview_frame, text='Create Video', command=self.create_video).pack(fill=tk.X)
def add_script(self):
script_text = simpledialog.askstring("Input", "Enter your script:")
if script_text:
self.scripts.append(script_text)
self.script_listbox.insert(tk.END, script_text)
def add_image(self):
file_path = filedialog.askopenfilename(filetypes=(("Image files", "*.jpg;*.png;*.gif;*.webp"), ("All files", "*.*")))
if file_path:
self.images.append(file_path)
self.image_listbox.insert(tk.END, file_path)
# 배경음악 설정 및 동영상 생성 기능은 위에서 제공한 설명에 따라 구현됩니다.
# 이 부분의 코드는 이전에 제공된 `create_video_clips` 및 `create_video` 메서드 내용을 참고하여 구현할 수 있습니다.
def set_background_music(self):
file_path = filedialog.askopenfilename(filetypes=(("Audio files", "*.mp3;*.wav"), ("All files", "*.*")))
if file_path:
self.background_music = file_path
messagebox.showinfo("Background Music Set", f"Background music set to: {file_path}")
def create_video(self):
clips = []
# 스크립트를 TTS로 변환하고 비디오 클립으로 추가
for script in self.scripts:
tts = gTTS(script, lang='ko')
tts_file = os.path.join(self.temp_folder, "tts.mp3")
tts.save(tts_file)
audioclip = AudioFileClip(tts_file)
txt_clip = TextClip(script, fontsize=24, color='white', bg_color='black', size=audioclip.size)
txt_clip = txt_clip.set_duration(audioclip.duration)
clip = CompositeVideoClip([txt_clip.set_pos('center')], size=audioclip.size).set_audio(audioclip)
clips.append(clip)
# 이미지를 비디오 클립으로 추가
for image in self.images:
img_clip = ImageClip(image).set_duration(3) # 예시로 3초 동안 표시
img_clip = resize(img_clip, height=720) # 이미지 크기 조정
clips.append(img_clip)
# 모든 클립을 하나의 비디오로 결합
final_clip = concatenate_videoclips(clips, method="compose")
# 배경음악 추가
if self.background_music:
background_music = AudioFileClip(self.background_music)
final_clip = final_clip.set_audio(background_music)
# Result 폴더 내 오늘 날짜로 시작하는 파일들의 개수를 세어 n 구하기
today_str = datetime.now().strftime("%Y-%m-%d")
existing_files = glob.glob(os.path.join(self.result_folder, f"{today_str}_*.mp4"))
file_number = len(existing_files) + 1
# 최종 비디오 파일 이름 설정
output_filename = f"{today_str}_{file_number:03d}.mp4"
output_path = os.path.join(self.result_folder, output_filename)
# 비디오 파일 저장
final_clip.write_videofile(output_path, fps=24)
messagebox.showinfo("Success", f"Video created successfully: {output_filename}")
def main():
root = tk.Tk()
app = VideoCreatorApp(root)
root.mainloop()
if __name__ == '__main__':
main()

76
MakeVideo4.py Normal file
View File

@ -0,0 +1,76 @@
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, simpledialog
class VideoCreatorApp:
def __init__(self, master):
self.master = master
self.master.title('Video Creator')
self.init_ui()
# 시퀀스 데이터 관리
self.sequence = [] # 스크립트, 이미지, 액션을 포함하는 시퀀스
def init_ui(self):
self.sequence_frame = ttk.Frame(self.master)
self.sequence_frame.pack(fill=tk.BOTH, expand=True)
self.add_script_button = ttk.Button(self.sequence_frame, text="Insert Script", command=self.insert_script)
self.add_script_button.pack(fill=tk.X)
self.add_image_button = ttk.Button(self.sequence_frame, text="Insert Image", command=self.insert_image)
self.add_image_button.pack(fill=tk.X)
self.add_action_button = ttk.Button(self.sequence_frame, text="Insert Action", command=self.insert_action)
self.add_action_button.pack(fill=tk.X)
self.create_video_button = ttk.Button(self.sequence_frame, text="Create Video", command=self.create_video)
self.create_video_button.pack(fill=tk.X)
self.sequence_listbox = tk.Listbox(self.sequence_frame)
self.sequence_listbox.pack(fill=tk.BOTH, expand=True)
def insert_script(self):
script_text = simpledialog.askstring("Input", "Enter your script:")
if script_text:
self.sequence.append({'type': 'script', 'content': script_text})
self.update_sequence_listbox()
def insert_image(self):
file_path = filedialog.askopenfilename(filetypes=(("Image files", "*.jpg;*.png;*.gif;*.webp"), ("All files", "*.*")))
if file_path:
self.sequence.append({'type': 'image', 'content': file_path})
self.update_sequence_listbox()
def insert_action(self):
action_type = simpledialog.askstring("Input", "Enter action type (e.g., 'sleep', 'clean'):")
if action_type == "sleep":
duration = simpledialog.askinteger("Input", "Enter duration (seconds):")
self.sequence.append({'type': 'action', 'action': action_type, 'duration': duration})
elif action_type == "clean":
self.sequence.append({'type': 'action', 'action': action_type})
self.update_sequence_listbox()
def update_sequence_listbox(self):
self.sequence_listbox.delete(0, tk.END)
for item in self.sequence:
if item['type'] == 'script':
self.sequence_listbox.insert(tk.END, f"script {item['content'][:50]}...")
elif item['type'] == 'image':
self.sequence_listbox.insert(tk.END, f"image {item['content']}")
elif item['type'] == 'action':
if item['action'] == 'sleep':
self.sequence_listbox.insert(tk.END, f"action sleep {item['duration']}")
elif item['action'] == 'clean':
self.sequence_listbox.insert(tk.END, "action clean screen")
def create_video(self):
# 여기에 비디오 생성 로직을 구현
pass
def main():
root = tk.Tk()
app = VideoCreatorApp(root)
root.mainloop()
if __name__ == '__main__':
main()

245
MakeVideo5.py Normal file
View File

@ -0,0 +1,245 @@
import os
import glob
from moviepy.editor import ImageClip, AudioFileClip, concatenate_videoclips, TextClip, CompositeVideoClip
import moviepy.editor as mpe
from gtts import gTTS
from datetime import datetime
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, simpledialog
from PIL import Image, ImageTk
import re
class VideoCreatorApp:
def __init__(self, master):
self.master = master
self.master.title('Video Creator')
self.sequence = []
self.image_thumbnails = {}
self.current_image = None
self.background_music = None
self.animation_after_id = None # 이 부분을 추가
self.init_ui()
def init_ui(self):
self.list_frame = ttk.Frame(self.master)
self.list_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
self.control_frame = ttk.Frame(self.master)
self.control_frame.pack(side=tk.RIGHT, fill=tk.Y)
self.sequence_listbox = tk.Listbox(self.list_frame, width=50, height=20)
self.sequence_listbox.pack(padx=10, pady=10)
self.sequence_listbox.bind('<<ListboxSelect>>', self.show_image_preview)
ttk.Button(self.control_frame, text="Insert Script", command=self.insert_script).pack(pady=5)
ttk.Button(self.control_frame, text="Insert Image", command=self.insert_image).pack(pady=5)
ttk.Button(self.control_frame, text="Insert Action", command=self.insert_action).pack(pady=5)
ttk.Button(self.control_frame, text="Set Background Music", command=self.set_background_music).pack(pady=5)
ttk.Button(self.control_frame, text="Create Video", command=self.create_video).pack(pady=5)
# 'Update' 버튼 추가
ttk.Button(self.control_frame, text="Update Item", command=self.update_item).pack(pady=5)
# 'Delete' 버튼 추가
ttk.Button(self.control_frame, text="Delete Item", command=self.delete_item).pack(pady=5)
self.preview_canvas = tk.Canvas(self.list_frame, width=100, height=100)
self.preview_canvas.pack(padx=10, pady=10)
def update_item(self):
selection = self.sequence_listbox.curselection()
if selection:
index = selection[0]
item = self.sequence[index]
# 스크립트나 이미지에 따라 다른 입력 요청
if item['type'] == 'script':
new_text = simpledialog.askstring("Update Script", "Enter new script:", initialvalue=item['content'])
if new_text is not None:
self.sequence[index]['content'] = new_text
elif item['type'] == 'image':
new_path = filedialog.askopenfilename(filetypes=(("Image files", "*.jpg;*.png;*.gif;*.webp"), ("All files", "*.*")), initialdir="/", title="Select file")
if new_path:
self.sequence[index]['content'] = new_path
self.update_sequence_listbox() # 리스트 박스 업데이트
def delete_item(self):
selection = self.sequence_listbox.curselection()
if selection:
index = selection[0]
del self.sequence[index] # 선택된 항목 삭제
self.update_sequence_listbox() # 리스트 박스 업데이트
def update_sequence_listbox(self):
self.sequence_listbox.delete(0, tk.END)
for item in self.sequence:
if item['type'] == 'script':
self.sequence_listbox.insert(tk.END, f"Script: {item['content'][:30]}")
elif item['type'] == 'image':
# 파일 이름만 표시
file_name = os.path.basename(item['content'])
self.sequence_listbox.insert(tk.END, f"Image: {file_name}")
def insert_script(self):
script_text = simpledialog.askstring("Input", "Enter your script:")
if script_text:
self.sequence.append({'type': 'script', 'content': script_text})
self.update_sequence_listbox()
def insert_image(self):
file_path = filedialog.askopenfilename(filetypes=(("Image files", "*.jpg;*.png;*.gif;*.webp"), ("All files", "*.*")))
if file_path:
self.sequence.append({'type': 'image', 'content': file_path})
self.update_sequence_listbox()
def insert_action(self):
action = simpledialog.askstring("Insert Action", "Enter action (clean or sleep):")
if action == "sleep":
duration = simpledialog.askinteger("Sleep Duration", "Enter duration in seconds:", parent=self.master)
if duration is not None:
self.sequence.append({'type': 'action', 'action': action, 'duration': duration})
elif action == "clean":
self.sequence.append({'type': 'action', 'action': action})
self.update_sequence_listbox()
def set_background_music(self):
file_path = filedialog.askopenfilename(filetypes=(("Audio files", "*.mp3;*.wav"), ("All files", "*.*")))
if file_path:
self.background_music = file_path
messagebox.showinfo("Background Music Set", f"Background music set to: {file_path}")
def create_tts_audio(self, text, filename):
# 자음만 있는 한글 문자열 패턴 확인 및 건너뜀
if re.fullmatch(r'[ㄱ-ㅎ]+', text):
print("자음만 있는 문자열은 TTS에서 제외됩니다:", text)
return False
# TTS 생성
try:
tts = gTTS(text=text, lang='ko', slow=False)
tts.save(filename)
return True
except Exception as e:
print(f"TTS 생성 중 오류 발생: {e}")
return False
def create_video(self):
clips = []
last_clip_end_time = 0 # 마지막 클립의 종료 시간을 추적합니다.
for index, item in enumerate(self.sequence):
if item['type'] == 'script':
tts_filename = f'temp_{datetime.now().strftime("%Y%m%d%H%M%S")}.mp3'
if self.create_tts_audio(item['content'], tts_filename):
audio_clip = AudioFileClip(tts_filename).set_start(last_clip_end_time)
txt_clip = TextClip(item['content'], fontsize=24, color='white', bg_color='black', align='South')\
.set_position(('center', 'bottom'))\
.set_duration(audio_clip.duration)\
.set_start(last_clip_end_time)
# 자막이 항상 최상위 레이어에 오도록 설정합니다.
video_clip = CompositeVideoClip([txt_clip], size=(1080,1920)).set_duration(audio_clip.duration).set_start(last_clip_end_time).set_audio(audio_clip)
clips.append(video_clip)
last_clip_end_time += audio_clip.duration
os.remove(tts_filename)
elif item['type'] == 'image':
if index > 0 and self.sequence[index - 1]['type'] == 'script':
# 이미지 클립의 지속 시간을 다음 스크립트 시작까지로 설정
next_script_start_time = last_clip_end_time
img_clip = ImageClip(item['content']).set_start(last_clip_end_time).set_duration(next_script_start_time - last_clip_end_time).resize(newsize=(1080, 1920))
clips.append(img_clip)
if clips:
final_clip = concatenate_videoclips(clips, method="chain", padding=-1)
output_path = self.generate_output_path()
final_clip.write_videofile(output_path, fps=24)
messagebox.showinfo("Video Creation", f"Video created successfully: {output_path}")
else:
messagebox.showerror("Video Creation", "No clips to create a video.")
def generate_output_path(self):
# 파일 경로 생성 로직 (변경 없음)
date_str = datetime.now().strftime("%Y-%m-%d")
output_dir = os.path.join("Result", date_str)
os.makedirs(output_dir, exist_ok=True)
file_count = len(os.listdir(output_dir)) # 이미 있는 파일의 개수를 기준으로 파일 이름 결정
return os.path.join(output_dir, f"{date_str}_{file_count + 1:03d}.mp4")
def update_sequence_listbox(self):
self.sequence_listbox.delete(0, tk.END)
for item in self.sequence:
item_desc = f"{item['type']}: {item.get('action', '')} {item.get('content', '')[:50]}"
self.sequence_listbox.insert(tk.END, item_desc)
def show_image_preview(self, event=None):
selection = self.sequence_listbox.curselection()
if selection:
selected_index = selection[0]
selected_item = self.sequence[selected_index]
if selected_item['type'] == 'image':
self.display_image_thumbnail(selected_item['content'])
def display_image_thumbnail(self, image_path):
self.preview_canvas.delete("all") # 이전 이미지 삭제
if self.animation_after_id:
self.master.after_cancel(self.animation_after_id) # 기존 애니메이션 타이머 취소
self.animation_after_id = None # 타이머 ID 초기화
if image_path.lower().endswith('.gif'): # 애니메이션 GIF 처리
self.process_gif(image_path)
else: # 정적 이미지 처리
img = Image.open(image_path)
img.thumbnail((100, 100), getattr(Image, 'Resampling', Image).LANCZOS)
photo = ImageTk.PhotoImage(img)
self.preview_canvas.create_image(50, 50, image=photo, anchor=tk.CENTER)
self.image_thumbnails[image_path] = photo # 참조 유지
def process_gif(self, image_path):
gif = Image.open(image_path)
self.gif_frames = []
try:
while True:
self.gif_frames.append(ImageTk.PhotoImage(gif.copy()))
gif.seek(len(self.gif_frames)) # 다음 프레임으로 이동
except EOFError:
pass # 모든 프레임을 처리했음
self.current_frame = 0
self.update_gif_frame() # GIF 애니메이션 시작
def update_gif_frame(self):
if self.gif_frames:
frame = self.gif_frames[self.current_frame]
self.preview_canvas.create_image(50, 50, image=frame, anchor=tk.CENTER)
self.current_frame = (self.current_frame + 1) % len(self.gif_frames)
self.animation_after_id = self.master.after(100, self.update_gif_frame)
def preview_gif(self, img, image_path, frame_number=0):
try:
img.seek(frame_number)
frame = ImageTk.PhotoImage(img)
self.preview_canvas.delete("all")
self.preview_canvas.create_image(50, 50, image=frame, anchor=tk.CENTER)
self.image_thumbnails[image_path] = frame # 참조 유지
self.current_image = image_path # 현재 GIF 이미지 경로 저장
frame_number += 1
delay = img.info.get('duration', 100) # 다음 프레임까지의 지연 시간, 기본값 100ms
self.master.after(delay, self.preview_gif, img, image_path, frame_number)
except EOFError: # 마지막 프레임에 도달하면 다시 시작
if self.current_image == image_path: # 현재 이미지가 여전히 같은 GIF인 경우에만 재시작
self.preview_gif(img, image_path, 0)
def main():
root = tk.Tk()
app = VideoCreatorApp(root)
root.mainloop()
if __name__ == '__main__':
main()

BIN
Resource/0303/2.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 650 KiB

BIN
Resource/0303/3.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 KiB

BIN
Resource/0303/4.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 KiB

BIN
Resource/0303/5.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 KiB

BIN
Resource/0303/6.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 KiB

BIN
Resource/0303/7.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 MiB

BIN
Resource/original.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1013 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 751 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 484 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 646 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 638 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 693 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 475 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 914 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 468 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 848 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 906 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More