Files
navipy/src/widgets/server_dialog.py
Storm Dragon 555ca0bba9 Initial commit
2025-12-15 04:09:55 -05:00

207 lines
7.3 KiB
Python

"""
Server connection dialog for adding/editing Navidrome servers
"""
from PySide6.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QFormLayout,
QLineEdit, QPushButton, QLabel, QMessageBox, QCheckBox
)
from PySide6.QtCore import Qt
from src.api.client import SubsonicClient, SubsonicError
from src.config.settings import Settings
class ServerDialog(QDialog):
"""Dialog for configuring a Navidrome server connection"""
def __init__(self, settings: Settings, serverName: str = None, parent=None):
"""
Initialize the server dialog
Args:
settings: Application settings instance
serverName: Name of existing server to edit (None for new server)
parent: Parent widget
"""
super().__init__(parent)
self.settings = settings
self.editingServer = serverName
self.isNew = serverName is None
self.setWindowTitle("Add Server" if self.isNew else "Edit Server")
self.setModal(True)
self.setMinimumWidth(400)
self.setupUi()
self.loadExistingData()
def setupUi(self):
"""Setup the dialog UI"""
layout = QVBoxLayout(self)
# Form layout for inputs
formLayout = QFormLayout()
# Server name
self.nameEdit = QLineEdit()
self.nameEdit.setAccessibleName("Server Name")
self.nameEdit.setAccessibleDescription("A friendly name for this server")
nameLabel = QLabel("&Name:")
nameLabel.setBuddy(self.nameEdit)
formLayout.addRow(nameLabel, self.nameEdit)
# Server URL
self.urlEdit = QLineEdit()
self.urlEdit.setAccessibleName("Server URL")
self.urlEdit.setAccessibleDescription("The full URL to your Navidrome server, for example https://music.example.com")
self.urlEdit.setPlaceholderText("https://music.example.com")
urlLabel = QLabel("&URL:")
urlLabel.setBuddy(self.urlEdit)
formLayout.addRow(urlLabel, self.urlEdit)
# Username
self.usernameEdit = QLineEdit()
self.usernameEdit.setAccessibleName("Username")
usernameLabel = QLabel("U&sername:")
usernameLabel.setBuddy(self.usernameEdit)
formLayout.addRow(usernameLabel, self.usernameEdit)
# Password
self.passwordEdit = QLineEdit()
self.passwordEdit.setEchoMode(QLineEdit.Password)
self.passwordEdit.setAccessibleName("Password")
passwordLabel = QLabel("&Password:")
passwordLabel.setBuddy(self.passwordEdit)
formLayout.addRow(passwordLabel, self.passwordEdit)
# Set as default checkbox
self.defaultCheckbox = QCheckBox("Set as &default server")
self.defaultCheckbox.setAccessibleName("Set as default server")
formLayout.addRow("", self.defaultCheckbox)
layout.addLayout(formLayout)
# Status label for connection test
self.statusLabel = QLabel("")
self.statusLabel.setAccessibleName("Connection Status")
self.statusLabel.setWordWrap(True)
layout.addWidget(self.statusLabel)
# Button layout
buttonLayout = QHBoxLayout()
self.testButton = QPushButton("&Test Connection")
self.testButton.setAccessibleName("Test Connection")
self.testButton.clicked.connect(self.testConnection)
buttonLayout.addWidget(self.testButton)
buttonLayout.addStretch()
self.cancelButton = QPushButton("&Cancel")
self.cancelButton.setAccessibleName("Cancel")
self.cancelButton.clicked.connect(self.reject)
buttonLayout.addWidget(self.cancelButton)
self.saveButton = QPushButton("&Save")
self.saveButton.setAccessibleName("Save")
self.saveButton.setDefault(True)
self.saveButton.clicked.connect(self.saveServer)
buttonLayout.addWidget(self.saveButton)
layout.addLayout(buttonLayout)
# Set initial focus
self.nameEdit.setFocus()
def loadExistingData(self):
"""Load existing server data if editing"""
if not self.isNew and self.editingServer:
server = self.settings.getServer(self.editingServer)
if server:
self.nameEdit.setText(self.editingServer)
self.nameEdit.setReadOnly(True) # Can't change name when editing
self.urlEdit.setText(server.get('url', ''))
self.usernameEdit.setText(server.get('username', ''))
self.passwordEdit.setText(server.get('password', ''))
# Check if this is the default server
defaultServer = self.settings.getDefaultServer()
self.defaultCheckbox.setChecked(defaultServer == self.editingServer)
def testConnection(self):
"""Test connection to the server"""
url = self.urlEdit.text().strip()
username = self.usernameEdit.text().strip()
password = self.passwordEdit.text()
if not url or not username or not password:
self.statusLabel.setText("Please fill in all fields")
return
self.statusLabel.setText("Testing connection...")
self.testButton.setEnabled(False)
try:
client = SubsonicClient(url, username, password)
if client.ping():
self.statusLabel.setText("Connection successful!")
else:
self.statusLabel.setText("Connection failed: Invalid response")
except SubsonicError as e:
self.statusLabel.setText(f"Connection failed: {e.message}")
except Exception as e:
self.statusLabel.setText(f"Connection failed: {str(e)}")
finally:
self.testButton.setEnabled(True)
def saveServer(self):
"""Save the server configuration"""
name = self.nameEdit.text().strip()
url = self.urlEdit.text().strip()
username = self.usernameEdit.text().strip()
password = self.passwordEdit.text()
# Validate inputs
if not name:
QMessageBox.warning(self, "Validation Error", "Please enter a server name")
self.nameEdit.setFocus()
return
if not url:
QMessageBox.warning(self, "Validation Error", "Please enter the server URL")
self.urlEdit.setFocus()
return
if not username:
QMessageBox.warning(self, "Validation Error", "Please enter a username")
self.usernameEdit.setFocus()
return
if not password:
QMessageBox.warning(self, "Validation Error", "Please enter a password")
self.passwordEdit.setFocus()
return
# Check for duplicate name when adding new server
if self.isNew and self.settings.getServer(name):
QMessageBox.warning(
self, "Validation Error",
f"A server named '{name}' already exists. Please choose a different name."
)
self.nameEdit.setFocus()
return
# Save the server
self.settings.addServer(name, url, username, password)
# Set as default if checked
if self.defaultCheckbox.isChecked():
self.settings.setDefaultServer(name)
self.accept()
def getServerName(self) -> str:
"""Get the name of the saved server"""
return self.nameEdit.text().strip()