Work done so far
This commit is contained in:
+242
-9
@@ -13,6 +13,13 @@ namespace PainoBar_Helper
|
||||
private StreamWriter? _inputWriter;
|
||||
private bool _isConnected;
|
||||
private readonly object _lockObject = new object();
|
||||
private PianobarEventMonitor? _eventMonitor;
|
||||
private bool _receivedStdOut;
|
||||
private bool _receivedStdErr;
|
||||
private readonly string _ioLogPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"PianobarHelper",
|
||||
"pianobar-io.log");
|
||||
|
||||
public event EventHandler<SongChangedEventArgs>? SongChanged;
|
||||
public event EventHandler<string>? StationChanged;
|
||||
@@ -52,32 +59,68 @@ namespace PainoBar_Helper
|
||||
public string? CurrentStation { get; private set; }
|
||||
public bool IsPlaying { get; private set; } = true;
|
||||
|
||||
public void Connect(string pianobarPath, string? configPath = null)
|
||||
public void Connect(string pianobarPath, string? configPath = null, string? eventFilePath = null)
|
||||
{
|
||||
if (IsConnected)
|
||||
throw new InvalidOperationException("Already connected to Pianobar.");
|
||||
|
||||
// Check if pianobar is already running
|
||||
_receivedStdOut = false;
|
||||
_receivedStdErr = false;
|
||||
|
||||
// Check if pianobar is already running (check both with and without .exe)
|
||||
var existingProcesses = Process.GetProcessesByName("pianobar");
|
||||
if (existingProcesses.Length == 0)
|
||||
{
|
||||
// Also try without extension
|
||||
string processName = Path.GetFileNameWithoutExtension(pianobarPath);
|
||||
existingProcesses = Process.GetProcessesByName(processName);
|
||||
}
|
||||
|
||||
if (existingProcesses.Length > 0)
|
||||
{
|
||||
foreach (var proc in existingProcesses)
|
||||
proc.Dispose();
|
||||
throw new InvalidOperationException("Pianobar is already running. Please close existing instances.");
|
||||
throw new InvalidOperationException("Pianobar is already running. Please close existing instances before connecting.");
|
||||
}
|
||||
|
||||
// Validate the pianobar path exists
|
||||
string fullPath = pianobarPath;
|
||||
|
||||
// If it's just a filename or not found, try to find it in PATH
|
||||
if (!File.Exists(fullPath))
|
||||
{
|
||||
string? foundPath = FindInPath(pianobarPath);
|
||||
if (!string.IsNullOrEmpty(foundPath))
|
||||
{
|
||||
fullPath = foundPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FileNotFoundException(
|
||||
$"Pianobar executable not found.\n\n" +
|
||||
$"Path checked: {pianobarPath}\n" +
|
||||
$"Full path: {Path.GetFullPath(pianobarPath)}\n\n" +
|
||||
$"Please verify:\n" +
|
||||
$"1. The path in Settings is correct\n" +
|
||||
$"2. Pianobar is installed\n" +
|
||||
$"3. The file exists at the specified location");
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = pianobarPath,
|
||||
FileName = fullPath,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardInput = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true,
|
||||
CreateNoWindow = true, // Run in background without terminal window
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
StandardOutputEncoding = Encoding.UTF8,
|
||||
StandardErrorEncoding = Encoding.UTF8
|
||||
StandardErrorEncoding = Encoding.UTF8,
|
||||
WorkingDirectory = Path.GetDirectoryName(fullPath) ?? Environment.CurrentDirectory
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(configPath))
|
||||
@@ -91,13 +134,35 @@ namespace PainoBar_Helper
|
||||
_process.Exited += Process_Exited;
|
||||
_process.EnableRaisingEvents = true;
|
||||
|
||||
_process.Start();
|
||||
bool started = _process.Start();
|
||||
|
||||
if (!started)
|
||||
{
|
||||
throw new InvalidOperationException("Process.Start() returned false. The process could not be started.");
|
||||
}
|
||||
|
||||
// Verify the process actually started
|
||||
if (_process.HasExited)
|
||||
{
|
||||
throw new InvalidOperationException($"Process started but immediately exited with code: {_process.ExitCode}");
|
||||
}
|
||||
|
||||
_inputWriter = _process.StandardInput;
|
||||
_process.BeginOutputReadLine();
|
||||
_process.BeginErrorReadLine();
|
||||
|
||||
IsConnected = true;
|
||||
RaiseEvent("Connected to Pianobar");
|
||||
|
||||
// Start monitoring event file if provided
|
||||
if (!string.IsNullOrEmpty(eventFilePath))
|
||||
{
|
||||
_eventMonitor = new PianobarEventMonitor();
|
||||
_eventMonitor.EventReceived += OnEventReceived;
|
||||
_eventMonitor.StartMonitoring(eventFilePath);
|
||||
}
|
||||
|
||||
RaiseEvent($"Pianobar started successfully (PID: {_process.Id})");
|
||||
RaiseEvent($"I/O log: {_ioLogPath}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -106,6 +171,59 @@ namespace PainoBar_Helper
|
||||
}
|
||||
}
|
||||
|
||||
private string? FindInPath(string command)
|
||||
{
|
||||
// Check if the command is available in PATH
|
||||
var pathEnv = Environment.GetEnvironmentVariable("PATH");
|
||||
if (string.IsNullOrEmpty(pathEnv))
|
||||
return null;
|
||||
|
||||
var paths = pathEnv.Split(Path.PathSeparator);
|
||||
var extensions = new[] { ".exe", ".cmd", ".bat", "" };
|
||||
|
||||
// Remove .exe if already present in command
|
||||
string baseCommand = Path.GetFileNameWithoutExtension(command);
|
||||
string commandExt = Path.GetExtension(command);
|
||||
|
||||
foreach (var path in paths)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(path))
|
||||
continue;
|
||||
|
||||
foreach (var ext in extensions)
|
||||
{
|
||||
// If command already has extension, use it as-is
|
||||
if (!string.IsNullOrEmpty(commandExt))
|
||||
{
|
||||
var fullPath = Path.Combine(path, command);
|
||||
if (File.Exists(fullPath))
|
||||
return fullPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
var fullPath = Path.Combine(path, baseCommand + ext);
|
||||
if (File.Exists(fullPath))
|
||||
return fullPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Skip paths that cause errors
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool IsCommandInPath(string command)
|
||||
{
|
||||
return !string.IsNullOrEmpty(FindInPath(command));
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
if (!IsConnected)
|
||||
@@ -113,7 +231,33 @@ namespace PainoBar_Helper
|
||||
|
||||
Cleanup();
|
||||
IsConnected = false;
|
||||
RaiseEvent("Disconnected from Pianobar");
|
||||
RaiseEvent("Disconnected from Pianobar (process detached)");
|
||||
}
|
||||
|
||||
public void DisconnectAndKill()
|
||||
{
|
||||
if (!IsConnected)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (_process != null && !_process.HasExited)
|
||||
{
|
||||
int pid = _process.Id;
|
||||
_process.Kill();
|
||||
_process.WaitForExit(5000); // Wait up to 5 seconds for graceful exit
|
||||
RaiseEvent($"Pianobar process killed (PID: {pid})");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
RaiseEvent($"Error killing process: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Cleanup();
|
||||
IsConnected = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void SendCommand(char command)
|
||||
@@ -172,6 +316,13 @@ namespace PainoBar_Helper
|
||||
return;
|
||||
|
||||
string line = e.Data;
|
||||
if (!_receivedStdOut)
|
||||
{
|
||||
_receivedStdOut = true;
|
||||
RaiseEvent("Received output on STDOUT");
|
||||
}
|
||||
|
||||
LogIoLine("STDOUT", line);
|
||||
OutputReceived?.Invoke(this, line);
|
||||
|
||||
ParseOutput(line);
|
||||
@@ -180,15 +331,44 @@ namespace PainoBar_Helper
|
||||
private void Process_ErrorDataReceived(object? sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.Data))
|
||||
{
|
||||
if (!_receivedStdErr)
|
||||
{
|
||||
_receivedStdErr = true;
|
||||
RaiseEvent("Received output on STDERR");
|
||||
}
|
||||
|
||||
LogIoLine("STDERR", e.Data);
|
||||
OutputReceived?.Invoke(this, e.Data);
|
||||
ParseOutput(e.Data);
|
||||
}
|
||||
}
|
||||
|
||||
private void Process_Exited(object? sender, EventArgs e)
|
||||
{
|
||||
if (!_receivedStdOut)
|
||||
RaiseEvent("No STDOUT data received before process exit");
|
||||
|
||||
IsConnected = false;
|
||||
RaiseEvent("Pianobar process exited");
|
||||
}
|
||||
|
||||
private void LogIoLine(string source, string line)
|
||||
{
|
||||
try
|
||||
{
|
||||
var logDir = Path.GetDirectoryName(_ioLogPath);
|
||||
if (!string.IsNullOrEmpty(logDir))
|
||||
Directory.CreateDirectory(logDir);
|
||||
|
||||
File.AppendAllText(_ioLogPath, $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] [{source}] {line}{Environment.NewLine}");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore logging errors
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseOutput(string line)
|
||||
{
|
||||
try
|
||||
@@ -278,6 +458,51 @@ namespace PainoBar_Helper
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEventReceived(object? sender, PianobarEventData eventData)
|
||||
{
|
||||
// Update metadata from event file
|
||||
if (!string.IsNullOrEmpty(eventData.Title))
|
||||
CurrentSong = eventData.Title;
|
||||
|
||||
if (!string.IsNullOrEmpty(eventData.Artist))
|
||||
CurrentArtist = eventData.Artist;
|
||||
|
||||
if (!string.IsNullOrEmpty(eventData.Album))
|
||||
CurrentAlbum = eventData.Album;
|
||||
|
||||
if (!string.IsNullOrEmpty(eventData.CoverArt))
|
||||
CurrentAlbumArtUrl = eventData.CoverArt;
|
||||
|
||||
if (!string.IsNullOrEmpty(eventData.StationName))
|
||||
{
|
||||
CurrentStation = eventData.StationName;
|
||||
StationChanged?.Invoke(this, CurrentStation);
|
||||
}
|
||||
|
||||
// Raise event based on event type
|
||||
if (!string.IsNullOrEmpty(eventData.Event))
|
||||
{
|
||||
switch (eventData.Event.ToLower())
|
||||
{
|
||||
case "songstart":
|
||||
IsPlaying = true;
|
||||
TriggerSongChanged();
|
||||
break;
|
||||
case "songfinish":
|
||||
break;
|
||||
case "songlove":
|
||||
RaiseEvent("Song Loved");
|
||||
break;
|
||||
case "songban":
|
||||
RaiseEvent("Song Banned");
|
||||
break;
|
||||
default:
|
||||
RaiseEvent($"Event: {eventData.Event}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RaiseEvent(string eventText)
|
||||
{
|
||||
EventOccurred?.Invoke(this, new PianobarEventArgs { EventText = eventText });
|
||||
@@ -287,6 +512,14 @@ namespace PainoBar_Helper
|
||||
{
|
||||
try
|
||||
{
|
||||
// Stop event monitoring
|
||||
if (_eventMonitor != null)
|
||||
{
|
||||
_eventMonitor.EventReceived -= OnEventReceived;
|
||||
_eventMonitor.Dispose();
|
||||
_eventMonitor = null;
|
||||
}
|
||||
|
||||
if (_process != null)
|
||||
{
|
||||
_process.OutputDataReceived -= Process_OutputDataReceived;
|
||||
|
||||
Reference in New Issue
Block a user