#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
#     "PyQt5==5.15.9",
#     "PyInstaller==5.13.2",
#     "requests==2.31.0",
# ]
# ///
import sys
import os
import json
import subprocess
import re
import urllib.request
import uuid
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, 
                            QLabel, QLineEdit, QPushButton, QComboBox, QTextEdit, 
                            QFileDialog, QMessageBox, QListWidget, QDialog, QFormLayout,
                            QDialogButtonBox, QInputDialog, QGroupBox, QCheckBox, QTabWidget,
                            QListWidgetItem, QMenu, QAction)
from PyQt5.QtCore import QThread, pyqtSignal, Qt
from PyQt5.QtGui import QIcon

# Hilfsfunktionen für den Ressourcenpfad
def get_base_path():
    """Gibt den Basispfad für Ressourcen zurück, funktioniert sowohl für PyInstaller als auch für reguläre Ausführung."""
    if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
        # PyInstaller-Bundled-Modus
        return sys._MEIPASS
    else:
        # Regulärer Modus
        return os.path.dirname(os.path.abspath(__file__))

def get_user_data_dir():
    if getattr(sys, 'frozen', False):
        return os.path.dirname(sys.executable)
    else:
        return os.path.dirname(os.path.abspath(__file__))

def get_temp_dir():
    """Gibt den Pfad zum temporären Verzeichnis zurück und erstellt es bei Bedarf."""
    base_dir = get_base_path()
    temp_dir = os.path.join(base_dir, "temp")
    if not os.path.exists(temp_dir):
        os.makedirs(temp_dir, exist_ok=True)
    return temp_dir

def get_user_presets_dir():
    # Presets-Ordner neben der EXE (bzw. Script)
    base = get_user_data_dir()
    path = os.path.join(base, "presets")
    if not os.path.exists(path):
        os.makedirs(path, exist_ok=True)
    return path

def mask_sensitive_data(command_string):
    """Maskiert sensible Daten wie Benutzernamen und Passwörter in der Befehlszeile."""
    # Benutzername maskieren (-u "username" oder --username "username")
    command_string = re.sub(r'(-u|--username)\s+("[^"]+"|\'[^\']+\'|\S+)', r'\1 "******"', command_string)
    
    # Passwort maskieren (-p "password" oder --password "password")
    command_string = re.sub(r'(-p|--password)\s+("[^"]+"|\'[^\']+\'|\S+)', r'\1 "******"', command_string)
    
    return command_string

CONFIG_FILE = os.path.join(get_user_data_dir(), "config.json")
PRESETS_DIR = os.path.join(get_base_path(), "presets")  # Nur für Lesezugriff auf mitgelieferte Presets
DEFAULT_CONFIG = {
    "output_dir": "",
    "use_local_ytdlp": True,
    "last_preset": "",
    "presets": [],
    "ytdlp_flags": {
        "ignore_config": False,
        "remux_mkv": False,
        "embed_metadata": False,
        "use_ffmpeg_location": False
    },
    "hide_default_presets": False,
    "enable_adn_tab": False,
    "mkvmerge_path": "C:\\Program Files\\MKVToolNix\\mkvmerge.exe",
    "ffmpeg_path": "C:\\ffmpeg\\bin\\ffmpeg.exe"
}

# Template-Variablen für Serien
SERIES_TEMPLATE = "{series} S{season}E{episode}{extension}"

class DownloadThread(QThread):
    update_signal = pyqtSignal(str)
    finished_signal = pyqtSignal(bool, str)
    format_selection_signal = pyqtSignal(list)  # Signal für Format-Auswahl
    format_id_input_signal = pyqtSignal()       # Signal für Format-ID Eingabe

    def __init__(self, url, output_dir, cmd_args, use_local_ytdlp, output_filename=None, 
                preset_data=None, config=None):
        super().__init__()
        self.url = url
        self.output_dir = output_dir
        self.cmd_args = cmd_args
        self.use_local_ytdlp = use_local_ytdlp
        self.output_filename = output_filename
        self.process = None
        self.abort = False
        self.format_id = None
        self.preset_data = preset_data or {}
        self.config = config or {}
        self.temp_files = []  # Liste der temporären Dateien für das Muxing

    def run(self):
        try:
            # Bestimme den Pfad zu yt-dlp
            if self.use_local_ytdlp:
                ytdlp_path = os.path.join(get_base_path(), "bin", "yt-dlp.exe")
                if not os.path.exists(ytdlp_path):
                    ytdlp_path = "yt-dlp"  # Fallback auf PATH
            else:
                ytdlp_path = "yt-dlp"
            
            # Prüfe, ob Format-Auswahl aktiviert ist
            if self.preset_data.get("use_format_selection", False):
                formats = self.get_available_formats(ytdlp_path)
                if self.abort:
                    return
                
                # Warte auf Format-ID Eingabe vom Benutzer
                self.format_selection_signal.emit(formats)
                self.format_id_input_signal.emit()
                
                # Warte auf Format-ID (wird über set_format_id gesetzt)
                while self.format_id is None:
                    if self.abort:
                        self.update_signal.emit("Abgebrochen.")
                        self.finished_signal.emit(False, "Download wurde abgebrochen.")
                        return
                    self.msleep(100)
                
                self.update_signal.emit(f"Format-ID ausgewählt: {self.format_id}")
            
            # Prüfe auf Dual-Audio-Muxing
            if self.preset_data.get("use_dual_audio", False):
                # Stelle sicher, dass --remux-video mkv nicht in den Argumenten ist
                self.cmd_args = re.sub(r'--remux-video\s+mkv\s*', '', self.cmd_args)
                self.update_signal.emit("Dual-Audio-Modus aktiv: --remux-video mkv wird ignoriert")
                self.perform_dual_audio_download(ytdlp_path)
                return
            
            # Normaler Download-Prozess (bestehender Code)
            cmd = [ytdlp_path]
            # Debug-Ausgabe
            self.update_signal.emit(f"Debug - self.cmd_args: {self.cmd_args}")
            self.update_signal.emit(f"Debug - self.output_dir: {self.output_dir}")
            self.update_signal.emit(f"Debug - self.output_filename: {self.output_filename}")
            
            if self.cmd_args:
                # Argumente per Split aufteilen, dabei aber Anführungszeichen berücksichtigen
                args = []
                in_quotes = False
                current_arg = ""
                
                for char in self.cmd_args:
                    if char == '"' or char == "'":
                        in_quotes = not in_quotes
                        current_arg += char
                    elif char.isspace() and not in_quotes:
                        if current_arg:
                            args.append(current_arg)
                            current_arg = ""
                    else:
                        current_arg += char
                        
                if current_arg:
                    args.append(current_arg)
                
                # Debug-Ausgabe
                self.update_signal.emit(f"Debug - Parsed args: {args}")    
                cmd.extend(args)
            
            # Bei Format-Auswahl die ID verwenden
            if self.format_id and not self.preset_data.get("use_dual_audio", False):
                cmd.extend(["-f", self.format_id])
            
            if self.output_dir:
                if self.output_filename:
                    output_path = os.path.join(self.output_dir, self.output_filename)
                    self.update_signal.emit(f"Debug - Vollständiger Ausgabepfad: {output_path}")
                    cmd.extend(["-o", output_path])
                else:
                    # Verwende benutzerdefinierten Namen, wenn in den Presets aktiviert
                    if self.preset_data.get("custom_output_template", False) and self.preset_data.get("output_template"):
                        output_template = self.preset_data.get("output_template")
                        output_path = os.path.join(self.output_dir, output_template)
                        self.update_signal.emit(f"Debug - Benutzerdefinierte Name: {output_path}")
                    else:
                        output_path = os.path.join(self.output_dir, "%(title)s.%(ext)s")
                        self.update_signal.emit(f"Debug - Standard-Ausgabepfad: {output_path}")
                    cmd.extend(["-o", output_path])
            elif self.output_filename:
                self.update_signal.emit(f"Debug - Nur Ausgabedateiname: {self.output_filename}")
                cmd.extend(["-o", self.output_filename])
            elif self.preset_data.get("custom_output_template", False) and self.preset_data.get("output_template"):
                # Wenn kein Ausgabeverzeichnis, aber benutzerdefinierte Vorlage vorhanden
                output_template = self.preset_data.get("output_template")
                self.update_signal.emit(f"Debug - Nur benutzerdefinierte Name: {output_template}")
                cmd.extend(["-o", output_template])
            
            cmd.append(self.url)
            
            # Debug-Ausgabe vor der Formatierung
            self.update_signal.emit(f"Debug - Befehlszeile vor Formatierung: {cmd}")
            
            # Formatiere die Befehlszeile mit Anführungszeichen für Elemente mit Leerzeichen
            formatted_cmd = []
            for arg in cmd:
                if " " in arg and not (arg.startswith('"') and arg.endswith('"')) and not (arg.startswith("'") and arg.endswith("'")):
                    formatted_cmd.append(f'"{arg}"')
                else:
                    formatted_cmd.append(arg)
            
            self.update_signal.emit(f"Ausführen: {' '.join(formatted_cmd)}")
            
            # Unterdrücke das CMD-Fenster unter Windows
            creationflags = subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0
            self.process = subprocess.Popen(
                cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                bufsize=1,
                universal_newlines=True,
                creationflags=creationflags
            )
            
            for line in self.process.stdout:
                if self.abort:
                    self.process.terminate()
                    self.update_signal.emit("Download abgebrochen.")
                    self.finished_signal.emit(False, "Download wurde abgebrochen.")
                    return
                self.update_signal.emit(line.strip())
            
            self.process.wait()
            
            if self.abort:
                self.finished_signal.emit(False, "Download wurde abgebrochen.")
            elif self.process.returncode == 0:
                self.finished_signal.emit(True, "Download erfolgreich abgeschlossen!")
            else:
                self.finished_signal.emit(False, f"Download fehlgeschlagen mit Exitcode {self.process.returncode}")
                
        except Exception as e:
            self.update_signal.emit(f"Fehler: {str(e)}")
            self.finished_signal.emit(False, f"Fehler: {str(e)}")
            
    def stop(self):
        self.abort = True
        if self.process:
            try:
                self.update_signal.emit("Versuche Download zu beenden...")
            except:
                pass

    def get_available_formats(self, ytdlp_path):
        """Führt yt-dlp -F aus, um verfügbare Formate zu erhalten."""
        self.update_signal.emit("Sammle verfügbare Formate...")
        
        cmd = [ytdlp_path, "-F"]
        
        # Authentifizierungsdaten hinzufügen, falls vorhanden
        if self.preset_data.get("username"):
            cmd.extend(["-u", self.preset_data["username"]])
        if self.preset_data.get("password"):
            cmd.extend(["-p", self.preset_data["password"]])
        
        # Ignore-Config, falls eingestellt
        if self.config.get("ytdlp_flags", {}).get("ignore_config", False):
            cmd.append("--ignore-config")
        
        cmd.append(self.url)
        
        self.update_signal.emit(f"Ausführen: {' '.join(cmd)}")
        
        # Unterdrücke das CMD-Fenster unter Windows
        creationflags = subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0
        process = subprocess.Popen(
            cmd,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            text=True,
            bufsize=1,
            universal_newlines=True,
            creationflags=creationflags
        )
        
        formats = []
        
        for line in process.stdout:
            if self.abort:
                process.terminate()
                return formats
            line = line.strip()
            formats.append(line)
            self.update_signal.emit(line)
        
        process.wait()
        
        if process.returncode != 0:
            self.update_signal.emit(f"Fehler beim Abrufen der Formate (Exitcode {process.returncode})")
        
        return formats

    def set_format_id(self, format_id):
        """Setzt die vom Benutzer ausgewählte Format-ID."""
        self.format_id = format_id

    def perform_dual_audio_download(self, ytdlp_path):
        """Führt Dual-Audio-Download und Muxing durch."""
        try:
            self.update_signal.emit("Starte Dual-Audio-Download...")
            
            # Temporäres Verzeichnis erstellen/sicherstellen
            temp_dir = get_temp_dir()
            self.update_signal.emit(f"Verwende temporäres Verzeichnis: {temp_dir}")
            
            # 1. Schritt: Format-Liste abrufen
            if not self.format_id:
                formats = self.get_available_formats(ytdlp_path)
                if self.abort:
                    self.finished_signal.emit(False, "Download wurde abgebrochen.")
                    return
                
                # Warte auf Format-ID Eingabe vom Benutzer
                self.format_selection_signal.emit(formats)
                self.format_id_input_signal.emit()
                
                # Warte auf Format-ID (wird über set_format_id gesetzt)
                while self.format_id is None:
                    if self.abort:
                        self.update_signal.emit("Abgebrochen.")
                        self.finished_signal.emit(False, "Download wurde abgebrochen.")
                        return
                    self.msleep(100)
            
            self.update_signal.emit(f"Format-ID ausgewählt: {self.format_id}")
            
            # 2. Schritt: Nummer für Dateinamen bestimmen (aus dem Template)
            nummer = "01"  # Standard, falls keine Serie
            if self.preset_data.get("has_series_template", False):
                # Prüfe, ob aktuelle Werte aus dem MainWindow verfügbar sind
                # Das MainWindow speichert aktuelle Werte in self.parent() (falls verfügbar)
                main_window = None
                try:
                    # Versuche, das MainWindow zu finden (normalerweise über parent-Beziehung)
                    parent = self.parent()
                    if parent and hasattr(parent, 'episode_input') and hasattr(parent, 'season_input'):
                        main_window = parent
                except:
                    self.update_signal.emit("Warnung: Konnte MainWindow nicht finden für aktuelle Serieneinstellungen.")
                
                if main_window:
                    # Verwende die Werte aus dem Hauptfenster
                    self.update_signal.emit("Verwende Serieneinstellungen aus dem Hauptfenster")
                    nummer = main_window.episode_input.text() or self.preset_data.get("episode", "01")
                else:
                    # Fallback auf Preset-Daten
                    nummer = self.preset_data.get("episode", "01")
            
            # 3. Schritt: Untertitel herunterladen
            self.update_signal.emit("Downloade Untertitel...")
            sub_cmd = [ytdlp_path, "--quiet", "--progress"]
            
            # Authentifizierungsdaten hinzufügen
            if self.preset_data.get("username"):
                sub_cmd.extend(["-u", self.preset_data["username"]])
            if self.preset_data.get("password"):
                sub_cmd.extend(["-p", self.preset_data["password"]])
            
            # Config ignorieren, falls eingestellt
            if self.config.get("ytdlp_flags", {}).get("ignore_config", False):
                sub_cmd.append("--ignore-config")
            
            # Untertitel herunterladen in das temporäre Verzeichnis
            sub_output_path = os.path.join(temp_dir, "subs.%(ext)s")
            sub_cmd.extend(["--all-subs", "--skip-download", "-o", sub_output_path])
            sub_cmd.append(self.url)
            
            self.update_signal.emit(f"Ausführen: {' '.join(sub_cmd)}")
            
            # Unterdrücke das CMD-Fenster unter Windows
            creationflags = subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0
            sub_process = subprocess.Popen(
                sub_cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                bufsize=1,
                universal_newlines=True,
                creationflags=creationflags
            )
            
            for line in sub_process.stdout:
                if self.abort:
                    sub_process.terminate()
                    self.finished_signal.emit(False, "Download wurde abgebrochen.")
                    return
                self.update_signal.emit(line.strip())
            
            sub_process.wait()
            
            # Untertiteldateien umbenennen (jetzt im temp-Verzeichnis)
            de_sub_filename = self.preset_data.get("de_sub_filename", f"subs.de.ass")
            de_forced_sub_filename = self.preset_data.get("de_forced_sub_filename", f"subs.de.forced.ass")
            
            de_sub_file = os.path.join(temp_dir, de_sub_filename)
            de_forced_sub_file = os.path.join(temp_dir, de_forced_sub_filename)
            
            # Prüfe, ob Dateien umbenannt werden müssen
            try:
                # Direkte Ausgabe der Dateiliste im Temp-Verzeichnis für Debugging
                self.update_signal.emit("Dateien im Temp-Verzeichnis:")
                for file in os.listdir(temp_dir):
                    self.update_signal.emit(f"  {file}")
                
                temp_de_ssa = os.path.join(temp_dir, "subs.de.ssa")
                temp_vde_ssa = os.path.join(temp_dir, "subs.vde.ssa")
                
                # Suche nach allen .ssa / .ass Dateien, falls die Namen nicht exakt stimmen
                sub_files = [f for f in os.listdir(temp_dir) if f.endswith('.ssa') or f.endswith('.ass')]
                self.update_signal.emit(f"Gefundene Untertiteldateien: {sub_files}")
                
                # Prüfe auf DE Untertitel
                for sub_file in sub_files:
                    if "de.ssa" in sub_file.lower() and not "vde" in sub_file.lower():
                        actual_de_file = os.path.join(temp_dir, sub_file)
                        if not os.path.exists(de_sub_file) or actual_de_file != de_sub_file:
                            # Richtige Datei gefunden, aber unter anderem Namen -> umbenennen
                            self.update_signal.emit(f"DE Untertitel gefunden als: {sub_file}")
                            if os.path.exists(de_sub_file):
                                os.remove(de_sub_file)  # Falls bereits vorhanden, erst löschen
                            os.rename(actual_de_file, de_sub_file)
                            self.update_signal.emit(f"Untertitel umbenannt: {actual_de_file} -> {de_sub_file}")
                            self.temp_files.append(de_sub_file)
                    elif "vde" in sub_file.lower() and ".ssa" in sub_file.lower():
                        actual_vde_file = os.path.join(temp_dir, sub_file)
                        if not os.path.exists(de_forced_sub_file) or actual_vde_file != de_forced_sub_file:
                            # Forced Untertitel gefunden, aber unter anderem Namen
                            self.update_signal.emit(f"VDE Forced Untertitel gefunden als: {sub_file}")
                            if os.path.exists(de_forced_sub_file):
                                os.remove(de_forced_sub_file)  # Falls bereits vorhanden, erst löschen
                            os.rename(actual_vde_file, de_forced_sub_file)
                            self.update_signal.emit(f"Forced Untertitel umbenannt: {actual_vde_file} -> {de_forced_sub_file}")
                            self.temp_files.append(de_forced_sub_file)
            
                # Standardprüfung wie bisher, falls die obigen Prüfungen nichts gefunden haben
                if os.path.exists(temp_de_ssa) and not os.path.exists(de_sub_file):
                    os.rename(temp_de_ssa, de_sub_file)
                    self.update_signal.emit(f"Untertitel umbenannt: {temp_de_ssa} -> {de_sub_file}")
                    self.temp_files.append(de_sub_file)
                
                if os.path.exists(temp_vde_ssa) and not os.path.exists(de_forced_sub_file):
                    os.rename(temp_vde_ssa, de_forced_sub_file)
                    self.update_signal.emit(f"Forced Untertitel umbenannt: {temp_vde_ssa} -> {de_forced_sub_file}")
                    self.temp_files.append(de_forced_sub_file)
            except Exception as e:
                self.update_signal.emit(f"Fehler beim Verarbeiten der Untertitel: {str(e)}")
            
            # 4. Schritt: Japanische Audio herunterladen
            jap_prefix = self.preset_data.get("jap_prefix", "vostde")
            format_suffix = self.preset_data.get("format_suffix", "-1")
            
            # Stelle sicher, dass wir .mp4 als Dateiendung verwenden
            temp_jp_filename_base = self.preset_data.get("temp_jp_filename", f"video_jp.mp4")
            # Ersetze %(ext)s durch mp4 (ohne Punkt), falls es noch im Dateinamen vorkommt
            if "%(ext)s" in temp_jp_filename_base:
                temp_jp_filename_base = temp_jp_filename_base.replace("%(ext)s", "mp4")
            # Stelle sicher, dass die Datei mit .mp4 endet
            if not temp_jp_filename_base.endswith(".mp4"):
                temp_jp_filename_base += ".mp4"
                
            # Kompletter Pfad im temp-Verzeichnis
            temp_jp_filename = os.path.join(temp_dir, temp_jp_filename_base)
            
            self.update_signal.emit("Downloade japanische Audio...")
            jap_cmd = [ytdlp_path, "--quiet", "--progress"]
            
            # Authentifizierungsdaten hinzufügen
            if self.preset_data.get("username"):
                jap_cmd.extend(["-u", self.preset_data["username"]])
            if self.preset_data.get("password"):
                jap_cmd.extend(["-p", self.preset_data["password"]])
            
            # Config ignorieren, falls eingestellt
            if self.config.get("ytdlp_flags", {}).get("ignore_config", False):
                jap_cmd.append("--ignore-config")
            
            # Format-ID für japanische Audio
            jap_format_id = f"{jap_prefix}-{self.format_id}{format_suffix}"
            jap_cmd.extend(["-f", jap_format_id, "-o", temp_jp_filename])
            jap_cmd.append(self.url)
            
            self.update_signal.emit(f"Ausführen: {' '.join(jap_cmd)}")
            
            jap_process = subprocess.Popen(
                jap_cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                bufsize=1,
                universal_newlines=True,
                creationflags=creationflags
            )
            
            for line in jap_process.stdout:
                if self.abort:
                    jap_process.terminate()
                    self.finished_signal.emit(False, "Download wurde abgebrochen.")
                    return
                self.update_signal.emit(line.strip())
            
            jap_process.wait()
            
            # Prüfe, ob die japanische Audiodatei existiert
            jp_file = temp_jp_filename
            if os.path.exists(jp_file):
                self.temp_files.append(jp_file)
            else:
                self.update_signal.emit(f"Warnung: Japanische Audiodatei {jp_file} nicht gefunden.")
                # Fehler zurückgeben, da ohne die Datei kein Muxing möglich ist
                self.finished_signal.emit(False, f"Fehler: Japanische Audiodatei {jp_file} nicht gefunden.")
                return
            
            # 5. Schritt: Deutsche Audio herunterladen
            ger_prefix = self.preset_data.get("ger_prefix", "vde")
            
            # Stelle sicher, dass wir .mp4 als Dateiendung verwenden
            temp_de_filename_base = self.preset_data.get("temp_de_filename", f"video_de.mp4")
            # Ersetze %(ext)s durch mp4 (ohne Punkt), falls es noch im Dateinamen vorkommt
            if "%(ext)s" in temp_de_filename_base:
                temp_de_filename_base = temp_de_filename_base.replace("%(ext)s", "mp4")
            # Stelle sicher, dass die Datei mit .mp4 endet
            if not temp_de_filename_base.endswith(".mp4"):
                temp_de_filename_base += ".mp4"
                
            # Kompletter Pfad im temp-Verzeichnis
            temp_de_filename = os.path.join(temp_dir, temp_de_filename_base)
            
            self.update_signal.emit("Downloade deutsche Audio...")
            ger_cmd = [ytdlp_path, "--quiet", "--progress"]
            
            # Authentifizierungsdaten hinzufügen
            if self.preset_data.get("username"):
                ger_cmd.extend(["-u", self.preset_data["username"]])
            if self.preset_data.get("password"):
                ger_cmd.extend(["-p", self.preset_data["password"]])
            
            # Config ignorieren, falls eingestellt
            if self.config.get("ytdlp_flags", {}).get("ignore_config", False):
                ger_cmd.append("--ignore-config")
            
            # Format-ID für deutsche Audio
            ger_format_id = f"{ger_prefix}-{self.format_id}{format_suffix}"
            ger_cmd.extend(["-f", ger_format_id, "-o", temp_de_filename])
            ger_cmd.append(self.url)
            
            self.update_signal.emit(f"Ausführen: {' '.join(ger_cmd)}")
            
            ger_process = subprocess.Popen(
                ger_cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                bufsize=1,
                universal_newlines=True,
                creationflags=creationflags
            )
            
            for line in ger_process.stdout:
                if self.abort:
                    ger_process.terminate()
                    self.finished_signal.emit(False, "Download wurde abgebrochen.")
                    return
                self.update_signal.emit(line.strip())
            
            ger_process.wait()
            
            # Prüfe, ob die deutsche Audiodatei existiert
            de_file = temp_de_filename
            if os.path.exists(de_file):
                self.temp_files.append(de_file)
            else:
                self.update_signal.emit(f"Warnung: Deutsche Audiodatei {de_file} nicht gefunden.")
                # Fehler zurückgeben, da ohne die Datei kein Muxing möglich ist
                self.finished_signal.emit(False, f"Fehler: Deutsche Audiodatei {de_file} nicht gefunden.")
                return
            
            # 6. Schritt: MKVMerge ausführen
            self.update_signal.emit("Starte MKV-Muxing...")
            
            # Bestimme MKVMerge-Pfad
            mkvmerge_path = self.config.get("mkvmerge_path", "C:\\Program Files\\MKVToolNix\\mkvmerge.exe")
            if not os.path.exists(mkvmerge_path):
                self.update_signal.emit(f"Fehler: MKVMerge nicht gefunden unter {mkvmerge_path}")
                self.finished_signal.emit(False, f"MKVMerge nicht gefunden. Bitte überprüfe den Pfad in den Optionen.")
                return
            
            # Ausgabedateiname bestimmen
            output_name = ""
            if self.preset_data.get("has_series_template", False):
                series = self.preset_data.get("series", "")
                output_name = f"{series} {nummer}.mkv"
            else:
                # Verwende die Standardausgabe des Programms
                output_name = self.output_filename
                if output_name is None or "%(ext)s" in output_name:
                    # Fallback-Name oder %(ext)s im Namen ersetzen
                    output_name = f"output_{nummer}.mkv"
                elif not output_name.endswith(".mkv"):
                    output_name = f"{output_name}.mkv"
            
            # Ausgabepfad bestimmen (NICHT im temp-Verzeichnis!)
            output_path = os.path.join(self.output_dir, output_name) if self.output_dir else output_name
            
            # MKVMerge-Befehl zusammenstellen
            mkvmerge_cmd = [
                mkvmerge_path,
                "--ui-language", "de",
                "--priority", "lower",
                "--output", output_path,
                "--language", "0:und",
                "--default-track-flag", "0:no",
                "--language", "1:ja",
                "--default-track-flag", "1:no",
                "(", jp_file, ")",
                "--no-video",
                "--no-global-tags",
                "--language", "1:de",
                "(", de_file, ")"
            ]
            
            # Untertitel hinzufügen, falls vorhanden
            if os.path.exists(de_sub_file):
                mkvmerge_cmd.extend(["--language", "0:de", "(", de_sub_file, ")"])
                self.update_signal.emit(f"Untertitel gefunden: {de_sub_file}")
            else:
                self.update_signal.emit(f"Warnung: Untertitel nicht gefunden: {de_sub_file}")
            
            if os.path.exists(de_forced_sub_file):
                mkvmerge_cmd.extend(["--language", "0:de", "--forced-display-flag", "0:yes", "(", de_forced_sub_file, ")"])
                self.update_signal.emit(f"Forced Untertitel gefunden: {de_forced_sub_file}")
            else:
                self.update_signal.emit(f"Warnung: Forced Untertitel nicht gefunden: {de_forced_sub_file}")
            
            # Track-Order (basierend auf den hinzugefügten Tracks)
            track_order = "0:0,1:1,0:1"
            if os.path.exists(de_sub_file):
                track_order += ",2:0"
            if os.path.exists(de_forced_sub_file):
                track_order += f",{3 if os.path.exists(de_sub_file) else 2}:0"
            
            mkvmerge_cmd.extend(["--track-order", track_order])
            
            # MKVMerge ausführen
            self.update_signal.emit(f"Ausführen MKVMerge: {' '.join(mkvmerge_cmd)}")
            
            mkvmerge_process = subprocess.Popen(
                mkvmerge_cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                bufsize=1,
                universal_newlines=True,
                creationflags=creationflags
            )
            
            for line in mkvmerge_process.stdout:
                if self.abort:
                    mkvmerge_process.terminate()
                    self.finished_signal.emit(False, "Muxing abgebrochen.")
                    return
                self.update_signal.emit(line.strip())
            
            mkvmerge_process.wait()
            
            if mkvmerge_process.returncode != 0 and mkvmerge_process.returncode != 1:  # 1 ist Warnung, aber OK
                self.update_signal.emit(f"MKVMerge fehlgeschlagen mit Exitcode {mkvmerge_process.returncode}")
                self.finished_signal.emit(False, f"MKVMerge fehlgeschlagen mit Exitcode {mkvmerge_process.returncode}")
                return
            
            # 7. Schritt: Aufräumen, falls gewünscht
            if self.preset_data.get("cleanup_temp", True):
                self.update_signal.emit("Aufräumen: Temporäre Dateien werden gelöscht...")
                for file in self.temp_files:
                    try:
                        if os.path.exists(file):
                            os.remove(file)
                            self.update_signal.emit(f"Gelöscht: {file}")
                    except Exception as e:
                        self.update_signal.emit(f"Fehler beim Löschen von {file}: {str(e)}")
            
            self.update_signal.emit(f"Dual-Audio-Download und Muxing erfolgreich abgeschlossen! Ausgabedatei: {output_path}")
            self.finished_signal.emit(True, f"Dual-Audio-Download und Muxing erfolgreich abgeschlossen!")
            
        except Exception as e:
            self.update_signal.emit(f"Fehler beim Dual-Audio-Download: {str(e)}")
            self.finished_signal.emit(False, f"Fehler beim Dual-Audio-Download: {str(e)}")

class PresetDialog(QDialog):
    def __init__(self, parent=None, preset_data=None):
        super().__init__(parent)
        self.setWindowTitle("Preset erstellen/bearbeiten")
        self.resize(500, 450)
        
        # Entferne den Hilfe-Button (Fragezeichen) in der Titelleiste
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
        
        # Icon für den Dialog setzen
        icon_path = os.path.join(get_base_path(), "icon.ico")
        if os.path.exists(icon_path):
            self.setWindowIcon(QIcon(icon_path))
        
        self.preset_data = preset_data or {
            "name": "", 
            "description": "", 
            "args": "",
            "has_series_template": False,
            "series_template": SERIES_TEMPLATE,
            "series": "",
            "season": "1",
            "episode": "1",
            "extension": ".mkv",
            "custom_path": "",
            "is_audio": False,
            "custom_output_template": False,
            "output_template": "%(title)s.%(ext)s"
        }
        
        self.setup_ui()
        
    def setup_ui(self):
        layout = QVBoxLayout()
        
        # Tabs hinzufügen
        tabs = QTabWidget()
        
        # Tab 1: Grundeinstellungen
        basic_tab = QWidget()
        form_layout = QFormLayout()
        
        self.name_edit = QLineEdit(self.preset_data["name"])
        self.description_edit = QLineEdit(self.preset_data["description"])
        self.args_edit = QTextEdit(self.preset_data["args"])
        
        form_layout.addRow("Name:", self.name_edit)
        form_layout.addRow("Beschreibung:", self.description_edit)
        form_layout.addRow("yt-dlp Argumente:", self.args_edit)
        
        # Eigener Pfad (immer sichtbar, mit Durchsuchen) - hierher verschoben
        self.custom_path_edit = QLineEdit(self.preset_data.get("custom_path", ""))
        self.custom_path_edit.setPlaceholderText("Optional: Eigener Zielordner für Download")
        custom_path_hbox = QHBoxLayout()
        custom_path_hbox.addWidget(self.custom_path_edit)
        self.custom_path_browse_btn = QPushButton("Durchsuchen...")
        self.custom_path_browse_btn.clicked.connect(self.browse_custom_path)
        custom_path_hbox.addWidget(self.custom_path_browse_btn)
        form_layout.addRow("Eigener Pfad:", custom_path_hbox)
        
        # Audio-Preset Checkbox
        self.is_audio_cb = QCheckBox("Ist Audio-Preset (kein Remux nach MKV)")
        self.is_audio_cb.setChecked(self.preset_data.get("is_audio", False))
        form_layout.addRow(self.is_audio_cb)
        
        # Referer
        self.referer_edit = QLineEdit(self.preset_data.get("referer", ""))
        self.referer_edit.setPlaceholderText("Optional: Referer-Link für --referer=")
        form_layout.addRow("Referer:", self.referer_edit)
        
        # HLS-ffmpeg Checkbox
        self.hls_ffmpeg_cb = QCheckBox("HLS-Streams mit ffmpeg herunterladen (--downloader ffmpeg --hls-use-mpegts)")
        self.hls_ffmpeg_cb.setChecked(self.preset_data.get("hls_ffmpeg", False))
        form_layout.addRow(self.hls_ffmpeg_cb)
        
        basic_tab.setLayout(form_layout)
        tabs.addTab(basic_tab, "Grundeinstellungen")
        
        # Tab 2: Untertitel
        subtitle_tab = QWidget()
        subtitle_layout = QFormLayout()
        
        # Untertitel-Optionen
        self.sublang_edit = QLineEdit(self.preset_data.get("sublang", ""))
        self.sublang_edit.setPlaceholderText("z.B. de, en, de,en ...")
        subtitle_layout.addRow("Untertitelsprache:", self.sublang_edit)
        
        self.embed_subs_cb = QCheckBox("Untertitel einbetten (--embed-subs)")
        self.embed_subs_cb.setChecked(self.preset_data.get("embed_subs", False))
        subtitle_layout.addRow(self.embed_subs_cb)
        
        # Untertitel-Format Dropdown
        self.subformat_combo = QComboBox()
        self.subformat_combo.addItem("(keine Konvertierung)", "")
        self.subformat_combo.addItem("srt", "srt")
        self.subformat_combo.addItem("ass", "ass")
        self.subformat_combo.addItem("tx3g", "tx3g")
        # Vorbelegen
        subformat = self.preset_data.get("subformat", "")
        idx = self.subformat_combo.findData(subformat)
        if idx >= 0:
            self.subformat_combo.setCurrentIndex(idx)
        subtitle_layout.addRow("Untertitel-Format:", self.subformat_combo)
        
        subtitle_tab.setLayout(subtitle_layout)
        tabs.addTab(subtitle_tab, "Untertitel")
        
        # Tab 3: Output Template
        output_tab = QWidget()
        output_layout = QFormLayout()
        
        # Custom Output Template Checkbox
        self.custom_output_template_cb = QCheckBox("Eigenen Namen verwenden")
        self.custom_output_template_cb.setChecked(self.preset_data.get("custom_output_template", False))
        output_layout.addRow(self.custom_output_template_cb)
        
        # Output Template Field
        self.output_template_edit = QLineEdit(self.preset_data.get("output_template", "%(title)s.%(ext)s"))
        self.output_template_edit.setPlaceholderText("z.B. %(title)s.%(ext)s, %(uploader)s/%(title)s.%(ext)s")
        output_layout.addRow("Name:", self.output_template_edit)
        
        # Add examples and documentation link
        examples_label = QLabel("<b>Beispiele:</b><br>" + 
                               "%(title)s-%(id)s.%(ext)s<br>" + 
                               "%(uploader)s/%(title)s.%(ext)s<br>" + 
                               "%(playlist)s/%(playlist_index)s-%(title)s.%(ext)s<br><br>" + 
                               "<a href='https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#output-template'>Mehr Beispiele in der yt-dlp Dokumentation</a>")
        examples_label.setOpenExternalLinks(True)
        examples_label.setTextFormat(Qt.RichText)
        output_layout.addRow(examples_label)
        
        output_tab.setLayout(output_layout)
        tabs.addTab(output_tab, "Name")
        
        # Tab 4: Authentifizierung
        auth_tab = QWidget()
        auth_layout = QFormLayout()
        
        # Login-Felder
        self.username_edit = QLineEdit(self.preset_data.get("username", ""))
        self.username_edit.setPlaceholderText("Optional: Benutzername für Login (-u)")
        auth_layout.addRow("Benutzername:", self.username_edit)
        
        pw_hbox = QHBoxLayout()
        self.password_edit = QLineEdit(self.preset_data.get("password", ""))
        self.password_edit.setEchoMode(QLineEdit.Password)
        self.password_edit.setPlaceholderText("Optional: Passwort für Login (-p)")
        self.show_pw_cb = QCheckBox("Passwort anzeigen")
        self.show_pw_cb.toggled.connect(self.toggle_password_visible)
        pw_hbox.addWidget(self.password_edit)
        pw_hbox.addWidget(self.show_pw_cb)
        auth_layout.addRow("Passwort:", pw_hbox)
        
        pw_hint = QLabel("Hinweis: Passwörter werden im Klartext lokal gespeichert!")
        pw_hint.setStyleSheet("color: red;")
        auth_layout.addRow(pw_hint)
        
        auth_tab.setLayout(auth_layout)
        tabs.addTab(auth_tab, "Authentifizierung")
        
        # Tab 4: Pfade & Serien
        path_tab = QWidget()
        path_layout = QFormLayout()
        
        # Serien-Template
        self.series_box = QGroupBox("Serien-Template aktivieren")
        self.series_box.setCheckable(True)
        self.series_box.setChecked(self.preset_data.get("has_series_template", False))
        
        series_layout = QFormLayout()
        
        self.template_edit = QLineEdit(self.preset_data.get("series_template", SERIES_TEMPLATE))
        self.series_edit = QLineEdit(self.preset_data.get("series", ""))
        self.season_edit = QLineEdit(self.preset_data.get("season", "1"))
        self.episode_edit = QLineEdit(self.preset_data.get("episode", "1"))
        
        series_layout.addRow("Template:", self.template_edit)
        series_layout.addRow("Serie:", self.series_edit)
        series_layout.addRow("Staffel:", self.season_edit)
        series_layout.addRow("Folge:", self.episode_edit)
        
        help_text = QLabel("Verwende {series}, {season}, {episode}, %(ext)s und {path} im Template.")
        series_layout.addRow(help_text)
        
        self.series_box.setLayout(series_layout)
        path_layout.addWidget(self.series_box)
        
        path_tab.setLayout(path_layout)
        tabs.addTab(path_tab, "Serien")
        
        # Tab 5: ADN (vorher Experte)
        adn_tab = QWidget()
        adn_layout = QVBoxLayout()
        
        # F-Option und Format-ID Checkbox
        self.use_format_selection_cb = QCheckBox("Format-Auswahl aktivieren")
        self.use_format_selection_cb.setChecked(self.preset_data.get("use_format_selection", False))
        self.use_format_selection_cb.toggled.connect(self.toggle_format_selection)
        adn_layout.addWidget(self.use_format_selection_cb)
        
        # Beschreibung der Format-Auswahl
        format_desc = QLabel(
            "Diese Option führt beim Start des Downloads zuerst 'yt-dlp -F' aus,\n"
            "um verfügbare Formate anzuzeigen. Anschließend wird nach einer Format-ID gefragt."
        )
        adn_layout.addWidget(format_desc)
        
        # Dual-Audio (Jap+Ger) und Untertitel Muxing aktivieren
        self.use_dual_audio_cb = QCheckBox("Dual-Audio Muxing aktivieren")
        self.use_dual_audio_cb.setChecked(self.preset_data.get("use_dual_audio", False))
        self.use_dual_audio_cb.toggled.connect(self.toggle_dual_audio)
        adn_layout.addWidget(self.use_dual_audio_cb)
        
        # Dual-Audio Gruppe
        self.dual_audio_group = QGroupBox("Dual-Audio Einstellungen")
        self.dual_audio_group.setEnabled(self.preset_data.get("use_dual_audio", False))
        
        dual_form = QFormLayout()
        
        # Format ID Präfixe
        self.jap_prefix_edit = QLineEdit(self.preset_data.get("jap_prefix", "vostde"))
        dual_form.addRow("Prefix für japanische Audio:", self.jap_prefix_edit)
        
        self.ger_prefix_edit = QLineEdit(self.preset_data.get("ger_prefix", "vde"))
        dual_form.addRow("Prefix für deutsche Audio:", self.ger_prefix_edit)
        
        # Suffix (Standard: -1)
        self.format_suffix_edit = QLineEdit(self.preset_data.get("format_suffix", "-1"))
        dual_form.addRow("Format-Suffix:", self.format_suffix_edit)
        
        # Dateinamen für temporäre Dateien
        self.temp_jp_filename_edit = QLineEdit(self.preset_data.get("temp_jp_filename", "video_jp.mp4"))
        dual_form.addRow("Temp. Dateiname JP:", self.temp_jp_filename_edit)
        
        self.temp_de_filename_edit = QLineEdit(self.preset_data.get("temp_de_filename", "video_de.mp4"))
        dual_form.addRow("Temp. Dateiname DE:", self.temp_de_filename_edit)
        
        # Untertitel-Dateien
        self.de_sub_filename_edit = QLineEdit(self.preset_data.get("de_sub_filename", "subs.de.ass"))
        dual_form.addRow("DE Untertitel-Datei:", self.de_sub_filename_edit)
        
        self.de_forced_sub_filename_edit = QLineEdit(self.preset_data.get("de_forced_sub_filename", "subs.de.forced.ass"))
        dual_form.addRow("DE forced Untertitel:", self.de_forced_sub_filename_edit)
        
        # Cleanup-Option
        self.cleanup_temp_cb = QCheckBox("Temporäre Dateien nach dem Muxing löschen")
        self.cleanup_temp_cb.setChecked(self.preset_data.get("cleanup_temp", True))
        dual_form.addRow(self.cleanup_temp_cb)
        
        self.dual_audio_group.setLayout(dual_form)
        adn_layout.addWidget(self.dual_audio_group)
        
        adn_tab.setLayout(adn_layout)
        
        # ADN Tab nur hinzufügen, wenn aktiviert
        self.adn_tab_index = None
        if self.parent() and self.parent().config.get("enable_adn_tab", False):
            self.adn_tab_index = tabs.addTab(adn_tab, "ADN")
        
        layout.addWidget(tabs)
        
        buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        buttons.accepted.connect(self.accept)
        buttons.rejected.connect(self.reject)
        
        layout.addWidget(buttons)
        self.setLayout(layout)
        
    def toggle_password_visible(self, checked):
        self.password_edit.setEchoMode(QLineEdit.Normal if checked else QLineEdit.Password)

    def toggle_format_selection(self, checked):
        # Diese Methode setzt Flags, wenn die Format-Auswahl aktiviert wird
        pass

    def toggle_dual_audio(self, checked):
        # Aktiviere/Deaktiviere die Dual-Audio-Einstellungen
        self.dual_audio_group.setEnabled(checked)

    def get_preset_data(self):
        return {
            "name": self.name_edit.text(),
            "description": self.description_edit.text(),
            "args": self.args_edit.toPlainText(),
            "has_series_template": self.series_box.isChecked(),
            "series_template": self.template_edit.text(),
            "series": self.series_edit.text(),
            "season": self.season_edit.text(),
            "episode": self.episode_edit.text(),
            "custom_path": self.custom_path_edit.text(),
            "is_audio": self.is_audio_cb.isChecked(),
            "username": self.username_edit.text(),
            "password": self.password_edit.text(),
            "referer": self.referer_edit.text(),
            "hls_ffmpeg": self.hls_ffmpeg_cb.isChecked(),
            "sublang": self.sublang_edit.text(),
            "embed_subs": self.embed_subs_cb.isChecked(),
            "subformat": self.subformat_combo.currentData(),
            "use_format_selection": self.use_format_selection_cb.isChecked(),
            "use_dual_audio": self.use_dual_audio_cb.isChecked(),
            "jap_prefix": self.jap_prefix_edit.text(),
            "ger_prefix": self.ger_prefix_edit.text(),
            "format_suffix": self.format_suffix_edit.text(),
            "temp_jp_filename": self.temp_jp_filename_edit.text(),
            "temp_de_filename": self.temp_de_filename_edit.text(),
            "de_sub_filename": self.de_sub_filename_edit.text(),
            "de_forced_sub_filename": self.de_forced_sub_filename_edit.text(),
            "cleanup_temp": self.cleanup_temp_cb.isChecked(),
            "custom_output_template": self.custom_output_template_cb.isChecked(),
            "output_template": self.output_template_edit.text()
        }

    def browse_custom_path(self):
        directory = QFileDialog.getExistingDirectory(self, "Eigenen Zielordner auswählen", self.custom_path_edit.text() or "")
        if directory:
            self.custom_path_edit.setText(directory)


class YtDlpDownloadThread(QThread):
    finished_signal = pyqtSignal(bool, str)
    def run(self):
        try:
            url = "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe"
            bin_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "bin")
            os.makedirs(bin_dir, exist_ok=True)
            dest_path = os.path.join(bin_dir, "yt-dlp.exe")
            urllib.request.urlretrieve(url, dest_path)
            self.finished_signal.emit(True, f"yt-dlp.exe wurde erfolgreich nach {dest_path} heruntergeladen.")
        except Exception as e:
            self.finished_signal.emit(False, f"Fehler beim Herunterladen von yt-dlp.exe: {str(e)}")


class OptionenDialog(QDialog):
    def __init__(self, output_dir, use_local_ytdlp, parent=None, ytdlp_flags=None):
        super().__init__(parent)
        self.setWindowTitle("Optionen")
        self.resize(420, 300)
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
        
        # Icon für den Dialog setzen
        icon_path = os.path.join(get_base_path(), "icon.ico")
        if os.path.exists(icon_path):
            self.setWindowIcon(QIcon(icon_path))
            
        self.selected_output_dir = output_dir
        self.selected_use_local_ytdlp = use_local_ytdlp
        self.selected_flags = ytdlp_flags or {
            "ignore_config": False,
            "remux_mkv": False,
            "embed_metadata": False,
            "use_ffmpeg_location": False
        }
        self.setup_ui()

    def setup_ui(self):
        main_layout = QVBoxLayout()
        tabs = QTabWidget()

        # Tab 1: Allgemein
        tab_allgemein = QWidget()
        layout = QFormLayout()
        self.output_dir_input = QLineEdit(self.selected_output_dir)
        browse_btn = QPushButton("Durchsuchen...")
        browse_btn.clicked.connect(self.browse_output_dir)
        hbox = QHBoxLayout()
        hbox.addWidget(self.output_dir_input)
        hbox.addWidget(browse_btn)
        layout.addRow("Standardpfad:", hbox)

        # MKVMerge-Pfad hinzufügen
        self.mkvmerge_path_input = QLineEdit(self.parent().config.get("mkvmerge_path", "C:\\Program Files\\MKVToolNix\\mkvmerge.exe") if self.parent() else "C:\\Program Files\\MKVToolNix\\mkvmerge.exe")
        mkvmerge_browse_btn = QPushButton("Durchsuchen...")
        mkvmerge_browse_btn.clicked.connect(self.browse_mkvmerge_path)
        mkvmerge_hbox = QHBoxLayout()
        mkvmerge_hbox.addWidget(self.mkvmerge_path_input)
        mkvmerge_hbox.addWidget(mkvmerge_browse_btn)
        layout.addRow("MKVMerge-Pfad:", mkvmerge_hbox)
        
        # FFmpeg-Pfad hinzufügen
        self.ffmpeg_path_input = QLineEdit(self.parent().config.get("ffmpeg_path", "C:\\ffmpeg\\bin\\ffmpeg.exe") if self.parent() else "C:\\ffmpeg\\bin\\ffmpeg.exe")
        ffmpeg_browse_btn = QPushButton("Durchsuchen...")
        ffmpeg_browse_btn.clicked.connect(self.browse_ffmpeg_path)
        ffmpeg_hbox = QHBoxLayout()
        ffmpeg_hbox.addWidget(self.ffmpeg_path_input)
        ffmpeg_hbox.addWidget(ffmpeg_browse_btn)
        layout.addRow("FFmpeg-Pfad:", ffmpeg_hbox)

        self.ytdlp_source_combo = QComboBox()
        self.ytdlp_source_combo.addItems(["Lokal (bin/yt-dlp.exe)", "System (PATH)"])
        self.ytdlp_source_combo.setCurrentIndex(0 if self.selected_use_local_ytdlp else 1)
        layout.addRow("yt-dlp-Quelle:", self.ytdlp_source_combo)
        
        # Default-Presets ausblenden
        self.hide_defaults_cb = QCheckBox("Default-Presets ausblenden")
        self.hide_defaults_cb.setChecked(self.parent().config.get("hide_default_presets", False) if self.parent() else False)
        layout.addRow(self.hide_defaults_cb)
        
        # ADN Tab aktivieren
        self.enable_adn_tab_cb = QCheckBox("ADN Tab aktivieren")
        self.enable_adn_tab_cb.setChecked(self.parent().config.get("enable_adn_tab", False) if self.parent() else False)
        layout.addRow(self.enable_adn_tab_cb)
        
        bin_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "bin")
        ytdlp_path = os.path.join(bin_dir, "yt-dlp.exe")
        # Button 1: Herunterladen
        self.download_btn = QPushButton("yt-dlp.exe herunterladen")
        self.download_btn.clicked.connect(self.download_ytdlp)
        self.download_btn.setEnabled(not os.path.exists(ytdlp_path))
        layout.addRow(self.download_btn)
        # Button 2: Updaten
        self.update_btn = QPushButton("yt-dlp.exe updaten")
        self.update_btn.clicked.connect(self.update_ytdlp)
        layout.addRow(self.update_btn)
        tab_allgemein.setLayout(layout)
        tabs.addTab(tab_allgemein, "Allgemein")

        # Tab 2: yt-dlp-Flags
        tab_flags = QWidget()
        flags_layout = QVBoxLayout()
        self.cb_ignore_config = QCheckBox("--ignore-config")
        self.cb_ignore_config.setChecked(self.selected_flags.get("ignore_config", False))
        flags_layout.addWidget(self.cb_ignore_config)
        flags_layout.addWidget(QLabel("Ignoriert die systemweite yt-dlp-Konfiguration."))
        self.cb_remux_mkv = QCheckBox("--remux-video mkv")
        self.cb_remux_mkv.setChecked(self.selected_flags.get("remux_mkv", False))
        flags_layout.addWidget(self.cb_remux_mkv)
        flags_layout.addWidget(QLabel("Remuxt das Video ins MKV-Format."))
        self.cb_embed_metadata = QCheckBox("--embed-metadata")
        self.cb_embed_metadata.setChecked(self.selected_flags.get("embed_metadata", False))
        flags_layout.addWidget(self.cb_embed_metadata)
        flags_layout.addWidget(QLabel("Betten Metadaten in die Ausgabedatei ein."))
        self.cb_use_ffmpeg_location = QCheckBox("--ffmpeg-location")
        self.cb_use_ffmpeg_location.setChecked(self.selected_flags.get("use_ffmpeg_location", False))
        flags_layout.addWidget(self.cb_use_ffmpeg_location)
        flags_layout.addWidget(QLabel("Nutzt den konfigurierten FFmpeg-Pfad für yt-dlp."))
        tab_flags.setLayout(flags_layout)
        tabs.addTab(tab_flags, "yt-dlp-Flags")

        # Tab 3: Info
        tab_info = QWidget()
        info_layout = QVBoxLayout()
        info_text = (
            "<b>Version:</b> 1.4<br>"
            "<b>&copy; 2025 Akamaru</b><br>"
            "<b>Sourcecode:</b> <a href='https://git.ponywave.de/Akamaru/video-download-helper'>https://git.ponywave.de/Akamaru/video-download-helper</a><br>"
            "<b>Erstellt mit Hilfe von Claude, GPT &amp; Gemini</b>"
        )
        info_label = QLabel(info_text)
        info_label.setOpenExternalLinks(True)
        info_label.setTextFormat(Qt.RichText)
        info_layout.addWidget(info_label)
        info_layout.addStretch(1)
        tab_info.setLayout(info_layout)
        tabs.addTab(tab_info, "Info")

        main_layout.addWidget(tabs)
        # Stelle sicher, dass QDialogButtonBox nur einmal erstellt und verbunden wird
        if hasattr(self, 'button_box'):
            try:
                main_layout.removeWidget(self.button_box)
                self.button_box.deleteLater()
            except Exception:
                pass
        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        main_layout.addWidget(self.button_box)
        self.setLayout(main_layout)

    def browse_output_dir(self):
        directory = QFileDialog.getExistingDirectory(self, "Standardpfad auswählen", self.output_dir_input.text() or os.path.expanduser("~"))
        if directory:
            self.output_dir_input.setText(directory)

    def browse_mkvmerge_path(self):
        file_path, _ = QFileDialog.getOpenFileName(self, "MKVMerge-Executable auswählen", 
                                                self.mkvmerge_path_input.text() or "C:\\Program Files\\MKVToolNix",
                                                "Executable (*.exe)")
        if file_path:
            self.mkvmerge_path_input.setText(file_path)

    def browse_ffmpeg_path(self):
        file_path, _ = QFileDialog.getOpenFileName(self, "FFmpeg-Executable auswählen", 
                                                self.ffmpeg_path_input.text() or "C:\\ffmpeg\\bin",
                                                "Executable (*.exe)")
        if file_path:
            self.ffmpeg_path_input.setText(file_path)

    def get_values(self):
        return (
            self.output_dir_input.text(),
            self.ytdlp_source_combo.currentIndex() == 0,
            {
                "ignore_config": self.cb_ignore_config.isChecked(),
                "remux_mkv": self.cb_remux_mkv.isChecked(),
                "embed_metadata": self.cb_embed_metadata.isChecked(),
                "use_ffmpeg_location": self.cb_use_ffmpeg_location.isChecked()
            },
            self.hide_defaults_cb.isChecked(),
            self.mkvmerge_path_input.text(),
            self.enable_adn_tab_cb.isChecked(),
            self.ffmpeg_path_input.text()
        )

    def download_ytdlp(self):
        self.download_btn.setEnabled(False)
        self.download_btn.setText("Lade herunter...")
        self.thread = YtDlpDownloadThread()
        self.thread.finished_signal.connect(self.download_finished)
        self.thread.start()

    def download_finished(self, success, message):
        bin_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "bin")
        ytdlp_path = os.path.join(bin_dir, "yt-dlp.exe")
        self.download_btn.setEnabled(not os.path.exists(ytdlp_path))
        self.download_btn.setText("yt-dlp.exe herunterladen")
        if success:
            QMessageBox.information(self, "Erfolg", message)
        else:
            QMessageBox.critical(self, "Fehler", message)

    def update_ytdlp(self):
        self.update_btn.setEnabled(False)
        self.update_btn.setText("Aktualisiere...")
        # Bestimme, ob lokal oder systemweit
        use_local = self.ytdlp_source_combo.currentIndex() == 0
        if use_local:
            bin_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "bin")
            ytdlp_path = os.path.join(bin_dir, "yt-dlp.exe")
            cmd = [ytdlp_path, "-U"]
        else:
            cmd = ["yt-dlp", "-U"]
        try:
            creationflags = subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0
            result = subprocess.run(cmd, capture_output=True, text=True, creationflags=creationflags)
            if result.returncode == 0:
                QMessageBox.information(self, "Erfolg", f"yt-dlp wurde aktualisiert.\n\n{result.stdout}")
            else:
                QMessageBox.warning(self, "Fehler", f"Fehler beim Updaten von yt-dlp:\n{result.stderr or result.stdout}")
        except Exception as e:
            QMessageBox.critical(self, "Fehler", f"Fehler beim Ausführen von yt-dlp -U: {str(e)}")
        self.update_btn.setEnabled(True)
        self.update_btn.setText("yt-dlp.exe updaten")


class QueueItem:
    """Repräsentiert einen Eintrag in der Download-Queue."""
    def __init__(self, url, preset_data, output_dir=None, output_filename=None, 
                 series_info=None, use_local_ytdlp=True, extra_args=None):
        self.id = str(uuid.uuid4())  # Eindeutige ID für diesen Queue-Eintrag
        self.url = url
        self.preset_data = preset_data.copy() if preset_data else {}
        self.output_dir = output_dir
        self.output_filename = output_filename
        self.series_info = series_info.copy() if series_info else {}
        self.use_local_ytdlp = use_local_ytdlp
        self.extra_args = extra_args or ""
        self.status = "Wartend"
        
    def get_display_name(self):
        """Gibt einen lesbaren Namen für die Queue-Anzeige zurück."""
        preset_name = self.preset_data.get("name", "Unbekannt")
        if self.series_info:
            series = self.series_info.get("series", "")
            season = self.series_info.get("season", "")
            episode = self.series_info.get("episode", "")
            if series and season and episode:
                return f"{self.url} - {series} S{season}E{episode} ({preset_name})"
        return f"{self.url} ({preset_name})"
        
    def to_dict(self):
        """Konvertiert das QueueItem in ein JSON-serialisierbares Dictionary."""
        return {
            "id": self.id,
            "url": self.url,
            "preset_data": self.preset_data,
            "output_dir": self.output_dir,
            "output_filename": self.output_filename,
            "series_info": self.series_info,
            "use_local_ytdlp": self.use_local_ytdlp,
            "extra_args": self.extra_args,
            "status": self.status
        }
    
    @classmethod
    def from_dict(cls, data):
        """Erstellt ein QueueItem aus einem Dictionary."""
        item = cls(
            url=data["url"],
            preset_data=data["preset_data"],
            output_dir=data["output_dir"],
            output_filename=data["output_filename"],
            series_info=data["series_info"],
            use_local_ytdlp=data["use_local_ytdlp"],
            extra_args=data["extra_args"]
        )
        item.id = data["id"]
        item.status = data["status"]
        return item


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Video Download Helper")
        self.resize(800, 600)
        
        # Icon für das Hauptfenster setzen
        icon_path = os.path.join(get_base_path(), "icon.ico")
        if os.path.exists(icon_path):
            self.setWindowIcon(QIcon(icon_path))
        
        # Stelle sicher, dass Verzeichnisse existieren
        self.ensure_directories()
        
        # Default-Presets automatisch anlegen, falls keine vorhanden
        if not any(f.endswith('.json') for f in os.listdir(PRESETS_DIR)):
            self.create_default_presets()
        
        self.config = self.load_config()
        self.presets = self.load_presets()
        
        self.download_thread = None
        self.download_queue = []  # Liste für die Download-Queue
        self.current_queue_item = None  # Aktuell laufender Download aus der Queue
        
        # UI initialisieren
        self.setup_ui()
        
        # Queue aus gespeicherter Datei laden (nach UI-Setup)
        self.load_queue()
        
    def ensure_directories(self):
        """Stellt sicher, dass alle benötigten Verzeichnisse existieren."""
        # Stelle sicher, dass der Presets-Ordner existiert
        os.makedirs(PRESETS_DIR, exist_ok=True)
        
        # Stelle sicher, dass der bin-Ordner existiert
        bin_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "bin")
        os.makedirs(bin_dir, exist_ok=True)
        
    def setup_ui(self):
        central_widget = QWidget()
        main_layout = QVBoxLayout()
        
        # URL Input
        url_layout = QHBoxLayout()
        url_layout.addWidget(QLabel("Video-URL:"))
        self.url_input = QLineEdit()
        url_layout.addWidget(self.url_input)
        main_layout.addLayout(url_layout)
        
        # Preset Selection
        preset_layout = QHBoxLayout()
        preset_layout.addWidget(QLabel("Preset:"))
        self.preset_combo = QComboBox()
        self.update_preset_combo()
        preset_layout.addWidget(self.preset_combo)
        
        # Preset Buttons
        self.add_preset_btn = QPushButton("Neu")
        self.add_preset_btn.clicked.connect(self.add_preset)
        preset_layout.addWidget(self.add_preset_btn)
        
        self.edit_preset_btn = QPushButton("Bearbeiten")
        self.edit_preset_btn.clicked.connect(self.edit_preset)
        preset_layout.addWidget(self.edit_preset_btn)
        
        self.delete_preset_btn = QPushButton("Löschen")
        self.delete_preset_btn.clicked.connect(self.delete_preset)
        preset_layout.addWidget(self.delete_preset_btn)
        
        main_layout.addLayout(preset_layout)
        
        # Serien-Einstellungen
        self.series_group = QGroupBox("Serien-Einstellungen")
        self.series_group.setVisible(False)
        
        series_form = QFormLayout()
        
        self.series_input = QLineEdit()
        self.season_input = QLineEdit()
        self.episode_input = QLineEdit()
        
        series_form.addRow("Serie:", self.series_input)
        series_form.addRow("Staffel:", self.season_input)
        series_form.addRow("Folge:", self.episode_input)
        
        self.custom_path_input = QLineEdit()
        
        series_form.addRow("Eigener Pfad:", self.custom_path_input)
        
        self.series_group.setLayout(series_form)
        main_layout.addWidget(self.series_group)
        
        # Options-Button statt Felder für Standardpfad und yt-dlp-Quelle
        options_layout = QHBoxLayout()
        self.optionen_btn = QPushButton("Optionen...")
        self.optionen_btn.clicked.connect(self.open_optionen_dialog)
        options_layout.addWidget(self.optionen_btn)
        main_layout.addLayout(options_layout)
        
        # Command Preview
        main_layout.addWidget(QLabel("Befehlsvorschau:"))
        self.cmd_preview = QTextEdit()
        self.cmd_preview.setReadOnly(True)
        self.cmd_preview.setMaximumHeight(60)
        main_layout.addWidget(self.cmd_preview)
        
        # Download Buttons
        download_buttons_layout = QHBoxLayout()
        self.download_btn = QPushButton("Download starten")
        self.download_btn.clicked.connect(self.start_download)
        download_buttons_layout.addWidget(self.download_btn)
        
        # Neu: Queue-Button
        self.queue_btn = QPushButton("Zur Queue hinzufügen")
        self.queue_btn.clicked.connect(self.add_to_queue)
        download_buttons_layout.addWidget(self.queue_btn)
        
        main_layout.addLayout(download_buttons_layout)
        
        # Neu: Tabbed Layout für Ausgabe und Queue
        self.tabs = QTabWidget()
        
        # Log Output Tab
        log_tab = QWidget()
        log_layout = QVBoxLayout()
        log_layout.addWidget(QLabel("Ausgabe:"))
        self.log_output = QTextEdit()
        self.log_output.setReadOnly(True)
        log_layout.addWidget(self.log_output)
        log_tab.setLayout(log_layout)
        self.tabs.addTab(log_tab, "Ausgabe")
        
        # Queue Tab
        queue_tab = QWidget()
        queue_layout = QVBoxLayout()
        queue_layout.addWidget(QLabel("Download-Queue:"))
        
        self.queue_list = QListWidget()
        self.queue_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self.queue_list.customContextMenuRequested.connect(self.show_queue_context_menu)
        queue_layout.addWidget(self.queue_list)
        
        queue_buttons = QHBoxLayout()
        self.start_queue_btn = QPushButton("Queue starten")
        self.start_queue_btn.clicked.connect(self.start_queue)
        queue_buttons.addWidget(self.start_queue_btn)
        
        self.clear_queue_btn = QPushButton("Queue leeren")
        self.clear_queue_btn.clicked.connect(self.clear_queue)
        queue_buttons.addWidget(self.clear_queue_btn)
        
        queue_layout.addLayout(queue_buttons)
        queue_tab.setLayout(queue_layout)
        self.tabs.addTab(queue_tab, "Queue")
        
        main_layout.addWidget(self.tabs)
        
        # Connect signals
        self.url_input.textChanged.connect(self.update_cmd_preview)
        self.preset_combo.currentIndexChanged.connect(self.preset_changed)
        
        # Serie, Staffel, Folge Signals
        self.series_input.textChanged.connect(self.update_cmd_preview)
        self.season_input.textChanged.connect(self.update_cmd_preview)
        self.episode_input.textChanged.connect(self.update_cmd_preview)
        self.custom_path_input.textChanged.connect(self.update_cmd_preview)
        
        central_widget.setLayout(main_layout)
        self.setCentralWidget(central_widget)
        
        # Initial update
        self.preset_changed()
        self.update_cmd_preview()
        self.update_queue_buttons()
        
    def load_config(self):
        if os.path.exists(CONFIG_FILE):
            try:
                with open(CONFIG_FILE, 'r') as f:
                    return json.load(f)
            except Exception:
                return DEFAULT_CONFIG.copy()
        return DEFAULT_CONFIG.copy()
    
    def save_config(self):
        self.config["output_dir"] = self.output_dir_input.text() if hasattr(self, 'output_dir_input') else self.config.get("output_dir", "")
        self.config["use_local_ytdlp"] = self.ytdlp_source_combo.currentIndex() == 0 if hasattr(self, 'ytdlp_source_combo') else self.config.get("use_local_ytdlp", True)
        if self.preset_combo.currentIndex() >= 0:
            self.config["last_preset"] = self.preset_combo.currentText()
        try:
            with open(CONFIG_FILE, 'w') as f:
                json.dump(self.config, f, indent=4)
        except Exception as e:
            self.log_output.append(f"Fehler beim Speichern der Konfiguration: {str(e)}")
    
    def load_presets(self):
        # Default-Presets immer aus dem Code laden
        defaults = [
            {
                "name": "Audio (MP3)",
                "description": "Nur Audio als MP3",
                "args": "-x --audio-format mp3",
                "has_series_template": False,
                "is_audio": True,
                "source": "default"
            },
            {
                "name": "Video (Best)",
                "description": "Beste Videoqualität",
                "args": "-f bestvideo+bestaudio",
                "has_series_template": False,
                "is_audio": False,
                "source": "default"
            }
        ]
        presets = defaults.copy()
        # User-Presets (können mitgelieferte überschreiben)
        user_presets_dir = get_user_presets_dir()
        for filename in os.listdir(user_presets_dir):
            if filename.endswith('.json'):
                try:
                    with open(os.path.join(user_presets_dir, filename), 'r', encoding='utf-8') as f:
                        preset = json.load(f)
                        preset["source"] = "user"
                        # Überschreibe ggf. gleichnamiges Preset
                        presets = [p for p in presets if p["name"] != preset["name"]]
                        presets.append(preset)
                except Exception as e:
                    self.log_output.append(f"Fehler beim Laden von User-Preset {filename}: {str(e)}")
        return presets
    
    def create_default_presets(self):
        defaults = [
            {
                "name": "Audio (MP3)",
                "description": "Nur Audio als MP3",
                "args": "-x --audio-format mp3",
                "has_series_template": False,
                "is_audio": True,
                "source": "default"
            },
            {
                "name": "Video (Best)",
                "description": "Beste Videoqualität",
                "args": "-f bestvideo+bestaudio",
                "has_series_template": False,
                "is_audio": False,
                "source": "default"
            }
        ]
        
        for preset in defaults:
            filename = f"{preset['name'].lower().replace(' ', '_')}.json"
            try:
                with open(os.path.join(PRESETS_DIR, filename), 'w') as f:
                    json.dump(preset, f, indent=4)
            except Exception as e:
                self.log_output.append(f"Fehler beim Erstellen von Standard-Preset {preset['name']}: {str(e)}")
    
    def update_preset_combo(self):
        self.preset_combo.clear()
        hide_defaults = self.config.get("hide_default_presets", False)
        for preset in self.presets:
            if hide_defaults and preset.get("source") == "default":
                continue
            self.preset_combo.addItem(preset["name"])
        # Letztes verwendetes Preset auswählen
        if self.config["last_preset"]:
            index = self.preset_combo.findText(self.config["last_preset"])
            if index >= 0:
                self.preset_combo.setCurrentIndex(index)
                
    def get_current_preset(self):
        if self.preset_combo.currentIndex() >= 0:
            preset_name = self.preset_combo.currentText()
            for preset in self.presets:
                if preset["name"] == preset_name:
                    return preset
        return None
    
    def add_preset(self):
        dialog = PresetDialog(self)
        if dialog.exec_() == QDialog.Accepted:
            preset_data = dialog.get_preset_data()
            if not preset_data["name"]:
                QMessageBox.warning(self, "Fehler", "Der Preset-Name darf nicht leer sein.")
                return
            for preset in self.presets:
                if preset["name"] == preset_data["name"]:
                    QMessageBox.warning(self, "Fehler", f"Ein Preset mit dem Namen '{preset_data['name']}' existiert bereits.")
                    return
            preset_data["source"] = "user"
            self.presets.append(preset_data)
            # Speichern des Presets im User-Presets-Ordner
            user_presets_dir = get_user_presets_dir()
            filename = f"{preset_data['name'].lower().replace(' ', '_')}.json"
            try:
                with open(os.path.join(user_presets_dir, filename), 'w', encoding='utf-8') as f:
                    json.dump(preset_data, f, indent=4, ensure_ascii=False)
                self.update_preset_combo()
                self.preset_combo.setCurrentText(preset_data["name"])
                self.update_cmd_preview()
            except Exception as e:
                self.log_output.append(f"Fehler beim Speichern des Presets: {str(e)}")
    
    def edit_preset(self):
        current_preset = self.get_current_preset()
        if not current_preset:
            QMessageBox.warning(self, "Warnung", "Kein Preset ausgewählt.")
            return
        dialog = PresetDialog(self, current_preset)
        if dialog.exec_() == QDialog.Accepted:
            new_preset_data = dialog.get_preset_data()
            if not new_preset_data["name"]:
                QMessageBox.warning(self, "Fehler", "Der Preset-Name darf nicht leer sein.")
                return
            if new_preset_data["name"] != current_preset["name"]:
                for preset in self.presets:
                    if preset["name"] == new_preset_data["name"] and preset != current_preset:
                        QMessageBox.warning(self, "Fehler", f"Ein Preset mit dem Namen '{new_preset_data['name']}' existiert bereits.")
                        return
            if new_preset_data["name"] != current_preset["name"]:
                old_filename = f"{current_preset['name'].lower().replace(' ', '_')}.json"
                try:
                    os.remove(os.path.join(get_user_presets_dir(), old_filename))
                except Exception as e:
                    self.log_output.append(f"Warnung: Konnte alte Preset-Datei nicht löschen: {str(e)}")
            for i, preset in enumerate(self.presets):
                if preset["name"] == current_preset["name"]:
                    new_preset_data["source"] = "user"
                    self.presets[i] = new_preset_data
                    break
            filename = f"{new_preset_data['name'].lower().replace(' ', '_')}.json"
            try:
                with open(os.path.join(get_user_presets_dir(), filename), 'w', encoding='utf-8') as f:
                    json.dump(new_preset_data, f, indent=4, ensure_ascii=False)
                old_current_index = self.preset_combo.currentIndex()
                self.update_preset_combo()
                new_index = self.preset_combo.findText(new_preset_data["name"])
                if new_index >= 0:
                    self.preset_combo.setCurrentIndex(new_index)
                else:
                    self.preset_combo.setCurrentIndex(old_current_index)
                self.update_cmd_preview()
            except Exception as e:
                self.log_output.append(f"Fehler beim Speichern des Presets: {str(e)}")
    
    def delete_preset(self):
        current_preset = self.get_current_preset()
        if not current_preset:
            QMessageBox.warning(self, "Warnung", "Kein Preset ausgewählt.")
            return
        reply = QMessageBox.question(
            self, 
            "Preset löschen",
            f"Möchten Sie das Preset '{current_preset['name']}' wirklich löschen?",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )
        if reply == QMessageBox.Yes:
            self.presets = [p for p in self.presets if p["name"] != current_preset["name"]]
            filename = f"{current_preset['name'].lower().replace(' ', '_')}.json"
            try:
                os.remove(os.path.join(get_user_presets_dir(), filename))
                self.update_preset_combo()
                self.update_cmd_preview()
            except Exception as e:
                self.log_output.append(f"Fehler beim Löschen des Presets: {str(e)}")
    
    def open_optionen_dialog(self):
        dialog = OptionenDialog(
            self.config["output_dir"],
            self.config["use_local_ytdlp"],
            self,
            self.config.get("ytdlp_flags", DEFAULT_CONFIG["ytdlp_flags"])
        )
        if dialog.exec_() == QDialog.Accepted:
            output_dir, use_local_ytdlp, ytdlp_flags, hide_defaults, mkvmerge_path, enable_adn_tab, ffmpeg_path = dialog.get_values()
            self.config["output_dir"] = output_dir
            self.config["use_local_ytdlp"] = use_local_ytdlp
            self.config["ytdlp_flags"] = ytdlp_flags
            self.config["hide_default_presets"] = hide_defaults
            self.config["mkvmerge_path"] = mkvmerge_path
            self.config["enable_adn_tab"] = enable_adn_tab
            self.config["ffmpeg_path"] = ffmpeg_path
            self.update_preset_combo()
            self.update_cmd_preview()
    
    def preset_changed(self):
        preset = self.get_current_preset()
        if not preset:
            self.series_group.setVisible(False)
            return
            
        # Aktualisiere Serien-UI basierend auf dem Preset
        has_series = preset.get("has_series_template", False)
        self.series_group.setVisible(has_series)
        
        # Setze den custom_path_input immer zurück, unabhängig vom Preset-Typ
        self.custom_path_input.setText(preset.get("custom_path", ""))
        
        if has_series:
            self.series_input.setText(preset.get("series", ""))
            self.season_input.setText(preset.get("season", "1"))
            self.episode_input.setText(preset.get("episode", "1"))
        
        self.update_cmd_preview()
    
    def update_cmd_preview(self):
        preset = self.get_current_preset()
        if not preset:
            self.cmd_preview.setText("Kein Preset ausgewählt.")
            return
            
        # Zeige Preset-Informationen in der Ausgabe an
        self.show_preset_info(preset)
            
        if self.config["use_local_ytdlp"]:
            ytdlp_path = os.path.join(get_base_path(), "bin", "yt-dlp.exe")
            if not os.path.exists(ytdlp_path):
                ytdlp_path = "yt-dlp"  # Fallback auf PATH
        else:
            ytdlp_path = "yt-dlp"
        cmd = [ytdlp_path]
        flags = self.config.get("ytdlp_flags", {})
        is_audio = preset.get("is_audio", False)
        # Login-Optionen
        if preset.get("username"):
            cmd.extend(["-u", preset["username"]])
        if preset.get("password"):
            cmd.extend(["-p", preset["password"]])
        # Referer
        if preset.get("referer"):
            cmd.append(f"--referer={preset['referer']}")
        # HLS-ffmpeg
        if preset.get("hls_ffmpeg"):
            cmd.extend(["--downloader", "ffmpeg", "--hls-use-mpegts"])
        # FFmpeg-Pfad
        if flags.get("use_ffmpeg_location") and self.config.get("ffmpeg_path"):
            cmd.extend(["--ffmpeg-location", self.config.get("ffmpeg_path")])
        # Untertitel
        if preset.get("sublang"):
            cmd.extend(["--sub-lang", preset["sublang"]])
        if preset.get("embed_subs"):
            cmd.append("--embed-subs")
        if preset.get("subformat"):
            cmd.extend(["--convert-subs", preset["subformat"]])
        if flags.get("ignore_config"):
            cmd.append("--ignore-config")
        if flags.get("remux_mkv") and not is_audio:
            cmd.extend(["--remux-video", "mkv"])
        if flags.get("embed_metadata"):
            cmd.append("--embed-metadata")
        if preset["args"]:
            args = []
            in_quotes = False
            current_arg = ""
            for char in preset["args"]:
                if char == '"' or char == "'":
                    in_quotes = not in_quotes
                    current_arg += char
                elif char.isspace() and not in_quotes:
                    if current_arg:
                        args.append(current_arg)
                        current_arg = ""
                else:
                    current_arg += char
            if current_arg:
                args.append(current_arg)
            cmd.extend(args)
            
        # Wenn ein eigener Pfad im Preset definiert ist, diesen verwenden
        custom_path = self.custom_path_input.text() or preset.get("custom_path", "")
        output_dir = custom_path if custom_path else self.config["output_dir"]
        
        # Generiere den Dateinamen ohne Debug-Ausgaben
        if preset.get("has_series_template", False):
            template = preset.get("series_template", SERIES_TEMPLATE)
            series = self.series_input.text() or preset.get("series", "")
            season = self.season_input.text() or preset.get("season", "1")
            episode = self.episode_input.text() or preset.get("episode", "1")
            custom_path = self.custom_path_input.text() or preset.get("custom_path", "")
            # Stelle sicher, dass der Pfad mit einem Trennzeichen endet, wenn er nicht leer ist
            if custom_path and not custom_path.endswith("/") and not custom_path.endswith("\\"):
                custom_path += "/"  # Verwende / als universellen Pfadtrenner
            output_name = template
            output_name = output_name.replace("{series}", series)
            output_name = output_name.replace("{season}", season)
            output_name = output_name.replace("{episode}", episode)
            # Entferne {path} Platzhalter, da custom_path jetzt als output_dir verwendet wird
            output_name = output_name.replace("{path}", "")
            # {extension} durch .%(ext)s ersetzen (auch rückwärtskompatibel)
            output_name = output_name.replace("{extension}", ".%(ext)s")
            # Falls jemand %(ext)s nicht im Template hat, ergänzen wir es am Ende
            if "%(ext)s" not in output_name:
                output_name += ".%(ext)s"
            # Entferne doppelte Schrägstriche
            while "//" in output_name:
                output_name = output_name.replace("//", "/")
            while "\\\\" in output_name:
                output_name = output_name.replace("\\\\", "\\")
            output_filename = output_name
        else:
            # Verwende benutzerdefinierte Ausgabevorlage, wenn in den Presets aktiviert
            if preset.get("custom_output_template", False) and preset.get("output_template"):
                output_filename = preset.get("output_template")
            else:
                output_filename = "%(title)s.%(ext)s"
                
        # Füge den Ausgabepfad zum Befehl hinzu
        if output_dir:
            output_path = os.path.join(output_dir, output_filename)
            cmd.extend(["-o", output_path])
        else:
            cmd.extend(["-o", output_filename])
        url = self.url_input.text() or "[URL]"
        cmd.append(url)
        formatted_cmd = []
        for arg in cmd:
            if " " in arg and not (arg.startswith('"') and arg.endswith('"')) and not (arg.startswith("'") and arg.endswith("'")):
                formatted_cmd.append(f'"{arg}"')
            else:
                formatted_cmd.append(arg)
        
        # Maskiere sensible Daten in der Befehlsvorschau
        command_string = " ".join(formatted_cmd)
        masked_command = mask_sensitive_data(command_string)
        self.cmd_preview.setText(masked_command)
        
    def show_preset_info(self, preset):
        """Zeigt Informationen zum ausgewählten Preset in der Ausgabe an."""
        self.log_output.clear()
        self.log_output.append(f"Preset: {preset['name']}")
        if preset.get("description"):
            self.log_output.append(f"Beschreibung: {preset['description']}")
        
        # Ausgabepfad
        custom_path = self.custom_path_input.text() or preset.get("custom_path", "")
        output_dir = custom_path if custom_path else self.config["output_dir"]
        self.log_output.append(f"Ausgabeverzeichnis: {output_dir or '(Standard)'}")
        
        # Serien-Informationen
        if preset.get("has_series_template", False):
            self.log_output.append("\nSerien-Einstellungen:")
            template = preset.get("series_template", SERIES_TEMPLATE)
            series = self.series_input.text() or preset.get("series", "")
            season = self.season_input.text() or preset.get("season", "1")
            episode = self.episode_input.text() or preset.get("episode", "1")
            
            self.log_output.append(f"  Template: {template}")
            self.log_output.append(f"  Serie: {series}")
            self.log_output.append(f"  Staffel: {season}")
            self.log_output.append(f"  Folge: {episode}")
            
            # Generiere Beispiel-Dateinamen
            output_name = template
            output_name = output_name.replace("{series}", series)
            output_name = output_name.replace("{season}", season)
            output_name = output_name.replace("{episode}", episode)
            output_name = output_name.replace("{path}", "")
            output_name = output_name.replace("{extension}", ".mp4")
            
            self.log_output.append(f"  Beispiel-Dateiname: {output_name}")
        
        # Preset-Argumente
        self.log_output.append("\nArgumente:")
        self.log_output.append(f"  {preset['args']}")
        
        # Besondere Eigenschaften
        special_features = []
        if preset.get("is_audio", False):
            special_features.append("Audio-Preset")
        if preset.get("username"):
            special_features.append("Authentifizierung")
        if preset.get("referer"):
            special_features.append("Referer")
        if preset.get("hls_ffmpeg", False):
            special_features.append("HLS-ffmpeg")
        if preset.get("sublang") or preset.get("embed_subs") or preset.get("subformat"):
            special_features.append("Untertitel")
        if preset.get("use_format_selection", False):
            special_features.append("Format-Auswahl")
        if preset.get("use_dual_audio", False):
            special_features.append("Dual-Audio")
            
        if special_features:
            self.log_output.append("\nBesondere Eigenschaften:")
            self.log_output.append(f"  {', '.join(special_features)}")
    
    def get_output_filename(self, preset):
        """Generiert den Ausgabedateinamen basierend auf dem Preset und den aktuellen Einstellungen."""
        if preset.get("has_series_template", False):
            template = preset.get("series_template", SERIES_TEMPLATE)
            series = self.series_input.text() or preset.get("series", "")
            season = self.season_input.text() or preset.get("season", "1")
            episode = self.episode_input.text() or preset.get("episode", "1")
            custom_path = self.custom_path_input.text() or preset.get("custom_path", "")
            # Stelle sicher, dass der Pfad mit einem Trennzeichen endet, wenn er nicht leer ist
            if custom_path and not custom_path.endswith("/") and not custom_path.endswith("\\"):
                custom_path += "/"  # Verwende / als universellen Pfadtrenner
            output_name = template
            output_name = output_name.replace("{series}", series)
            output_name = output_name.replace("{season}", season)
            output_name = output_name.replace("{episode}", episode)
            # Entferne {path} Platzhalter, da custom_path jetzt als output_dir verwendet wird
            output_name = output_name.replace("{path}", "")
            # {extension} durch .%(ext)s ersetzen (auch rückwärtskompatibel)
            output_name = output_name.replace("{extension}", ".%(ext)s")
            # Falls jemand %(ext)s nicht im Template hat, ergänzen wir es am Ende
            if "%(ext)s" not in output_name:
                output_name += ".%(ext)s"
            # Entferne doppelte Schrägstriche
            while "//" in output_name:
                output_name = output_name.replace("//", "/")
            while "\\\\" in output_name:
                output_name = output_name.replace("\\\\", "\\")
            return output_name
        # Im Nicht-Serienfall einfach den Standardnamen zurückgeben
        return "%(title)s.%(ext)s"
    
    def start_download(self):
        # Wenn bereits ein Download läuft und der Button als "Abbrechen" angezeigt wird
        if self.download_thread and self.download_thread.isRunning():
            self.abort_download()
            return
            
        url = self.url_input.text()
        if not url:
            QMessageBox.warning(self, "Fehler", "Bitte geben Sie eine URL ein.")
            return
        preset = self.get_current_preset()
        if not preset:
            QMessageBox.warning(self, "Fehler", "Bitte wählen Sie ein Preset aus.")
            return
        
        # Wenn im Preset ein eigener Pfad definiert ist, diesen bevorzugen
        custom_path = self.custom_path_input.text() or preset.get("custom_path", "")
        # Wenn custom_path gesetzt ist, verwenden wir diesen anstelle des standard output_dir
        output_dir = custom_path if custom_path else self.config["output_dir"]
        
        use_local_ytdlp = self.config["use_local_ytdlp"]
        flags = self.config.get("ytdlp_flags", {})
        is_audio = preset.get("is_audio", False)
        use_dual_audio = preset.get("use_dual_audio", False)
        
        # Aktualisiere die Serien-Informationen im Preset für den Download
        # (nur temporär für diesen Download, nicht dauerhaft speichern)
        if preset.get("has_series_template", False):
            updated_preset = preset.copy()
            updated_preset["series"] = self.series_input.text() or preset.get("series", "")
            updated_preset["season"] = self.season_input.text() or preset.get("season", "1")
            updated_preset["episode"] = self.episode_input.text() or preset.get("episode", "1")
        else:
            updated_preset = preset
        
        extra_args = []
        if preset.get("username"):
            extra_args.extend(["-u", preset["username"]])
        if preset.get("password"):
            extra_args.extend(["-p", preset["password"]])
        if preset.get("referer"):
            extra_args.append(f"--referer={preset['referer']}")
        if preset.get("hls_ffmpeg"):
            extra_args.extend(["--downloader", "ffmpeg", "--hls-use-mpegts"])
        # FFmpeg-Pfad
        if flags.get("use_ffmpeg_location") and self.config.get("ffmpeg_path"):
            extra_args.extend(["--ffmpeg-location", self.config.get("ffmpeg_path")])
        if preset.get("sublang"):
            extra_args.extend(["--sub-lang", preset["sublang"]])
        if preset.get("embed_subs"):
            extra_args.append("--embed-subs")
        if preset.get("subformat"):
            extra_args.extend(["--convert-subs", preset["subformat"]])
        if flags.get("ignore_config"):
            extra_args.append("--ignore-config")
        # --remux-video mkv nicht verwenden, wenn Dual-Audio aktiv ist
        if flags.get("remux_mkv") and not is_audio and not use_dual_audio:
            extra_args.extend(["--remux-video", "mkv"])
        if flags.get("embed_metadata"):
            extra_args.append("--embed-metadata")
        
        full_args = (" ".join(extra_args) + " " + preset["args"]).strip() if extra_args else preset["args"]
        
        self.save_config()
        self.log_output.clear()
        self.log_output.append(f"Starte Download von: {url}")
        output_filename = None
        if preset.get("has_series_template", False):
            output_filename = self.get_output_filename(updated_preset)
            self.log_output.append(f"Verwende Ausgabedateiname: {output_filename}")
        self.log_output.append("Download-Parameter:")
        self.log_output.append(f"URL: {url}")
        self.log_output.append(f"Ausgabeverzeichnis: {output_dir}")
        self.log_output.append(f"Argumente: {preset['args']}")
        self.log_output.append(f"Lokales yt-dlp: {use_local_ytdlp}")
        self.log_output.append(f"yt-dlp-Flags: {flags}")
        self.log_output.append(f"Audio-Preset: {is_audio}")
        self.log_output.append(f"Benutzername: {preset.get('username','')}")
        self.log_output.append(f"Referer: {preset.get('referer','')}")
        self.log_output.append(f"HLS-ffmpeg: {preset.get('hls_ffmpeg',False)}")
        self.log_output.append(f"Untertitelsprache: {preset.get('sublang','')}")
        self.log_output.append(f"Embed-Subs: {preset.get('embed_subs',False)}")
        self.log_output.append(f"Untertitel-Format: {preset.get('subformat','')}")
        self.log_output.append(f"Ausgabedateiname: {output_filename}")
        
        # FFmpeg-Pfad
        if flags.get("use_ffmpeg_location") and self.config.get("ffmpeg_path"):
            self.log_output.append(f"FFmpeg-Pfad: {self.config.get('ffmpeg_path')}")
        
        # Format-Auswahl und Dual-Audio
        if preset.get("use_format_selection", False):
            self.log_output.append("Format-Auswahl ist aktiviert.")
        if preset.get("use_dual_audio", False):
            self.log_output.append("Dual-Audio Muxing ist aktiviert.")
            if preset.get("use_dual_audio", False) and not self.config.get("mkvmerge_path"):
                QMessageBox.warning(self, "Fehler", "Für Dual-Audio Muxing muss der MKVMerge-Pfad in den Optionen angegeben werden.")
                return
        
        self.download_thread = DownloadThread(
            url=url,
            output_dir=output_dir,
            cmd_args=full_args,
            use_local_ytdlp=use_local_ytdlp,
            output_filename=output_filename,
            preset_data=updated_preset,  # Hier das aktualisierte Preset verwenden
            config=self.config
        )
        self.download_thread.update_signal.connect(self.update_log)
        self.download_thread.finished_signal.connect(self.download_finished)
        
        # Format-Auswahl für Experten-Modus
        if preset.get("use_format_selection", False) or preset.get("use_dual_audio", False):
            self.download_thread.format_selection_signal.connect(self.show_format_selection)
            self.download_thread.format_id_input_signal.connect(self.prompt_format_id)
        
        # Ändere den Button-Text und deaktiviere UI-Elemente während des Downloads
        self.download_btn.setText("Download abbrechen")
        self.disable_ui_during_download(True)
        
        self.download_thread.start()
    
    def abort_download(self):
        """Bricht den aktuellen Download ab."""
        if self.download_thread and self.download_thread.isRunning():
            self.log_output.append("Abbruch angefordert...")
            self.download_thread.stop()
            # Button wird in download_finished wieder auf "Download starten" gesetzt
    
    def disable_ui_during_download(self, disable=True):
        """Deaktiviert/aktiviert UI-Elemente während des Downloads."""
        self.url_input.setReadOnly(disable)
        self.preset_combo.setEnabled(not disable)
        self.add_preset_btn.setEnabled(not disable)
        self.edit_preset_btn.setEnabled(not disable)
        self.delete_preset_btn.setEnabled(not disable)
        self.optionen_btn.setEnabled(not disable)
        if hasattr(self, 'series_input'):
            self.series_input.setReadOnly(disable)
        if hasattr(self, 'season_input'):
            self.season_input.setReadOnly(disable)
        if hasattr(self, 'episode_input'):
            self.episode_input.setReadOnly(disable)
        if hasattr(self, 'custom_path_input'):
            self.custom_path_input.setReadOnly(disable)
    
    def update_log(self, text):
        self.log_output.append(text)
        # Scroll zum Ende
        scrollbar = self.log_output.verticalScrollBar()
        scrollbar.setValue(scrollbar.maximum())
    
    def download_finished(self, success, message):
        # UI-Elemente wieder aktivieren
        self.disable_ui_during_download(False)
        # Button-Text zurücksetzen
        self.download_btn.setText("Download starten")
        
        if success:
            self.log_output.append(message)
            QMessageBox.information(self, "Erfolg", message)
        else:
            self.log_output.append(f"Fehler: {message}")
            QMessageBox.warning(self, "Fehler", message)
            
        # Queue-Buttons nach Download aktualisieren
        self.update_queue_buttons()
    
    def closeEvent(self, event):
        self.save_config()
        self.save_queue()  # Queue beim Beenden speichern
        event.accept()

    def load_queue(self):
        """Lädt die gespeicherte Download-Queue aus einer Datei."""
        queue_file = os.path.join(get_user_data_dir(), "queue.json")
        if os.path.exists(queue_file):
            try:
                with open(queue_file, 'r', encoding='utf-8') as f:
                    queue_data = json.load(f)
                    self.download_queue = [QueueItem.from_dict(item) for item in queue_data]
                    # Sicherer Log-Aufruf
                    if hasattr(self, 'log_output'):
                        self.log_output.append(f"Download-Queue mit {len(self.download_queue)} Elementen geladen.")
                    # Queue-Liste aktualisieren
                    if hasattr(self, 'queue_list'):
                        self.update_queue_list()
            except Exception as e:
                if hasattr(self, 'log_output'):
                    self.log_output.append(f"Fehler beim Laden der Download-Queue: {str(e)}")
                print(f"Fehler beim Laden der Download-Queue: {str(e)}")
                self.download_queue = []
                
        # Aktualisiere Queue-Buttons nach dem Laden
        if hasattr(self, 'start_queue_btn') and hasattr(self, 'clear_queue_btn'):
            self.update_queue_buttons()
    
    def save_queue(self):
        """Speichert die Download-Queue in eine Datei."""
        queue_file = os.path.join(get_user_data_dir(), "queue.json")
        try:
            queue_data = [item.to_dict() for item in self.download_queue]
            with open(queue_file, 'w', encoding='utf-8') as f:
                json.dump(queue_data, f, indent=4, ensure_ascii=False)
        except Exception as e:
            if hasattr(self, 'log_output'):
                self.log_output.append(f"Fehler beim Speichern der Download-Queue: {str(e)}")
            print(f"Fehler beim Speichern der Download-Queue: {str(e)}")
    
    def add_to_queue(self):
        """Fügt den aktuellen Download zur Queue hinzu."""
        url = self.url_input.text()
        if not url:
            QMessageBox.warning(self, "Fehler", "Bitte geben Sie eine URL ein.")
            return
        preset = self.get_current_preset()
        if not preset:
            QMessageBox.warning(self, "Fehler", "Bitte wählen Sie ein Preset aus.")
            return
        
        # Wenn im Preset ein eigener Pfad definiert ist, diesen bevorzugen
        custom_path = self.custom_path_input.text() or preset.get("custom_path", "")
        # Wenn custom_path gesetzt ist, verwenden wir diesen anstelle des standard output_dir
        output_dir = custom_path if custom_path else self.config["output_dir"]
        
        series_info = None
        output_filename = None
        
        # Wenn es ein Serien-Preset ist, die Serien-Infos separat speichern
        if preset.get("has_series_template", False):
            series_info = {
                "series": self.series_input.text() or preset.get("series", ""),
                "season": self.season_input.text() or preset.get("season", "1"),
                "episode": self.episode_input.text() or preset.get("episode", "1"),
                "template": preset.get("series_template", SERIES_TEMPLATE)
            }
            output_filename = self.get_output_filename(preset)
        
        # Flags und Extra-Argumente vorbereiten
        flags = self.config.get("ytdlp_flags", {})
        is_audio = preset.get("is_audio", False)
        extra_args = []
        
        if preset.get("username"):
            extra_args.extend(["-u", preset["username"]])
        if preset.get("password"):
            extra_args.extend(["-p", preset["password"]])
        if preset.get("referer"):
            extra_args.append(f"--referer={preset['referer']}")
        if preset.get("hls_ffmpeg"):
            extra_args.extend(["--downloader", "ffmpeg", "--hls-use-mpegts"])
        # FFmpeg-Pfad
        if flags.get("use_ffmpeg_location") and self.config.get("ffmpeg_path"):
            extra_args.extend(["--ffmpeg-location", self.config.get("ffmpeg_path")])
        if preset.get("sublang"):
            extra_args.extend(["--sub-lang", preset["sublang"]])
        if preset.get("embed_subs"):
            extra_args.append("--embed-subs")
        if preset.get("subformat"):
            extra_args.extend(["--convert-subs", preset["subformat"]])
        if flags.get("ignore_config"):
            extra_args.append("--ignore-config")
        if flags.get("remux_mkv") and not is_audio:
            extra_args.extend(["--remux-video", "mkv"])
        if flags.get("embed_metadata"):
            extra_args.append("--embed-metadata")
        
        full_args = (" ".join(extra_args) + " " + preset["args"]).strip() if extra_args else preset["args"]
        
        # Erstelle ein Queue-Element
        queue_item = QueueItem(
            url=url,
            preset_data=preset,
            output_dir=output_dir,
            output_filename=output_filename,
            series_info=series_info,
            use_local_ytdlp=self.config["use_local_ytdlp"],
            extra_args=full_args
        )
        
        # Füge das Element zur Queue hinzu
        self.download_queue.append(queue_item)
        
        # Aktualisiere die Queue-Liste
        self.update_queue_list()
        
        # Zur Ausgabe wechseln und Hinzufügen bestätigen
        self.tabs.setCurrentIndex(0)  # Wechsle zum Ausgabe-Tab
        self.log_output.append(f"Download wurde zur Queue hinzugefügt: {queue_item.get_display_name()}")
        
        # Optional: Leere die URL-Box
        self.url_input.clear()
        
        # Aktualisiere Queue-Buttons
        self.update_queue_buttons()
        
        # Queue speichern
        self.save_queue()
    
    def update_queue_list(self):
        """Aktualisiert die Anzeige der Queue-Liste."""
        self.queue_list.clear()
        for item in self.download_queue:
            list_item = QListWidgetItem(f"{item.status}: {item.get_display_name()}")
            list_item.setData(Qt.UserRole, item.id)  # Speichere die ID als Daten
            self.queue_list.addItem(list_item)
    
    def update_queue_buttons(self):
        """Aktualisiert den Status der Queue-Buttons basierend auf dem Zustand der Queue."""
        has_items = len(self.download_queue) > 0
        is_downloading = self.download_thread and self.download_thread.isRunning()
        
        self.start_queue_btn.setEnabled(has_items and not is_downloading)
        self.clear_queue_btn.setEnabled(has_items and not is_downloading)
    
    def show_queue_context_menu(self, position):
        """Zeigt das Kontextmenü für die Queue-Liste an."""
        if not self.queue_list.count():
            return
            
        menu = QMenu()
        
        # Aktionen erstellen
        remove_action = QAction("Entfernen", self)
        remove_action.triggered.connect(self.remove_selected_queue_item)
        
        move_up_action = QAction("Nach oben", self)
        move_up_action.triggered.connect(lambda: self.move_queue_item(-1))
        
        move_down_action = QAction("Nach unten", self)
        move_down_action.triggered.connect(lambda: self.move_queue_item(1))
        
        # Prüfe ob ein Element ausgewählt ist
        if self.queue_list.currentItem():
            menu.addAction(remove_action)
            menu.addAction(move_up_action)
            menu.addAction(move_down_action)
            
            # Zeige Menü
            menu.exec_(self.queue_list.mapToGlobal(position))
    
    def remove_selected_queue_item(self):
        """Entfernt das ausgewählte Element aus der Queue."""
        current_item = self.queue_list.currentItem()
        if not current_item:
            return
            
        item_id = current_item.data(Qt.UserRole)
        self.download_queue = [item for item in self.download_queue if item.id != item_id]
        self.update_queue_list()
        self.update_queue_buttons()
        
        # Queue speichern
        self.save_queue()
    
    def move_queue_item(self, direction):
        """Verschiebt ein Queue-Element nach oben oder unten."""
        current_row = self.queue_list.currentRow()
        if current_row < 0:
            return
            
        new_row = current_row + direction
        if new_row < 0 or new_row >= self.queue_list.count():
            return
            
        # Tausche Elemente in der Queue
        self.download_queue[current_row], self.download_queue[new_row] = \
            self.download_queue[new_row], self.download_queue[current_row]
        
        # Aktualisiere UI
        self.update_queue_list()
        self.queue_list.setCurrentRow(new_row)
        
        # Queue speichern
        self.save_queue()
    
    def start_queue(self):
        """Startet die Download-Queue."""
        if not self.download_queue:
            QMessageBox.information(self, "Queue leer", "Die Download-Queue ist leer.")
            return
            
        if self.download_thread and self.download_thread.isRunning():
            QMessageBox.warning(self, "Download läuft", "Es läuft bereits ein Download.")
            return
            
        # Starte den ersten Download in der Queue
        self.process_next_queue_item()
    
    def process_next_queue_item(self):
        """Verarbeitet das nächste Element in der Queue."""
        if not self.download_queue:
            self.log_output.append("Download-Queue abgeschlossen.")
            self.current_queue_item = None
            self.update_queue_buttons()
            return
            
        # Nehme das erste Element aus der Queue
        self.current_queue_item = self.download_queue[0]
        self.current_queue_item.status = "Wird heruntergeladen"
        self.update_queue_list()
        
        # Starte den Download
        self.tabs.setCurrentIndex(0)  # Wechsle zum Ausgabe-Tab
        self.log_output.clear()
        self.log_output.append(f"Starte Download von: {self.current_queue_item.url}")
        self.log_output.append(f"Preset: {self.current_queue_item.preset_data.get('name', 'Unbekannt')}")
        self.log_output.append(f"Ausgabeverzeichnis: {self.current_queue_item.output_dir}")
        
        # Spezielle Optionen anzeigen
        use_format_selection = self.current_queue_item.preset_data.get("use_format_selection", False)
        use_dual_audio = self.current_queue_item.preset_data.get("use_dual_audio", False)
        
        # Überprüfe und aktualisiere ggf. Serien-Informationen aus dem Hauptfenster
        if self.current_queue_item.preset_data.get("has_series_template", False) and self.current_queue_item.series_info:
            # Aktualisiere mit aktuellen Werten aus UI wenn verfügbar
            updated_preset = self.current_queue_item.preset_data.copy()
            
            # Aktuelle Werte aus dem Hauptfenster verwenden (falls Felder sichtbar sind)
            if hasattr(self, 'series_input') and self.series_input.isVisible():
                self.current_queue_item.series_info["series"] = self.series_input.text() or self.current_queue_item.series_info.get("series", "")
                self.current_queue_item.series_info["season"] = self.season_input.text() or self.current_queue_item.series_info.get("season", "1")
                self.current_queue_item.series_info["episode"] = self.episode_input.text() or self.current_queue_item.series_info.get("episode", "1")
            
            updated_preset["series"] = self.current_queue_item.series_info.get("series", "")
            updated_preset["season"] = self.current_queue_item.series_info.get("season", "1")
            updated_preset["episode"] = self.current_queue_item.series_info.get("episode", "1")
            
            self.current_queue_item.preset_data = updated_preset
        
        if use_format_selection:
            self.log_output.append("Format-Auswahl ist aktiviert.")
        if use_dual_audio:
            self.log_output.append("Dual-Audio Muxing ist aktiviert.")
            # Überprüfung des MKVMerge-Pfads
            if not self.config.get("mkvmerge_path"):
                self.log_output.append("Warnung: Kein MKVMerge-Pfad konfiguriert. Dual-Audio kann fehlschlagen.")
        
        # Falls Dual-Audio aktiviert ist, sollten wir --remux-video mkv aus den Argumenten entfernen
        if use_dual_audio:
            cmd_args = self.current_queue_item.extra_args
            # Entferne --remux-video mkv aus den Argumenten, wenn vorhanden
            cmd_args = re.sub(r'--remux-video\s+mkv\s*', '', cmd_args)
            self.current_queue_item.extra_args = cmd_args
        
        # Download-Thread erstellen und starten
        self.download_thread = DownloadThread(
            url=self.current_queue_item.url,
            output_dir=self.current_queue_item.output_dir,
            cmd_args=self.current_queue_item.extra_args,
            use_local_ytdlp=self.current_queue_item.use_local_ytdlp,
            output_filename=self.current_queue_item.output_filename,
            preset_data=self.current_queue_item.preset_data,
            config=self.config
        )
        
        self.download_thread.update_signal.connect(self.update_log)
        self.download_thread.finished_signal.connect(self.queue_download_finished)
        
        # Format-Auswahl für Experten-Modus
        if self.current_queue_item.preset_data.get("use_format_selection", False) or self.current_queue_item.preset_data.get("use_dual_audio", False):
            self.download_thread.format_selection_signal.connect(self.show_format_selection)
            self.download_thread.format_id_input_signal.connect(self.prompt_format_id)
        
        # Ändere den Button-Text und deaktiviere UI-Elemente während des Downloads
        self.download_btn.setText("Download abbrechen")
        self.disable_ui_during_download(True)
        
        self.download_thread.start()
    
    def queue_download_finished(self, success, message):
        """Callback wenn ein Download aus der Queue fertig ist."""
        # UI-Elemente wieder aktivieren
        self.disable_ui_during_download(False)
        # Button-Text zurücksetzen
        self.download_btn.setText("Download starten")
        
        # Bearbeite das aktuelle Queue-Element
        if self.current_queue_item:
            self.current_queue_item.status = "Fertig" if success else "Fehler"
            
            # Entferne das Element aus der Queue (es bleibt in der Liste, aber mit Status)
            if self.download_queue and self.download_queue[0].id == self.current_queue_item.id:
                self.download_queue.pop(0)
            
            self.update_queue_list()
            
            # Queue speichern
            self.save_queue()
        
        if success:
            self.log_output.append(message)
        else:
            self.log_output.append(f"Fehler: {message}")
        
        # Wenn die Queue nicht manuell abgebrochen wurde, verarbeite das nächste Element
        if not message.startswith("Download wurde abgebrochen"):
            self.process_next_queue_item()
        
        # Aktualisiere Queue-Buttons
        self.update_queue_buttons()
    
    def clear_queue(self):
        """Leert die Download-Queue."""
        if not self.download_queue:
            return
            
        reply = QMessageBox.question(
            self, 
            "Queue leeren",
            "Möchten Sie wirklich die gesamte Download-Queue leeren?",
            QMessageBox.Yes | QMessageBox.No, 
            QMessageBox.No
        )
        
        if reply == QMessageBox.Yes:
            self.download_queue.clear()
            self.update_queue_list()
            self.update_queue_buttons()
            
            # Queue speichern (leere Liste)
            self.save_queue()

    def show_format_selection(self, formats):
        """Zeigt die verfügbaren Formate in der Ausgabe an."""
        self.log_output.append("\n=== Verfügbare Formate ===")
        for format_line in formats:
            self.log_output.append(format_line)
        self.log_output.append("=====================")

    def prompt_format_id(self):
        """Fordert den Benutzer zur Eingabe einer Format-ID auf."""
        dialog = QInputDialog(self)
        dialog.setWindowTitle("Format-ID eingeben")
        dialog.setLabelText("Bitte gib die gewünschte Format-ID ein:")
        dialog.setInputMode(QInputDialog.TextInput)
        dialog.setWindowFlags(dialog.windowFlags() & ~Qt.WindowContextHelpButtonHint)  # Fragezeichen entfernen
        
        # Icon für den Dialog setzen
        icon_path = os.path.join(get_base_path(), "icon.ico")
        if os.path.exists(icon_path):
            dialog.setWindowIcon(QIcon(icon_path))
        
        ok = dialog.exec_()
        format_id = dialog.textValue()
        
        if ok and format_id:
            self.log_output.append(f"Format-ID ausgewählt: {format_id}")
            # Setze die Format-ID im Download-Thread
            if self.download_thread and self.download_thread.isRunning():
                self.download_thread.set_format_id(format_id)
        else:
            # Bei Abbruch den Download beenden
            self.log_output.append("Format-Auswahl abgebrochen.")
            if self.download_thread and self.download_thread.isRunning():
                self.download_thread.abort = True


if __name__ == "__main__":
    app = QApplication(sys.argv)
    
    # Globales Icon für die Anwendung setzen
    icon_path = os.path.join(get_base_path(), "icon.ico")
    if os.path.exists(icon_path):
        app.setWindowIcon(QIcon(icon_path))
    
    window = MainWindow()
    window.show()
    sys.exit(app.exec_()) 