• 27-10-2025, 01:09:20
    #1
    Nedir:

    audio klasöründeki müzikleri sırayla alır, images klasöründeki resimlerden her 5 saniyeye 1 tane düşecek şekilde rasgele görsel seçer,
    fontklasöründen ttf çağırır ve şarkının ismini, sizin sosyal medya kullanıcı adını vb videoya ekler,
    en son output klasörüne .mp4 formatında kaydeder.

    Örneğin : https://www.tiktok.com/@arabeskradyo6

    Bunları youtube, tiktok, instagram hesaplarınıza yükleyin (bunu da otomatik yapacak script üzerinde çalışıyorum)
    Python kullanarak görselleri rastgele karıştıran, müziğin ilk 30 saniyesini alan ve üzerine şarkı adı ile kullanıcı adını ekleyerek 30 saniyelik videolar üreten bir bot hazırladık. MoviePy ve Pillow kütüphaneleriyle kendi video otomasyon sistemini nasıl kurabileceğini adım adım anlatıyoruz.
    Bu yazıda Python kullanarak tamamen otomatik çalışan bir video oluşturma botu geliştiriyoruz. Elimizdeki görselleri rastgele karıştırıyor, seçtiğimiz müzik dosyalarının ilk 30 saniyesini alıyor, şarkı adını ve kullanıcı adımızı ekrana basıyor. Tüm bunları MoviePy ve Pillow kütüphaneleri ile 30 saniyelik MP4 formatında çıktı olarak alıyoruz.
    Böyle bir sistem, YouTube Shorts veya Instagram Reels gibi platformlara hızlı içerik üretmek isteyenler için harika bir otomasyon fikri. Kendi klasöründeki müzikleri ve görselleri kullanarak her çalıştırmada farklı sonuçlar üreten bir mini video üretim motoru aslında.

    Kurulum Adımları
    1. Python ve FFmpeg kur.
    2. MoviePy, Pillow, Numpy kütüphanelerini yükle.
    3. Görselleri images/, müzikleri audio/ klasörüne yerleştir.
    4. make_videos.py betiğini çalıştır.

    cd "C:\Users\Win10\Desktop\video maker"
    venv\Scripts\activate
    python make_videos.py


    Kaynaklar bana aittir, alıntı değildir:
    Github
    TR : Python ile Otomatik Video OluÅŸturma (MoviePy + Pillow)
    EN : Automating Video Creation with Python (MoviePy + Pillow)


    # --- Pillow 10+ uyumluluk yaması (MoviePy için) ---
    import PIL
    from PIL import Image
    if not hasattr(Image, "ANTIALIAS"):
        # Pillow 10+ sürümlerinde kaldırıldı -> LANCZOS ile eşleştir
        Image.ANTIALIAS = Image.Resampling.LANCZOS
    # ---------------------------------------------------
    
    import os
    import random
    import re
    from pathlib import Path
    from typing import List, Tuple
    
    from PIL import Image, ImageDraw, ImageFont
    import numpy as np
    from moviepy.editor import (
        AudioFileClip,
        ImageClip,
        CompositeVideoClip,
        concatenate_videoclips,
        vfx,
    )
    
    # ==== KULLANICI AYARLARI ====
    USERNAME = "@arabeskradyo6"        # Videoda görünecek kullanıcı adı
    OUTPUT_SIZE = (1080, 1920)        # (genişlik, yükseklik) 9:16 Reels/TikTok tarzı. İstersen (1920,1080) yap.
    FPS = 30                          # Çıktı FPS
    DURATION = 30                     # Video süresi (sabit 30 sn)
    IMAGES_PER_VIDEO = 6              # 6 görsel -> her biri ~5sn. images/ azsa otomatik düşer.
    FONT_PATH = "fonts/Rubik-SemiBold.ttf"  # Türkçe karakter destekli bir TTF. Yoksa default yedek kullanır.
    TITLE_FONT_SIZE = 64
    USER_FONT_SIZE = 42
    TEXT_MARGIN = 40                  # Kenarlardan iç boşluk (px)
    TEXT_BG_ALPHA = 140               # Metin arkaplan saydamlığı (0-255)
    
    # ==== KAYNAK KLASÖRLER ====
    AUDIO_DIR = Path("audio")
    IMAGES_DIR = Path("images")
    OUTPUT_DIR = Path("output")
    OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
    
    # ==== YARDIMCI FONKSİYONLAR ====
    def list_media_files(folder: Path, exts: Tuple[str, ...]) -> List[Path]:
        return sorted([p for p in folder.glob("*") if p.suffix.lower() in exts])
    
    def infer_title_from_filename(path: Path) -> str:
        # "Artist - Song Name (feat.) .mp3" -> "Artist - Song Name (feat.)"
        title = path.stem
        # Kaba temizlik: underscore -> boşluk, çift boşlukları düzelt
        title = re.sub(r"[_]+", " ", title).strip()
        title = re.sub(r"\s{2,}", " ", title)
        return title
    
    def load_font(size: int) -> ImageFont.FreeTypeFont:
        try:
            if Path(FONT_PATH).exists():
                return ImageFont.truetype(FONT_PATH, size=size)
            # Sistem fontlarına düşmek için isim vermeden dener (platforma göre değişir)
            return ImageFont.truetype("DejaVuSans.ttf", size=size)
        except Exception:
            # Son çare: PIL default bitmap font (Türkçe destek sınırlı olabilir)
            return ImageFont.load_default()
    
    def make_text_overlay(
        title: str,
        username: str,
        size: Tuple[int, int],
        margin: int = 40,
        title_size: int = 64,
        user_size: int = 42,
        bg_alpha: int = 140,
    ) -> str:
        """
        Metinleri (şarkı adı + kullanıcı adı) şeffaf PNG'ye yazar ve dosya yolunu döner.
        Pillow 10/11 uyumlu: textbbox() varsa onu, yoksa font.getsize() kullanır.
        """
        W, H = size
        title_font = load_font(title_size)
        user_font  = load_font(user_size)
    
        # Ölçüm yardımcıları
        from PIL import ImageDraw as _ImageDraw
        def measure(draw_obj, text, font):
            # Pillow 8+ : textbbox mevcut, en doğru ölçüm
            if hasattr(draw_obj, "textbbox"):
                l, t, r, b = draw_obj.textbbox((0, 0), text, font=font)
                return (r - l, b - t)
            # Eski sürümler için yedek
            return font.getsize(text)
    
        # Satır kaydırma
        draw_tmp = ImageDraw.Draw(Image.new("RGBA", (W, H)))
        max_text_width = W - 2 * margin
    
        def wrap_text(text, font, max_width):
            words = text.split()
            lines, line = [], ""
            for w in words:
                test = (line + " " + w).strip()
                tw, _ = measure(draw_tmp, test, font)
                if tw <= max_text_width or not line:
                    line = test
                else:
                    lines.append(line)
                    line = w
            if line:
                lines.append(line)
            return lines
    
        title_lines = wrap_text(title, title_font, max_text_width)
        user_lines  = wrap_text(f"@{username}", user_font, max_text_width)
    
        # Yükseklik hesapla
        line_spacing = 8
        def block_height(lines, font):
            if not lines:
                return 0
            heights = [measure(draw_tmp, ln, font)[1] for ln in lines]
            return sum(heights) + line_spacing * (len(lines) - 1)
    
        title_h = block_height(title_lines, title_font)
        user_h  = block_height(user_lines,  user_font)
    
        block_pad_v = 16
        total_h = title_h + user_h + block_pad_v * 2 + 10
    
        overlay = Image.new("RGBA", (W, total_h), (0, 0, 0, 0))
        bg = Image.new("RGBA", (W, total_h), (0, 0, 0, bg_alpha))
        overlay.paste(bg, (0, 0))
    
        draw = ImageDraw.Draw(overlay)
    
        # Başlığı merkezle
        y = block_pad_v
        for ln in title_lines:
            tw, th = measure(draw, ln, title_font)
            draw.text(((W - tw) // 2, y), ln, font=title_font, fill=(255, 255, 255, 255))
            y += th + line_spacing
    
        y += 2  # küçük boşluk
    
        # Kullanıcı adını merkezle
        for ln in user_lines:
            tw, th = measure(draw, ln, user_font)
            draw.text(((W - tw) // 2, y), ln, font=user_font, fill=(220, 220, 220, 255))
            y += th + line_spacing
    
        tmp_path = OUTPUT_DIR / f"overlay_{abs(hash(title))}.png"
        overlay.save(tmp_path)
        return str(tmp_path)
    
    
    def make_image_clip(img_path: Path, duration: float, target_size: Tuple[int, int]) -> ImageClip:
        """
        Görseli ekranı dolduracak şekilde letterbox olmadan kırp/zoom et (cover),
        hafif bir yavaÅŸ zoom efekti ekle.
        """
        W, H = target_size
        clip = ImageClip(str(img_path)).set_duration(duration)
        # Önce ortalama bir cover yapalım:
        clip = clip.resize(height=H) if clip.h < clip.w else clip.resize(width=W)
    
        # Cover: gerekiyorsa kırp
        # Eni boyu aşan kenarları merkezden kırp
        if clip.w < W:
            clip = clip.resize(width=W)
        if clip.h < H:
            clip = clip.resize(height=H)
        x_center = (clip.w - W) / 2
        y_center = (clip.h - H) / 2
        clip = clip.crop(x1=x_center, y1=y_center, x2=x_center + W, y2=y_center + H)
    
        # YumuÅŸak zoom (Ken Burns hissi)
        # 30 sn'de %6 büyüme için ~0.002/ sn
        zoom_rate = 0.002  # istersen arttır/azalt
        clip = clip.resize(lambda t: 1 + zoom_rate * t)
    
        return clip
    
    def build_video_for_audio(audio_path: Path, images: List[Path]) -> Path:
        # Başlık + overlay PNG
        title = infer_title_from_filename(audio_path)
        overlay_png = make_text_overlay(
            title=title,
            username=USERNAME,
            size=OUTPUT_SIZE,
            margin=TEXT_MARGIN,
            title_size=TITLE_FONT_SIZE,
            user_size=USER_FONT_SIZE,
            bg_alpha=TEXT_BG_ALPHA,
        )
    
        # 30 sn ses
        audio = AudioFileClip(str(audio_path)).subclip(0, DURATION)
    
        # Görsel süre/adet
        n_images = min(IMAGES_PER_VIDEO, len(images)) if len(images) > 0 else 1
        if n_images <= 0:
            raise RuntimeError("images/ klasöründe görsel bulunamadı.")
        segment = DURATION / n_images
    
        chosen = random.sample(images, n_images) if len(images) >= n_images else images[:]
        while len(chosen) < n_images:
            chosen += random.sample(images, min(len(images), n_images - len(chosen)))
    
        # Arka plan klipleri (Ken Burns hafif zoom içerir)
        clips = [make_image_clip(p, segment, OUTPUT_SIZE) for p in chosen]
        bg = concatenate_videoclips(clips, method="chain").set_duration(DURATION)
    
        # Overlay bant
        overlay_clip = (
            ImageClip(overlay_png)
            .set_duration(DURATION)
            .set_position(("center", "top"))
        )
    
        # Final kompozit
        final = CompositeVideoClip([bg, overlay_clip], size=OUTPUT_SIZE).set_audio(audio)
    
        # Çıktı adı
        safe_title = re.sub(r"[^\w\-]+", "_", title)[:80]
        out_path = OUTPUT_DIR / f"{safe_title or 'video'}.mp4"
    
        # Yaz ve temizle
        try:
            final.write_videofile(
                str(out_path),
                fps=FPS,
                codec="libx264",
                audio_codec="aac",
                threads=os.cpu_count() or 4,
                preset="medium",
                ffmpeg_params=["-movflags", "+faststart", "-pix_fmt", "yuv420p"],
                temp_audiofile=str(OUTPUT_DIR / "temp-audio.m4a"),
                remove_temp=True,
            )
        finally:
            # Geçici overlay PNG'sini sil (yazım başarısız olsa bile)
            try:
                os.remove(overlay_png)
            except Exception:
                pass
    
        return out_path
    
    
    
    def main():
        audio_files = list_media_files(AUDIO_DIR, (".mp3", ".wav", ".m4a", ".flac", ".aac", ".ogg"))
        image_files = list_media_files(IMAGES_DIR, (".jpg", ".jpeg", ".png", ".webp"))
    
        if not audio_files:
            raise SystemExit("audio/ klasöründe ses dosyası bulunamadı.")
        if not image_files:
            raise SystemExit("images/ klasöründe görsel bulunamadı.")
    
        random.shuffle(image_files)
    
        print(f"{len(audio_files)} şarkı bulundu, {len(image_files)} görsel bulundu.")
        for i, a in enumerate(audio_files, 1):
            print(f"[{i}/{len(audio_files)}] {a.name} -> video oluÅŸturuluyor...")
            try:
                out = build_video_for_audio(a, image_files)
                print(f" ✔ {out}")
            except Exception as e:
                print(f" ✖ Hata: {a.name}: {e}")
    
    if __name__ == "__main__":
        main()
  • 27-10-2025, 01:25:32
    #2
    Elinize sağlık, kendiniz mi yazdınız?
  • 27-10-2025, 01:29:01
    #3
    tristy adlı üyeden alıntı: mesajı görüntüle
    Elinize sağlık, kendiniz mi yazdınız?
    evet chatgpt ile birlikte, aktif olarak kullanıyorum, biraz daha geliştirmeye çalışıyorum. otomatik upload vb gibi.
  • 30-10-2025, 02:02:04
    #4
    masaüstü program gibi veya online php script gibi yapılamaz mı
  • 30-10-2025, 15:35:20
    #5
    Github linki kırık hocam
  • 03-11-2025, 02:54:51
    #6
    offonarts adlı üyeden alıntı: mesajı görüntüle
    masaüstü program gibi veya online php script gibi yapılamaz mı
    masaüstü program işi mümkün aslında, deneyeyim.
  • 03-11-2025, 02:55:27
    #7
    xLyra adlı üyeden alıntı: mesajı görüntüle
    Github linki kırık hocam
    kontrol ettim çalışıyor gibi geldi bana ama, yeniden yükleyeyim.
  • 03-11-2025, 13:52:47
    #8
    teşekkürler hocam