Add generic-mode URL validation and UI
Introduce a "generic_mode" option to allow validating/downloading from non-YouTube sites and wire it through the UI, config, and validation logic. Key changes: - Add generic_mode default to ConfigManager and persist setting from DownloadSettingsDialog (checkbox + help text). - Extend validate_video_url to accept a generic_mode flag and allow any http/https URL with a domain when enabled; pass this flag from Analysis and Download flows. - Update YTSageApp to load/save generic_mode, update URL placeholder and settings tooltip behavior, and refresh tooltip when settings change. - Improve robustness in FormatTableMixin: handle None/incorrect types for format_note, abr, resolution, ext, and codec values to avoid type errors and ensure consistent display. - Add localization entries for generic mode, placeholder, and related help text across multiple language files and update the in-app default localization strings. These changes enable broader site support via yt-dlp while hardening UI format handling and keeping user settings persistent.
This commit is contained in:
@@ -731,7 +731,7 @@ def parse_yt_dlp_error(error_message: str) -> str:
|
|||||||
return _("ytdlp_errors.generic_error", error=error_message)
|
return _("ytdlp_errors.generic_error", error=error_message)
|
||||||
|
|
||||||
|
|
||||||
def validate_video_url(url: str) -> tuple[bool, str]:
|
def validate_video_url(url: str, generic_mode: bool = False) -> tuple[bool, str]:
|
||||||
"""
|
"""
|
||||||
Validate a video URL for supported platforms.
|
Validate a video URL for supported platforms.
|
||||||
|
|
||||||
@@ -743,6 +743,10 @@ def validate_video_url(url: str) -> tuple[bool, str]:
|
|||||||
- is_valid: True if URL is valid, False otherwise
|
- is_valid: True if URL is valid, False otherwise
|
||||||
- error_message: Empty string if valid, error description if invalid
|
- error_message: Empty string if valid, error description if invalid
|
||||||
|
|
||||||
|
Args:
|
||||||
|
url: URL entered by the user.
|
||||||
|
generic_mode: When True, allow any http/https URL with a valid domain.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
>>> is_valid, error = validate_video_url("https://youtube.com/watch?v=xxx")
|
>>> is_valid, error = validate_video_url("https://youtube.com/watch?v=xxx")
|
||||||
>>> if not is_valid:
|
>>> if not is_valid:
|
||||||
@@ -771,6 +775,10 @@ def validate_video_url(url: str) -> tuple[bool, str]:
|
|||||||
if not parsed.netloc:
|
if not parsed.netloc:
|
||||||
return False, _("url_validation.missing_domain")
|
return False, _("url_validation.missing_domain")
|
||||||
|
|
||||||
|
if generic_mode:
|
||||||
|
logger.info(f"Generic mode enabled, allowing URL: {url}")
|
||||||
|
return True, ""
|
||||||
|
|
||||||
# YTSage focuses on YouTube and YouTube Music only
|
# YTSage focuses on YouTube and YouTube Music only
|
||||||
# Supported YouTube domains
|
# Supported YouTube domains
|
||||||
youtube_domains = [
|
youtube_domains = [
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ class AnalysisMixin:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Validate URL before processing
|
# Validate URL before processing
|
||||||
is_valid, error_message = validate_video_url(url)
|
is_valid, error_message = validate_video_url(url, generic_mode=self.generic_mode_enabled)
|
||||||
if not is_valid:
|
if not is_valid:
|
||||||
QMessageBox.warning(self, _("main_ui.error_title"), error_message)
|
QMessageBox.warning(self, _("main_ui.error_title"), error_message)
|
||||||
if hasattr(self, "animate_widget_shake"):
|
if hasattr(self, "animate_widget_shake"):
|
||||||
|
|||||||
@@ -195,6 +195,24 @@ class DownloadSettingsDialog(QDialog):
|
|||||||
speed_group_box.setLayout(speed_layout)
|
speed_group_box.setLayout(speed_layout)
|
||||||
layout.addWidget(speed_group_box)
|
layout.addWidget(speed_group_box)
|
||||||
|
|
||||||
|
# --- Generic Mode Section ---
|
||||||
|
generic_mode_group_box = QGroupBox(_("settings.generic_mode"))
|
||||||
|
generic_mode_layout = QVBoxLayout()
|
||||||
|
|
||||||
|
self.generic_mode_enabled = ConfigManager.get("generic_mode") or False
|
||||||
|
|
||||||
|
self.generic_mode_checkbox = QCheckBox(_("settings.enable_generic_mode"))
|
||||||
|
self.generic_mode_checkbox.setChecked(self.generic_mode_enabled)
|
||||||
|
generic_mode_layout.addWidget(self.generic_mode_checkbox)
|
||||||
|
|
||||||
|
generic_mode_help_label = QLabel(_("settings.generic_mode_help"))
|
||||||
|
generic_mode_help_label.setWordWrap(True)
|
||||||
|
generic_mode_help_label.setStyleSheet("color: #cccccc; margin: 5px; font-size: 11px;")
|
||||||
|
generic_mode_layout.addWidget(generic_mode_help_label)
|
||||||
|
|
||||||
|
generic_mode_group_box.setLayout(generic_mode_layout)
|
||||||
|
layout.addWidget(generic_mode_group_box)
|
||||||
|
|
||||||
# --- Output Format Settings Section ---
|
# --- Output Format Settings Section ---
|
||||||
output_format_group_box = QGroupBox(_("settings.output_format_settings"))
|
output_format_group_box = QGroupBox(_("settings.output_format_settings"))
|
||||||
output_format_layout = QVBoxLayout()
|
output_format_layout = QVBoxLayout()
|
||||||
@@ -349,6 +367,10 @@ class DownloadSettingsDialog(QDialog):
|
|||||||
"""Returns whether force output format is enabled."""
|
"""Returns whether force output format is enabled."""
|
||||||
return self.force_format_checkbox.isChecked()
|
return self.force_format_checkbox.isChecked()
|
||||||
|
|
||||||
|
def get_generic_mode_enabled(self) -> bool:
|
||||||
|
"""Returns whether generic mode is enabled."""
|
||||||
|
return self.generic_mode_checkbox.isChecked()
|
||||||
|
|
||||||
def get_preferred_format(self) -> str:
|
def get_preferred_format(self) -> str:
|
||||||
"""Returns the selected preferred format (lowercase)."""
|
"""Returns the selected preferred format (lowercase)."""
|
||||||
format_map = {0: "mp4", 1: "webm", 2: "mkv"}
|
format_map = {0: "mp4", 1: "webm", 2: "mkv"}
|
||||||
@@ -405,6 +427,8 @@ class DownloadSettingsDialog(QDialog):
|
|||||||
def accept(self) -> None:
|
def accept(self) -> None:
|
||||||
"""Override accept to save format settings."""
|
"""Override accept to save format settings."""
|
||||||
try:
|
try:
|
||||||
|
ConfigManager.set("generic_mode", self.get_generic_mode_enabled())
|
||||||
|
|
||||||
# Save output format settings
|
# Save output format settings
|
||||||
force_format = self.get_force_format_enabled()
|
force_format = self.get_force_format_enabled()
|
||||||
preferred_format = self.get_preferred_format()
|
preferred_format = self.get_preferred_format()
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ class FormatTableMixin:
|
|||||||
audio_formats = [
|
audio_formats = [
|
||||||
f
|
f
|
||||||
for f in self.all_formats
|
for f in self.all_formats
|
||||||
if (f.get("vcodec") == "none" or "audio only" in f.get("format_note", "").lower())
|
if (f.get("vcodec") == "none" or "audio only" in str(f.get("format_note") or "").lower())
|
||||||
and f.get("acodec") != "none"
|
and f.get("acodec") != "none"
|
||||||
and f.get("filesize") is not None
|
and f.get("filesize") is not None
|
||||||
]
|
]
|
||||||
@@ -224,7 +224,8 @@ class FormatTableMixin:
|
|||||||
except (ValueError, IndexError):
|
except (ValueError, IndexError):
|
||||||
return 0
|
return 0
|
||||||
else:
|
else:
|
||||||
return f.get("abr", 0)
|
abr = f.get("abr") or 0
|
||||||
|
return abr if isinstance(abr, (int, float)) else 0
|
||||||
|
|
||||||
video_formats.sort(key=get_quality, reverse=True)
|
video_formats.sort(key=get_quality, reverse=True)
|
||||||
audio_formats.sort(key=get_quality, reverse=True)
|
audio_formats.sort(key=get_quality, reverse=True)
|
||||||
@@ -320,7 +321,9 @@ class FormatTableMixin:
|
|||||||
self.format_table.setItem(row, 1, quality_item)
|
self.format_table.setItem(row, 1, quality_item)
|
||||||
|
|
||||||
# Resolution
|
# Resolution
|
||||||
resolution = f.get("resolution", "N/A")
|
resolution = f.get("resolution") or "N/A"
|
||||||
|
if not isinstance(resolution, str):
|
||||||
|
resolution = str(resolution)
|
||||||
|
|
||||||
if is_playlist_mode:
|
if is_playlist_mode:
|
||||||
# Column 2 for playlist mode: Resolution
|
# Column 2 for playlist mode: Resolution
|
||||||
@@ -361,7 +364,8 @@ class FormatTableMixin:
|
|||||||
self.format_table.setItem(row, 4, hdr_item)
|
self.format_table.setItem(row, 4, hdr_item)
|
||||||
else:
|
else:
|
||||||
# Extension for normal mode (column 2)
|
# Extension for normal mode (column 2)
|
||||||
self.format_table.setItem(row, 2, QTableWidgetItem(f.get("ext", "").upper()))
|
extension = str(f.get("ext") or "")
|
||||||
|
self.format_table.setItem(row, 2, QTableWidgetItem(extension.upper()))
|
||||||
|
|
||||||
# Audio Status column
|
# Audio Status column
|
||||||
needs_audio = f.get("acodec") == "none" and f.get("vcodec") != "none"
|
needs_audio = f.get("acodec") == "none" and f.get("vcodec") != "none"
|
||||||
@@ -387,11 +391,11 @@ class FormatTableMixin:
|
|||||||
|
|
||||||
# Column 5: Codec
|
# Column 5: Codec
|
||||||
if f.get("vcodec") == "none":
|
if f.get("vcodec") == "none":
|
||||||
codec = f.get("acodec", "N/A")
|
codec = str(f.get("acodec") or "N/A")
|
||||||
else:
|
else:
|
||||||
codec = f"{f.get('vcodec', 'N/A')}"
|
codec = str(f.get("vcodec") or "N/A")
|
||||||
if f.get("acodec") != "none":
|
if f.get("acodec") != "none":
|
||||||
codec += f" / {f.get('acodec', 'N/A')}"
|
codec += f" / {str(f.get('acodec') or 'N/A')}"
|
||||||
self.format_table.setItem(row, 5, QTableWidgetItem(codec))
|
self.format_table.setItem(row, 5, QTableWidgetItem(codec))
|
||||||
|
|
||||||
# Column 7: FPS (Frame Rate)
|
# Column 7: FPS (Frame Rate)
|
||||||
@@ -469,7 +473,9 @@ class FormatTableMixin:
|
|||||||
|
|
||||||
if format_info.get("vcodec") == "none":
|
if format_info.get("vcodec") == "none":
|
||||||
# Audio quality
|
# Audio quality
|
||||||
abr = format_info.get("abr", 0)
|
abr = format_info.get("abr") or 0
|
||||||
|
if not isinstance(abr, (int, float)):
|
||||||
|
abr = 0
|
||||||
if abr >= 256:
|
if abr >= 256:
|
||||||
return _("formats.best_audio")
|
return _("formats.best_audio")
|
||||||
elif abr >= 192:
|
elif abr >= 192:
|
||||||
|
|||||||
@@ -257,6 +257,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin, AnalysisMixin):
|
|||||||
self.preferred_output_format = ConfigManager.get("preferred_output_format") or "mp4"
|
self.preferred_output_format = ConfigManager.get("preferred_output_format") or "mp4"
|
||||||
self.force_audio_format = ConfigManager.get("force_audio_format") or False
|
self.force_audio_format = ConfigManager.get("force_audio_format") or False
|
||||||
self.preferred_audio_format = ConfigManager.get("preferred_audio_format") or "best"
|
self.preferred_audio_format = ConfigManager.get("preferred_audio_format") or "best"
|
||||||
|
self.generic_mode_enabled = ConfigManager.get("generic_mode") or False
|
||||||
# Track if video analysis is completed
|
# Track if video analysis is completed
|
||||||
self.analysis_completed = False
|
self.analysis_completed = False
|
||||||
|
|
||||||
@@ -351,7 +352,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin, AnalysisMixin):
|
|||||||
url_layout.setSpacing(10)
|
url_layout.setSpacing(10)
|
||||||
|
|
||||||
self.url_input = QLineEdit()
|
self.url_input = QLineEdit()
|
||||||
self.url_input.setPlaceholderText(_("main_ui.url_placeholder"))
|
self._update_url_placeholder()
|
||||||
self.url_input.returnPressed.connect(self.analyze_url) # Analyze on Enter key
|
self.url_input.returnPressed.connect(self.analyze_url) # Analyze on Enter key
|
||||||
self.url_input.textChanged.connect(self._on_url_text_changed) # Enable/disable analyze button
|
self.url_input.textChanged.connect(self._on_url_text_changed) # Enable/disable analyze button
|
||||||
self.url_input.setMinimumHeight(42)
|
self.url_input.setMinimumHeight(42)
|
||||||
@@ -489,13 +490,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin, AnalysisMixin):
|
|||||||
# --- Rename Path Button to Settings Button ---
|
# --- Rename Path Button to Settings Button ---
|
||||||
self.settings_button = QPushButton(_("buttons.download_settings")) # Renamed button
|
self.settings_button = QPushButton(_("buttons.download_settings")) # Renamed button
|
||||||
self.settings_button.clicked.connect(self.show_download_settings_dialog) # Renamed method
|
self.settings_button.clicked.connect(self.show_download_settings_dialog) # Renamed method
|
||||||
self.settings_button.setToolTip(
|
self._update_settings_tooltip()
|
||||||
_(
|
|
||||||
"main_ui.settings_tooltip",
|
|
||||||
path=self.last_path,
|
|
||||||
speed_limit=_("main_ui.speed_limit_none"),
|
|
||||||
)
|
|
||||||
) # Update initial tooltip
|
|
||||||
# --- End Settings Button ---
|
# --- End Settings Button ---
|
||||||
|
|
||||||
self.download_btn = QPushButton(_("buttons.download"))
|
self.download_btn = QPushButton(_("buttons.download"))
|
||||||
@@ -581,6 +576,27 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin, AnalysisMixin):
|
|||||||
"""Enable or disable the Analyze button based on URL input content."""
|
"""Enable or disable the Analyze button based on URL input content."""
|
||||||
self.analyze_button.setEnabled(bool(text.strip()))
|
self.analyze_button.setEnabled(bool(text.strip()))
|
||||||
|
|
||||||
|
def _get_speed_limit_tooltip_text(self) -> str:
|
||||||
|
"""Return the current speed limit string for the settings tooltip."""
|
||||||
|
if self.speed_limit_value:
|
||||||
|
return f"{self.speed_limit_value} {['KB/s', 'MB/s'][self.speed_limit_unit_index]}"
|
||||||
|
return _("main_ui.speed_limit_none")
|
||||||
|
|
||||||
|
def _update_settings_tooltip(self) -> None:
|
||||||
|
"""Refresh the download settings tooltip text."""
|
||||||
|
self.settings_button.setToolTip(
|
||||||
|
_(
|
||||||
|
"main_ui.settings_tooltip",
|
||||||
|
path=self.last_path,
|
||||||
|
speed_limit=self._get_speed_limit_tooltip_text(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _update_url_placeholder(self) -> None:
|
||||||
|
"""Update the URL placeholder based on the selected validation mode."""
|
||||||
|
placeholder_key = "main_ui.url_placeholder_generic" if self.generic_mode_enabled else "main_ui.url_placeholder"
|
||||||
|
self.url_input.setPlaceholderText(_(placeholder_key))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def paste_url(self) -> None:
|
def paste_url(self) -> None:
|
||||||
@@ -631,18 +647,18 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin, AnalysisMixin):
|
|||||||
audio_format_changed = True
|
audio_format_changed = True
|
||||||
logger.info(f"Audio format settings updated - Force: {self.force_audio_format}, Preferred: {self.preferred_audio_format}")
|
logger.info(f"Audio format settings updated - Force: {self.force_audio_format}, Preferred: {self.preferred_audio_format}")
|
||||||
|
|
||||||
|
# Update Generic Mode Setting
|
||||||
|
new_generic_mode = dialog.get_generic_mode_enabled()
|
||||||
|
generic_mode_changed = False
|
||||||
|
if new_generic_mode != self.generic_mode_enabled:
|
||||||
|
self.generic_mode_enabled = new_generic_mode
|
||||||
|
generic_mode_changed = True
|
||||||
|
self._update_url_placeholder()
|
||||||
|
logger.info(f"Generic mode updated - Enabled: {self.generic_mode_enabled}")
|
||||||
|
|
||||||
# Update Tooltip if anything changed
|
# Update Tooltip if anything changed
|
||||||
if path_changed or limit_changed or format_changed or audio_format_changed:
|
if path_changed or limit_changed or format_changed or audio_format_changed or generic_mode_changed:
|
||||||
limit_text = _("main_ui.speed_limit_none")
|
self._update_settings_tooltip()
|
||||||
if self.speed_limit_value:
|
|
||||||
limit_text = f"{self.speed_limit_value} {['KB/s', 'MB/s'][self.speed_limit_unit_index]}"
|
|
||||||
self.settings_button.setToolTip(
|
|
||||||
_(
|
|
||||||
"main_ui.settings_tooltip",
|
|
||||||
path=self.last_path,
|
|
||||||
speed_limit=limit_text,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def start_download(self) -> None:
|
def start_download(self) -> None:
|
||||||
if self.is_updating_ytdlp:
|
if self.is_updating_ytdlp:
|
||||||
@@ -669,7 +685,7 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin, AnalysisMixin):
|
|||||||
# --- End Path Change ---
|
# --- End Path Change ---
|
||||||
|
|
||||||
# Validate URL before starting download
|
# Validate URL before starting download
|
||||||
is_valid, error_message = validate_video_url(url)
|
is_valid, error_message = validate_video_url(url, generic_mode=self.generic_mode_enabled)
|
||||||
if not is_valid:
|
if not is_valid:
|
||||||
QMessageBox.warning(self, _("main_ui.error_title"), error_message)
|
QMessageBox.warning(self, _("main_ui.error_title"), error_message)
|
||||||
self.animate_widget_shake(self.url_input)
|
self.animate_widget_shake(self.url_input)
|
||||||
|
|||||||
@@ -274,6 +274,9 @@
|
|||||||
"browse": "تصفح...",
|
"browse": "تصفح...",
|
||||||
"speed_limit": "حد السرعة",
|
"speed_limit": "حد السرعة",
|
||||||
"speed_limit_placeholder": "بدون",
|
"speed_limit_placeholder": "بدون",
|
||||||
|
"generic_mode": "الوضع العام",
|
||||||
|
"enable_generic_mode": "تفعيل الوضع العام (دعم المواقع غير التابعة ليوتيوب)",
|
||||||
|
"generic_mode_help": "يسمح بالتنزيل من Dailymotion وCBC Gem ومواقع أخرى يدعمها yt-dlp.",
|
||||||
"auto_update_ytdlp": "التحديثات التلقائية لـ yt-dlp",
|
"auto_update_ytdlp": "التحديثات التلقائية لـ yt-dlp",
|
||||||
"enable_auto_updates": "تفعيل التحديثات التلقائية لـ yt-dlp",
|
"enable_auto_updates": "تفعيل التحديثات التلقائية لـ yt-dlp",
|
||||||
"update_frequency": "تكرار التحديثات:",
|
"update_frequency": "تكرار التحديثات:",
|
||||||
@@ -337,6 +340,7 @@
|
|||||||
},
|
},
|
||||||
"main_ui": {
|
"main_ui": {
|
||||||
"url_placeholder": "أدخل رابط فيديو يوتيوب أو قائمة تشغيل",
|
"url_placeholder": "أدخل رابط فيديو يوتيوب أو قائمة تشغيل",
|
||||||
|
"url_placeholder_generic": "أدخل رابط فيديو أو قائمة تشغيل من أي موقع مدعوم",
|
||||||
"merge_subtitles": "دمج الترجمات",
|
"merge_subtitles": "دمج الترجمات",
|
||||||
"save_thumbnail": "حفظ الصورة المصغرة",
|
"save_thumbnail": "حفظ الصورة المصغرة",
|
||||||
"save_description": "حفظ الوصف",
|
"save_description": "حفظ الوصف",
|
||||||
|
|||||||
@@ -274,6 +274,9 @@
|
|||||||
"browse": "Durchsuchen...",
|
"browse": "Durchsuchen...",
|
||||||
"speed_limit": "Geschwindigkeitsbegrenzung",
|
"speed_limit": "Geschwindigkeitsbegrenzung",
|
||||||
"speed_limit_placeholder": "Keine",
|
"speed_limit_placeholder": "Keine",
|
||||||
|
"generic_mode": "Generischer Modus",
|
||||||
|
"enable_generic_mode": "Generischen Modus aktivieren (Unterstützung für Nicht-YouTube-Seiten)",
|
||||||
|
"generic_mode_help": "Ermöglicht Downloads von Dailymotion, CBC Gem und anderen von yt-dlp unterstützten Seiten.",
|
||||||
"auto_update_ytdlp": "yt-dlp automatisch aktualisieren",
|
"auto_update_ytdlp": "yt-dlp automatisch aktualisieren",
|
||||||
"enable_auto_updates": "Automatische yt-dlp-Updates aktivieren",
|
"enable_auto_updates": "Automatische yt-dlp-Updates aktivieren",
|
||||||
"update_frequency": "Update-Häufigkeit:",
|
"update_frequency": "Update-Häufigkeit:",
|
||||||
@@ -337,6 +340,7 @@
|
|||||||
},
|
},
|
||||||
"main_ui": {
|
"main_ui": {
|
||||||
"url_placeholder": "YouTube-Video- oder Playlist-URL eingeben",
|
"url_placeholder": "YouTube-Video- oder Playlist-URL eingeben",
|
||||||
|
"url_placeholder_generic": "Video- oder Playlist-URL von jeder unterstützten Seite eingeben",
|
||||||
"merge_subtitles": "Untertitel zusammenführen",
|
"merge_subtitles": "Untertitel zusammenführen",
|
||||||
"save_thumbnail": "Thumbnail speichern",
|
"save_thumbnail": "Thumbnail speichern",
|
||||||
"save_description": "Beschreibung speichern",
|
"save_description": "Beschreibung speichern",
|
||||||
|
|||||||
@@ -275,6 +275,9 @@
|
|||||||
"browse": "Browse...",
|
"browse": "Browse...",
|
||||||
"speed_limit": "Speed Limit",
|
"speed_limit": "Speed Limit",
|
||||||
"speed_limit_placeholder": "None",
|
"speed_limit_placeholder": "None",
|
||||||
|
"generic_mode": "Generic Mode",
|
||||||
|
"enable_generic_mode": "Enable Generic Mode (support non-YouTube sites)",
|
||||||
|
"generic_mode_help": "Allows downloading from Dailymotion, CBC Gem, and other sites supported by yt-dlp.",
|
||||||
"auto_update_ytdlp": "Auto-Update yt-dlp",
|
"auto_update_ytdlp": "Auto-Update yt-dlp",
|
||||||
"enable_auto_updates": "Enable automatic yt-dlp updates",
|
"enable_auto_updates": "Enable automatic yt-dlp updates",
|
||||||
"update_frequency": "Update frequency:",
|
"update_frequency": "Update frequency:",
|
||||||
@@ -338,6 +341,7 @@
|
|||||||
},
|
},
|
||||||
"main_ui": {
|
"main_ui": {
|
||||||
"url_placeholder": "Enter YouTube video or playlist URL",
|
"url_placeholder": "Enter YouTube video or playlist URL",
|
||||||
|
"url_placeholder_generic": "Enter video or playlist URL from any supported site",
|
||||||
"merge_subtitles": "Merge Subtitles",
|
"merge_subtitles": "Merge Subtitles",
|
||||||
"save_thumbnail": "Save Thumbnail",
|
"save_thumbnail": "Save Thumbnail",
|
||||||
"save_description": "Save Description",
|
"save_description": "Save Description",
|
||||||
|
|||||||
@@ -257,6 +257,9 @@
|
|||||||
"browse": "Examinar...",
|
"browse": "Examinar...",
|
||||||
"speed_limit": "Límite de Velocidad",
|
"speed_limit": "Límite de Velocidad",
|
||||||
"speed_limit_placeholder": "Ninguno",
|
"speed_limit_placeholder": "Ninguno",
|
||||||
|
"generic_mode": "Modo genérico",
|
||||||
|
"enable_generic_mode": "Habilitar modo genérico (compatibilidad con sitios que no son YouTube)",
|
||||||
|
"generic_mode_help": "Permite descargar desde Dailymotion, CBC Gem y otros sitios compatibles con yt-dlp.",
|
||||||
"auto_update_ytdlp": "Auto-Actualizar yt-dlp",
|
"auto_update_ytdlp": "Auto-Actualizar yt-dlp",
|
||||||
"enable_auto_updates": "Habilitar actualizaciones automáticas de yt-dlp",
|
"enable_auto_updates": "Habilitar actualizaciones automáticas de yt-dlp",
|
||||||
"update_frequency": "Frecuencia de actualización:",
|
"update_frequency": "Frecuencia de actualización:",
|
||||||
@@ -320,6 +323,7 @@
|
|||||||
},
|
},
|
||||||
"main_ui": {
|
"main_ui": {
|
||||||
"url_placeholder": "Ingresa URL de video o lista de YouTube",
|
"url_placeholder": "Ingresa URL de video o lista de YouTube",
|
||||||
|
"url_placeholder_generic": "Ingresa la URL de un video o una lista de cualquier sitio compatible",
|
||||||
"merge_subtitles": "Combinar Subtítulos",
|
"merge_subtitles": "Combinar Subtítulos",
|
||||||
"save_thumbnail": "Guardar Miniatura",
|
"save_thumbnail": "Guardar Miniatura",
|
||||||
"save_description": "Guardar Descripción",
|
"save_description": "Guardar Descripción",
|
||||||
|
|||||||
@@ -274,6 +274,9 @@
|
|||||||
"browse": "Parcourir...",
|
"browse": "Parcourir...",
|
||||||
"speed_limit": "Limite de vitesse",
|
"speed_limit": "Limite de vitesse",
|
||||||
"speed_limit_placeholder": "Aucune",
|
"speed_limit_placeholder": "Aucune",
|
||||||
|
"generic_mode": "Mode générique",
|
||||||
|
"enable_generic_mode": "Activer le mode générique (prise en charge des sites non-YouTube)",
|
||||||
|
"generic_mode_help": "Permet le téléchargement depuis Dailymotion, CBC Gem et d'autres sites pris en charge par yt-dlp.",
|
||||||
"auto_update_ytdlp": "Mise à jour automatique de yt-dlp",
|
"auto_update_ytdlp": "Mise à jour automatique de yt-dlp",
|
||||||
"enable_auto_updates": "Activer les mises à jour automatiques de yt-dlp",
|
"enable_auto_updates": "Activer les mises à jour automatiques de yt-dlp",
|
||||||
"update_frequency": "Fréquence de mise à jour :",
|
"update_frequency": "Fréquence de mise à jour :",
|
||||||
@@ -337,6 +340,7 @@
|
|||||||
},
|
},
|
||||||
"main_ui": {
|
"main_ui": {
|
||||||
"url_placeholder": "Entrer l'URL de la vidéo ou de la playlist YouTube",
|
"url_placeholder": "Entrer l'URL de la vidéo ou de la playlist YouTube",
|
||||||
|
"url_placeholder_generic": "Entrez l'URL d'une vidéo ou d'une playlist depuis n'importe quel site pris en charge",
|
||||||
"merge_subtitles": "Fusionner les sous-titres",
|
"merge_subtitles": "Fusionner les sous-titres",
|
||||||
"save_thumbnail": "Sauvegarder la miniature",
|
"save_thumbnail": "Sauvegarder la miniature",
|
||||||
"save_description": "Sauvegarder la description",
|
"save_description": "Sauvegarder la description",
|
||||||
|
|||||||
@@ -274,6 +274,9 @@
|
|||||||
"browse": "ब्राउज़ करें...",
|
"browse": "ब्राउज़ करें...",
|
||||||
"speed_limit": "गति सीमा",
|
"speed_limit": "गति सीमा",
|
||||||
"speed_limit_placeholder": "कोई नहीं",
|
"speed_limit_placeholder": "कोई नहीं",
|
||||||
|
"generic_mode": "जेनेरिक मोड",
|
||||||
|
"enable_generic_mode": "जेनेरिक मोड सक्षम करें (गैर-YouTube साइटों के लिए समर्थन)",
|
||||||
|
"generic_mode_help": "Dailymotion, CBC Gem और yt-dlp द्वारा समर्थित अन्य साइटों से डाउनलोड की अनुमति देता है।",
|
||||||
"auto_update_ytdlp": "yt-dlp स्वचालित अपडेट",
|
"auto_update_ytdlp": "yt-dlp स्वचालित अपडेट",
|
||||||
"enable_auto_updates": "yt-dlp स्वचालित अपडेट सक्षम करें",
|
"enable_auto_updates": "yt-dlp स्वचालित अपडेट सक्षम करें",
|
||||||
"update_frequency": "अपडेट आवृत्ति:",
|
"update_frequency": "अपडेट आवृत्ति:",
|
||||||
@@ -337,6 +340,7 @@
|
|||||||
},
|
},
|
||||||
"main_ui": {
|
"main_ui": {
|
||||||
"url_placeholder": "YouTube वीडियो या प्लेलिस्ट URL दर्ज करें",
|
"url_placeholder": "YouTube वीडियो या प्लेलिस्ट URL दर्ज करें",
|
||||||
|
"url_placeholder_generic": "किसी भी समर्थित साइट से वीडियो या प्लेलिस्ट URL दर्ज करें",
|
||||||
"merge_subtitles": "उपशीर्षक मर्ज करें",
|
"merge_subtitles": "उपशीर्षक मर्ज करें",
|
||||||
"save_thumbnail": "थंबनेल सेव करें",
|
"save_thumbnail": "थंबनेल सेव करें",
|
||||||
"save_description": "विवरण सेव करें",
|
"save_description": "विवरण सेव करें",
|
||||||
|
|||||||
@@ -274,6 +274,9 @@
|
|||||||
"browse": "Jelajahi...",
|
"browse": "Jelajahi...",
|
||||||
"speed_limit": "Batas kecepatan",
|
"speed_limit": "Batas kecepatan",
|
||||||
"speed_limit_placeholder": "Tidak ada",
|
"speed_limit_placeholder": "Tidak ada",
|
||||||
|
"generic_mode": "Mode generik",
|
||||||
|
"enable_generic_mode": "Aktifkan mode generik (mendukung situs non-YouTube)",
|
||||||
|
"generic_mode_help": "Memungkinkan pengunduhan dari Dailymotion, CBC Gem, dan situs lain yang didukung oleh yt-dlp.",
|
||||||
"auto_update_ytdlp": "Pembaruan otomatis yt-dlp",
|
"auto_update_ytdlp": "Pembaruan otomatis yt-dlp",
|
||||||
"enable_auto_updates": "Aktifkan pembaruan otomatis yt-dlp",
|
"enable_auto_updates": "Aktifkan pembaruan otomatis yt-dlp",
|
||||||
"update_frequency": "Frekuensi pembaruan:",
|
"update_frequency": "Frekuensi pembaruan:",
|
||||||
@@ -337,6 +340,7 @@
|
|||||||
},
|
},
|
||||||
"main_ui": {
|
"main_ui": {
|
||||||
"url_placeholder": "Masukkan URL video atau playlist YouTube",
|
"url_placeholder": "Masukkan URL video atau playlist YouTube",
|
||||||
|
"url_placeholder_generic": "Masukkan URL video atau playlist dari situs apa pun yang didukung",
|
||||||
"merge_subtitles": "Gabungkan subtitle",
|
"merge_subtitles": "Gabungkan subtitle",
|
||||||
"save_thumbnail": "Simpan thumbnail",
|
"save_thumbnail": "Simpan thumbnail",
|
||||||
"save_description": "Simpan deskripsi",
|
"save_description": "Simpan deskripsi",
|
||||||
|
|||||||
@@ -274,6 +274,9 @@
|
|||||||
"browse": "Sfoglia...",
|
"browse": "Sfoglia...",
|
||||||
"speed_limit": "Limite velocità",
|
"speed_limit": "Limite velocità",
|
||||||
"speed_limit_placeholder": "Nessuno",
|
"speed_limit_placeholder": "Nessuno",
|
||||||
|
"generic_mode": "Modalità generica",
|
||||||
|
"enable_generic_mode": "Abilita modalità generica (supporta siti non YouTube)",
|
||||||
|
"generic_mode_help": "Consente il download da Dailymotion, CBC Gem e altri siti supportati da yt-dlp.",
|
||||||
"auto_update_ytdlp": "Aggiornamenti automatici yt-dlp",
|
"auto_update_ytdlp": "Aggiornamenti automatici yt-dlp",
|
||||||
"enable_auto_updates": "Abilita aggiornamenti automatici yt-dlp",
|
"enable_auto_updates": "Abilita aggiornamenti automatici yt-dlp",
|
||||||
"update_frequency": "Frequenza aggiornamenti:",
|
"update_frequency": "Frequenza aggiornamenti:",
|
||||||
@@ -337,6 +340,7 @@
|
|||||||
},
|
},
|
||||||
"main_ui": {
|
"main_ui": {
|
||||||
"url_placeholder": "Inserisci URL video YouTube o playlist",
|
"url_placeholder": "Inserisci URL video YouTube o playlist",
|
||||||
|
"url_placeholder_generic": "Inserisci l'URL di un video o di una playlist da qualsiasi sito supportato",
|
||||||
"merge_subtitles": "Unisci sottotitoli",
|
"merge_subtitles": "Unisci sottotitoli",
|
||||||
"save_thumbnail": "Salva miniatura",
|
"save_thumbnail": "Salva miniatura",
|
||||||
"save_description": "Salva descrizione",
|
"save_description": "Salva descrizione",
|
||||||
|
|||||||
@@ -274,6 +274,9 @@
|
|||||||
"browse": "参照...",
|
"browse": "参照...",
|
||||||
"speed_limit": "速度制限",
|
"speed_limit": "速度制限",
|
||||||
"speed_limit_placeholder": "なし",
|
"speed_limit_placeholder": "なし",
|
||||||
|
"generic_mode": "汎用モード",
|
||||||
|
"enable_generic_mode": "汎用モードを有効化(YouTube以外のサイトをサポート)",
|
||||||
|
"generic_mode_help": "Dailymotion、CBC Gem、および yt-dlp が対応する他のサイトからのダウンロードを可能にします。",
|
||||||
"auto_update_ytdlp": "yt-dlp自動更新",
|
"auto_update_ytdlp": "yt-dlp自動更新",
|
||||||
"enable_auto_updates": "yt-dlp自動更新を有効化",
|
"enable_auto_updates": "yt-dlp自動更新を有効化",
|
||||||
"update_frequency": "更新頻度:",
|
"update_frequency": "更新頻度:",
|
||||||
@@ -337,6 +340,7 @@
|
|||||||
},
|
},
|
||||||
"main_ui": {
|
"main_ui": {
|
||||||
"url_placeholder": "YouTubeの動画URLまたはプレイリストURLを入力",
|
"url_placeholder": "YouTubeの動画URLまたはプレイリストURLを入力",
|
||||||
|
"url_placeholder_generic": "対応サイトの動画またはプレイリストURLを入力",
|
||||||
"merge_subtitles": "字幕を結合",
|
"merge_subtitles": "字幕を結合",
|
||||||
"save_thumbnail": "サムネイルを保存",
|
"save_thumbnail": "サムネイルを保存",
|
||||||
"save_description": "説明を保存",
|
"save_description": "説明を保存",
|
||||||
|
|||||||
@@ -274,6 +274,9 @@
|
|||||||
"browse": "Przeglądaj...",
|
"browse": "Przeglądaj...",
|
||||||
"speed_limit": "Limit prędkości",
|
"speed_limit": "Limit prędkości",
|
||||||
"speed_limit_placeholder": "Brak",
|
"speed_limit_placeholder": "Brak",
|
||||||
|
"generic_mode": "Tryb ogólny",
|
||||||
|
"enable_generic_mode": "Włącz tryb ogólny (obsługa stron innych niż YouTube)",
|
||||||
|
"generic_mode_help": "Umożliwia pobieranie z Dailymotion, CBC Gem i innych stron obsługiwanych przez yt-dlp.",
|
||||||
"auto_update_ytdlp": "Automatyczne aktualizacje yt-dlp",
|
"auto_update_ytdlp": "Automatyczne aktualizacje yt-dlp",
|
||||||
"enable_auto_updates": "Włącz automatyczne aktualizacje yt-dlp",
|
"enable_auto_updates": "Włącz automatyczne aktualizacje yt-dlp",
|
||||||
"update_frequency": "Częstotliwość aktualizacji:",
|
"update_frequency": "Częstotliwość aktualizacji:",
|
||||||
@@ -337,6 +340,7 @@
|
|||||||
},
|
},
|
||||||
"main_ui": {
|
"main_ui": {
|
||||||
"url_placeholder": "Wprowadź URL wideo YouTube lub playlisty",
|
"url_placeholder": "Wprowadź URL wideo YouTube lub playlisty",
|
||||||
|
"url_placeholder_generic": "Wprowadź URL filmu lub playlisty z dowolnej obsługiwanej strony",
|
||||||
"merge_subtitles": "Połącz napisy",
|
"merge_subtitles": "Połącz napisy",
|
||||||
"save_thumbnail": "Zapisz miniaturę",
|
"save_thumbnail": "Zapisz miniaturę",
|
||||||
"save_description": "Zapisz opis",
|
"save_description": "Zapisz opis",
|
||||||
|
|||||||
@@ -274,6 +274,9 @@
|
|||||||
"browse": "Procurar...",
|
"browse": "Procurar...",
|
||||||
"speed_limit": "Limite de Velocidade",
|
"speed_limit": "Limite de Velocidade",
|
||||||
"speed_limit_placeholder": "Nenhum",
|
"speed_limit_placeholder": "Nenhum",
|
||||||
|
"generic_mode": "Modo genérico",
|
||||||
|
"enable_generic_mode": "Ativar modo genérico (suporte a sites que não são do YouTube)",
|
||||||
|
"generic_mode_help": "Permite baixar de Dailymotion, CBC Gem e outros sites compatíveis com o yt-dlp.",
|
||||||
"auto_update_ytdlp": "Auto-Atualizar yt-dlp",
|
"auto_update_ytdlp": "Auto-Atualizar yt-dlp",
|
||||||
"enable_auto_updates": "Habilitar atualizações automáticas do yt-dlp",
|
"enable_auto_updates": "Habilitar atualizações automáticas do yt-dlp",
|
||||||
"update_frequency": "Frequência de atualização:",
|
"update_frequency": "Frequência de atualização:",
|
||||||
@@ -337,6 +340,7 @@
|
|||||||
},
|
},
|
||||||
"main_ui": {
|
"main_ui": {
|
||||||
"url_placeholder": "Digite a URL do vídeo ou playlist do YouTube",
|
"url_placeholder": "Digite a URL do vídeo ou playlist do YouTube",
|
||||||
|
"url_placeholder_generic": "Digite a URL de um vídeo ou playlist de qualquer site compatível",
|
||||||
"merge_subtitles": "Mesclar Legendas",
|
"merge_subtitles": "Mesclar Legendas",
|
||||||
"save_thumbnail": "Salvar Miniatura",
|
"save_thumbnail": "Salvar Miniatura",
|
||||||
"save_description": "Salvar Descrição",
|
"save_description": "Salvar Descrição",
|
||||||
|
|||||||
@@ -274,6 +274,9 @@
|
|||||||
"browse": "Обзор...",
|
"browse": "Обзор...",
|
||||||
"speed_limit": "Ограничение скорости",
|
"speed_limit": "Ограничение скорости",
|
||||||
"speed_limit_placeholder": "Нет",
|
"speed_limit_placeholder": "Нет",
|
||||||
|
"generic_mode": "Универсальный режим",
|
||||||
|
"enable_generic_mode": "Включить универсальный режим (поддержка сайтов не только YouTube)",
|
||||||
|
"generic_mode_help": "Позволяет скачивать с Dailymotion, CBC Gem и других сайтов, поддерживаемых yt-dlp.",
|
||||||
"auto_update_ytdlp": "Автообновление yt-dlp",
|
"auto_update_ytdlp": "Автообновление yt-dlp",
|
||||||
"enable_auto_updates": "Включить автоматические обновления yt-dlp",
|
"enable_auto_updates": "Включить автоматические обновления yt-dlp",
|
||||||
"update_frequency": "Частота обновления:",
|
"update_frequency": "Частота обновления:",
|
||||||
@@ -337,6 +340,7 @@
|
|||||||
},
|
},
|
||||||
"main_ui": {
|
"main_ui": {
|
||||||
"url_placeholder": "Введите URL видео или плейлиста YouTube",
|
"url_placeholder": "Введите URL видео или плейлиста YouTube",
|
||||||
|
"url_placeholder_generic": "Введите URL видео или плейлиста с любого поддерживаемого сайта",
|
||||||
"merge_subtitles": "Объединить субтитры",
|
"merge_subtitles": "Объединить субтитры",
|
||||||
"save_thumbnail": "Сохранить миниатюру",
|
"save_thumbnail": "Сохранить миниатюру",
|
||||||
"save_description": "Сохранить описание",
|
"save_description": "Сохранить описание",
|
||||||
|
|||||||
@@ -272,6 +272,9 @@
|
|||||||
"browse": "Gözat...",
|
"browse": "Gözat...",
|
||||||
"speed_limit": "Hız sınırı",
|
"speed_limit": "Hız sınırı",
|
||||||
"speed_limit_placeholder": "Yok",
|
"speed_limit_placeholder": "Yok",
|
||||||
|
"generic_mode": "Genel mod",
|
||||||
|
"enable_generic_mode": "Genel modu etkinleştir (YouTube dışı siteleri destekle)",
|
||||||
|
"generic_mode_help": "Dailymotion, CBC Gem ve yt-dlp tarafından desteklenen diğer sitelerden indirmeye izin verir.",
|
||||||
"auto_update_ytdlp": "yt-dlp otomatik güncelleme",
|
"auto_update_ytdlp": "yt-dlp otomatik güncelleme",
|
||||||
"enable_auto_updates": "yt-dlp otomatik güncellemelerini etkinleştir",
|
"enable_auto_updates": "yt-dlp otomatik güncellemelerini etkinleştir",
|
||||||
"update_frequency": "Güncelleme sıklığı:",
|
"update_frequency": "Güncelleme sıklığı:",
|
||||||
@@ -335,6 +338,7 @@
|
|||||||
},
|
},
|
||||||
"main_ui": {
|
"main_ui": {
|
||||||
"url_placeholder": "YouTube video veya oynatma listesi URL'sini girin",
|
"url_placeholder": "YouTube video veya oynatma listesi URL'sini girin",
|
||||||
|
"url_placeholder_generic": "Desteklenen herhangi bir siteden video veya oynatma listesi URL'si girin",
|
||||||
"merge_subtitles": "Altyazıları birleştir",
|
"merge_subtitles": "Altyazıları birleştir",
|
||||||
"save_thumbnail": "Küçük resmi kaydet",
|
"save_thumbnail": "Küçük resmi kaydet",
|
||||||
"save_description": "Açıklamayı kaydet",
|
"save_description": "Açıklamayı kaydet",
|
||||||
|
|||||||
@@ -262,6 +262,9 @@
|
|||||||
"browse": "浏览...",
|
"browse": "浏览...",
|
||||||
"speed_limit": "速度限制",
|
"speed_limit": "速度限制",
|
||||||
"speed_limit_placeholder": "无",
|
"speed_limit_placeholder": "无",
|
||||||
|
"generic_mode": "通用模式",
|
||||||
|
"enable_generic_mode": "启用通用模式(支持非 YouTube 网站)",
|
||||||
|
"generic_mode_help": "允许从 Dailymotion、CBC Gem 以及其他受 yt-dlp 支持的网站下载。",
|
||||||
"auto_update_ytdlp": "自动更新 yt-dlp",
|
"auto_update_ytdlp": "自动更新 yt-dlp",
|
||||||
"enable_auto_updates": "启用 yt-dlp 自动更新",
|
"enable_auto_updates": "启用 yt-dlp 自动更新",
|
||||||
"update_frequency": "更新频率:",
|
"update_frequency": "更新频率:",
|
||||||
@@ -325,6 +328,7 @@
|
|||||||
},
|
},
|
||||||
"main_ui": {
|
"main_ui": {
|
||||||
"url_placeholder": "输入 YouTube 视频或播放列表网址",
|
"url_placeholder": "输入 YouTube 视频或播放列表网址",
|
||||||
|
"url_placeholder_generic": "输入任意受支持网站的视频或播放列表网址",
|
||||||
"merge_subtitles": "合并字幕",
|
"merge_subtitles": "合并字幕",
|
||||||
"save_thumbnail": "保存缩略图",
|
"save_thumbnail": "保存缩略图",
|
||||||
"save_description": "保存描述",
|
"save_description": "保存描述",
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ class ConfigManager:
|
|||||||
_settings: Dict[str, Any] = {}
|
_settings: Dict[str, Any] = {}
|
||||||
_default_config: Dict[str, Any] = {
|
_default_config: Dict[str, Any] = {
|
||||||
"download_path": str(USER_HOME_DIR / "Downloads"),
|
"download_path": str(USER_HOME_DIR / "Downloads"),
|
||||||
|
"generic_mode": False,
|
||||||
"speed_limit_value": None,
|
"speed_limit_value": None,
|
||||||
"speed_limit_unit_index": 0,
|
"speed_limit_unit_index": 0,
|
||||||
"cookie_source": "browser", # "browser" or "file"
|
"cookie_source": "browser", # "browser" or "file"
|
||||||
|
|||||||
@@ -70,12 +70,23 @@ class LocalizationManager:
|
|||||||
"custom_options": "Custom Options",
|
"custom_options": "Custom Options",
|
||||||
"settings": "Settings"
|
"settings": "Settings"
|
||||||
},
|
},
|
||||||
|
"settings": {
|
||||||
|
"generic_mode": "Generic Mode",
|
||||||
|
"enable_generic_mode": "Enable Generic Mode (support non-YouTube sites)",
|
||||||
|
"generic_mode_help": "Allows downloading from Dailymotion, CBC Gem, and other sites supported by yt-dlp."
|
||||||
|
},
|
||||||
"tabs": {
|
"tabs": {
|
||||||
"cookies": "Login with Cookies",
|
"cookies": "Login with Cookies",
|
||||||
"custom_command": "Custom Command",
|
"custom_command": "Custom Command",
|
||||||
"proxy": "Proxy",
|
"proxy": "Proxy",
|
||||||
"language": "Language"
|
"language": "Language"
|
||||||
},
|
},
|
||||||
|
"main_ui": {
|
||||||
|
"url_placeholder": "Enter YouTube video or playlist URL",
|
||||||
|
"url_placeholder_generic": "Enter video or playlist URL from any supported site",
|
||||||
|
"settings_tooltip": "Current Path: {path}\nSpeed Limit: {speed_limit}",
|
||||||
|
"speed_limit_none": "None"
|
||||||
|
},
|
||||||
"language": {
|
"language": {
|
||||||
"select_language": "Select Language:",
|
"select_language": "Select Language:",
|
||||||
"current_language": "Current language: {language}",
|
"current_language": "Current language: {language}",
|
||||||
|
|||||||
Reference in New Issue
Block a user