Add proxy configuration support to downloader and GUI
Introduces proxy and geo-verification proxy options to the downloader and GUI dialogs, allowing users to specify network proxies for downloads and geo-restricted content. Updates the DownloadThread and main app logic to pass and apply these proxy settings, and adds validation and status indicators in the custom options dialog.
This commit is contained in:
@@ -326,9 +326,121 @@ class CustomOptionsDialog(QDialog):
|
||||
)
|
||||
command_layout.addWidget(self.log_output)
|
||||
|
||||
# === Proxy Tab ===
|
||||
proxy_tab = QWidget()
|
||||
proxy_layout = QVBoxLayout(proxy_tab)
|
||||
|
||||
# Help text
|
||||
proxy_help_text = QLabel(
|
||||
"Configure proxy settings for network connections and geo-verification.\n"
|
||||
"Proxy can help bypass regional restrictions and improve download performance."
|
||||
)
|
||||
proxy_help_text.setWordWrap(True)
|
||||
proxy_help_text.setStyleSheet("color: #999999; padding: 10px;")
|
||||
proxy_layout.addWidget(proxy_help_text)
|
||||
|
||||
# Main Proxy section
|
||||
main_proxy_group = QGroupBox("Main Proxy")
|
||||
main_proxy_layout = QVBoxLayout(main_proxy_group)
|
||||
|
||||
main_proxy_help = QLabel("Use the specified HTTP/HTTPS/SOCKS proxy for all connections.")
|
||||
main_proxy_help.setWordWrap(True)
|
||||
main_proxy_help.setStyleSheet("color: #999999; font-size: 11px;")
|
||||
main_proxy_layout.addWidget(main_proxy_help)
|
||||
|
||||
# Main proxy input
|
||||
main_proxy_input_layout = QHBoxLayout()
|
||||
main_proxy_input_layout.addWidget(QLabel("Proxy URL:"))
|
||||
self.proxy_url_input = QLineEdit()
|
||||
self.proxy_url_input.setPlaceholderText("e.g., http://proxy.example.com:8080 or socks5://user:pass@127.0.0.1:1080")
|
||||
self.proxy_url_input.textChanged.connect(self.validate_proxy_inputs)
|
||||
main_proxy_input_layout.addWidget(self.proxy_url_input)
|
||||
main_proxy_layout.addLayout(main_proxy_input_layout)
|
||||
|
||||
# Example text
|
||||
example_label = QLabel("Examples: http://proxy.com:8080, https://proxy.com:8080, socks5://127.0.0.1:1080")
|
||||
example_label.setStyleSheet("color: #888888; font-size: 10px; font-style: italic;")
|
||||
main_proxy_layout.addWidget(example_label)
|
||||
|
||||
proxy_layout.addWidget(main_proxy_group)
|
||||
|
||||
# Geo-verification Proxy section
|
||||
geo_proxy_group = QGroupBox("Geo-verification Proxy")
|
||||
geo_proxy_layout = QVBoxLayout(geo_proxy_group)
|
||||
|
||||
geo_proxy_help = QLabel(
|
||||
"Use this proxy to verify IP address for geo-restricted sites. "
|
||||
"The main proxy (if set) is used for actual downloading."
|
||||
)
|
||||
geo_proxy_help.setWordWrap(True)
|
||||
geo_proxy_help.setStyleSheet("color: #999999; font-size: 11px;")
|
||||
geo_proxy_layout.addWidget(geo_proxy_help)
|
||||
|
||||
# Geo proxy input
|
||||
geo_proxy_input_layout = QHBoxLayout()
|
||||
geo_proxy_input_layout.addWidget(QLabel("Geo Proxy URL:"))
|
||||
self.geo_proxy_url_input = QLineEdit()
|
||||
self.geo_proxy_url_input.setPlaceholderText("e.g., http://us-proxy.example.com:8080")
|
||||
self.geo_proxy_url_input.textChanged.connect(self.validate_proxy_inputs)
|
||||
geo_proxy_input_layout.addWidget(self.geo_proxy_url_input)
|
||||
geo_proxy_layout.addLayout(geo_proxy_input_layout)
|
||||
|
||||
proxy_layout.addWidget(geo_proxy_group)
|
||||
|
||||
# Proxy status indicator
|
||||
self.proxy_status = QLabel("")
|
||||
self.proxy_status.setStyleSheet("color: #999999; font-style: italic;")
|
||||
proxy_layout.addWidget(self.proxy_status)
|
||||
|
||||
# Clear buttons
|
||||
clear_layout = QHBoxLayout()
|
||||
clear_main_proxy_btn = QPushButton("Clear Main Proxy")
|
||||
clear_main_proxy_btn.clicked.connect(lambda: self.proxy_url_input.clear())
|
||||
clear_main_proxy_btn.setStyleSheet(
|
||||
"""
|
||||
QPushButton {
|
||||
padding: 6px 12px;
|
||||
background-color: #444444;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #555555;
|
||||
}
|
||||
"""
|
||||
)
|
||||
clear_layout.addWidget(clear_main_proxy_btn)
|
||||
|
||||
clear_geo_proxy_btn = QPushButton("Clear Geo Proxy")
|
||||
clear_geo_proxy_btn.clicked.connect(lambda: self.geo_proxy_url_input.clear())
|
||||
clear_geo_proxy_btn.setStyleSheet(
|
||||
"""
|
||||
QPushButton {
|
||||
padding: 6px 12px;
|
||||
background-color: #444444;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #555555;
|
||||
}
|
||||
"""
|
||||
)
|
||||
clear_layout.addWidget(clear_geo_proxy_btn)
|
||||
|
||||
clear_layout.addStretch()
|
||||
proxy_layout.addLayout(clear_layout)
|
||||
|
||||
proxy_layout.addStretch()
|
||||
|
||||
# Add tabs to the tab widget
|
||||
self.tab_widget.addTab(cookies_tab, "Login with Cookies")
|
||||
self.tab_widget.addTab(command_tab, "Custom Command")
|
||||
self.tab_widget.addTab(proxy_tab, "Proxy")
|
||||
|
||||
# Dialog buttons
|
||||
button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
|
||||
@@ -523,6 +635,78 @@ class CustomOptionsDialog(QDialog):
|
||||
"""Returns True if browser cookies mode is selected"""
|
||||
return self.cookie_browser_radio.isChecked()
|
||||
|
||||
def get_proxy_url(self) -> str | None:
|
||||
"""Returns the main proxy URL if specified"""
|
||||
proxy_url = self.proxy_url_input.text().strip()
|
||||
return proxy_url if proxy_url else None
|
||||
|
||||
def get_geo_proxy_url(self) -> str | None:
|
||||
"""Returns the geo-verification proxy URL if specified"""
|
||||
geo_proxy_url = self.geo_proxy_url_input.text().strip()
|
||||
return geo_proxy_url if geo_proxy_url else None
|
||||
|
||||
def validate_proxy_url(self, url: str) -> bool:
|
||||
"""Basic validation for proxy URL format"""
|
||||
if not url:
|
||||
return True # Empty is OK
|
||||
|
||||
# Check if it starts with a valid scheme
|
||||
valid_schemes = ['http://', 'https://', 'socks5://', 'socks4://']
|
||||
if not any(url.lower().startswith(scheme) for scheme in valid_schemes):
|
||||
return False
|
||||
|
||||
# Basic URL format check (contains at least host:port)
|
||||
try:
|
||||
# Remove the scheme to check host:port part
|
||||
for scheme in valid_schemes:
|
||||
if url.lower().startswith(scheme):
|
||||
host_port = url[len(scheme):]
|
||||
break
|
||||
|
||||
# Skip user:pass@ part if present
|
||||
if '@' in host_port:
|
||||
host_port = host_port.split('@')[1]
|
||||
|
||||
# Should have at least host:port
|
||||
if ':' in host_port:
|
||||
host, port = host_port.split(':', 1)
|
||||
if host and port.isdigit():
|
||||
return True
|
||||
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def validate_proxy_inputs(self) -> None:
|
||||
"""Validate proxy inputs and update status"""
|
||||
main_proxy = self.proxy_url_input.text().strip()
|
||||
geo_proxy = self.geo_proxy_url_input.text().strip()
|
||||
|
||||
if not main_proxy and not geo_proxy:
|
||||
self.proxy_status.setText("")
|
||||
return
|
||||
|
||||
issues = []
|
||||
|
||||
if main_proxy and not self.validate_proxy_url(main_proxy):
|
||||
issues.append("Invalid main proxy URL format")
|
||||
|
||||
if geo_proxy and not self.validate_proxy_url(geo_proxy):
|
||||
issues.append("Invalid geo proxy URL format")
|
||||
|
||||
if issues:
|
||||
self.proxy_status.setText(" | ".join(issues))
|
||||
self.proxy_status.setStyleSheet("color: #ff6666; font-style: italic;")
|
||||
else:
|
||||
status_parts = []
|
||||
if main_proxy:
|
||||
status_parts.append("Main proxy configured")
|
||||
if geo_proxy:
|
||||
status_parts.append("Geo proxy configured")
|
||||
|
||||
self.proxy_status.setText(" | ".join(status_parts))
|
||||
self.proxy_status.setStyleSheet("color: #00cc00; font-style: italic;")
|
||||
|
||||
def run_custom_command(self) -> None:
|
||||
url = self._parent.url_input.text().strip()
|
||||
if not url:
|
||||
|
||||
@@ -110,6 +110,9 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
||||
# Initialize cookie settings - ensure they start clean
|
||||
self.cookie_file_path = None
|
||||
self.browser_cookies_option = None
|
||||
# Initialize proxy settings
|
||||
self.proxy_url = None
|
||||
self.geo_proxy_url = None
|
||||
self.speed_limit_value = None # Store speed limit value
|
||||
self.speed_limit_unit_index = 0 # Store speed limit unit index (0: KB/s, 1: MB/s)
|
||||
self.download_section = None
|
||||
@@ -1023,6 +1026,8 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
||||
rate_limit=rate_limit, # Pass the calculated rate limit
|
||||
download_section=self.download_section, # Pass the download section
|
||||
force_keyframes=self.force_keyframes, # Pass the force keyframes setting
|
||||
proxy_url=self.proxy_url, # Pass the proxy URL
|
||||
geo_proxy_url=self.geo_proxy_url, # Pass the geo-verification proxy URL
|
||||
)
|
||||
|
||||
# Connect signals
|
||||
@@ -1402,6 +1407,32 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
||||
)
|
||||
# If neither is selected, both remain None (cleared above)
|
||||
|
||||
# Handle proxy options
|
||||
proxy_url = dialog.get_proxy_url()
|
||||
geo_proxy_url = dialog.get_geo_proxy_url()
|
||||
|
||||
# Clear existing proxy settings
|
||||
self.proxy_url = None
|
||||
self.geo_proxy_url = None
|
||||
|
||||
if proxy_url:
|
||||
self.proxy_url = proxy_url
|
||||
logger.info(f"Main proxy set: {self.proxy_url}")
|
||||
QMessageBox.information(
|
||||
self,
|
||||
"Proxy Set",
|
||||
f"Main proxy set: {proxy_url}",
|
||||
)
|
||||
|
||||
if geo_proxy_url:
|
||||
self.geo_proxy_url = geo_proxy_url
|
||||
logger.info(f"Geo-verification proxy set: {self.geo_proxy_url}")
|
||||
QMessageBox.information(
|
||||
self,
|
||||
"Geo Proxy Set",
|
||||
f"Geo-verification proxy set: {geo_proxy_url}",
|
||||
)
|
||||
|
||||
def show_about_dialog(self) -> None: # ADDED METHOD HERE
|
||||
dialog = AboutDialog(self)
|
||||
dialog.exec()
|
||||
@@ -1708,6 +1739,13 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin): # Inherit from
|
||||
elif self.browser_cookies_option:
|
||||
cmd.extend(["--cookies-from-browser", self.browser_cookies_option])
|
||||
|
||||
# Add proxy settings if available
|
||||
if self.proxy_url:
|
||||
cmd.extend(["--proxy", self.proxy_url])
|
||||
|
||||
if self.geo_proxy_url:
|
||||
cmd.extend(["--geo-verification-proxy", self.geo_proxy_url])
|
||||
|
||||
# Execute command with hidden console window on Windows
|
||||
# Extra logic moved to src\utils\ytsage_constants.py
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60, creationflags=SUBPROCESS_CREATIONFLAGS)
|
||||
|
||||
Reference in New Issue
Block a user