From 80d6395c8ed6f7f02bb88fbaf4e29b6240138062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Marchal?= Date: Sun, 29 Aug 2021 00:32:41 +0200 Subject: [PATCH] First implementation of MpcNET Exceptions thrown for playback to investigate, but get data is okay --- App.xaml.cs | 2 - Handlers/MPDHandler.cs | 432 ++-- MPDCtrl/BinaryDownloader.cs | 1418 ------------- MPDCtrl/Classes/AlbumImage.cs | 29 - MPDCtrl/Classes/Playlist.cs | 53 - MPDCtrl/Classes/Result.cs | 52 - MPDCtrl/Classes/Song.cs | 232 --- MPDCtrl/Classes/Status.cs | 149 -- MPDCtrl/MPC.cs | 3662 --------------------------------- Views/MainWindow.xaml.cs | 73 +- Views/Settings.xaml | 2 +- Views/Settings.xaml.cs | 2 +- unison.csproj | 4 + unison.sln | 21 +- 14 files changed, 297 insertions(+), 5834 deletions(-) delete mode 100644 MPDCtrl/BinaryDownloader.cs delete mode 100644 MPDCtrl/Classes/AlbumImage.cs delete mode 100644 MPDCtrl/Classes/Playlist.cs delete mode 100644 MPDCtrl/Classes/Result.cs delete mode 100644 MPDCtrl/Classes/Song.cs delete mode 100644 MPDCtrl/Classes/Status.cs delete mode 100644 MPDCtrl/MPC.cs diff --git a/App.xaml.cs b/App.xaml.cs index 31095d5..eb9e3e1 100644 --- a/App.xaml.cs +++ b/App.xaml.cs @@ -27,8 +27,6 @@ namespace unison Systray = (TaskbarIcon)FindResource("SystrayTaskbar"); Current.Properties["systray"] = Systray; - - MPD.Start(); } protected override void OnExit(ExitEventArgs e) diff --git a/Handlers/MPDHandler.cs b/Handlers/MPDHandler.cs index 87385b6..ea6f73d 100644 --- a/Handlers/MPDHandler.cs +++ b/Handlers/MPDHandler.cs @@ -1,37 +1,45 @@ using System; +using System.Collections.Generic; using System.Diagnostics; +using System.IO; +using System.Net; +using System.Text.RegularExpressions; +using System.Threading; using System.Threading.Tasks; -using System.Windows; -using System.Windows.Threading; -using MPDCtrl.Models; +using System.Windows.Media.Imaging; +using MpcNET; +using MpcNET.Commands.Database; +using MpcNET.Commands.Playback; +using MpcNET.Commands.Reflection; +using MpcNET.Commands.Status; namespace unison { public class MPDHandler { - private readonly MPC _mpd = new(); - public bool _connected; + public string _version; public int _currentVolume; public bool _currentRandom; public bool _currentRepeat; public bool _currentSingle; public bool _currentConsume; - public double _currentElapsed; + public double _currentTime; + public double _totalTime; - private Status _currentStatus = null; - private SongInfoEx _currentSong = null; - private AlbumImage _currentAlbumCover = null; + BitmapFrame _cover; - public double _elapsed; - private double _time; + public static MpdStatus BOGUS_STATUS = new MpdStatus(0, false, false, false, false, -1, -1, -1, MpdState.Unknown, -1, -1, -1, -1, TimeSpan.Zero, TimeSpan.Zero, -1, -1, -1, -1, -1, "", ""); + public MpdStatus CurrentStatus { get; private set; } = BOGUS_STATUS; + + MpcNET.Types.IMpdFile CurrentSong { get; set; } private readonly System.Timers.Timer _elapsedTimer; private async void ElapsedTimer(object sender, System.Timers.ElapsedEventArgs e) { - if ((_elapsed < _time) && (_mpd.MpdStatus.MpdState == Status.MpdPlayState.Play)) + if ((_currentTime < _totalTime) && (CurrentStatus.State == MpdState.Play)) { - _elapsed += 0.5; + _currentTime += 0.5; await Task.Delay(5); } else @@ -40,267 +48,297 @@ namespace unison } } - bool IsBusy = false; + private MpcConnection _connection; + private IPEndPoint _mpdEndpoint; + + private CancellationTokenSource cancelToken; public MPDHandler() { - _mpd.IsBusy += new MPC.IsBusyEvent(OnMpcIsBusy); + cancelToken = new CancellationTokenSource(); - _mpd.MpdIdleConnected += new MPC.IsMpdIdleConnectedEvent(OnMpdIdleConnected); - - _mpd.ConnectionStatusChanged += new MPC.ConnectionStatusChangedEvent(OnConnectionStatusChanged); - _mpd.ConnectionError += new MPC.ConnectionErrorEvent(OnConnectionError); - - _mpd.MpdPlayerStatusChanged += new MPC.MpdPlayerStatusChangedEvent(OnMpdPlayerStatusChanged); - _mpd.MpdCurrentQueueChanged += new MPC.MpdCurrentQueueChangedEvent(OnMpdCurrentQueueChanged); - - _mpd.MpdAlbumArtChanged += new MPC.MpdAlbumArtChangedEvent(OnAlbumArtChanged); + Initialize(); _elapsedTimer = new System.Timers.Timer(500); _elapsedTimer.Elapsed += new System.Timers.ElapsedEventHandler(ElapsedTimer); - - DispatcherTimer timer = new DispatcherTimer(); - timer.Interval = TimeSpan.FromSeconds(2); - timer.Tick += QueryStatus; - timer.Start(); } - private void OnMpcIsBusy(MPC sender, bool on) + private async void Initialize() { - IsBusy = on; - } - - public void Start() - { - Task.Run(() => _mpd.MpdIdleConnect(Properties.Settings.Default.mpd_host, Properties.Settings.Default.mpd_port)); - } - - private void OnMpdIdleConnected(MPC sender) - { - Trace.WriteLine($"Connection to mpd {_mpd.MpdVerText}..."); - LoadInitialData(); - } - - private void OnConnectionStatusChanged(MPC sender, MPC.ConnectionStatus status) - { - Trace.WriteLine("Connection changed..."); - } - - private void OnConnectionError(MPC sender, string msg) - { - Trace.WriteLine("Connection ERROR!"); - LoadInitialData(); - } - - private void OnMpdPlayerStatusChanged(MPC sender) - { - Trace.WriteLine("Status changed..."); - - UpdateStatus(); - } - - private void OnMpdCurrentQueueChanged(MPC sender) - { - Trace.WriteLine("Queue changed..."); - - UpdateStatus(); - } - - private void OnAlbumArtChanged(MPC sender) - { - // AlbumArt - if (Application.Current == null) { return; } - Application.Current.Dispatcher.Invoke(() => + var token = cancelToken.Token; + _connection = await Connect(token); + if (_connection.IsConnected) { - if ((!_mpd.AlbumCover.IsDownloading) && _mpd.AlbumCover.IsSuccess) + _connected = true; + _version = _connection.Version; + } + Trace.WriteLine("is connected: " + _connected); + Trace.WriteLine("version: " + _version); + + await UpdateStatusAsync(); + await UpdateSongAsync(); + + Loop(token); + } + + private async Task Connect(CancellationToken token) + { + IPAddress.TryParse(Properties.Settings.Default.mpd_host, out IPAddress ipAddress); + + _mpdEndpoint = new IPEndPoint(ipAddress, Properties.Settings.Default.mpd_port); + MpcConnection connection = new MpcConnection(_mpdEndpoint); + await connection.ConnectAsync(token); + + if (!string.IsNullOrEmpty(Properties.Settings.Default.mpd_password)) + { + MpcNET.Message.IMpdMessage result = await connection.SendAsync(new PasswordCommand(Properties.Settings.Default.mpd_password)); + if (!result.IsResponseValid) { - if ((_mpd.MpdCurrentSong != null) && (_mpd.AlbumCover.AlbumImageSource != null)) + string mpdError = result.Response?.Result?.MpdError; + } + } + + return connection; + } + + private void Loop(CancellationToken token) + { + Task.Run(async () => + { + while (true) + { + try { - if (!String.IsNullOrEmpty(_mpd.MpdCurrentSong.File)) - { - if (_mpd.MpdCurrentSong.File == _mpd.AlbumCover.SongFilePath) - { - Trace.WriteLine("found cover"); - _currentAlbumCover = _mpd.AlbumCover; - } - } + if (token.IsCancellationRequested || _connection == null) + break; + + Trace.WriteLine("loop"); + var idleChanges = await _connection.SendAsync(new IdleCommand("stored_playlist playlist player mixer output options")); + + if (idleChanges.IsResponseValid) + await HandleIdleResponseAsync(idleChanges.Response.Content); + else + throw new Exception(idleChanges.Response?.Content); + } + catch (Exception e) + { + System.Diagnostics.Debug.WriteLine($"Error in Idle connection thread: {e.Message}"); } } - }); + + }).ConfigureAwait(false); } - public async void LoadInitialData() + private async Task HandleIdleResponseAsync(string subsystems) { - //todo : test if the password works - - IsBusy = true; - await Task.Delay(5); - - CommandResult result = await _mpd.MpdIdleSendPassword(Properties.Settings.Default.mpd_password); - - if (result.IsSuccess) + if (subsystems.Contains("player") || subsystems.Contains("mixer") || subsystems.Contains("output") || subsystems.Contains("options")) { - _connected = await _mpd.MpdCommandConnectionStart(Properties.Settings.Default.mpd_host, Properties.Settings.Default.mpd_port, Properties.Settings.Default.mpd_password); - if (_connected) + await UpdateStatusAsync(); + + if (subsystems.Contains("player")) { - await _mpd.MpdSendUpdate(); - - result = await _mpd.MpdIdleQueryStatus(); - await Task.Delay(5); - - if (result.IsSuccess) - { - _currentVolume = _mpd.MpdStatus.MpdVolume; - _currentRandom = _mpd.MpdStatus.MpdRandom; - _currentRepeat = _mpd.MpdStatus.MpdRepeat; - _currentSingle = _mpd.MpdStatus.MpdSingle; - _currentConsume = _mpd.MpdStatus.MpdConsume; - _currentElapsed = _mpd.MpdStatus.MpdSongElapsed; - } - - await Task.Delay(5); - CommandResult song = await _mpd.MpdIdleQueryCurrentSong(); - await Task.Delay(5); - if (song != null) - _currentSong = _mpd.MpdCurrentSong; - - await Task.Delay(5); - _mpd.MpdIdleStart(); - await Task.Delay(5); - UpdateStatus(); + await UpdateSongAsync(); } } } - public async void QueryStatus(object sender, EventArgs e) + bool _isUpdatingStatus = false; + private async Task UpdateStatusAsync() { - if (IsBusy) + if (_connection == null) return; + + if (_isUpdatingStatus) return; + _isUpdatingStatus = true; + + try + { + MpcNET.Message.IMpdMessage response = await _connection.SendAsync(new StatusCommand()); + if (response != null && response.IsResponseValid) + { + CurrentStatus = response.Response.Content; + UpdateStatus(); + } + else + throw new Exception(); + } + catch + { + await Connect(cancelToken.Token); + } + _isUpdatingStatus = false; + } + + private async Task UpdateSongAsync() + { + MpcNET.Message.IMpdMessage response = await _connection.SendAsync(new CurrentSongCommand()); + if (response != null && response.IsResponseValid) + { + CurrentSong = response.Response.Content; + UpdateSong(); + } + } + + public async Task SafelySendCommandAsync(IMpcCommand command) + { + try + { + var response = await _connection.SendAsync(command); + if (!response.IsResponseValid) + { + // If we have an MpdError string, only show that as the error to avoid extra noise + var mpdError = response.Response?.Result?.MpdError; + if (mpdError != null && mpdError != "") + throw new Exception(mpdError); + else + throw new Exception($"Invalid server response: {response}."); + } + + return response.Response.Content; + } + catch (Exception e) + { + Trace.WriteLine($"Sending {command.GetType().Name} failed: {e.Message}"); + } + + return default(T); + } + + private async void GetAlbumBitmap(string path, CancellationToken token = default) + { + List data = new List(); + try + { + if (_connection == null) // We got cancelled + return; + + long totalBinarySize = 9999; + long currentSize = 0; + + do + { + var albumReq = await _connection.SendAsync(new AlbumArtCommand(path, currentSize)); + if (!albumReq.IsResponseValid) break; + + var response = albumReq.Response.Content; + if (response.Binary == 0) break; // MPD isn't giving us any more data, let's roll with what we have. + + totalBinarySize = response.Size; + currentSize += response.Binary; + data.AddRange(response.Data); + Debug.WriteLine($"Downloading albumart: {currentSize}/{totalBinarySize}"); + } while (currentSize < totalBinarySize && !token.IsCancellationRequested); + } + catch (Exception e) + { + Debug.WriteLine("Exception caught while getting albumart: " + e); + return; + } + + if (data.Count == 0) + { + Trace.WriteLine("empty cover"); + _cover = null; + return; + } + + using var stream = new MemoryStream(data.ToArray()); + _cover = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); + UpdateCover(); + } + + public void UpdateSong() + { + if (!_connected) + return; + if (CurrentSong == null) return; - Trace.WriteLine("Querying status..."); + Trace.WriteLine($"[DEBUG] Song: name[{CurrentSong.Title}] artist[{CurrentSong.Artist}] album[{CurrentSong.Album}] uri[{CurrentSong.Path}]"); - CommandResult result = await _mpd.MpdQueryStatus(); - await Task.Delay(5); + _currentTime = CurrentStatus.Elapsed.TotalSeconds; + _totalTime = CurrentSong.Time; + if (!_elapsedTimer.Enabled) + _elapsedTimer.Start(); - if (result.IsSuccess) - { - result = await _mpd.MpdQueryCurrentSong(); - await Task.Delay(5); - - if (result.IsSuccess) - { - UpdateStatus(); - } - } + string uri = Regex.Escape(CurrentSong.Path); + GetAlbumBitmap(uri); } - public async void UpdateStatus() + public void UpdateCover() + { + + } + + public void UpdateStatus() { if (!_connected) return; - await Task.Delay(50); + if (CurrentStatus == null) + return; - _currentStatus = _mpd.MpdStatus; + _currentRandom = CurrentStatus.Random; + _currentRepeat = CurrentStatus.Repeat; + _currentConsume = CurrentStatus.Consume; + _currentSingle = CurrentStatus.Single; + _currentVolume = CurrentStatus.Volume; - _currentRandom = _mpd.MpdStatus.MpdRandom; - _currentRepeat = _mpd.MpdStatus.MpdRepeat; - _currentConsume = _mpd.MpdStatus.MpdConsume; - _currentSingle = _mpd.MpdStatus.MpdSingle; - - _currentVolume = _mpd.MpdStatus.MpdVolume; - _currentElapsed = _mpd.MpdStatus.MpdSongElapsed; - - _currentSong = _mpd.MpdCurrentSong; - - _time = _mpd.MpdStatus.MpdSongTime; - - _elapsed = _mpd.MpdStatus.MpdSongElapsed; - - if (_mpd.MpdStatus.MpdState == Status.MpdPlayState.Play) - { - if (!_elapsedTimer.Enabled) - _elapsedTimer.Start(); - } - else - { - _elapsedTimer.Stop(); - } - - if (_currentSong != null) - await _mpd.MpdQueryAlbumArt(_currentSong.File, false); + Trace.WriteLine($"[DEBUG] Status: volume[{CurrentStatus.Volume}] random[{CurrentStatus.Random}] consume[{CurrentStatus.Consume}] State[{CurrentStatus.State}] Bitrate[{CurrentStatus.Bitrate}]"); } - public SongInfoEx GetCurrentSong() => _currentSong; - public Status GetStatus() => _currentStatus; - public AlbumImage GetCover() => _currentAlbumCover; + public MpcNET.Types.IMpdFile GetCurrentSong() => CurrentSong; + public MpdStatus GetStatus() => CurrentStatus; + public BitmapFrame GetCover() => _cover; + public string GetVersion() => _version; public async void Prev() { - if (!IsBusy) - await _mpd.MpdPlaybackPrev(_currentVolume); + await SafelySendCommandAsync(new PreviousCommand()); } public async void Next() { - if (!IsBusy) - await _mpd.MpdPlaybackNext(_currentVolume); + await SafelySendCommandAsync(new NextCommand()); } public async void PlayPause() { - if (IsBusy) - return; - if (_mpd.MpdStatus.MpdState == Status.MpdPlayState.Play) - await _mpd.MpdPlaybackPause(); - else if (_mpd.MpdStatus.MpdState == Status.MpdPlayState.Pause) - await _mpd.MpdPlaybackPlay(_currentVolume); + await SafelySendCommandAsync(new PauseResumeCommand()); } public async void Random() { - if (!IsBusy) - await _mpd.MpdSetRandom(!_currentRandom); + await SafelySendCommandAsync(new RandomCommand(!_currentRandom)); } public async void Repeat() { - if (!IsBusy) - await _mpd.MpdSetRepeat(!_currentRepeat); + await SafelySendCommandAsync(new RepeatCommand(!_currentRepeat)); } public async void Single() { - if (!IsBusy) - await _mpd.MpdSetSingle(!_currentSingle); + await SafelySendCommandAsync(new SingleCommand(!_currentSingle)); } public async void Consume() { - if (!IsBusy) - await _mpd.MpdSetConsume(!_currentConsume); + await SafelySendCommandAsync(new ConsumeCommand(!_currentConsume)); } public async void SetVolume(int value) { - if (!IsBusy) - await _mpd.MpdSetVolume(value); + await SafelySendCommandAsync(new SetVolumeCommand((byte)value)); } - public async void SetTime(int value) + public async void SetTime(double value) { - if (!IsBusy) - await _mpd.MpdPlaybackSeek(_mpd.MpdCurrentSong.Id, value); + await SafelySendCommandAsync(new SeekCurCommand(value)); } public bool IsPlaying() { - return _currentStatus?.MpdState == MPDCtrl.Models.Status.MpdPlayState.Play; - } - - public string GetVersion() - { - return _mpd.MpdVerText; + return CurrentStatus?.State == MpdState.Play; } } } diff --git a/MPDCtrl/BinaryDownloader.cs b/MPDCtrl/BinaryDownloader.cs deleted file mode 100644 index 47a8156..0000000 --- a/MPDCtrl/BinaryDownloader.cs +++ /dev/null @@ -1,1418 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net.Sockets; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Media.Imaging; - -namespace MPDCtrl.Models -{ - public class BinaryDownloader - { - private static TcpClient _binaryConnection = new(); - private StreamReader _binaryReader; - private StreamWriter _binaryWriter; - - private AlbumImage _albumCover = new(); - public AlbumImage AlbumCover { get => _albumCover; } - - private string _host; - private int _port; - //private string _password; - - private string MpdVersion { get; set; } - - public BinaryDownloader() - { - - } - - public async Task MpdBinaryConnectionStart(string host, int port, string password) - { - ConnectionResult r = await MpdBinaryConnect(host, port); - - if (r.IsSuccess) - { - CommandResult d = await MpdBinarySendPassword(password); - - if (d.IsSuccess) - { - // ここでIdleにして、以降はnoidle + cmd + idleの組み合わせでやる。 - // ただし、実際にはidleのあとReadしていないからタイムアウトで切断されてしまう模様。 - - // awaitが必要だった。 - //d = await MpdSendIdle(); - - return d.IsSuccess; - } - } - - return false; - } - - private async Task MpdBinaryConnect(string host, int port) - { - ConnectionResult result = new(); - - //IsMpdCommandConnected = false; - - _binaryConnection = new TcpClient(); - - _host = host; - _port = port; - - //DebugCommandOutput?.Invoke(this, "TCP Command Connection: Connecting." + "\n" + "\n"); - - //ConnectionState = ConnectionStatus.Connecting; - - //Debug.WriteLine("TCP Binary Connection: Connecting. " + host + " " + port.ToString()); - - try - { - await _binaryConnection.ConnectAsync(_host, _port); - - if (_binaryConnection.Client == null) - { - Debug.WriteLine("_binaryConnection.Client == null. " + host + " " + port.ToString()); - - result.ErrorMessage = "_binaryConnection.Client == null"; - - return result; - } - - if (_binaryConnection.Client.Connected) - { - //DebugCommandOutput?.Invoke(this, "TCP Command Connection: Connection established." + "\n" + "\n"); - - //ConnectionState = ConnectionStatus.Connected; - - var tcpStream = _binaryConnection.GetStream(); - //tcpStream.ReadTimeout = System.Threading.Timeout.Infinite; - // - tcpStream.ReadTimeout = 3000; - - _binaryReader = new(tcpStream); - _binaryWriter = new(tcpStream); - _binaryWriter.AutoFlush = true; - - string response = await _binaryReader.ReadLineAsync(); - - if (response != null) - { - if (response.StartsWith("OK MPD ")) - { - MpdVersion = response.Replace("OK MPD ", string.Empty).Trim(); - - //Debug.WriteLine("TCP Binary Connection: Connected. MPD " + VerText); - - //DebugCommandOutput?.Invoke(this, "<<<<" + response.Trim() + "\n" + "\n"); - - //IsMpdCommandConnected = true; - - result.IsSuccess = true; - - // Done for now. - } - } - else - { - Debug.WriteLine("**** TCP Binary Connection: MPD did not respond with proper respose@MpdBinaryConnect"); - - //DebugCommandOutput?.Invoke(this, "TCP Command Connection: MPD did not respond with proper respose." + "\n" + "\n"); - - //ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - //ConnectionError?.Invoke(this, "TCP connection error: MPD did not respond with proper respose."); - } - } - else - { - //? - - Debug.WriteLine("**** !client.Client.Connected@MpdBinaryConnect"); - - //DebugCommandOutput?.Invoke(this, "TCP Command Connection: FAIL to established... Client not connected." + "\n" + "\n"); - - //ConnectionState = ConnectionStatus.NeverConnected; - - //ConnectionError?.Invoke(this, "TCP Command Connection: FAIL to established... Client not connected."); - } - } - catch (Exception e) - { - Debug.WriteLine("**** Exception@MpdBinaryConnect: " + e.Message); - //DebugCommandOutput?.Invoke(this, "TCP Command Connection: Error while connecting. Fail to connect (Exception): " + e.Message + "\n" + "\n"); - - //ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - //ConnectionError?.Invoke(this, "TCP connection error: " + e.Message); - } - - return result; - } - - private async Task MpdBinarySendPassword(string password = "") - { - //_password = password; - - CommandResult ret = new(); - - if (string.IsNullOrEmpty(password)) - { - ret.IsSuccess = true; - ret.ResultText = "OK";//Or OK - ret.ErrorMessage = ""; - - return ret; - } - - string cmd = "password " + password + "\n"; - - return await MpdBinarySendCommand(cmd); - - } - - private async Task MpdBinarySendCommand(string cmd, bool isAutoIdling = false) - { - CommandResult ret = new(); - - if (_binaryConnection.Client == null) - { - return ret; - } - - if ((_binaryWriter == null) || (_binaryReader == null)) - { - Debug.WriteLine("@MpdBinarySendCommand: " + "_commandWriter or _commandReader is null"); - - //ret.IsSuccess = false; - //ret.ErrorMessage = "_commandWriter or _commandReader is null"; - - //DebugCommandOutput?.Invoke(this, string.Format("################ Error :@{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "MpdSendCommand", "_commandWriter or _commandReader is null", cmd.Trim(), Environment.NewLine, "", Environment.NewLine + Environment.NewLine)); - - return ret; - } - - if (!_binaryConnection.Client.Connected) - { - Debug.WriteLine("@MpdBinarySendCommand: " + "NOT IsMpdCommandConnected"); - - //ret.IsSuccess = false; - //ret.ErrorMessage = "NOT IsMpdCommandConnected"; - - //DebugCommandOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "MpdSendCommand", "!CommandConnection.Client.Connected", cmd.Trim(), Environment.NewLine, "", Environment.NewLine + Environment.NewLine)); - - return ret; - } - - // WriteAsync - try - { - //IsBusy?.Invoke(this, true); - - if (cmd.Trim().StartsWith("idle")) - { - //DebugCommandOutput?.Invoke(this, ">>>>" + cmd.Trim() + "\n" + "\n"); - - await _binaryWriter.WriteAsync(cmd.Trim() + "\n"); - - if (!isAutoIdling) - { - ret.IsSuccess = true; - - //IsBusy?.Invoke(this, false); - return ret; - } - } - else - { - string cmdDummy = cmd; - if (cmd.StartsWith("password ")) - cmdDummy = "password ****"; - - cmdDummy = cmdDummy.Trim().Replace("\n", "\n" + ">>>>"); - - //if (isAutoIdling) - //DebugCommandOutput?.Invoke(this, ">>>>" + "noidle\n>>>>" + cmdDummy.Trim() + "\n>>>>idle player" + "\n" + "\n"); - //else - //DebugCommandOutput?.Invoke(this, ">>>>" + cmdDummy.Trim() + "\n" + "\n"); - - if (isAutoIdling) - await _binaryWriter.WriteAsync("noidle\n" + cmd.Trim() + "\n" + "idle player\n"); - else - await _binaryWriter.WriteAsync(cmd.Trim() + "\n"); - } - } - catch (System.IO.IOException e) - { - // IOException : Unable to write data to the transport connection: 確立された接続がホスト コンピューターのソウトウェアによって中止されました。 - - ret.IsSuccess = false; - ret.ErrorMessage = e.Message; - /* - // Could be application shutdopwn. - if ((ConnectionState == ConnectionStatus.Disconnecting) || (ConnectionState == ConnectionStatus.DisconnectedByUser)) - { - - } - else - { - DebugCommandOutput?.Invoke(this, string.Format("################ Error@{0}, Reason:{1}, Data:{2}, {3} Exception: {4} {5}", "WriteAsync@MpdSendCommand", "IOException", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - // タイムアウトしていたらここで「も」エラーになる模様。 - - IsMpdCommandConnected = false; - - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting... " + Environment.NewLine + Environment.NewLine)); - - try - { - _commandConnection.Client.Shutdown(SocketShutdown.Both); - _commandConnection.Close(); - } - catch { } - - ConnectionResult newCon = await MpdCommandConnect(_host, _port); - - if (newCon.IsSuccess) - { - CommandResult d = await MpdCommandSendPassword(_password); - - if (d.IsSuccess) - { - d = await MpdSendCommand("idle player"); - - if (d.IsSuccess) - { - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting Success. @IOExceptionOfWriteAsync" + Environment.NewLine + Environment.NewLine)); - - ret = await MpdSendCommand(cmd, isAutoIdling); - } - } - } - else - { - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting Failed. " + Environment.NewLine + Environment.NewLine)); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "The connection (command) has been terminated (IOException): " + e.Message); - } - } - */ - - //IsBusy?.Invoke(this, false); - return ret; - } - catch (Exception e) - { - Debug.WriteLine("Exception@MpdBinarySendCommand: " + cmd.Trim() + " WriteAsync " + e.Message); - - ret.IsSuccess = false; - ret.ErrorMessage = e.Message; - /* - if ((ConnectionState == ConnectionStatus.Disconnecting) || (ConnectionState == ConnectionStatus.DisconnectedByUser)) - { - - } - else - { - DebugCommandOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "WriteAsync@MpdSendCommand", "Exception", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - //ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - //ConnectionError?.Invoke(this, "The connection (command) has been terminated (Exception): " + e.Message); - } - */ - //IsBusy?.Invoke(this, false); - return ret; - } - - // ReadLineAsync - try - { - //IsBusy?.Invoke(this, true); - - StringBuilder stringBuilder = new(); - - bool isDoubleOk = false; - //bool isAck = false; - string ackText = ""; - bool isNullReturn = false; - - while (true) - { - string line = await _binaryReader.ReadLineAsync(); - - if (line != null) - { - if (line.StartsWith("ACK")) - { - Debug.WriteLine("ACK line @MpdBinarySendCommand: " + cmd.Trim() + " and " + line); - - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - - ret.ErrorMessage = line; - ackText = line; - //isAck = true; - - break; - } - else if (line.StartsWith("OK")) - { - if (isAutoIdling) - { - if (isDoubleOk) - { - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - - ret.IsSuccess = true; - - break; - } - - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - - isDoubleOk = true; - - } - else - { - ret.IsSuccess = true; - - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - - break; - } - } - else if (line.StartsWith("changed: ")) - { - // noidleでついてくるかもしれないchanged. idleConnectionで見ているからここでは無視したいが・・・。 - - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - } - else - { - if (!string.IsNullOrEmpty(line)) - { - stringBuilder.Append(line + "\n"); // << has to be \n - } - } - } - else - { - isNullReturn = true; - - break; - } - } - - if (isNullReturn) - { - Debug.WriteLine("@MpdBinarySendCommand ReadLineAsync isNullReturn"); - /* - DebugCommandOutput?.Invoke(this, string.Format("################ Error @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "ReadLineAsync@MpdSendCommand", "ReadLineAsync received null data", cmd.Trim(), Environment.NewLine, "", Environment.NewLine + Environment.NewLine)); - - ret.ResultText = stringBuilder.ToString(); - ret.ErrorMessage = "ReadLineAsync@MpdSendCommand received null data"; - - // タイムアウトしていたらここで「も」エラーになる模様。 - - IsMpdCommandConnected = false; - - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting... " + Environment.NewLine + Environment.NewLine)); - - try - { - _commandConnection.Client.Shutdown(SocketShutdown.Both); - _commandConnection.Close(); - } - catch { } - - ConnectionResult newCon = await MpdCommandConnect(_host, _port); - - if (newCon.IsSuccess) - { - CommandResult d = await MpdCommandSendPassword(_password); - - if (d.IsSuccess) - { - d = await MpdSendCommand("idle player"); - - if (d.IsSuccess) - { - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting Success. @isNullReturn" + Environment.NewLine + Environment.NewLine)); - - ret = await MpdSendCommand(cmd, isAutoIdling); - } - } - } - else - { - // - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting Failed. " + Environment.NewLine + Environment.NewLine)); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "The connection (command) has been terminated (null return)."); - } - */ - //IsBusy?.Invoke(this, false); - return ret; - } - else - { - //DebugCommandOutput?.Invoke(this, "<<<<" + stringBuilder.ToString().Trim().Replace("\n", "\n" + "<<<<") + "\n" + "\n"); - - //if (isAck) - // MpdAckError?.Invoke(this, ackText + " (@MSC)"); - - ret.ResultText = stringBuilder.ToString(); - - //IsBusy?.Invoke(this, false); - return ret; - } - - } - catch (System.InvalidOperationException e) - { - // The stream is currently in use by a previous operation on the stream. - - Debug.WriteLine("InvalidOperationException@MpdBinarySendCommand: " + cmd.Trim() + " ReadLineAsync ---- " + e.Message); - - //DebugCommandOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "ReadLineAsync@MpdSendCommand", "InvalidOperationException (Most likely the connection is overloaded)", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - ret.IsSuccess = false; - ret.ErrorMessage = e.Message; - - //IsBusy?.Invoke(this, false); - return ret; - } - catch (System.IO.IOException e) - { - // IOException : Unable to write data to the transport connection: 確立された接続がホスト コンピューターのソウトウェアによって中止されました。 - - Debug.WriteLine("IOException@MpdBinarySendCommand: " + cmd.Trim() + " ReadLineAsync ---- " + e.Message); - - ret.IsSuccess = false; - ret.ErrorMessage = e.Message; - /* - // Could be application shutdopwn. - if ((ConnectionState == ConnectionStatus.Disconnecting) || (ConnectionState == ConnectionStatus.DisconnectedByUser)) - { - - } - else - { - DebugCommandOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "ReadLineAsync@MpdSendCommand", "IOException", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - // タイムアウトしていたらここで「も」エラーになる模様。 - - IsMpdCommandConnected = false; - - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting... " + Environment.NewLine + Environment.NewLine)); - - try - { - _commandConnection.Client.Shutdown(SocketShutdown.Both); - _commandConnection.Close(); - } - catch { } - - ConnectionResult newCon = await MpdCommandConnect(_host, _port); - - if (newCon.IsSuccess) - { - CommandResult d = await MpdCommandSendPassword(_password); - - if (d.IsSuccess) - { - d = await MpdSendCommand("idle player"); - - if (d.IsSuccess) - { - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting Success. @IOExceptionOfReadLineAsync" + Environment.NewLine + Environment.NewLine)); - - ret = await MpdSendCommand(cmd, isAutoIdling); - } - } - } - else - { - // Unable to read data from the transport connection: 既に接続済みのソケットに対して接続を要求しました。. - - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting Failed. " + Environment.NewLine + Environment.NewLine)); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "The connection (command) has been terminated (IOException): " + e.Message); - } - } - */ - //IsBusy?.Invoke(this, false); - return ret; - } - catch (Exception e) - { - Debug.WriteLine("Exception@MpdBinarySendCommand: " + cmd.Trim() + " ReadLineAsync ---- " + e.Message); - - ret.IsSuccess = false; - ret.ErrorMessage = e.Message; - - //DebugCommandOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "ReadLineAsync@MpdSendCommand", "Exception", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - //IsBusy?.Invoke(this, false); - return ret; - } - - } - - private async Task MpdBinarySendBinaryCommand(string cmd, bool isAutoIdling = false) - { - CommandBinaryResult ret = new(); - - if (_binaryConnection.Client == null) - { - Debug.WriteLine("@MpdBinarySendBinaryCommand: " + "TcpClient.Client == null"); - - ret.IsSuccess = false; - ret.ErrorMessage = "TcpClient.Client == null"; - - //DebugCommandOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "MpdCommandGetBinary", "TcpClient.Client == null", cmd.Trim(), Environment.NewLine, "", Environment.NewLine + Environment.NewLine)); - - return ret; - } - - if ((_binaryWriter == null) || (_binaryReader == null)) - { - Debug.WriteLine("@MpdBinarySendBinaryCommand: " + "_binaryWriter or _binaryReader is null"); - - ret.IsSuccess = false; - ret.ErrorMessage = "_binaryWriter or _binaryReader is null"; - - //DebugCommandOutput?.Invoke(this, string.Format("################ Error :@{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "MpdCommandGetBinary", "_commandWriter or _commandReader is null", cmd.Trim(), Environment.NewLine, "", Environment.NewLine + Environment.NewLine)); - - return ret; - } - - if (!_binaryConnection.Client.Connected) - { - Debug.WriteLine("@MpdBinarySendBinaryCommand: " + "NOT IsMpdCommandConnected"); - - ret.IsSuccess = false; - ret.ErrorMessage = "NOT IsMpdCommandConnected"; - - //DebugCommandOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "MpdCommandGetBinary", "!CommandConnection.Client.Connected", cmd.Trim(), Environment.NewLine, "", Environment.NewLine + Environment.NewLine)); - - return ret; - } - - // WriteAsync - try - { - if (cmd.Trim().StartsWith("idle")) - { - //DebugCommandOutput?.Invoke(this, ">>>>" + cmd.Trim() + "\n" + "\n"); - - await _binaryWriter.WriteAsync(cmd.Trim() + "\n"); - - if (!isAutoIdling) - return ret; - } - else - { - string cmdDummy = cmd; - if (cmd.StartsWith("password ")) - cmdDummy = "password ****"; - - cmdDummy = cmdDummy.Trim().Replace("\n", "\n" + ">>>>"); - - //if (isAutoIdling) - // DebugCommandOutput?.Invoke(this, ">>>>" + "noidle\n>>>>" + cmdDummy.Trim() + "\n>>>>idle player" + "\n" + "\n"); - //else - // DebugCommandOutput?.Invoke(this, ">>>>" + cmdDummy.Trim() + "\n" + "\n"); - - if (isAutoIdling) - await _binaryWriter.WriteAsync("noidle\n" + cmd.Trim() + "\n" + "idle player\n"); - else - await _binaryWriter.WriteAsync(cmd.Trim() + "\n"); - } - } - catch (System.IO.IOException e) - { - // IOException : Unable to write data to the transport connection: 確立された接続がホスト コンピューターのソウトウェアによって中止されました。 - - Debug.WriteLine("Exception@MpdBinarySendBinaryCommand: " + cmd.Trim() + " WriteAsync " + e.Message); - - ret.IsSuccess = false; - ret.ErrorMessage = e.Message; - /* - // Could be application shutdopwn. - if ((ConnectionState == ConnectionStatus.Disconnecting) || (ConnectionState == ConnectionStatus.DisconnectedByUser)) - { - - } - else - { - DebugCommandOutput?.Invoke(this, string.Format("################ Error@{0}, Reason:{1}, Data:{2}, {3} Exception: {4} {5}", "WriteAsync@MpdCommandGetBinary", "IOException", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - // タイムアウトしていたらここで「も」エラーになる模様。 - - IsMpdCommandConnected = false; - - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting... " + Environment.NewLine + Environment.NewLine)); - - _commandConnection.Close(); - - ConnectionResult newCon = await MpdCommandConnect(_host, _port); - - if (newCon.IsSuccess) - { - CommandResult d = await MpdCommandSendPassword(_password); - - if (d.IsSuccess) - { - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting Success. " + Environment.NewLine + Environment.NewLine)); - - ret = await MpdCommandGetBinary(cmd, isAutoIdling); - } - } - else - { - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting Failed. " + Environment.NewLine + Environment.NewLine)); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "The connection (command) has been terminated (IOException): " + e.Message); - } - } - */ - return ret; - } - catch (Exception e) - { - Debug.WriteLine("Exception@MpdBinarySendBinaryCommand: " + cmd.Trim() + " WriteAsync " + e.Message); - - ret.IsSuccess = false; - ret.ErrorMessage = e.Message; - /* - if ((ConnectionState == ConnectionStatus.Disconnecting) || (ConnectionState == ConnectionStatus.DisconnectedByUser)) - { - - } - else - { - DebugCommandOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "WriteAsync@MpdCommandGetBinary", "Exception", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - //ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - //ConnectionError?.Invoke(this, "The connection (command) has been terminated (Exception): " + e.Message); - } - */ - return ret; - } - - // ReadAsync - try - { - StringBuilder stringBuilder = new(); - - byte[] bin = Array.Empty(); - - bool isDoubleOk = false; - bool isAck = false; - string ackText = ""; - bool isNullReturn = false; - bool isBinaryFound = false; - - bool isWaitForOK = true; - - while (isWaitForOK) - { - - int readSize = 0; - int bufferSize = 5000; - byte[] buffer = new byte[bufferSize]; - byte[] bindata = Array.Empty(); - - using (MemoryStream ms = new()) - { - while ((readSize = await _binaryReader.BaseStream.ReadAsync(buffer, 0, buffer.Length)) > 0) - { - ms.Write(buffer, 0, readSize); - - if (readSize < bufferSize) - { - break; - } - } - - bindata = ms.ToArray(); - } - - if (bindata.Length == 0) - { - isNullReturn = true; - - isWaitForOK = false; - } - else - { - - bin = CombineByteArray(bin, bindata); - - //Debug.WriteLine("Done .Write:" + bin.Length.ToString()); - - string res = Encoding.Default.GetString(bindata, 0, bindata.Length); - - List values = res.Split("\n").ToList(); - - foreach (var line in values) - { - if (line != null) - { - if (line.StartsWith("ACK")) - { - //Debug.WriteLine("ACK line @MpdCommandGetBinary: " + cmd.Trim() + " and " + line); - - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - - ret.ErrorMessage = line; - ackText = line; - isAck = true; - - isWaitForOK = false; - - break; - } - else if (line.StartsWith("changed: ")) - { - // noidleでついてくるかもしれないchanged. idleConnectionで見ているからここでは無視したいが・・・。 - - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - } - else if (line.StartsWith("size: ")) - { - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - - List s = line.Split(':').ToList(); - if (s.Count > 1) - { - if (Int32.TryParse(s[1], out int i)) - { - ret.WholeSize = i; - } - } - } - else if (line.StartsWith("type: ")) - { - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - - List s = line.Split(':').ToList(); - if (s.Count > 1) - { - ret.Type = s[1].Trim(); - } - } - else if (line.StartsWith("binary: ")) - { - isBinaryFound = true; - - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - - stringBuilder.Append("{binary data}" + "\n"); - - List s = line.Split(':').ToList(); - if (s.Count > 1) - { - if (Int32.TryParse(s[1], out int i)) - { - ret.ChunkSize = i; - } - } - - } - else if (line == "OK") // == - { - if (isAutoIdling) - { - if (isDoubleOk) - { - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - - ret.IsSuccess = true; - - isWaitForOK = false; - break; - } - - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - - isDoubleOk = true; - - } - else - { - ret.IsSuccess = true; - - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - - isWaitForOK = false; - break; - } - } - else - { - // This should be binary data if not reading correctly above. - - //Debug.WriteLine(line); - } - } - else - { - isNullReturn = true; - - isWaitForOK = false; - break; - } - } - - } - } - - if (isNullReturn) - { - Debug.WriteLine("@MpdBinarySendBinaryCommand ReadAsync isNullReturn"); - - //DebugCommandOutput?.Invoke(this, string.Format("################ Error @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "ReadAsync@MpdCommandGetBinary", "ReadLineAsync received null data", cmd.Trim(), Environment.NewLine, "", Environment.NewLine + Environment.NewLine)); - - ret.ErrorMessage = "ReadAsync@MpdBinarySendBinaryCommand received null data"; - - return ret; - } - else - { - //DebugCommandOutput?.Invoke(this, "<<<<" + stringBuilder.ToString().Trim().Replace("\n", "\n" + "<<<<") + "\n" + "\n"); - - if (isAck) - { - // とりあえず今の所、アルバムカバーのfile not existsは無視するようにしている。 - //MpdAckError?.Invoke(this, ackText + " (@MCGB)"); - - ret.ErrorMessage = ackText + " (@MpdBinarySendBinaryCommand)"; - - return ret; - } - else if (isBinaryFound) - { - //return ret; - //return ParseAlbumArtResponse(bin); - return ParseAlbumImageData(bin); - } - else - { - //Debug.WriteLine("No binary data(size) found. Could be a readpicture command?"); - - // TODO: - - //DebugCommandOutput?.Invoke(this, "No binary data(size) found. Could be a readpicture command?" + "\n" + "\n"); - - ret.ErrorMessage = "No binary data(size) found. Could be a readpicture command? (@MpdBinarySendBinaryCommand)"; - - return ret; - } - } - } - catch (System.InvalidOperationException e) - { - // The stream is currently in use by a previous operation on the stream. - - Debug.WriteLine("InvalidOperationException@MpdBinarySendBinaryCommand: " + cmd.Trim() + " ReadAsync ---- " + e.Message); - - //DebugCommandOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "ReadAsync@MpdCommandGetBinary", "InvalidOperationException (Most likely the connection is overloaded)", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - ret.IsSuccess = false; - ret.ErrorMessage = e.Message; - - return ret; - } - catch (System.IO.IOException e) - { - // IOException : Unable to write data to the transport connection: 確立された接続がホスト コンピューターのソウトウェアによって中止されました。 - - Debug.WriteLine("IOException@MpdBinarySendBinaryCommand: " + cmd.Trim() + " ReadAsync ---- " + e.Message); - - ret.IsSuccess = false; - ret.ErrorMessage = e.Message; - /* - // Could be application shutdopwn. - if ((ConnectionState == ConnectionStatus.Disconnecting) || (ConnectionState == ConnectionStatus.DisconnectedByUser)) - { - - } - else - { - DebugCommandOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "ReadAsync@MpdCommandGetBinary", "IOException", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - // タイムアウトしていたらここで「も」エラーになる模様。 - - IsMpdCommandConnected = false; - - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting... " + Environment.NewLine + Environment.NewLine)); - - _commandConnection.Close(); - - ConnectionResult newCon = await MpdCommandConnect(_host, _port); - - if (newCon.IsSuccess) - { - CommandResult d = await MpdCommandSendPassword(_password); - - if (d.IsSuccess) - { - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting Success. " + Environment.NewLine + Environment.NewLine)); - - ret = await MpdCommandGetBinary(cmd, isAutoIdling); - } - } - else - { - // Unable to read data from the transport connection: 既に接続済みのソケットに対して接続を要求しました。. - - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting Failed. " + Environment.NewLine + Environment.NewLine)); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "The connection (command) has been terminated (IOException): " + e.Message); - } - } - */ - return ret; - } - catch (Exception e) - { - Debug.WriteLine("Exception@MpdBinarySendBinaryCommand: " + cmd.Trim() + " ReadAsync ---- " + e.Message); - - ret.IsSuccess = false; - ret.ErrorMessage = e.Message; - - //DebugCommandOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "ReadAsync@MpdCommandGetBinary", "Exception", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - return ret; - } - - } - - private CommandBinaryResult ParseAlbumImageData(byte[] data) - { - CommandBinaryResult r = new(); - - //if (MpdStop) return r; - - if (data.Length > 1000000) //2000000000 - { - Debug.WriteLine("**ParseAlbumImageData: binary file size too big: " + data.Length.ToString()); - - _albumCover.IsDownloading = false; - return r; - } - - if (string.IsNullOrEmpty(_albumCover.SongFilePath)) - { - Debug.WriteLine("**ParseAlbumImageData: File path is not set."); - - _albumCover.IsDownloading = false; - return r; - } - - if (!_albumCover.IsDownloading) - { - Debug.WriteLine("**ParseAlbumImageData: IsDownloading = false. Downloading canceld? ."); - - _albumCover.IsDownloading = false; - return r; - } - - try - { - //int gabStart = gabPre; - //int gabEnd = gabAfter; - int gabStart = 0; - int gabEnd = 0; - - string res = Encoding.Default.GetString(data, 0, data.Length); - - int binSize = 0; - int binResSize = 0; - - List values = res.Split('\n').ToList(); - - bool found = false; - foreach (var val in values) - { - if (val.StartsWith("size: ")) - { - found = true; - - gabStart = gabStart + val.Length + 1; - - List s = val.Split(':').ToList(); - if (s.Count > 1) - { - if (Int32.TryParse(s[1], out int i)) - { - binSize = i; - - r.WholeSize = i; - } - } - - } - else if (val.StartsWith("type: ")) - { - gabStart = gabStart + val.Length + 1; - - List s = val.Split(':').ToList(); - if (s.Count > 1) - { - r.Type = s[1]; - } - - } - else if (val.StartsWith("binary: ")) - { - gabStart = gabStart + val.Length + 1; - - List s = val.Split(':').ToList(); - if (s.Count > 1) - { - if (Int32.TryParse(s[1], out int i)) - { - binResSize = i; - - r.ChunkSize = binResSize; - } - } - - } - else if (val == "OK") // == - { - //gabEnd = gabEnd + val.Length + 1; - if (found) - { - gabEnd = gabEnd + val.Length + 1; - //System.Diagnostics.Debug.WriteLine("OK:after " + val); - } - else - { - gabStart = gabStart + val.Length + 1; - //System.Diagnostics.Debug.WriteLine("OK:before " + val); - } - - } - else if (val.StartsWith("ACK")) - { - // ACK 応答はここまで到達しないはず。 - - if (found) - { - gabEnd = gabEnd + val.Length + 1; - //System.Diagnostics.Debug.WriteLine("changed:after " + val); - } - else - { - gabStart = gabStart + val.Length + 1; - //System.Diagnostics.Debug.WriteLine("changed:before " + val); - } - } - else if (val.StartsWith("changed:")) - { - // Song is changed...so should skip ?? - //DataReceived_ParseData(val); - - if (found) - { - gabEnd = gabEnd + val.Length + 1; - //System.Diagnostics.Debug.WriteLine("changed:after " + val); - } - else - { - gabStart = gabStart + val.Length + 1; - //System.Diagnostics.Debug.WriteLine("changed:before " + val); - } - - } - else - { - // should be binary... - } - } - - gabEnd++; // - - // test - //gabEnd = 4; // \n O K \n - - - if (binSize > 1000000) - { - Debug.WriteLine("binary file too big: " + binSize.ToString()); - - //DebugCommandOutput?.Invoke(this, "binary file too big: " + binSize.ToString() + "\n" + "\n"); - - _albumCover.IsDownloading = false; - - return r; - } - - if ((binSize == 0)) - { - Debug.WriteLine("binary file size is Zero: " + binSize.ToString() + ", " + binResSize.ToString() + ", " + data.Length.ToString()); - - _albumCover.IsDownloading = false; - - return r; - } - - if (binResSize != ((data.Length - gabStart) - gabEnd)) - { - Debug.WriteLine("binary file size mismatch: " + binSize.ToString() + ", [" + binResSize.ToString() + ", " + (data.Length - gabStart - gabEnd) + "], " + data.Length.ToString()); - - //DebugCommandOutput?.Invoke(this, "binary file size mismatch." + "\n" + "\n"); - - //DebugCommandOutput?.Invoke(this, "raw text data:\n" + res + "\n" + "\n"); - - _albumCover.IsDownloading = false; - - return r; - } - - r.WholeSize = binSize; - r.ChunkSize = binResSize; - - // 今回受け取ったバイナリ用にバイトアレイをイニシャライズ - byte[] resBinary = new byte[data.Length - gabStart - gabEnd]; - // 今回受け取ったバイナリをresBinaryへコピー - Array.Copy(data, gabStart, resBinary, 0, resBinary.Length); - - r.BinaryData = resBinary; - - r.IsSuccess = true; - - return r; - - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine("Error@ParseAlbumImageData (l): " + ex.Message); - - _albumCover.IsDownloading = false; - return r; - } - } - - private static byte[] CombineByteArray(byte[] first, byte[] second) - { - byte[] ret = new byte[first.Length + second.Length]; - Buffer.BlockCopy(first, 0, ret, 0, first.Length); - Buffer.BlockCopy(second, 0, ret, first.Length, second.Length); - return ret; - } - - public async Task MpdQueryAlbumArt(string uri, bool isUsingReadpicture) - { - if (string.IsNullOrEmpty(uri)) - { - CommandResult f = new(); - f.ErrorMessage = "IsNullOrEmpty(uri)"; - f.IsSuccess = false; - return f; - } - - - /* - if (songId != MpdStatus.MpdSongID) - { - // probably you clicked on "Next" too farst or double clicked. - Debug.WriteLine("Error@MpdQueryAlbumArt: songId != MpdStatus.MpdSongID. Ignoring."); - - CommandResult f = new CommandResult(); - f.IsSuccess = false; - return f; - } - */ - - //Debug.WriteLine("Downloading..." + uri); - - _albumCover.SongFilePath = uri; - _albumCover.IsDownloading = true; - /* - Application.Current.Dispatcher.Invoke(() => - { - _albumCover = new AlbumImage(); - _albumCover.IsDownloading = true; - _albumCover.SongFilePath = uri; - _albumCover.AlbumImageSource = null; - _albumCover.BinaryData = new byte[0]; - _albumCover.BinarySize = 0; - }); - */ - - uri = Regex.Escape(uri); - - string cmd = "albumart \"" + uri + "\" 0" + "\n"; - if (isUsingReadpicture) - if (CompareVersionString(MpdVersion,"0.22.0") >= 0) - cmd = "readpicture \"" + uri + "\" 0" + "\n"; - - CommandBinaryResult result = await MpdBinarySendBinaryCommand(cmd, false); - - bool r = false; - - if (result.IsSuccess) - { - _albumCover.BinaryData = result.BinaryData; - - if ((result.WholeSize != 0) && (result.WholeSize == result.ChunkSize)) - { - Application.Current.Dispatcher.Invoke(() => - { - _albumCover.AlbumImageSource = BitmaSourceFromByteArray(_albumCover.BinaryData); - _albumCover.IsSuccess = true; - _albumCover.IsDownloading = false; - - //MpdAlbumArtChanged?.Invoke(this); - }); - - r = true; - //Debug.WriteLine("一発できた"); - } - else - { - //Debug.WriteLine("何回かみにいくみたい"); - - if ((result.WholeSize != 0) && (result.WholeSize > result.ChunkSize)) - { - while ((result.WholeSize > _albumCover.BinaryData.Length) && result.IsSuccess) - { - result = await MpdReQueryAlbumArt(_albumCover.SongFilePath, _albumCover.BinaryData.Length, isUsingReadpicture); - - if (result.IsSuccess && (result.BinaryData != null)) - _albumCover.BinaryData = CombineByteArray(_albumCover.BinaryData, result.BinaryData); - } - - //Debug.WriteLine("何回かできた"); - - if (result.IsSuccess && (_albumCover.BinaryData != null)) - { - Application.Current.Dispatcher.Invoke(() => - { - _albumCover.AlbumImageSource = BitmaSourceFromByteArray(_albumCover.BinaryData); - _albumCover.IsSuccess = true; - _albumCover.IsDownloading = false; - - //MpdAlbumArtChanged?.Invoke(this); - }); - - r = true; - } - } - } - } - - CommandResult b = new(); - b.IsSuccess = r; - if (!r) - { - b.ErrorMessage = result.ErrorMessage; - } - - return b; - } - - private async Task MpdReQueryAlbumArt(string uri, int offset, bool isUsingReadpicture) - { - if (string.IsNullOrEmpty(uri)) - { - CommandBinaryResult f = new(); - f.ErrorMessage = "IsNullOrEmpty(uri)"; - f.IsSuccess = false; - return f; - } - - if (!_albumCover.IsDownloading) - { - Debug.WriteLine("Error@MpdQueryAlbumArt: _albumCover.IsDownloading == false. Ignoring."); - - CommandBinaryResult f = new(); - f.IsSuccess = false; - return f; - } - - if (_albumCover.SongFilePath != uri) - { - Debug.WriteLine("Error@MpdQueryAlbumArt: _albumCover.SongFilePath != uri. Ignoring."); - - _albumCover.IsDownloading = false; - - CommandBinaryResult f = new(); - f.IsSuccess = false; - return f; - } - - uri = Regex.Escape(uri); - - string cmd = "albumart \"" + uri + "\" " + offset.ToString() + "\n"; - if (isUsingReadpicture) - if (CompareVersionString(MpdVersion, "0.22.0") >= 0) - cmd = "readpicture \"" + uri + "\" " + offset.ToString() + "\n"; - - return await MpdBinarySendBinaryCommand(cmd, false); - } - - private static int CompareVersionString(string a, string b) - { - return (new System.Version(a)).CompareTo(new System.Version(b)); - } - - private static BitmapSource BitmaSourceFromByteArray(byte[] buffer) - { - using var stream = new MemoryStream(buffer); - return BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); - } - - public void MpdBinaryConnectionDisconnect() - { - //Debug.WriteLine("TCP Binary Connection: Disconnecting."); - - try - { - if (_binaryConnection.Client != null) - _binaryConnection.Client.Shutdown(SocketShutdown.Both); - _binaryConnection.Close(); - } - catch { } - - } - - } -} diff --git a/MPDCtrl/Classes/AlbumImage.cs b/MPDCtrl/Classes/AlbumImage.cs deleted file mode 100644 index 53dfb6a..0000000 --- a/MPDCtrl/Classes/AlbumImage.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Media; -using System.Windows.Media.Imaging; - -namespace MPDCtrl.Models -{ - /// - /// AlbumCover class. - /// - public class AlbumImage - { - public bool IsDownloading { get; set; } - - public bool IsSuccess { get; set; } - - public string SongFilePath { get; set; } - - public byte[] BinaryData { get; set; } = Array.Empty(); - - public int BinarySize { get; set; } - - public ImageSource AlbumImageSource { get; set; } - - } -} diff --git a/MPDCtrl/Classes/Playlist.cs b/MPDCtrl/Classes/Playlist.cs deleted file mode 100644 index d9f0921..0000000 --- a/MPDCtrl/Classes/Playlist.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MPDCtrl.Models -{ - public class Playlist - { - public string Name { get; set; } = ""; - - private string _lastModified; - public string LastModified - { - get - { - return _lastModified; - } - set - { - if (_lastModified == value) - return; - - _lastModified = value; - } - } - - public string LastModifiedFormated - { - get - { - DateTime _lastModifiedDateTime = default; //new DateTime(1998,04,30) - - if (!string.IsNullOrEmpty(_lastModified)) - { - try - { - _lastModifiedDateTime = DateTime.Parse(_lastModified, null, System.Globalization.DateTimeStyles.RoundtripKind); - } - catch - { - System.Diagnostics.Debug.WriteLine("Wrong LastModified timestamp format. " + _lastModified); - } - } - - var culture = System.Globalization.CultureInfo.CurrentCulture; - return _lastModifiedDateTime.ToString(culture); - } - } - - } -} diff --git a/MPDCtrl/Classes/Result.cs b/MPDCtrl/Classes/Result.cs deleted file mode 100644 index d235d0d..0000000 --- a/MPDCtrl/Classes/Result.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MPDCtrl.Models -{ - public class Result - { - public bool IsSuccess; - public string ErrorMessage; - } - - public class ConnectionResult: Result - { - - } - - // generic - public class CommandResult : Result - { - public string ResultText; - } - - public class CommandBinaryResult : Result - { - public int WholeSize; - public int ChunkSize; - public string Type; - public byte[] BinaryData; - } - - // for commands that return playlist songs. - public class CommandPlaylistResult : CommandResult - { - public ObservableCollection PlaylistSongs; - } - - // for commands that return search result. - public class CommandSearchResult : CommandResult - { - public ObservableCollection SearchResult; - } - - // TODO: Not used? - public class IdleResult : CommandResult - { - - } -} diff --git a/MPDCtrl/Classes/Song.cs b/MPDCtrl/Classes/Song.cs deleted file mode 100644 index d85d65e..0000000 --- a/MPDCtrl/Classes/Song.cs +++ /dev/null @@ -1,232 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -//using MPDCtrl.Common; - -namespace MPDCtrl.Models -{ - // SongFile > SongInfo > SongInfoEx - - /// - /// Generic song file class. (for listall) - /// - public class SongFile// : ViewModelBase - { - public string File { get; set; } = ""; - - } - - /// - /// SongInfo class. (for playlist or search result) - /// - public class SongInfo : SongFile - { - public string Title { get; set; } = ""; - public string Track { get; set; } = ""; - public string Disc { get; set; } = ""; - public string Time { get; set; } = ""; - public string TimeFormated - { - get - { - string _timeFormatted = ""; - try - { - if (!string.IsNullOrEmpty(Time)) - { - int sec, min, hour, s; - - double dtime = double.Parse(Time); - sec = Convert.ToInt32(dtime); - - //sec = Int32.Parse(_time); - min = sec / 60; - s = sec % 60; - hour = min / 60; - min %= 60; - - if ((hour == 0) && min == 0) - { - _timeFormatted = String.Format("{0}", s); - } - else if ((hour == 0) && (min != 0)) - { - _timeFormatted = String.Format("{0}:{1:00}", min, s); - } - else if ((hour != 0) && (min != 0)) - { - _timeFormatted = String.Format("{0}:{1:00}:{2:00}", hour, min, s); - } - else if (hour != 0) - { - _timeFormatted = String.Format("{0}:{1:00}:{2:00}", hour, min, s); - } - else - { - System.Diagnostics.Debug.WriteLine("Oops@TimeFormated: " + Time + " : " + hour.ToString() + " " + min.ToString() + " " + s.ToString()); - } - } - } - catch (FormatException e) - { - // Ignore. - // System.Diagnostics.Debug.WriteLine(e.Message); - System.Diagnostics.Debug.WriteLine("Wrong Time format. " + Time + " " + e.Message); - } - - return _timeFormatted; - } - - } - public double TimeSort - { - get - { - double dtime = double.NaN; - try - { - if (Time != "") - dtime = double.Parse(Time); - } - catch { } - return dtime; - } - } - public string Duration { get; set; } = ""; - public string Artist { get; set; } = ""; - public string Album { get; set; } = ""; - public string AlbumArtist { get; set; } = ""; - public string Composer { get; set; } = ""; - public string Date { get; set; } = ""; - public string Genre { get; set; } = ""; - - private string _lastModified; - public string LastModified - { - get - { - return _lastModified; - } - set - { - if (_lastModified == value) - return; - - _lastModified = value; - } - } - - public string LastModifiedFormated - { - get - { - DateTime _lastModifiedDateTime = default; //new DateTime(1998,04,30) - - if (!string.IsNullOrEmpty(_lastModified)) - { - try - { - _lastModifiedDateTime = DateTime.Parse(_lastModified, null, System.Globalization.DateTimeStyles.RoundtripKind); - } - catch - { - System.Diagnostics.Debug.WriteLine("Wrong LastModified timestamp format. " + _lastModified); - } - } - - var culture = System.Globalization.CultureInfo.CurrentCulture; - return _lastModifiedDateTime.ToString(culture); - } - } - - // for sorting and (playlist pos) - private int _index; - public int Index - { - get - { - return _index; - } - set - { - if (_index == value) - return; - - _index = value; - //this.NotifyPropertyChanged(nameof(Index)); - } - } - - private bool _isSelected; - public bool IsSelected - { - get - { - return _isSelected; - } - set - { - if (_isSelected == value) - return; - - _isSelected = value; - - //NotifyPropertyChanged("IsSelected"); - } - } - - public int IndexPlusOne - { - get - { - return _index+1; - } - } - } - - /// - /// song class with some extra info. (for queue) - /// - public class SongInfoEx : SongInfo - { - // Queue specific - - public string Id { get; set; } - - private string _pos; - public string Pos - { - get - { - return _pos; - } - set - { - if (_pos == value) - return; - - _pos = value; - //this.NotifyPropertyChanged(nameof(Pos)); - } - } - - private bool _isPlaying; - public bool IsPlaying - { - get - { - return _isPlaying; - } - set - { - if (_isPlaying == value) - return; - - _isPlaying = value; - //this.NotifyPropertyChanged(nameof(IsPlaying)); - } - } - } -} diff --git a/MPDCtrl/Classes/Status.cs b/MPDCtrl/Classes/Status.cs deleted file mode 100644 index e14099e..0000000 --- a/MPDCtrl/Classes/Status.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MPDCtrl.Models -{ - /// - /// MPD "status" class. (for "status" command result) - /// - public class Status - { - public enum MpdPlayState - { - Play, Pause, Stop - }; - - private MpdPlayState _ps; - private string _bitrate; - private int _volume = 50; - private bool _volumeIsSet; - private bool _repeat; - private bool _random; - private bool _consume; - private bool _single; - private string _songID = ""; - private double _songTime = 0; - private double _songElapsed = 0; - private string _error = ""; - - public MpdPlayState MpdState - { - get { return _ps; } - set { _ps = value; } - } - - public string MpdBitrate - { - get { return _bitrate; } - set - { - _bitrate = value; - } - } - - public int MpdVolume - { - get { return _volume; } - set - { - _volume = value; - } - } - - public bool MpdVolumeIsSet - { - get { return _volumeIsSet; } - set - { - _volumeIsSet = value; - } - } - - public bool MpdRepeat - { - get { return _repeat; } - set - { - _repeat = value; - } - } - - public bool MpdRandom - { - get { return _random; } - set - { - _random = value; - } - } - public bool MpdConsume - { - get { return _consume; } - set - { - _consume = value; - } - } - - public bool MpdSingle - { - get { return _single; } - set - { - _single = value; - } - } - - public string MpdSongID - { - get { return _songID; } - set - { - _songID = value; - } - } - - public double MpdSongTime - { - get { return _songTime; } - set - { - _songTime = value; - } - } - - public double MpdSongElapsed - { - get { return _songElapsed; } - set - { - _songElapsed = value; - } - } - public string MpdError - { - get { return _error; } - set - { - _error = value; - } - } - - public void Reset() - { - _volume = 50; - _volumeIsSet = false; - _repeat = false; - _random = false; - _consume = false; - _songID = ""; - _songTime = 0; - _songElapsed = 0; - _error = ""; - } - - } -} diff --git a/MPDCtrl/MPC.cs b/MPDCtrl/MPC.cs deleted file mode 100644 index 7e95275..0000000 --- a/MPDCtrl/MPC.cs +++ /dev/null @@ -1,3662 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net.Sockets; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Media.Imaging; -using unison; - -namespace MPDCtrl.Models -{ - /// - /// MPD client class. - /// - public class MPC - { - #region == Consts, Properties, etc == - - private string _host; - public string MpdHost - { - get { return _host; } - } - - private int _port; - public int MpdPort - { - get { return _port; } - } - - private string _password; - public string MpdPassword - { - get { return _password; } - } - - private string _mpdVerText; - public string MpdVerText - { - get { return _mpdVerText; } - set - { - if (value == _mpdVerText) - return; - - _mpdVerText = value; - - } - } - - private readonly Status _status = new(); - public Status MpdStatus - { - get { return _status; } - } - - public bool MpdStop { get; set; } - - private SongInfoEx _currentSong; - public SongInfoEx MpdCurrentSong - { - // You need to either get "status" and "queue" before hand, or "currentsong". - get - { - return _currentSong; - } - } - - private ObservableCollection _queue = new(); - public ObservableCollection CurrentQueue - { - get { return _queue; } - } - - private ObservableCollection _playlists = new(); - public ObservableCollection Playlists - { - get { return _playlists; } - } - - private readonly ObservableCollection _localFiles = new(); - public ObservableCollection LocalFiles - { - get { return _localFiles; } - } - - private readonly ObservableCollection _localDirectories = new(); - public ObservableCollection LocalDirectories - { - get { return _localDirectories; } - } - - private readonly ObservableCollection _searchResult = new(); - public ObservableCollection SearchResult - { - get { return _searchResult; } - } - - // TODO: - private AlbumImage _albumCover = new(); - public AlbumImage AlbumCover - { - get { return _albumCover; } - } - - #endregion - - #region == Connections == - - private static TcpClient _commandConnection = new(); - private StreamReader _commandReader; - private StreamWriter _commandWriter; - - private static TcpClient _idleConnection = new(); - private static StreamReader _idleReader; - private static StreamWriter _idleWriter; - - public enum ConnectionStatus - { - NeverConnected, - Connecting, - Connected, - DisconnectedByUser, - DisconnectedByHost, - ConnectFail_Timeout, - ReceiveFail_Timeout, - SendFail_Timeout, - SendFail_NotConnected, - Disconnecting, - Disconnected, - SeeConnectionErrorEvent - } - - private ConnectionStatus _connectionState = ConnectionStatus.NeverConnected; - public ConnectionStatus ConnectionState - { - get - { - return _connectionState; - } - private set - { - if (value == _connectionState) - return; - - _connectionState = value; - - ConnectionStatusChanged?.Invoke(this, _connectionState); - } - } - - // TODO:ちゃんと使っていないので利用するか削除すべきか。 - public bool IsMpdCommandConnected { get; set; } - public bool IsMpdIdleConnected { get; set; } - - #endregion - - #region == Events == - - public delegate void IsBusyEvent(MPC sender, bool on); - public event IsBusyEvent IsBusy; - - public delegate void DebugCommandOutputEvent(MPC sender, string data); - public event DebugCommandOutputEvent DebugCommandOutput; - - public delegate void DebugIdleOutputEvent(MPC sender, string data); - public event DebugIdleOutputEvent DebugIdleOutput; - - public delegate void ConnectionStatusChangedEvent(MPC sender, ConnectionStatus status); - public event ConnectionStatusChangedEvent ConnectionStatusChanged; - - public delegate void ConnectionErrorEvent(MPC sender, string data); - public event ConnectionErrorEvent ConnectionError; - - public delegate void IsMpdIdleConnectedEvent(MPC sender); - public event IsMpdIdleConnectedEvent MpdIdleConnected; - - public delegate void MpdAckErrorEvent(MPC sender, string data); - public event MpdAckErrorEvent MpdAckError; - - public delegate void MpdPlayerStatusChangedEvent(MPC sender); - public event MpdPlayerStatusChangedEvent MpdPlayerStatusChanged; - - public delegate void MpdCurrentQueueChangedEvent(MPC sender); - public event MpdCurrentQueueChangedEvent MpdCurrentQueueChanged; - - //public delegate void MpdPlaylistsChangedEvent(MPC sender); - //public event MpdPlaylistsChangedEvent MpdPlaylistsChanged; - - public delegate void MpdAlbumArtChangedEvent(MPC sender); - public event MpdAlbumArtChangedEvent MpdAlbumArtChanged; - - public delegate void MpcProgressEvent(MPC sender, string msg); - public event MpcProgressEvent MpcProgress; - - #endregion - - public MPC() - { - - } - - #region == Idle Connection == - - public async Task MpdIdleConnectionStart(string host, int port, string password) - { - // TODO: not used for now. - - ConnectionResult r = await MpdIdleConnect(host, port); - - bool ret = false; - - if (r.IsSuccess) - { - CommandResult d = await MpdIdleSendPassword(password); - - if (d.IsSuccess) - { - ret = true; - - } - } - - return ret; - } - - public async Task MpdIdleConnect(string host, int port) - { - ConnectionResult result = new(); - - IsMpdIdleConnected = false; - - _idleConnection = new TcpClient(); - - _host = host; - _port = port; - - DebugIdleOutput?.Invoke(this, "TCP Idle Connection: Connecting." + "\n" + "\n"); - MpcProgress?.Invoke(this, "Connecting..."); - - ConnectionState = ConnectionStatus.Connecting; - - try - { - IsBusy?.Invoke(this, true); - - await _idleConnection.ConnectAsync(_host, _port); - - // TODO: - if (_idleConnection.Client == null) - { - Debug.WriteLine("_idleConnection.Client == null. " + host + " " + port.ToString()); - - result.ErrorMessage = "_idleConnection.Client == null"; - - DebugIdleOutput?.Invoke(this, "TCP Idle Connection: Error while connecting. Fail to connect... "+ "\n" + "\n"); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "TCP connection error..."); - - IsBusy?.Invoke(this, false); - return result; - } - - if (_idleConnection.Client.Connected) - { - DebugIdleOutput?.Invoke(this, "TCP Idle Connection: Connection established." + "\n" + "\n"); - MpcProgress?.Invoke(this, "Connection established...."); - - ConnectionState = ConnectionStatus.Connected; - - var tcpStream = _idleConnection.GetStream(); - tcpStream.ReadTimeout = System.Threading.Timeout.Infinite; - - _idleReader = new StreamReader(tcpStream); - _idleWriter = new StreamWriter(tcpStream) - { - AutoFlush = true - }; - - string response = await _idleReader.ReadLineAsync(); - - if (response.StartsWith("OK MPD ")) - { - MpdVerText = response.Replace("OK MPD ", string.Empty).Trim(); - - DebugIdleOutput?.Invoke(this, "<<<<" + response.Trim() + "\n" + "\n"); - MpcProgress?.Invoke(this, response.Trim()); - - IsMpdIdleConnected = true; - - result.IsSuccess = true; - - MpdIdleConnected?.Invoke(this); - - // Done for now. - } - else - { - DebugIdleOutput?.Invoke(this, "TCP Idle Connection: MPD did not respond with proper respose." + "\n" + "\n"); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "TCP connection error: MPD did not respond with proper respose."); - } - } - else - { - //? - - Debug.WriteLine("**** !client.Client.Connected"); - - DebugIdleOutput?.Invoke(this, "TCP Idle Connection: FAIL to established... Client not connected." + "\n" + "\n"); - - ConnectionState = ConnectionStatus.NeverConnected; - - ConnectionError?.Invoke(this, "TCP Idle Connection: FAIL to established... Client not connected."); - } - } - catch (SocketException e) - { - // TODO: Test. - - //e.SocketErrorCode - - DebugIdleOutput?.Invoke(this, "TCP Idle Connection: Error while connecting. Fail to connect: " + e.Message + "\n" + "\n"); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "TCP connection error: " + e.Message); - } - catch (Exception e) - { - // TODO: Test. - - DebugIdleOutput?.Invoke(this, "TCP Idle Connection: Error while connecting. Fail to connect: " + e.Message + "\n" + "\n"); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "TCP connection error: " + e.Message); - } - - - IsBusy?.Invoke(this, false); - return result; - } - - public async Task MpdIdleSendPassword(string password = "") - { - //MpcProgress?.Invoke(this, "Sending password..."); - - _password = password; - - CommandResult ret = new(); - - if (string.IsNullOrEmpty(password)) - { - ret.IsSuccess = true; - ret.ResultText = "OK"; - ret.ErrorMessage = ""; - - return ret; - } - - string cmd = "password " + password + "\n"; - - return await MpdIdleSendCommand(cmd); - - } - - private async Task MpdIdleSendCommand(string cmd) - { - CommandResult ret = new(); - - if (_idleConnection.Client == null) - { - Debug.WriteLine("@MpdIdleSendCommand: " + "TcpClient.Client == null"); - - ret.IsSuccess = false; - ret.ErrorMessage = "TcpClient.Client == null"; - - DebugIdleOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "MpdIdleSendCommand", "TcpClient.Client == null", cmd.Trim(), Environment.NewLine, "", Environment.NewLine + Environment.NewLine)); - - return ret; - } - - if ((_idleWriter == null) || (_idleReader == null)) - { - Debug.WriteLine("@MpdIdleSendCommand: " + "_idleWriter or _idleReader is null"); - - ret.IsSuccess = false; - ret.ErrorMessage = "_idleWriter or _idleReader is null"; - - DebugIdleOutput?.Invoke(this, string.Format("################ Error :@{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "MpdIdleSendCommand", "_idleWriter or _idleReader is null", cmd.Trim(), Environment.NewLine, "", Environment.NewLine + Environment.NewLine)); - - return ret; - } - - if (!_idleConnection.Client.Connected) - { - Debug.WriteLine("Exception@MpdIdleSendCommand: " + "NOT IsMpdIdleConnected"); - - ret.IsSuccess = false; - ret.ErrorMessage = "NOT IsMpdIdleConnected"; - - DebugIdleOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "MpdIdleSendCommand", "!CommandConnection.Client.Connected", cmd.Trim(), Environment.NewLine, "", Environment.NewLine + Environment.NewLine)); - - return ret; - } - - string cmdDummy = cmd; - if (cmd.StartsWith("password ")) - cmdDummy = "password ****"; - - //DebugIdleOutput?.Invoke(this, ">>>>" + cmdDummy.Trim() + "\n" + "\n"); - Task nowait = Task.Run(() => DebugIdleOutput?.Invoke(this, ">>>>" + cmdDummy.Trim() + "\n" + "\n")); - - try - { - if (cmd.Trim().StartsWith("idle")) - { - await _idleWriter.WriteAsync(cmd.Trim() + "\n"); - - return ret; - } - else - { - //await _idleWriter.WriteAsync("noidle\n" + cmd.Trim() + "\n"); - await _idleWriter.WriteAsync(cmd.Trim() + "\n"); - } - } - catch (System.IO.IOException e) - { - Debug.WriteLine("IOException@MpdIdleSendCommand: " + cmd.Trim() + " WriteAsync :" + e.Message); - - ret.IsSuccess = false; - ret.ErrorMessage = e.Message; - - // Could be application shutdopwn. - if ((ConnectionState == ConnectionStatus.Disconnecting) || (ConnectionState == ConnectionStatus.DisconnectedByUser)) - { - - } - else - { - DebugIdleOutput?.Invoke(this, string.Format("################ Error@{0}, Reason:{1}, Data:{2}, {3} Exception: {4} {5}", "WriteAsync@MpdIdleSendCommand", "IOException", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "The connection (idle) has been terminated (IOException): " + e.Message); - } - - return ret; - } - catch (Exception e) - { - Debug.WriteLine("Exception@MpdIdleSendCommand: " + cmd.Trim() + " WriteAsync " + e.Message); - - ret.IsSuccess = false; - ret.ErrorMessage = e.Message; - - if ((ConnectionState == ConnectionStatus.Disconnecting) || (ConnectionState == ConnectionStatus.DisconnectedByUser)) - { - - } - else - { - DebugIdleOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "WriteAsync@MpdIdleSendCommand", "Exception", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "The connection (idle) has been terminated (Exception): " + e.Message); - } - - return ret; - } - - try - { - StringBuilder stringBuilder = new(); - - bool isAck = false; - string ackText = ""; - - while (true) - { - string line = await _idleReader.ReadLineAsync(); - - if (line != null) - { - if (line.StartsWith("ACK")) - { - Debug.WriteLine("ACK line @MpdIdleSendCommand: " + cmd.Trim() + " and " + line); - - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - - isAck = true; - ackText = line; - ret.ErrorMessage = line; - - break; - } - else if (line.StartsWith("OK")) - { - ret.IsSuccess = true; - - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - - break; - } - else if (line.StartsWith("changed: ")) - { - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - } - else - { - if (!string.IsNullOrEmpty(line)) - { - stringBuilder.Append(line + "\n"); // << has to be \n - } - else - { - Debug.WriteLine("line == IsNullOrEmpty"); - - break; - } - } - } - else - { - Debug.WriteLine("@MpdIdleSendCommand ReadLineAsync line != null"); - - DebugIdleOutput?.Invoke(this, string.Format("################ Error @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "ReadLineAsync@MpdIdleSendCommand", "ReadLineAsync received null data", cmd.Trim(), Environment.NewLine, "", Environment.NewLine + Environment.NewLine)); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "The connection (idle) has been terminated. "); - - ret.ResultText = stringBuilder.ToString(); - ret.ErrorMessage = "ReadLineAsync@MpdIdleSendCommand received null data"; - - break; - } - } - - nowait = Task.Run(() => DebugIdleOutput?.Invoke(this, "<<<<" + stringBuilder.ToString().Trim().Replace("\n", "\n" + "<<<<") + "\n" + "\n")); - - if (isAck) - nowait = Task.Run(() => MpdAckError?.Invoke(this, ackText + " (@MISC)")); - - ret.ResultText = stringBuilder.ToString(); - - return ret; - } - catch (System.InvalidOperationException e) - { - // The stream is currently in use by a previous operation on the stream. - - Debug.WriteLine("InvalidOperationException@MpdIdleSendCommand: " + cmd.Trim() + " ReadLineAsync ---- " + e.Message); - - DebugIdleOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "ReadLineAsync@MpdIdleSendCommand", "InvalidOperationException", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - //ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - //ConnectionError?.Invoke(this, "The connection (idle) has been terminated. Most likely the connection has been overloaded."); - - - //await _idleWriter.WriteAsync("noidle\n"); - //await _idleWriter.WriteAsync("idle player\n"); - //await Task.Delay(1000); - - ret.IsSuccess = false; - ret.ErrorMessage = e.Message; - - return ret; - } - catch (Exception e) - { - Debug.WriteLine("Exception@MpdIdleSendCommand: " + cmd.Trim() + " ReadLineAsync ---- " + e.Message); - - ret.IsSuccess = false; - ret.ErrorMessage = e.Message; - - DebugIdleOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "ReadLineAsync@MpdIdleSendCommand", "Exception", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - return ret; - } - - } - - public async Task MpdIdleQueryStatus() - { - MpcProgress?.Invoke(this, "[Background] Querying status..."); - - CommandResult result = await MpdIdleSendCommand("status"); - if (result.IsSuccess) - { - MpcProgress?.Invoke(this, "[Background] Parsing status..."); - result.IsSuccess = await ParseStatus(result.ResultText); - - MpcProgress?.Invoke(this, "[Background] Status updated."); - } - - return result; - } - - public async Task MpdIdleQueryCurrentSong() - { - MpcProgress?.Invoke(this, "[Background] Querying current song info..."); - - CommandResult result = await MpdIdleSendCommand("currentsong"); - if (result.IsSuccess) - { - MpcProgress?.Invoke(this, "[Background] Parsing current song info..."); - result.IsSuccess = await ParseCurrentSong(result.ResultText); - - MpcProgress?.Invoke(this, "[Background] Current song info updated."); - } - - return result; - } - - public async Task MpdIdleQueryCurrentQueue() - { - MpcProgress?.Invoke(this, "[Background] Querying queue..."); - - CommandResult result = await MpdIdleSendCommand("playlistinfo"); - if (result.IsSuccess) - { - MpcProgress?.Invoke(this, "[Background] Parsing queue..."); - result.IsSuccess = await ParsePlaylistInfo(result.ResultText); - - MpcProgress?.Invoke(this, "[Background] Queue updated."); - } - - return result; - } - - public async Task MpdIdleQueryPlaylists() - { - MpcProgress?.Invoke(this, "[Background] Querying playlists..."); - - CommandResult result = await MpdIdleSendCommand("listplaylists"); - if (result.IsSuccess) - { - MpcProgress?.Invoke(this, "[Background] Parsing playlists..."); - result.IsSuccess = await ParsePlaylists(result.ResultText); - - MpcProgress?.Invoke(this, "[Background] Playlists updated."); - } - - return result; - } - - public async Task MpdIdleQueryListAll() - { - MpcProgress?.Invoke(this, "[Background] Querying files and directories..."); - - CommandResult result = await MpdIdleSendCommand("listall"); - if (result.IsSuccess) - { - MpcProgress?.Invoke(this, "[Background] Parsing files and directories..."); - result.IsSuccess = await ParseListAll(result.ResultText); - - MpcProgress?.Invoke(this, "[Background] Files and directories updated."); - } - - return result; - } - - public void MpdIdleStart() - { - //MpcProgress?.Invoke(this, ""); - - MpdIdle(); - } - - private async void MpdIdle() - { - if (MpdStop) - return; - - if (_idleConnection.Client == null) - { - Debug.WriteLine("@MpdIdle: " + "TcpClient.Client == null"); - - DebugIdleOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "MpdIdle", "TcpClient.Client == null", "", Environment.NewLine, "", Environment.NewLine + Environment.NewLine)); - - return; - } - - if ((_commandWriter == null) || (_commandReader == null)) - { - Debug.WriteLine("@MpdIdle: " + "_idleWriter or _idleReader is null"); - - DebugIdleOutput?.Invoke(this, string.Format("################ Error :@{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "MpdIdle", "_idleWriter or _idleReader is null", "", Environment.NewLine, "", Environment.NewLine + Environment.NewLine)); - - return; - } - - if (!_idleConnection.Client.Connected) - { - Debug.WriteLine("@MpdIdle: " + "!_idleConnection.Client.Connected"); - - DebugIdleOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "MpdIdle", "!_idleConnection.Client.Connected", "", Environment.NewLine, "", Environment.NewLine + Environment.NewLine)); - - return; - } - - string cmd = "idle player mixer options playlist stored_playlist\n"; - - DebugIdleOutput?.Invoke(this, ">>>>" + cmd.Trim() + "\n" + "\n"); - - try - { - await _idleWriter.WriteAsync(cmd); - } - catch (IOException e) - { - // connection terminated by ... something. - - Debug.WriteLine("[IOException@MpdIdle] ({0} ):\n{1}", "WriteAsync", e.Message); - - DebugIdleOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "WriteAsync@MpdIdle", "IOException", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "The connection (idle) has been terminated. IOException: " + e.Message); - - return; - } - catch (Exception e) - { - Debug.WriteLine("[Exception@MpdIdle] ({0} ):\n{1}", "WriteAsync", e.Message); - - DebugIdleOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "WriteAsync@MpdIdle", "Exception", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "The connection (idle) has been terminated. Exception: " + e.Message); - - return; - } - - try - { - StringBuilder stringBuilder = new(); - - bool isAck = false; - string ackText = ""; - - while (true) - { - if (MpdStop) - break; - - string line = await _idleReader.ReadLineAsync(); - - if (line != null) - { - if (line.StartsWith("ACK")) - { - Debug.WriteLine("ACK: " + line); - - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - - isAck = true; - ackText = line; - - break; - } - else if (line.StartsWith("OK")) - { - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - - break; - } - else if (line.StartsWith("changed: ")) - { - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - } - else - { - if (!string.IsNullOrEmpty(line)) - { - stringBuilder.Append(line + "\n"); - } - } - } - else - { - if ((ConnectionState == ConnectionStatus.Disconnecting) || (ConnectionState == ConnectionStatus.DisconnectedByUser) || (ConnectionState == ConnectionStatus.Connecting)) - { - // nothing wrong. - } - else - { - Debug.WriteLine("TCP Idle Connection: ReadLineAsync @MpdIdle received null data."); - - DebugIdleOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "ReadLineAsync@MpdIdle", "ReadLineAsync@MpdIdle received null data", cmd.Trim(), Environment.NewLine, "", Environment.NewLine + Environment.NewLine)); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "The connection has been terminated."); - - break; - } - - return; - } - } - - string result = stringBuilder.ToString(); - - DebugIdleOutput?.Invoke(this, "<<<<" + result.Trim().Replace("\n", "\n" + "<<<<") + "\n" + "\n"); - - if (isAck) - { - MpdAckError?.Invoke(this, ackText + " (@idle)"); - } - else - { - await ParseSubSystemsAndRaiseChangedEvent(result); - } - } - catch (System.IO.IOException e) - { - // Could be application shutdopwn. - - if ((ConnectionState == ConnectionStatus.Disconnecting) || (ConnectionState == ConnectionStatus.DisconnectedByUser) || (ConnectionState == ConnectionStatus.Connecting)) - { - // no problem - } - else - { - Debug.WriteLine("[IOException@MpdIdle] ({0}):\n{1}", "ReadLineAsync: " + ConnectionState.ToString(), e.Message); - - DebugIdleOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "ReadLineAsync@MpdIdle", "IOException", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "The connection (idle) has been terminated. Exception: " + e.Message); - } - } - catch (System.InvalidOperationException e) - { - // The stream is currently in use by a previous operation on the stream. - - DebugIdleOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "ReadLineAsync@MpdIdle", "InvalidOperationException", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "The connection (idle) has been terminated. Most likely the connection is overloaded."); - - } - catch (Exception e) - { - // Could be application shutdopwn. - - if ((ConnectionState == ConnectionStatus.Disconnecting) || (ConnectionState == ConnectionStatus.DisconnectedByUser)) - { - // no problem...probably. - } - else - { - Debug.WriteLine("[Exception@MpdIdle] ({0} ):\n{1}", "ReadLineAsync", e.Message); - - DebugIdleOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "ReadLineAsync@MpdIdle", "Exception", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "The connection (idle) has been terminated. Exception: " + e.Message); - } - } - } - - private async Task ParseSubSystemsAndRaiseChangedEvent(string result) - { - /* - public enum MpdSubSystems - { - //player mixer options playlist stored_playlist - //database: the song database has been modified after update. - //update: a database update has started or finished. If the database was modified during the update, the database event is also emitted. - //stored_playlist: a stored playlist has been modified, renamed, created or deleted - //playlist: the queue (i.e.the current playlist) has been modified - //player: the player has been started, stopped or seeked - //mixer: the volume has been changed - //output: an audio output has been added, removed or modified(e.g.renamed, enabled or disabled) - //options: options like repeat, random, crossfade, replay gain - //partition: a partition was added, removed or changed - //sticker: the sticker database has been modified. - //subscription: a client has subscribed or unsubscribed to a channel - //message: a message was received on a channel this client is subscribed to; this event is only emitted when the queue is empty - //neighbor: a neighbor was found or lost - //mount: the mount list has changed - - player, - mixer, - options, - playlist, - stored_playlist - } - */ - - List SubSystems = result.Split('\n').ToList(); - - try - { - bool isPlayer = false; - bool isCurrentQueue = false; - //bool isStoredPlaylist = false; - - foreach (string line in SubSystems) - { - if (line.ToLower() == "changed: playlist") - { - // playlist: the queue (i.e.the current playlist) has been modified - isCurrentQueue = true; - } - if (line.ToLower() == "changed: player") - { - // player: the player has been started, stopped or seeked - isPlayer = true; - } - if (line.ToLower() == "changed: options") - { - // options: options like repeat, random, crossfade, replay gain - isPlayer = true; - } - if (line.ToLower() == "changed: mixer") - { - // mixer: the volume has been changed - isPlayer = true; - } - if (line.ToLower() == "changed: stored_playlist") - { - // stored_playlist: a stored playlist has been modified, renamed, created or deleted - //isStoredPlaylist = true; - } - } - - if (isPlayer) - { - CommandResult idleResult = await MpdIdleQueryStatus(); - if (idleResult.IsSuccess) - { - MpdPlayerStatusChanged?.Invoke(this); - } - } - - // custom test - if (isCurrentQueue) - { - CommandResult idleResult = await MpdIdleQueryCurrentSong(); - if (idleResult.IsSuccess) - { - MpdCurrentQueueChanged?.Invoke(this); - } - } - - // takes lot of time - /*if (isCurrentQueue) - { - CommandResult idleResult = await MpdIdleQueryCurrentQueue(); - if (idleResult.IsSuccess) - { - MpdCurrentQueueChanged?.Invoke(this); - } - } - - if (isStoredPlaylist) - { - CommandResult idleResult = await MpdIdleQueryPlaylists(); - if (idleResult.IsSuccess) - { - MpdPlaylistsChanged?.Invoke(this); - } - }*/ - - //MpcProgress?.Invoke(this, ""); - - // start over. - MpdIdle(); - } - catch - { - Debug.WriteLine("**Error@ParseSubSystemsAndRaseChangedEvent: " + result); - } - - return true; - } - - #endregion - - #region == Command Connection == - - public async Task MpdCommandConnectionStart(string host, int port, string password) - { - ConnectionResult r = await MpdCommandConnect(host, port); - - if (r.IsSuccess) - { - CommandResult d = await MpdCommandSendPassword(password); - - if (d.IsSuccess) - { - // ここでIdleにして、以降はnoidle + cmd + idleの組み合わせでやる。 - // ただし、実際にはidleのあとReadしていないからタイムアウトで切断されてしまう模様。 - - d = await MpdSendIdle(); - - return d.IsSuccess; - } - } - - return false; - } - - public async Task MpdCommandConnect(string host, int port) - { - ConnectionResult result = new(); - - IsMpdCommandConnected = false; - - _commandConnection = new TcpClient(); - - _host = host; - _port = port; - - - DebugCommandOutput?.Invoke(this, "TCP Command Connection: Connecting." + "\n" + "\n"); - - ConnectionState = ConnectionStatus.Connecting; - - try - { - await _commandConnection.ConnectAsync(_host, _port); - - // TODO: - if (_commandConnection.Client == null) - { - Debug.WriteLine("_commandConnection.Client == null. " + host + " " + port.ToString()); - - result.ErrorMessage = "_commandConnection.Client == null"; - - DebugCommandOutput?.Invoke(this, "TCP Command Connection: Error while connecting. Fail to connect... " + "\n" + "\n"); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "TCP connection error..."); - - return result; - } - - if (_commandConnection.Client.Connected) - { - DebugCommandOutput?.Invoke(this, "TCP Command Connection: Connection established." + "\n" + "\n"); - - ConnectionState = ConnectionStatus.Connected; - - var tcpStream = _commandConnection.GetStream(); - //tcpStream.ReadTimeout = System.Threading.Timeout.Infinite; - // - tcpStream.ReadTimeout = 2000; - - _commandReader = new StreamReader(tcpStream); - _commandWriter = new StreamWriter(tcpStream) - { - AutoFlush = true - }; - - string response = await _commandReader.ReadLineAsync(); - - if (response.StartsWith("OK MPD ")) - { - MpdVerText = response.Replace("OK MPD ", string.Empty).Trim(); - - DebugCommandOutput?.Invoke(this, "<<<<" + response.Trim() + "\n" + "\n"); - - IsMpdCommandConnected = true; - - result.IsSuccess = true; - - // Done for now. - } - else - { - DebugCommandOutput?.Invoke(this, "TCP Command Connection: MPD did not respond with proper respose." + "\n" + "\n"); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "TCP connection error: MPD did not respond with proper respose."); - } - } - else - { - //? - - Debug.WriteLine("**** !client.Client.Connected"); - - DebugCommandOutput?.Invoke(this, "TCP Command Connection: FAIL to established... Client not connected." + "\n" + "\n"); - - ConnectionState = ConnectionStatus.NeverConnected; - - ConnectionError?.Invoke(this, "TCP Command Connection: FAIL to established... Client not connected."); - } - } - catch (SocketException e) - { - // TODO: Test. - - //e.SocketErrorCode - - DebugCommandOutput?.Invoke(this, "TCP Command Connection: Error while connecting. Fail to connect: " + e.Message + "\n" + "\n"); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "TCP connection error: " + e.Message); - } - catch (Exception e) - { - DebugCommandOutput?.Invoke(this, "TCP Command Connection: Error while connecting. Fail to connect (Exception): " + e.Message + "\n" + "\n"); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "TCP connection error: " + e.Message); - } - - return result; - } - - public async Task MpdCommandSendPassword(string password = "") - { - _password = password; - - CommandResult ret = new(); - - if (string.IsNullOrEmpty(password)) - { - ret.IsSuccess = true; - ret.ResultText = "OK"; - ret.ErrorMessage = ""; - - return ret; - } - - string cmd = "password " + password + "\n"; - - return await MpdSendCommand(cmd); - - } - - // TODO: Rename MpdSendCommand to MpdCommandSendCommand - private async Task MpdSendCommand(string cmd, bool isAutoIdling = false) - { - CommandResult ret = new(); - - if (_commandConnection.Client == null) - { - Debug.WriteLine("@MpdSendCommand: " + "TcpClient.Client == null"); - - ret.IsSuccess = false; - ret.ErrorMessage = "TcpClient.Client == null"; - - DebugCommandOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "MpdSendCommand", "TcpClient.Client == null", cmd.Trim(), Environment.NewLine, "", Environment.NewLine + Environment.NewLine)); - - return ret; - } - - if ((_commandWriter == null) || (_commandReader == null)) - { - Debug.WriteLine("@MpdSendCommand: " + "_commandWriter or _commandReader is null"); - - ret.IsSuccess = false; - ret.ErrorMessage = "_commandWriter or _commandReader is null"; - - DebugCommandOutput?.Invoke(this, string.Format("################ Error :@{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "MpdSendCommand", "_commandWriter or _commandReader is null", cmd.Trim(), Environment.NewLine, "", Environment.NewLine + Environment.NewLine)); - - return ret; - } - - if (!_commandConnection.Client.Connected) - { - Debug.WriteLine("@MpdSendCommand: " + "NOT IsMpdCommandConnected"); - - ret.IsSuccess = false; - ret.ErrorMessage = "NOT IsMpdCommandConnected"; - - DebugCommandOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "MpdSendCommand", "!CommandConnection.Client.Connected", cmd.Trim(), Environment.NewLine, "", Environment.NewLine + Environment.NewLine)); - - return ret; - } - - // WriteAsync - try - { - IsBusy?.Invoke(this, true); - - if (cmd.Trim().StartsWith("idle")) - { - //DebugCommandOutput?.Invoke(this, ">>>>" + cmd.Trim() + "\n" + "\n"); - Task nowait = Task.Run(() => DebugCommandOutput?.Invoke(this, ">>>>" + cmd.Trim() + "\n" + "\n")); - - await _commandWriter.WriteAsync(cmd.Trim() + "\n"); - - if (!isAutoIdling) - { - ret.IsSuccess = true; - - IsBusy?.Invoke(this, false); - return ret; - } - } - else - { - string cmdDummy = cmd; - if (cmd.StartsWith("password ")) - cmdDummy = "password ****"; - - cmdDummy = cmdDummy.Trim().Replace("\n", "\n" + ">>>>"); - - Task nowait; - if (isAutoIdling) - //DebugCommandOutput?.Invoke(this, ">>>>" + "noidle\n>>>>" + cmdDummy.Trim() + "\n>>>>idle player" + "\n" + "\n"); - nowait = Task.Run(() => DebugCommandOutput?.Invoke(this, ">>>>" + "noidle\n>>>>" + cmdDummy.Trim() + "\n>>>>idle player" + "\n" + "\n")); - else - //DebugCommandOutput?.Invoke(this, ">>>>" + cmdDummy.Trim() + "\n" + "\n"); - nowait = Task.Run(() => DebugCommandOutput?.Invoke(this, ">>>>" + cmdDummy.Trim() + "\n" + "\n")); - - if (isAutoIdling) - await _commandWriter.WriteAsync("noidle\n" + cmd.Trim() + "\n" + "idle player\n"); - else - await _commandWriter.WriteAsync(cmd.Trim() + "\n"); - } - } - catch (System.IO.IOException e) - { - // IOException : Unable to write data to the transport connection: 確立された接続がホスト コンピューターのソウトウェアによって中止されました。 - - ret.IsSuccess = false; - ret.ErrorMessage = e.Message; - - // Could be application shutdopwn. - if ((ConnectionState == ConnectionStatus.Disconnecting) || (ConnectionState == ConnectionStatus.DisconnectedByUser)) - { - - } - else - { - DebugCommandOutput?.Invoke(this, string.Format("################ Error@{0}, Reason:{1}, Data:{2}, {3} Exception: {4} {5}", "WriteAsync@MpdSendCommand", "IOException", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - // タイムアウトしていたらここで「も」エラーになる模様。 - - IsMpdCommandConnected = false; - - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting... " + Environment.NewLine + Environment.NewLine)); - - try - { - //_commandConnection.Client.Shutdown(SocketShutdown.Both); - _commandConnection.Close(); - } - catch { } - - ConnectionResult newCon = await MpdCommandConnect(_host,_port); - - if (newCon.IsSuccess) - { - CommandResult d = await MpdCommandSendPassword(_password); - - if (d.IsSuccess) - { - d = await MpdSendCommand("idle player"); - - if (d.IsSuccess) - { - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting Success. @IOExceptionOfWriteAsync" + Environment.NewLine + Environment.NewLine)); - - ret = await MpdSendCommand(cmd, isAutoIdling); - } - } - } - else - { - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting Failed. " + Environment.NewLine + Environment.NewLine)); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "The connection (command) has been terminated (IOException): " + e.Message); - } - } - - IsBusy?.Invoke(this, false); - return ret; - } - catch (Exception e) - { - Debug.WriteLine("Exception@MpdSendCommand: " + cmd.Trim() + " WriteAsync " + e.Message); - - ret.IsSuccess = false; - ret.ErrorMessage = e.Message; - - if ((ConnectionState == ConnectionStatus.Disconnecting) || (ConnectionState == ConnectionStatus.DisconnectedByUser)) - { - - } - else - { - DebugCommandOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "WriteAsync@MpdSendCommand", "Exception", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - //ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - //ConnectionError?.Invoke(this, "The connection (command) has been terminated (Exception): " + e.Message); - } - - IsBusy?.Invoke(this, false); - return ret; - } - - if ((ConnectionState == ConnectionStatus.Disconnecting) || (ConnectionState == ConnectionStatus.DisconnectedByUser)) - { - return ret; - } - - // ReadLineAsync - try - { - IsBusy?.Invoke(this, true); - - StringBuilder stringBuilder = new(); - - bool isDoubleOk = false; - bool isAck = false; - string ackText = ""; - bool isNullReturn = false; - - while (true) - { - string line = await _commandReader.ReadLineAsync(); - - if (line != null) - { - if (line.StartsWith("ACK")) - { - Debug.WriteLine("ACK line @MpdSendCommand: " + cmd.Trim() + " and " + line); - - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - - ret.ErrorMessage = line; - ackText = line; - isAck = true; - - break; - } - else if (line.StartsWith("OK")) - { - if (isAutoIdling) - { - if (isDoubleOk) - { - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - - ret.IsSuccess = true; - - break; - } - - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - - isDoubleOk = true; - - } - else - { - ret.IsSuccess = true; - - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - - break; - } - } - else if (line.StartsWith("changed: ")) - { - // noidleでついてくるかもしれないchanged. idleConnectionで見ているからここでは無視したいが・・・。 - - if (!string.IsNullOrEmpty(line)) - stringBuilder.Append(line + "\n"); - } - else - { - if (!string.IsNullOrEmpty(line)) - { - stringBuilder.Append(line + "\n"); // << has to be \n - } - } - } - else - { - isNullReturn = true; - - break; - } - } - - if (isNullReturn) - { - Debug.WriteLine("@MpdSendCommand ReadLineAsync isNullReturn"); - - DebugCommandOutput?.Invoke(this, string.Format("################ Error @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "ReadLineAsync@MpdSendCommand", "ReadLineAsync received null data", cmd.Trim(), Environment.NewLine, "", Environment.NewLine + Environment.NewLine)); - - ret.ResultText = stringBuilder.ToString(); - ret.ErrorMessage = "ReadLineAsync@MpdSendCommand received null data"; - - // タイムアウトしていたらここで「も」エラーになる模様。 - - if ((ConnectionState == ConnectionStatus.Disconnecting) || (ConnectionState == ConnectionStatus.DisconnectedByUser)) - { - IsBusy?.Invoke(this, false); - return ret; - } - - IsMpdCommandConnected = false; - - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting... " + Environment.NewLine + Environment.NewLine)); - - try - { - //_commandConnection.Client.Shutdown(SocketShutdown.Both); - _commandConnection.Close(); - } - catch { } - - ConnectionResult newCon = await MpdCommandConnect(_host, _port); - - if (newCon.IsSuccess) - { - CommandResult d = await MpdCommandSendPassword(_password); - - if (d.IsSuccess) - { - d = await MpdSendCommand("idle player"); - - if (d.IsSuccess) - { - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting Success. @isNullReturn" + Environment.NewLine + Environment.NewLine)); - - ret = await MpdSendCommand(cmd, isAutoIdling); - } - } - } - else - { - // - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting Failed. " + Environment.NewLine + Environment.NewLine)); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "The connection (command) has been terminated (null return)."); - } - - IsBusy?.Invoke(this, false); - return ret; - } - else - { - //DebugCommandOutput?.Invoke(this, "<<<<" + stringBuilder.ToString().Trim().Replace("\n", "\n" + "<<<<") + "\n" + "\n"); - Task nowait = Task.Run(() => DebugCommandOutput?.Invoke(this, "<<<<" + stringBuilder.ToString().Trim().Replace("\n", "\n" + "<<<<") + "\n" + "\n")); - - if (isAck) - nowait = Task.Run(() => MpdAckError?.Invoke(this, ackText + " (@MSC)")); - - ret.ResultText = stringBuilder.ToString(); - - IsBusy?.Invoke(this, false); - return ret; - } - - } - catch (System.InvalidOperationException e) - { - // The stream is currently in use by a previous operation on the stream. - - Debug.WriteLine("InvalidOperationException@MpdSendCommand: " + cmd.Trim() + " ReadLineAsync ---- " + e.Message); - - DebugCommandOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "ReadLineAsync@MpdSendCommand", "InvalidOperationException (Most likely the connection is overloaded)", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - //ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - //ConnectionError?.Invoke(this, "The connection (command) has been terminated. Most likely the connection has been overloaded."); - - //TODO: testing. null return? - //_commandReader.DiscardBufferedData(); - - - ret.IsSuccess = false; - ret.ErrorMessage = e.Message; - - IsBusy?.Invoke(this, false); - return ret; - } - catch (System.IO.IOException e) - { - // IOException : Unable to write data to the transport connection: 確立された接続がホスト コンピューターのソウトウェアによって中止されました。 - Debug.WriteLine("IOException@MpdSendCommand: " + cmd.Trim() + " ReadLineAsync ---- " + e.Message); - - ret.IsSuccess = false; - ret.ErrorMessage = e.Message; - - // Could be application shutdopwn. - if ((ConnectionState == ConnectionStatus.Disconnecting) || (ConnectionState == ConnectionStatus.DisconnectedByUser)) - { - - } - else - { - DebugCommandOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "ReadLineAsync@MpdSendCommand", "IOException", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - // タイムアウトしていたらここで「も」エラーになる模様。 - - IsMpdCommandConnected = false; - - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting... " + Environment.NewLine + Environment.NewLine)); - - try - { - //_commandConnection.Client.Shutdown(SocketShutdown.Both); - _commandConnection.Close(); - } - catch { } - - ConnectionResult newCon = await MpdCommandConnect(_host, _port); - - if (newCon.IsSuccess) - { - CommandResult d = await MpdCommandSendPassword(_password); - - if (d.IsSuccess) - { - d = await MpdSendCommand("idle player"); - - if (d.IsSuccess) - { - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting Success. @IOExceptionOfReadLineAsync" + Environment.NewLine + Environment.NewLine)); - - ret = await MpdSendCommand(cmd, isAutoIdling); - } - } - } - else - { - // Unable to read data from the transport connection: 既に接続済みのソケットに対して接続を要求しました。. - - DebugCommandOutput?.Invoke(this, string.Format("Reconnecting Failed. " + Environment.NewLine + Environment.NewLine)); - - ConnectionState = ConnectionStatus.SeeConnectionErrorEvent; - - ConnectionError?.Invoke(this, "The connection (command) has been terminated (IOException): " + e.Message); - } - } - - IsBusy?.Invoke(this, false); - return ret; - } - catch (Exception e) - { - Debug.WriteLine("Exception@MpdSendCommand: " + cmd.Trim() + " ReadLineAsync ---- " + e.Message); - - ret.IsSuccess = false; - ret.ErrorMessage = e.Message; - - DebugCommandOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "ReadLineAsync@MpdSendCommand", "Exception", cmd.Trim(), Environment.NewLine, e.Message, Environment.NewLine + Environment.NewLine)); - - IsBusy?.Invoke(this, false); - return ret; - } - - } - - #endregion - - #region == Command Connection's MPD Commands with results other than OK == - - public async Task MpdSendIdle() - { - return await MpdSendCommand("idle player"); - } - - public async Task MpdSendNoIdle() - { - return await MpdSendCommand("noidle"); - } - - public async Task MpdQueryStatus(bool autoIdling = true) - { - CommandResult result = await MpdSendCommand("status", autoIdling); - if (result.IsSuccess) - { - result.IsSuccess = await ParseStatus(result.ResultText); - } - - return result; - } - - public async Task MpdQueryCurrentSong(bool autoIdling = true) - { - CommandResult result = await MpdSendCommand("currentsong", autoIdling); - if (result.IsSuccess) - { - result.IsSuccess = await ParseCurrentSong(result.ResultText); - } - - return result; - } - - public async Task MpdQueryCurrentQueue(bool autoIdling = true) - { - CommandResult result = await MpdSendCommand("playlistinfo", autoIdling); - if (result.IsSuccess) - { - result.IsSuccess = await ParsePlaylistInfo(result.ResultText); - } - - return result; - } - - public async Task MpdQueryPlaylists(bool autoIdling = true) - { - CommandResult result = await MpdSendCommand("listplaylists", autoIdling); - if (result.IsSuccess) - { - result.IsSuccess = await ParsePlaylists(result.ResultText); - } - - return result; - } - - public async Task MpdQueryListAll(bool autoIdling = true) - { - MpcProgress?.Invoke(this, "[Background] Querying files and directories..."); - - CommandResult result = await MpdSendCommand("listall", autoIdling); - if (result.IsSuccess) - { - MpcProgress?.Invoke(this, "[Background] Parsing files and directories..."); - result.IsSuccess = await ParseListAll(result.ResultText); - MpcProgress?.Invoke(this, "[Background] Files and directories updated."); - } - - return result; - } - - public async Task MpdSearch(string queryTag, string queryShiki, string queryValue, bool autoIdling = true) - { - MpcProgress?.Invoke(this, "[Background] Searching..."); - - CommandSearchResult result = new(); - - if (Application.Current == null) { return result; } - Application.Current.Dispatcher.Invoke(() => - { - SearchResult.Clear(); - }); - - if (string.IsNullOrEmpty(queryTag) || string.IsNullOrEmpty(queryValue) || string.IsNullOrEmpty(queryShiki)) - { - result.IsSuccess = false; - return result; - } - - var expression = queryTag + " " + queryShiki + " \'" + Regex.Escape(queryValue) + "\'"; - - string cmd = "search \"(" + expression + ")\"\n"; - - CommandResult cm = await MpdSendCommand(cmd, autoIdling); - if (cm.IsSuccess) - { - MpcProgress?.Invoke(this, "[Background] Parsing search result..."); - if (await ParseSearchResult(cm.ResultText)) - { - result.IsSuccess = true; - - result.SearchResult = this.SearchResult; - - MpcProgress?.Invoke(this, "[Background] Search result updated."); - } - } - - return result; - } - - public async Task MpdQueryPlaylistSongs(string playlistName, bool autoIdling = true) - { - CommandPlaylistResult result = new(); - - if (string.IsNullOrEmpty(playlistName)) - { - result.IsSuccess = false; - return result; - } - - MpcProgress?.Invoke(this, "[Background] Querying playlist info..."); - - playlistName = Regex.Escape(playlistName); - - CommandResult cm = await MpdSendCommand("listplaylistinfo \"" + playlistName + "\"", autoIdling); - if (cm.IsSuccess) - { - MpcProgress?.Invoke(this, "[Background] Parsing playlist info..."); - - result.IsSuccess = cm.IsSuccess; - result.PlaylistSongs = ParsePlaylistSongsResult(cm.ResultText); - - MpcProgress?.Invoke(this, "[Background] Playlist info is updated."); - } - - return result; - } - - public async Task MpdQueryAlbumArt(string uri, bool isUsingReadpicture) - { - CommandResult f = new(); - if (string.IsNullOrEmpty(uri)) - { - f.IsSuccess = false; - return f; - } - - BinaryDownloader hoge = new(); - - bool asdf = await hoge.MpdBinaryConnectionStart(MpdHost,MpdPort,MpdPassword); - - CommandResult b = new(); - - if (asdf) - { - b = await hoge.MpdQueryAlbumArt(uri, isUsingReadpicture); - - if (b.IsSuccess) - { - if (Application.Current == null) { return f; } - Application.Current.Dispatcher.Invoke(() => - { - _albumCover = hoge.AlbumCover; - }); - - await Task.Delay(1300); - - //Debug.WriteLine("AlbumArt Donwloaded... "); - - MpdAlbumArtChanged?.Invoke(this); - } - else - { - //Debug.WriteLine("why... " + b.ErrorMessage); - } - } - else - { - Debug.WriteLine("damn... "); - } - - hoge.MpdBinaryConnectionDisconnect(); - - return b; - } - - #endregion - - #region == Command Connection's MPD Commands with boolean result == - - public async Task MpdSendUpdate() - { - CommandResult result = await MpdSendCommand("update", true); - - return result; - } - - public async Task MpdPlaybackPlay(int volume, string songId = "") - { - string cmd = "play"; - - if (songId != "") - { - cmd = "playid " + songId; - } - - if (MpdStatus.MpdVolumeIsSet) - { - CommandResult result = await MpdSendCommand(cmd, true); - - return result; - } - else - { - string cmdList = "command_list_begin" + "\n"; - cmdList = cmdList + cmd + "\n"; - cmdList = cmdList + "setvol " + volume.ToString() + "\n"; - cmdList = cmdList + "command_list_end" + "\n"; - - CommandResult result = await MpdSendCommand(cmdList, true); - - return result; - } - } - - public async Task MpdPlaybackPause() - { - CommandResult result = await MpdSendCommand("pause 1", true); - - return result; - } - - public async Task MpdPlaybackResume(int volume) - { - if (MpdStatus.MpdVolumeIsSet) - { - CommandResult result = await MpdSendCommand("pause 0", true); - - return result; - } - else - { - string cmd = "command_list_begin" + "\n"; - cmd += "pause 0\n"; - cmd = cmd + "setvol " + volume.ToString() + "\n"; - cmd = cmd + "command_list_end" + "\n"; - - CommandResult result = await MpdSendCommand(cmd, true); - - return result; - } - } - - public async Task MpdPlaybackStop() - { - CommandResult result = await MpdSendCommand("stop", true); - - return result; - } - - public async Task MpdPlaybackNext(int volume) - { - if (MpdStatus.MpdVolumeIsSet) - { - CommandResult result = await MpdSendCommand("next", true); - - return result; - } - else - { - string cmd = "command_list_begin" + "\n"; - cmd += "next\n"; - cmd = cmd + "setvol " + volume.ToString() + "\n"; - cmd = cmd + "command_list_end" + "\n"; - - CommandResult result = await MpdSendCommand(cmd, true); - - return result; - } - } - - public async Task MpdPlaybackPrev(int volume) - { - if (MpdStatus.MpdVolumeIsSet) - { - CommandResult result = await MpdSendCommand("previous", true); - - return result; - } - else - { - string cmd = "command_list_begin" + "\n"; - cmd += "previous\n"; - cmd = cmd + "setvol " + volume.ToString() + "\n"; - cmd = cmd + "command_list_end" + "\n"; - - CommandResult result = await MpdSendCommand(cmd, true); - - return result; - } - } - - public async Task MpdSetVolume(int v) - { - if (v == _status.MpdVolume) - { - CommandResult f = new() - { - IsSuccess = true - }; - return f; - } - - CommandResult result = await MpdSendCommand("setvol " + v.ToString(), true); - - return result; - } - - public async Task MpdPlaybackSeek(string songId, int seekTime) - { - if ((songId == "") || (seekTime == 0)) - { - CommandResult f = new() - { - IsSuccess = true - }; - return f; - } - - CommandResult result = await MpdSendCommand("seekid " + songId + " " + seekTime.ToString(), true); - - return result; - } - - public async Task MpdSetRepeat(bool on) - { - if (_status.MpdRepeat == on) - { - CommandResult f = new() - { - IsSuccess = true - }; - return f; - } - - string cmd; - if (on) - { - cmd = "repeat 1"; - } - else - { - cmd = "repeat 0"; - } - - CommandResult result = await MpdSendCommand(cmd, true); - - return result; - } - - public async Task MpdSetRandom(bool on) - { - if (_status.MpdRandom == on) - { - CommandResult f = new() - { - IsSuccess = true - }; - return f; - } - - string cmd; - if (on) - { - cmd = "random 1"; - } - else - { - cmd = "random 0"; - } - - CommandResult result = await MpdSendCommand(cmd, true); - - return result; - } - - public async Task MpdSetConsume(bool on) - { - if (_status.MpdConsume == on) - { - CommandResult f = new() - { - IsSuccess = true - }; - return f; - } - - string cmd; - if (on) - { - cmd = "consume 1"; - } - else - { - cmd = "consume 0"; - } - - CommandResult result = await MpdSendCommand(cmd, true); - - return result; - } - - public async Task MpdSetSingle(bool on) - { - if (_status.MpdSingle == on) - { - CommandResult f = new() - { - IsSuccess = true - }; - return f; - } - - string cmd; - if (on) - { - cmd = "single 1"; - } - else - { - cmd = "single 0"; - } - - CommandResult result = await MpdSendCommand(cmd, true); - - return result; - } - - public async Task MpdClear() - { - CommandResult result = await MpdSendCommand("clear", true); - - return result; - } - - public async Task MpdSave(string playlistName) - { - if (string.IsNullOrEmpty(playlistName)) - { - CommandResult f = new() - { - IsSuccess = false - }; - return f; - } - - playlistName = Regex.Escape(playlistName); - - CommandResult result = await MpdSendCommand("save \"" + playlistName + "\"", true); - - return result; - } - - public async Task MpdAdd(string uri) - { - if (string.IsNullOrEmpty(uri)) - { - CommandResult f = new() - { - IsSuccess = false - }; - return f; - } - - uri = Regex.Escape(uri); - - CommandResult result = await MpdSendCommand("add \"" + uri + "\"", true); - - return result; - } - - public async Task MpdAdd(List uris) - { - if (uris.Count < 1) - { - CommandResult f = new() - { - IsSuccess = false - }; - return f; - } - - string cmd = "command_list_begin" + "\n"; - foreach (var uri in uris) - { - var urie = Regex.Escape(uri); - cmd = cmd + "add \"" + urie + "\"\n"; - } - cmd = cmd + "command_list_end" + "\n"; - - CommandResult result = await MpdSendCommand(cmd, true); - - return result; - } - - public async Task MpdDeleteId(List ids) - { - if (ids.Count < 1) - { - CommandResult f = new() - { - IsSuccess = false - }; - return f; - } - - string cmd = "command_list_begin" + "\n"; - foreach (var id in ids) - { - cmd = cmd + "deleteid " + id + "\n"; - } - cmd = cmd + "command_list_end" + "\n"; - - return await MpdSendCommand(cmd, true); - } - - public async Task MpdDeleteId(string id) - { - if (string.IsNullOrEmpty(id)) - { - CommandResult f = new() - { - IsSuccess = false - }; - return f; - } - - string cmd = "deleteid " + id + "\n"; - - return await MpdSendCommand(cmd, true); - } - - public async Task MpdMoveId(Dictionary IdToNewPosPair) - { - if (IdToNewPosPair == null) - { - CommandResult f = new() - { - IsSuccess = false - }; - return f; - } - if (IdToNewPosPair.Count < 1) - { - CommandResult f = new() - { - IsSuccess = false - }; - return f; - } - - string cmd = "command_list_begin" + "\n"; - foreach (KeyValuePair pair in IdToNewPosPair) - { - cmd = cmd + "moveid " + pair.Key + " " + pair.Value + "\n"; - } - cmd = cmd + "command_list_end" + "\n"; - - CommandResult result = await MpdSendCommand(cmd, true); - - return result; - } - - public async Task MpdChangePlaylist(string playlistName) - { - if (string.IsNullOrEmpty(playlistName)) - { - CommandResult f = new() - { - IsSuccess = false - }; - return f; - } - - playlistName = Regex.Escape(playlistName); - - string cmd = "command_list_begin" + "\n"; - cmd = cmd + "clear" + "\n"; - cmd = cmd + "load \"" + playlistName + "\"\n"; - cmd = cmd + "command_list_end" + "\n"; - - CommandResult result = await MpdSendCommand(cmd, true); - - return result; - } - - public async Task MpdLoadPlaylist(string playlistName) - { - if (string.IsNullOrEmpty(playlistName)) - { - CommandResult f = new() - { - IsSuccess = false - }; - return f; - } - - playlistName = Regex.Escape(playlistName); - - string cmd = "load \"" + playlistName + "\""; - - CommandResult result = await MpdSendCommand(cmd, true); - - return result; - } - - public async Task MpdRenamePlaylist(string playlistName, string newPlaylistName) - { - if (string.IsNullOrEmpty(playlistName) || string.IsNullOrEmpty(newPlaylistName)) - { - CommandResult f = new() - { - IsSuccess = false - }; - return f; - } - - playlistName = Regex.Escape(playlistName); - newPlaylistName = Regex.Escape(newPlaylistName); - - string cmd = "rename \"" + playlistName + "\" \"" + newPlaylistName + "\""; - - CommandResult result = await MpdSendCommand(cmd, true); - - return result; - } - - public async Task MpdRemovePlaylist(string playlistName) - { - if (string.IsNullOrEmpty(playlistName)) - { - CommandResult f = new() - { - IsSuccess = false - }; - return f; - } - - playlistName = Regex.Escape(playlistName); - - string cmd = "rm \"" + playlistName + "\""; - - CommandResult result = await MpdSendCommand(cmd, true); - - return result; - } - - public async Task MpdPlaylistAdd(string playlistName, List uris) - { - if (string.IsNullOrEmpty(playlistName)) - { - CommandResult f = new() - { - IsSuccess = false - }; - return f; - } - if (uris == null) - { - CommandResult f = new() - { - IsSuccess = false - }; - return f; - } - if (uris.Count < 1) - { - CommandResult f = new() - { - IsSuccess = false - }; - return f; - } - - playlistName = Regex.Escape(playlistName); - - string cmd = "command_list_begin" + "\n"; - foreach (var uri in uris) - { - var urie = Regex.Escape(uri); - cmd = cmd + "playlistadd " + "\"" + playlistName + "\"" + " " + "\"" + urie + "\"\n"; - } - cmd = cmd + "command_list_end" + "\n"; - - CommandResult result = await MpdSendCommand(cmd, true); - - return result; - } - - public async Task MpdPlaylistDelete(string playlistName, int pos) - { - if (string.IsNullOrEmpty(playlistName)) - { - CommandResult f = new() - { - IsSuccess = false - }; - return f; - } - - playlistName = Regex.Escape(playlistName); - - //playlistdelete {NAME} {SONGPOS} - string cmd = "playlistdelete \"" + playlistName + "\"" + " " + pos.ToString(); - - CommandResult result = await MpdSendCommand(cmd, true); - - return result; - } - - /* NOT GOOD. Multiple deletion with SONGPO causes pos to shift. - public async Task MpdPlaylistDelete(string playlistName, List posList) - { - if (string.IsNullOrEmpty(playlistName)) - { - CommandResult f = new CommandResult(); - f.IsSuccess = false; - return f; - } - if (posList == null) - { - CommandResult f = new CommandResult(); - f.IsSuccess = false; - return f; - } - if (posList.Count < 1) - { - CommandResult f = new CommandResult(); - f.IsSuccess = false; - return f; - } - - playlistName = Regex.Escape(playlistName); - - string cmd = "command_list_begin" + "\n"; - foreach (var pos in posList) - { - cmd = cmd + "playlistdelete " + "\"" + playlistName + "\"" + " " + pos.ToString() + "\n"; - } - cmd = cmd + "command_list_end" + "\n"; - - CommandResult result = await MpdSendCommand(cmd, true); - - return result; - } - */ - - public async Task MpdPlaylistClear(string playlistName) - { - if (string.IsNullOrEmpty(playlistName)) - { - CommandResult f = new() - { - IsSuccess = false - }; - return f; - } - - playlistName = Regex.Escape(playlistName); - - //playlistclear {NAME} - string cmd = "playlistclear \"" + playlistName + "\""; - - CommandResult result = await MpdSendCommand(cmd, true); - - return result; - } - - #endregion - - #region == Response parser methods == - - private Task ParseStatus(string result) - { - if (MpdStop) { return Task.FromResult(false); } - if (string.IsNullOrEmpty(result)) return Task.FromResult(false); - - if (result.Trim() == "OK") - { - DebugCommandOutput?.Invoke(this, "################(Error) " + "An empty result (OK) returened for a status command." + Environment.NewLine + Environment.NewLine); - DebugCommandOutput?.Invoke(this, string.Format("################ Error: @{0}, Reason: {1}, Data: {2}, {3} Exception: {4} {5}", "ParseStatus", "An empty result (OK) returened for a status command.", "", Environment.NewLine, "", Environment.NewLine + Environment.NewLine)); - - Debug.WriteLine("@ParseStatus: An empty result (OK) returened for a status command."); - - return Task.FromResult(false); - } - - List resultLines = result.Split('\n').ToList(); - - if (resultLines.Count == 0) return Task.FromResult(false); - - var comparer = StringComparer.OrdinalIgnoreCase; - Dictionary MpdStatusValues = new(comparer); - - try - { - IsBusy?.Invoke(this, true); - - Application.Current.Dispatcher.Invoke(() => - { - _status.Reset(); - - foreach (string line in resultLines) - { - string[] StatusValuePair = line.Split(':'); - if (StatusValuePair.Length > 1) - { - if (MpdStatusValues.ContainsKey(StatusValuePair[0].Trim())) - { - // new - - MpdStatusValues[StatusValuePair[0].Trim()] = line.Replace(StatusValuePair[0].Trim() + ": ", ""); - } - else - { - MpdStatusValues.Add(StatusValuePair[0].Trim(), line.Replace(StatusValuePair[0].Trim() + ": ", "")); - } - } - } - - // Play state - if (MpdStatusValues.ContainsKey("state")) - { - switch (MpdStatusValues["state"]) - { - case "play": - { - _status.MpdState = Status.MpdPlayState.Play; - break; - } - case "pause": - { - _status.MpdState = Status.MpdPlayState.Pause; - break; - } - case "stop": - { - _status.MpdState = Status.MpdPlayState.Stop; - break; - } - } - } - - // Volume - if (MpdStatusValues.ContainsKey("volume")) - { - try - { - _status.MpdVolume = Int32.Parse(MpdStatusValues["volume"]); - - _status.MpdVolumeIsSet = true; - } - catch (FormatException e) - { - System.Diagnostics.Debug.WriteLine(e.Message); - } - } - - if (MpdStatusValues.ContainsKey("bitrate")) - { - try - { - _status.MpdBitrate = MpdStatusValues["bitrate"]; - } - catch (FormatException e) - { - System.Diagnostics.Debug.WriteLine(e.Message); - } - } - - // songID - _status.MpdSongID = ""; - if (MpdStatusValues.ContainsKey("songid")) - { - _status.MpdSongID = MpdStatusValues["songid"]; - } - - // Repeat opt bool. - if (MpdStatusValues.ContainsKey("repeat")) - { - try - { - if (MpdStatusValues["repeat"] == "1") - { - _status.MpdRepeat = true; - } - else - { - _status.MpdRepeat = false; - } - - } - catch (FormatException e) - { - System.Diagnostics.Debug.WriteLine(e.Message); - } - } - - // Random opt bool. - if (MpdStatusValues.ContainsKey("random")) - { - try - { - if (Int32.Parse(MpdStatusValues["random"]) > 0) - { - _status.MpdRandom = true; - } - else - { - _status.MpdRandom = false; - } - - } - catch (FormatException e) - { - System.Diagnostics.Debug.WriteLine(e.Message); - } - } - - // Consume opt bool. - if (MpdStatusValues.ContainsKey("consume")) - { - try - { - if (Int32.Parse(MpdStatusValues["consume"]) > 0) - { - _status.MpdConsume = true; - } - else - { - _status.MpdConsume = false; - } - - } - catch (FormatException e) - { - System.Diagnostics.Debug.WriteLine(e.Message); - } - } - - // Single opt bool. - if (MpdStatusValues.ContainsKey("single")) - { - try - { - if (Int32.Parse(MpdStatusValues["single"]) > 0) - { - _status.MpdSingle = true; - } - else - { - _status.MpdSingle = false; - } - - } - catch (FormatException e) - { - System.Diagnostics.Debug.WriteLine(e.Message); - } - } - - // Song time. deprecated. - if (MpdStatusValues.ContainsKey("time")) - { - try - { - if (MpdStatusValues["time"].Split(':').Length > 1) - { - _status.MpdSongTime = Double.Parse(MpdStatusValues["time"].Split(':')[1].Trim()); - _status.MpdSongElapsed = Double.Parse(MpdStatusValues["time"].Split(':')[0].Trim()); - } - } - catch (FormatException e) - { - System.Diagnostics.Debug.WriteLine(e.Message); - } - } - - // Song time elapsed. - if (MpdStatusValues.ContainsKey("elapsed")) - { - try - { - _status.MpdSongElapsed = Double.Parse(MpdStatusValues["elapsed"]); - } - catch { } - } - - // Song duration. - if (MpdStatusValues.ContainsKey("duration")) - { - try - { - _status.MpdSongTime = Double.Parse(MpdStatusValues["duration"]); - } - catch { } - } - - // Error - if (MpdStatusValues.ContainsKey("error")) - { - _status.MpdError = MpdStatusValues["error"]; - } - else - { - _status.MpdError = ""; - } - - // TODO: more? - }); - - } - catch (Exception ex) - { - Debug.WriteLine("Exception@ParseStatus:" + ex.Message); - - IsBusy?.Invoke(this, false); - return Task.FromResult(false); - } - finally - { - IsBusy?.Invoke(this, false); - } - - return Task.FromResult(true); - } - - private Task ParseCurrentSong(string result) - { - if (MpdStop) return Task.FromResult(false); - - bool isEmptyResult = false; - - if (string.IsNullOrEmpty(result)) - isEmptyResult = true; - - if (result.Trim() == "OK") - isEmptyResult = true; - - List resultLines = result.Split('\n').ToList(); - if (resultLines == null) - isEmptyResult = true; - if (resultLines.Count == 0) - isEmptyResult = true; - - if (isEmptyResult) - { - return Task.FromResult(true); - } - - IsBusy?.Invoke(this, true); - - try - { - var comparer = StringComparer.OrdinalIgnoreCase; - Dictionary SongValues = new(comparer); - - foreach (string value in resultLines) - { - string[] StatusValuePair = value.Trim().Split(':'); - if (StatusValuePair.Length > 1) - { - if (SongValues.ContainsKey(StatusValuePair[0].Trim())) - { - // Shouldn't be happening here except "Genre" - if (StatusValuePair[0].Trim() == "Genre") - { - SongValues["Genre"] = SongValues["Genre"] + "/" + value.Replace(StatusValuePair[0].Trim() + ": ", ""); - } - } - else - { - SongValues.Add(StatusValuePair[0].Trim(), value.Replace(StatusValuePair[0].Trim() + ": ", "")); - } - } - } - - if ((SongValues.Count > 0) && SongValues.ContainsKey("Id")) - { - SongInfoEx sng = FillSongInfoEx(SongValues, -1); - - if (sng != null) - { - if (_currentSong.Id != sng.Id) - _currentSong = sng; - } - - SongValues.Clear(); - } - } - catch (Exception ex) - { - Debug.WriteLine("Error@ParseCurrentSong: " + ex.Message); - - return Task.FromResult(false); - } - finally - { - IsBusy?.Invoke(this, false); - } - - return Task.FromResult(true); - } - - private Task ParsePlaylistInfo(string result) - { - if (MpdStop) return Task.FromResult(false); - - bool isEmptyResult = false; - - if (string.IsNullOrEmpty(result)) - isEmptyResult = true; - - if (result.Trim() == "OK") - isEmptyResult = true; - - List resultLines = result.Split('\n').ToList(); - if (resultLines == null) - isEmptyResult = true; - if (resultLines.Count == 0) - isEmptyResult = true; - - if (isEmptyResult) - { - if (Application.Current == null) { return Task.FromResult(false); } - Application.Current.Dispatcher.Invoke(() => - { - CurrentQueue.Clear(); - }); - - return Task.FromResult(true); - } - - // aka Queue - - /* - file: Creedence Clearwater Revival/Chronicle, Vol. 1/11 Who'll Stop the Rain.mp3 - Last-Modified: 2011-08-03T16:08:06Z - Artist: Creedence Clearwater Revival - AlbumArtist: Creedence Clearwater Revival - Title: Who'll Stop the Rain - Album: Chronicle, Vol. 1 - Track: 11 - Date: 1976 - Genre: Rock - Composer: John Fogerty - Time: 149 - duration: 149.149 - Pos: 5 - Id: 22637 - */ - - /* - try - { - - resultLines.Clear(); - resultLines.Add("file: Slamhaus/Slamwave/06 The Trial.flac"); - resultLines.Add("Last-Modified: 2021-05-27T19:20:50Z"); - resultLines.Add("Title: The Trial"); - resultLines.Add("Artist: Slamhaus"); - resultLines.Add("Date: 2021"); - resultLines.Add("Comment: Visit https://slamhaus.bandcamp.com"); - resultLines.Add("Album: Slamwave"); - resultLines.Add("Track: 6"); - resultLines.Add("AlbumArtist: Slamhaus"); - resultLines.Add("Time: 340"); - resultLines.Add("duration: 339.504"); - resultLines.Add("Pos: 5"); - resultLines.Add("Id: 1438"); - resultLines.Add("file: Harris Heller/Synthwave/Sunset/01 - Zig the Zag.flac"); - resultLines.Add("Last-Modified: 2021-06-03T16:52:10Z"); - resultLines.Add("Album: Sunset"); - resultLines.Add("AlbumArtist: Harris Heller"); - resultLines.Add("Artist: Harris Heller"); - resultLines.Add("Date: 2021-03-05"); - resultLines.Add("Disc: 1"); - resultLines.Add("Genre: Electro"); - resultLines.Add("Genre: Dance"); - resultLines.Add("Title: Zig the Zag"); - resultLines.Add("Track: 1"); - resultLines.Add("Time: 126"); - resultLines.Add("duration: 126.250"); - resultLines.Add("Pos: 6"); - resultLines.Add("Id: 1439"); - } - catch (Exception ex) - { - Debug.WriteLine("Error@ParsePlaylistInfo: " + ex.Message); - } - */ - - try - { - IsBusy?.Invoke(this, true); - - ObservableCollection tmpQueue = new(); - /* - if (Application.Current == null) { return Task.FromResult(false); } - Application.Current.Dispatcher.Invoke(() => - { - CurrentQueue.Clear(); - }); - */ - - var comparer = StringComparer.OrdinalIgnoreCase; - Dictionary SongValues = new(comparer); - - int i = 0; - - foreach (string value in resultLines) - { - string[] StatusValuePair = value.Trim().Split(':'); - if (StatusValuePair.Length > 1) - { - if (SongValues.ContainsKey(StatusValuePair[0].Trim())) - { - if (SongValues.ContainsKey("Id")) - { - SongInfoEx sng = FillSongInfoEx(SongValues, i); - - if (sng != null) - { - /* - if (Application.Current == null) { return Task.FromResult(false); } - Application.Current.Dispatcher.Invoke(() => - { - CurrentQueue.Add(sng); - }); - */ - //CurrentQueue.Add(sng); - tmpQueue.Add(sng); - - i++; - - MpcProgress?.Invoke(this, string.Format("[Background] Parsing queue item... ({0})", i)); - - SongValues.Clear(); - } - } - /* - if (!SongValues.ContainsKey(StatusValuePair[0].Trim())) - { - SongValues.Add(StatusValuePair[0].Trim(), value.Replace(StatusValuePair[0].Trim() + ": ", "")); - } - else - { - if (StatusValuePair[0].Trim() == "Genre") - { - SongValues["Genre"] = SongValues["Genre"] + "/" + value.Replace(StatusValuePair[0].Trim() + ": ", ""); - } - } - */ - } - - if (!SongValues.ContainsKey(StatusValuePair[0].Trim())) - { - SongValues.Add(StatusValuePair[0].Trim(), value.Replace(StatusValuePair[0].Trim() + ": ", "")); - } - else - { - if (StatusValuePair[0].Trim() == "Genre") - { - SongValues["Genre"] = SongValues["Genre"] + "/" + value.Replace(StatusValuePair[0].Trim() + ": ", ""); - } - } - } - } - - if ((SongValues.Count > 0) && SongValues.ContainsKey("Id")) - { - SongInfoEx sng = FillSongInfoEx(SongValues, i); - - if (sng != null) - { - SongValues.Clear(); - /* - if (Application.Current == null) { return Task.FromResult(false); } - Application.Current.Dispatcher.Invoke(() => - { - CurrentQueue.Add(sng); - }); - */ - //CurrentQueue.Add(sng); - tmpQueue.Add(sng); - - MpcProgress?.Invoke(this, string.Format("[Background] Parsing queue item... ({0})", i+1)); - } - } - - // test - MpcProgress?.Invoke(this, "[Background] Updating internal queue list..."); - if (Application.Current == null) { return Task.FromResult(false); } - Application.Current.Dispatcher.Invoke(() => - { - //CurrentQueue.Clear(); - //_queue = tmpQueue; - _queue = new ObservableCollection(tmpQueue); - }); - MpcProgress?.Invoke(this, "[Background] Internal queue list is updated."); - - } - catch (Exception ex) - { - Debug.WriteLine("Exception@ParsePlaylistInfo: " + ex.Message); - - return Task.FromResult(false); - } - finally - { - IsBusy?.Invoke(this, false); - } - - return Task.FromResult(true); - } - - private SongInfoEx FillSongInfoEx(Dictionary SongValues, int i) - { - try - { - SongInfoEx sng = new(); - - if (SongValues.ContainsKey("Id")) - { - sng.Id = SongValues["Id"]; - } - - if (SongValues.ContainsKey("Title")) - { - sng.Title = SongValues["Title"]; - } - else - { - sng.Title = ""; - if (SongValues.ContainsKey("file")) - { - sng.Title = Path.GetFileName(SongValues["file"]); - } - } - - if (SongValues.ContainsKey("Artist")) - { - sng.Artist = SongValues["Artist"]; - } - else - { - if (SongValues.ContainsKey("AlbumArtist")) - { - // TODO: Should I? - sng.Artist = SongValues["AlbumArtist"]; - } - else - { - sng.Artist = ""; - } - } - - if (SongValues.ContainsKey("Last-Modified")) - { - sng.LastModified = SongValues["Last-Modified"]; - } - - if (SongValues.ContainsKey("AlbumArtist")) - { - sng.AlbumArtist = SongValues["AlbumArtist"]; - } - - if (SongValues.ContainsKey("Album")) - { - sng.Album = SongValues["Album"]; - } - - if (SongValues.ContainsKey("Track")) - { - sng.Track = SongValues["Track"]; - } - - if (SongValues.ContainsKey("Disc")) - { - sng.Disc = SongValues["Disc"]; - } - - if (SongValues.ContainsKey("Date")) - { - sng.Date = SongValues["Date"]; - } - - if (SongValues.ContainsKey("Genre")) - { - sng.Genre = SongValues["Genre"]; - } - - if (SongValues.ContainsKey("Composer")) - { - sng.Composer = SongValues["Composer"]; - } - - if (SongValues.ContainsKey("Time")) - { - sng.Time = SongValues["Time"]; - } - - if (SongValues.ContainsKey("duration")) - { - sng.Time = SongValues["duration"]; - sng.Duration = SongValues["duration"]; - } - - if (SongValues.ContainsKey("Pos")) - { - sng.Pos = SongValues["Pos"]; - } - - if (SongValues.ContainsKey("file")) - { - sng.File = SongValues["file"]; - } - - // for sorting. - sng.Index = i; - /* - if (i < 0) - { - // -1 means we are not parsing queue but parsing currentsong. ParsePlaylists(string result) - { - bool isEmptyResult = false; - - if (string.IsNullOrEmpty(result)) - isEmptyResult = true; - - List resultLines = result.Split('\n').ToList(); - - if (resultLines.Count == 0) - isEmptyResult = true; - - if (isEmptyResult) - { - if (Application.Current == null) { return Task.FromResult(false); } - Application.Current.Dispatcher.Invoke(() => - { - Playlists.Clear(); - }); - - return Task.FromResult(true); - } - - try - { - IsBusy?.Invoke(this, true); - - ObservableCollection tmpPlaylists = new(); - - Playlist pl = null; - - foreach (string value in resultLines) - { - if (value.StartsWith("playlist:")) - { - - pl = new Playlist - { - Name = value.Replace("playlist: ", "") - }; - - tmpPlaylists.Add(pl); - } - else if (value.StartsWith("Last-Modified: ")) - { - if (pl != null) - pl.LastModified = value.Replace("Last-Modified: ", ""); - } - else if (value.StartsWith("OK")) - { - // Ignoring. - } - } - - if (Application.Current == null) { return Task.FromResult(false); } - Application.Current.Dispatcher.Invoke(() => - { - _playlists = new ObservableCollection(tmpPlaylists); - }); - - /* - if (Application.Current == null) { return Task.FromResult(false); } - Application.Current.Dispatcher.Invoke(() => - { - Playlists.Clear(); - - // Tmp list for sorting. - //List slTmp = new List(); - - Playlist pl = null; - - foreach (string value in resultLines) - { - if (value.StartsWith("playlist:")) - { - - pl = new Playlist - { - Name = value.Replace("playlist: ", "") - }; - - Playlists.Add(pl); - } - else if (value.StartsWith("Last-Modified: ")) - { - if (pl != null) - pl.LastModified = value.Replace("Last-Modified: ", ""); - } - else if (value.StartsWith("OK")) - { - // Ignoring. - } - } - }); - */ - } - catch (Exception e) - { - Debug.WriteLine("Error@ParsePlaylists: " + e.ToString()); - - IsBusy?.Invoke(this, false); - return Task.FromResult(false); - } - finally - { - IsBusy?.Invoke(this, false); - } - - return Task.FromResult(true); - } - - private Task ParseListAll(string result) - { - if (MpdStop) return Task.FromResult(false); - - if (string.IsNullOrEmpty(result)) return Task.FromResult(false); - - List resultLines = result.Split('\n').ToList(); - - if (resultLines.Count == 0) return Task.FromResult(false); - - try - { - IsBusy?.Invoke(this, true); - - if (Application.Current == null) { return Task.FromResult(false); } - Application.Current.Dispatcher.Invoke(() => - { - LocalFiles.Clear(); - LocalDirectories.Clear(); - }); - - SongFile song = null; - - int i = 0; - - foreach (string value in resultLines) - { - if (value.StartsWith("directory:")) - { - if (Application.Current == null) { return Task.FromResult(false); } - Application.Current.Dispatcher.Invoke(() => - { - LocalDirectories.Add(value.Replace("directory: ", "")); - }); - - //LocalDirectories.Add(value.Replace("directory: ", "")); - - MpcProgress?.Invoke(this, string.Format("[Background] Parsing files and directories ({0})...", i)); - } - else if (value.StartsWith("file:")) - { - song = new SongFile - { - File = value.Replace("file: ", "") - }; - - if (Application.Current == null) { return Task.FromResult(false); } - Application.Current.Dispatcher.Invoke(() => - { - LocalFiles.Add(song); - }); - - //LocalFiles.Add(song); - - i++; - - MpcProgress?.Invoke(this, string.Format("[Background] Parsing files and directories ({0})...", i)); - } - else if ((value.StartsWith("OK"))) - { - // Ignoring. - } - else - { - //Debug.WriteLine(value); - } - } - - MpcProgress?.Invoke(this, "[Background] Parsing files and directories is done."); - - } - catch (Exception e) - { - Debug.WriteLine("Error@ParseListAll: " + e.ToString()); - - IsBusy?.Invoke(this, false); - return Task.FromResult(false); ; - } - finally - { - IsBusy?.Invoke(this, false); - } - - return Task.FromResult(true); ; - } - - private Task ParseSearchResult(string result) - { - if (MpdStop) return Task.FromResult(false); - - if (Application.Current == null) { return Task.FromResult(false); } - Application.Current.Dispatcher.Invoke(() => - { - SearchResult.Clear(); - }); - - if (string.IsNullOrEmpty(result)) return Task.FromResult(false); - - //if (result.Trim() == "OK") return true; - - List resultLines = result.Split('\n').ToList(); - if (resultLines == null) return Task.FromResult(true); - if (resultLines.Count == 0) return Task.FromResult(true); - - - - // "listplaylist" .. songs containd in a playlist. - /* - file: Nina Simone/Cellular Soundtrack/Sinnerman (remix).mp3 - Last-Modified: 2020-09-07T22:56:19Z - Artist: Nina Simone - Album: Cellular Soundtrack - Title: Sinnerman (remix) - Genre: Soundtrack - Time: 274 - duration: 274.364 - */ - - // "find" search result - /* - file: 2Pac/Better Dayz/17 Better Dayz (Feat. Mr. Biggs).mp3 - Last-Modified: 2011-02-27T14:20:18Z - Format: 44100:f:2 - Time: 258 - duration: 257.677 - Artist: 2Pac - Album: Better Dayz - Title: Better Dayz (Feat. Mr. Biggs) - Track: 17 - Genre: Rap - Date: 2002 - */ - - try - { - IsBusy?.Invoke(this, true); - - var comparer = StringComparer.OrdinalIgnoreCase; - Dictionary SongValues = new(comparer); - - int i = 0; - - foreach (string line in resultLines) - { - string[] ValuePair = line.Split(':'); - if (ValuePair.Length > 1) - { - if (SongValues.ContainsKey(ValuePair[0].Trim())) - { - // Contains means new one. - - // save old one and clear songvalues. - if (SongValues.ContainsKey("file")) - { - SongInfo sng = FillSongInfo(SongValues, i); - - i++; - - SongValues.Clear(); - - if (Application.Current == null) { return Task.FromResult(false);} - Application.Current.Dispatcher.Invoke(() => - { - SearchResult.Add(sng); - }); - - MpcProgress?.Invoke(this, string.Format("[Background] Parsing search result item... ({0})", i)); - } - - // start over - //SongValues.Add(ValuePair[0].Trim(), line.Replace(ValuePair[0].Trim() + ": ", "")); - } - /* - else - { - SongValues.Add(ValuePair[0].Trim(), line.Replace(ValuePair[0].Trim() + ": ", "")); - } - */ - if (!SongValues.ContainsKey(ValuePair[0].Trim())) - { - SongValues.Add(ValuePair[0].Trim(), line.Replace(ValuePair[0].Trim() + ": ", "")); - } - else - { - if (ValuePair[0].Trim() == "Genre") - { - SongValues["Genre"] = SongValues["Genre"] + "/" + line.Replace(ValuePair[0].Trim() + ": ", ""); - } - } - } - } - - // last one - if ((SongValues.Count > 0) && SongValues.ContainsKey("file")) - { - SongInfo sng = FillSongInfo(SongValues, i); - - SongValues.Clear(); - - if (Application.Current == null) { return Task.FromResult(false);} - Application.Current.Dispatcher.Invoke(() => - { - SearchResult.Add(sng); - }); - - MpcProgress?.Invoke(this, string.Format("[Background] Parsing search result item... ({0})", i+1)); - } - - } - catch (Exception ex) - { - Debug.WriteLine("Error@ParseSearchResult: " + ex.Message); - - IsBusy?.Invoke(this, false); - return Task.FromResult(false); - } - finally - { - IsBusy?.Invoke(this, false); - - MpcProgress?.Invoke(this, "[Background] Search result loaded."); - } - - return Task.FromResult(true); - } - - private ObservableCollection ParsePlaylistSongsResult(string result) - { - ObservableCollection songList = new(); - - if (MpdStop) return songList; - - if (string.IsNullOrEmpty(result)) return songList; - - if (result.Trim() == "OK") return songList; - - List resultLines = result.Split('\n').ToList(); - if (resultLines == null) return songList; - if (resultLines.Count == 0) return songList; - - - // "listplaylist" .. songs containd in a playlist. - /* - file: Nina Simone/Cellular Soundtrack/Sinnerman (remix).mp3 - Last-Modified: 2020-09-07T22:56:19Z - Artist: Nina Simone - Album: Cellular Soundtrack - Title: Sinnerman (remix) - Genre: Soundtrack - Time: 274 - duration: 274.364 - */ - - // "find" search result - /* - file: 2Pac/Better Dayz/17 Better Dayz (Feat. Mr. Biggs).mp3 - Last-Modified: 2011-02-27T14:20:18Z - Format: 44100:f:2 - Time: 258 - duration: 257.677 - Artist: 2Pac - Album: Better Dayz - Title: Better Dayz (Feat. Mr. Biggs) - Track: 17 - Genre: Rap - Date: 2002 - */ - - try - { - var comparer = StringComparer.OrdinalIgnoreCase; - Dictionary SongValues = new(comparer); - - int i = 0; - - foreach (string line in resultLines) - { - string[] ValuePair = line.Split(':'); - if (ValuePair.Length > 1) - { - if (SongValues.ContainsKey(ValuePair[0].Trim())) - { - // Contains means new one. - - // save old one and clear songvalues. - if (SongValues.ContainsKey("file")) - { - SongInfo sng = FillSongInfo(SongValues, i); - - i++; - - SongValues.Clear(); - - //if (Application.Current == null) { return songList; } - //Application.Current.Dispatcher.Invoke(() => - //{ - songList.Add(sng); - //}); - - MpcProgress?.Invoke(this, string.Format("[Background] Parsing playlist item... ({0})", i)); - } - - // start over - //SongValues.Add(ValuePair[0].Trim(), line.Replace(ValuePair[0].Trim() + ": ", "")); - } - /* - else - { - SongValues.Add(ValuePair[0].Trim(), line.Replace(ValuePair[0].Trim() + ": ", "")); - } - */ - if (!SongValues.ContainsKey(ValuePair[0].Trim())) - { - SongValues.Add(ValuePair[0].Trim(), line.Replace(ValuePair[0].Trim() + ": ", "")); - } - else - { - if (ValuePair[0].Trim() == "Genre") - { - SongValues["Genre"] = SongValues["Genre"] + "/" + line.Replace(ValuePair[0].Trim() + ": ", ""); - } - } - } - } - - // last one - if ((SongValues.Count > 0) && SongValues.ContainsKey("file")) - { - SongInfo sng = FillSongInfo(SongValues, i); - - SongValues.Clear(); - - //if (Application.Current == null) { return songList; } - //Application.Current.Dispatcher.Invoke(() => - //{ - songList.Add(sng); - //}); - MpcProgress?.Invoke(this, string.Format("[Background] Parsing playlist item... ({0})", i+1)); - } - - } - catch (Exception ex) - { - Debug.WriteLine("Error@ParsePlaylistSongsResult: " + ex.Message); - - return songList; - } - - return songList; - } - - private static SongInfo FillSongInfo(Dictionary SongValues, int i) - { - - SongInfo sng = new(); - - if (SongValues.ContainsKey("Title")) - { - sng.Title = SongValues["Title"]; - } - else - { - sng.Title = ""; - if (SongValues.ContainsKey("file")) - { - sng.Title = Path.GetFileName(SongValues["file"]); - } - } - - if (SongValues.ContainsKey("Artist")) - { - sng.Artist = SongValues["Artist"]; - } - else - { - if (SongValues.ContainsKey("AlbumArtist")) - { - // TODO: Should I? - sng.Artist = SongValues["AlbumArtist"]; - } - else - { - sng.Artist = ""; - } - } - - if (SongValues.ContainsKey("Last-Modified")) - { - sng.LastModified = SongValues["Last-Modified"]; - } - - if (SongValues.ContainsKey("AlbumArtist")) - { - sng.AlbumArtist = SongValues["AlbumArtist"]; - } - - if (SongValues.ContainsKey("Album")) - { - sng.Album = SongValues["Album"]; - } - - if (SongValues.ContainsKey("Track")) - { - sng.Track = SongValues["Track"]; - } - - if (SongValues.ContainsKey("Disc")) - { - sng.Disc = SongValues["Disc"]; - } - - if (SongValues.ContainsKey("Date")) - { - sng.Date = SongValues["Date"]; - } - - if (SongValues.ContainsKey("Genre")) - { - sng.Genre = SongValues["Genre"]; - } - - if (SongValues.ContainsKey("Composer")) - { - sng.Composer = SongValues["Composer"]; - } - - if (SongValues.ContainsKey("Time")) - { - sng.Time = SongValues["Time"]; - } - - if (SongValues.ContainsKey("duration")) - { - sng.Time = SongValues["duration"]; - sng.Duration = SongValues["duration"]; - } - - if (SongValues.ContainsKey("file")) - { - sng.File = SongValues["file"]; - } - - // for sorting. (and playlist pos) - sng.Index = i; - - return sng; - } - - #endregion - - public void MpdDisconnect() - { - try - { - IsBusy?.Invoke(this, true); - - ConnectionState = ConnectionStatus.Disconnecting; - - if (_commandConnection.Client != null) - _commandConnection.Client.Shutdown(SocketShutdown.Both); - _commandConnection.Close(); - } - catch { } - finally - { - IsBusy?.Invoke(this, false); - ConnectionState = ConnectionStatus.DisconnectedByUser; - } - - try - { - IsBusy?.Invoke(this, true); - - ConnectionState = ConnectionStatus.Disconnecting; - - if (_idleConnection.Client != null) - _idleConnection.Client.Shutdown(SocketShutdown.Both); - _idleConnection.Close(); - } - catch { } - finally - { - IsBusy?.Invoke(this, false); - ConnectionState = ConnectionStatus.DisconnectedByUser; - } - - } - - } -} - diff --git a/Views/MainWindow.xaml.cs b/Views/MainWindow.xaml.cs index e171ba3..ccd0cdb 100644 --- a/Views/MainWindow.xaml.cs +++ b/Views/MainWindow.xaml.cs @@ -4,7 +4,6 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Threading; using System.Windows.Interop; -using System.Windows.Controls.Primitives; using System.Windows.Input; namespace unison @@ -50,6 +49,11 @@ namespace unison border.BorderThickness = BaseThickness; } + public string FormatSeconds(int time) + { + TimeSpan timespan = TimeSpan.FromSeconds(time); + return timespan.ToString(@"mm\:ss"); + } public string FormatSeconds(double time) { TimeSpan timespan = TimeSpan.FromSeconds(time); @@ -58,23 +62,22 @@ namespace unison public void UpdateInterface() { - if (mpd.GetCurrentSong() != null && mpd.GetStatus() != null) - { - SongTitle.Text = mpd.GetCurrentSong().Title; - SongTitle.ToolTip = mpd.GetCurrentSong().File; - SongArtist.Text = mpd.GetCurrentSong().Artist; - SongAlbum.Text = mpd.GetCurrentSong().Album; - if (mpd.GetCurrentSong().Date.Length > 0) - SongAlbum.Text += $" ({ mpd.GetCurrentSong().Date})"; - Bitrate.Text = mpd.GetCurrentSong().File.Substring(mpd.GetCurrentSong().File.LastIndexOf(".") + 1) + " – "; - Bitrate.Text += mpd.GetStatus().MpdBitrate + "kbps"; + if (mpd.GetCurrentSong() == null || mpd.GetStatus() == null) + return; - CurrentTime.Text = FormatSeconds(mpd._elapsed); - EndTime.Text = FormatSeconds(mpd.GetStatus().MpdSongTime); + SongTitle.Text = mpd.GetCurrentSong().Title; + SongTitle.ToolTip = mpd.GetCurrentSong().Path; + SongArtist.Text = mpd.GetCurrentSong().Artist; + SongAlbum.Text = mpd.GetCurrentSong().Album; + if (mpd.GetCurrentSong().Date != null) + SongAlbum.Text += $" ({ mpd.GetCurrentSong().Date})"; + Bitrate.Text = mpd.GetCurrentSong().Path.Substring(mpd.GetCurrentSong().Path.LastIndexOf(".") + 1) + " – "; + Bitrate.Text += mpd.GetStatus().Bitrate + "kbps"; - if (!System.Double.IsNaN(mpd.GetCurrentSong().TimeSort)) - TimeSlider.Value = mpd._elapsed / mpd.GetCurrentSong().TimeSort * 100; - } + CurrentTime.Text = FormatSeconds(mpd._currentTime); + EndTime.Text = FormatSeconds(mpd.GetCurrentSong().Time); + + TimeSlider.Value = mpd._currentTime / mpd.GetCurrentSong().Time * 100; if (VolumeSlider.Value != mpd._currentVolume) { @@ -87,12 +90,6 @@ namespace unison else PlayPause.Text = "\xedb5"; - SnapcastHandler snapcast = (SnapcastHandler)Application.Current.Properties["snapcast"]; - if (snapcast.Started) - SnapcastText.Text = "Stop Snapcast"; - else - SnapcastText.Text = "Start Snapcast"; - Connection.Text = (mpd._connected ? "✔️" : "❌") + $"{Properties.Settings.Default.mpd_host}:{Properties.Settings.Default.mpd_port}"; UpdateButton(ref BorderRandom, mpd._currentRandom); @@ -100,21 +97,23 @@ namespace unison UpdateButton(ref BorderSingle, mpd._currentSingle); UpdateButton(ref BorderConsume, mpd._currentConsume); - if (mpd.GetCover() != null) + if (mpd.GetCover() == null) { - if ((!mpd.GetCover().IsDownloading) && mpd.GetCover().IsSuccess) - { - if (mpd.GetCurrentSong().File == mpd.GetCover().SongFilePath) - { - Cover.Source = mpd.GetCover().AlbumImageSource; - Cover.Visibility = Visibility.Visible; - NoCover.Visibility = Visibility.Collapsed; - return; - } - } + NoCover.Visibility = Visibility.Visible; + Cover.Visibility = Visibility.Collapsed; } - NoCover.Visibility = Visibility.Visible; - Cover.Visibility = Visibility.Collapsed; + else if (Cover.Source != mpd.GetCover()) + { + Cover.Source = mpd.GetCover(); + Cover.Visibility = Visibility.Visible; + NoCover.Visibility = Visibility.Collapsed; + } + + SnapcastHandler snapcast = (SnapcastHandler)Application.Current.Properties["snapcast"]; + if (snapcast.Started) + SnapcastText.Text = "Stop Snapcast"; + else + SnapcastText.Text = "Start Snapcast"; } public void Pause_Clicked(object sender, RoutedEventArgs e) => mpd.PlayPause(); @@ -157,10 +156,10 @@ namespace unison Slider slider = (Slider)sender; double SongPercentage = slider.Value; - double SongTime = mpd.GetStatus().MpdSongTime; + double SongTime = mpd._totalTime; double SeekTime = SongPercentage / 100 * SongTime; - mpd.SetTime((int)SeekTime); + mpd.SetTime(SeekTime); } private void VolumeSlider_DragCompleted(object sender, MouseButtonEventArgs e) diff --git a/Views/Settings.xaml b/Views/Settings.xaml index c038284..dbde6c3 100644 --- a/Views/Settings.xaml +++ b/Views/Settings.xaml @@ -133,7 +133,7 @@ unison is free software. It is built with the following technologies: - ※ MPDCtrl + ※ Stylophone's updated MpcNETwpf-notifyiconEmoji.WPF diff --git a/Views/Settings.xaml.cs b/Views/Settings.xaml.cs index 0caabbd..a8d564c 100644 --- a/Views/Settings.xaml.cs +++ b/Views/Settings.xaml.cs @@ -81,7 +81,7 @@ namespace unison { SaveSettings(); MPDHandler mpd = (MPDHandler)Application.Current.Properties["mpd"]; - mpd.Start(); + // connect to mpd UpdateConnectionStatus(); } diff --git a/unison.csproj b/unison.csproj index 3ea2955..914883b 100644 --- a/unison.csproj +++ b/unison.csproj @@ -76,6 +76,10 @@ + + + + True diff --git a/unison.sln b/unison.sln index 56338a3..8c12821 100644 --- a/unison.sln +++ b/unison.sln @@ -3,18 +3,37 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.31515.178 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "unison", "unison.csproj", "{489048C4-3FCA-4573-B34C-943D03F94D04}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "unison", "unison.csproj", "{489048C4-3FCA-4573-B34C-943D03F94D04}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MpcNET", "..\MpcNET\MpcNET.csproj", "{AD425CBC-9C18-4B4A-AB51-4DE8F50FB6A9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MpcNET.Test", "..\MpcNET.Test\MpcNET.Test.csproj", "{6789E959-2DB2-412E-8CC1-C1564068F03D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU + Release-Stable|Any CPU = Release-Stable|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {489048C4-3FCA-4573-B34C-943D03F94D04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {489048C4-3FCA-4573-B34C-943D03F94D04}.Debug|Any CPU.Build.0 = Debug|Any CPU {489048C4-3FCA-4573-B34C-943D03F94D04}.Release|Any CPU.ActiveCfg = Release|Any CPU {489048C4-3FCA-4573-B34C-943D03F94D04}.Release|Any CPU.Build.0 = Release|Any CPU + {489048C4-3FCA-4573-B34C-943D03F94D04}.Release-Stable|Any CPU.ActiveCfg = Release|Any CPU + {489048C4-3FCA-4573-B34C-943D03F94D04}.Release-Stable|Any CPU.Build.0 = Release|Any CPU + {AD425CBC-9C18-4B4A-AB51-4DE8F50FB6A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD425CBC-9C18-4B4A-AB51-4DE8F50FB6A9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AD425CBC-9C18-4B4A-AB51-4DE8F50FB6A9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD425CBC-9C18-4B4A-AB51-4DE8F50FB6A9}.Release|Any CPU.Build.0 = Release|Any CPU + {AD425CBC-9C18-4B4A-AB51-4DE8F50FB6A9}.Release-Stable|Any CPU.ActiveCfg = Release-Stable|Any CPU + {AD425CBC-9C18-4B4A-AB51-4DE8F50FB6A9}.Release-Stable|Any CPU.Build.0 = Release-Stable|Any CPU + {6789E959-2DB2-412E-8CC1-C1564068F03D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6789E959-2DB2-412E-8CC1-C1564068F03D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6789E959-2DB2-412E-8CC1-C1564068F03D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6789E959-2DB2-412E-8CC1-C1564068F03D}.Release|Any CPU.Build.0 = Release|Any CPU + {6789E959-2DB2-412E-8CC1-C1564068F03D}.Release-Stable|Any CPU.ActiveCfg = Release|Any CPU + {6789E959-2DB2-412E-8CC1-C1564068F03D}.Release-Stable|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE