From ba5089f3c218fb104e4e9dfe249b524e8e464048 Mon Sep 17 00:00:00 2001 From: Akamaru Date: Mon, 5 May 2025 15:41:20 +0200 Subject: [PATCH] =?UTF-8?q?F=C3=BCge=20Unterst=C3=BCtzung=20f=C3=BCr=20Dua?= =?UTF-8?q?l-Audio-Downloads=20und=20Format-Auswahl=20hinzu.=20Implementie?= =?UTF-8?q?re=20neue=20Funktionen=20zur=20Maskierung=20sensibler=20Daten?= =?UTF-8?q?=20in=20Befehlen,=20verbessere=20die=20Benutzeroberfl=C3=A4che?= =?UTF-8?q?=20mit=20Tabs=20f=C3=BCr=20Preset-Einstellungen=20und=20aktuali?= =?UTF-8?q?siere=20die=20Log-Ausgabe=20f=C3=BCr=20bessere=20Benutzererfahr?= =?UTF-8?q?ung.=20Erg=C3=A4nze=20die=20M=C3=B6glichkeit,=20tempor=C3=A4re?= =?UTF-8?q?=20Dateien=20zu=20verwalten=20und=20den=20MKVMerge-Pfad=20in=20?= =?UTF-8?q?den=20Optionen=20zu=20konfigurieren.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- icon.ico | Bin 0 -> 67646 bytes main.py | 842 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 814 insertions(+), 28 deletions(-) create mode 100644 icon.ico diff --git a/icon.ico b/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..7934aedea6b613f495e4f0ee7b16495954385542 GIT binary patch literal 67646 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 bcWD&aa9w?X38Kt*OIOd=GEcKNN 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): @@ -194,6 +697,11 @@ class PresetDialog(QDialog): self.setWindowTitle("Preset erstellen/bearbeiten") self.resize(500, 450) + # 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": "", @@ -212,6 +720,12 @@ class PresetDialog(QDialog): 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"]) @@ -222,6 +736,16 @@ class PresetDialog(QDialog): 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)) @@ -237,13 +761,21 @@ class PresetDialog(QDialog): 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 ...") - form_layout.addRow("Untertitelsprache:", self.sublang_edit) + 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)) - form_layout.addRow(self.embed_subs_cb) + subtitle_layout.addRow(self.embed_subs_cb) # Untertitel-Format Dropdown self.subformat_combo = QComboBox() @@ -256,22 +788,20 @@ class PresetDialog(QDialog): idx = self.subformat_combo.findData(subformat) if idx >= 0: self.subformat_combo.setCurrentIndex(idx) - form_layout.addRow("Untertitel-Format:", self.subformat_combo) + subtitle_layout.addRow("Untertitel-Format:", self.subformat_combo) - # Eigener Pfad (immer sichtbar, mit Durchsuchen) - 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) + subtitle_tab.setLayout(subtitle_layout) + tabs.addTab(subtitle_tab, "Untertitel") + + # Tab 3: 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)") - form_layout.addRow("Benutzername:", self.username_edit) + 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) @@ -280,10 +810,18 @@ class PresetDialog(QDialog): self.show_pw_cb.toggled.connect(self.toggle_password_visible) pw_hbox.addWidget(self.password_edit) pw_hbox.addWidget(self.show_pw_cb) - form_layout.addRow("Passwort:", pw_hbox) + auth_layout.addRow("Passwort:", pw_hbox) + pw_hint = QLabel("Hinweis: Passwörter werden im Klartext lokal gespeichert!") pw_hint.setStyleSheet("color: red;") - form_layout.addRow(pw_hint) + 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") @@ -306,9 +844,81 @@ class PresetDialog(QDialog): series_layout.addRow(help_text) self.series_box.setLayout(series_layout) + path_layout.addWidget(self.series_box) - layout.addLayout(form_layout) - 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) @@ -320,6 +930,14 @@ class PresetDialog(QDialog): 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(), @@ -338,7 +956,17 @@ class PresetDialog(QDialog): "hls_ffmpeg": self.hls_ffmpeg_cb.isChecked(), "sublang": self.sublang_edit.text(), "embed_subs": self.embed_subs_cb.isChecked(), - "subformat": self.subformat_combo.currentData() + "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() } def browse_custom_path(self): @@ -367,6 +995,12 @@ class OptionenDialog(QDialog): self.setWindowTitle("Optionen") self.resize(420, 250) 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 { @@ -390,14 +1024,31 @@ class OptionenDialog(QDialog): 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) + 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 @@ -466,6 +1117,13 @@ class OptionenDialog(QDialog): 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 get_values(self): return ( self.output_dir_input.text(), @@ -475,7 +1133,9 @@ class OptionenDialog(QDialog): "remux_mkv": self.cb_remux_mkv.isChecked(), "embed_metadata": self.cb_embed_metadata.isChecked() }, - self.hide_defaults_cb.isChecked() + self.hide_defaults_cb.isChecked(), + self.mkvmerge_path_input.text(), + self.enable_adn_tab_cb.isChecked() ) def download_ytdlp(self): @@ -581,6 +1241,11 @@ class MainWindow(QMainWindow): 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() @@ -949,11 +1614,13 @@ class MainWindow(QMainWindow): self.config.get("ytdlp_flags", DEFAULT_CONFIG["ytdlp_flags"]) ) if dialog.exec_() == QDialog.Accepted: - output_dir, use_local_ytdlp, ytdlp_flags, hide_defaults = dialog.get_values() + output_dir, use_local_ytdlp, ytdlp_flags, hide_defaults, mkvmerge_path, enable_adn_tab = 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.update_preset_combo() self.update_cmd_preview() @@ -1092,7 +1759,11 @@ class MainWindow(QMainWindow): formatted_cmd.append(f'"{arg}"') else: formatted_cmd.append(arg) - self.cmd_preview.setText(" ".join(formatted_cmd)) + + # 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 start_download(self): # Wenn bereits ein Download läuft und der Button als "Abbrechen" angezeigt wird @@ -1117,6 +1788,18 @@ class MainWindow(QMainWindow): 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"]]) @@ -1134,17 +1817,20 @@ class MainWindow(QMainWindow): 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: + # --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(preset) + 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}") @@ -1160,16 +1846,33 @@ class MainWindow(QMainWindow): 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}") + + # 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 + 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) @@ -1460,18 +2163,61 @@ class MainWindow(QMainWindow): 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 + 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) @@ -1531,9 +2277,49 @@ class MainWindow(QMainWindow): # 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_()) \ No newline at end of file