İhtiyacı olan vardır belki metini gpt ye yazdır istersen api çek metinide yazsın kodlarla oynar ayarlarsınız görselerin oldugu klasörleri seç video yapsın. Klasörden her defasında rast gele sıralama olarak fotoğraf seçer ve 5 saniye olarak yanyana koyar %50 ihtimalle yavaşça yaklaştırma %50 ihtimalle yavaşça uzaklaştırma efekti ekler hepsine geçiş efekti ekler eğer klasörde video varsa video içerisinden rast gele 3 saniye kesit alır aynalama ekler ve 3 görselden sonra 3 saniyelik kesit koyar birden fazla video varsa 3er saniye alır sonra tekrar ilk videodan rastgele 3 saniye alır hep aynı videodan almaz aynalama kullanır hepsine otomatik sese çevirir sınırsız ses düzenler isteyen kafasına göre özellik ekleyebilir

import os
import sys
import time
import re
import asyncio
import random
import threading
import gc # Bellek (RAM) temizliği için
from datetime import datetime
import numpy as np
import PIL.Image
from PIL import ImageFilter
import tkinter as tk
from tkinter import filedialog, messagebox
# CustomTkinter İçe Aktarımı
import customtkinter as ctk
# MoviePy - Pillow 10+ uyumluluk yaması
if not hasattr(PIL.Image, 'ANTIALIAS'):
PIL.Image.ANTIALIAS = PIL.Image.Resampling.LANCZOS
import edge_tts
from moviepy.editor import ImageClip, AudioFileClip, VideoFileClip, concatenate_videoclips, VideoClip, vfx
# ==================== TEMA AYARLARI ====================
ctk.set_appearance_mode("Dark") # "Dark" veya "Light"
ctk.set_default_color_theme("blue") # "blue", "green", "dark-blue"
IMAGE_DURATION = 5.0 # Her fotoğrafın kalma süresi (sn)
VIDEO_CLIP_DURATION = 3.0 # Videolardan çekilecek kesit süresi (sn)
TRANSITION_DURATION = 1.0 # Geçiş yumuşaklığı (sn)
# İNGİLİZCE SES SEÇENEKLERİ
VOICES = {
"Erkek Ses (Christopher - Derin & Etkileyici) [EN]": "en-US-ChristopherNeural",
"Kadın Ses (Ava - Doğal & Akıcı) [EN]": "en-US-AvaNeural",
"Erkek Ses (Eric - Haberci Tonu) [EN]": "en-US-EricNeural",
"Kadın Ses (Emma - Enerjik) [EN]": "en-US-EmmaNeural"
}
def clean_script_for_tts(text):
text = re.sub(r'\(.*?\)', '', text)
text = re.sub(r'\[.*?\]', '', text)
text = re.sub(r'^(Narrator|Speaker|User|Host|Voiceover)\s*:\s*', '', text, flags=re.IGNORECASE | re.MULTILINE)
text = text.replace('*', '').replace('#', '')
text = re.sub(r'\n+', '\n', text)
return text.strip()
async def text_to_speech(text, output_filename, voice_code):
communicate = edge_tts.Communicate(text, voice_code)
await communicate.save(output_filename)
def create_capcut_style_clip(img_path, duration=5.0, target_size=(1920, 1080)):
tw, th = target_size
img = PIL.Image.open(img_path).convert("RGB")
img_w, img_h = img.size
# 1. Arka Plan (Bulanık Zemin)
ratio_bg = max(tw / img_w, th / img_h)
bg_w, bg_h = int(img_w * ratio_bg), int(img_h * ratio_bg)
bg_img = img.resize((bg_w, bg_h), PIL.Image.Resampling.LANCZOS)
left = (bg_w - tw) // 2
top = (bg_h - th) // 2
bg_img = bg_img.crop((left, top, left + tw, top + th)).filter(ImageFilter.GaussianBlur(radius=30))
# 2. Ön Plan Temel Boyutlandırması
ratio_fg = min(tw / img_w, th / img_h)
fg_w, fg_h = int(img_w * ratio_fg), int(img_h * ratio_fg)
fg_base = img.resize((fg_w, fg_h), PIL.Image.Resampling.LANCZOS)
zoom_direction = random.choice(["in", "out"])
# 3. Canlı Ken Burns Zoom Efekt Üreteci
def make_frame(t):
progress = t / duration if duration > 0 else 0
scale = 1.0 + (0.12 * progress) if zoom_direction == "in" else 1.12 - (0.12 * progress)
curr_w = int(fg_w * scale)
curr_h = int(fg_h * scale)
fg_resized = fg_base.resize((curr_w, curr_h), PIL.Image.Resampling.LANCZOS)
frame = bg_img.copy()
paste_x = (tw - curr_w) // 2
paste_y = (th - curr_h) // 2
frame.paste(fg_resized, (paste_x, paste_y))
return np.array(frame)
return VideoClip(make_frame, duration=duration)
def get_random_video_snippet(video_path, clip_duration=3.0, target_size=(1920, 1080)):
"""Seçilen videodan ANLIK olarak rastgele 3sn sessiz, %50 aynalanmış kesit ve ana dosyayı döndürür."""
try:
full_video = VideoFileClip(video_path)
dur = full_video.duration
if dur <= 0.5:
full_video.close()
return None, None
# 1. Her çağrıldığında RASTGELE bir saniye seçer
if dur > clip_duration:
start_time = random.uniform(0, dur - clip_duration)
sub_clip = full_video.subclip(start_time, start_time + clip_duration)
else:
sub_clip = full_video.subclip(0, dur)
# 2. SESİ KAPAT
sub_clip = sub_clip.without_audio()
# 3. %50 İHTİMALLE YATAY AYNALAMA
if random.choice([True, False]):
sub_clip = sub_clip.fx(vfx.mirror_x)
# 4. 1080p Boyutlandırma
sub_clip = sub_clip.resize(height=target_size[1])
if sub_clip.w > target_size[0]:
sub_clip = sub_clip.crop(x_center=sub_clip.w/2, width=target_size[0])
return sub_clip, full_video
except Exception:
return None, None
# ==================== KUYRUKLU MODERN ARAYÜZ ====================
class VideoBotApp(ctk.CTk):
def __init__(self):
super().__init__()
self.title("🎬 YouTube Content Studio Pro v4.0 (GPU & RAM Accelerated)")
self.geometry("1080x880")
self.resizable(True, True)
self.selected_image_folder = os.path.abspath("images")
if not os.path.exists(self.selected_image_folder):
os.makedirs(self.selected_image_folder)
self.queue = []
self.build_gui()
def build_gui(self):
header_frame = ctk.CTkFrame(self, fg_color="transparent")
header_frame.pack(fill="x", padx=20, pady=(15, 5))
title_label = ctk.CTkLabel(
header_frame, text="🎬 YOUTUBE CONTENT STUDIO (NVIDIA NVENC GPU ACCELERATED)",
font=ctk.CTkFont(family="Segoe UI", size=18, weight="bold")
)
title_label.pack(side="left")
self.status_badge = ctk.CTkLabel(
header_frame, text="● SİSTEM HAZIR",
font=ctk.CTkFont(family="Segoe UI", size=11, weight="bold"),
text_color="#10b981", fg_color="#1f2937", corner_radius=8, padx=12, pady=4
)
self.status_badge.pack(side="right")
main_layout = ctk.CTkFrame(self, fg_color="transparent")
main_layout.pack(fill="both", expand=True, padx=20, pady=5)
left_column = ctk.CTkFrame(main_layout, fg_color="transparent")
left_column.pack(side="left", fill="both", expand=True, padx=(0, 10))
right_column = ctk.CTkFrame(main_layout, width=340, corner_radius=12)
right_column.pack(side="right", fill="both", padx=(10, 0))
# --- SOL KOLON ---
editor_card = ctk.CTkFrame(left_column, corner_radius=12)
editor_card.pack(fill="both", expand=True, pady=(0, 10))
editor_header = ctk.CTkFrame(editor_card, fg_color="transparent")
editor_header.pack(fill="x", padx=15, pady=(10, 5))
ctk.CTkLabel(editor_header, text="📝 İngilizce Senaryo / Metin Girişi", font=ctk.CTkFont(size=13, weight="bold")).pack(side="left")
btn_clear = ctk.CTkButton(
editor_header, text="🗑️ Temizle", width=70, height=24,
fg_color="transparent", hover_color="#374151", text_color="#9ca3af",
command=self.clear_text
)
btn_clear.pack(side="right")
self.script_input = ctk.CTkTextbox(editor_card, font=("Consolas", 12), corner_radius=8, height=180)
self.script_input.pack(fill="both", expand=True, padx=15, pady=5)
self.script_input.bind("<KeyRelease>", self.update_stats)
stats_frame = ctk.CTkFrame(editor_card, fg_color="transparent")
stats_frame.pack(fill="x", padx=15, pady=(5, 10))
self.lbl_chars = ctk.CTkLabel(stats_frame, text="Karakter: 0", font=ctk.CTkFont(size=11, weight="bold"), text_color="#3b82f6")
self.lbl_chars.pack(side="left", padx=(0, 15))
self.lbl_time = ctk.CTkLabel(stats_frame, text="Süre: ~0 sn", font=ctk.CTkFont(size=11), text_color="#9ca3af")
self.lbl_time.pack(side="left")
settings_card = ctk.CTkFrame(left_column, corner_radius=12)
settings_card.pack(fill="x", pady=5)
settings_inner = ctk.CTkFrame(settings_card, fg_color="transparent")
settings_inner.pack(fill="x", padx=15, pady=10)
ctk.CTkLabel(settings_inner, text="🎙️ İngilizce Ses:", font=ctk.CTkFont(size=11, weight="bold")).grid(row=0, column=0, sticky="w", pady=4)
self.voice_var = ctk.StringVar(value=list(VOICES.keys())[0])
voice_dropdown = ctk.CTkOptionMenu(settings_inner, variable=self.voice_var, values=list(VOICES.keys()), width=350, corner_radius=8)
voice_dropdown.grid(row=0, column=1, sticky="w", pady=4, padx=(10, 0))
ctk.CTkLabel(settings_inner, text="📁 Medya Klasörü:", font=ctk.CTkFont(size=11, weight="bold")).grid(row=1, column=0, sticky="w", pady=4)
folder_frame = ctk.CTkFrame(settings_inner, fg_color="transparent")
folder_frame.grid(row=1, column=1, sticky="ew", pady=4, padx=(10, 0))
self.lbl_folder_path = ctk.CTkLabel(
folder_frame, text=self.selected_image_folder, font=ctk.CTkFont(size=10),
text_color="#9ca3af", fg_color="#111827", corner_radius=8, anchor="w", padx=10, height=30, width=240
)
self.lbl_folder_path.pack(side="left", padx=(0, 5))
btn_select_folder = ctk.CTkButton(folder_frame, text="📂 Seç", width=60, height=30, corner_radius=8, command=self.select_folder)
btn_select_folder.pack(side="right")
btn_add_queue = ctk.CTkButton(
left_column, text="➕ BU VİDEOYU KUYRUĞA EKLE", font=ctk.CTkFont(size=12, weight="bold"),
fg_color="#10b981", hover_color="#059669", height=38, corner_radius=8, command=self.add_to_queue
)
btn_add_queue.pack(fill="x", pady=(5, 0))
# --- SAĞ KOLON ---
queue_header = ctk.CTkFrame(right_column, fg_color="transparent")
queue_header.pack(fill="x", padx=15, pady=(12, 5))
ctk.CTkLabel(queue_header, text="📋 İş Kuyruğu", font=ctk.CTkFont(size=13, weight="bold")).pack(side="left")
btn_clear_queue = ctk.CTkButton(
queue_header, text="Kuyruğu Temizle", width=90, height=22,
fg_color="transparent", hover_color="#ef4444", text_color="#f87171", command=self.clear_queue
)
btn_clear_queue.pack(side="right")
self.queue_box = ctk.CTkTextbox(right_column, font=("Consolas", 10), corner_radius=8)
self.queue_box.pack(fill="both", expand=True, padx=12, pady=5)
self.queue_box.configure(state="disabled")
self.lbl_queue_count = ctk.CTkLabel(right_column, text="Bekleyen İş: 0 Video", font=ctk.CTkFont(size=11, weight="bold"), text_color="#9ca3af")
self.lbl_queue_count.pack(pady=(5, 12))
# --- ALT AKSİYON & LOG ---
action_frame = ctk.CTkFrame(self, fg_color="transparent")
action_frame.pack(fill="x", padx=20, pady=5)
self.btn_start_batch = ctk.CTkButton(
action_frame, text="⚡ KUYRUĞU BAŞLAT (SABAHA KADAR RENDER ET)", font=ctk.CTkFont(size=13, weight="bold"),
height=45, corner_radius=10, command=self.start_batch_process
)
self.btn_start_batch.pack(fill="x", pady=(0, 5))
self.progress = ctk.CTkProgressBar(action_frame, height=10, corner_radius=5)
self.progress.pack(fill="x")
self.progress.set(0)
log_card = ctk.CTkFrame(self, corner_radius=12)
log_card.pack(fill="both", expand=True, padx=20, pady=(5, 15))
self.log_area = ctk.CTkTextbox(log_card, font=("Consolas", 10), text_color="#10b981", fg_color="#090a0f", corner_radius=8, height=140)
self.log_area.pack(fill="both", expand=True, padx=12, pady=10)
self.log("🚀 Sistem hazır! GPU Hızlandırması (NVIDIA NVENC) Etkinleştirildi.")
def select_folder(self):
folder = filedialog.askdirectory(title="Görsellerin ve Videoların Olduğu Klasörü Seçin", initialdir=self.selected_image_folder)
if folder:
self.selected_image_folder = os.path.abspath(folder)
display_path = self.selected_image_folder if len(self.selected_image_folder) < 32 else "..." + self.selected_image_folder[-28:]
self.lbl_folder_path.configure(text=display_path)
def clear_text(self):
self.script_input.delete("1.0", tk.END)
self.update_stats()
def update_stats(self, event=None):
text = self.script_input.get("1.0", tk.END).strip()
char_count = len(text)
est_seconds = int(char_count / 14.5) if char_count > 0 else 0
self.lbl_chars.configure(text=f"Karakter: {char_count:,}")
self.lbl_time.configure(text=f"Süre: ~{est_seconds} sn")
def add_to_queue(self):
text = self.script_input.get("1.0", tk.END).strip()
if not text:
messagebox.showwarning("Eksik Metin", "Lütfen önce İngilizce senaryo metnini girin kanka!")
return
item = {
"id": len(self.queue) + 1,
"text": text,
"folder": self.selected_image_folder,
"voice_name": self.voice_var.get(),
"voice_code": VOICES[self.voice_var.get()]
}
self.queue.append(item)
self.refresh_queue_ui()
self.log(f"➕ [KUYRUK] {item['id']}. Video eklendi! ({len(text)} Karakter | Klasör: {os.path.basename(item['folder'])})")
self.clear_text()
def clear_queue(self):
self.queue.clear()
self.refresh_queue_ui()
self.log("🧹 İş kuyruğu tamamen temizlendi.")
def refresh_queue_ui(self):
self.queue_box.configure(state="normal")
self.queue_box.delete("1.0", tk.END)
for i, item in enumerate(self.queue, 1):
folder_name = os.path.basename(item['folder'])
snippet = item['text'][:25].replace('\n', ' ') + "..."
self.queue_box.insert(tk.END, f"#{i} | {folder_name}\n 📝 {snippet}\n-----------------------------------\n")
self.queue_box.configure(state="disabled")
self.lbl_queue_count.configure(text=f"Bekleyen İş: {len(self.queue)} Video")
def log(self, message):
now = datetime.now().strftime("%H:%M:%S")
self.log_area.insert(tk.END, f"[{now}] {message}\n")
self.log_area.see(tk.END)
def set_progress(self, val):
self.progress.set(val / 100.0)
def set_status(self, text, is_working=False):
color = "#f59e0b" if is_working else "#10b981"
self.status_badge.configure(text=f"● {text.upper()}", text_color=color)
def start_batch_process(self):
if not self.queue:
messagebox.showwarning("Kuyruk Boş", "Kuyrukta hiç video yok! Önce metin girip 'Kuyruğa Ekle' butonuna bas kanka.")
return
self.btn_start_batch.configure(state="disabled", text="⏳ GECE MODU AKTİF (GPU RENDER EDİLİYOR...)")
self.set_status("GPU RENDER EDİLİYOR", is_working=True)
threading.Thread(target=self.run_batch_queue, daemon=True).start()
def run_batch_queue(self):
total_jobs = len(self.queue)
self.log(f"🚀 TOPLU İŞLEM BAŞLATILDI! Toplam {total_jobs} video NVIDIA GPU desteğiyle render edilecek.")
for index, item in enumerate(list(self.queue), 1):
# Her döngüde RAM temizliği için açılan tüm clip nesnelerini burada toplayacağız
clips_to_close = []
audio = None
video = None
final_video = None
try:
self.log(f"\n🎬 === [VİDEO {index}/{total_jobs} BAŞLADI] ===")
self.set_progress(5)
# 1. Metin & İngilizce Ses
clean_text = clean_script_for_tts(item['text'])
self.log(f"🎙️ İngilizce Ses üretiliyor ({item['voice_name']})...")
audio_path = f"temp_voice_{index}.mp3"
asyncio.run(text_to_speech(clean_text, audio_path, item['voice_code']))
audio = AudioFileClip(audio_path)
audio_duration = audio.duration
self.set_progress(25)
# 2. Klasördeki Resim ve Videoları Arama
media_folder = item['folder']
all_files = os.listdir(media_folder)
raw_images = [os.path.join(media_folder, f) for f in all_files if f.lower().endswith(('.png', '.jpg', '.jpeg'))]
raw_videos = [os.path.join(media_folder, f) for f in all_files if f.lower().endswith(('.mp4', '.mov', '.avi', '.mkv'))]
if not raw_images and not raw_videos:
self.log(f"❌ HATA: '{media_folder}' klasöründe hiç resim veya video yok! Atlanıyor.")
if audio: audio.close()
if os.path.exists(audio_path): os.remove(audio_path)
continue
self.log(f"📁 Klasör İçeriği: {len(raw_images)} Resim, {len(raw_videos)} Video bulundu.")
# Fotoğraf Klip Listesi
random.shuffle(raw_images)
img_clips = []
for img_p in raw_images:
ic = create_capcut_style_clip(img_p, duration=IMAGE_DURATION)
img_clips.append(ic)
clips_to_close.append(ic)
# 3. Klipleri Harmanla (Tüm Videolar Arasında Eşit Sirkülasyon + Dinamik Kesim)
final_clips_sequence = []
current_timeline_duration = 0
img_idx = 0
video_pointer = 0 # Videoları sırayla eşit gezmek için imleç
if raw_videos:
random.shuffle(raw_videos)
while current_timeline_duration < audio_duration + 2.0:
# 2 Fotoğraf Ekle
for _ in range(2):
if img_clips:
c = img_clips[img_idx % len(img_clips)]
final_clips_sequence.append(c)
current_timeline_duration += (IMAGE_DURATION - TRANSITION_DURATION)
img_idx += 1
# 1 Video Kesiti Ekle (Eğer klasörde video varsa)
if raw_videos:
selected_video_path = raw_videos[video_pointer % len(raw_videos)]
video_pointer += 1
# O an videodan RASTGELE sıfırdan 3 saniye kes
vc, full_vid = get_random_video_snippet(selected_video_path, clip_duration=VIDEO_CLIP_DURATION)
if vc:
final_clips_sequence.append(vc)
current_timeline_duration += (VIDEO_CLIP_DURATION - TRANSITION_DURATION)
clips_to_close.append(vc)
if full_vid:
clips_to_close.append(full_vid)
# Yumuşak Geçişler (Crossfade)
processed_clips = []
for i, clip in enumerate(final_clips_sequence):
if i > 0:
clip = clip.crossfadein(TRANSITION_DURATION)
processed_clips.append(clip)
self.set_progress(60)
# 4. GPU (NVIDIA NVENC) RENDER
output_name = f"final_video_{index}_{datetime.now().strftime('%H%M%S')}.mp4"
self.log(f"🔥 GPU Render Alınıyor (NVIDIA NVENC + Ryzen 12 Çekirdek) -> {output_name}")
video = concatenate_videoclips(processed_clips, padding=-TRANSITION_DURATION, method="compose")
video = video.set_duration(audio_duration)
final_video = video.set_audio(audio)
# =========================================================
# 8 GB NVIDIA EKRAN KARTI (NVENC) VE RYZEN 5 5600 DESTEKLİ RENDER
# =========================================================
final_video.write_videofile(
output_name,
fps=24,
codec="h264_nvenc", # İşlemci (libx264) YERİNE NVIDIA GPU NVENC ÇİPİ
audio_codec="aac", # Standart yüksek kaliteli ses
preset="p1", # En hızlı GPU donanım profili
threads=12 # Ryzen 5 5600'ün 12 çekirdeğini sonuna kadar çalıştırır
)
self.set_progress(100)
self.log(f"✅ [VİDEO {index}/{total_jobs} BİTTİ] Başarıyla tamamlandı!")
except Exception as e:
self.log(f"❌ Video #{index} işlenirken HATA oluştu: {e}. Sıradaki videoya geçiliyor...")
finally:
# ================= TEMİZLİK VE RAM TEMİZLİĞİ =================
self.log("🧹 Bellek (RAM) ve geçici dosyalar temizleniyor...")
if final_video:
try: final_video.close()
except: pass
if video:
try: video.close()
except: pass
if audio:
try: audio.close()
except: pass
for c in clips_to_close:
try: c.close()
except: pass
clips_to_close.clear()
if os.path.exists(audio_path):
try: os.remove(audio_path)
except: pass
# Garbage Collector çalıştırarak RAM'i sıfırlıyoruz
gc.collect()
self.log("\n🎉🎉 TÜM KUYRUK TAMAMLANDI! Videoların hazır kanka!")
messagebox.showinfo("İşlem Bitti", f"Kuyruktaki {total_jobs} video başarıyla oluşturuldu!")
self.queue.clear()
self.refresh_queue_ui()
self.set_status("HAZIR", is_working=False)
self.btn_start_batch.configure(state="normal", text="⚡ KUYRUĞU BAŞLAT (SABAHA KADAR RENDER ET)")
if __name__ == "__main__":
app = VideoBotApp()
app.mainloop()