program

Youtube Video İndirici 2026 | Ücretsiz YouTube video indirme aracı | Kendin Kodla

```html

Bu rehberde Python kullanarak Windows üzerinde çalışan bir YouTube video indirme arayüzünün nasıl geliştirileceğini öğreneceksiniz. Projede CustomTkinter ile masaüstü arayüzü, yt-dlp ile indirme işlemleri ve PyInstaller ile EXE paketleme adımları ele alınıyor. Kaynak kodu kendi bilgisayarınızda çalıştırabilir ve ihtiyaçlarınıza göre geliştirebilirsiniz.

Önemli: Bu projeyi yalnızca indirme iznine sahip olduğunuz, size ait veya kullanımına izin verilen içeriklerde kullanın. YouTube'un kullanım şartlarına ve ilgili içeriklerin telif haklarına uyun. Kod, DRM veya erişim kısıtlamalarını aşmak için tasarlanmamıştır.

1. Uygulamanın özellikleri

Hazırlayacağımız uygulama, komut satırını sürekli kullanmak istemeyenler için basit bir masaüstü arayüzü sunar.

  • YouTube bağlantısı girebileceğiniz URL alanı.
  • İndirme klasörünü seçme imkânı.
  • Video kalitesi tercihi.
  • İndirme durumunu ve ilerleme çubuğunu gösterme.
  • İşlem tamamlandığında kullanıcıya bilgi verme.
  • Hatalı bağlantılar ve indirme hataları için uyarılar.
  • Windows için EXE dosyasına dönüştürülebilen Python projesi.

2. Kullanılan teknolojiler

TeknolojiGörevi
PythonUygulamanın temel programlama dili.
CustomTkinterModern görünümlü masaüstü arayüzü.
yt-dlpDesteklenen bağlantılardaki medya indirme işlemleri.
FFmpegGerekli durumlarda video ve ses akışlarını birleştirme veya dönüştürme.
PyInstallerPython uygulamasını Windows çalıştırılabilir dosyasına paketleme.

3. Python video indirici kurulumu

Adım 1: Python'ı yükleyin

Python kurulu değilse Python'ın resmî indirme sayfasını açın ve Windows sürümünü yükleyin. Kurulum ekranında Add Python to PATH seçeneğini işaretleyin.

Kurulumdan sonra Komut İstemi'ni açıp aşağıdaki komutlardan birini çalıştırın:

python --version

Bu komut çalışmazsa şu alternatifi deneyebilirsiniz:

py --version

Adım 2: Proje klasörünü oluşturun

Masaüstünde YouTubeIndirici adında bir klasör oluşturun. Klasörün içinde terminal açın veya Komut İstemi üzerinden klasöre geçin.

cd %USERPROFILE%\Desktop
mkdir YouTubeIndirici
cd YouTubeIndirici

Adım 3: Sanal ortam oluşturun

Sanal ortam, projenin Python paketlerini diğer projelerden ayrı tutar.

python -m venv .venv
.venv\Scripts\activate

Windows'ta python komutu tanınmıyorsa komutları py ile çalıştırmayı deneyin.

Adım 4: Gerekli kütüphaneleri yükleyin

python -m pip install --upgrade pip
python -m pip install customtkinter yt-dlp pyinstaller

FFmpeg'i ayrıca kurmanız gerekebilir. Kurulum ve kullanım ayrıntıları için FFmpeg resmî indirme sayfasına bakabilirsiniz.

4. YouTube video indirici Python kaynak kodu

Aşağıdaki kodu main.py adıyla kaydedin. Arayüz, indirme işlemini ayrı bir iş parçacığında yürütür; böylece işlem sürerken pencerenin yanıt vermeye devam etmesi sağlanır.

Not: YouTube zaman zaman indirme yöntemlerini değiştirebilir. Bu nedenle yt-dlp paketini güncel tutun. Kodun çalışması, her bağlantının veya her formatın her koşulda indirilebileceği anlamına gelmez.
import os
import threading
import queue
from pathlib import Path
from urllib.parse import urlparse

import customtkinter as ctk
import yt_dlp
from tkinter import filedialog, messagebox


APP_TITLE = "Web Dayı | Video İndirici"
DEFAULT_FOLDER = str(Path.home() / "Downloads")


class VideoDownloader(ctk.CTk):
    def __init__(self):
        super().__init__()

        self.title(APP_TITLE)
        self.geometry("720x590")
        self.minsize(620, 520)

        ctk.set_appearance_mode("light")
        ctk.set_default_color_theme("blue")

        self.events = queue.Queue()
        self.downloading = False
        self.download_folder = DEFAULT_FOLDER

        Path(self.download_folder).mkdir(
            parents=True, exist_ok=True
        )

        self.create_widgets()
        self.after(100, self.process_events)

    def create_widgets(self):
        self.grid_columnconfigure(0, weight=1)

        header = ctk.CTkFrame(self, corner_radius=14)
        header.grid(
            row=0, column=0, padx=22, pady=(22, 12),
            sticky="ew"
        )
        header.grid_columnconfigure(0, weight=1)

        ctk.CTkLabel(
            header,
            text="VIDEO İNDİRİCİ",
            font=ctk.CTkFont(size=25, weight="bold")
        ).grid(row=0, column=0, padx=20, pady=(18, 4))

        ctk.CTkLabel(
            header,
            text="Python • CustomTkinter • yt-dlp",
            text_color="#606770",
            font=ctk.CTkFont(size=13)
        ).grid(row=1, column=0, padx=20, pady=(0, 18))

        panel = ctk.CTkFrame(self, corner_radius=14)
        panel.grid(
            row=1, column=0, padx=22, pady=8, sticky="ew"
        )
        panel.grid_columnconfigure(0, weight=1)

        ctk.CTkLabel(
            panel, text="Video bağlantısı",
            anchor="w", font=ctk.CTkFont(weight="bold")
        ).grid(row=0, column=0, padx=18, pady=(17, 6), sticky="ew")

        self.url_entry = ctk.CTkEntry(
            panel,
            placeholder_text="İndirme izniniz olan video bağlantısını girin"
        )
        self.url_entry.grid(
            row=1, column=0, padx=18, pady=(0, 14), sticky="ew"
        )

        ctk.CTkLabel(
            panel, text="Video kalitesi",
            anchor="w", font=ctk.CTkFont(weight="bold")
        ).grid(row=2, column=0, padx=18, pady=(2, 6), sticky="ew")

        self.quality_menu = ctk.CTkOptionMenu(
            panel,
            values=[
                "En iyi kalite",
                "1080p",
                "720p",
                "480p",
                "360p"
            ]
        )
        self.quality_menu.set("En iyi kalite")
        self.quality_menu.grid(
            row=3, column=0, padx=18, pady=(0, 14), sticky="ew"
        )

        ctk.CTkLabel(
            panel, text="Kayıt klasörü",
            anchor="w", font=ctk.CTkFont(weight="bold")
        ).grid(row=4, column=0, padx=18, pady=(2, 6), sticky="ew")

        folder_row = ctk.CTkFrame(panel, fg_color="transparent")
        folder_row.grid(
            row=5, column=0, padx=18, pady=(0, 18), sticky="ew"
        )
        folder_row.grid_columnconfigure(0, weight=1)

        self.folder_label = ctk.CTkLabel(
            folder_row,
            text=self.download_folder,
            anchor="w",
            wraplength=450
        )
        self.folder_label.grid(
            row=0, column=0, padx=(0, 10), sticky="ew"
        )

        self.folder_button = ctk.CTkButton(
            folder_row,
            text="Klasör seç",
            width=110,
            command=self.choose_folder
        )
        self.folder_button.grid(row=0, column=1)

        self.progress = ctk.CTkProgressBar(self)
        self.progress.grid(
            row=2, column=0, padx=22, pady=(17, 4), sticky="ew"
        )
        self.progress.set(0)

        self.status_label = ctk.CTkLabel(
            self, text="İndirme için hazır.",
            anchor="w", wraplength=650
        )
        self.status_label.grid(
            row=3, column=0, padx=24, pady=(4, 12), sticky="ew"
        )

        self.download_button = ctk.CTkButton(
            self,
            text="İndirmeyi başlat",
            height=44,
            font=ctk.CTkFont(size=15, weight="bold"),
            command=self.start_download
        )
        self.download_button.grid(
            row=4, column=0, padx=22, pady=(0, 12), sticky="ew"
        )

        ctk.CTkLabel(
            self,
            text="Yalnızca indirme izniniz bulunan içerikleri kullanın.",
            text_color="#707780",
            wraplength=650
        ).grid(row=5, column=0, padx=22, pady=(0, 18))

    def choose_folder(self):
        if self.downloading:
            return

        folder = filedialog.askdirectory(
            initialdir=self.download_folder,
            title="Kayıt klasörünü seçin"
        )

        if folder:
            self.download_folder = folder
            self.folder_label.configure(text=folder)

    @staticmethod
    def is_valid_url(url):
        try:
            parsed = urlparse(url.strip())
            host = (parsed.hostname or "").lower()

            allowed_hosts = (
                "youtube.com",
                "www.youtube.com",
                "m.youtube.com",
                "youtu.be",
                "www.youtube-nocookie.com",
            )

            return (
                parsed.scheme in ("http", "https")
                and any(
                    host == allowed or host.endswith("." + allowed)
                    for allowed in allowed_hosts
                )
            )
        except (ValueError, TypeError):
            return False

    def start_download(self):
        if self.downloading:
            return

        url = self.url_entry.get().strip()

        if not self.is_valid_url(url):
            messagebox.showerror(
                "Geçersiz bağlantı",
                "Lütfen desteklenen bir YouTube bağlantısı girin."
            )
            return

        if not os.path.isdir(self.download_folder):
            messagebox.showerror(
                "Klasör hatası",
                "Seçilen kayıt klasörü bulunamadı."
            )
            return

        quality = self.quality_menu.get()
        self.downloading = True
        self.download_button.configure(
            state="disabled", text="İndiriliyor..."
        )
        self.folder_button.configure(state="disabled")
        self.progress.set(0)
        self.status_label.configure(text="İndirme hazırlanıyor...")

        worker = threading.Thread(
            target=self.download_worker,
            args=(url, quality, self.download_folder),
            daemon=True
        )
        worker.start()

    def download_worker(self, url, quality, folder):
        def progress_hook(data):
            status = data.get("status")

            if status == "downloading":
                total = (
                    data.get("total_bytes")
                    or data.get("total_bytes_estimate")
                )
                downloaded = data.get("downloaded_bytes", 0)

                percent = downloaded / total if total else 0
                speed = data.get("_speed_str", "")
                eta = data.get("_eta_str", "")

                self.events.put((
                    "progress",
                    max(0, min(percent, 1)),
                    f"İndiriliyor: %{percent * 100:.1f} "
                    f"| Hız: {speed} | Kalan: {eta}"
                ))

            elif status == "finished":
                self.events.put((
                    "progress", 1,
                    "Dosya alındı. Son işlemler yapılıyor..."
                ))

        formats = {
            "En iyi kalite":
                "bestvideo*+bestaudio/best",
            "1080p":
                "bestvideo[height<=1080]+bestaudio/best[height<=1080]",
            "720p":
                "bestvideo[height<=720]+bestaudio/best[height<=720]",
            "480p":
                "bestvideo[height<=480]+bestaudio/best[height<=480]",
            "360p":
                "bestvideo[height<=360]+bestaudio/best[height<=360]",
        }

        options = {
            "format": formats.get(quality, "bestvideo*+bestaudio/best"),
            "outtmpl": os.path.join(folder, "%(title).180s.%(ext)s"),
            "noplaylist": True,
            "progress_hooks": [progress_hook],
            "retries": 3,
            "quiet": True,
            "no_warnings": True,
            "windowsfilenames": True,
        }

        try:
            with yt_dlp.YoutubeDL(options) as downloader:
                downloader.download([url])

            self.events.put(("done", True, "İndirme tamamlandı."))

        except Exception as exc:
            # Kullanıcı arayüzünü engellememek için
            # hata bilgisi ana pencereye kuyruk üzerinden iletilir.
            self.events.put((
                "done", False,
                f"İndirme başarısız: {str(exc)[:700]}"
            ))

    def process_events(self):
        try:
            while True:
                event = self.events.get_nowait()
                event_type = event[0]

                if event_type == "progress":
                    _, value, message = event
                    self.progress.set(value)
                    self.status_label.configure(text=message)

                elif event_type == "done":
                    _, success, message = event
                    self.downloading = False

                    self.download_button.configure(
                        state="normal", text="İndirmeyi başlat"
                    )
                    self.folder_button.configure(state="normal")
                    self.status_label.configure(text=message)

                    if success:
                        self.progress.set(1)
                        messagebox.showinfo("Tamamlandı", message)
                    else:
                        messagebox.showerror("İndirme hatası", message)

        except queue.Empty:
            pass

        self.after(100, self.process_events)


if __name__ == "__main__":
    app = VideoDownloader()
    app.mainloop()

5. Python uygulamasını çalıştırma

Kodu main.py adıyla proje klasörüne kaydettikten sonra sanal ortamın aktif olduğundan emin olun. Ardından şu komutu çalıştırın:

python main.py

Uygulama penceresi açıldığında indirme izniniz bulunan desteklenen bir YouTube bağlantısı girin. Kayıt klasörünü ve kalite tercihini seçerek işlemi başlatabilirsiniz.

Kalite hakkında: Seçilen çözünürlüğün bulunması videonun kaynak formatlarına bağlıdır. Bazı yüksek çözünürlüklü videolarda ses ve görüntü ayrı akışlarda sunulabilir; bu durumda FFmpeg gerekebilir.

6. Python uygulamasını EXE dosyasına dönüştürme

Adım 1: PyInstaller'ı yükleyin

python -m pip install pyinstaller

Adım 2: EXE dosyasını oluşturun

Proje klasöründe aşağıdaki komutu çalıştırın:

python -m PyInstaller --noconfirm --clean --windowed --name WebDayiVideoIndirici main.py

İşlem tamamlandığında uygulama genellikle dist/WebDayiVideoIndirici/ klasöründe bulunur. Oluşturulan EXE dosyasını bu klasördeki diğer dosyalarla birlikte tutun; yalnızca EXE dosyasını başka bir klasöre taşımak her zaman yeterli değildir.

FFmpeg ve EXE notu

FFmpeg gerektiren indirmelerde FFmpeg'in uygulama tarafından bulunabilir olması gerekir. En kolay yöntem, FFmpeg'i sisteme kurup PATH değişkenine eklemektir. Başka bilgisayarlarda kullanılacak bir paket hazırlıyorsanız FFmpeg'in dağıtım koşullarını ve gerekli dosyaların pakette bulunup bulunmadığını ayrıca kontrol edin.

7. Sık karşılaşılan hatalar ve çözümleri

Python komutu tanınmıyor

Python kurulumu sırasında PATH seçeneği işaretlenmemiş olabilir. Python'ı yeniden kurun veya terminalde py komutunu deneyin.

ModuleNotFoundError hatası

Gerekli paketler aktif sanal ortama kurulmamış olabilir. Sanal ortamı etkinleştirip aşağıdaki komutu çalıştırın:

python -m pip install customtkinter yt-dlp pyinstaller

FFmpeg bulunamadı

FFmpeg'i yükleyin ve terminalden ffmpeg -version komutuyla kontrol edin. Komut tanınmıyorsa PATH ayarını veya uygulamanın FFmpeg dosyalarına erişimini kontrol edin.

Video indirilemiyor

  • Bağlantının doğru ve erişilebilir olduğunu kontrol edin.
  • yt-dlp paketini güncelleyin:
python -m pip install --upgrade yt-dlp
  • Seçilen kalite için uygun formatın bulunup bulunmadığını kontrol edin.
  • İçeriğin indirilmesine izin verildiğinden ve ek erişim şartları bulunmadığından emin olun.
  • Hata sürerse terminalden çalıştırarak hata mesajını inceleyin.

EXE açılmıyor veya hemen kapanıyor

Uygulamayı terminalden çalıştırarak gerçek hata mesajını görün. Sorunun eksik paket, FFmpeg yolu veya paketleme ayarı kaynaklı olup olmadığını bu şekilde ayırabilirsiniz. Geliştirme sırasında --windowed parametresini kaldırmak hata mesajlarını görmeyi kolaylaştırabilir.

8. Uygulama nasıl geliştirilebilir?

Temel uygulama çalıştıktan sonra aşağıdaki özellikleri ekleyebilirsiniz:

  • İndirme geçmişi ve tamamlanan dosyaların listesi.
  • Dosya adı ve kayıt konumu için daha ayrıntılı seçenekler.
  • İndirme iptali ve işlem durumunu daha ayrıntılı gösteren bir arayüz.
  • Farklı işletim sistemleri için ayrı paketleme ve test süreçleri.
  • Otomatik güncelleme veya paket sürümünü gösteren bir bilgi alanı.

Linux ve macOS üzerinde de Python arayüzü geliştirilebilir; ancak EXE dosyası Windows'a özgüdür. Diğer işletim sistemleri için paketlemeyi o sistemlerde ayrıca test etmeniz gerekir.

9. Kullanım koşulları ve telif hakları

Bir içeriğin internet üzerinden görüntülenebiliyor olması, o içeriği istediğiniz şekilde indirip yeniden yayımlama hakkınız olduğu anlamına gelmez. İndirdiğiniz videoların kullanım şartlarını, telif haklarını ve platform kurallarını dikkate alın. Bu projeyi yalnızca yasal olarak erişme ve indirme hakkınız olan içeriklerle kullanın.

Sonuç

Bu rehberde Python, CustomTkinter ve yt-dlp kullanarak masaüstü video indirme arayüzü oluşturmayı; gerekli paketleri kurmayı, uygulamayı çalıştırmayı ve PyInstaller ile Windows için paketlemeyi ele aldık.

Kod, geliştirilebilir bir başlangıç projesidir. Gerçek kullanım öncesinde farklı bağlantılarla test edin, hata mesajlarını kontrol edin ve paketlediğiniz uygulamayı hedef bilgisayarda doğrulayın.

Sık sorulan sorular

Bu uygulama ücretsiz mi?

Evet bu paylaşılan kodlar ücretsizdir. İstediğiniz gibi kullanabilirsiniz.

EXE oluşturmak için Python gerekli mi?

EXE dosyasını oluşturacağınız bilgisayarda Python ve geliştirme paketleri gerekir. Son kullanıcı bilgisayarında gereksinimler paketleme biçimine bağlıdır; uygulamayı hedef bilgisayarda test etmelisiniz.

FFmpeg olmadan çalışır mı?

Basit indirmeler bazı durumlarda FFmpeg olmadan çalışabilir. Ayrı ses ve görüntü akışlarının birleştirilmesi gerektiğinde FFmpeg gerekebilir.

Her YouTube videosu indirilebilir mi?

Hayır. Erişim, format, platform değişiklikleri ve içerik kullanım koşulları indirmeyi etkileyebilir. Yalnızca indirme izniniz olan içerikleri kullanın.

Web Dayı — Python, yazılım ve teknoloji rehberleri.

```