Skip to content

Commit

Permalink
Initial Design. Needs feedback.
Browse files Browse the repository at this point in the history
Added base classes, methods and etc.
  • Loading branch information
Yucked committed Jun 17, 2019
1 parent 6448b14 commit d5edc28
Show file tree
Hide file tree
Showing 18 changed files with 435 additions and 1 deletion.
54 changes: 54 additions & 0 deletions Commons/BaseClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Victoria.Commons.Entities;

namespace Victoria.Commons
{
public abstract class BaseClient : IAsyncDisposable
{
internal readonly WSClient _ws;

public BaseClient(BaseConfiguration config)
{
_ws = new WSClient($"ws://{config.Hostname}:{config.Port}", default);
_ws.OnMessageReceived += OnMessageReceivedAsync;
_ws.OnConnectionClosed += OnConnectionClosedAsync;
}

public async Task InitializeAsync()
{
await _ws.ConnectAsync().ConfigureAwait(false);
}

public Task ConnectToVoiceChannelAsync()
{
return Task.CompletedTask;
}

public Task DisconnectFromVoiceChannelAsync()
{
return Task.CompletedTask;
}

public Task MoveVoiceChannelAsync()
{
return Task.CompletedTask;
}

private Task OnMessageReceivedAsync(ReadOnlyMemory<byte> bytes)
{
throw new NotImplementedException();
}

private Task OnConnectionClosedAsync(object arg)
{
throw new NotImplementedException();
}

public async ValueTask DisposeAsync()
{
await _ws.DisposeAsync().ConfigureAwait(false);
}
}
}
100 changes: 100 additions & 0 deletions Commons/BasePlayer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using System;
using System.Threading.Tasks;
using Victoria.Commons.Entities;
using Victoria.Commons.Entities.Enums;
using Victoria.Commons.Queue;

namespace Victoria.Commons
{
/// <summary>
///
/// </summary>
public abstract class BasePlayer : IAsyncDisposable
{
/// <summary>
///
/// </summary>
public int CurrentVolume { get; internal set; }

/// <summary>
///
/// </summary>
public PlayerState PlayerState { get; internal set; }

/// <summary>
///
/// </summary>
public DefaultQueue<IQueueObject> Queue { get; private set; }

/// <summary>
///
/// </summary>
/// <param name="track"></param>
/// <returns></returns>
public abstract Task PlayAsync(BaseTrack track);

/// <summary>
///
/// </summary>
/// <param name="track"></param>
/// <param name="startTime"></param>
/// <param name="endTime"></param>
/// <returns></returns>
public abstract Task PlayAsync(BaseTrack track, TimeSpan startTime, TimeSpan endTime);

/// <summary>
///
/// </summary>
/// <param name="tracks"></param>
/// <returns></returns>
public abstract Task PlayAsync(params BaseTrack[] tracks);

/// <summary>
///
/// </summary>
/// <returns></returns>
public abstract Task StopAsync();

/// <summary>
///
/// </summary>
/// <returns></returns>
public abstract Task PauseAsync();

/// <summary>
///
/// </summary>
/// <returns></returns>
public abstract Task ResumeAsync();

/// <summary>
///
/// </summary>
/// <returns></returns>
public abstract Task<BaseTrack> SkipAsync();

/// <summary>
///
/// </summary>
/// <param name="volume"></param>
/// <returns></returns>
public abstract Task ChangeVolumeAsync(uint volume);

/// <summary>
///
/// </summary>
/// <returns></returns>
public abstract Task ChangeEqualizerAsync();

/// <summary>
///
/// </summary>
/// <returns></returns>
public async ValueTask DisposeAsync()
{
PlayerState = PlayerState.DISPOSED;
await StopAsync().ConfigureAwait(false);
// DESTROY PLAYER
}
}
}
8 changes: 8 additions & 0 deletions Commons/Entities/BaseConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Victoria.Commons.Entities
{
public sealed class BaseConfiguration
{
public string Hostname { get; set; }
public ushort Port { get; set; }
}
}
15 changes: 15 additions & 0 deletions Commons/Entities/BaseTrack.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Victoria.Commons.Queue;

namespace Victoria.Commons.Entities
{
public class BaseTrack : IQueueObject
{
public string Hash { get; }
public string Id { get; }
public string Url { get; }
public string Title { get; }
public long Duration { get; }
public long Position { get; }
public bool CanStream { get; }
}
}
13 changes: 13 additions & 0 deletions Commons/Entities/Enums/PlayerState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Victoria.Commons.Entities.Enums
{
public enum PlayerState
{
CONNECTED,
CONNECTING,
PLAYING,
STOPPED,
PAUSED,
DISCONNECTED,
DISPOSED
}
}
6 changes: 6 additions & 0 deletions Commons/Entities/IqueueObject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Victoria.Commons.Entities
{
public class IqueueObject
{
}
}
15 changes: 15 additions & 0 deletions Commons/Extensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using Victoria.Commons.Entities.Enums;

namespace Victoria.Commons
{
public static class Extensions
{
public static void EnsureState(this PlayerState playerState, params PlayerState[] states)
{
foreach (var state in states)
if (playerState == state)
throw new Exception($"Player can't be used since player state is currently set to {state}.");
}
}
}
17 changes: 17 additions & 0 deletions Commons/Queue/DefaultQueue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Collections.Generic;

namespace Victoria.Commons.Queue
{
public sealed class DefaultQueue<T> where T : IQueueObject
{
private readonly object _lock;
private readonly LinkedList<T> _list;

public DefaultQueue()
{
_lock = new object();
_list = new LinkedList<T>();
}

}
}
6 changes: 6 additions & 0 deletions Commons/Queue/IQueueObject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Victoria.Commons.Queue
{
public interface IQueueObject
{
}
}
130 changes: 129 additions & 1 deletion Commons/WSClient.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,137 @@
using System;
using System.Net.WebSockets;
using System.Threading.Tasks;
using System.Threading;
using System.Text.Json.Serialization;
using System.Collections.Generic;

namespace Victoria.Commons
{
public sealed class WSClient
public sealed class WSClient : IAsyncDisposable
{
private ClientWebSocket socket;
private CancellationTokenSource
connectionToken,
normalToken,
mainToken;
private readonly string hostAddress;

public event Func<object, Task> OnConnectionClosed;
public event Func<ReadOnlyMemory<byte>, Task> OnMessageReceived;

public WSClient(string url, IReadOnlyDictionary<string, string> headers)
{
hostAddress = url;
connectionToken = new CancellationTokenSource();
normalToken = new CancellationTokenSource(TimeSpan.FromSeconds(30));
mainToken = CancellationTokenSource.CreateLinkedTokenSource(connectionToken.Token, normalToken.Token);
socket = new ClientWebSocket();

if (headers.Count != 0)
foreach (var (key, value) in headers)
socket.Options.SetRequestHeader(key, value);
}

public async Task ConnectAsync()
{
try
{
await socket.ConnectAsync(new Uri(hostAddress), connectionToken.Token)
.ContinueWith(VerifyConnectionAsync)
.ConfigureAwait(false);
}
catch
{
// TODO: Ignore exceptions or log them?
}
}

public async Task DisconnectAsync()
{
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Requested.", normalToken.Token)
.ConfigureAwait(false);

// TODO: Log connection closed.
}

public async Task SendAsync<T>(T value)
{
var bytes = JsonSerializer.ToUtf8Bytes(value);
await socket.SendAsync(bytes, WebSocketMessageType.Text, true, normalToken.Token)
.ConfigureAwait(false);

// TODO: Log bytes sent.
}

public async ValueTask DisposeAsync()
{
mainToken.Cancel(false);
await DisconnectAsync()
.ConfigureAwait(false);
socket.Dispose();
}

private async Task VerifyConnectionAsync(Task task)
{
if (task.IsCanceled || task.IsFaulted || task.Exception != null)
{
var error =
task.IsCanceled ?
"WS connection task was cancelled!" :
task.IsFaulted ?
"Task faulted when trying to connect to WS server." :
task.Exception.Message;

await RetryConnectionAsync()
.ConfigureAwait(false);
// TODO: Log error
}
else
{
await ReceiveAsync()
.ConfigureAwait(false);
}

async Task RetryConnectionAsync()
{
while (socket.State != WebSocketState.Open)
{
await Task.Delay(TimeSpan.FromSeconds(15))
.ConfigureAwait(false);

await ConnectAsync()
.ConfigureAwait(false);
}
}
}

private async Task ReceiveAsync()
{
while (!connectionToken.IsCancellationRequested && socket.State is WebSocketState.Open)
{
try
{
var memory = new Memory<byte>();
var result = await socket.ReceiveAsync(memory, normalToken.Token)
.ConfigureAwait(false);
switch (result.MessageType)
{
case WebSocketMessageType.Close:
// TODO: Raise OnClosed event.
break;

case WebSocketMessageType.Text:
await OnMessageReceived.Invoke(memory)
.ConfigureAwait(false);
break;
}
}
catch (Exception ex)
{
// TODO: Log exceptions.
// TODO: Raise OnClosed event.
}
}
}
}
}
Loading

0 comments on commit d5edc28

Please sign in to comment.