Initial commit: Local Transcription App v1.0
Phase 1 Complete - Standalone Desktop Application Features: - Real-time speech-to-text with Whisper (faster-whisper) - PySide6 desktop GUI with settings dialog - Web server for OBS browser source integration - Audio capture with automatic sample rate detection and resampling - Noise suppression with Voice Activity Detection (VAD) - Configurable display settings (font, timestamps, fade duration) - Settings apply without restart (with automatic model reloading) - Auto-fade for web display transcriptions - CPU/GPU support with automatic device detection - Standalone executable builds (PyInstaller) - CUDA build support (works on systems without CUDA hardware) Components: - Audio capture with sounddevice - Noise reduction with noisereduce + webrtcvad - Transcription with faster-whisper - GUI with PySide6 - Web server with FastAPI + WebSocket - Configuration system with YAML Build System: - Standard builds (CPU-only): build.sh / build.bat - CUDA builds (universal): build-cuda.sh / build-cuda.bat - Comprehensive BUILD.md documentation - Cross-platform support (Linux, Windows) Documentation: - README.md with project overview and quick start - BUILD.md with detailed build instructions - NEXT_STEPS.md with future enhancement roadmap - INSTALL.md with setup instructions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
141
client/config.py
Normal file
141
client/config.py
Normal file
@@ -0,0 +1,141 @@
|
||||
"""Configuration management for the local transcription application."""
|
||||
|
||||
import os
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
|
||||
class Config:
|
||||
"""Manages application configuration with YAML file storage."""
|
||||
|
||||
def __init__(self, config_path: Optional[str] = None):
|
||||
"""
|
||||
Initialize configuration.
|
||||
|
||||
Args:
|
||||
config_path: Path to configuration file. If None, uses default location.
|
||||
"""
|
||||
self.app_dir = Path.home() / ".local-transcription"
|
||||
self.app_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if config_path is None:
|
||||
self.config_path = self.app_dir / "config.yaml"
|
||||
else:
|
||||
self.config_path = Path(config_path)
|
||||
|
||||
self.config: Dict[str, Any] = {}
|
||||
self.load()
|
||||
|
||||
def load(self) -> None:
|
||||
"""Load configuration from file or create default if not exists."""
|
||||
if self.config_path.exists():
|
||||
with open(self.config_path, 'r') as f:
|
||||
self.config = yaml.safe_load(f) or {}
|
||||
else:
|
||||
# Load default configuration
|
||||
default_config_path = Path(__file__).parent.parent / "config" / "default_config.yaml"
|
||||
if default_config_path.exists():
|
||||
with open(default_config_path, 'r') as f:
|
||||
self.config = yaml.safe_load(f) or {}
|
||||
else:
|
||||
self.config = self._get_default_config()
|
||||
|
||||
# Save the default configuration
|
||||
self.save()
|
||||
|
||||
def save(self) -> None:
|
||||
"""Save current configuration to file."""
|
||||
with open(self.config_path, 'w') as f:
|
||||
yaml.dump(self.config, f, default_flow_style=False, indent=2)
|
||||
|
||||
def get(self, key_path: str, default: Any = None) -> Any:
|
||||
"""
|
||||
Get configuration value using dot notation.
|
||||
|
||||
Args:
|
||||
key_path: Dot-separated path to config value (e.g., "audio.sample_rate")
|
||||
default: Default value if key not found
|
||||
|
||||
Returns:
|
||||
Configuration value or default
|
||||
"""
|
||||
keys = key_path.split('.')
|
||||
value = self.config
|
||||
|
||||
for key in keys:
|
||||
if isinstance(value, dict) and key in value:
|
||||
value = value[key]
|
||||
else:
|
||||
return default
|
||||
|
||||
return value
|
||||
|
||||
def set(self, key_path: str, value: Any) -> None:
|
||||
"""
|
||||
Set configuration value using dot notation.
|
||||
|
||||
Args:
|
||||
key_path: Dot-separated path to config value (e.g., "audio.sample_rate")
|
||||
value: Value to set
|
||||
"""
|
||||
keys = key_path.split('.')
|
||||
config = self.config
|
||||
|
||||
# Navigate to the parent dict
|
||||
for key in keys[:-1]:
|
||||
if key not in config:
|
||||
config[key] = {}
|
||||
config = config[key]
|
||||
|
||||
# Set the value
|
||||
config[keys[-1]] = value
|
||||
self.save()
|
||||
|
||||
def _get_default_config(self) -> Dict[str, Any]:
|
||||
"""Get hardcoded default configuration."""
|
||||
return {
|
||||
'user': {
|
||||
'name': 'User',
|
||||
'id': ''
|
||||
},
|
||||
'audio': {
|
||||
'input_device': 'default',
|
||||
'sample_rate': 16000,
|
||||
'chunk_duration': 3.0
|
||||
},
|
||||
'noise_suppression': {
|
||||
'enabled': True,
|
||||
'strength': 0.7,
|
||||
'method': 'noisereduce'
|
||||
},
|
||||
'transcription': {
|
||||
'model': 'base',
|
||||
'device': 'auto',
|
||||
'language': 'en',
|
||||
'task': 'transcribe'
|
||||
},
|
||||
'processing': {
|
||||
'use_vad': True,
|
||||
'min_confidence': 0.5
|
||||
},
|
||||
'server_sync': {
|
||||
'enabled': False,
|
||||
'url': 'ws://localhost:8000',
|
||||
'api_key': ''
|
||||
},
|
||||
'display': {
|
||||
'show_timestamps': True,
|
||||
'max_lines': 100,
|
||||
'font_size': 12,
|
||||
'theme': 'dark'
|
||||
}
|
||||
}
|
||||
|
||||
def reset_to_default(self) -> None:
|
||||
"""Reset configuration to default values."""
|
||||
self.config = self._get_default_config()
|
||||
self.save()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Config(path={self.config_path})"
|
||||
Reference in New Issue
Block a user