3663 lines
126 KiB
C#
3663 lines
126 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// MPD client class.
|
|
/// </summary>
|
|
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<SongInfoEx> _queue = new();
|
|
public ObservableCollection<SongInfoEx> CurrentQueue
|
|
{
|
|
get { return _queue; }
|
|
}
|
|
|
|
private ObservableCollection<Playlist> _playlists = new();
|
|
public ObservableCollection<Playlist> Playlists
|
|
{
|
|
get { return _playlists; }
|
|
}
|
|
|
|
private readonly ObservableCollection<SongFile> _localFiles = new();
|
|
public ObservableCollection<SongFile> LocalFiles
|
|
{
|
|
get { return _localFiles; }
|
|
}
|
|
|
|
private readonly ObservableCollection<String> _localDirectories = new();
|
|
public ObservableCollection<String> LocalDirectories
|
|
{
|
|
get { return _localDirectories; }
|
|
}
|
|
|
|
private readonly ObservableCollection<SongInfo> _searchResult = new();
|
|
public ObservableCollection<SongInfo> 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<bool> 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<ConnectionResult> 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<CommandResult> 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<CommandResult> 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<CommandResult> 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<CommandResult> 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<CommandResult> 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<CommandResult> 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<CommandResult> 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<bool> 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<string> 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<bool> 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<ConnectionResult> 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<CommandResult> 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<CommandResult> 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<CommandResult> MpdSendIdle()
|
|
{
|
|
return await MpdSendCommand("idle player");
|
|
}
|
|
|
|
public async Task<CommandResult> MpdSendNoIdle()
|
|
{
|
|
return await MpdSendCommand("noidle");
|
|
}
|
|
|
|
public async Task<CommandResult> MpdQueryStatus(bool autoIdling = true)
|
|
{
|
|
CommandResult result = await MpdSendCommand("status", autoIdling);
|
|
if (result.IsSuccess)
|
|
{
|
|
result.IsSuccess = await ParseStatus(result.ResultText);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<CommandResult> MpdQueryCurrentSong(bool autoIdling = true)
|
|
{
|
|
CommandResult result = await MpdSendCommand("currentsong", autoIdling);
|
|
if (result.IsSuccess)
|
|
{
|
|
result.IsSuccess = await ParseCurrentSong(result.ResultText);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<CommandResult> MpdQueryCurrentQueue(bool autoIdling = true)
|
|
{
|
|
CommandResult result = await MpdSendCommand("playlistinfo", autoIdling);
|
|
if (result.IsSuccess)
|
|
{
|
|
result.IsSuccess = await ParsePlaylistInfo(result.ResultText);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<CommandResult> MpdQueryPlaylists(bool autoIdling = true)
|
|
{
|
|
CommandResult result = await MpdSendCommand("listplaylists", autoIdling);
|
|
if (result.IsSuccess)
|
|
{
|
|
result.IsSuccess = await ParsePlaylists(result.ResultText);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<CommandResult> 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<CommandSearchResult> 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<CommandPlaylistResult> 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<CommandResult> 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<CommandResult> MpdSendUpdate()
|
|
{
|
|
CommandResult result = await MpdSendCommand("update", true);
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<CommandResult> 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<CommandResult> MpdPlaybackPause()
|
|
{
|
|
CommandResult result = await MpdSendCommand("pause 1", true);
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<CommandResult> 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<CommandResult> MpdPlaybackStop()
|
|
{
|
|
CommandResult result = await MpdSendCommand("stop", true);
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<CommandResult> 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<CommandResult> 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<CommandResult> 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<CommandResult> 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<CommandResult> 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<CommandResult> 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<CommandResult> 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<CommandResult> 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<CommandResult> MpdClear()
|
|
{
|
|
CommandResult result = await MpdSendCommand("clear", true);
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<CommandResult> 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<CommandResult> 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<CommandResult> MpdAdd(List<string> 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<CommandResult> MpdDeleteId(List<string> 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<CommandResult> 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<CommandResult> MpdMoveId(Dictionary<string, string> 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<string, string> 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<CommandResult> 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<CommandResult> 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<CommandResult> 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<CommandResult> 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<CommandResult> MpdPlaylistAdd(string playlistName, List<string> 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<CommandResult> 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<CommandResult> MpdPlaylistDelete(string playlistName, List<int> 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<CommandResult> 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<bool> 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<string> resultLines = result.Split('\n').ToList();
|
|
|
|
if (resultLines.Count == 0) return Task.FromResult(false);
|
|
|
|
var comparer = StringComparer.OrdinalIgnoreCase;
|
|
Dictionary<string, string> 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<bool> 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<string> 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<string, string> 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<bool> 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<string> 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<SongInfoEx> tmpQueue = new();
|
|
/*
|
|
if (Application.Current == null) { return Task.FromResult(false); }
|
|
Application.Current.Dispatcher.Invoke(() =>
|
|
{
|
|
CurrentQueue.Clear();
|
|
});
|
|
*/
|
|
|
|
var comparer = StringComparer.OrdinalIgnoreCase;
|
|
Dictionary<string, string> 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<SongInfoEx>(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<string, string> 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.<Oppes currentsong does not return pos..
|
|
if (string.IsNullOrEmpty(sng.Pos))
|
|
{
|
|
int tmpi = -1;
|
|
try
|
|
{
|
|
tmpi = Int32.Parse(sng.Pos);
|
|
sng.Index = tmpi;
|
|
}
|
|
catch{}
|
|
}
|
|
}
|
|
*/
|
|
|
|
//
|
|
if (sng.Id == _status.MpdSongID)
|
|
{
|
|
_currentSong = sng;
|
|
}
|
|
|
|
return sng;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.WriteLine("Error@FillSongInfoEx: " + e.ToString());
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private Task<bool> ParsePlaylists(string result)
|
|
{
|
|
bool isEmptyResult = false;
|
|
|
|
if (string.IsNullOrEmpty(result))
|
|
isEmptyResult = true;
|
|
|
|
List<string> 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<Playlist> 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<Playlist>(tmpPlaylists);
|
|
});
|
|
|
|
/*
|
|
if (Application.Current == null) { return Task.FromResult(false); }
|
|
Application.Current.Dispatcher.Invoke(() =>
|
|
{
|
|
Playlists.Clear();
|
|
|
|
// Tmp list for sorting.
|
|
//List<string> slTmp = new List<string>();
|
|
|
|
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<bool> ParseListAll(string result)
|
|
{
|
|
if (MpdStop) return Task.FromResult(false);
|
|
|
|
if (string.IsNullOrEmpty(result)) return Task.FromResult(false);
|
|
|
|
List<string> 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<bool> 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<string> 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<string, string> 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<SongInfo> ParsePlaylistSongsResult(string result)
|
|
{
|
|
ObservableCollection<SongInfo> songList = new();
|
|
|
|
if (MpdStop) return songList;
|
|
|
|
if (string.IsNullOrEmpty(result)) return songList;
|
|
|
|
if (result.Trim() == "OK") return songList;
|
|
|
|
List<string> 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<string, string> 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<string, string> 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;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|