Adding dependency injection

This commit is contained in:
watsonb8 2020-01-31 20:41:45 -05:00
parent f8ad2f459e
commit 48d0ffa77d
27 changed files with 550 additions and 114 deletions

View File

@ -150,7 +150,7 @@
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll</HintPath> <HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Bcl.AsyncInterfaces"> <Reference Include="Microsoft.Bcl.AsyncInterfaces">
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.1.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll</HintPath> <HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Linq.Async"> <Reference Include="System.Linq.Async">
<HintPath>..\packages\System.Linq.Async.4.0.0\lib\net461\System.Linq.Async.dll</HintPath> <HintPath>..\packages\System.Linq.Async.4.0.0\lib\net461\System.Linq.Async.dll</HintPath>
@ -164,6 +164,10 @@
<Reference Include="CarouselView.FormsPlugin.Abstractions"> <Reference Include="CarouselView.FormsPlugin.Abstractions">
<HintPath>..\packages\CarouselView.FormsPlugin.5.2.0\lib\netstandard2.0\CarouselView.FormsPlugin.Abstractions.dll</HintPath> <HintPath>..\packages\CarouselView.FormsPlugin.5.2.0\lib\netstandard2.0\CarouselView.FormsPlugin.Abstractions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Autofac">
<HintPath>..\packages\Autofac.5.0.0\lib\net461\Autofac.dll</HintPath>
</Reference>
<Reference Include="System.ComponentModel.Composition" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="gtk-gui\gui.stetic"> <EmbeddedResource Include="gtk-gui\gui.stetic">

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Autofac" version="5.0.0" targetFramework="net47" />
<package id="CarouselView.FormsPlugin" version="5.2.0" targetFramework="net47" /> <package id="CarouselView.FormsPlugin" version="5.2.0" targetFramework="net47" />
<package id="DLToolkit.Forms.Controls.FlowListView" version="2.0.11" targetFramework="net47" /> <package id="DLToolkit.Forms.Controls.FlowListView" version="2.0.11" targetFramework="net47" />
<package id="Google.Protobuf" version="3.10.1" targetFramework="net47" /> <package id="Google.Protobuf" version="3.10.1" targetFramework="net47" />
@ -13,7 +14,7 @@
<package id="LibVLCSharp.GTK" version="3.3.1" targetFramework="net47" /> <package id="LibVLCSharp.GTK" version="3.3.1" targetFramework="net47" />
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net47" /> <package id="Microsoft.Bcl" version="1.1.10" targetFramework="net47" />
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net47" /> <package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net47" />
<package id="Microsoft.Bcl.AsyncInterfaces" version="1.0.0" targetFramework="net47" /> <package id="Microsoft.Bcl.AsyncInterfaces" version="1.1.0" targetFramework="net47" />
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net47" /> <package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net47" />
<package id="OpenTK" version="3.1.0" targetFramework="net47" /> <package id="OpenTK" version="3.1.0" targetFramework="net47" />
<package id="OpenTK.GLControl" version="3.0.1" targetFramework="net47" /> <package id="OpenTK.GLControl" version="3.0.1" targetFramework="net47" />

View File

@ -13,6 +13,7 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Grpc" Version="2.26.0" /> <PackageReference Include="Grpc" Version="2.26.0" />
<PackageReference Include="Autofac" Version="5.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Protobuf Remove="..\Aurora\Proto\general.proto" /> <Protobuf Remove="..\Aurora\Proto\general.proto" />

View File

@ -0,0 +1,29 @@
using NUnit.Framework;
using Aurora.Proto.PartyV2;
using Aurora.Services.Server;
using Grpc.Core;
using System.Threading.Tasks;
using System.Linq;
namespace Aurora.test.ControllerTests
{
public class MediaControllerTests
{
private RemotePartyService.RemotePartyServiceClient _remotePartyService;
private Channel _channel;
[SetUp]
public void Setup()
{
ServerService.Instance.Start("testParty", "asdf");
_channel = new Channel(string.Format("{0}:{1}", ServerService.GetLocalIPAddress(), 8080), ChannelCredentials.Insecure);
_remotePartyService = new RemotePartyService.RemotePartyServiceClient(_channel);
}
[TearDown]
public async Task TearDown()
{
await ServerService.Instance.Stop();
await _channel.ShutdownAsync();
}
}
}

View File

@ -139,5 +139,50 @@ namespace Aurora.test.ControllerTests
Assert.AreEqual(resp.Members.Count, 5); Assert.AreEqual(resp.Members.Count, 5);
Assert.False(resp.Members.Any(member => member.UserName == "Ke$sha")); Assert.False(resp.Members.Any(member => member.UserName == "Ke$sha"));
} }
static object[] PagingCases =
{
new object[] {"Tupac", "Aubrey Grahm", "Beyonce Knowls", "Ke$ha", "A$ap Ferg", "asdf", "sdfa", "dfas", "fasd"},
};
[Test]
[TestCaseSource("PagingCases")]
public void MemberPagingTest(object[] members)
{
foreach (string name in members)
{
Member member = _remotePartyService.CreateMember(new CreateMemberRequest()
{
Parent = "party1",
Member = new Member()
{
UserName = name
}
});
Assert.NotNull(member);
}
//List members
ListMembersResponse resp = _remotePartyService.ListMembers(new ListMembersRequest()
{
Parent = "party1",
PageSize = 2,
});
string nextPageToken = resp.NextPageToken;
Assert.AreEqual(resp.Members.Count, 2);
//List members
resp = _remotePartyService.ListMembers(new ListMembersRequest()
{
Parent = "party1",
PageSize = 2,
PageToken = nextPageToken,
});
Assert.AreEqual(resp.Members.Count, 2);
}
} }
} }

View File

@ -1,19 +1,51 @@
using System; using System;
using Aurora.Design.Views.Main; using Aurora.Design.Views.Main;
using Aurora.Design.Views.Albums;
using Aurora.Design.Views.Artists;
using Aurora.Design.Views.Party;
using Aurora.Design.Views.Profile;
using Aurora.Design.Views.Songs;
using Aurora.Design.Views.Stations;
using Aurora.Services.ClientService;
using Autofac;
using LibVLCSharp.Shared; using LibVLCSharp.Shared;
using Xamarin.Forms; using Xamarin.Forms;
using Xamarin.Forms.Xaml; using Aurora.Services.Player;
using Aurora.Services.Settings;
namespace Aurora namespace Aurora
{ {
public partial class App : Application public partial class App : Application
{ {
private static IContainer _container;
public App() public App()
{ {
InitializeComponent(); InitializeComponent();
Core.Initialize(); Core.Initialize();
MainPage = new MainView(); //Register DI
ContainerBuilder _builder = new ContainerBuilder();
// _builder.RegisterInstance<IPlayer>(new PlayerService()).SingleInstance();
_builder.RegisterType<PlayerService>().As<IPlayer>().SingleInstance();
_builder.RegisterType<SettingsService>().As<ISettingsService>().SingleInstance();
_builder.RegisterType<ClientService>().As<IClientService>().SingleInstance();
_builder.RegisterType<MainView>().SingleInstance();
_builder.RegisterType<AlbumsViewModel>();
_builder.RegisterType<ArtistsViewModel>();
_builder.RegisterType<PartyViewModel>();
_builder.RegisterType<ProfileViewModel>();
_builder.RegisterType<SongsViewModel>();
_builder.RegisterType<StationsViewModel>();
// _builder.RegisterInstance<ISettingsService>(new SettingsService()).SingleInstance();
_container = _builder.Build();
MainPage = _container.Resolve<MainView>();
}
public static IContainer Container
{
get { return _container; }
} }
protected override void OnStart() protected override void OnStart()

View File

@ -23,6 +23,7 @@
<PackageReference Include="Sharpnado.Forms.HorizontalListView" Version="1.3.0" /> <PackageReference Include="Sharpnado.Forms.HorizontalListView" Version="1.3.0" />
<PackageReference Include="DLToolkit.Forms.Controls.FlowListView" Version="2.0.11" /> <PackageReference Include="DLToolkit.Forms.Controls.FlowListView" Version="2.0.11" />
<PackageReference Include="CarouselView.FormsPlugin" Version="5.2.0" /> <PackageReference Include="CarouselView.FormsPlugin" Version="5.2.0" />
<PackageReference Include="Autofac" Version="5.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Design\" /> <Folder Include="Design\" />

View File

@ -9,8 +9,8 @@ using Xamarin.Forms;
using Xamarin.Forms.Xaml; using Xamarin.Forms.Xaml;
using Aurora.Models.Media; using Aurora.Models.Media;
using Aurora.Design.Components.MediaPlayer; using Aurora.Design.Components.MediaPlayer;
using Aurora.Services.PlayerService; using Aurora.Services.Player;
using System.Threading; using Autofac;
namespace Aurora.Design.Views.Main namespace Aurora.Design.Views.Main
{ {
@ -38,10 +38,10 @@ namespace Aurora.Design.Views.Main
private Dictionary<int, BaseViewModel> _viewModels; private Dictionary<int, BaseViewModel> _viewModels;
private BaseViewModel _lastViewModel; private BaseViewModel _lastViewModel;
private Player _playerComponent; private Player _playerComponent;
private PlayerService _playerService; private IPlayer _playerService;
private ContentPresenter _viewContent; private ContentPresenter _viewContent;
public MainView() public MainView(IPlayer player)
{ {
InitializeComponent(); InitializeComponent();
BindingContext = new MainViewModel(); BindingContext = new MainViewModel();
@ -50,7 +50,7 @@ namespace Aurora.Design.Views.Main
_playerComponent = Player; _playerComponent = Player;
_viewContent = (ContentPresenter)Content.FindByName("ViewContent"); _viewContent = (ContentPresenter)Content.FindByName("ViewContent");
_playerService = PlayerService.Instance; _playerService = player;
MasterPage.ListView.ItemSelected += OnNavItemSelected; MasterPage.ListView.ItemSelected += OnNavItemSelected;
@ -89,7 +89,7 @@ namespace Aurora.Design.Views.Main
} }
//Instantiate new view model //Instantiate new view model
vm = (BaseViewModel)Activator.CreateInstance(item.TargetViewModelType); vm = (BaseViewModel)App.Container.Resolve(item.TargetViewModelType); //Activator.CreateInstance(item.TargetViewModelType);
_viewModels.Add(item.Id, vm); _viewModels.Add(item.Id, vm);
} }
@ -135,7 +135,7 @@ namespace Aurora.Design.Views.Main
else else
{ {
//Instantiate new view model //Instantiate new view model
vm = (BaseViewModel)Activator.CreateInstance(firstNavItem.TargetViewModelType); vm = (BaseViewModel)App.Container.Resolve(firstNavItem.TargetViewModelType); //(BaseViewModel)Activator.CreateInstance(firstNavItem.TargetViewModelType);
_viewModels.Add(firstNavItem.Id, vm); _viewModels.Add(firstNavItem.Id, vm);
} }

View File

@ -9,10 +9,11 @@ using Aurora.Proto.Party;
using Aurora.Proto.Events; using Aurora.Proto.Events;
using Aurora.Services.ClientService; using Aurora.Services.ClientService;
using Aurora.Services.ClientService.Events; using Aurora.Services.ClientService.Events;
using Aurora.Services.PlayerService; using Aurora.Services.Player;
using Aurora.Services.EventManager; using Aurora.Services.EventManager;
using Aurora.Models.Media; using Aurora.Models.Media;
using Aurora.Design.Views.Party.NewPartyDialog; using Aurora.Design.Views.Party.NewPartyDialog;
using Aurora.Services.Settings;
namespace Aurora.Design.Views.Party namespace Aurora.Design.Views.Party
{ {
@ -32,22 +33,25 @@ namespace Aurora.Design.Views.Party
private ObservableCollection<PartyMember> _members; private ObservableCollection<PartyMember> _members;
private ObservableCollection<BaseMedia> _queue; private ObservableCollection<BaseMedia> _queue;
private BaseMedia _selectedMedia; private BaseMedia _selectedMedia;
private ClientService _client; private IClientService _client;
private ISettingsService _settingsService;
private int _selectedTabIndex; private int _selectedTabIndex;
public PartyViewModel() public PartyViewModel(ISettingsService settingsService, IClientService clientService)
{ {
_members = new ObservableCollection<PartyMember>(); _members = new ObservableCollection<PartyMember>();
_queue = new ObservableCollection<BaseMedia>(); _queue = new ObservableCollection<BaseMedia>();
this._settingsService = settingsService;
SetState(PartyState.SelectingHost); SetState(PartyState.SelectingHost);
PlayCommand = new Command(OnDoubleClickCommandExecute, CanDoubleClickCommandExecute); PlayCommand = new Command(OnDoubleClickCommandExecute, CanDoubleClickCommandExecute);
LeavePartyCommand = new Command(OnLeavePartyCommandExecute, CanLeavePartyCommandExecute); LeavePartyCommand = new Command(OnLeavePartyCommandExecute, CanLeavePartyCommandExecute);
_client = ClientService.Instance; _client = clientService;
_client.OnMediaPaused += this.OnRemoteMediaPaused; _client.OnMediaPaused += this.OnRemoteMediaPaused;
_client.OnMediaResumed += this.OnRemoteMediaResumed; _client.OnMediaResumed += this.OnRemoteMediaResumed;
@ -245,7 +249,7 @@ namespace Aurora.Design.Views.Party
private async void OnJoinCommandExecute() private async void OnJoinCommandExecute()
{ {
SetState(PartyState.Connecting); SetState(PartyState.Connecting);
_client.Start(_hostname, SettingsService.Instance.DefaultPort.ToString()); _client.Start(_hostname, this._settingsService.DefaultPort.ToString());
await JoinParty(false); await JoinParty(false);
//TODO add cancellation token //TODO add cancellation token
@ -272,7 +276,7 @@ namespace Aurora.Design.Views.Party
ServerService.Instance.Start(); ServerService.Instance.Start();
string localHost = ServerService.GetLocalIPAddress(); string localHost = ServerService.GetLocalIPAddress();
_client.IsHost = true; _client.IsHost = true;
_client.Start(localHost, SettingsService.Instance.DefaultPort.ToString()); _client.Start(localHost, this._settingsService.DefaultPort.ToString());
await JoinParty(true); await JoinParty(true);
@ -387,10 +391,10 @@ namespace Aurora.Design.Views.Party
{ {
JoinPartyResponse resp = await _client.RemotePartyClient.JoinPartyAsync(new JoinPartyRequest JoinPartyResponse resp = await _client.RemotePartyClient.JoinPartyAsync(new JoinPartyRequest
{ {
UserName = SettingsService.Instance.Username, UserName = this._settingsService.Username,
}); });
SettingsService.Instance.ClientId = resp.ClientId; this._settingsService.ClientId = resp.ClientId;
RefreshMembers(); RefreshMembers();
@ -447,13 +451,13 @@ namespace Aurora.Design.Views.Party
req.EventTypes.Add(EventType.PartyMemberLeft); req.EventTypes.Add(EventType.PartyMemberLeft);
req.EventTypes.Add(EventType.MediaPlaying); req.EventTypes.Add(EventType.MediaPlaying);
req.EventTypes.Add(EventType.MediaStopped); req.EventTypes.Add(EventType.MediaStopped);
if (!string.IsNullOrWhiteSpace(SettingsService.Instance.ClientId)) if (!string.IsNullOrWhiteSpace(this._settingsService.ClientId))
{ {
req.ClientId = SettingsService.Instance.ClientId; req.ClientId = this._settingsService.ClientId;
} }
Console.WriteLine(string.Format("CLIENT {0} - SubscribeToEvents called from client with id", SettingsService.Instance.ClientId)); Console.WriteLine(string.Format("CLIENT {0} - SubscribeToEvents called from client with id", this._settingsService.ClientId));
await _client.RemoteEventClient.SubscribeToEventsAsync(req); await _client.RemoteEventClient.SubscribeToEventsAsync(req);
} }
private async Task UnsubscribeFromEvents() private async Task UnsubscribeFromEvents()

View File

@ -1,32 +1,34 @@
using System; using System;
using Aurora.Services; using Aurora.Services.Settings;
namespace Aurora.Design.Views.Profile namespace Aurora.Design.Views.Profile
{ {
public class ProfileViewModel : BaseViewModel public class ProfileViewModel : BaseViewModel
{ {
private ISettingsService _settingsService;
public ProfileViewModel() public ProfileViewModel(ISettingsService settingsService)
{ {
this._settingsService = settingsService;
} }
public string Username public string Username
{ {
get { return SettingsService.Instance.Username; } get { return this._settingsService.Username; }
set set
{ {
SettingsService.Instance.Username = value; this._settingsService.Username = value;
OnPropertyChanged("Username"); OnPropertyChanged("Username");
} }
} }
public string Port public string Port
{ {
get { return SettingsService.Instance.DefaultPort.ToString(); } get { return this._settingsService.DefaultPort.ToString(); }
set set
{ {
Int32.TryParse(value, out int portNum); Int32.TryParse(value, out int portNum);
SettingsService.Instance.DefaultPort = portNum; this._settingsService.DefaultPort = portNum;
OnPropertyChanged("Port"); OnPropertyChanged("Port");
} }
} }

View File

@ -44,7 +44,13 @@ service RemotePartyService {
rpc ListMedia(ListMediaRequest) returns (ListMediaResponse); rpc ListMedia(ListMediaRequest) returns (ListMediaResponse);
//Get //Get
rpc GetMedia(GetMediaRequest) returns (RemoteMedia); rpc GetMedia(GetMediaRequest) returns (Media);
//Create
rpc CreateMedia(CreateMediaRequest) returns (Media);
//Delete
rpc DeleteMedia(DeleteMediaRequest) returns (Aurora.Proto.General.Empty) {};
//CUSTOM: Stream //CUSTOM: Stream
rpc StreamMedia(StreamMediaRequest) returns (stream Aurora.Proto.General.Chunk) {}; rpc StreamMedia(StreamMediaRequest) returns (stream Aurora.Proto.General.Chunk) {};
@ -97,7 +103,10 @@ message Member {
//Resource name of the party member to be returned (Added by server) //Resource name of the party member to be returned (Added by server)
string name = 1; string name = 1;
string userName = 2; string userName = 2;
//Added by server
string ipAddress = 3; string ipAddress = 3;
//Added by server //Added by server
google.protobuf.Timestamp addedOn = 4; google.protobuf.Timestamp addedOn = 4;
} }
@ -134,7 +143,7 @@ message DeleteMemberRequest {
string name = 1; string name = 1;
} }
message RemoteMedia { message Media {
//Resource name of the remote media object //Resource name of the remote media object
string name = 1; string name = 1;
string title = 2; string title = 2;
@ -151,7 +160,7 @@ message ListMediaRequest {
} }
message ListMediaResponse { message ListMediaResponse {
repeated RemoteMedia media = 1; repeated Media media = 1;
string nextPageToken = 3; string nextPageToken = 3;
} }
@ -160,6 +169,17 @@ message GetMediaRequest {
string name = 1; string name = 1;
} }
message CreateMediaRequest {
//Resource name of the parent collection of the member to be created (The party)
string parent = 1;
Media media = 2;
}
message DeleteMediaRequest {
//Resource name of the member to be deleted
string name = 1;
}
message StreamMediaRequest { message StreamMediaRequest {
//Resource name of the media requested //Resource name of the media requested
string name = 1; string name = 1;
@ -199,7 +219,7 @@ message BaseEvent {
} }
message NewMediaPlayingEvent { message NewMediaPlayingEvent {
RemoteMedia media = 1; Media media = 1;
} }
message MediaResumedEvent { message MediaResumedEvent {

View File

@ -2,7 +2,9 @@ using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Aurora.Proto.Sync; using Aurora.Proto.Sync;
using Aurora.Proto.General; using Aurora.Proto.General;
using Aurora.Services.PlayerService; using Aurora.Services.Player;
using Aurora;
using Autofac;
namespace Aurora.RemoteImpl namespace Aurora.RemoteImpl
{ {
@ -19,31 +21,36 @@ namespace Aurora.RemoteImpl
Grpc.Core.IServerStreamWriter<Sync> responseStream, Grpc.Core.IServerStreamWriter<Sync> responseStream,
Grpc.Core.ServerCallContext context) Grpc.Core.ServerCallContext context)
{ {
bool continueSync = true; using (var scope = App.Container.BeginLifetimeScope())
string currentId = PlayerService.Instance.CurrentMedia.Id;
MediaChangedEventHandler mediaChanged = (sender, e) =>
{ {
if (e.NewId != currentId) IPlayer player = scope.Resolve<IPlayer>();
bool continueSync = true;
string currentId = player.CurrentMedia.Id;
MediaChangedEventHandler mediaChanged = (sender, e) =>
{ {
continueSync = false; if (e.NewId != currentId)
} {
}; continueSync = false;
}
PlayerService.Instance.MediaChanged += mediaChanged;
while (continueSync)
{
float length = PlayerService.Instance.CurrentMediaLength;
Sync sync = new Sync()
{
TrackPosition = PlayerService.Instance.CurrentMediaPosition,
ServerTimeTicks = Utils.TimeUtils.GetNetworkTime().DateTime.Ticks
}; };
await responseStream.WriteAsync(sync);
Console.WriteLine("Sent Sync"); player.MediaChanged += mediaChanged;
await Task.Delay(5000);
while (continueSync)
{
float length = player.CurrentMediaLength;
Sync sync = new Sync()
{
TrackPosition = player.CurrentMediaPosition,
ServerTimeTicks = Utils.TimeUtils.GetNetworkTime().DateTime.Ticks
};
await responseStream.WriteAsync(sync);
Console.WriteLine("Sent Sync");
await Task.Delay(5000);
}
} }
} }
} }
} }

View File

@ -8,11 +8,12 @@ using Aurora.Proto.Playback;
using Aurora.Proto.Sync; using Aurora.Proto.Sync;
using Aurora.Services.ClientService.Events; using Aurora.Services.ClientService.Events;
using System.Collections.Generic; using System.Collections.Generic;
using Aurora.Services.Settings;
namespace Aurora.Services.ClientService namespace Aurora.Services.ClientService
{ {
public class ClientService : BaseService<ClientService> public class ClientService : IClientService
{ {
private RemotePartyService.RemotePartyServiceClient _remotePartyClient; private RemotePartyService.RemotePartyServiceClient _remotePartyClient;
private RemoteEventService.RemoteEventServiceClient _remoteEventsClient; private RemoteEventService.RemoteEventServiceClient _remoteEventsClient;
@ -20,17 +21,19 @@ namespace Aurora.Services.ClientService
private RemoteSyncService.RemoteSyncServiceClient _remoteSyncClient; private RemoteSyncService.RemoteSyncServiceClient _remoteSyncClient;
private Channel _channel; private Channel _channel;
CancellationTokenSource _eventCancellationTokenSource; private CancellationTokenSource _eventCancellationTokenSource;
private ISettingsService _settingsService;
public ClientService() public ClientService(ISettingsService settingsService)
{ {
this._settingsService = settingsService;
} }
public MediaPausedEventHandler OnMediaPaused; public MediaPausedEventHandler OnMediaPaused { get; set; }
public NewMediaPlayingEventHandler OnNewMediaPlaying; public NewMediaPlayingEventHandler OnNewMediaPlaying { get; set; }
public PartyMemberJoinedEventHandler OnPartyMemberJoined; public PartyMemberJoinedEventHandler OnPartyMemberJoined { get; set; }
public PartyMemberLeftEventHandler OnPartyMemberLeft; public PartyMemberLeftEventHandler OnPartyMemberLeft { get; set; }
public MediaResumedEventHandler OnMediaResumed; public MediaResumedEventHandler OnMediaResumed { get; set; }
public RemotePartyService.RemotePartyServiceClient RemotePartyClient public RemotePartyService.RemotePartyServiceClient RemotePartyClient
{ {
@ -94,10 +97,10 @@ namespace Aurora.Services.ClientService
public async Task GetEvents() public async Task GetEvents()
{ {
_eventCancellationTokenSource = new CancellationTokenSource(); _eventCancellationTokenSource = new CancellationTokenSource();
string clientId = SettingsService.Instance.ClientId; string clientId = this._settingsService.ClientId;
Console.WriteLine(string.Format("CLIENT {0} - GetEvents called from client with id", clientId)); Console.WriteLine(string.Format("CLIENT {0} - GetEvents called from client with id", clientId));
using (AsyncServerStreamingCall<BaseEvent> eventStream = _remoteEventsClient using (AsyncServerStreamingCall<BaseEvent> eventStream = _remoteEventsClient
.GetEvents(new EventsRequest { ClientId = SettingsService.Instance.ClientId })) .GetEvents(new EventsRequest { ClientId = this._settingsService.ClientId }))
{ {
try try
{ {

View File

@ -0,0 +1,42 @@
using Aurora.Services.ClientService.Events;
using Aurora.Proto.Events;
using Aurora.Proto.Party;
using Aurora.Proto.Playback;
using Aurora.Proto.Sync;
using System.Threading.Tasks;
namespace Aurora.Services.ClientService
{
public interface IClientService
{
MediaPausedEventHandler OnMediaPaused { get; set; }
NewMediaPlayingEventHandler OnNewMediaPlaying { get; set; }
PartyMemberJoinedEventHandler OnPartyMemberJoined { get; set; }
PartyMemberLeftEventHandler OnPartyMemberLeft { get; set; }
MediaResumedEventHandler OnMediaResumed { get; set; }
RemotePartyService.RemotePartyServiceClient RemotePartyClient { get; }
RemoteEventService.RemoteEventServiceClient RemoteEventClient { get; }
RemotePlaybackService.RemotePlaybackServiceClient RemotePlaybackClient { get; }
RemoteSyncService.RemoteSyncServiceClient RemoteSyncClient { get; }
bool IsStarted { get; }
bool IsHost { get; set; }
void Start(string hostname, string port);
void Close();
/// <summary>
/// Asynchronous function for processing events off of the event stream.
/// </summary>
/// <returns></returns>
Task GetEvents();
void StopEvents();
}
}

View File

@ -0,0 +1,64 @@
using System;
using System.Threading.Tasks;
using System.Threading;
using Grpc.Core;
using Aurora.Models.Media;
using Aurora.Proto.Sync;
using LibVLCSharp.Shared;
namespace Aurora.Services.Player
{
public interface IPlayer
{
/// <summary>
/// Event handler for changing playback states.
/// </summary>
event PlaybackStateChangedEventHandler PlaybackStateChanged;
event MediaChangedEventHandler MediaChanged;
/// <summary>
/// The state of playback
/// </summary>
/// <value></value>
PlaybackState PlaybackState { get; }
bool IsLoaded { get; }
bool IsMediaLoaded(BaseMedia media);
BaseMedia CurrentMedia { get; }
float CurrentMediaPosition { get; }
long CurrentMediaLength { get; }
/// <summary>
/// Load media into the media player.
/// </summary>
/// <param name="media">Media to load</param>
Task LoadMedia(BaseMedia media);
/// <summary>
/// Play currently loaded media.
/// </summary>
void Play();
/// <summary>
/// Pause currently loaded media.
/// </summary>
void Pause();
/// <summary>
/// Stop currently loaded media.
/// </summary>
void Stop();
void Enqueue(BaseMedia song);
void Dequeue(BaseMedia song);
}
}

View File

@ -1,7 +1,7 @@
using System; using System;
using Aurora.Models.Media; using Aurora.Models.Media;
namespace Aurora.Services.PlayerService namespace Aurora.Services.Player
{ {
public delegate void MediaChangedEventHandler(object source, MediaChangedEventArgs e); public delegate void MediaChangedEventHandler(object source, MediaChangedEventArgs e);

View File

@ -0,0 +1,12 @@
using System;
namespace Aurora.Services.Player
{
public enum PlaybackState
{
Playing,
Stopped,
Buffering,
}
}

View File

@ -1,7 +1,7 @@
using System; using System;
using Aurora.Models.Media; using Aurora.Models.Media;
namespace Aurora.Services.PlayerService namespace Aurora.Services.Player
{ {
public delegate void PlaybackStateChangedEventHandler(object source, PlaybackStateChangedEventArgs e); public delegate void PlaybackStateChangedEventHandler(object source, PlaybackStateChangedEventArgs e);

View File

@ -6,10 +6,10 @@ using Aurora.Models.Media;
using Aurora.Proto.Sync; using Aurora.Proto.Sync;
using LibVLCSharp.Shared; using LibVLCSharp.Shared;
namespace Aurora.Services.PlayerService namespace Aurora.Services.Player
{ {
public class PlayerService : BaseService<PlayerService> public class PlayerService : BaseService<PlayerService>, IPlayer
{ {
private const long _ticksPerMillisecond = 10000; private const long _ticksPerMillisecond = 10000;
private BaseMedia _currentMedia; private BaseMedia _currentMedia;

View File

@ -1,9 +0,0 @@
using System;
public enum PlaybackState
{
Playing,
Stopped,
Buffering,
}

View File

@ -0,0 +1,38 @@
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Aurora.Proto.PartyV2;
namespace Aurora.Services.Server.Controllers
{
public partial class RemotePartyController : RemotePartyService.RemotePartyServiceBase
{
/// <summary>
/// Constructor for partial class
/// </summary>
public RemotePartyController(string partyName, string description)
{
this._startDateTime = DateTime.UtcNow;
this._displayName = partyName;
this._description = description;
this._memberList = new SortedList<string, Member>();
this._mediaList = new SortedList<string, Models.Media.BaseMedia>();
string userName = "testUser";
this._eventManager = new EventManager.EventManager();
this._hostMember = new Member()
{
Name = GetNewMemberResourceName(_partyResourceName, ServerService.GetLocalIPAddress(), userName),
UserName = userName,
IpAddress = ServerService.GetLocalIPAddress(),
};
this._memberList.Add(_hostMember.Name, _hostMember);
//Add media from library
}
}
}

View File

@ -1,29 +1,158 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Collections.Generic;
using Aurora.Proto.PartyV2; using Aurora.Proto.PartyV2;
using Aurora.Models.Media;
using Aurora.Proto.General;
using Aurora.Services.Player;
using Autofac;
namespace Aurora.Services.Server.Controllers namespace Aurora.Services.Server.Controllers
{ {
public partial class RemotePartyController : RemotePartyService.RemotePartyServiceBase public partial class RemotePartyController : RemotePartyService.RemotePartyServiceBase
{ {
private SortedList<string, BaseMedia> _mediaList;
public override Task<ListMediaResponse> ListMedia(ListMediaRequest request, Grpc.Core.ServerCallContext context) public override Task<ListMediaResponse> ListMedia(ListMediaRequest request, Grpc.Core.ServerCallContext context)
{ {
throw new NotImplementedException(); ListMediaResponse resp = new ListMediaResponse();
int startIdx = 0;
if (!string.IsNullOrEmpty(request.PageToken))
{
startIdx = _memberList.IndexOfKey(request.PageToken) + 1;
}
int pageSize = request.PageSize;
if (pageSize > _mediaList.Count)
{
pageSize = _mediaList.Count;
}
//Gather page
List<BaseMedia> baseMedia = new List<BaseMedia>(_mediaList.Values);
foreach (BaseMedia media in baseMedia)
{
if (media.Metadata is AudioMetadata)
{
AudioMetadata meta = media.Metadata as AudioMetadata;
resp.Media.Add(new Media()
{
Name = media.Id,
Title = meta.Title,
Album = meta.Album,
Artist = meta.Artist,
Duration = meta.Duration
});
}
}
resp.NextPageToken = resp.Media[resp.Media.Count - 1].Name;
return Task.FromResult(resp);
} }
public override Task<RemoteMedia> GetMedia(GetMediaRequest request, Grpc.Core.ServerCallContext context) public override Task<Media> GetMedia(GetMediaRequest request, Grpc.Core.ServerCallContext context)
{
_mediaList.TryGetValue(request.Name, out BaseMedia baseMedia);
if (baseMedia == null)
{
throw new KeyNotFoundException();
}
Media media = new Media();
if (baseMedia.Metadata != null && baseMedia.Metadata is AudioMetadata)
{
AudioMetadata metadata = baseMedia.Metadata as AudioMetadata;
media.Name = baseMedia.Id;
media.Title = metadata.Title;
media.Artist = metadata.Artist;
media.Album = metadata.Album;
}
return Task.FromResult(media);
}
public override Task<Media> CreateMedia(CreateMediaRequest request, Grpc.Core.ServerCallContext context)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override Task StreamMedia(StreamMediaRequest request, Grpc.Core.IServerStreamWriter<Proto.General.Chunk> responseStream, Grpc.Core.ServerCallContext context) public override Task<Empty> DeleteMedia(DeleteMediaRequest request, Grpc.Core.ServerCallContext context)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override Task SyncMedia(SyncMediaRequest request, Grpc.Core.IServerStreamWriter<Sync> responseStream, Grpc.Core.ServerCallContext context) public override async Task StreamMedia(StreamMediaRequest request, Grpc.Core.IServerStreamWriter<Proto.General.Chunk> responseStream, Grpc.Core.ServerCallContext context)
{ {
throw new NotImplementedException(); BaseMedia originalSong = LibraryService.Instance.GetSong(request.Name);
if (!(originalSong is LocalAudio))
{
return;
}
//Copy media object to not interfere with other threads
LocalAudio songCopy = new LocalAudio((LocalAudio)originalSong);
try
{
//Load only if not already loaded. (Multiple clients may be requesting media)
if (!songCopy.IsLoaded)
{
await songCopy.Load();
}
//Send stream
Console.WriteLine("Begin sending file");
byte[] buffer = new byte[2048]; // read in chunks of 2KB
int bytesRead;
while ((bytesRead = songCopy.DataStream.Read(buffer, 0, buffer.Length)) > 0)
{
Google.Protobuf.ByteString bufferByteString = Google.Protobuf.ByteString.CopyFrom(buffer);
await responseStream.WriteAsync(new Chunk { Content = bufferByteString });
}
Console.WriteLine("Done sending file");
}
catch (Exception ex)
{
Console.WriteLine("Exception caught while sending audio file: " + ex.Message);
}
}
public override async Task SyncMedia(SyncMediaRequest request, Grpc.Core.IServerStreamWriter<Sync> responseStream, Grpc.Core.ServerCallContext context)
{
bool continueSync = true;
using (var scope = App.Container.BeginLifetimeScope())
{
IPlayer player = scope.Resolve<IPlayer>();
string currentId = player.CurrentMedia.Id;
MediaChangedEventHandler mediaChanged = (sender, e) =>
{
if (e.NewId != currentId)
{
continueSync = false;
}
};
player.MediaChanged += mediaChanged;
while (continueSync)
{
float length = player.CurrentMediaLength;
Sync sync = new Sync()
{
TrackPosition = player.CurrentMediaPosition,
ServerTimeTicks = Utils.TimeUtils.GetNetworkTime().DateTime.Ticks
};
await responseStream.WriteAsync(sync);
Console.WriteLine("Sent Sync");
await Task.Delay(5000);
}
}
} }
} }
} }

View File

@ -25,19 +25,21 @@ namespace Aurora.Services.Server.Controllers
startIdx = _memberList.IndexOfKey(request.PageToken) + 1; startIdx = _memberList.IndexOfKey(request.PageToken) + 1;
} }
int pageSize = request.PageSize;
//Assign pageSize //Assign pageSize
if (request.PageSize > _memberList.Count) if (pageSize > _memberList.Count)
{ {
request.PageSize = _memberList.Count; pageSize = _memberList.Count;
} }
//Gather page //Gather page
List<Member> members = new List<Member>(_memberList.Values); List<Member> members = new List<Member>(_memberList.Values);
resp.Members.AddRange(members.GetRange(startIdx, request.PageSize)); resp.Members.AddRange(members.GetRange(startIdx, pageSize));
//Set next page token //Set next page token
resp.NextPageToken = resp.Members[(startIdx + request.PageSize) - 1].Name; resp.NextPageToken = resp.Members[resp.Members.Count - 1].Name;
return Task.FromResult(resp); return Task.FromResult(resp);
} }

View File

@ -17,30 +17,6 @@ namespace Aurora.Services.Server.Controllers
private EventManager.EventManager _eventManager; private EventManager.EventManager _eventManager;
/// <summary>
/// Constructor for partial class
/// </summary>
public RemotePartyController(string partyName, string description)
{
this._startDateTime = DateTime.UtcNow;
this._displayName = partyName;
this._description = description;
this._memberList = new SortedList<string, Member>();
string userName = "testUser";
this._eventManager = new EventManager.EventManager();
this._hostMember = new Member()
{
Name = GetNewMemberResourceName(_partyResourceName, ServerService.GetLocalIPAddress(), userName),
UserName = userName,
IpAddress = ServerService.GetLocalIPAddress(),
};
this._memberList.Add(_hostMember.Name, _hostMember);
}
public override Task<Party> GetParty(Proto.General.Empty request, Grpc.Core.ServerCallContext context) public override Task<Party> GetParty(Proto.General.Empty request, Grpc.Core.ServerCallContext context)
{ {
Party party = new Party() Party party = new Party()

View File

@ -8,13 +8,14 @@ using Aurora.Proto.Events;
using Aurora.Proto.Party; using Aurora.Proto.Party;
using Aurora.Proto.Playback; using Aurora.Proto.Playback;
using Aurora.Proto.Sync; using Aurora.Proto.Sync;
using Aurora.Services.Settings;
using Autofac;
namespace Aurora.Services namespace Aurora.Services
{ {
public class ServerService : BaseService<ServerService> public class ServerService : BaseService<ServerService>
{ {
private int _port = SettingsService.Instance.DefaultPort; private int _port;
private string _hostname; private string _hostname;
private Grpc.Core.Server _server; private Grpc.Core.Server _server;
@ -30,6 +31,11 @@ namespace Aurora.Services
public ServerService() public ServerService()
{ {
string host = GetLocalIPAddress(); string host = GetLocalIPAddress();
using (var scope = App.Container.BeginLifetimeScope())
{
var service = scope.Resolve<ISettingsService>();
this._port = service.DefaultPort;
}
if (string.IsNullOrWhiteSpace(host)) if (string.IsNullOrWhiteSpace(host))
{ {

View File

@ -0,0 +1,27 @@
using Plugin.Settings.Abstractions;
namespace Aurora.Services.Settings
{
public interface ISettingsService
{
ISettings AppSettings { get; set; }
/// <summary>
/// The user's username. This is persisted.
/// </summary>
/// <value></value>
string Username { get; set; }
/// <summary>
/// The default port to use. This is persisted.
/// </summary>
/// <value></value>
int DefaultPort { get; set; }
/// <summary>
/// The current sessions clientId. This is assigned by the server. This is not persisted.
/// </summary>
/// <value></value>
string ClientId { get; set; }
}
}

View File

@ -3,9 +3,9 @@ using System.Threading;
using Plugin.Settings; using Plugin.Settings;
using Plugin.Settings.Abstractions; using Plugin.Settings.Abstractions;
namespace Aurora.Services namespace Aurora.Services.Settings
{ {
public class SettingsService : BaseService<SettingsService> public class SettingsService : ISettingsService
{ {
private Lazy<ISettings> _appSettings; private Lazy<ISettings> _appSettings;
private string _usernameKey = "username"; private string _usernameKey = "username";