First implementation of MpcNET
Exceptions thrown for playback to investigate, but get data is okay
This commit is contained in:
parent
765cb4f4ea
commit
80d6395c8e
@ -27,8 +27,6 @@ namespace unison
|
|||||||
|
|
||||||
Systray = (TaskbarIcon)FindResource("SystrayTaskbar");
|
Systray = (TaskbarIcon)FindResource("SystrayTaskbar");
|
||||||
Current.Properties["systray"] = Systray;
|
Current.Properties["systray"] = Systray;
|
||||||
|
|
||||||
MPD.Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnExit(ExitEventArgs e)
|
protected override void OnExit(ExitEventArgs e)
|
||||||
|
@ -1,37 +1,45 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows.Media.Imaging;
|
||||||
using System.Windows.Threading;
|
using MpcNET;
|
||||||
using MPDCtrl.Models;
|
using MpcNET.Commands.Database;
|
||||||
|
using MpcNET.Commands.Playback;
|
||||||
|
using MpcNET.Commands.Reflection;
|
||||||
|
using MpcNET.Commands.Status;
|
||||||
|
|
||||||
namespace unison
|
namespace unison
|
||||||
{
|
{
|
||||||
public class MPDHandler
|
public class MPDHandler
|
||||||
{
|
{
|
||||||
private readonly MPC _mpd = new();
|
|
||||||
|
|
||||||
public bool _connected;
|
public bool _connected;
|
||||||
|
public string _version;
|
||||||
public int _currentVolume;
|
public int _currentVolume;
|
||||||
public bool _currentRandom;
|
public bool _currentRandom;
|
||||||
public bool _currentRepeat;
|
public bool _currentRepeat;
|
||||||
public bool _currentSingle;
|
public bool _currentSingle;
|
||||||
public bool _currentConsume;
|
public bool _currentConsume;
|
||||||
public double _currentElapsed;
|
public double _currentTime;
|
||||||
|
public double _totalTime;
|
||||||
|
|
||||||
private Status _currentStatus = null;
|
BitmapFrame _cover;
|
||||||
private SongInfoEx _currentSong = null;
|
|
||||||
private AlbumImage _currentAlbumCover = null;
|
|
||||||
|
|
||||||
public double _elapsed;
|
public static MpdStatus BOGUS_STATUS = new MpdStatus(0, false, false, false, false, -1, -1, -1, MpdState.Unknown, -1, -1, -1, -1, TimeSpan.Zero, TimeSpan.Zero, -1, -1, -1, -1, -1, "", "");
|
||||||
private double _time;
|
public MpdStatus CurrentStatus { get; private set; } = BOGUS_STATUS;
|
||||||
|
|
||||||
|
MpcNET.Types.IMpdFile CurrentSong { get; set; }
|
||||||
|
|
||||||
private readonly System.Timers.Timer _elapsedTimer;
|
private readonly System.Timers.Timer _elapsedTimer;
|
||||||
private async void ElapsedTimer(object sender, System.Timers.ElapsedEventArgs e)
|
private async void ElapsedTimer(object sender, System.Timers.ElapsedEventArgs e)
|
||||||
{
|
{
|
||||||
if ((_elapsed < _time) && (_mpd.MpdStatus.MpdState == Status.MpdPlayState.Play))
|
if ((_currentTime < _totalTime) && (CurrentStatus.State == MpdState.Play))
|
||||||
{
|
{
|
||||||
_elapsed += 0.5;
|
_currentTime += 0.5;
|
||||||
await Task.Delay(5);
|
await Task.Delay(5);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -40,267 +48,297 @@ namespace unison
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsBusy = false;
|
private MpcConnection _connection;
|
||||||
|
private IPEndPoint _mpdEndpoint;
|
||||||
|
|
||||||
|
private CancellationTokenSource cancelToken;
|
||||||
|
|
||||||
public MPDHandler()
|
public MPDHandler()
|
||||||
{
|
{
|
||||||
_mpd.IsBusy += new MPC.IsBusyEvent(OnMpcIsBusy);
|
cancelToken = new CancellationTokenSource();
|
||||||
|
|
||||||
_mpd.MpdIdleConnected += new MPC.IsMpdIdleConnectedEvent(OnMpdIdleConnected);
|
Initialize();
|
||||||
|
|
||||||
_mpd.ConnectionStatusChanged += new MPC.ConnectionStatusChangedEvent(OnConnectionStatusChanged);
|
|
||||||
_mpd.ConnectionError += new MPC.ConnectionErrorEvent(OnConnectionError);
|
|
||||||
|
|
||||||
_mpd.MpdPlayerStatusChanged += new MPC.MpdPlayerStatusChangedEvent(OnMpdPlayerStatusChanged);
|
|
||||||
_mpd.MpdCurrentQueueChanged += new MPC.MpdCurrentQueueChangedEvent(OnMpdCurrentQueueChanged);
|
|
||||||
|
|
||||||
_mpd.MpdAlbumArtChanged += new MPC.MpdAlbumArtChangedEvent(OnAlbumArtChanged);
|
|
||||||
|
|
||||||
_elapsedTimer = new System.Timers.Timer(500);
|
_elapsedTimer = new System.Timers.Timer(500);
|
||||||
_elapsedTimer.Elapsed += new System.Timers.ElapsedEventHandler(ElapsedTimer);
|
_elapsedTimer.Elapsed += new System.Timers.ElapsedEventHandler(ElapsedTimer);
|
||||||
|
|
||||||
DispatcherTimer timer = new DispatcherTimer();
|
|
||||||
timer.Interval = TimeSpan.FromSeconds(2);
|
|
||||||
timer.Tick += QueryStatus;
|
|
||||||
timer.Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMpcIsBusy(MPC sender, bool on)
|
private async void Initialize()
|
||||||
{
|
{
|
||||||
IsBusy = on;
|
var token = cancelToken.Token;
|
||||||
}
|
_connection = await Connect(token);
|
||||||
|
if (_connection.IsConnected)
|
||||||
public void Start()
|
|
||||||
{
|
|
||||||
Task.Run(() => _mpd.MpdIdleConnect(Properties.Settings.Default.mpd_host, Properties.Settings.Default.mpd_port));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnMpdIdleConnected(MPC sender)
|
|
||||||
{
|
|
||||||
Trace.WriteLine($"Connection to mpd {_mpd.MpdVerText}...");
|
|
||||||
LoadInitialData();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnConnectionStatusChanged(MPC sender, MPC.ConnectionStatus status)
|
|
||||||
{
|
|
||||||
Trace.WriteLine("Connection changed...");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnConnectionError(MPC sender, string msg)
|
|
||||||
{
|
|
||||||
Trace.WriteLine("Connection ERROR!");
|
|
||||||
LoadInitialData();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnMpdPlayerStatusChanged(MPC sender)
|
|
||||||
{
|
|
||||||
Trace.WriteLine("Status changed...");
|
|
||||||
|
|
||||||
UpdateStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnMpdCurrentQueueChanged(MPC sender)
|
|
||||||
{
|
|
||||||
Trace.WriteLine("Queue changed...");
|
|
||||||
|
|
||||||
UpdateStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnAlbumArtChanged(MPC sender)
|
|
||||||
{
|
|
||||||
// AlbumArt
|
|
||||||
if (Application.Current == null) { return; }
|
|
||||||
Application.Current.Dispatcher.Invoke(() =>
|
|
||||||
{
|
{
|
||||||
if ((!_mpd.AlbumCover.IsDownloading) && _mpd.AlbumCover.IsSuccess)
|
_connected = true;
|
||||||
|
_version = _connection.Version;
|
||||||
|
}
|
||||||
|
Trace.WriteLine("is connected: " + _connected);
|
||||||
|
Trace.WriteLine("version: " + _version);
|
||||||
|
|
||||||
|
await UpdateStatusAsync();
|
||||||
|
await UpdateSongAsync();
|
||||||
|
|
||||||
|
Loop(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<MpcConnection> Connect(CancellationToken token)
|
||||||
|
{
|
||||||
|
IPAddress.TryParse(Properties.Settings.Default.mpd_host, out IPAddress ipAddress);
|
||||||
|
|
||||||
|
_mpdEndpoint = new IPEndPoint(ipAddress, Properties.Settings.Default.mpd_port);
|
||||||
|
MpcConnection connection = new MpcConnection(_mpdEndpoint);
|
||||||
|
await connection.ConnectAsync(token);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(Properties.Settings.Default.mpd_password))
|
||||||
|
{
|
||||||
|
MpcNET.Message.IMpdMessage<string> result = await connection.SendAsync(new PasswordCommand(Properties.Settings.Default.mpd_password));
|
||||||
|
if (!result.IsResponseValid)
|
||||||
{
|
{
|
||||||
if ((_mpd.MpdCurrentSong != null) && (_mpd.AlbumCover.AlbumImageSource != null))
|
string mpdError = result.Response?.Result?.MpdError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Loop(CancellationToken token)
|
||||||
|
{
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (!String.IsNullOrEmpty(_mpd.MpdCurrentSong.File))
|
if (token.IsCancellationRequested || _connection == null)
|
||||||
{
|
break;
|
||||||
if (_mpd.MpdCurrentSong.File == _mpd.AlbumCover.SongFilePath)
|
|
||||||
{
|
Trace.WriteLine("loop");
|
||||||
Trace.WriteLine("found cover");
|
var idleChanges = await _connection.SendAsync(new IdleCommand("stored_playlist playlist player mixer output options"));
|
||||||
_currentAlbumCover = _mpd.AlbumCover;
|
|
||||||
}
|
if (idleChanges.IsResponseValid)
|
||||||
}
|
await HandleIdleResponseAsync(idleChanges.Response.Content);
|
||||||
|
else
|
||||||
|
throw new Exception(idleChanges.Response?.Content);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"Error in Idle connection thread: {e.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
}).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void LoadInitialData()
|
private async Task HandleIdleResponseAsync(string subsystems)
|
||||||
{
|
{
|
||||||
//todo : test if the password works
|
if (subsystems.Contains("player") || subsystems.Contains("mixer") || subsystems.Contains("output") || subsystems.Contains("options"))
|
||||||
|
|
||||||
IsBusy = true;
|
|
||||||
await Task.Delay(5);
|
|
||||||
|
|
||||||
CommandResult result = await _mpd.MpdIdleSendPassword(Properties.Settings.Default.mpd_password);
|
|
||||||
|
|
||||||
if (result.IsSuccess)
|
|
||||||
{
|
{
|
||||||
_connected = await _mpd.MpdCommandConnectionStart(Properties.Settings.Default.mpd_host, Properties.Settings.Default.mpd_port, Properties.Settings.Default.mpd_password);
|
await UpdateStatusAsync();
|
||||||
if (_connected)
|
|
||||||
|
if (subsystems.Contains("player"))
|
||||||
{
|
{
|
||||||
await _mpd.MpdSendUpdate();
|
await UpdateSongAsync();
|
||||||
|
|
||||||
result = await _mpd.MpdIdleQueryStatus();
|
|
||||||
await Task.Delay(5);
|
|
||||||
|
|
||||||
if (result.IsSuccess)
|
|
||||||
{
|
|
||||||
_currentVolume = _mpd.MpdStatus.MpdVolume;
|
|
||||||
_currentRandom = _mpd.MpdStatus.MpdRandom;
|
|
||||||
_currentRepeat = _mpd.MpdStatus.MpdRepeat;
|
|
||||||
_currentSingle = _mpd.MpdStatus.MpdSingle;
|
|
||||||
_currentConsume = _mpd.MpdStatus.MpdConsume;
|
|
||||||
_currentElapsed = _mpd.MpdStatus.MpdSongElapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.Delay(5);
|
|
||||||
CommandResult song = await _mpd.MpdIdleQueryCurrentSong();
|
|
||||||
await Task.Delay(5);
|
|
||||||
if (song != null)
|
|
||||||
_currentSong = _mpd.MpdCurrentSong;
|
|
||||||
|
|
||||||
await Task.Delay(5);
|
|
||||||
_mpd.MpdIdleStart();
|
|
||||||
await Task.Delay(5);
|
|
||||||
UpdateStatus();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void QueryStatus(object sender, EventArgs e)
|
bool _isUpdatingStatus = false;
|
||||||
|
private async Task UpdateStatusAsync()
|
||||||
{
|
{
|
||||||
if (IsBusy)
|
if (_connection == null) return;
|
||||||
|
|
||||||
|
if (_isUpdatingStatus) return;
|
||||||
|
_isUpdatingStatus = true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
MpcNET.Message.IMpdMessage<MpdStatus> response = await _connection.SendAsync(new StatusCommand());
|
||||||
|
if (response != null && response.IsResponseValid)
|
||||||
|
{
|
||||||
|
CurrentStatus = response.Response.Content;
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
await Connect(cancelToken.Token);
|
||||||
|
}
|
||||||
|
_isUpdatingStatus = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UpdateSongAsync()
|
||||||
|
{
|
||||||
|
MpcNET.Message.IMpdMessage<MpcNET.Types.IMpdFile> response = await _connection.SendAsync(new CurrentSongCommand());
|
||||||
|
if (response != null && response.IsResponseValid)
|
||||||
|
{
|
||||||
|
CurrentSong = response.Response.Content;
|
||||||
|
UpdateSong();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<T> SafelySendCommandAsync<T>(IMpcCommand<T> command)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await _connection.SendAsync(command);
|
||||||
|
if (!response.IsResponseValid)
|
||||||
|
{
|
||||||
|
// If we have an MpdError string, only show that as the error to avoid extra noise
|
||||||
|
var mpdError = response.Response?.Result?.MpdError;
|
||||||
|
if (mpdError != null && mpdError != "")
|
||||||
|
throw new Exception(mpdError);
|
||||||
|
else
|
||||||
|
throw new Exception($"Invalid server response: {response}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Response.Content;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Trace.WriteLine($"Sending {command.GetType().Name} failed: {e.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void GetAlbumBitmap(string path, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
List<byte> data = new List<byte>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_connection == null) // We got cancelled
|
||||||
|
return;
|
||||||
|
|
||||||
|
long totalBinarySize = 9999;
|
||||||
|
long currentSize = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
var albumReq = await _connection.SendAsync(new AlbumArtCommand(path, currentSize));
|
||||||
|
if (!albumReq.IsResponseValid) break;
|
||||||
|
|
||||||
|
var response = albumReq.Response.Content;
|
||||||
|
if (response.Binary == 0) break; // MPD isn't giving us any more data, let's roll with what we have.
|
||||||
|
|
||||||
|
totalBinarySize = response.Size;
|
||||||
|
currentSize += response.Binary;
|
||||||
|
data.AddRange(response.Data);
|
||||||
|
Debug.WriteLine($"Downloading albumart: {currentSize}/{totalBinarySize}");
|
||||||
|
} while (currentSize < totalBinarySize && !token.IsCancellationRequested);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Exception caught while getting albumart: " + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.Count == 0)
|
||||||
|
{
|
||||||
|
Trace.WriteLine("empty cover");
|
||||||
|
_cover = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var stream = new MemoryStream(data.ToArray());
|
||||||
|
_cover = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||||
|
UpdateCover();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateSong()
|
||||||
|
{
|
||||||
|
if (!_connected)
|
||||||
|
return;
|
||||||
|
if (CurrentSong == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Trace.WriteLine("Querying status...");
|
Trace.WriteLine($"[DEBUG] Song: name[{CurrentSong.Title}] artist[{CurrentSong.Artist}] album[{CurrentSong.Album}] uri[{CurrentSong.Path}]");
|
||||||
|
|
||||||
CommandResult result = await _mpd.MpdQueryStatus();
|
_currentTime = CurrentStatus.Elapsed.TotalSeconds;
|
||||||
await Task.Delay(5);
|
_totalTime = CurrentSong.Time;
|
||||||
|
if (!_elapsedTimer.Enabled)
|
||||||
|
_elapsedTimer.Start();
|
||||||
|
|
||||||
if (result.IsSuccess)
|
string uri = Regex.Escape(CurrentSong.Path);
|
||||||
{
|
GetAlbumBitmap(uri);
|
||||||
result = await _mpd.MpdQueryCurrentSong();
|
|
||||||
await Task.Delay(5);
|
|
||||||
|
|
||||||
if (result.IsSuccess)
|
|
||||||
{
|
|
||||||
UpdateStatus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void UpdateStatus()
|
public void UpdateCover()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateStatus()
|
||||||
{
|
{
|
||||||
if (!_connected)
|
if (!_connected)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await Task.Delay(50);
|
if (CurrentStatus == null)
|
||||||
|
return;
|
||||||
|
|
||||||
_currentStatus = _mpd.MpdStatus;
|
_currentRandom = CurrentStatus.Random;
|
||||||
|
_currentRepeat = CurrentStatus.Repeat;
|
||||||
|
_currentConsume = CurrentStatus.Consume;
|
||||||
|
_currentSingle = CurrentStatus.Single;
|
||||||
|
_currentVolume = CurrentStatus.Volume;
|
||||||
|
|
||||||
_currentRandom = _mpd.MpdStatus.MpdRandom;
|
Trace.WriteLine($"[DEBUG] Status: volume[{CurrentStatus.Volume}] random[{CurrentStatus.Random}] consume[{CurrentStatus.Consume}] State[{CurrentStatus.State}] Bitrate[{CurrentStatus.Bitrate}]");
|
||||||
_currentRepeat = _mpd.MpdStatus.MpdRepeat;
|
|
||||||
_currentConsume = _mpd.MpdStatus.MpdConsume;
|
|
||||||
_currentSingle = _mpd.MpdStatus.MpdSingle;
|
|
||||||
|
|
||||||
_currentVolume = _mpd.MpdStatus.MpdVolume;
|
|
||||||
_currentElapsed = _mpd.MpdStatus.MpdSongElapsed;
|
|
||||||
|
|
||||||
_currentSong = _mpd.MpdCurrentSong;
|
|
||||||
|
|
||||||
_time = _mpd.MpdStatus.MpdSongTime;
|
|
||||||
|
|
||||||
_elapsed = _mpd.MpdStatus.MpdSongElapsed;
|
|
||||||
|
|
||||||
if (_mpd.MpdStatus.MpdState == Status.MpdPlayState.Play)
|
|
||||||
{
|
|
||||||
if (!_elapsedTimer.Enabled)
|
|
||||||
_elapsedTimer.Start();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_elapsedTimer.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_currentSong != null)
|
|
||||||
await _mpd.MpdQueryAlbumArt(_currentSong.File, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SongInfoEx GetCurrentSong() => _currentSong;
|
public MpcNET.Types.IMpdFile GetCurrentSong() => CurrentSong;
|
||||||
public Status GetStatus() => _currentStatus;
|
public MpdStatus GetStatus() => CurrentStatus;
|
||||||
public AlbumImage GetCover() => _currentAlbumCover;
|
public BitmapFrame GetCover() => _cover;
|
||||||
|
public string GetVersion() => _version;
|
||||||
|
|
||||||
public async void Prev()
|
public async void Prev()
|
||||||
{
|
{
|
||||||
if (!IsBusy)
|
await SafelySendCommandAsync(new PreviousCommand());
|
||||||
await _mpd.MpdPlaybackPrev(_currentVolume);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void Next()
|
public async void Next()
|
||||||
{
|
{
|
||||||
if (!IsBusy)
|
await SafelySendCommandAsync(new NextCommand());
|
||||||
await _mpd.MpdPlaybackNext(_currentVolume);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void PlayPause()
|
public async void PlayPause()
|
||||||
{
|
{
|
||||||
if (IsBusy)
|
await SafelySendCommandAsync(new PauseResumeCommand());
|
||||||
return;
|
|
||||||
if (_mpd.MpdStatus.MpdState == Status.MpdPlayState.Play)
|
|
||||||
await _mpd.MpdPlaybackPause();
|
|
||||||
else if (_mpd.MpdStatus.MpdState == Status.MpdPlayState.Pause)
|
|
||||||
await _mpd.MpdPlaybackPlay(_currentVolume);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void Random()
|
public async void Random()
|
||||||
{
|
{
|
||||||
if (!IsBusy)
|
await SafelySendCommandAsync(new RandomCommand(!_currentRandom));
|
||||||
await _mpd.MpdSetRandom(!_currentRandom);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void Repeat()
|
public async void Repeat()
|
||||||
{
|
{
|
||||||
if (!IsBusy)
|
await SafelySendCommandAsync(new RepeatCommand(!_currentRepeat));
|
||||||
await _mpd.MpdSetRepeat(!_currentRepeat);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void Single()
|
public async void Single()
|
||||||
{
|
{
|
||||||
if (!IsBusy)
|
await SafelySendCommandAsync(new SingleCommand(!_currentSingle));
|
||||||
await _mpd.MpdSetSingle(!_currentSingle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void Consume()
|
public async void Consume()
|
||||||
{
|
{
|
||||||
if (!IsBusy)
|
await SafelySendCommandAsync(new ConsumeCommand(!_currentConsume));
|
||||||
await _mpd.MpdSetConsume(!_currentConsume);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void SetVolume(int value)
|
public async void SetVolume(int value)
|
||||||
{
|
{
|
||||||
if (!IsBusy)
|
await SafelySendCommandAsync(new SetVolumeCommand((byte)value));
|
||||||
await _mpd.MpdSetVolume(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void SetTime(int value)
|
public async void SetTime(double value)
|
||||||
{
|
{
|
||||||
if (!IsBusy)
|
await SafelySendCommandAsync(new SeekCurCommand(value));
|
||||||
await _mpd.MpdPlaybackSeek(_mpd.MpdCurrentSong.Id, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsPlaying()
|
public bool IsPlaying()
|
||||||
{
|
{
|
||||||
return _currentStatus?.MpdState == MPDCtrl.Models.Status.MpdPlayState.Play;
|
return CurrentStatus?.State == MpdState.Play;
|
||||||
}
|
|
||||||
|
|
||||||
public string GetVersion()
|
|
||||||
{
|
|
||||||
return _mpd.MpdVerText;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,29 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Media;
|
|
||||||
using System.Windows.Media.Imaging;
|
|
||||||
|
|
||||||
namespace MPDCtrl.Models
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// AlbumCover class.
|
|
||||||
/// </summary>
|
|
||||||
public class AlbumImage
|
|
||||||
{
|
|
||||||
public bool IsDownloading { get; set; }
|
|
||||||
|
|
||||||
public bool IsSuccess { get; set; }
|
|
||||||
|
|
||||||
public string SongFilePath { get; set; }
|
|
||||||
|
|
||||||
public byte[] BinaryData { get; set; } = Array.Empty<byte>();
|
|
||||||
|
|
||||||
public int BinarySize { get; set; }
|
|
||||||
|
|
||||||
public ImageSource AlbumImageSource { get; set; }
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MPDCtrl.Models
|
|
||||||
{
|
|
||||||
public class Playlist
|
|
||||||
{
|
|
||||||
public string Name { get; set; } = "";
|
|
||||||
|
|
||||||
private string _lastModified;
|
|
||||||
public string LastModified
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _lastModified;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_lastModified == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_lastModified = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string LastModifiedFormated
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
DateTime _lastModifiedDateTime = default; //new DateTime(1998,04,30)
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(_lastModified))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_lastModifiedDateTime = DateTime.Parse(_lastModified, null, System.Globalization.DateTimeStyles.RoundtripKind);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine("Wrong LastModified timestamp format. " + _lastModified);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var culture = System.Globalization.CultureInfo.CurrentCulture;
|
|
||||||
return _lastModifiedDateTime.ToString(culture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MPDCtrl.Models
|
|
||||||
{
|
|
||||||
public class Result
|
|
||||||
{
|
|
||||||
public bool IsSuccess;
|
|
||||||
public string ErrorMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ConnectionResult: Result
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// generic
|
|
||||||
public class CommandResult : Result
|
|
||||||
{
|
|
||||||
public string ResultText;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CommandBinaryResult : Result
|
|
||||||
{
|
|
||||||
public int WholeSize;
|
|
||||||
public int ChunkSize;
|
|
||||||
public string Type;
|
|
||||||
public byte[] BinaryData;
|
|
||||||
}
|
|
||||||
|
|
||||||
// for commands that return playlist songs.
|
|
||||||
public class CommandPlaylistResult : CommandResult
|
|
||||||
{
|
|
||||||
public ObservableCollection<SongInfo> PlaylistSongs;
|
|
||||||
}
|
|
||||||
|
|
||||||
// for commands that return search result.
|
|
||||||
public class CommandSearchResult : CommandResult
|
|
||||||
{
|
|
||||||
public ObservableCollection<SongInfo> SearchResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Not used?
|
|
||||||
public class IdleResult : CommandResult
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,232 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
//using MPDCtrl.Common;
|
|
||||||
|
|
||||||
namespace MPDCtrl.Models
|
|
||||||
{
|
|
||||||
// SongFile > SongInfo > SongInfoEx
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generic song file class. (for listall)
|
|
||||||
/// </summary>
|
|
||||||
public class SongFile// : ViewModelBase
|
|
||||||
{
|
|
||||||
public string File { get; set; } = "";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// SongInfo class. (for playlist or search result)
|
|
||||||
/// </summary>
|
|
||||||
public class SongInfo : SongFile
|
|
||||||
{
|
|
||||||
public string Title { get; set; } = "";
|
|
||||||
public string Track { get; set; } = "";
|
|
||||||
public string Disc { get; set; } = "";
|
|
||||||
public string Time { get; set; } = "";
|
|
||||||
public string TimeFormated
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
string _timeFormatted = "";
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(Time))
|
|
||||||
{
|
|
||||||
int sec, min, hour, s;
|
|
||||||
|
|
||||||
double dtime = double.Parse(Time);
|
|
||||||
sec = Convert.ToInt32(dtime);
|
|
||||||
|
|
||||||
//sec = Int32.Parse(_time);
|
|
||||||
min = sec / 60;
|
|
||||||
s = sec % 60;
|
|
||||||
hour = min / 60;
|
|
||||||
min %= 60;
|
|
||||||
|
|
||||||
if ((hour == 0) && min == 0)
|
|
||||||
{
|
|
||||||
_timeFormatted = String.Format("{0}", s);
|
|
||||||
}
|
|
||||||
else if ((hour == 0) && (min != 0))
|
|
||||||
{
|
|
||||||
_timeFormatted = String.Format("{0}:{1:00}", min, s);
|
|
||||||
}
|
|
||||||
else if ((hour != 0) && (min != 0))
|
|
||||||
{
|
|
||||||
_timeFormatted = String.Format("{0}:{1:00}:{2:00}", hour, min, s);
|
|
||||||
}
|
|
||||||
else if (hour != 0)
|
|
||||||
{
|
|
||||||
_timeFormatted = String.Format("{0}:{1:00}:{2:00}", hour, min, s);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine("Oops@TimeFormated: " + Time + " : " + hour.ToString() + " " + min.ToString() + " " + s.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (FormatException e)
|
|
||||||
{
|
|
||||||
// Ignore.
|
|
||||||
// System.Diagnostics.Debug.WriteLine(e.Message);
|
|
||||||
System.Diagnostics.Debug.WriteLine("Wrong Time format. " + Time + " " + e.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _timeFormatted;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
public double TimeSort
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
double dtime = double.NaN;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (Time != "")
|
|
||||||
dtime = double.Parse(Time);
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
return dtime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public string Duration { get; set; } = "";
|
|
||||||
public string Artist { get; set; } = "";
|
|
||||||
public string Album { get; set; } = "";
|
|
||||||
public string AlbumArtist { get; set; } = "";
|
|
||||||
public string Composer { get; set; } = "";
|
|
||||||
public string Date { get; set; } = "";
|
|
||||||
public string Genre { get; set; } = "";
|
|
||||||
|
|
||||||
private string _lastModified;
|
|
||||||
public string LastModified
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _lastModified;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_lastModified == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_lastModified = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string LastModifiedFormated
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
DateTime _lastModifiedDateTime = default; //new DateTime(1998,04,30)
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(_lastModified))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_lastModifiedDateTime = DateTime.Parse(_lastModified, null, System.Globalization.DateTimeStyles.RoundtripKind);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine("Wrong LastModified timestamp format. " + _lastModified);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var culture = System.Globalization.CultureInfo.CurrentCulture;
|
|
||||||
return _lastModifiedDateTime.ToString(culture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// for sorting and (playlist pos)
|
|
||||||
private int _index;
|
|
||||||
public int Index
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _index;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_index == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_index = value;
|
|
||||||
//this.NotifyPropertyChanged(nameof(Index));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _isSelected;
|
|
||||||
public bool IsSelected
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _isSelected;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_isSelected == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_isSelected = value;
|
|
||||||
|
|
||||||
//NotifyPropertyChanged("IsSelected");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int IndexPlusOne
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _index+1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// song class with some extra info. (for queue)
|
|
||||||
/// </summary>
|
|
||||||
public class SongInfoEx : SongInfo
|
|
||||||
{
|
|
||||||
// Queue specific
|
|
||||||
|
|
||||||
public string Id { get; set; }
|
|
||||||
|
|
||||||
private string _pos;
|
|
||||||
public string Pos
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _pos;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_pos == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_pos = value;
|
|
||||||
//this.NotifyPropertyChanged(nameof(Pos));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _isPlaying;
|
|
||||||
public bool IsPlaying
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _isPlaying;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_isPlaying == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_isPlaying = value;
|
|
||||||
//this.NotifyPropertyChanged(nameof(IsPlaying));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,149 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MPDCtrl.Models
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// MPD "status" class. (for "status" command result)
|
|
||||||
/// </summary>
|
|
||||||
public class Status
|
|
||||||
{
|
|
||||||
public enum MpdPlayState
|
|
||||||
{
|
|
||||||
Play, Pause, Stop
|
|
||||||
};
|
|
||||||
|
|
||||||
private MpdPlayState _ps;
|
|
||||||
private string _bitrate;
|
|
||||||
private int _volume = 50;
|
|
||||||
private bool _volumeIsSet;
|
|
||||||
private bool _repeat;
|
|
||||||
private bool _random;
|
|
||||||
private bool _consume;
|
|
||||||
private bool _single;
|
|
||||||
private string _songID = "";
|
|
||||||
private double _songTime = 0;
|
|
||||||
private double _songElapsed = 0;
|
|
||||||
private string _error = "";
|
|
||||||
|
|
||||||
public MpdPlayState MpdState
|
|
||||||
{
|
|
||||||
get { return _ps; }
|
|
||||||
set { _ps = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public string MpdBitrate
|
|
||||||
{
|
|
||||||
get { return _bitrate; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_bitrate = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int MpdVolume
|
|
||||||
{
|
|
||||||
get { return _volume; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_volume = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool MpdVolumeIsSet
|
|
||||||
{
|
|
||||||
get { return _volumeIsSet; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_volumeIsSet = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool MpdRepeat
|
|
||||||
{
|
|
||||||
get { return _repeat; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_repeat = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool MpdRandom
|
|
||||||
{
|
|
||||||
get { return _random; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_random = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public bool MpdConsume
|
|
||||||
{
|
|
||||||
get { return _consume; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_consume = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool MpdSingle
|
|
||||||
{
|
|
||||||
get { return _single; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_single = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string MpdSongID
|
|
||||||
{
|
|
||||||
get { return _songID; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_songID = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public double MpdSongTime
|
|
||||||
{
|
|
||||||
get { return _songTime; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_songTime = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public double MpdSongElapsed
|
|
||||||
{
|
|
||||||
get { return _songElapsed; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_songElapsed = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public string MpdError
|
|
||||||
{
|
|
||||||
get { return _error; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_error = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
_volume = 50;
|
|
||||||
_volumeIsSet = false;
|
|
||||||
_repeat = false;
|
|
||||||
_random = false;
|
|
||||||
_consume = false;
|
|
||||||
_songID = "";
|
|
||||||
_songTime = 0;
|
|
||||||
_songElapsed = 0;
|
|
||||||
_error = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
3662
MPDCtrl/MPC.cs
3662
MPDCtrl/MPC.cs
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,6 @@ using System.Windows;
|
|||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
using System.Windows.Interop;
|
using System.Windows.Interop;
|
||||||
using System.Windows.Controls.Primitives;
|
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
|
||||||
namespace unison
|
namespace unison
|
||||||
@ -50,6 +49,11 @@ namespace unison
|
|||||||
border.BorderThickness = BaseThickness;
|
border.BorderThickness = BaseThickness;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string FormatSeconds(int time)
|
||||||
|
{
|
||||||
|
TimeSpan timespan = TimeSpan.FromSeconds(time);
|
||||||
|
return timespan.ToString(@"mm\:ss");
|
||||||
|
}
|
||||||
public string FormatSeconds(double time)
|
public string FormatSeconds(double time)
|
||||||
{
|
{
|
||||||
TimeSpan timespan = TimeSpan.FromSeconds(time);
|
TimeSpan timespan = TimeSpan.FromSeconds(time);
|
||||||
@ -58,23 +62,22 @@ namespace unison
|
|||||||
|
|
||||||
public void UpdateInterface()
|
public void UpdateInterface()
|
||||||
{
|
{
|
||||||
if (mpd.GetCurrentSong() != null && mpd.GetStatus() != null)
|
if (mpd.GetCurrentSong() == null || mpd.GetStatus() == null)
|
||||||
{
|
return;
|
||||||
SongTitle.Text = mpd.GetCurrentSong().Title;
|
|
||||||
SongTitle.ToolTip = mpd.GetCurrentSong().File;
|
|
||||||
SongArtist.Text = mpd.GetCurrentSong().Artist;
|
|
||||||
SongAlbum.Text = mpd.GetCurrentSong().Album;
|
|
||||||
if (mpd.GetCurrentSong().Date.Length > 0)
|
|
||||||
SongAlbum.Text += $" ({ mpd.GetCurrentSong().Date})";
|
|
||||||
Bitrate.Text = mpd.GetCurrentSong().File.Substring(mpd.GetCurrentSong().File.LastIndexOf(".") + 1) + " – ";
|
|
||||||
Bitrate.Text += mpd.GetStatus().MpdBitrate + "kbps";
|
|
||||||
|
|
||||||
CurrentTime.Text = FormatSeconds(mpd._elapsed);
|
SongTitle.Text = mpd.GetCurrentSong().Title;
|
||||||
EndTime.Text = FormatSeconds(mpd.GetStatus().MpdSongTime);
|
SongTitle.ToolTip = mpd.GetCurrentSong().Path;
|
||||||
|
SongArtist.Text = mpd.GetCurrentSong().Artist;
|
||||||
|
SongAlbum.Text = mpd.GetCurrentSong().Album;
|
||||||
|
if (mpd.GetCurrentSong().Date != null)
|
||||||
|
SongAlbum.Text += $" ({ mpd.GetCurrentSong().Date})";
|
||||||
|
Bitrate.Text = mpd.GetCurrentSong().Path.Substring(mpd.GetCurrentSong().Path.LastIndexOf(".") + 1) + " – ";
|
||||||
|
Bitrate.Text += mpd.GetStatus().Bitrate + "kbps";
|
||||||
|
|
||||||
if (!System.Double.IsNaN(mpd.GetCurrentSong().TimeSort))
|
CurrentTime.Text = FormatSeconds(mpd._currentTime);
|
||||||
TimeSlider.Value = mpd._elapsed / mpd.GetCurrentSong().TimeSort * 100;
|
EndTime.Text = FormatSeconds(mpd.GetCurrentSong().Time);
|
||||||
}
|
|
||||||
|
TimeSlider.Value = mpd._currentTime / mpd.GetCurrentSong().Time * 100;
|
||||||
|
|
||||||
if (VolumeSlider.Value != mpd._currentVolume)
|
if (VolumeSlider.Value != mpd._currentVolume)
|
||||||
{
|
{
|
||||||
@ -87,12 +90,6 @@ namespace unison
|
|||||||
else
|
else
|
||||||
PlayPause.Text = "\xedb5";
|
PlayPause.Text = "\xedb5";
|
||||||
|
|
||||||
SnapcastHandler snapcast = (SnapcastHandler)Application.Current.Properties["snapcast"];
|
|
||||||
if (snapcast.Started)
|
|
||||||
SnapcastText.Text = "Stop Snapcast";
|
|
||||||
else
|
|
||||||
SnapcastText.Text = "Start Snapcast";
|
|
||||||
|
|
||||||
Connection.Text = (mpd._connected ? "✔️" : "❌") + $"{Properties.Settings.Default.mpd_host}:{Properties.Settings.Default.mpd_port}";
|
Connection.Text = (mpd._connected ? "✔️" : "❌") + $"{Properties.Settings.Default.mpd_host}:{Properties.Settings.Default.mpd_port}";
|
||||||
|
|
||||||
UpdateButton(ref BorderRandom, mpd._currentRandom);
|
UpdateButton(ref BorderRandom, mpd._currentRandom);
|
||||||
@ -100,21 +97,23 @@ namespace unison
|
|||||||
UpdateButton(ref BorderSingle, mpd._currentSingle);
|
UpdateButton(ref BorderSingle, mpd._currentSingle);
|
||||||
UpdateButton(ref BorderConsume, mpd._currentConsume);
|
UpdateButton(ref BorderConsume, mpd._currentConsume);
|
||||||
|
|
||||||
if (mpd.GetCover() != null)
|
if (mpd.GetCover() == null)
|
||||||
{
|
{
|
||||||
if ((!mpd.GetCover().IsDownloading) && mpd.GetCover().IsSuccess)
|
NoCover.Visibility = Visibility.Visible;
|
||||||
{
|
Cover.Visibility = Visibility.Collapsed;
|
||||||
if (mpd.GetCurrentSong().File == mpd.GetCover().SongFilePath)
|
|
||||||
{
|
|
||||||
Cover.Source = mpd.GetCover().AlbumImageSource;
|
|
||||||
Cover.Visibility = Visibility.Visible;
|
|
||||||
NoCover.Visibility = Visibility.Collapsed;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
NoCover.Visibility = Visibility.Visible;
|
else if (Cover.Source != mpd.GetCover())
|
||||||
Cover.Visibility = Visibility.Collapsed;
|
{
|
||||||
|
Cover.Source = mpd.GetCover();
|
||||||
|
Cover.Visibility = Visibility.Visible;
|
||||||
|
NoCover.Visibility = Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
SnapcastHandler snapcast = (SnapcastHandler)Application.Current.Properties["snapcast"];
|
||||||
|
if (snapcast.Started)
|
||||||
|
SnapcastText.Text = "Stop Snapcast";
|
||||||
|
else
|
||||||
|
SnapcastText.Text = "Start Snapcast";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Pause_Clicked(object sender, RoutedEventArgs e) => mpd.PlayPause();
|
public void Pause_Clicked(object sender, RoutedEventArgs e) => mpd.PlayPause();
|
||||||
@ -157,10 +156,10 @@ namespace unison
|
|||||||
Slider slider = (Slider)sender;
|
Slider slider = (Slider)sender;
|
||||||
|
|
||||||
double SongPercentage = slider.Value;
|
double SongPercentage = slider.Value;
|
||||||
double SongTime = mpd.GetStatus().MpdSongTime;
|
double SongTime = mpd._totalTime;
|
||||||
double SeekTime = SongPercentage / 100 * SongTime;
|
double SeekTime = SongPercentage / 100 * SongTime;
|
||||||
|
|
||||||
mpd.SetTime((int)SeekTime);
|
mpd.SetTime(SeekTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void VolumeSlider_DragCompleted(object sender, MouseButtonEventArgs e)
|
private void VolumeSlider_DragCompleted(object sender, MouseButtonEventArgs e)
|
||||||
|
@ -133,7 +133,7 @@
|
|||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock TextWrapping="Wrap" VerticalAlignment="Top">
|
<TextBlock TextWrapping="Wrap" VerticalAlignment="Top">
|
||||||
unison is free software. It is built with the following technologies:<LineBreak/>
|
unison is free software. It is built with the following technologies:<LineBreak/>
|
||||||
※ <Hyperlink NavigateUri="https://torum.github.io/MPDCtrl/" RequestNavigate="Hyperlink_RequestNavigate">MPDCtrl</Hyperlink><LineBreak/>
|
※ <Hyperlink NavigateUri="https://github.com/Difegue/Stylophone" RequestNavigate="Hyperlink_RequestNavigate">Stylophone</Hyperlink>'s updated MpcNET<LineBreak/>
|
||||||
※ <Hyperlink NavigateUri="https://github.com/hardcodet/wpf-notifyicon" RequestNavigate="Hyperlink_RequestNavigate">wpf-notifyicon</Hyperlink><LineBreak/>
|
※ <Hyperlink NavigateUri="https://github.com/hardcodet/wpf-notifyicon" RequestNavigate="Hyperlink_RequestNavigate">wpf-notifyicon</Hyperlink><LineBreak/>
|
||||||
※ <Hyperlink NavigateUri="https://github.com/samhocevar/emoji.wpf" RequestNavigate="Hyperlink_RequestNavigate">Emoji.WPF</Hyperlink>
|
※ <Hyperlink NavigateUri="https://github.com/samhocevar/emoji.wpf" RequestNavigate="Hyperlink_RequestNavigate">Emoji.WPF</Hyperlink>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
|
@ -81,7 +81,7 @@ namespace unison
|
|||||||
{
|
{
|
||||||
SaveSettings();
|
SaveSettings();
|
||||||
MPDHandler mpd = (MPDHandler)Application.Current.Properties["mpd"];
|
MPDHandler mpd = (MPDHandler)Application.Current.Properties["mpd"];
|
||||||
mpd.Start();
|
// connect to mpd
|
||||||
UpdateConnectionStatus();
|
UpdateConnectionStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +76,10 @@
|
|||||||
<PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="1.1.0" />
|
<PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="1.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\MpcNET\MpcNET.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Update="Properties\Settings.Designer.cs">
|
<Compile Update="Properties\Settings.Designer.cs">
|
||||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||||
|
21
unison.sln
21
unison.sln
@ -3,18 +3,37 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
|||||||
# Visual Studio Version 16
|
# Visual Studio Version 16
|
||||||
VisualStudioVersion = 16.0.31515.178
|
VisualStudioVersion = 16.0.31515.178
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "unison", "unison.csproj", "{489048C4-3FCA-4573-B34C-943D03F94D04}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "unison", "unison.csproj", "{489048C4-3FCA-4573-B34C-943D03F94D04}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MpcNET", "..\MpcNET\MpcNET.csproj", "{AD425CBC-9C18-4B4A-AB51-4DE8F50FB6A9}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MpcNET.Test", "..\MpcNET.Test\MpcNET.Test.csproj", "{6789E959-2DB2-412E-8CC1-C1564068F03D}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
Release|Any CPU = Release|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
|
Release-Stable|Any CPU = Release-Stable|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{489048C4-3FCA-4573-B34C-943D03F94D04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{489048C4-3FCA-4573-B34C-943D03F94D04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{489048C4-3FCA-4573-B34C-943D03F94D04}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{489048C4-3FCA-4573-B34C-943D03F94D04}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{489048C4-3FCA-4573-B34C-943D03F94D04}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{489048C4-3FCA-4573-B34C-943D03F94D04}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{489048C4-3FCA-4573-B34C-943D03F94D04}.Release|Any CPU.Build.0 = Release|Any CPU
|
{489048C4-3FCA-4573-B34C-943D03F94D04}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{489048C4-3FCA-4573-B34C-943D03F94D04}.Release-Stable|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{489048C4-3FCA-4573-B34C-943D03F94D04}.Release-Stable|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{AD425CBC-9C18-4B4A-AB51-4DE8F50FB6A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{AD425CBC-9C18-4B4A-AB51-4DE8F50FB6A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{AD425CBC-9C18-4B4A-AB51-4DE8F50FB6A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{AD425CBC-9C18-4B4A-AB51-4DE8F50FB6A9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{AD425CBC-9C18-4B4A-AB51-4DE8F50FB6A9}.Release-Stable|Any CPU.ActiveCfg = Release-Stable|Any CPU
|
||||||
|
{AD425CBC-9C18-4B4A-AB51-4DE8F50FB6A9}.Release-Stable|Any CPU.Build.0 = Release-Stable|Any CPU
|
||||||
|
{6789E959-2DB2-412E-8CC1-C1564068F03D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{6789E959-2DB2-412E-8CC1-C1564068F03D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{6789E959-2DB2-412E-8CC1-C1564068F03D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{6789E959-2DB2-412E-8CC1-C1564068F03D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{6789E959-2DB2-412E-8CC1-C1564068F03D}.Release-Stable|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{6789E959-2DB2-412E-8CC1-C1564068F03D}.Release-Stable|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
Loading…
Reference in New Issue
Block a user