using System.Text; using System.Text.Json; namespace PainoBar_Helper { /// /// Manages Pianobar profiles including creation, deletion, and configuration file generation /// public class ProfileManager { private const string ProfilesFileName = "profiles.json"; private readonly string _profilesFilePath; private List _profiles; private string? _activeProfileName; public ProfileManager() { string appDataPath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "PianobarHelper" ); Directory.CreateDirectory(appDataPath); _profilesFilePath = Path.Combine(appDataPath, ProfilesFileName); _profiles = new List(); LoadProfiles(); } public event EventHandler? ProfilesChanged; public event EventHandler? ActiveProfileChanged; public IReadOnlyList Profiles => _profiles.AsReadOnly(); public string? ActiveProfileName => _activeProfileName; public Profile? GetActiveProfile() { if (string.IsNullOrEmpty(_activeProfileName)) return null; return _profiles.FirstOrDefault(p => p.Name == _activeProfileName); } public void SetActiveProfile(string profileName) { var profile = _profiles.FirstOrDefault(p => p.Name == profileName); if (profile == null) throw new ArgumentException($"Profile '{profileName}' does not exist."); _activeProfileName = profileName; SaveProfiles(); // Save immediately to persist the active profile ActiveProfileChanged?.Invoke(this, EventArgs.Empty); } public void CreateProfile(string name, ProfileSettings settings) { if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Profile name cannot be empty."); if (_profiles.Any(p => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase))) throw new InvalidOperationException($"Profile '{name}' already exists."); var profile = new Profile { Name = name, Settings = settings }; _profiles.Add(profile); if (_profiles.Count == 1) _activeProfileName = name; SaveProfiles(); ProfilesChanged?.Invoke(this, EventArgs.Empty); if (settings.OverrideCredentials && !string.IsNullOrEmpty(settings.ConfigFilePath)) GenerateConfigFile(profile); } public void UpdateProfile(string name, ProfileSettings settings) { var profile = _profiles.FirstOrDefault(p => p.Name == name); if (profile == null) throw new ArgumentException($"Profile '{name}' does not exist."); profile.Settings = settings; SaveProfiles(); ProfilesChanged?.Invoke(this, EventArgs.Empty); if (settings.OverrideCredentials && !string.IsNullOrEmpty(settings.ConfigFilePath)) GenerateConfigFile(profile); } public void DeleteProfile(string name) { var profile = _profiles.FirstOrDefault(p => p.Name == name); if (profile == null) return; _profiles.Remove(profile); if (_activeProfileName == name) { _activeProfileName = _profiles.FirstOrDefault()?.Name; ActiveProfileChanged?.Invoke(this, EventArgs.Empty); } SaveProfiles(); ProfilesChanged?.Invoke(this, EventArgs.Empty); } private void LoadProfiles() { if (!File.Exists(_profilesFilePath)) { _profiles = new List(); return; } try { string json = File.ReadAllText(_profilesFilePath); var data = JsonSerializer.Deserialize(json); if (data != null) { _profiles = data.Profiles ?? new List(); _activeProfileName = data.ActiveProfile; } } catch { _profiles = new List(); } } private void SaveProfiles() { var data = new ProfileData { Profiles = _profiles, ActiveProfile = _activeProfileName }; string json = JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(_profilesFilePath, json); } private void GenerateConfigFile(Profile profile) { var settings = profile.Settings; if (string.IsNullOrEmpty(settings.ConfigFilePath)) return; try { var configDir = Path.GetDirectoryName(settings.ConfigFilePath); if (!string.IsNullOrEmpty(configDir)) Directory.CreateDirectory(configDir); // Generate event command script path var eventCmdPath = Path.Combine(configDir, "eventcmd.cmd"); GenerateEventCommandScript(eventCmdPath, profile); var sb = new StringBuilder(); sb.AppendLine("# Pianobar configuration file"); sb.AppendLine($"# Generated by Pianobar Helper for profile: {profile.Name}"); sb.AppendLine(); if (settings.OverrideCredentials) { if (!string.IsNullOrEmpty(settings.PianobarUser)) sb.AppendLine($"user = {settings.PianobarUser}"); if (!string.IsNullOrEmpty(settings.PianobarPassword)) sb.AppendLine($"password = {settings.PianobarPassword}"); sb.AppendLine(); } // Add event command directive sb.AppendLine($"event_command = \"{eventCmdPath}\""); sb.AppendLine(); // Additional settings for better integration sb.AppendLine("# Output formatting"); sb.AppendLine("format_nowplaying_song = %t|%a|%l|%r"); sb.AppendLine("format_station_list = %i) %n"); sb.AppendLine(); File.WriteAllText(settings.ConfigFilePath, sb.ToString()); } catch (Exception ex) { throw new InvalidOperationException($"Failed to generate config file: {ex.Message}", ex); } } private void GenerateEventCommandScript(string scriptPath, Profile profile) { try { // Create the directory for the event output file var appDataPath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "PianobarHelper" ); Directory.CreateDirectory(appDataPath); var eventOutputPath = Path.Combine(appDataPath, $"events_{profile.Name}.json"); // Generate Windows batch script for event handling var sb = new StringBuilder(); sb.AppendLine("@echo off"); sb.AppendLine("REM Pianobar event command script"); sb.AppendLine("REM Generated by Pianobar Helper"); sb.AppendLine(); sb.AppendLine($"set \"EVENT_OUTPUT={eventOutputPath}\""); sb.AppendLine(); sb.AppendLine("REM Create JSON output for the event"); sb.AppendLine("("); sb.AppendLine(" echo {"); sb.AppendLine(" echo \"event\": \"%1\","); sb.AppendLine(" echo \"timestamp\": \"%date% %time%\","); sb.AppendLine(" echo \"artist\": \"%artist%\","); sb.AppendLine(" echo \"title\": \"%title%\","); sb.AppendLine(" echo \"album\": \"%album%\","); sb.AppendLine(" echo \"coverArt\": \"%coverArt%\","); sb.AppendLine(" echo \"stationName\": \"%stationName%\","); sb.AppendLine(" echo \"songDuration\": \"%songDuration%\","); sb.AppendLine(" echo \"songPlayed\": \"%songPlayed%\","); sb.AppendLine(" echo \"rating\": \"%rating%\","); sb.AppendLine(" echo \"detailUrl\": \"%detailUrl%\""); sb.AppendLine(" echo }"); sb.AppendLine(") > \"%EVENT_OUTPUT%\""); sb.AppendLine(); sb.AppendLine("exit /b 0"); File.WriteAllText(scriptPath, sb.ToString()); } catch (Exception ex) { throw new InvalidOperationException($"Failed to generate event command script: {ex.Message}", ex); } } public string? GetActiveConfigPath() { var activeProfile = GetActiveProfile(); if (activeProfile?.Settings.OverrideCredentials == true) return activeProfile.Settings.ConfigFilePath; return null; } public string? GetEventOutputPath() { var activeProfile = GetActiveProfile(); if (activeProfile == null) return null; var appDataPath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "PianobarHelper" ); return Path.Combine(appDataPath, $"events_{activeProfile.Name}.json"); } } public class Profile { public string Name { get; set; } = string.Empty; public ProfileSettings Settings { get; set; } = new ProfileSettings(); } public class ProfileSettings { // Pianobar Path public string PianobarPath { get; set; } = "pianobar.exe"; // Output Settings public bool EnableTextFile { get; set; } public string TextFilePath { get; set; } = string.Empty; public bool EnableWebServer { get; set; } public int WebServerPort { get; set; } = 8080; public bool EnableToastNotifications { get; set; } public bool EnableSMTC { get; set; } // Pianobar Config & Auth public bool OverrideCredentials { get; set; } public string ConfigFilePath { get; set; } = string.Empty; public string PianobarUser { get; set; } = string.Empty; public string PianobarPassword { get; set; } = string.Empty; // Process Management public bool KillProcessOnDisconnect { get; set; } } internal class ProfileData { public List? Profiles { get; set; } public string? ActiveProfile { get; set; } } }