Add localization for status and error messages
Replaced hardcoded status and error messages with localized strings throughout the downloader, main GUI, update dialog, and video info components. Updated English and Spanish language files with new keys for download statuses, errors, playlist info, and UI labels to improve internationalization and maintainability.
This commit is contained in:
@@ -154,6 +154,22 @@
|
|||||||
|
|
||||||
"download": {
|
"download": {
|
||||||
"preparing": "Preparing 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_enter_url": "Please enter a URL first.",
|
||||||
"please_set_path": "Please set a download path first.",
|
"please_set_path": "Please set a download path first.",
|
||||||
"please_enter_url_and_path": "Please enter a URL and set download path.",
|
"please_enter_url_and_path": "Please enter a URL and set download path.",
|
||||||
@@ -164,6 +180,7 @@
|
|||||||
"checking": "Checking for updates...",
|
"checking": "Checking for updates...",
|
||||||
"update_available": "Update available!\nCurrent version: {current}\nLatest version: {latest}",
|
"update_available": "Update available!\nCurrent version: {current}\nLatest version: {latest}",
|
||||||
"up_to_date": "yt-dlp is up to date (version {version})",
|
"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.",
|
"could_not_determine": "Could not determine versions.",
|
||||||
"error_comparing": "Error comparing versions: {error}",
|
"error_comparing": "Error comparing versions: {error}",
|
||||||
"update_available_failed": "Update available! (Comparison failed)\nCurrent: {current}\nLatest: {latest}",
|
"update_available_failed": "Update available! (Comparison failed)\nCurrent: {current}\nLatest: {latest}",
|
||||||
@@ -329,6 +346,34 @@
|
|||||||
"status": {
|
"status": {
|
||||||
"ready": "Ready",
|
"ready": "Ready",
|
||||||
"file_exists": "⚠️ File already exists",
|
"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..."
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+46
-5
@@ -113,15 +113,27 @@
|
|||||||
"geo_configured": "Geo proxy configurado"
|
"geo_configured": "Geo proxy configurado"
|
||||||
},
|
},
|
||||||
"download": {
|
"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!",
|
"completed": "✅ ¡Descarga completada!",
|
||||||
"video_completed": "✅ ¡Descarga de video completada!",
|
"video_completed": "✅ ¡Descarga de video completada!",
|
||||||
"audio_completed": "✅ ¡Descarga de audio completada!",
|
"audio_completed": "✅ ¡Descarga de audio completada!",
|
||||||
"subtitle_completed": "✅ ¡Descarga de subtítulos completada!",
|
"subtitle_completed": "✅ ¡Descarga de subtítulos completada!",
|
||||||
"please_set_path": "Por favor establece una ruta de descarga usando 'Cambiar Ruta'",
|
"completed_cleaning": "✅ ¡Descarga completada! Limpiando...",
|
||||||
"please_enter_url": "Por favor ingresa una URL",
|
"cancelled": "Descarga cancelada",
|
||||||
"please_enter_url_and_path": "Por favor ingresa URL y establece ruta de descarga",
|
"processing_playlist": "📋 Procesando datos de lista de reproducción...",
|
||||||
"please_select_format": "Por favor selecciona un formato"
|
"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": {
|
"formats": {
|
||||||
"show_formats": "Mostrar formatos:",
|
"show_formats": "Mostrar formatos:",
|
||||||
@@ -180,6 +192,7 @@
|
|||||||
"checking": "Verificando actualizaciones...",
|
"checking": "Verificando actualizaciones...",
|
||||||
"update_available": "¡Actualización disponible!\nVersión actual: {current}\nÚltima versión: {latest}",
|
"update_available": "¡Actualización disponible!\nVersión actual: {current}\nÚltima versión: {latest}",
|
||||||
"up_to_date": "yt-dlp está actualizado (versión {version})",
|
"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.",
|
"could_not_determine": "No se pudieron determinar las versiones.",
|
||||||
"error_comparing": "Error al comparar versiones: {error}",
|
"error_comparing": "Error al comparar versiones: {error}",
|
||||||
"update_available_failed": "¡Actualización disponible! (Comparación fallida)\nActual: {current}\nÚltima: {latest}",
|
"update_available_failed": "¡Actualización disponible! (Comparación fallida)\nActual: {current}\nÚltima: {latest}",
|
||||||
@@ -315,6 +328,34 @@
|
|||||||
"status": {
|
"status": {
|
||||||
"ready": "Listo",
|
"ready": "Listo",
|
||||||
"file_exists": "⚠️ El archivo ya existe",
|
"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..."
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,8 +9,12 @@ from PySide6.QtCore import QObject, QThread, Signal
|
|||||||
|
|
||||||
from src.core.ytsage_yt_dlp import get_yt_dlp_path
|
from src.core.ytsage_yt_dlp import get_yt_dlp_path
|
||||||
from src.utils.ytsage_constants import SUBPROCESS_CREATIONFLAGS
|
from src.utils.ytsage_constants import SUBPROCESS_CREATIONFLAGS
|
||||||
|
from src.utils.ytsage_localization import LocalizationManager
|
||||||
from src.utils.ytsage_logger import logger
|
from src.utils.ytsage_logger import logger
|
||||||
|
|
||||||
|
# Shorthand for localization
|
||||||
|
_ = LocalizationManager.get_text
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import yt_dlp # Keep yt_dlp import here - only downloader uses it.
|
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)
|
cmd_str = " ".join(shlex.quote(str(arg)) for arg in cmd)
|
||||||
logger.debug(f"Executing command: {cmd_str}")
|
logger.debug(f"Executing command: {cmd_str}")
|
||||||
|
|
||||||
self.status_signal.emit("🚀 Starting download...")
|
self.status_signal.emit(_("download.starting"))
|
||||||
self.progress_signal.emit(0)
|
self.progress_signal.emit(0)
|
||||||
|
|
||||||
# Start the process
|
# Start the process
|
||||||
@@ -454,7 +458,7 @@ class DownloadThread(QThread):
|
|||||||
# Add delay before cleanup to allow file handles to be released
|
# Add delay before cleanup to allow file handles to be released
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
self.cleanup_partial_files()
|
self.cleanup_partial_files()
|
||||||
self.status_signal.emit("Download cancelled")
|
self.status_signal.emit(_("download.cancelled"))
|
||||||
return
|
return
|
||||||
|
|
||||||
# Wait if paused
|
# Wait if paused
|
||||||
@@ -471,20 +475,20 @@ class DownloadThread(QThread):
|
|||||||
# return code 127 typically means command not found
|
# return code 127 typically means command not found
|
||||||
if return_code == 127:
|
if return_code == 127:
|
||||||
self.error_signal.emit(
|
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
|
return
|
||||||
|
|
||||||
if return_code == 0:
|
if return_code == 0:
|
||||||
self.progress_signal.emit(100)
|
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
|
# Clean up subtitle files if they were merged, with a small delay
|
||||||
# to ensure the embedding process has completed
|
# to ensure the embedding process has completed
|
||||||
if self.merge_subs:
|
if self.merge_subs:
|
||||||
# Add a significant delay to ensure ffmpeg has released all file handles
|
# Add a significant delay to ensure ffmpeg has released all file handles
|
||||||
# and any post-processing is complete
|
# 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
|
time.sleep(3) # Increased delay to 3 seconds
|
||||||
self.cleanup_subtitle_files()
|
self.cleanup_subtitle_files()
|
||||||
|
|
||||||
@@ -492,7 +496,7 @@ class DownloadThread(QThread):
|
|||||||
else:
|
else:
|
||||||
# Check if it was cancelled
|
# Check if it was cancelled
|
||||||
if self.cancelled:
|
if self.cancelled:
|
||||||
self.status_signal.emit("Download cancelled")
|
self.status_signal.emit(_("download.cancelled"))
|
||||||
else:
|
else:
|
||||||
# Provide more descriptive error message for possible yt-dlp conflicts
|
# Provide more descriptive error message for possible yt-dlp conflicts
|
||||||
if return_code == 1:
|
if return_code == 1:
|
||||||
@@ -549,19 +553,19 @@ class DownloadThread(QThread):
|
|||||||
|
|
||||||
# Check if this is explicitly an audio stream download
|
# Check if this is explicitly an audio stream download
|
||||||
if is_audio_download or "Downloading audio" in line:
|
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
|
# Video file extensions with likely video content
|
||||||
elif ext in [".mp4", ".webm", ".mkv", ".avi", ".mov", ".flv"]:
|
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
|
# Audio file extensions
|
||||||
elif ext in [".mp3", ".m4a", ".aac", ".wav", ".ogg", ".opus", ".flac"]:
|
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
|
# Subtitle file extensions
|
||||||
elif ext in [".vtt", ".srt", ".ass", ".ssa"]:
|
elif ext in [".vtt", ".srt", ".ass", ".ssa"]:
|
||||||
self.status_signal.emit(f"⏬ Downloading subtitle...")
|
self.status_signal.emit(_("download.downloading_subtitle"))
|
||||||
# Default case
|
# Default case
|
||||||
else:
|
else:
|
||||||
self.status_signal.emit(f"⏬ Downloading...")
|
self.status_signal.emit(_("download.downloading"))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Error extracting filename from line '{line}': {e}")
|
logger.exception(f"Error extracting filename from line '{line}': {e}")
|
||||||
self.status_signal.emit("⚡ Downloading...") # Fallback status
|
self.status_signal.emit("⚡ Downloading...") # Fallback status
|
||||||
@@ -569,11 +573,11 @@ class DownloadThread(QThread):
|
|||||||
|
|
||||||
# Check for specific download types in the output
|
# Check for specific download types in the output
|
||||||
if "Downloading video" in line:
|
if "Downloading video" in line:
|
||||||
self.status_signal.emit(f"⏬ Downloading video...")
|
self.status_signal.emit(_("download.downloading_video"))
|
||||||
return
|
return
|
||||||
|
|
||||||
elif "Downloading audio" in line:
|
elif "Downloading audio" in line:
|
||||||
self.status_signal.emit(f"⏬ Downloading audio...")
|
self.status_signal.emit(_("download.downloading_audio"))
|
||||||
return
|
return
|
||||||
|
|
||||||
# Detect subtitle file creation
|
# Detect subtitle file creation
|
||||||
@@ -596,7 +600,7 @@ class DownloadThread(QThread):
|
|||||||
subtitle_file = colon_parts[-1].strip()
|
subtitle_file = colon_parts[-1].strip()
|
||||||
|
|
||||||
# Show subtitle download message
|
# 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
|
# Store the subtitle file path for later deletion if merging is enabled
|
||||||
if self.merge_subs:
|
if self.merge_subs:
|
||||||
subtitle_path = Path(subtitle_file)
|
subtitle_path = Path(subtitle_file)
|
||||||
@@ -609,24 +613,24 @@ class DownloadThread(QThread):
|
|||||||
|
|
||||||
# Send status updates based on output line content
|
# Send status updates based on output line content
|
||||||
if "Downloading webpage" in line or "Extracting URL" in line:
|
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)
|
self.progress_signal.emit(0)
|
||||||
elif "Downloading API JSON" in line:
|
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)
|
self.progress_signal.emit(0)
|
||||||
elif "Downloading m3u8 information" in line:
|
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)
|
self.progress_signal.emit(0)
|
||||||
elif "[download] Downloading video " in line:
|
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:
|
elif "[download] Downloading audio " in line:
|
||||||
self.status_signal.emit("⏬ Downloading audio...")
|
self.status_signal.emit(_("download.downloading_audio"))
|
||||||
elif "Downloading format" in line:
|
elif "Downloading format" in line:
|
||||||
# Try to detect if it's audio or video format
|
# Try to detect if it's audio or video format
|
||||||
if " - audio only" in line:
|
if " - audio only" in line:
|
||||||
self.status_signal.emit("⏬ Downloading audio...")
|
self.status_signal.emit(_("download.downloading_audio"))
|
||||||
elif " - video only" in line:
|
elif " - video only" in line:
|
||||||
self.status_signal.emit("⏬ Downloading video...")
|
self.status_signal.emit(_("download.downloading_video"))
|
||||||
else:
|
else:
|
||||||
# Don't emit generic message - format is unclear
|
# Don't emit generic message - format is unclear
|
||||||
pass
|
pass
|
||||||
@@ -699,18 +703,18 @@ class DownloadThread(QThread):
|
|||||||
|
|
||||||
# Video file extensions
|
# Video file extensions
|
||||||
if ext in [".mp4", ".webm", ".mkv", ".avi", ".mov", ".flv"]:
|
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
|
# Audio file extensions
|
||||||
elif ext in [".mp3", ".m4a", ".aac", ".wav", ".ogg", ".opus", ".flac"]:
|
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
|
# Subtitle file extensions
|
||||||
elif ext in [".vtt", ".srt", ".ass", ".ssa"]:
|
elif ext in [".vtt", ".srt", ".ass", ".ssa"]:
|
||||||
self.status_signal.emit(f"✅ Subtitle download completed!")
|
self.status_signal.emit(_("download.subtitle_completed"))
|
||||||
# Default case
|
# Default case
|
||||||
else:
|
else:
|
||||||
self.status_signal.emit("✅ Download completed!")
|
self.status_signal.emit(_("download.completed"))
|
||||||
else:
|
else:
|
||||||
self.status_signal.emit("✅ Download completed!")
|
self.status_signal.emit(_("download.completed"))
|
||||||
|
|
||||||
self.update_details.emit("") # Clear details label on completion
|
self.update_details.emit("") # Clear details label on completion
|
||||||
|
|
||||||
|
|||||||
@@ -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_utils import get_ytdlp_version, load_config, save_config
|
||||||
from src.core.ytsage_yt_dlp import get_yt_dlp_path
|
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_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_localization import _
|
||||||
from src.utils.ytsage_logger import logger
|
from src.utils.ytsage_logger import logger
|
||||||
|
|
||||||
@@ -278,7 +282,7 @@ class UpdateThread(QThread):
|
|||||||
self.update_status.emit(f"❌ Error during pip update: {e}")
|
self.update_status.emit(f"❌ Error during pip update: {e}")
|
||||||
return False
|
return False
|
||||||
else:
|
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)
|
self.update_progress.emit(95)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
+30
-30
@@ -777,7 +777,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
# Ensure there are entries before proceeding
|
# Ensure there are entries before proceeding
|
||||||
if not self.playlist_entries:
|
if not self.playlist_entries:
|
||||||
logger.error("Playlist contains no valid videos.")
|
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_info_label_visible.emit(False)
|
||||||
self.signals.playlist_select_btn_visible.emit(False)
|
self.signals.playlist_select_btn_visible.emit(False)
|
||||||
return
|
return
|
||||||
@@ -786,7 +786,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
first_video_url = self.playlist_entries[0].get("url")
|
first_video_url = self.playlist_entries[0].get("url")
|
||||||
if not first_video_url:
|
if not first_video_url:
|
||||||
logger.error("Could not get URL for the first playlist video.")
|
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_info_label_visible.emit(False)
|
||||||
self.signals.playlist_select_btn_visible.emit(False)
|
self.signals.playlist_select_btn_visible.emit(False)
|
||||||
return
|
return
|
||||||
@@ -801,9 +801,9 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
self.signals.update_status.emit(user_friendly_error)
|
self.signals.update_status.emit(user_friendly_error)
|
||||||
return
|
return
|
||||||
# Update playlist info label text (remains the same)
|
# Update playlist info label text (remains the same)
|
||||||
playlist_text = (
|
playlist_text = _("playlist.display_format",
|
||||||
f"Playlist: {basic_info.get('title', 'Unknown Playlist')} | " f"{len(self.playlist_entries)} videos"
|
title=basic_info.get('title', _('playlist.unknown')),
|
||||||
) # Simplified label
|
count=len(self.playlist_entries))
|
||||||
|
|
||||||
# update signal method from QMetaObject.invokeMethod to signals
|
# update signal method from QMetaObject.invokeMethod to signals
|
||||||
self.signals.playlist_info_label_text.emit(playlist_text)
|
self.signals.playlist_info_label_text.emit(playlist_text)
|
||||||
@@ -897,7 +897,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Error in analysis: {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
|
# Ensure playlist UI is hidden on error too
|
||||||
# update signal method from QMetaObject.invokeMethod to signals
|
# update signal method from QMetaObject.invokeMethod to signals
|
||||||
self.signals.playlist_info_label_visible.emit(False)
|
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)}"
|
rate_limit = f"{int(limit_value * 1024 * 1024)}"
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# Use a signal to show error in status bar, similar to URL/Path errors
|
# 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
|
return
|
||||||
# --- End speed limit update ---
|
# --- End speed limit update ---
|
||||||
|
|
||||||
@@ -1089,7 +1089,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
self.toggle_download_controls(True)
|
self.toggle_download_controls(True)
|
||||||
self.pause_btn.setVisible(False)
|
self.pause_btn.setVisible(False)
|
||||||
self.cancel_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
|
self.download_details_label.setText("") # Clear details label on error
|
||||||
|
|
||||||
def update_progress_bar(self, value) -> None:
|
def update_progress_bar(self, value) -> None:
|
||||||
@@ -1104,11 +1104,11 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
if self.current_download:
|
if self.current_download:
|
||||||
self.current_download.paused = not self.current_download.paused
|
self.current_download.paused = not self.current_download.paused
|
||||||
if self.current_download.paused:
|
if self.current_download.paused:
|
||||||
self.pause_btn.setText("Resume")
|
self.pause_btn.setText(_("buttons.resume"))
|
||||||
self.signals.update_status.emit("Download paused")
|
self.signals.update_status.emit(_("download.paused"))
|
||||||
else:
|
else:
|
||||||
self.pause_btn.setText(_("buttons.pause"))
|
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:
|
def check_for_updates(self) -> None:
|
||||||
try:
|
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
|
def show_update_dialog(self, latest_version, release_url, changelog) -> None: # Added changelog parameter
|
||||||
msg = QDialog(self)
|
msg = QDialog(self)
|
||||||
msg.setWindowTitle("Update Available")
|
msg.setWindowTitle(_("update_dialog.title"))
|
||||||
msg.setMinimumWidth(600) # Increased width for better layout
|
msg.setMinimumWidth(600) # Increased width for better layout
|
||||||
msg.setMinimumHeight(450) # Increased height for better spacing
|
msg.setMinimumHeight(450) # Increased height for better spacing
|
||||||
|
|
||||||
@@ -1160,7 +1160,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
header_layout.addWidget(icon_label)
|
header_layout.addWidget(icon_label)
|
||||||
|
|
||||||
# Title
|
# Title
|
||||||
title_label = QLabel("<h2 style='color: #c90000; margin: 0;'>Update Available</h2>")
|
title_label = QLabel(f"<h2 style='color: #c90000; margin: 0;'>{_('update_dialog.title')}</h2>")
|
||||||
header_layout.addWidget(title_label)
|
header_layout.addWidget(title_label)
|
||||||
header_layout.addStretch()
|
header_layout.addStretch()
|
||||||
|
|
||||||
@@ -1466,16 +1466,16 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
|
|
||||||
# Video file extensions
|
# Video file extensions
|
||||||
if ext in [".mp4", ".webm", ".mkv", ".avi", ".mov", ".flv"]:
|
if ext in [".mp4", ".webm", ".mkv", ".avi", ".mov", ".flv"]:
|
||||||
self.status_label.setText(f"⚠️ Video file already exists")
|
self.status_label.setText(_("status.video_file_exists"))
|
||||||
# Audio file extensions
|
# Audio file extensions
|
||||||
elif ext in [".mp3", ".m4a", ".aac", ".wav", ".ogg", ".opus", ".flac"]:
|
elif ext in [".mp3", ".m4a", ".aac", ".wav", ".ogg", ".opus", ".flac"]:
|
||||||
self.status_label.setText(f"⚠️ Audio file already exists")
|
self.status_label.setText(_("status.audio_file_exists"))
|
||||||
# Subtitle file extensions
|
# Subtitle file extensions
|
||||||
elif ext in [".vtt", ".srt", ".ass", ".ssa"]:
|
elif ext in [".vtt", ".srt", ".ass", ".ssa"]:
|
||||||
self.status_label.setText(f"⚠️ Subtitle file already exists")
|
self.status_label.setText(_("status.subtitle_file_exists"))
|
||||||
# Default case
|
# Default case
|
||||||
else:
|
else:
|
||||||
self.status_label.setText("⚠️ File already exists")
|
self.status_label.setText(_("status.file_exists"))
|
||||||
|
|
||||||
# Show a simple message dialog
|
# Show a simple message dialog
|
||||||
msg_box = QMessageBox()
|
msg_box = QMessageBox()
|
||||||
@@ -1579,7 +1579,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
# Clear progress/status when controls are re-enabled
|
# Clear progress/status when controls are re-enabled
|
||||||
if enabled:
|
if enabled:
|
||||||
self.progress_bar.setValue(0)
|
self.progress_bar.setValue(0)
|
||||||
self.status_label.setText("Ready")
|
self.status_label.setText(_("status.ready"))
|
||||||
self.download_details_label.setText("") # Clear details label
|
self.download_details_label.setText("") # Clear details label
|
||||||
|
|
||||||
def handle_format_selection(self, button) -> None:
|
def handle_format_selection(self, button) -> None:
|
||||||
@@ -1655,7 +1655,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
def cancel_download(self) -> None:
|
def cancel_download(self) -> None:
|
||||||
if self.current_download:
|
if self.current_download:
|
||||||
self.current_download.cancelled = True
|
self.current_download.cancelled = True
|
||||||
self.status_label.setText("Cancelling download...") # Set status directly
|
self.status_label.setText(_("status.cancelling")) # Set status directly
|
||||||
self.download_details_label.setText("") # Clear details label on cancellation
|
self.download_details_label.setText("") # Clear details label on cancellation
|
||||||
|
|
||||||
def show_ffmpeg_dialog(self) -> None:
|
def show_ffmpeg_dialog(self) -> None:
|
||||||
@@ -1735,7 +1735,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
yt_dlp_path = get_yt_dlp_path()
|
yt_dlp_path = get_yt_dlp_path()
|
||||||
if not yt_dlp_path:
|
if not yt_dlp_path:
|
||||||
logger.error("yt-dlp executable not found. Please install yt-dlp first.")
|
logger.error("yt-dlp executable not found. Please install yt-dlp first.")
|
||||||
self.signals.update_status.emit("Error: yt-dlp executable not found. Please install yt-dlp first.")
|
self.signals.update_status.emit(_("errors.ytdlp_not_found"))
|
||||||
self.signals.playlist_info_label_visible.emit(False)
|
self.signals.playlist_info_label_visible.emit(False)
|
||||||
self.signals.playlist_select_btn_visible.emit(False)
|
self.signals.playlist_select_btn_visible.emit(False)
|
||||||
return
|
return
|
||||||
@@ -1769,7 +1769,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
|
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
logger.error(f"yt-dlp failed: {result.stderr}")
|
logger.error(f"yt-dlp failed: {result.stderr}")
|
||||||
self.signals.update_status.emit(f"Error: yt-dlp failed: {result.stderr}")
|
self.signals.update_status.emit(_("errors.ytdlp_failed", error=result.stderr))
|
||||||
self.signals.playlist_info_label_visible.emit(False)
|
self.signals.playlist_info_label_visible.emit(False)
|
||||||
self.signals.playlist_select_btn_visible.emit(False)
|
self.signals.playlist_select_btn_visible.emit(False)
|
||||||
return
|
return
|
||||||
@@ -1778,7 +1778,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
|
|
||||||
if not json_lines:
|
if not json_lines:
|
||||||
logger.error("No data returned from yt-dlp")
|
logger.error("No data returned from yt-dlp")
|
||||||
self.signals.update_status.emit("Error: No data returned from yt-dlp")
|
self.signals.update_status.emit(_("errors.no_data_returned"))
|
||||||
self.signals.playlist_info_label_visible.emit(False)
|
self.signals.playlist_info_label_visible.emit(False)
|
||||||
self.signals.playlist_select_btn_visible.emit(False)
|
self.signals.playlist_select_btn_visible.emit(False)
|
||||||
return
|
return
|
||||||
@@ -1787,7 +1787,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
first_info = json.loads(json_lines[0])
|
first_info = json.loads(json_lines[0])
|
||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
logger.error(f"Failed to parse yt-dlp output: {e}")
|
logger.error(f"Failed to parse yt-dlp output: {e}")
|
||||||
self.signals.update_status.emit(f"Error: Failed to parse yt-dlp output: {e}")
|
self.signals.update_status.emit(_("errors.parse_failed", error=str(e)))
|
||||||
self.signals.playlist_info_label_visible.emit(False)
|
self.signals.playlist_info_label_visible.emit(False)
|
||||||
self.signals.playlist_select_btn_visible.emit(False)
|
self.signals.playlist_select_btn_visible.emit(False)
|
||||||
return
|
return
|
||||||
@@ -1812,7 +1812,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
|
|
||||||
if not self.playlist_entries:
|
if not self.playlist_entries:
|
||||||
logger.error("Playlist contains no valid videos.")
|
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_info_label_visible.emit(False)
|
||||||
self.signals.playlist_select_btn_visible.emit(False)
|
self.signals.playlist_select_btn_visible.emit(False)
|
||||||
return
|
return
|
||||||
@@ -1821,9 +1821,9 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
self.video_info = self.playlist_entries[0]
|
self.video_info = self.playlist_entries[0]
|
||||||
|
|
||||||
# Update playlist info label
|
# Update playlist info label
|
||||||
playlist_text = (
|
playlist_text = _("playlist.display_format",
|
||||||
f"Playlist: {first_info.get('title', 'Unknown Playlist')} | " f"{len(self.playlist_entries)} videos"
|
title=first_info.get('title', _('playlist.unknown')),
|
||||||
)
|
count=len(self.playlist_entries))
|
||||||
# update signal method from QMetaObject.invokeMethod to signals
|
# update signal method from QMetaObject.invokeMethod to signals
|
||||||
self.signals.playlist_info_label_text.emit(playlist_text)
|
self.signals.playlist_info_label_text.emit(playlist_text)
|
||||||
self.signals.playlist_info_label_visible.emit(True)
|
self.signals.playlist_info_label_visible.emit(True)
|
||||||
@@ -1852,7 +1852,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
# Verify we have format information
|
# Verify we have format information
|
||||||
if not self.video_info or "formats" not in self.video_info:
|
if not self.video_info or "formats" not in self.video_info:
|
||||||
logger.error("No format information available")
|
logger.error("No format information available")
|
||||||
self.signals.update_status.emit("Error: No format information available.")
|
self.signals.update_status.emit(_("errors.no_format_info"))
|
||||||
self.signals.playlist_info_label_visible.emit(False)
|
self.signals.playlist_info_label_visible.emit(False)
|
||||||
self.signals.playlist_select_btn_visible.emit(False)
|
self.signals.playlist_select_btn_visible.emit(False)
|
||||||
return
|
return
|
||||||
@@ -1895,7 +1895,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
logger.error("Analysis timed out. Please try again.")
|
logger.error("Analysis timed out. Please try again.")
|
||||||
self.signals.update_status.emit("Error: Analysis timed out. Please try again.")
|
self.signals.update_status.emit(_("errors.analysis_timeout"))
|
||||||
self.signals.playlist_info_label_visible.emit(False)
|
self.signals.playlist_info_label_visible.emit(False)
|
||||||
self.signals.playlist_select_btn_visible.emit(False)
|
self.signals.playlist_select_btn_visible.emit(False)
|
||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
@@ -1905,6 +1905,6 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
|||||||
self.signals.playlist_select_btn_visible.emit(False)
|
self.signals.playlist_select_btn_visible.emit(False)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Analysis failed: {e}")
|
logger.error(f"Analysis failed: {e}")
|
||||||
self.signals.update_status.emit(f"Error: Analysis failed: {e}")
|
self.signals.update_status.emit(_("errors.analysis_failed", error=str(e)))
|
||||||
self.signals.playlist_info_label_visible.emit(False)
|
self.signals.playlist_info_label_visible.emit(False)
|
||||||
self.signals.playlist_select_btn_visible.emit(False)
|
self.signals.playlist_select_btn_visible.emit(False)
|
||||||
|
|||||||
@@ -218,10 +218,10 @@ class VideoInfoMixin:
|
|||||||
|
|
||||||
if hasattr(self, "is_playlist") and self.is_playlist:
|
if hasattr(self, "is_playlist") and self.is_playlist:
|
||||||
# Playlist Mode: Show playlist title and video count
|
# Playlist Mode: Show playlist title and video count
|
||||||
self.title_label.setText(self.playlist_info.get("title", "Unknown Playlist"))
|
self.title_label.setText(self.playlist_info.get("title", _("playlist.unknown")))
|
||||||
|
|
||||||
num_videos = len(getattr(self, "playlist_entries", []))
|
num_videos = len(getattr(self, "playlist_entries", []))
|
||||||
self.duration_label.setText(f"Total Videos: {num_videos}")
|
self.duration_label.setText(_("playlist.total_videos", count=num_videos))
|
||||||
|
|
||||||
# Hide video-specific info
|
# Hide video-specific info
|
||||||
self.channel_label.setText("")
|
self.channel_label.setText("")
|
||||||
@@ -295,7 +295,7 @@ class VideoInfoMixin:
|
|||||||
logger.info(f"Selected subtitles: {self.selected_subtitles}")
|
logger.info(f"Selected subtitles: {self.selected_subtitles}")
|
||||||
# Update UI to reflect selection
|
# Update UI to reflect selection
|
||||||
count = len(self.selected_subtitles)
|
count = len(self.selected_subtitles)
|
||||||
self.selected_subs_label.setText(f"{count} selected")
|
self.selected_subs_label.setText(_("subtitle_selection.count_selected", count=count))
|
||||||
self.subtitle_select_btn.setProperty("subtitlesSelected", count > 0)
|
self.subtitle_select_btn.setProperty("subtitlesSelected", count > 0)
|
||||||
|
|
||||||
# Enable/disable the merge checkbox in the parent window
|
# Enable/disable the merge checkbox in the parent window
|
||||||
|
|||||||
Reference in New Issue
Block a user