169 lines
5.5 KiB
Python
169 lines
5.5 KiB
Python
"""
|
|
Configuration management with XDG compliance
|
|
"""
|
|
|
|
import os
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Optional, Dict, Any
|
|
|
|
APP_VENDOR = "stormux"
|
|
APP_NAME = "navipy"
|
|
|
|
|
|
def getConfigDir() -> Path:
|
|
"""Get XDG-compliant config directory"""
|
|
xdgConfig = os.environ.get('XDG_CONFIG_HOME')
|
|
baseDir = Path(xdgConfig) if xdgConfig else Path.home() / '.config'
|
|
configDir = baseDir / APP_VENDOR / APP_NAME
|
|
|
|
configDir.mkdir(parents=True, exist_ok=True)
|
|
return configDir
|
|
|
|
|
|
def getDataDir() -> Path:
|
|
"""Get XDG-compliant data directory"""
|
|
xdgData = os.environ.get('XDG_DATA_HOME')
|
|
baseDir = Path(xdgData) if xdgData else Path.home() / '.local' / 'share'
|
|
dataDir = baseDir / APP_VENDOR / APP_NAME
|
|
|
|
dataDir.mkdir(parents=True, exist_ok=True)
|
|
return dataDir
|
|
|
|
|
|
def getCacheDir() -> Path:
|
|
"""Get XDG-compliant cache directory"""
|
|
xdgCache = os.environ.get('XDG_CACHE_HOME')
|
|
baseDir = Path(xdgCache) if xdgCache else Path.home() / '.cache'
|
|
cacheDir = baseDir / APP_VENDOR / APP_NAME
|
|
|
|
cacheDir.mkdir(parents=True, exist_ok=True)
|
|
return cacheDir
|
|
|
|
|
|
class Settings:
|
|
"""Application settings manager"""
|
|
|
|
def __init__(self):
|
|
self.configDir = getConfigDir()
|
|
self.configFile = self.configDir / 'settings.json'
|
|
self.serversFile = self.configDir / 'servers.json'
|
|
self._legacyConfigDir = self._getLegacyConfigDir()
|
|
self._settings = self._loadSettings()
|
|
self._servers = self._loadServers()
|
|
|
|
def _getLegacyConfigDir(self) -> Path:
|
|
"""Legacy configuration path kept for backwards compatibility."""
|
|
xdgConfig = os.environ.get('XDG_CONFIG_HOME')
|
|
baseDir = Path(xdgConfig) if xdgConfig else Path.home() / '.config'
|
|
return baseDir / APP_NAME
|
|
|
|
def _loadSettings(self) -> Dict[str, Any]:
|
|
"""Load settings from file"""
|
|
defaults = {
|
|
'general': {
|
|
'defaultServer': None,
|
|
'startMinimized': False
|
|
},
|
|
'playback': {
|
|
'volume': 100,
|
|
'shuffle': False,
|
|
'repeat': 'none', # none, one, all
|
|
'gapless': True
|
|
},
|
|
'interface': {
|
|
'pageStep': 5,
|
|
'announceTrackChanges': True
|
|
}
|
|
}
|
|
|
|
for candidate in (self.configFile, self._legacyConfigDir / 'settings.json'):
|
|
if candidate.exists():
|
|
try:
|
|
with open(candidate, 'r', encoding='utf-8') as f:
|
|
loaded = json.load(f)
|
|
# Merge loaded settings with defaults
|
|
for section in defaults:
|
|
if section in loaded:
|
|
defaults[section].update(loaded[section])
|
|
return defaults
|
|
except (json.JSONDecodeError, IOError):
|
|
continue
|
|
|
|
return defaults
|
|
|
|
def _loadServers(self) -> Dict[str, Dict[str, str]]:
|
|
"""Load server configurations from file"""
|
|
for candidate in (self.serversFile, self._legacyConfigDir / 'servers.json'):
|
|
if candidate.exists():
|
|
try:
|
|
with open(candidate, 'r', encoding='utf-8') as f:
|
|
return json.load(f)
|
|
except (json.JSONDecodeError, IOError):
|
|
continue
|
|
return {}
|
|
|
|
def save(self) -> None:
|
|
"""Save settings to file"""
|
|
try:
|
|
with open(self.configFile, 'w', encoding='utf-8') as f:
|
|
json.dump(self._settings, f, indent=2)
|
|
except IOError as e:
|
|
print(f"Failed to save settings: {e}")
|
|
|
|
def saveServers(self) -> None:
|
|
"""Save server configurations to file"""
|
|
try:
|
|
with open(self.serversFile, 'w', encoding='utf-8') as f:
|
|
json.dump(self._servers, f, indent=2)
|
|
except IOError as e:
|
|
print(f"Failed to save servers: {e}")
|
|
|
|
def get(self, section: str, key: str, default: Any = None) -> Any:
|
|
"""Get a setting value"""
|
|
return self._settings.get(section, {}).get(key, default)
|
|
|
|
def set(self, section: str, key: str, value: Any) -> None:
|
|
"""Set a setting value"""
|
|
if section not in self._settings:
|
|
self._settings[section] = {}
|
|
self._settings[section][key] = value
|
|
self.save()
|
|
|
|
# Server management
|
|
|
|
def getServers(self) -> Dict[str, Dict[str, str]]:
|
|
"""Get all server configurations"""
|
|
return self._servers
|
|
|
|
def getServer(self, name: str) -> Optional[Dict[str, str]]:
|
|
"""Get a specific server configuration"""
|
|
return self._servers.get(name)
|
|
|
|
def addServer(self, name: str, url: str, username: str, password: str) -> None:
|
|
"""Add or update a server configuration"""
|
|
self._servers[name] = {
|
|
'url': url,
|
|
'username': username,
|
|
'password': password
|
|
}
|
|
self.saveServers()
|
|
|
|
def removeServer(self, name: str) -> None:
|
|
"""Remove a server configuration"""
|
|
if name in self._servers:
|
|
del self._servers[name]
|
|
self.saveServers()
|
|
|
|
def getDefaultServer(self) -> Optional[str]:
|
|
"""Get the default server name"""
|
|
return self.get('general', 'defaultServer')
|
|
|
|
def setDefaultServer(self, name: str) -> None:
|
|
"""Set the default server"""
|
|
self.set('general', 'defaultServer', name)
|
|
|
|
def hasServers(self) -> bool:
|
|
"""Check if any servers are configured"""
|
|
return len(self._servers) > 0
|