""" Data path management utilities for the karaoke downloader. Provides centralized data directory path management and file path resolution. """ import os from pathlib import Path from typing import Optional from .config_manager import get_config_manager class DataPathManager: """ Manages data directory paths and provides utilities for resolving file paths relative to the configured data directory. """ def __init__(self, data_dir: Optional[str] = None): """ Initialize the data path manager. Args: data_dir: Optional custom data directory path. If None, uses config. """ self._data_dir = data_dir # If a custom data directory is provided, look for config.json in that directory if data_dir: config_file = Path(data_dir) / "config.json" self._config_manager = get_config_manager(str(config_file)) else: # Otherwise, use the default config.json in the root directory self._config_manager = get_config_manager() @property def data_dir(self) -> Path: """ Get the configured data directory path. Returns: Path to the data directory """ if self._data_dir: return Path(self._data_dir) # Get from config config = self._config_manager.get_config() data_dir = getattr(config.folder_structure, 'data_dir', 'data') return Path(data_dir) def get_path(self, filename: str) -> Path: """ Get the full path to a file in the data directory. Args: filename: Name of the file (e.g., 'config.json', 'channels.json') Returns: Full path to the file """ return self.data_dir / filename def get_channels_json_path(self) -> Path: """Get path to channels.json file.""" return self.get_path('channels.json') def get_channels_txt_path(self) -> Path: """Get path to channels.txt file.""" return self.get_path('channels.txt') def get_songlist_path(self) -> Path: """Get path to songList.json file.""" return self.get_path('songList.json') def get_songlist_tracking_path(self) -> Path: """Get path to songlist_tracking.json file.""" return self.get_path('songlist_tracking.json') def get_karaoke_tracking_path(self) -> Path: """Get path to karaoke_tracking.json file.""" return self.get_path('karaoke_tracking.json') def get_server_duplicates_tracking_path(self) -> Path: """Get path to server_duplicates_tracking.json file.""" return self.get_path('server_duplicates_tracking.json') def get_manual_videos_path(self) -> Path: """Get path to manual_videos.json file.""" return self.get_path('manual_videos.json') def get_songs_path(self) -> Path: """Get path to songs.json file.""" return self.get_path('songs.json') def get_channel_cache_dir(self) -> Path: """Get path to channel_cache directory.""" return self.get_path('channel_cache') def get_channel_cache_path(self, channel_id: str) -> Path: """Get path to a specific channel cache file.""" return self.get_channel_cache_dir() / f"{channel_id}.json" def get_download_plan_cache_path(self, plan_name: str, **kwargs) -> Path: """Get path to download plan cache file.""" # Create a hash from kwargs for unique cache files import hashlib if kwargs: kwargs_str = str(sorted(kwargs.items())) hash_suffix = hashlib.md5(kwargs_str.encode()).hexdigest()[:8] plan_name = f"{plan_name}_{hash_suffix}" return self.get_path(f"plan_latest_per_channel_{plan_name}.json") def get_unmatched_report_path(self, timestamp: Optional[str] = None) -> Path: """Get path to unmatched songs report file.""" if timestamp: return self.get_path(f"unmatched_songs_report_{timestamp}.json") return self.get_path("unmatched_songs_report.json") def ensure_data_dir_exists(self) -> None: """Ensure the data directory exists.""" self.data_dir.mkdir(parents=True, exist_ok=True) def list_data_files(self) -> list: """List all files in the data directory.""" if not self.data_dir.exists(): return [] files = [] for file_path in self.data_dir.iterdir(): if file_path.is_file(): files.append(file_path.name) return sorted(files) def file_exists(self, filename: str) -> bool: """Check if a file exists in the data directory.""" return self.get_path(filename).exists() # Global data path manager instance _data_path_manager: Optional[DataPathManager] = None def get_data_path_manager(data_dir: Optional[str] = None) -> DataPathManager: """ Get the global data path manager instance. Args: data_dir: Optional custom data directory path Returns: DataPathManager instance """ global _data_path_manager if _data_path_manager is None or data_dir is not None: _data_path_manager = DataPathManager(data_dir) return _data_path_manager def get_data_path(filename: str, data_dir: Optional[str] = None) -> Path: """ Get the full path to a file in the data directory. Args: filename: Name of the file data_dir: Optional custom data directory path Returns: Full path to the file """ return get_data_path_manager(data_dir).get_path(filename) def get_data_dir(data_dir: Optional[str] = None) -> Path: """ Get the configured data directory path. Args: data_dir: Optional custom data directory path Returns: Path to the data directory """ return get_data_path_manager(data_dir).data_dir