diff --git a/languages/ar.json b/languages/ar.json index 91b10d2..a3ba2ff 100644 --- a/languages/ar.json +++ b/languages/ar.json @@ -277,7 +277,14 @@ "update_frequency_group": "تكرار التحديثات", "save_settings": "حفظ الإعدادات", "settings_saved_successfully": "✅ تم حفظ الإعدادات بنجاح!", - "error_saving": "❌ خطأ في حفظ الإعدادات: {error}" + "error_saving": "❌ خطأ في حفظ الإعدادات: {error}", + "output_format_settings": "إعدادات تنسيق الإخراج", + "force_output_format": "فرض تنسيق الإخراج عند الدمج", + "preferred_format": "التنسيق المفضل:", + "force_format_help": "عند التمكين، سيتم تحويل الفيديوهات المدموجة إلى تنسيقك المفضل. قم بالتعطيل للسماح لـ yt-dlp بالقرار تلقائياً.", + "format_mp4": "MP4 (الأكثر توافقاً)", + "format_webm": "WebM (حديث، مفتوح)", + "format_mkv": "MKV (غني بالميزات)" }, "main_ui": { "url_placeholder": "أدخل رابط فيديو يوتيوب أو قائمة تشغيل", diff --git a/languages/de.json b/languages/de.json index fce7d10..579af68 100644 --- a/languages/de.json +++ b/languages/de.json @@ -277,7 +277,14 @@ "update_frequency_group": "Update-Häufigkeit", "save_settings": "Einstellungen speichern", "settings_saved_successfully": "✅ Einstellungen erfolgreich gespeichert!", - "error_saving": "❌ Fehler beim Speichern der Einstellungen: {error}" + "error_saving": "❌ Fehler beim Speichern der Einstellungen: {error}", + "output_format_settings": "Ausgabeformat-Einstellungen", + "force_output_format": "Ausgabeformat beim Zusammenführen erzwingen", + "preferred_format": "Bevorzugtes Format:", + "force_format_help": "Wenn aktiviert, werden zusammengeführte Videos in Ihr bevorzugtes Format konvertiert. Deaktivieren Sie diese Option, damit yt-dlp automatisch entscheidet.", + "format_mp4": "MP4 (Am kompatibelsten)", + "format_webm": "WebM (Modern, offen)", + "format_mkv": "MKV (Funktionsreich)" }, "main_ui": { "url_placeholder": "YouTube-Video- oder Playlist-URL eingeben", diff --git a/languages/en.json b/languages/en.json index 8b07099..f4b4bf3 100644 --- a/languages/en.json +++ b/languages/en.json @@ -277,7 +277,14 @@ "update_frequency_group": "Update Frequency", "save_settings": "Save Settings", "settings_saved_successfully": "✅ Settings saved successfully!", - "error_saving": "❌ Error saving settings: {error}" + "error_saving": "❌ Error saving settings: {error}", + "output_format_settings": "Output Format Settings", + "force_output_format": "Force output format when merging", + "preferred_format": "Preferred format:", + "force_format_help": "When enabled, merged videos will be converted to your preferred format. Disable to let yt-dlp decide automatically.", + "format_mp4": "MP4 (Most compatible)", + "format_webm": "WebM (Modern, open)", + "format_mkv": "MKV (Feature-rich)" }, "main_ui": { "url_placeholder": "Enter YouTube video or playlist URL", diff --git a/languages/es.json b/languages/es.json index 10a1792..89cbb32 100644 --- a/languages/es.json +++ b/languages/es.json @@ -260,7 +260,14 @@ "update_frequency_group": "Frecuencia de Actualización", "save_settings": "Guardar Configuración", "settings_saved_successfully": "✅ ¡Configuración guardada exitosamente!", - "error_saving": "❌ Error al guardar configuración: {error}" + "error_saving": "❌ Error al guardar configuración: {error}", + "output_format_settings": "Configuración de Formato de Salida", + "force_output_format": "Forzar formato de salida al combinar", + "preferred_format": "Formato preferido:", + "force_format_help": "Cuando está habilitado, los videos combinados se convertirán a su formato preferido. Deshabilitar para dejar que yt-dlp decida automáticamente.", + "format_mp4": "MP4 (Más compatible)", + "format_webm": "WebM (Moderno, abierto)", + "format_mkv": "MKV (Rico en funciones)" }, "main_ui": { "url_placeholder": "Ingresa URL de video o lista de YouTube", diff --git a/languages/fr.json b/languages/fr.json index e752ed5..391f4ed 100644 --- a/languages/fr.json +++ b/languages/fr.json @@ -277,7 +277,14 @@ "update_frequency_group": "Fréquence de mise à jour", "save_settings": "Sauvegarder les paramètres", "settings_saved_successfully": "✅ Paramètres sauvegardés avec succès !", - "error_saving": "❌ Erreur lors de la sauvegarde des paramètres : {error}" + "error_saving": "❌ Erreur lors de la sauvegarde des paramètres : {error}", + "output_format_settings": "Paramètres du format de sortie", + "force_output_format": "Forcer le format de sortie lors de la fusion", + "preferred_format": "Format préféré :", + "force_format_help": "Lorsqu'activé, les vidéos fusionnées seront converties vers votre format préféré. Désactivez pour laisser yt-dlp décider automatiquement.", + "format_mp4": "MP4 (Plus compatible)", + "format_webm": "WebM (Moderne, ouvert)", + "format_mkv": "MKV (Riche en fonctionnalités)" }, "main_ui": { "url_placeholder": "Entrer l'URL de la vidéo ou de la playlist YouTube", diff --git a/languages/hi.json b/languages/hi.json index c3ad3dd..b33846e 100644 --- a/languages/hi.json +++ b/languages/hi.json @@ -277,7 +277,14 @@ "update_frequency_group": "अपडेट आवृत्ति", "save_settings": "सेटिंग्स सेव करें", "settings_saved_successfully": "✅ सेटिंग्स सफलतापूर्वक सेव हुईं!", - "error_saving": "❌ सेटिंग्स सेव करने में त्रुटि: {error}" + "error_saving": "❌ सेटिंग्स सेव करने में त्रुटि: {error}", + "output_format_settings": "आउटपुट प्रारूप सेटिंग्स", + "force_output_format": "मर्ज करते समय आउटपुट प्रारूप बाध्य करें", + "preferred_format": "पसंदीदा प्रारूप:", + "force_format_help": "सक्षम होने पर, मर्ज किए गए वीडियो आपके पसंदीदा प्रारूप में परिवर्तित हो जाएंगे। yt-dlp को स्वचालित रूप से निर्णय लेने देने के लिए अक्षम करें।", + "format_mp4": "MP4 (सबसे संगत)", + "format_webm": "WebM (आधुनिक, खुला)", + "format_mkv": "MKV (सुविधा समृद्ध)" }, "main_ui": { "url_placeholder": "YouTube वीडियो या प्लेलिस्ट URL दर्ज करें", diff --git a/languages/id.json b/languages/id.json index 69986de..8114465 100644 --- a/languages/id.json +++ b/languages/id.json @@ -277,7 +277,14 @@ "update_frequency_group": "Frekuensi pembaruan", "save_settings": "Simpan pengaturan", "settings_saved_successfully": "✅ Pengaturan berhasil disimpan!", - "error_saving": "❌ Kesalahan menyimpan pengaturan: {error}" + "error_saving": "❌ Kesalahan menyimpan pengaturan: {error}", + "output_format_settings": "Pengaturan Format Output", + "force_output_format": "Paksa format output saat menggabungkan", + "preferred_format": "Format yang disukai:", + "force_format_help": "Jika diaktifkan, video yang digabungkan akan dikonversi ke format pilihan Anda. Nonaktifkan untuk membiarkan yt-dlp memutuskan secara otomatis.", + "format_mp4": "MP4 (Paling kompatibel)", + "format_webm": "WebM (Modern, terbuka)", + "format_mkv": "MKV (Kaya fitur)" }, "main_ui": { "url_placeholder": "Masukkan URL video atau playlist YouTube", diff --git a/languages/it.json b/languages/it.json index f08aad8..c0a3afc 100644 --- a/languages/it.json +++ b/languages/it.json @@ -277,7 +277,14 @@ "update_frequency_group": "Frequenza aggiornamenti", "save_settings": "Salva impostazioni", "settings_saved_successfully": "✅ Impostazioni salvate con successo!", - "error_saving": "❌ Errore nel salvataggio impostazioni: {error}" + "error_saving": "❌ Errore nel salvataggio impostazioni: {error}", + "output_format_settings": "Impostazioni formato output", + "force_output_format": "Forza formato output durante la fusione", + "preferred_format": "Formato preferito:", + "force_format_help": "Quando attivato, i video uniti verranno convertiti nel formato preferito. Disattiva per lasciare che yt-dlp decida automaticamente.", + "format_mp4": "MP4 (Più compatibile)", + "format_webm": "WebM (Moderno, aperto)", + "format_mkv": "MKV (Ricco di funzionalità)" }, "main_ui": { "url_placeholder": "Inserisci URL video YouTube o playlist", diff --git a/languages/ja.json b/languages/ja.json index 1018749..827fe96 100644 --- a/languages/ja.json +++ b/languages/ja.json @@ -277,7 +277,14 @@ "update_frequency_group": "更新頻度", "save_settings": "設定を保存", "settings_saved_successfully": "✅ 設定が正常に保存されました!", - "error_saving": "❌ 設定の保存エラー: {error}" + "error_saving": "❌ 設定の保存エラー: {error}", + "output_format_settings": "出力形式設定", + "force_output_format": "マージ時に出力形式を強制", + "preferred_format": "優先形式:", + "force_format_help": "有効にすると、マージされた動画は優先形式に変換されます。無効にすると、yt-dlpが自動的に決定します。", + "format_mp4": "MP4(最も互換性が高い)", + "format_webm": "WebM(モダン、オープン)", + "format_mkv": "MKV(機能豊富)" }, "main_ui": { "url_placeholder": "YouTubeの動画URLまたはプレイリストURLを入力", diff --git a/languages/pl.json b/languages/pl.json index 5caa024..c64e153 100644 --- a/languages/pl.json +++ b/languages/pl.json @@ -277,7 +277,14 @@ "update_frequency_group": "Częstotliwość aktualizacji", "save_settings": "Zapisz ustawienia", "settings_saved_successfully": "✅ Ustawienia zapisane pomyślnie!", - "error_saving": "❌ Błąd zapisywania ustawień: {error}" + "error_saving": "❌ Błąd zapisywania ustawień: {error}", + "output_format_settings": "Ustawienia formatu wyjściowego", + "force_output_format": "Wymuś format wyjściowy podczas łączenia", + "preferred_format": "Preferowany format:", + "force_format_help": "Po włączeniu połączone filmy zostaną przekonwertowane na wybrany format. Wyłącz, aby pozwolić yt-dlp zdecydować automatycznie.", + "format_mp4": "MP4 (Najbardziej kompatybilny)", + "format_webm": "WebM (Nowoczesny, otwarty)", + "format_mkv": "MKV (Bogaty w funkcje)" }, "main_ui": { "url_placeholder": "Wprowadź URL wideo YouTube lub playlisty", diff --git a/languages/pt.json b/languages/pt.json index 1c4ec0e..4d5503a 100644 --- a/languages/pt.json +++ b/languages/pt.json @@ -277,7 +277,14 @@ "update_frequency_group": "Frequência de Atualização", "save_settings": "Salvar Configurações", "settings_saved_successfully": "✅ Configurações salvas com sucesso!", - "error_saving": "❌ Erro ao salvar configurações: {error}" + "error_saving": "❌ Erro ao salvar configurações: {error}", + "output_format_settings": "Configurações de Formato de Saída", + "force_output_format": "Forçar formato de saída ao mesclar", + "preferred_format": "Formato preferido:", + "force_format_help": "Quando ativado, os vídeos mesclados serão convertidos para o seu formato preferido. Desative para deixar o yt-dlp decidir automaticamente.", + "format_mp4": "MP4 (Mais compatível)", + "format_webm": "WebM (Moderno, aberto)", + "format_mkv": "MKV (Rico em recursos)" }, "main_ui": { "url_placeholder": "Digite a URL do vídeo ou playlist do YouTube", diff --git a/languages/ru.json b/languages/ru.json index 4b1fdc5..8878d74 100644 --- a/languages/ru.json +++ b/languages/ru.json @@ -277,7 +277,14 @@ "update_frequency_group": "Частота обновления", "save_settings": "Сохранить настройки", "settings_saved_successfully": "✅ Настройки успешно сохранены!", - "error_saving": "❌ Ошибка сохранения настроек: {error}" + "error_saving": "❌ Ошибка сохранения настроек: {error}", + "output_format_settings": "Настройки выходного формата", + "force_output_format": "Принудительно задать выходной формат при объединении", + "preferred_format": "Предпочтительный формат:", + "force_format_help": "Когда включено, объединённые видео будут конвертированы в ваш предпочтительный формат. Отключите, чтобы yt-dlp решал автоматически.", + "format_mp4": "MP4 (Наиболее совместимый)", + "format_webm": "WebM (Современный, открытый)", + "format_mkv": "MKV (Многофункциональный)" }, "main_ui": { "url_placeholder": "Введите URL видео или плейлиста YouTube", diff --git a/languages/tr.json b/languages/tr.json index 0847fd3..14ae8a2 100644 --- a/languages/tr.json +++ b/languages/tr.json @@ -277,7 +277,14 @@ "update_frequency_group": "Güncelleme sıklığı", "save_settings": "Ayarları kaydet", "settings_saved_successfully": "✅ Ayarlar başarıyla kaydedildi!", - "error_saving": "❌ Ayarları kaydetme hatası: {error}" + "error_saving": "❌ Ayarları kaydetme hatası: {error}", + "output_format_settings": "Çıktı Formatı Ayarları", + "force_output_format": "Birleştirirken çıktı formatını zorla", + "preferred_format": "Tercih edilen format:", + "force_format_help": "Etkinleştirildiğinde, birleştirilen videolar tercih ettiğiniz formata dönüştürülür. yt-dlp'nin otomatik karar vermesini sağlamak için devre dışı bırakın.", + "format_mp4": "MP4 (En uyumlu)", + "format_webm": "WebM (Modern, açık)", + "format_mkv": "MKV (Özellik açısından zengin)" }, "main_ui": { "url_placeholder": "YouTube video veya oynatma listesi URL'sini girin", diff --git a/languages/zh.json b/languages/zh.json index 80dbc0d..c232493 100644 --- a/languages/zh.json +++ b/languages/zh.json @@ -277,7 +277,14 @@ "update_frequency_group": "更新频率", "save_settings": "保存设置", "settings_saved_successfully": "✅ 设置保存成功!", - "error_saving": "❌ 保存设置时出错:{error}" + "error_saving": "❌ 保存设置时出错:{error}", + "output_format_settings": "输出格式设置", + "force_output_format": "合并时强制输出格式", + "preferred_format": "首选格式:", + "force_format_help": "启用后,合并的视频将转换为您的首选格式。禁用以让 yt-dlp 自动决定。", + "format_mp4": "MP4(最兼容)", + "format_webm": "WebM(现代、开放)", + "format_mkv": "MKV(功能丰富)" }, "main_ui": { "url_placeholder": "输入 YouTube 视频或播放列表网址", diff --git a/src/core/ytsage_downloader.py b/src/core/ytsage_downloader.py index 18dae05..cf7b029 100644 --- a/src/core/ytsage_downloader.py +++ b/src/core/ytsage_downloader.py @@ -58,6 +58,8 @@ class DownloadThread(QThread): force_keyframes=False, proxy_url=None, geo_proxy_url=None, + force_output_format=False, + preferred_output_format="mp4", ) -> None: super().__init__() self.url = url @@ -81,6 +83,8 @@ class DownloadThread(QThread): self.force_keyframes = force_keyframes self.proxy_url = proxy_url self.geo_proxy_url = geo_proxy_url + self.force_output_format = force_output_format + self.preferred_output_format = preferred_output_format self.paused = False self.cancelled = False self.process = None @@ -189,6 +193,17 @@ class DownloadThread(QThread): res_value = self.resolution if self.resolution else "720" # Default to 720p if no resolution specified cmd.extend(["-S", f"res:{res_value}"]) + # Force output format if enabled and merging is needed + if self.force_output_format and not self.is_audio_only: + if self.format_has_audio: + # Progressive format (video with audio) - use remux to convert container + cmd.extend(["--remux-video", self.preferred_output_format]) + logger.debug(f"Using --remux-video to force progressive format to: {self.preferred_output_format}") + else: + # Merging video+audio - force merge output format + cmd.extend(["--merge-output-format", self.preferred_output_format]) + logger.debug(f"Using --merge-output-format to force merged format to: {self.preferred_output_format}") + # Output template with resolution in filename # Use string concatenation instead of Path.joinpath to avoid Path object issues base_path = self.path.as_posix() diff --git a/src/gui/ytsage_gui_dialogs/ytsage_dialogs_settings.py b/src/gui/ytsage_gui_dialogs/ytsage_dialogs_settings.py index 8dfa93b..585f07a 100644 --- a/src/gui/ytsage_gui_dialogs/ytsage_dialogs_settings.py +++ b/src/gui/ytsage_gui_dialogs/ytsage_dialogs_settings.py @@ -36,6 +36,7 @@ from src.core.ytsage_utils import ( from src.gui.ytsage_gui_dialogs.ytsage_dialogs_update import YTDLPUpdateDialog from src.utils.ytsage_logger import logger from src.utils.ytsage_localization import _ +from src.utils.ytsage_config_manager import ConfigManager class DownloadSettingsDialog(QDialog): @@ -199,6 +200,47 @@ class DownloadSettingsDialog(QDialog): speed_group_box.setLayout(speed_layout) layout.addWidget(speed_group_box) + # --- Output Format Settings Section --- + output_format_group_box = QGroupBox(_("settings.output_format_settings")) + output_format_layout = QVBoxLayout() + + # Load current format settings from ConfigManager + self.force_format_enabled = ConfigManager.get("force_output_format") or False + self.preferred_format_value = ConfigManager.get("preferred_output_format") or "mp4" + + # Enable/Disable force output format checkbox + self.force_format_checkbox = QCheckBox(_("settings.force_output_format")) + self.force_format_checkbox.setChecked(self.force_format_enabled) + output_format_layout.addWidget(self.force_format_checkbox) + + # Format selection layout + format_select_layout = QHBoxLayout() + format_label = QLabel(_("settings.preferred_format")) + format_label.setStyleSheet("color: #ffffff; margin-top: 5px;") + format_select_layout.addWidget(format_label) + + self.format_combo = QComboBox() + self.format_combo.addItems([ + _("settings.format_mp4"), + _("settings.format_webm"), + _("settings.format_mkv") + ]) + # Set current selection based on saved format + format_index_map = {"mp4": 0, "webm": 1, "mkv": 2} + self.format_combo.setCurrentIndex(format_index_map.get(self.preferred_format_value, 0)) + format_select_layout.addWidget(self.format_combo) + format_select_layout.addStretch() + output_format_layout.addLayout(format_select_layout) + + # Help text + help_label = QLabel(_("settings.force_format_help")) + help_label.setWordWrap(True) + help_label.setStyleSheet("color: #cccccc; margin: 5px; font-size: 10px;") + output_format_layout.addWidget(help_label) + + output_format_group_box.setLayout(output_format_layout) + layout.addWidget(output_format_group_box) + # --- Auto-Update yt-dlp Section --- auto_update_group_box = QGroupBox(_("settings.auto_update_ytdlp")) auto_update_layout = QVBoxLayout() @@ -278,6 +320,15 @@ class DownloadSettingsDialog(QDialog): """Returns the index of the selected speed limit unit.""" return self.speed_limit_unit.currentIndex() + def get_force_format_enabled(self) -> bool: + """Returns whether force output format is enabled.""" + return self.force_format_checkbox.isChecked() + + def get_preferred_format(self) -> str: + """Returns the selected preferred format (lowercase).""" + format_map = {0: "mp4", 1: "webm", 2: "mkv"} + return format_map.get(self.format_combo.currentIndex(), "mp4") + def _create_styled_message_box(self, icon, title, text) -> QMessageBox: """Create a styled QMessageBox that matches the app theme.""" msg_box = QMessageBox(self) @@ -334,11 +385,17 @@ class DownloadSettingsDialog(QDialog): return enabled, frequency def accept(self) -> None: - """Override accept to save auto-update settings.""" + """Override accept to save auto-update and format settings.""" try: # Save auto-update settings enabled, frequency = self.get_auto_update_settings() + # Save output format settings + force_format = self.get_force_format_enabled() + preferred_format = self.get_preferred_format() + ConfigManager.set("force_output_format", force_format) + ConfigManager.set("preferred_output_format", preferred_format) + if update_auto_update_settings(enabled, frequency): QMessageBox.information( self, diff --git a/src/gui/ytsage_gui_main.py b/src/gui/ytsage_gui_main.py index ee4688b..28f3906 100644 --- a/src/gui/ytsage_gui_main.py +++ b/src/gui/ytsage_gui_main.py @@ -124,6 +124,9 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from self.speed_limit_unit_index = 0 # Store speed limit unit index (0: KB/s, 1: MB/s) self.download_section = None self.force_keyframes = False + # Initialize output format settings + self.force_output_format = ConfigManager.get("force_output_format") or False + self.preferred_output_format = ConfigManager.get("preferred_output_format") or "mp4" self.init_ui() self.setStyleSheet( @@ -768,8 +771,18 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from f"Speed limit updated to: {self.speed_limit_value} {['KB/s', 'MB/s'][self.speed_limit_unit_index] if self.speed_limit_value else 'None'}" ) + # Update Output Format Settings + new_force_format = dialog.get_force_format_enabled() + new_preferred_format = dialog.get_preferred_format() + format_changed = False + if new_force_format != self.force_output_format or new_preferred_format != self.preferred_output_format: + self.force_output_format = new_force_format + self.preferred_output_format = new_preferred_format + format_changed = True + logger.info(f"Output format settings updated - Force: {self.force_output_format}, Preferred: {self.preferred_output_format}") + # Update Tooltip if anything changed - if path_changed or limit_changed: + if path_changed or limit_changed or format_changed: limit_text = "None" if self.speed_limit_value: limit_text = f"{self.speed_limit_value} {['KB/s', 'MB/s'][self.speed_limit_unit_index]}" @@ -876,6 +889,8 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from force_keyframes=self.force_keyframes, # Pass the force keyframes setting proxy_url=self.proxy_url, # Pass the proxy URL geo_proxy_url=self.geo_proxy_url, # Pass the geo-verification proxy URL + force_output_format=self.force_output_format, # Pass force output format setting + preferred_output_format=self.preferred_output_format, # Pass preferred format ) # Connect signals diff --git a/src/utils/ytsage_config_manager.py b/src/utils/ytsage_config_manager.py index ff3a04b..f7b9f9e 100644 --- a/src/utils/ytsage_config_manager.py +++ b/src/utils/ytsage_config_manager.py @@ -80,6 +80,8 @@ class ConfigManager: "auto_update_frequency": "daily", "last_update_check": 0, "language": "en", + "force_output_format": False, + "preferred_output_format": "mp4", "cached_versions": { "ytdlp": {"version": None, "path": None, "last_check": 0, "path_mtime": 0}, "ffmpeg": {"version": None, "path": None, "last_check": 0, "path_mtime": 0},