neo-pod-desktop/src/config_loader.py

250 lines
8.0 KiB
Python

#!/usr/bin/env python3
"""
Configuration Loader Module for iPod Nano Transfer Tool
Handles reading and writing configuration settings
"""
import os
import logging
import configparser
from pathlib import Path
from typing import Any, Dict, Optional
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
class ConfigLoader:
"""Class to handle configuration loading and saving"""
def __init__(self, config_file: Optional[str] = None):
"""
Initialize the configuration loader
Args:
config_file: Path to configuration file (optional)
"""
if not config_file:
self.config_file = os.path.join(
os.path.expanduser("~"), ".config", "neo-pod-desktop", "config.ini"
)
self._migrate_old_config()
else:
self.config_file = config_file
self.config = configparser.ConfigParser()
# Load configuration
self.load()
def _migrate_old_config(self):
"""Migrate config from old project-root location to XDG config dir."""
old_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"config.ini"
)
if os.path.exists(old_path) and not os.path.exists(self.config_file):
logger.info("Migrating config from %s to %s", old_path, self.config_file)
os.makedirs(os.path.dirname(self.config_file), exist_ok=True)
os.rename(old_path, self.config_file)
def load(self) -> bool:
"""
Load configuration from file
Returns:
True if successful, False otherwise
"""
try:
if os.path.exists(self.config_file):
logger.info(f"Loading configuration from {self.config_file}")
self.config.read(self.config_file)
return True
else:
logger.warning(f"Configuration file {self.config_file} not found, using defaults")
self._set_defaults()
return False
except Exception as e:
logger.error(f"Failed to load configuration: {e}")
self._set_defaults()
return False
def save(self) -> bool:
"""
Save configuration to file
Returns:
True if successful, False otherwise
"""
try:
logger.info(f"Saving configuration to {self.config_file}")
os.makedirs(os.path.dirname(self.config_file), exist_ok=True)
with open(self.config_file, 'w') as f:
self.config.write(f)
return True
except Exception as e:
logger.error(f"Failed to save configuration: {e}")
return False
def _set_defaults(self):
"""Set default configuration values"""
# General section
if not self.config.has_section('General'):
self.config.add_section('General')
self.config.set('General', 'output_dir', os.path.join(os.path.expanduser("~"), "Music", "iPod"))
self.config.set('General', 'format', 'm4a')
self.config.set('General', 'quality', '256')
self.config.set('General', 'clean_temp', 'true')
# Video section
if not self.config.has_section('Video'):
self.config.add_section('Video')
self.config.set('Video', 'enable_video', 'false')
self.config.set('Video', 'resolution', '640x480')
# Metadata section
if not self.config.has_section('Metadata'):
self.config.add_section('Metadata')
self.config.set('Metadata', 'embed_artwork', 'true')
self.config.set('Metadata', 'use_musicbrainz', 'false')
# Device section
if not self.config.has_section('Device'):
self.config.add_section('Device')
self.config.set('Device', 'auto_detect', 'true')
self.config.set('Device', 'mount_point', '')
# Advanced section
if not self.config.has_section('Advanced'):
self.config.add_section('Advanced')
self.config.set('Advanced', 'temp_dir', 'temp')
self.config.set('Advanced', 'concurrent_downloads', '2')
self.config.set('Advanced', 'debug', 'false')
def get(self, section: str, option: str, fallback: Any = None) -> Any:
"""
Get configuration value
Args:
section: Configuration section
option: Configuration option
fallback: Fallback value if option not found
Returns:
Configuration value
"""
try:
return self.config.get(section, option, fallback=fallback)
except (configparser.NoSectionError, configparser.NoOptionError):
return fallback
def get_boolean(self, section: str, option: str, fallback: bool = False) -> bool:
"""
Get boolean configuration value
Args:
section: Configuration section
option: Configuration option
fallback: Fallback value if option not found
Returns:
Boolean configuration value
"""
try:
return self.config.getboolean(section, option, fallback=fallback)
except (configparser.NoSectionError, configparser.NoOptionError):
return fallback
def get_int(self, section: str, option: str, fallback: int = 0) -> int:
"""
Get integer configuration value
Args:
section: Configuration section
option: Configuration option
fallback: Fallback value if option not found
Returns:
Integer configuration value
"""
try:
return self.config.getint(section, option, fallback=fallback)
except (configparser.NoSectionError, configparser.NoOptionError):
return fallback
def get_float(self, section: str, option: str, fallback: float = 0.0) -> float:
"""
Get float configuration value
Args:
section: Configuration section
option: Configuration option
fallback: Fallback value if option not found
Returns:
Float configuration value
"""
try:
return self.config.getfloat(section, option, fallback=fallback)
except (configparser.NoSectionError, configparser.NoOptionError):
return fallback
def set(self, section: str, option: str, value: Any) -> None:
"""
Set configuration value
Args:
section: Configuration section
option: Configuration option
value: Configuration value
"""
if not self.config.has_section(section):
self.config.add_section(section)
self.config.set(section, option, str(value))
def get_all(self) -> Dict[str, Dict[str, str]]:
"""
Get all configuration values
Returns:
Dictionary of all configuration values
"""
result = {}
for section in self.config.sections():
result[section] = {}
for option in self.config.options(section):
result[section][option] = self.config.get(section, option)
return result
if __name__ == "__main__":
# Example usage
config = ConfigLoader()
# Get configuration values
output_dir = config.get('General', 'output_dir')
format = config.get('General', 'format')
quality = config.get_int('General', 'quality')
print(f"Output directory: {output_dir}")
print(f"Format: {format}")
print(f"Quality: {quality} kbps")
# Set configuration value
config.set('General', 'format', 'mp3')
# Save configuration
config.save()