diff --git a/languages/en.json b/languages/en.json index bb5f6c6..1e69b7d 100644 --- a/languages/en.json +++ b/languages/en.json @@ -154,6 +154,22 @@ "download": { "preparing": "Preparing download...", + "starting": "🚀 Starting download...", + "fetching_info": "🔍 Fetching video information...", + "preparing_streams": "🎯 Preparing video streams...", + "downloading_audio": "⏬ Downloading audio...", + "downloading_video": "⏬ Downloading video...", + "downloading_subtitle": "⏬ Downloading subtitle...", + "downloading": "⏬ Downloading...", + "completed": "✅ Download completed!", + "video_completed": "✅ Video download completed!", + "audio_completed": "✅ Audio download completed!", + "subtitle_completed": "✅ Subtitle download completed!", + "completed_cleaning": "✅ Download completed! Cleaning up...", + "cancelled": "Download cancelled", + "processing_playlist": "📋 Processing playlist data...", + "paused": "Download paused", + "resumed": "Download resumed", "please_enter_url": "Please enter a URL first.", "please_set_path": "Please set a download path first.", "please_enter_url_and_path": "Please enter a URL and set download path.", @@ -164,6 +180,7 @@ "checking": "Checking for updates...", "update_available": "Update available!\nCurrent version: {current}\nLatest version: {latest}", "up_to_date": "yt-dlp is up to date (version {version})", + "already_up_to_date": "✅ yt-dlp is already up to date!", "could_not_determine": "Could not determine versions.", "error_comparing": "Error comparing versions: {error}", "update_available_failed": "Update available! (Comparison failed)\nCurrent: {current}\nLatest: {latest}", @@ -329,6 +346,34 @@ "status": { "ready": "Ready", "file_exists": "⚠️ File already exists", + "video_file_exists": "⚠️ Video file already exists", + "audio_file_exists": "⚠️ Audio file already exists", + "subtitle_file_exists": "⚠️ Subtitle file already exists", "cancelling": "Cancelling download..." + }, + "errors": { + "playlist_no_videos": "Error: Playlist contains no valid videos.", + "playlist_no_url": "Error: Could not get URL for the first playlist video.", + "ytdlp_not_found": "Error: yt-dlp executable not found. Please install yt-dlp first.", + "ytdlp_not_found_path": "Error: yt-dlp executable not found. This could be due to improper installation or a PATH issue.", + "no_data_returned": "Error: No data returned from yt-dlp", + "no_format_info": "Error: No format information available.", + "analysis_timeout": "Error: Analysis timed out. Please try again.", + "invalid_speed_limit": "❌ Error: Invalid speed limit value set in settings.", + "ytdlp_failed": "Error: yt-dlp failed: {error}", + "parse_failed": "Error: Failed to parse yt-dlp output: {error}", + "analysis_failed": "Error: Analysis failed: {error}", + "generic_error": "Error: {error}" + }, + "update_dialog": { + "title": "Update Available" + }, + "playlist": { + "unknown": "Unknown Playlist", + "total_videos": "Total Videos: {count}", + "display_format": "Playlist: {title} | {count} videos" + }, + "subtitle_selection": { + "count_selected": "{count} selected" } } \ No newline at end of file diff --git a/languages/es.json b/languages/es.json index 05dbf62..6dbbb41 100644 --- a/languages/es.json +++ b/languages/es.json @@ -113,15 +113,27 @@ "geo_configured": "Geo proxy configurado" }, "download": { - "preparing": "🚀 Preparando tu descarga...", + "preparing": "Preparando descarga...", + "starting": "🚀 Iniciando descarga...", + "fetching_info": "🔍 Obteniendo información del video...", + "preparing_streams": "🎯 Preparando flujos de video...", + "downloading_audio": "⏬ Descargando audio...", + "downloading_video": "⏬ Descargando video...", + "downloading_subtitle": "⏬ Descargando subtítulos...", + "downloading": "⏬ Descargando...", "completed": "✅ ¡Descarga completada!", "video_completed": "✅ ¡Descarga de video completada!", "audio_completed": "✅ ¡Descarga de audio completada!", "subtitle_completed": "✅ ¡Descarga de subtítulos completada!", - "please_set_path": "Por favor establece una ruta de descarga usando 'Cambiar Ruta'", - "please_enter_url": "Por favor ingresa una URL", - "please_enter_url_and_path": "Por favor ingresa URL y establece ruta de descarga", - "please_select_format": "Por favor selecciona un formato" + "completed_cleaning": "✅ ¡Descarga completada! Limpiando...", + "cancelled": "Descarga cancelada", + "processing_playlist": "📋 Procesando datos de lista de reproducción...", + "paused": "Descarga pausada", + "resumed": "Descarga reanudada", + "please_enter_url": "Por favor ingresa una URL primero.", + "please_set_path": "Por favor establece una ruta de descarga primero.", + "please_enter_url_and_path": "Por favor ingresa URL y establece ruta de descarga.", + "please_select_format": "Por favor selecciona un formato primero." }, "formats": { "show_formats": "Mostrar formatos:", @@ -180,6 +192,7 @@ "checking": "Verificando actualizaciones...", "update_available": "¡Actualización disponible!\nVersión actual: {current}\nÚltima versión: {latest}", "up_to_date": "yt-dlp está actualizado (versión {version})", + "already_up_to_date": "✅ ¡yt-dlp ya está actualizado!", "could_not_determine": "No se pudieron determinar las versiones.", "error_comparing": "Error al comparar versiones: {error}", "update_available_failed": "¡Actualización disponible! (Comparación fallida)\nActual: {current}\nÚltima: {latest}", @@ -315,6 +328,34 @@ "status": { "ready": "Listo", "file_exists": "⚠️ El archivo ya existe", + "video_file_exists": "⚠️ El archivo de video ya existe", + "audio_file_exists": "⚠️ El archivo de audio ya existe", + "subtitle_file_exists": "⚠️ El archivo de subtítulos ya existe", "cancelling": "Cancelando descarga..." + }, + "errors": { + "playlist_no_videos": "Error: La lista de reproducción no contiene videos válidos.", + "playlist_no_url": "Error: No se pudo obtener la URL del primer video de la lista.", + "ytdlp_not_found": "Error: Ejecutable de yt-dlp no encontrado. Por favor instala yt-dlp primero.", + "ytdlp_not_found_path": "Error: Ejecutable de yt-dlp no encontrado. Esto podría deberse a una instalación incorrecta o un problema de PATH.", + "no_data_returned": "Error: yt-dlp no devolvió datos", + "no_format_info": "Error: No hay información de formato disponible.", + "analysis_timeout": "Error: Se agotó el tiempo de análisis. Por favor inténtalo de nuevo.", + "invalid_speed_limit": "❌ Error: Valor de límite de velocidad inválido en configuración.", + "ytdlp_failed": "Error: yt-dlp falló: {error}", + "parse_failed": "Error: Falló al analizar salida de yt-dlp: {error}", + "analysis_failed": "Error: Análisis falló: {error}", + "generic_error": "Error: {error}" + }, + "update_dialog": { + "title": "Actualización disponible" + }, + "playlist": { + "unknown": "Lista de reproducción desconocida", + "total_videos": "Total de Videos: {count}", + "display_format": "Lista de reproducción: {title} | {count} videos" + }, + "subtitle_selection": { + "count_selected": "{count} seleccionados" } } \ No newline at end of file diff --git a/src/core/ytsage_downloader.py b/src/core/ytsage_downloader.py index 029b119..b62bf13 100644 --- a/src/core/ytsage_downloader.py +++ b/src/core/ytsage_downloader.py @@ -9,8 +9,12 @@ from PySide6.QtCore import QObject, QThread, Signal from src.core.ytsage_yt_dlp import get_yt_dlp_path from src.utils.ytsage_constants import SUBPROCESS_CREATIONFLAGS +from src.utils.ytsage_localization import LocalizationManager from src.utils.ytsage_logger import logger +# Shorthand for localization +_ = LocalizationManager.get_text + try: import yt_dlp # Keep yt_dlp import here - only downloader uses it. @@ -423,7 +427,7 @@ class DownloadThread(QThread): cmd_str = " ".join(shlex.quote(str(arg)) for arg in cmd) logger.debug(f"Executing command: {cmd_str}") - self.status_signal.emit("🚀 Starting download...") + self.status_signal.emit(_("download.starting")) self.progress_signal.emit(0) # Start the process @@ -454,7 +458,7 @@ class DownloadThread(QThread): # Add delay before cleanup to allow file handles to be released time.sleep(1) self.cleanup_partial_files() - self.status_signal.emit("Download cancelled") + self.status_signal.emit(_("download.cancelled")) return # Wait if paused @@ -471,20 +475,20 @@ class DownloadThread(QThread): # return code 127 typically means command not found if return_code == 127: self.error_signal.emit( - "Error: yt-dlp executable not found. This could be due to improper installation or a PATH issue." + _("errors.ytdlp_not_found_path") ) return if return_code == 0: self.progress_signal.emit(100) - self.status_signal.emit("✅ Download completed!") + self.status_signal.emit(_("download.completed")) # Clean up subtitle files if they were merged, with a small delay # to ensure the embedding process has completed if self.merge_subs: # Add a significant delay to ensure ffmpeg has released all file handles # and any post-processing is complete - self.status_signal.emit("✅ Download completed! Cleaning up...") + self.status_signal.emit(_("download.completed_cleaning")) time.sleep(3) # Increased delay to 3 seconds self.cleanup_subtitle_files() @@ -492,7 +496,7 @@ class DownloadThread(QThread): else: # Check if it was cancelled if self.cancelled: - self.status_signal.emit("Download cancelled") + self.status_signal.emit(_("download.cancelled")) else: # Provide more descriptive error message for possible yt-dlp conflicts if return_code == 1: @@ -549,19 +553,19 @@ class DownloadThread(QThread): # Check if this is explicitly an audio stream download if is_audio_download or "Downloading audio" in line: - self.status_signal.emit(f"⏬ Downloading audio...") + self.status_signal.emit(_("download.downloading_audio")) # Video file extensions with likely video content elif ext in [".mp4", ".webm", ".mkv", ".avi", ".mov", ".flv"]: - self.status_signal.emit(f"⏬ Downloading video...") + self.status_signal.emit(_("download.downloading_video")) # Audio file extensions elif ext in [".mp3", ".m4a", ".aac", ".wav", ".ogg", ".opus", ".flac"]: - self.status_signal.emit(f"⏬ Downloading audio...") + self.status_signal.emit(_("download.downloading_audio")) # Subtitle file extensions elif ext in [".vtt", ".srt", ".ass", ".ssa"]: - self.status_signal.emit(f"⏬ Downloading subtitle...") + self.status_signal.emit(_("download.downloading_subtitle")) # Default case else: - self.status_signal.emit(f"⏬ Downloading...") + self.status_signal.emit(_("download.downloading")) except Exception as e: logger.exception(f"Error extracting filename from line '{line}': {e}") self.status_signal.emit("⚡ Downloading...") # Fallback status @@ -569,11 +573,11 @@ class DownloadThread(QThread): # Check for specific download types in the output if "Downloading video" in line: - self.status_signal.emit(f"⏬ Downloading video...") + self.status_signal.emit(_("download.downloading_video")) return elif "Downloading audio" in line: - self.status_signal.emit(f"⏬ Downloading audio...") + self.status_signal.emit(_("download.downloading_audio")) return # Detect subtitle file creation @@ -596,7 +600,7 @@ class DownloadThread(QThread): subtitle_file = colon_parts[-1].strip() # Show subtitle download message - self.status_signal.emit(f"⏬ Downloading subtitle...") + self.status_signal.emit(_("download.downloading_subtitle")) # Store the subtitle file path for later deletion if merging is enabled if self.merge_subs: subtitle_path = Path(subtitle_file) @@ -609,24 +613,24 @@ class DownloadThread(QThread): # Send status updates based on output line content if "Downloading webpage" in line or "Extracting URL" in line: - self.status_signal.emit("🔍 Fetching video information...") + self.status_signal.emit(_("download.fetching_info")) self.progress_signal.emit(0) elif "Downloading API JSON" in line: - self.status_signal.emit("📋 Processing playlist data...") + self.status_signal.emit(_("download.processing_playlist")) self.progress_signal.emit(0) elif "Downloading m3u8 information" in line: - self.status_signal.emit("🎯 Preparing video streams...") + self.status_signal.emit(_("download.preparing_streams")) self.progress_signal.emit(0) elif "[download] Downloading video " in line: - self.status_signal.emit("⏬ Downloading video...") + self.status_signal.emit(_("download.downloading_video")) elif "[download] Downloading audio " in line: - self.status_signal.emit("⏬ Downloading audio...") + self.status_signal.emit(_("download.downloading_audio")) elif "Downloading format" in line: # Try to detect if it's audio or video format if " - audio only" in line: - self.status_signal.emit("⏬ Downloading audio...") + self.status_signal.emit(_("download.downloading_audio")) elif " - video only" in line: - self.status_signal.emit("⏬ Downloading video...") + self.status_signal.emit(_("download.downloading_video")) else: # Don't emit generic message - format is unclear pass @@ -699,18 +703,18 @@ class DownloadThread(QThread): # Video file extensions if ext in [".mp4", ".webm", ".mkv", ".avi", ".mov", ".flv"]: - self.status_signal.emit(f"✅ Video download completed!") + self.status_signal.emit(_("download.video_completed")) # Audio file extensions elif ext in [".mp3", ".m4a", ".aac", ".wav", ".ogg", ".opus", ".flac"]: - self.status_signal.emit(f"✅ Audio download completed!") + self.status_signal.emit(_("download.audio_completed")) # Subtitle file extensions elif ext in [".vtt", ".srt", ".ass", ".ssa"]: - self.status_signal.emit(f"✅ Subtitle download completed!") + self.status_signal.emit(_("download.subtitle_completed")) # Default case else: - self.status_signal.emit("✅ Download completed!") + self.status_signal.emit(_("download.completed")) else: - self.status_signal.emit("✅ Download completed!") + self.status_signal.emit(_("download.completed")) self.update_details.emit("") # Clear details label on completion diff --git a/src/gui/ytsage_gui_dialogs/ytsage_dialogs_update.py b/src/gui/ytsage_gui_dialogs/ytsage_dialogs_update.py index d0b38ce..532262c 100644 --- a/src/gui/ytsage_gui_dialogs/ytsage_dialogs_update.py +++ b/src/gui/ytsage_gui_dialogs/ytsage_dialogs_update.py @@ -18,6 +18,10 @@ from PySide6.QtWidgets import QDialog, QHBoxLayout, QLabel, QProgressBar, QPushB from src.core.ytsage_utils import get_ytdlp_version, load_config, save_config from src.core.ytsage_yt_dlp import get_yt_dlp_path from src.utils.ytsage_constants import OS_NAME, SUBPROCESS_CREATIONFLAGS, YTDLP_APP_BIN_PATH, YTDLP_DOWNLOAD_URL +from src.utils.ytsage_localization import LocalizationManager + +# Shorthand for localization +_ = LocalizationManager.get_text from src.utils.ytsage_localization import _ from src.utils.ytsage_logger import logger @@ -278,7 +282,7 @@ class UpdateThread(QThread): self.update_status.emit(f"❌ Error during pip update: {e}") return False else: - self.update_status.emit("✅ yt-dlp is already up to date!") + self.update_status.emit(_("update.already_up_to_date")) self.update_progress.emit(95) return True diff --git a/src/gui/ytsage_gui_main.py b/src/gui/ytsage_gui_main.py index 6d58965..f4a745c 100644 --- a/src/gui/ytsage_gui_main.py +++ b/src/gui/ytsage_gui_main.py @@ -777,7 +777,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from # Ensure there are entries before proceeding if not self.playlist_entries: logger.error("Playlist contains no valid videos.") - self.signals.update_status.emit("Error: Playlist contains no valid videos.") + self.signals.update_status.emit(_("errors.playlist_no_videos")) self.signals.playlist_info_label_visible.emit(False) self.signals.playlist_select_btn_visible.emit(False) return @@ -786,7 +786,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from first_video_url = self.playlist_entries[0].get("url") if not first_video_url: logger.error("Could not get URL for the first playlist video.") - self.signals.update_status.emit("Error: Could not get URL for the first playlist video.") + self.signals.update_status.emit(_("errors.playlist_no_url")) self.signals.playlist_info_label_visible.emit(False) self.signals.playlist_select_btn_visible.emit(False) return @@ -801,9 +801,9 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from self.signals.update_status.emit(user_friendly_error) return # Update playlist info label text (remains the same) - playlist_text = ( - f"Playlist: {basic_info.get('title', 'Unknown Playlist')} | " f"{len(self.playlist_entries)} videos" - ) # Simplified label + playlist_text = _("playlist.display_format", + title=basic_info.get('title', _('playlist.unknown')), + count=len(self.playlist_entries)) # update signal method from QMetaObject.invokeMethod to signals self.signals.playlist_info_label_text.emit(playlist_text) @@ -897,7 +897,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from except Exception as e: logger.exception(f"Error in analysis: {e}") - self.signals.update_status.emit(f"Error: {e}") + self.signals.update_status.emit(_("errors.generic_error", error=str(e))) # Ensure playlist UI is hidden on error too # update signal method from QMetaObject.invokeMethod to signals self.signals.playlist_info_label_visible.emit(False) @@ -999,7 +999,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from rate_limit = f"{int(limit_value * 1024 * 1024)}" except ValueError: # Use a signal to show error in status bar, similar to URL/Path errors - self.signals.update_status.emit("❌ Error: Invalid speed limit value set in settings.") + self.signals.update_status.emit(_("errors.invalid_speed_limit")) return # --- End speed limit update --- @@ -1089,7 +1089,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from self.toggle_download_controls(True) self.pause_btn.setVisible(False) self.cancel_btn.setVisible(False) - self.status_label.setText(f"Error: {error_message}") + self.status_label.setText(_("errors.generic_error", error=error_message)) self.download_details_label.setText("") # Clear details label on error def update_progress_bar(self, value) -> None: @@ -1104,11 +1104,11 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from if self.current_download: self.current_download.paused = not self.current_download.paused if self.current_download.paused: - self.pause_btn.setText("Resume") - self.signals.update_status.emit("Download paused") + self.pause_btn.setText(_("buttons.resume")) + self.signals.update_status.emit(_("download.paused")) else: self.pause_btn.setText(_("buttons.pause")) - self.signals.update_status.emit("Download resumed") + self.signals.update_status.emit(_("download.resumed")) def check_for_updates(self) -> None: try: @@ -1131,7 +1131,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from def show_update_dialog(self, latest_version, release_url, changelog) -> None: # Added changelog parameter msg = QDialog(self) - msg.setWindowTitle("Update Available") + msg.setWindowTitle(_("update_dialog.title")) msg.setMinimumWidth(600) # Increased width for better layout msg.setMinimumHeight(450) # Increased height for better spacing @@ -1160,7 +1160,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from header_layout.addWidget(icon_label) # Title - title_label = QLabel("