diff --git a/Aurora.gtk/Aurora.gtk.csproj b/Aurora.gtk/Aurora.gtk.csproj index 34acb22..d80583a 100644 --- a/Aurora.gtk/Aurora.gtk.csproj +++ b/Aurora.gtk/Aurora.gtk.csproj @@ -150,7 +150,7 @@ ..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll - ..\packages\Microsoft.Bcl.AsyncInterfaces.1.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll ..\packages\System.Linq.Async.4.0.0\lib\net461\System.Linq.Async.dll @@ -164,6 +164,10 @@ ..\packages\CarouselView.FormsPlugin.5.2.0\lib\netstandard2.0\CarouselView.FormsPlugin.Abstractions.dll + + ..\packages\Autofac.5.0.0\lib\net461\Autofac.dll + + diff --git a/Aurora.gtk/packages.config b/Aurora.gtk/packages.config index 652707f..45a8c07 100644 --- a/Aurora.gtk/packages.config +++ b/Aurora.gtk/packages.config @@ -1,5 +1,6 @@  + @@ -13,7 +14,7 @@ - + diff --git a/Aurora.sln b/Aurora.sln index 59160f4..2140969 100644 --- a/Aurora.sln +++ b/Aurora.sln @@ -5,6 +5,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aurora", "Aurora\Aurora.csp EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aurora.gtk", "Aurora.gtk\Aurora.gtk.csproj", "{E8C8C24A-5C51-47CB-B241-F5A9F0E808B1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aurora.test", "Aurora.test\Aurora.test.csproj", "{45680D8A-1AF1-4D93-AAC0-59CDB01CED5D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|iPhoneSimulator = Debug|iPhoneSimulator @@ -39,5 +41,17 @@ Global {E8C8C24A-5C51-47CB-B241-F5A9F0E808B1}.Debug|Any CPU.Build.0 = Debug|iPhoneSimulator {E8C8C24A-5C51-47CB-B241-F5A9F0E808B1}.Release|Any CPU.ActiveCfg = Release|iPhoneSimulator {E8C8C24A-5C51-47CB-B241-F5A9F0E808B1}.Release|Any CPU.Build.0 = Release|iPhoneSimulator + {45680D8A-1AF1-4D93-AAC0-59CDB01CED5D}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {45680D8A-1AF1-4D93-AAC0-59CDB01CED5D}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {45680D8A-1AF1-4D93-AAC0-59CDB01CED5D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {45680D8A-1AF1-4D93-AAC0-59CDB01CED5D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {45680D8A-1AF1-4D93-AAC0-59CDB01CED5D}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {45680D8A-1AF1-4D93-AAC0-59CDB01CED5D}.Debug|iPhone.Build.0 = Debug|Any CPU + {45680D8A-1AF1-4D93-AAC0-59CDB01CED5D}.Release|iPhone.ActiveCfg = Release|Any CPU + {45680D8A-1AF1-4D93-AAC0-59CDB01CED5D}.Release|iPhone.Build.0 = Release|Any CPU + {45680D8A-1AF1-4D93-AAC0-59CDB01CED5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {45680D8A-1AF1-4D93-AAC0-59CDB01CED5D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {45680D8A-1AF1-4D93-AAC0-59CDB01CED5D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {45680D8A-1AF1-4D93-AAC0-59CDB01CED5D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Aurora.test/Aurora.test.csproj b/Aurora.test/Aurora.test.csproj new file mode 100644 index 0000000..6c6b85c --- /dev/null +++ b/Aurora.test/Aurora.test.csproj @@ -0,0 +1,117 @@ + + + netcoreapp3.0 + false + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + \ No newline at end of file diff --git a/Aurora.test/ControllerTests/EventTests.cs b/Aurora.test/ControllerTests/EventTests.cs new file mode 100644 index 0000000..513331c --- /dev/null +++ b/Aurora.test/ControllerTests/EventTests.cs @@ -0,0 +1,100 @@ + +using NUnit.Framework; +using Aurora.Proto.Party; +using Aurora.Services.Server; +using Aurora.Services.EventManager; +using Grpc.Core; +using System.Threading.Tasks; +using System.Threading; +using Autofac; +namespace Aurora.test.ControllerTests +{ + public class EventTest + { + private RemotePartyService.RemotePartyServiceClient _remotePartyService; + private Channel _channel; + private IContainer _container; + private IServerService _serverService; + + #region Setup + [SetUp] + public void Setup() + { + _container = SetupUtil.SetupOneTime(); + _remotePartyService = SetupUtil.Setup(ref _container, ref _serverService, ref _channel); + } + + [TearDown] + public async Task TearDown() + { + await _serverService.Stop(); + await _channel.ShutdownAsync(); + _container.Dispose(); + } + #endregion Setup + [Test] + [TestCase(EventType.MediaPlaying)] + public void Asdf(EventType value) + { + Assert.AreEqual(1, 1); + } + + [Test] + [TestCase(EventType.MediaPlaying)] + [TestCase(EventType.MediaStopped)] + [TestCase(EventType.MemberCreated)] + [TestCase(EventType.MemberDeleted)] + public async Task TestEventSubscriptions(EventType value) + { + using (var scope = _container.BeginLifetimeScope()) + { + IEventManager eventManager = scope.Resolve(); + + //Create new party member + Member member = _remotePartyService.CreateMember(new CreateMemberRequest() + { + Parent = "party1", + Member = new Member() + { + UserName = "newMember1", + IpAddress = ServerService.GetLocalIPAddress(), + } + }); + + //Subscribe to event type + _remotePartyService.CreateEventSubscription(new CreateEventSubscriptionRequest() + { + Parent = member.Name, + EventSubscription = new EventSubscription() + { + Type = value + } + }); + + BaseEvent @event = new BaseEvent + { + EventType = value, + }; + //Fire event + + CancellationTokenSource eventCancellationTokenSource = new CancellationTokenSource(); + BaseEvent newEvent = null; + + Task.Run(async () => { await Task.Delay(1000); eventManager.FireEvent(@event); }); + + using (AsyncServerStreamingCall eventStream = _remotePartyService + .GetEvents(new GetEventsRequest() { Parent = member.Name })) + { + while ((!eventCancellationTokenSource.Token.IsCancellationRequested && + await eventStream.ResponseStream.MoveNext(eventCancellationTokenSource.Token))) + { + newEvent = new BaseEvent(eventStream.ResponseStream.Current); + break; + } + }; + + Assert.AreEqual(newEvent.EventType, value); + } + } + } +} \ No newline at end of file diff --git a/Aurora.test/ControllerTests/MediaControllerTest.cs b/Aurora.test/ControllerTests/MediaControllerTest.cs new file mode 100644 index 0000000..cf9e4ba --- /dev/null +++ b/Aurora.test/ControllerTests/MediaControllerTest.cs @@ -0,0 +1,78 @@ +using NUnit.Framework; +using Aurora.Proto.Party; +using Aurora.Services.Server; +using Grpc.Core; +using System.Threading.Tasks; +using System.Linq; +using System.IO; +using Autofac; + +namespace Aurora.test.ControllerTests +{ + public class MediaControllerTests + { + private RemotePartyService.RemotePartyServiceClient _remotePartyService; + private Channel _channel; + private IContainer _container; + private IServerService _serverService; + + #region Setup + [OneTimeSetUp] + public void SetupOneTime() + { + _container = SetupUtil.SetupOneTime(); + } + + [OneTimeTearDown] + public void TearDownOneTime() + { + _container.Dispose(); + } + + [SetUp] + public void Setup() + { + _remotePartyService = SetupUtil.Setup(ref _container, ref _serverService, ref _channel); + } + + [TearDown] + public async Task TearDown() + { + await _serverService.Stop(); + await _channel.ShutdownAsync(); + } + #endregion Setup + + [Test] + public void TestNotEmpty() + { + ListMediaResponse resp = _remotePartyService.ListMedia(new ListMediaRequest() + { + Parent = "testParty", + PageSize = 5 + }); + + Assert.NotNull(resp.Media); + Assert.NotZero(resp.Media.Count); + Assert.AreEqual(resp.Media.Count, 5); + } + + [Test] + public void GetMediaTest() + { + ListMediaResponse resp = _remotePartyService.ListMedia(new ListMediaRequest() + { + Parent = "testParty", + PageSize = 5 + }); + + Media media = _remotePartyService.GetMedia(new GetMediaRequest() + { + Name = resp.Media[0].Name + }); + + Assert.NotNull(media); + Assert.AreEqual(media.Name, resp.Media[0].Name); + } + } +} \ No newline at end of file diff --git a/Aurora.test/ControllerTests/MembersControllerTest.cs b/Aurora.test/ControllerTests/MembersControllerTest.cs new file mode 100644 index 0000000..212ffc2 --- /dev/null +++ b/Aurora.test/ControllerTests/MembersControllerTest.cs @@ -0,0 +1,206 @@ +using NUnit.Framework; +using Aurora.Proto.Party; +using Aurora.Services.Server; +using Grpc.Core; +using System.Threading.Tasks; +using System.Linq; +using Autofac; + +namespace Aurora.test.ControllerTests +{ + public class MemberControllerTests + { + private RemotePartyService.RemotePartyServiceClient _remotePartyService; + private Channel _channel; + + private IContainer _container; + private IServerService _serverService; + + #region Setup + [OneTimeSetUp] + public void SetupOneTime() + { + _container = SetupUtil.SetupOneTime(); + } + + [OneTimeTearDown] + public void TearDownOneTime() + { + _container.Dispose(); + } + + [SetUp] + public void Setup() + { + _remotePartyService = SetupUtil.Setup(ref _container, ref _serverService, ref _channel); + } + + [TearDown] + public async Task TearDown() + { + await _serverService.Stop(); + await _channel.ShutdownAsync(); + } + #endregion Setup + + + [Test] + public void DefaultTest() + { + ListMembersResponse resp = _remotePartyService.ListMembers(new ListMembersRequest() + { + Parent = "party1", + PageSize = 10, + }); + Assert.NotNull(resp); + Assert.GreaterOrEqual(resp.Members.Count, 1); + } + + [Test] + [TestCase("Alex")] + [TestCase("Alex Goldberg")] + [TestCase("Alex/goldberg")] + [TestCase("alex@welcome.com")] + public void CreateMemberTest(string value) + { + Member member = _remotePartyService.CreateMember(new CreateMemberRequest() + { + Parent = "party1", + Member = new Member() + { + UserName = value, + IpAddress = ServerService.GetLocalIPAddress(), + } + }); + + Assert.NotNull(member); + } + + static object[] MultipleMembersCases = + { + new object[] {"Tupac", "Aubrey Grahm", "Beyonce Knowls", "Ke$ha", "A$ap Ferg"}, + }; + + [Test] + [TestCaseSource("MultipleMembersCases")] + public void CreateMultiplMembersTest(object[] memberNames) + { + //Add members + foreach (string name in memberNames) + { + 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 = 10, + }); + Assert.NotNull(resp); + Assert.AreEqual(resp.Members.Count, 6); + } + + [Test] + [TestCaseSource("MultipleMembersCases")] + public void DeleteMemberTest(object[] memberNames) + { + //Add members + foreach (string name in memberNames) + { + 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 = 10, + }); + Assert.NotNull(resp); + Assert.AreEqual(resp.Members.Count, 6); + + string keshaResourceName = resp.Members.First(member => member.UserName == "Ke$ha").Name; + + //Delete member + + _remotePartyService.DeleteMember(new DeleteMemberRequest() + { + Name = keshaResourceName + }); + + //List members + resp = _remotePartyService.ListMembers(new ListMembersRequest() + { + Parent = "party1", + PageSize = 10, + }); + Assert.NotNull(resp); + Assert.AreEqual(resp.Members.Count, 5); + 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); + } + } +} \ No newline at end of file diff --git a/Aurora.test/ControllerTests/PartyControllerTest.cs b/Aurora.test/ControllerTests/PartyControllerTest.cs new file mode 100644 index 0000000..9c10b8d --- /dev/null +++ b/Aurora.test/ControllerTests/PartyControllerTest.cs @@ -0,0 +1,53 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using Aurora.Proto.Party; +using Aurora.Services.Server; +using Grpc.Core; +using Autofac; + +namespace Aurora.test.ControllerTests +{ + public class PartyControllerTests + { + private RemotePartyService.RemotePartyServiceClient _remotePartyService; + private Channel _channel; + private IContainer _container; + private IServerService _serverService; + + #region Setup + [OneTimeSetUp] + public void SetupOneTime() + { + _container = SetupUtil.SetupOneTime(); + } + + [OneTimeTearDown] + public void TearDownOneTime() + { + _container.Dispose(); + } + + [SetUp] + public void Setup() + { + _remotePartyService = SetupUtil.Setup(ref _container, ref _serverService, ref _channel); + } + + [TearDown] + public async Task TearDown() + { + await _serverService.Stop(); + await _channel.ShutdownAsync(); + } + #endregion Setup + + [Test] + public void DefaultTest() + { + Party party = _remotePartyService.GetParty(new Proto.General.Empty()); + + Assert.NotNull(party); + Assert.AreEqual(party.Name, "party/party1"); + } + } +} \ No newline at end of file diff --git a/Aurora.test/ControllerTests/Setup.cs b/Aurora.test/ControllerTests/Setup.cs new file mode 100644 index 0000000..f011bf7 --- /dev/null +++ b/Aurora.test/ControllerTests/Setup.cs @@ -0,0 +1,39 @@ +using Autofac; +using Aurora.Proto.Party; +using Aurora.Services.Server; +using Aurora.Services.Library; +using Aurora.Services.Settings; +using Aurora.Services.EventManager; +using Aurora.test.Models.Mock; +using System.IO; +using Grpc.Core; + +namespace Aurora.test.ControllerTests +{ + public class SetupUtil + { + public static IContainer SetupOneTime() + { + ContainerBuilder builder = new ContainerBuilder(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterInstance(new SettingsServiceMock() + { + Username = "Test User 1", + DefaultPort = 4005, + LibraryLocation = string.Format("{0}/Resources", Directory.GetCurrentDirectory()) + }).SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + return builder.Build(); + } + + public static RemotePartyService.RemotePartyServiceClient Setup(ref IContainer container, ref IServerService serverService, ref Channel channel) + { + serverService = container.Resolve(); + serverService.Start("testParty", "asdf"); + channel = new Channel(string.Format("{0}:{1}", ServerService.GetLocalIPAddress(), 8080), ChannelCredentials.Insecure); + return new RemotePartyService.RemotePartyServiceClient(channel); + } + + } +} \ No newline at end of file diff --git a/Aurora.test/Models/CallContext.cs b/Aurora.test/Models/CallContext.cs new file mode 100644 index 0000000..420c245 --- /dev/null +++ b/Aurora.test/Models/CallContext.cs @@ -0,0 +1,39 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; + +namespace Aurora.test.Models +{ + public class CallContext : ServerCallContext + { + protected override string MethodCore => throw new NotImplementedException(); + + protected override string HostCore => throw new NotImplementedException(); + + protected override string PeerCore => throw new NotImplementedException(); + + protected override DateTime DeadlineCore => throw new NotImplementedException(); + + protected override Metadata RequestHeadersCore => throw new NotImplementedException(); + + protected override CancellationToken CancellationTokenCore => throw new NotImplementedException(); + + protected override Metadata ResponseTrailersCore => throw new NotImplementedException(); + + protected override Status StatusCore { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + protected override WriteOptions WriteOptionsCore { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + protected override AuthContext AuthContextCore => throw new NotImplementedException(); + + protected override ContextPropagationToken CreatePropagationTokenCore(ContextPropagationOptions options) + { + throw new NotImplementedException(); + } + + protected override Task WriteResponseHeadersAsyncCore(Metadata responseHeaders) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Aurora.test/Models/Mock/SettingsServiceMock.cs b/Aurora.test/Models/Mock/SettingsServiceMock.cs new file mode 100644 index 0000000..6126c92 --- /dev/null +++ b/Aurora.test/Models/Mock/SettingsServiceMock.cs @@ -0,0 +1,35 @@ +using Aurora.Services.Settings; +using Plugin.Settings.Abstractions; + +namespace Aurora.test.Models.Mock +{ + public class SettingsServiceMock : ISettingsService + { + public SettingsServiceMock() + { + + } + + public ISettings AppSettings { get; set; } + + /// + /// The user's username. This is persisted. + /// + /// + public string Username { get; set; } + + /// + /// The default port to use. This is persisted. + /// + /// + public int DefaultPort { get; set; } + + /// + /// The current sessions clientId. This is assigned by the server. This is not persisted. + /// + /// + public string ClientName { get; set; } + + public string LibraryLocation { get; set; } + } +} \ No newline at end of file diff --git a/Aurora.test/Resources/Jidenna/The Chief/01 A Bull's Tale.mp3 b/Aurora.test/Resources/Jidenna/The Chief/01 A Bull's Tale.mp3 new file mode 100755 index 0000000..4378585 Binary files /dev/null and b/Aurora.test/Resources/Jidenna/The Chief/01 A Bull's Tale.mp3 differ diff --git a/Aurora.test/Resources/Jidenna/The Chief/02 Chief Don't Run.mp3 b/Aurora.test/Resources/Jidenna/The Chief/02 Chief Don't Run.mp3 new file mode 100755 index 0000000..dc40b81 Binary files /dev/null and b/Aurora.test/Resources/Jidenna/The Chief/02 Chief Don't Run.mp3 differ diff --git a/Aurora.test/Resources/Jidenna/The Chief/03 Trampoline.mp3 b/Aurora.test/Resources/Jidenna/The Chief/03 Trampoline.mp3 new file mode 100755 index 0000000..dea9b1f Binary files /dev/null and b/Aurora.test/Resources/Jidenna/The Chief/03 Trampoline.mp3 differ diff --git a/Aurora.test/Resources/Jidenna/The Chief/04 Bambi.mp3 b/Aurora.test/Resources/Jidenna/The Chief/04 Bambi.mp3 new file mode 100755 index 0000000..0ea6edd Binary files /dev/null and b/Aurora.test/Resources/Jidenna/The Chief/04 Bambi.mp3 differ diff --git a/Aurora.test/Resources/Jidenna/The Chief/05 Helicopters _ Beware.mp3 b/Aurora.test/Resources/Jidenna/The Chief/05 Helicopters _ Beware.mp3 new file mode 100755 index 0000000..457fe3f Binary files /dev/null and b/Aurora.test/Resources/Jidenna/The Chief/05 Helicopters _ Beware.mp3 differ diff --git a/Aurora.test/Resources/Jidenna/The Chief/06 Long Live The Chief.mp3 b/Aurora.test/Resources/Jidenna/The Chief/06 Long Live The Chief.mp3 new file mode 100755 index 0000000..04ff67d Binary files /dev/null and b/Aurora.test/Resources/Jidenna/The Chief/06 Long Live The Chief.mp3 differ diff --git a/Aurora.test/Resources/Jidenna/The Chief/07 2 Points.mp3 b/Aurora.test/Resources/Jidenna/The Chief/07 2 Points.mp3 new file mode 100755 index 0000000..6501991 Binary files /dev/null and b/Aurora.test/Resources/Jidenna/The Chief/07 2 Points.mp3 differ diff --git a/Aurora.test/Resources/Jidenna/The Chief/08 The Let Out.mp3 b/Aurora.test/Resources/Jidenna/The Chief/08 The Let Out.mp3 new file mode 100755 index 0000000..9509e2d Binary files /dev/null and b/Aurora.test/Resources/Jidenna/The Chief/08 The Let Out.mp3 differ diff --git a/Aurora.test/Resources/Jidenna/The Chief/09 Safari.mp3 b/Aurora.test/Resources/Jidenna/The Chief/09 Safari.mp3 new file mode 100755 index 0000000..64a4e6c Binary files /dev/null and b/Aurora.test/Resources/Jidenna/The Chief/09 Safari.mp3 differ diff --git a/Aurora.test/Resources/Jidenna/The Chief/10 Adaora.mp3 b/Aurora.test/Resources/Jidenna/The Chief/10 Adaora.mp3 new file mode 100755 index 0000000..22d0f69 Binary files /dev/null and b/Aurora.test/Resources/Jidenna/The Chief/10 Adaora.mp3 differ diff --git a/Aurora.test/Resources/Jidenna/The Chief/11 Little Bit More.mp3 b/Aurora.test/Resources/Jidenna/The Chief/11 Little Bit More.mp3 new file mode 100755 index 0000000..7cf9c19 Binary files /dev/null and b/Aurora.test/Resources/Jidenna/The Chief/11 Little Bit More.mp3 differ diff --git a/Aurora.test/Resources/Jidenna/The Chief/12 Some Kind Of Way.mp3 b/Aurora.test/Resources/Jidenna/The Chief/12 Some Kind Of Way.mp3 new file mode 100755 index 0000000..1b81852 Binary files /dev/null and b/Aurora.test/Resources/Jidenna/The Chief/12 Some Kind Of Way.mp3 differ diff --git a/Aurora.test/Resources/Jidenna/The Chief/13 White Niggas.mp3 b/Aurora.test/Resources/Jidenna/The Chief/13 White Niggas.mp3 new file mode 100755 index 0000000..ccf5195 Binary files /dev/null and b/Aurora.test/Resources/Jidenna/The Chief/13 White Niggas.mp3 differ diff --git a/Aurora.test/Resources/Jidenna/The Chief/14 Bully Of The Earth.mp3 b/Aurora.test/Resources/Jidenna/The Chief/14 Bully Of The Earth.mp3 new file mode 100755 index 0000000..784ef6f Binary files /dev/null and b/Aurora.test/Resources/Jidenna/The Chief/14 Bully Of The Earth.mp3 differ diff --git a/Aurora.test/Resources/Mac Miller/Best Day Ever/01 Best Day Ever (Prod. By_ ID Labs).m4a b/Aurora.test/Resources/Mac Miller/Best Day Ever/01 Best Day Ever (Prod. By_ ID Labs).m4a new file mode 100755 index 0000000..5c002a1 Binary files /dev/null and b/Aurora.test/Resources/Mac Miller/Best Day Ever/01 Best Day Ever (Prod. By_ ID Labs).m4a differ diff --git a/Aurora.test/Resources/Mac Miller/Best Day Ever/02 Get Up (Prod. By_ Teddy Roxpin).m4a b/Aurora.test/Resources/Mac Miller/Best Day Ever/02 Get Up (Prod. By_ Teddy Roxpin).m4a new file mode 100755 index 0000000..96fbcb3 Binary files /dev/null and b/Aurora.test/Resources/Mac Miller/Best Day Ever/02 Get Up (Prod. By_ Teddy Roxpin).m4a differ diff --git a/Aurora.test/Resources/Mac Miller/Best Day Ever/03 Donald Trump (Prod. By_ Sap).m4a b/Aurora.test/Resources/Mac Miller/Best Day Ever/03 Donald Trump (Prod. By_ Sap).m4a new file mode 100755 index 0000000..5ad4e13 Binary files /dev/null and b/Aurora.test/Resources/Mac Miller/Best Day Ever/03 Donald Trump (Prod. By_ Sap).m4a differ diff --git a/Aurora.test/Resources/Mac Miller/Best Day Ever/04 Oy Vey (Prod By_ ID Labs).mp3 b/Aurora.test/Resources/Mac Miller/Best Day Ever/04 Oy Vey (Prod By_ ID Labs).mp3 new file mode 100755 index 0000000..c758aa5 Binary files /dev/null and b/Aurora.test/Resources/Mac Miller/Best Day Ever/04 Oy Vey (Prod By_ ID Labs).mp3 differ diff --git a/Aurora.test/Resources/Mac Miller/Best Day Ever/05 I'll Be There (feat. Phonte) (Prod. By_ Beanz 'n' Kornbread).mp3 b/Aurora.test/Resources/Mac Miller/Best Day Ever/05 I'll Be There (feat. Phonte) (Prod. By_ Beanz 'n' Kornbread).mp3 new file mode 100755 index 0000000..1edcf91 Binary files /dev/null and b/Aurora.test/Resources/Mac Miller/Best Day Ever/05 I'll Be There (feat. Phonte) (Prod. By_ Beanz 'n' Kornbread).mp3 differ diff --git a/Aurora.test/Resources/Mac Miller/Best Day Ever/06 Wear My Hat (Prod. By_ Chuck Inglish).m4a b/Aurora.test/Resources/Mac Miller/Best Day Ever/06 Wear My Hat (Prod. By_ Chuck Inglish).m4a new file mode 100755 index 0000000..5410711 Binary files /dev/null and b/Aurora.test/Resources/Mac Miller/Best Day Ever/06 Wear My Hat (Prod. By_ Chuck Inglish).m4a differ diff --git a/Aurora.test/Resources/Mac Miller/Best Day Ever/07 Wake Up (Prod By_ Sap & ID Labs).m4a b/Aurora.test/Resources/Mac Miller/Best Day Ever/07 Wake Up (Prod By_ Sap & ID Labs).m4a new file mode 100755 index 0000000..8e94ac6 Binary files /dev/null and b/Aurora.test/Resources/Mac Miller/Best Day Ever/07 Wake Up (Prod By_ Sap & ID Labs).m4a differ diff --git a/Aurora.test/Resources/Mac Miller/Best Day Ever/08 All Around The World (Prod. By_ Just Blaze).m4a b/Aurora.test/Resources/Mac Miller/Best Day Ever/08 All Around The World (Prod. By_ Just Blaze).m4a new file mode 100755 index 0000000..c2cfe88 Binary files /dev/null and b/Aurora.test/Resources/Mac Miller/Best Day Ever/08 All Around The World (Prod. By_ Just Blaze).m4a differ diff --git a/Aurora.test/Resources/Mac Miller/Best Day Ever/09 Down The Rabbit Hole (Prod. By_ Blue of The Sore Losers).m4a b/Aurora.test/Resources/Mac Miller/Best Day Ever/09 Down The Rabbit Hole (Prod. By_ Blue of The Sore Losers).m4a new file mode 100755 index 0000000..85ccc30 Binary files /dev/null and b/Aurora.test/Resources/Mac Miller/Best Day Ever/09 Down The Rabbit Hole (Prod. By_ Blue of The Sore Losers).m4a differ diff --git a/Aurora.test/Resources/Mac Miller/Best Day Ever/10 In The Air (Prod By_ Ritz Reynolds).m4a b/Aurora.test/Resources/Mac Miller/Best Day Ever/10 In The Air (Prod By_ Ritz Reynolds).m4a new file mode 100755 index 0000000..96f324e Binary files /dev/null and b/Aurora.test/Resources/Mac Miller/Best Day Ever/10 In The Air (Prod By_ Ritz Reynolds).m4a differ diff --git a/Aurora.test/Resources/Mac Miller/Best Day Ever/11 Play Ya Cards (Prod By_ Chuck Inglish).m4a b/Aurora.test/Resources/Mac Miller/Best Day Ever/11 Play Ya Cards (Prod By_ Chuck Inglish).m4a new file mode 100755 index 0000000..b8694c5 Binary files /dev/null and b/Aurora.test/Resources/Mac Miller/Best Day Ever/11 Play Ya Cards (Prod By_ Chuck Inglish).m4a differ diff --git a/Aurora.test/Resources/Mac Miller/Best Day Ever/12 She Said (Prod By_ Khrysis).m4a b/Aurora.test/Resources/Mac Miller/Best Day Ever/12 She Said (Prod By_ Khrysis).m4a new file mode 100755 index 0000000..d1917d7 Binary files /dev/null and b/Aurora.test/Resources/Mac Miller/Best Day Ever/12 She Said (Prod By_ Khrysis).m4a differ diff --git a/Aurora.test/Resources/Mac Miller/Best Day Ever/13 Life Ain't Easy (Prod. By_ ID Labs).m4a b/Aurora.test/Resources/Mac Miller/Best Day Ever/13 Life Ain't Easy (Prod. By_ ID Labs).m4a new file mode 100755 index 0000000..afa0d89 Binary files /dev/null and b/Aurora.test/Resources/Mac Miller/Best Day Ever/13 Life Ain't Easy (Prod. By_ ID Labs).m4a differ diff --git a/Aurora.test/Resources/Mac Miller/Best Day Ever/14 Snooze (Prod By_ ID Labs).m4a b/Aurora.test/Resources/Mac Miller/Best Day Ever/14 Snooze (Prod By_ ID Labs).m4a new file mode 100755 index 0000000..f1573a8 Binary files /dev/null and b/Aurora.test/Resources/Mac Miller/Best Day Ever/14 Snooze (Prod By_ ID Labs).m4a differ diff --git a/Aurora.test/Resources/Mac Miller/Best Day Ever/15 Keep Floatin' (feat. Wiz Khalifa) (Prod. By_ ID Labs).mp3 b/Aurora.test/Resources/Mac Miller/Best Day Ever/15 Keep Floatin' (feat. Wiz Khalifa) (Prod. By_ ID Labs).mp3 new file mode 100755 index 0000000..c813526 Binary files /dev/null and b/Aurora.test/Resources/Mac Miller/Best Day Ever/15 Keep Floatin' (feat. Wiz Khalifa) (Prod. By_ ID Labs).mp3 differ diff --git a/Aurora.test/Resources/Mac Miller/Best Day Ever/16 BDE Bonus (Prod. By_ ID Labs).mp3 b/Aurora.test/Resources/Mac Miller/Best Day Ever/16 BDE Bonus (Prod. By_ ID Labs).mp3 new file mode 100755 index 0000000..1eb1a11 Binary files /dev/null and b/Aurora.test/Resources/Mac Miller/Best Day Ever/16 BDE Bonus (Prod. By_ ID Labs).mp3 differ diff --git a/Aurora/App.xaml.cs b/Aurora/App.xaml.cs index cc5f24c..487ba7d 100644 --- a/Aurora/App.xaml.cs +++ b/Aurora/App.xaml.cs @@ -1,19 +1,57 @@ using System; 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.EventManager; +using Aurora.Services.Server; +using Aurora.Services.Client; +using Autofac; using LibVLCSharp.Shared; using Xamarin.Forms; -using Xamarin.Forms.Xaml; +using Aurora.Services.Player; +using Aurora.Services.Settings; +using Aurora.Services.Library; namespace Aurora { public partial class App : Application { + private static IContainer _container; public App() { InitializeComponent(); Core.Initialize(); - MainPage = new MainView(); + //Register DI + ContainerBuilder _builder = new ContainerBuilder(); + // _builder.RegisterInstance(new PlayerService()).SingleInstance(); + _builder.RegisterType().As().SingleInstance(); + _builder.RegisterType().As().SingleInstance(); + _builder.RegisterType().As().SingleInstance(); + _builder.RegisterType().As().SingleInstance(); + _builder.RegisterType().As().SingleInstance(); + _builder.RegisterType().As().SingleInstance(); + _builder.RegisterType().SingleInstance(); + _builder.RegisterType(); + _builder.RegisterType(); + _builder.RegisterType(); + _builder.RegisterType(); + _builder.RegisterType(); + _builder.RegisterType(); + + // _builder.RegisterInstance(new SettingsService()).SingleInstance(); + _container = _builder.Build(); + + MainPage = _container.Resolve(); + } + + public static IContainer Container + { + get { return _container; } } protected override void OnStart() diff --git a/Aurora/Aurora.csproj b/Aurora/Aurora.csproj index 1c6f392..06593eb 100644 --- a/Aurora/Aurora.csproj +++ b/Aurora/Aurora.csproj @@ -23,6 +23,7 @@ + @@ -51,7 +52,7 @@ - + @@ -61,15 +62,12 @@ - - - - - - - + + + + @@ -77,11 +75,11 @@ - - - - - - + + + + + + \ No newline at end of file diff --git a/Aurora/Design/Components/MemberList/MemberList.xaml.cs b/Aurora/Design/Components/MemberList/MemberList.xaml.cs index 7a61fe4..0d09652 100644 --- a/Aurora/Design/Components/MemberList/MemberList.xaml.cs +++ b/Aurora/Design/Components/MemberList/MemberList.xaml.cs @@ -24,7 +24,7 @@ namespace Aurora.Design.Components.MemberList /// public static readonly BindableProperty MembersProperty = BindableProperty.Create(propertyName: "Members", - returnType: typeof(ObservableCollection), + returnType: typeof(ObservableCollection), declaringType: typeof(MemberList), defaultBindingMode: BindingMode.Default, propertyChanged: OnMembersChanged); @@ -33,11 +33,11 @@ namespace Aurora.Design.Components.MemberList /// Backing property for MembersProperty /// /// - public ObservableCollection Members + public ObservableCollection Members { get { - return (ObservableCollection)GetValue(MembersProperty); + return (ObservableCollection)GetValue(MembersProperty); } set { diff --git a/Aurora/Design/Views/MainView/MainView.xaml.cs b/Aurora/Design/Views/MainView/MainView.xaml.cs index a008571..67d7f16 100644 --- a/Aurora/Design/Views/MainView/MainView.xaml.cs +++ b/Aurora/Design/Views/MainView/MainView.xaml.cs @@ -9,8 +9,8 @@ using Xamarin.Forms; using Xamarin.Forms.Xaml; using Aurora.Models.Media; using Aurora.Design.Components.MediaPlayer; -using Aurora.Services.PlayerService; -using System.Threading; +using Aurora.Services.Player; +using Autofac; namespace Aurora.Design.Views.Main { @@ -38,10 +38,10 @@ namespace Aurora.Design.Views.Main private Dictionary _viewModels; private BaseViewModel _lastViewModel; private Player _playerComponent; - private PlayerService _playerService; + private IPlayer _playerService; private ContentPresenter _viewContent; - public MainView() + public MainView(IPlayer player) { InitializeComponent(); BindingContext = new MainViewModel(); @@ -50,7 +50,7 @@ namespace Aurora.Design.Views.Main _playerComponent = Player; _viewContent = (ContentPresenter)Content.FindByName("ViewContent"); - _playerService = PlayerService.Instance; + _playerService = player; MasterPage.ListView.ItemSelected += OnNavItemSelected; @@ -89,7 +89,7 @@ namespace Aurora.Design.Views.Main } //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); } @@ -135,7 +135,7 @@ namespace Aurora.Design.Views.Main else { //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); } diff --git a/Aurora/Design/Views/Party/PartyViewModel.cs b/Aurora/Design/Views/Party/PartyViewModel.cs index b9daa6d..d047e63 100644 --- a/Aurora/Design/Views/Party/PartyViewModel.cs +++ b/Aurora/Design/Views/Party/PartyViewModel.cs @@ -1,18 +1,18 @@ using System; using System.Collections.ObjectModel; +using System.Collections.Generic; using System.Threading.Tasks; +using System.Threading; using System.Linq; using Xamarin.Forms; -using Aurora.Services; -using Aurora.Proto.General; using Aurora.Proto.Party; -using Aurora.Proto.Events; -using Aurora.Services.ClientService; -using Aurora.Services.ClientService.Events; -using Aurora.Services.PlayerService; -using Aurora.Services.EventManager; using Aurora.Models.Media; +using Aurora.Services.Client; using Aurora.Design.Views.Party.NewPartyDialog; +using Aurora.Services.Settings; +using Aurora.Services.Server; +using Aurora.Services.EventManager; +using Grpc.Core; namespace Aurora.Design.Views.Party { @@ -24,42 +24,55 @@ namespace Aurora.Design.Views.Party Hosting, Connecting, } - + delegate void EventHandler(BaseEvent e); public class PartyViewModel : BaseViewModel { private PartyState _state; private string _hostname = ""; - private ObservableCollection _members; + private ObservableCollection _members; private ObservableCollection _queue; private BaseMedia _selectedMedia; - private ClientService _client; + // private IClientService _client; + private ISettingsService _settingsService; + private IClientService _clientService; + private IServerService _serverService; + private IEventManager _eventManager; + + private CancellationTokenSource _eventCancellationTokenSource; + + private Dictionary _eventHandlers; private int _selectedTabIndex; - public PartyViewModel() + public PartyViewModel( + ISettingsService settingsService, + IServerService serverService, + IEventManager eventManager, + IClientService clientService) { - _members = new ObservableCollection(); + _members = new ObservableCollection(); _queue = new ObservableCollection(); + this._settingsService = settingsService; + this._serverService = serverService; + this._eventManager = eventManager; + this._clientService = clientService; + SetState(PartyState.SelectingHost); PlayCommand = new Command(OnDoubleClickCommandExecute, CanDoubleClickCommandExecute); LeavePartyCommand = new Command(OnLeavePartyCommandExecute, CanLeavePartyCommandExecute); - _client = ClientService.Instance; - - _client.OnMediaPaused += this.OnRemoteMediaPaused; - _client.OnMediaResumed += this.OnRemoteMediaResumed; - _client.OnNewMediaPlaying += this.OnNewRemoteMediaPlaying; - _client.OnPartyMemberJoined += this.OnPartyMemberJoined; - _client.OnPartyMemberLeft += this.OnPartyMemberLeft; - - } - - ~PartyViewModel() - { - //Task.Run(ServerService.Instance.Stop); + //Setup event handlers + _eventHandlers = new Dictionary() + { + {BaseEvent.DerivedEventOneofCase.MediaPausedEvent, this.OnRemoteMediaPaused}, + {BaseEvent.DerivedEventOneofCase.MediaResumedEvent, this.OnRemoteMediaResumed}, + {BaseEvent.DerivedEventOneofCase.NewMediaPlayingEvent, this.OnNewRemoteMediaPlaying}, + {BaseEvent.DerivedEventOneofCase.MemberCreatedEvent, this.OnPartyMemberJoined}, + {BaseEvent.DerivedEventOneofCase.MemberDeletedEvent, this.OnPartyMemberLeft} + }; } #region Properties @@ -74,7 +87,7 @@ namespace Aurora.Design.Views.Party /// Publc property for the members list /// /// - public ObservableCollection Members + public ObservableCollection Members { get { @@ -136,7 +149,7 @@ namespace Aurora.Design.Views.Party if (this._state == PartyState.Hosting || this._state == PartyState.InParty) { - await _client.GetEvents().ConfigureAwait(false); + await this.GetEvents().ConfigureAwait(false); } else { @@ -173,7 +186,7 @@ namespace Aurora.Design.Views.Party /// public override Task OnInactive() { - _client.StopEvents(); + this._eventCancellationTokenSource.Cancel(); return Task.FromResult(null); } @@ -182,7 +195,7 @@ namespace Aurora.Design.Views.Party /// /// /// - public void OnRemoteMediaPaused(object sender, MediaPausedEventArgs args) + public void OnRemoteMediaPaused(BaseEvent e) { StopPlaying(); } @@ -192,9 +205,9 @@ namespace Aurora.Design.Views.Party /// /// /// - public void OnNewRemoteMediaPlaying(object sender, NewMediaPlayingEventArgs args) + public void OnNewRemoteMediaPlaying(BaseEvent e) { - PlayFromBeginning(GetMediaFromQueue(args.Event.Media.Id)); + PlayFromBeginning(GetMediaFromQueue(e.NewMediaPlayingEvent.Media.Name)); } /// @@ -202,7 +215,7 @@ namespace Aurora.Design.Views.Party /// /// /// - public void OnRemoteMediaResumed(object sender, MediaResumedEventArgs args) + public void OnRemoteMediaResumed(BaseEvent e) { PlayResume(); } @@ -212,17 +225,9 @@ namespace Aurora.Design.Views.Party /// /// /// - public void OnPartyMemberJoined(object sender, PartyMemberJoinedEventArgs args) + public void OnPartyMemberJoined(BaseEvent e) { - PartyMember member = new PartyMember - { - UserName = args.Event.Member.UserName, - Id = args.Event.Member.Id, - IpAddress = args.Event.Member.IpAddress, - Port = args.Event.Member.Port - }; - - Members.Add(member); + Members.Add(e.MemberCreatedEvent.Member); } /// @@ -230,10 +235,10 @@ namespace Aurora.Design.Views.Party /// /// /// - public void OnPartyMemberLeft(object sender, PartyMemberLeftEventArgs args) + public void OnPartyMemberLeft(BaseEvent e) { - var found = Members.Where(x => x.Id == args.Event.Member.Id); - foreach (PartyMember member in found) + var found = Members.Where(x => x.Name == e.MemberDeletedEvent.MemberName); + foreach (Member member in found) { _members.Remove(member); } @@ -245,14 +250,14 @@ namespace Aurora.Design.Views.Party private async void OnJoinCommandExecute() { SetState(PartyState.Connecting); - _client.Start(_hostname, SettingsService.Instance.DefaultPort.ToString()); + _clientService.Start(_hostname, this._settingsService.DefaultPort.ToString()); await JoinParty(false); //TODO add cancellation token try { SetState(PartyState.InParty); - await _client.GetEvents().ConfigureAwait(true); + await GetEvents().ConfigureAwait(true); } catch (Exception ex) { @@ -269,10 +274,9 @@ namespace Aurora.Design.Views.Party { //Change state SetState(PartyState.Connecting); - ServerService.Instance.Start(); + _serverService.Start("test", "asdf"); string localHost = ServerService.GetLocalIPAddress(); - _client.IsHost = true; - _client.Start(localHost, SettingsService.Instance.DefaultPort.ToString()); + _clientService.Start(localHost, this._settingsService.DefaultPort.ToString()); await JoinParty(true); @@ -280,7 +284,7 @@ namespace Aurora.Design.Views.Party try { SetState(PartyState.Hosting); - await _client.GetEvents().ConfigureAwait(true); + await GetEvents().ConfigureAwait(false); } catch (Exception ex) { @@ -295,7 +299,10 @@ namespace Aurora.Design.Views.Party private async void OnLeavePartyCommandExecute() { - await _client.RemotePartyClient.LeavePartyAsync(new LeavePartyRequest()); + await _clientService.RemotePartyServiceClient.DeleteMemberAsync(new DeleteMemberRequest() + { + Name = _settingsService.ClientName + }); } private bool CanLeavePartyCommandExecute() @@ -311,7 +318,7 @@ namespace Aurora.Design.Views.Party AudioMetadata meta = _selectedMedia.Metadata as AudioMetadata; MediaPausedEvent mediaPaused = new MediaPausedEvent(); - EventManager.Instance.FireEvent(new BaseEvent() + _eventManager.FireEvent(new BaseEvent() { MediaPausedEvent = mediaPaused }); @@ -322,7 +329,7 @@ namespace Aurora.Design.Views.Party AudioMetadata meta = _selectedMedia.Metadata as AudioMetadata; MediaResumedEvent mediaResumed = new MediaResumedEvent(); - EventManager.Instance.FireEvent(new BaseEvent() + _eventManager.FireEvent(new BaseEvent() { MediaResumedEvent = mediaResumed }); @@ -353,16 +360,17 @@ namespace Aurora.Design.Views.Party AudioMetadata meta = _selectedMedia.Metadata as AudioMetadata; NewMediaPlayingEvent mediaPlaying = new NewMediaPlayingEvent() { - Media = new RemoteMediaData() + Media = new Media() { - Id = _selectedMedia.Id, + //TODO need full resource name + Name = _selectedMedia.Id, Title = meta.Title, Artist = meta.Artist, Album = meta.Album, } }; - EventManager.Instance.FireEvent(new BaseEvent() + _eventManager.FireEvent(new BaseEvent() { NewMediaPlayingEvent = mediaPlaying }); @@ -385,24 +393,31 @@ namespace Aurora.Design.Views.Party { try { - JoinPartyResponse resp = await _client.RemotePartyClient.JoinPartyAsync(new JoinPartyRequest + Member resp = await _clientService.RemotePartyServiceClient.CreateMemberAsync(new CreateMemberRequest { - UserName = SettingsService.Instance.Username, + Member = new Member() + { + UserName = this._settingsService.Username, + } }); - SettingsService.Instance.ClientId = resp.ClientId; + this._settingsService.ClientName = resp.Name; - RefreshMembers(); + await RefreshMembers(); //Subscribe to events await SubscribeToEvents(); Queue.Clear(); - QueueResponse queueResponse = _client.RemotePartyClient.GetQueue(new Empty()); + ListMediaResponse mediaResponse = await _clientService.RemotePartyServiceClient.ListMediaAsync(new ListMediaRequest() + { + PageSize = 50, + Parent = "TODO" + }); //Convert received data to remote audio models - foreach (RemoteMediaData data in queueResponse.MediaList) + foreach (Media data in mediaResponse.Media) { //Assign received metadata (since this can't be aquired from a file) AudioMetadata meta = new AudioMetadata(); @@ -411,11 +426,10 @@ namespace Aurora.Design.Views.Party meta.Artist = data.Artist; meta.Duration = data.Duration; - RemoteAudio remote = new RemoteAudio(data.Id, + RemoteAudio remote = new RemoteAudio(data.Name, asHost, meta, - _client.RemotePlaybackClient, - _client.RemoteSyncClient); + _clientService.RemotePartyServiceClient); Queue.Add(remote); OnPropertyChanged("Queue"); @@ -430,47 +444,52 @@ namespace Aurora.Design.Views.Party private async Task LeaveParty() { //Stop receiving events - _client.StopEvents(); + // _client.StopEvents(); //Unsubscribe await UnsubscribeFromEvents(); //Leave party - LeavePartyRequest leaveReq = new LeavePartyRequest(); - await _client.RemotePartyClient.LeavePartyAsync(leaveReq); + DeleteMemberRequest req = new DeleteMemberRequest() + { + Name = _settingsService.ClientName + }; + + await _clientService.RemotePartyServiceClient.DeleteMemberAsync(req); } private async Task SubscribeToEvents() { - SubscribeRequest req = new SubscribeRequest(); - req.EventTypes.Add(EventType.PartyMemberJoined); - req.EventTypes.Add(EventType.PartyMemberLeft); - req.EventTypes.Add(EventType.MediaPlaying); - req.EventTypes.Add(EventType.MediaStopped); - if (!string.IsNullOrWhiteSpace(SettingsService.Instance.ClientId)) - { - req.ClientId = SettingsService.Instance.ClientId; - } + CreateEventSubscriptionListRequest req = new CreateEventSubscriptionListRequest(); + req.Parent = this._settingsService.ClientName; + req.EventSubscriptions.Add(new EventSubscription() { Type = EventType.MemberCreated }); + req.EventSubscriptions.Add(new EventSubscription() { Type = EventType.MemberDeleted }); + req.EventSubscriptions.Add(new EventSubscription() { Type = EventType.MediaPlaying }); + req.EventSubscriptions.Add(new EventSubscription() { Type = EventType.MediaStopped }); - - Console.WriteLine(string.Format("CLIENT {0} - SubscribeToEvents called from client with id", SettingsService.Instance.ClientId)); - await _client.RemoteEventClient.SubscribeToEventsAsync(req); + Console.WriteLine(string.Format("CLIENT {0} - SubscribeToEvents called from client with id", this._settingsService.ClientName)); + await _clientService.RemotePartyServiceClient.CreateEventSubscriptionListAsync(req); } private async Task UnsubscribeFromEvents() { - UnsubscribeAllRequest unsubscribeReq = new UnsubscribeAllRequest(); - await _client.RemoteEventClient.UnsubscribeFromAllAsync(unsubscribeReq); + DeleteAllEventSubscriptionsRequest unsubscribeReq = new DeleteAllEventSubscriptionsRequest(); + await _clientService.RemotePartyServiceClient.DeleteAllEventSubscriptionsAsync(unsubscribeReq); } /// /// Refresh members list. /// - private void RefreshMembers() + private async Task RefreshMembers() { Members.Clear(); - MembersResponse response = _client.RemotePartyClient.GetPartyMembers(new Empty()); + ListMembersResponse response = await _clientService.RemotePartyServiceClient.ListMembersAsync( + new ListMembersRequest() + { + Parent = "TODO", + PageSize = 50, + }); //Add members - foreach (PartyMember member in response.Members) + foreach (Member member in response.Members) { Members.Add(member); } @@ -510,6 +529,47 @@ namespace Aurora.Design.Views.Party base.ChangePlayerState(null, Main.PlayAction.Pause); } + /// + /// Asynchronous function for processing events off of the event stream. + /// + /// + public async Task GetEvents() + { + _eventCancellationTokenSource = new CancellationTokenSource(); + string clientName = this._settingsService.ClientName; + Console.WriteLine(string.Format("CLIENT {0} - GetEvents called from client with id", clientName)); + using (AsyncServerStreamingCall eventStream = _clientService.RemotePartyServiceClient + .GetEvents(new GetEventsRequest { Parent = this._settingsService.ClientName })) + { + try + { + while (!_eventCancellationTokenSource.Token.IsCancellationRequested && + await eventStream.ResponseStream.MoveNext(_eventCancellationTokenSource.Token)) + { + try + { + BaseEvent e = new BaseEvent(eventStream.ResponseStream.Current); + + _eventHandlers.TryGetValue(e.DerivedEventCase, out EventHandler handler); + + if (handler != null && handler != null) + { + handler.Invoke(e); + } + } + catch (Exception ex) + { + Console.WriteLine("Exception while parsing event ---" + ex.Message); + } + + } + } + catch (Exception ex) + { + Console.WriteLine(string.Format("EXCEPTION while parsing events --- " + ex.Message)); + } + } + } #endregion Private Methods } } \ No newline at end of file diff --git a/Aurora/Design/Views/Profile/ProfileView.xaml b/Aurora/Design/Views/Profile/ProfileView.xaml index 58bdbf2..a2fd09d 100644 --- a/Aurora/Design/Views/Profile/ProfileView.xaml +++ b/Aurora/Design/Views/Profile/ProfileView.xaml @@ -22,6 +22,14 @@ + + \ No newline at end of file diff --git a/Aurora/Design/Views/Profile/ProfileViewModel.cs b/Aurora/Design/Views/Profile/ProfileViewModel.cs index b71e876..be75ad2 100644 --- a/Aurora/Design/Views/Profile/ProfileViewModel.cs +++ b/Aurora/Design/Views/Profile/ProfileViewModel.cs @@ -1,34 +1,46 @@ using System; -using Aurora.Services; +using Aurora.Services.Settings; namespace Aurora.Design.Views.Profile { public class ProfileViewModel : BaseViewModel { + private ISettingsService _settingsService; - public ProfileViewModel() + public ProfileViewModel(ISettingsService settingsService) { + this._settingsService = settingsService; } public string Username { - get { return SettingsService.Instance.Username; } + get { return this._settingsService.Username; } set { - SettingsService.Instance.Username = value; + this._settingsService.Username = value; OnPropertyChanged("Username"); } } public string Port { - get { return SettingsService.Instance.DefaultPort.ToString(); } + get { return this._settingsService.DefaultPort.ToString(); } set { Int32.TryParse(value, out int portNum); - SettingsService.Instance.DefaultPort = portNum; + this._settingsService.DefaultPort = portNum; OnPropertyChanged("Port"); } } + + public string LibraryPath + { + get { return this._settingsService.LibraryLocation; } + set + { + this._settingsService.LibraryLocation = value; + OnPropertyChanged("LibraryPath"); + } + } } } diff --git a/Aurora/Design/Views/Songs/SongsViewModel.cs b/Aurora/Design/Views/Songs/SongsViewModel.cs index 588f0e4..b651342 100644 --- a/Aurora/Design/Views/Songs/SongsViewModel.cs +++ b/Aurora/Design/Views/Songs/SongsViewModel.cs @@ -1,6 +1,6 @@ using System.Collections.ObjectModel; using Aurora.Models.Media; -using Aurora.Services; +using Aurora.Services.Library; using Xamarin.Forms; namespace Aurora.Design.Views.Songs @@ -10,15 +10,18 @@ namespace Aurora.Design.Views.Songs #region Fields private ObservableCollection _songsList; private BaseMedia _selectedSong; + private ILibraryService _libraryService; #endregion Fields #region Constructor - public SongsViewModel() + public SongsViewModel(ILibraryService libraryService) { _songsList = new ObservableCollection(); DoubleClickCommand = new Command(OnDoubleClickExecute, OnDoubleClickCanExecute); + this._libraryService = libraryService; + Initialize(); } @@ -45,7 +48,7 @@ namespace Aurora.Design.Views.Songs public void Initialize() { - SongsList = LibraryService.Instance.GetLibrary(); + SongsList = this._libraryService.GetLibrary(); } #endregion Methods diff --git a/Aurora/Models/Media/BaseMedia.cs b/Aurora/Models/Media/BaseMedia.cs index c3a22e8..474186d 100644 --- a/Aurora/Models/Media/BaseMedia.cs +++ b/Aurora/Models/Media/BaseMedia.cs @@ -10,6 +10,7 @@ namespace Aurora.Models.Media public BaseMedia() { + //TODO need to make sure this is unique Id = Guid.NewGuid().ToString(); } diff --git a/Aurora/Models/Media/RemoteAudio.cs b/Aurora/Models/Media/RemoteAudio.cs index 679b57e..5620722 100644 --- a/Aurora/Models/Media/RemoteAudio.cs +++ b/Aurora/Models/Media/RemoteAudio.cs @@ -3,27 +3,23 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using Aurora.Proto.General; -using Aurora.Proto.Playback; -using Aurora.Proto.Sync; +using Aurora.Proto.Party; namespace Aurora.Models.Media { public class RemoteAudio : BaseMedia { - private RemotePlaybackService.RemotePlaybackServiceClient _remotePlaybackClient; - private RemoteSyncService.RemoteSyncServiceClient _remoteSyncClient; + private RemotePartyService.RemotePartyServiceClient _remotePartyService; private CancellationTokenSource _cancellationTokenSource; #region Constructor public RemoteAudio(string id, bool fromHost, AudioMetadata metadata, - RemotePlaybackService.RemotePlaybackServiceClient playbackClient, - RemoteSyncService.RemoteSyncServiceClient syncClient) + RemotePartyService.RemotePartyServiceClient partyClient) { this.Id = id; - this._remotePlaybackClient = playbackClient; - this._remoteSyncClient = syncClient; + this._remotePartyService = partyClient; this.Metadata = metadata; this.FromHost = fromHost; @@ -41,11 +37,11 @@ namespace Aurora.Models.Media get { return MediaTypeEnum.Audio; } } - public RemoteSyncService.RemoteSyncServiceClient RemoteSyncClient + public RemotePartyService.RemotePartyServiceClient RemotePartyServiceClient { get { - return _remoteSyncClient; + return _remotePartyService; } } @@ -58,7 +54,7 @@ namespace Aurora.Models.Media public override async Task Load() { this.DataStream = new MemoryStream(); - using (var call = _remotePlaybackClient.GetSongStream(new SongRequest() { Id = this.Id })) + using (var call = _remotePartyService.StreamMedia(new StreamMediaRequest { Name = this.Id })) { try { diff --git a/Aurora/Models/PartyMember.cs b/Aurora/Models/PartyMember.cs index 920b6a6..b3eec9b 100644 --- a/Aurora/Models/PartyMember.cs +++ b/Aurora/Models/PartyMember.cs @@ -5,17 +5,17 @@ namespace Aurora.Proto.Party /// /// Partial PartyMember class with a constructor that generates a new id /// - public partial class PartyMember + public partial class Member { - public PartyMember(string id) + public Member(string id) { if (!string.IsNullOrWhiteSpace(id)) { - Id = id; + Name = id; } else { - Id = Guid.NewGuid().ToString(); + Name = Guid.NewGuid().ToString(); } } } diff --git a/Aurora/Proto/events.proto b/Aurora/Proto/events.proto deleted file mode 100644 index 08a0bf1..0000000 --- a/Aurora/Proto/events.proto +++ /dev/null @@ -1,77 +0,0 @@ -syntax = "proto3"; - -package Aurora.Proto.Events; - -import "Proto/general.proto"; -import "Proto/party.proto"; - -service RemoteEventService { - //Party Service - rpc GetEvents(EventsRequest) returns (stream BaseEvent) {}; - rpc SubscribeToEvents(SubscribeRequest) returns(SubscriptionResponse); - rpc UnsubscribeFromEvents(UnsubscribeRequest) returns (SubscriptionResponse); - rpc UnsubscribeFromAll(UnsubscribeAllRequest) returns (SubscriptionResponse); -} - -message EventsRequest { - string clientId = 1; -} - -/* Subscription messages */ -message SubscribeRequest { - repeated EventType eventTypes = 1; - string clientId = 2; -} - -message UnsubscribeRequest { - repeated EventType eventTypes = 1; - string clientId = 2; -} - -message UnsubscribeAllRequest { - string clientId = 1; -} - -message SubscriptionResponse { - bool successful = 1; -} - -/* Event Types */ -enum EventType { - PartyMemberJoined = 0; - PartyMemberLeft = 1; - MediaPlaying = 2; - MediaStopped = 3; -} -message BaseEvent { - EventType eventType = 1; - string clientKey = 2; - - oneof derivedEvent { - PartyMemberJoinedEvent partyMemberJoinedEvent = 3; - PartyMemberLeftEvent partyMemberLeftEvent = 4; - NewMediaPlayingEvent newMediaPlayingEvent = 5; - MediaPausedEvent mediaPausedEvent = 6; - MediaResumedEvent mediaResumedEvent = 7; - } -} - -message NewMediaPlayingEvent { - Aurora.Proto.Party.RemoteMediaData media = 1; -} - -message MediaResumedEvent { - Aurora.Proto.General.Empty empty = 1; -} - -message MediaPausedEvent { - Aurora.Proto.General.Empty empty = 1; -} - -message PartyMemberJoinedEvent { - Aurora.Proto.Party.PartyMember member = 1; -} - -message PartyMemberLeftEvent { - Aurora.Proto.Party.PartyMember member = 1; -} \ No newline at end of file diff --git a/Aurora/Proto/party.proto b/Aurora/Proto/party.proto index d5e54a4..d520b3a 100644 --- a/Aurora/Proto/party.proto +++ b/Aurora/Proto/party.proto @@ -3,55 +3,287 @@ syntax = "proto3"; package Aurora.Proto.Party; import "Proto/general.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/field_mask.proto"; + +//Party + //Members + //EventSubscriptions + //Events + //Media service RemotePartyService { - //Party Service - rpc JoinParty(JoinPartyRequest) returns (JoinPartyResponse); - rpc LeaveParty(LeavePartyRequest) returns (LeavePartyResponse); - rpc GetPartyMembers(Aurora.Proto.General.Empty) returns (MembersResponse); - rpc GetQueue(Aurora.Proto.General.Empty) returns (QueueResponse); + //************** + //Party Resource + //************** + //Get Party + rpc GetParty(Aurora.Proto.General.Empty) returns (Party); + + //*************** + //Member Resource + //*************** + //List + rpc ListMembers(ListMembersRequest) returns (ListMembersResponse); + + //Get + rpc GetMember(GetMemberRequest) returns (Member); + + //Update + rpc UpdateMember(UpdateMemberRequest) returns (Member); + + //Create + rpc CreateMember(CreateMemberRequest) returns (Member); + + //Delete + rpc DeleteMember(DeleteMemberRequest) returns (Aurora.Proto.General.Empty) {}; + + //************** + //Media Resource + //************** + //List + rpc ListMedia(ListMediaRequest) returns (ListMediaResponse); + + //Get + rpc GetMedia(GetMediaRequest) returns (Media); + + //Create + rpc CreateMedia(CreateMediaRequest) returns (Media); + + //Delete + rpc DeleteMedia(DeleteMediaRequest) returns (Aurora.Proto.General.Empty) {}; + + //CUSTOM: Stream + rpc StreamMedia(StreamMediaRequest) returns (stream Aurora.Proto.General.Chunk) {}; + + //CUSTOM: Sync + rpc SyncMedia(SyncMediaRequest) returns (stream Sync) {}; + + //*************************** + //EventSubscriptions Resource + //*************************** + //List + rpc ListEventSubscriptions(ListEventSubscriptionsRequest) returns (ListEventSubscriptionsResponse); + + //Create + rpc CreateEventSubscription(CreateEventSubscriptionRequest) returns (EventSubscription); + + //Delete + rpc DeleteEventSubscription(DeleteEventSubscriptionRequest) returns (Aurora.Proto.General.Empty); + + //CUSTOM: Create EventSubscription List + rpc CreateEventSubscriptionList(CreateEventSubscriptionListRequest) returns (CreateEventSubscriptionListResponse); + + //CUSTOM: Delete all + rpc DeleteAllEventSubscriptions(DeleteAllEventSubscriptionsRequest) returns (Aurora.Proto.General.Empty); + + //***** + //Event + //***** + //Get + rpc GetEvents(GetEventsRequest) returns (stream BaseEvent) {}; } -message JoinPartyRequest { - string userName = 2; +message Party { + //The resource name of the party + string name = 1; + string displayName = 2; + string description = 3; + string hostIp = 4; + Member hostMember = 5; + google.protobuf.Timestamp createdOn = 6; } -message JoinPartyResponse { - PartyJoinedStatusEnum status = 1; - string clientId = 2; -} - -message LeavePartyRequest { - string clientId = 1; +enum PartyJoinedStatusEnum { + InParty = 0; + NotInParty = 1; } message LeavePartyResponse { PartyJoinedStatusEnum status = 1; } -message PartyMember { - string userName = 1; - string id = 2; +message Member { + //Resource name of the party member to be returned (Added by server) + string name = 1; + string userName = 2; + + //Added by server string ipAddress = 3; - int32 port = 4; -} -message MembersResponse { - repeated PartyMember members = 1; + + //Added by server + google.protobuf.Timestamp addedOn = 4; } -enum PartyJoinedStatusEnum { - Connected = 0; - Disconnected = 1; +message ListMembersRequest { + //Resource name of the parent of the members collection to be returned (The party) + string parent = 1; + int32 pageSize = 2; + string pageToken = 3; } -message QueueResponse{ - repeated RemoteMediaData mediaList = 1; +message ListMembersResponse { + repeated Member members = 1; + string nextPageToken = 2; +} +message GetMemberRequest { + //Resource name of the member to be returned + string name = 1; } -message RemoteMediaData { - string id = 1; +message CreateMemberRequest { + //Resource name of the parent collection of the member to be created (The party) + string parent = 1; + Member member = 2; +} + +message UpdateMemberRequest { + Member member = 1; + google.protobuf.FieldMask updateMask = 2; +} + +message DeleteMemberRequest { + //Resource name of the member to be deleted + string name = 1; +} + +message Media { + //Resource name of the remote media object + string name = 1; string title = 2; string artist = 3; string album = 4; string duration = 5; } + +message ListMediaRequest { + //Resource name of the parent of the media collection to be listed (The party) + string parent = 1; + int32 pageSize = 2; + string pageToken = 3; +} + +message ListMediaResponse { + repeated Media media = 1; + string nextPageToken = 3; +} + +message GetMediaRequest { + //Resource name of the media requested + 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 { + //Resource name of the media requested + string name = 1; +} + +message SyncMediaRequest { + //Resource name of the media to sync with + string name = 1; +} + +message Sync { + int64 serverTimeTicks = 1; + float trackPosition= 2; +} + +/* Event Types */ +enum EventType { + MemberCreated = 0; + MemberDeleted = 1; + MediaPlaying = 2; + MediaStopped = 3; +} + +message BaseEvent { + //Resource name of the event ? + string name = 1; + EventType eventType = 2; + string clientKey = 3; + + oneof derivedEvent { + MemberCreatedEvent memberCreatedEvent = 4; + MemberDeletedEvent memberDeletedEvent = 5; + NewMediaPlayingEvent newMediaPlayingEvent = 6; + MediaPausedEvent mediaPausedEvent = 7; + MediaResumedEvent mediaResumedEvent = 8; + } +} + +message NewMediaPlayingEvent { + Media media = 1; +} + +message MediaResumedEvent { + Aurora.Proto.General.Empty empty = 1; +} + +message MediaPausedEvent { + Aurora.Proto.General.Empty empty = 1; +} + +message MemberCreatedEvent { + Member member = 1; +} + +message MemberDeletedEvent { + string memberName = 1; +} + +message EventSubscription { + EventType type = 2; +} + +message ListEventSubscriptionsRequest { + //Resource name of parent to the subscription list (The member) + string parent = 1; + int32 pageSize = 2; + string pageToken = 3; +} + +message ListEventSubscriptionsResponse { + repeated EventSubscription subscriptions = 1; +} + +message CreateEventSubscriptionRequest { + //Resource name of the parent to the subscription list (The member) + string parent = 1; + EventSubscription eventSubscription = 2; +} + +message DeleteEventSubscriptionRequest { + //Resource name of the subscription to delete + string parent = 1; + EventType type = 2; +} + +message CreateEventSubscriptionListRequest { + //Resource name of the parent to the subscription list (The member) + string parent = 1; + repeated EventSubscription eventSubscriptions = 2; +} + +message CreateEventSubscriptionListResponse { + repeated EventSubscription eventSubscriptions = 1; +} + +message DeleteAllEventSubscriptionsRequest { + //Resource name of the parent to the subscription list (the member) + string parent = 1; +} + +message GetEventsRequest { + //Resource name of the parent to the event stream (the member) + string parent = 1; +} diff --git a/Aurora/Proto/playback.proto b/Aurora/Proto/playback.proto deleted file mode 100644 index 0f6832a..0000000 --- a/Aurora/Proto/playback.proto +++ /dev/null @@ -1,13 +0,0 @@ -syntax = "proto3"; - -package Aurora.Proto.Playback; - -import "Proto/general.proto"; - -service RemotePlaybackService { - rpc GetSongStream(SongRequest) returns (stream Aurora.Proto.General.Chunk) {}; -} - -message SongRequest { - string id = 1; -} \ No newline at end of file diff --git a/Aurora/Proto/sync.proto b/Aurora/Proto/sync.proto deleted file mode 100644 index 7235163..0000000 --- a/Aurora/Proto/sync.proto +++ /dev/null @@ -1,15 +0,0 @@ -syntax = "proto3"; - -package Aurora.Proto.Sync; - -import "Proto/general.proto"; -import "google/protobuf/timestamp.proto"; - -service RemoteSyncService { - rpc GetMediaSync(Aurora.Proto.General.Empty) returns (stream Sync) {}; -} - -message Sync { - int64 serverTimeTicks = 1; - float trackPosition= 2; -} \ No newline at end of file diff --git a/Aurora/RemoteImpl/RemoteEventImpl.cs b/Aurora/RemoteImpl/RemoteEventImpl.cs deleted file mode 100644 index 7a50b8a..0000000 --- a/Aurora/RemoteImpl/RemoteEventImpl.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Threading; -using System.Collections.Generic; -using Aurora.Services; -using Aurora.Utils; -using System.Linq; -using Aurora.Services.EventManager; -using Aurora.Proto.Events; - -namespace Aurora.RemoteImpl -{ - public class RemoteEventServiceImpl : RemoteEventService.RemoteEventServiceBase - { - public RemoteEventServiceImpl() - { - - } - - /// - /// RPC for getting event stream for a particular client. - /// - /// Empty - /// The response stream - /// gRPC client context - /// - public override Task GetEvents(EventsRequest request, - Grpc.Core.IServerStreamWriter responseStream, - Grpc.Core.ServerCallContext context) - { - string peerId = Misc.Combine(new string[] { context.Peer, request.ClientId }); - Console.WriteLine(string.Format("SERVER - Events request received from peer: {0}", peerId)); - - AutoResetEvent are = new AutoResetEvent(false); - Action callback = (BaseEvent bEvent) => - { - Console.WriteLine(string.Format("SERVER - Event fired for peer: {0}", peerId)); - //TODO need to remove callback if stream no longer exists IE. Client crashed or stopped - responseStream.WriteAsync(bEvent); - - }; - - Action cancelled = () => - { - are.Set(); - }; - - EventManager.Instance.AddEventHandler(callback, cancelled, Misc.Combine(new string[] { context.Peer, request.ClientId })); - are.WaitOne(); - return Task.FromResult(null); - } - - /// - /// RPC for subscribing to remote events - /// - /// SubscribeRequest - /// gRPC client context - /// - public override Task SubscribeToEvents(SubscribeRequest request, Grpc.Core.ServerCallContext context) - { - Console.WriteLine(string.Format("SERVER - Subscription from client with id: {0}", request.ClientId)); - EventManager.Instance.AddSubscriptionList(Misc.Combine(new string[] { context.Peer, request.ClientId }), request.EventTypes.ToList()); - - return Task.FromResult(new SubscriptionResponse { Successful = true }); - } - - /// - /// RPC for unsubscibing from events - /// - /// UnsubscribeRequest - /// gRPC client context - /// - public override Task UnsubscribeFromEvents(UnsubscribeRequest request, Grpc.Core.ServerCallContext context) - { - EventType[] eventTypes = null; - request.EventTypes.CopyTo(eventTypes, 0); - - EventManager.Instance.RemoveSubscriptionList(Misc.Combine(new string[] { context.Peer, request.ClientId }), eventTypes.ToList()); - - return Task.FromResult(new SubscriptionResponse { Successful = true }); - } - - /// - /// RPC for unsubscribing from all events - /// - /// UnsubscribeAllRequest - /// gRPC client context - /// - public override Task UnsubscribeFromAll(UnsubscribeAllRequest request, Grpc.Core.ServerCallContext context) - { - EventManager.Instance.RemoveAllSubscriptions(Misc.Combine(new string[] { context.Peer, request.ClientId })); - - return Task.FromResult(new SubscriptionResponse { Successful = true }); - } - } - -} \ No newline at end of file diff --git a/Aurora/RemoteImpl/RemotePartyImpl.cs b/Aurora/RemoteImpl/RemotePartyImpl.cs deleted file mode 100644 index b9bd630..0000000 --- a/Aurora/RemoteImpl/RemotePartyImpl.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Collections.ObjectModel; -using System.Linq; -using Aurora.Utils; -using Aurora.Proto.Party; -using Aurora.Proto.Events; -using Aurora.Services.EventManager; -using Aurora.Services; -using Aurora.Models.Media; - -namespace Aurora.RemoteImpl -{ - public class RemotePartyServiceImpl : RemotePartyService.RemotePartyServiceBase - { - /// - /// Dictionary of party members. Key -> ClientId - /// - private ObservableCollection _partyMembers; - - public RemotePartyServiceImpl() - { - _partyMembers = new ObservableCollection(); - } - - public ObservableCollection PartyMembers - { - get - { - return _partyMembers; - } - } - - public override Task JoinParty(JoinPartyRequest request, Grpc.Core.ServerCallContext context) - { - PartyMember partyMember = new PartyMember("") - { - UserName = request.UserName, - IpAddress = context.Host, - }; - - Console.WriteLine("SERVER - Client joined party: " + partyMember.Id); - - _partyMembers.Add(partyMember); - - BaseEvent e = new BaseEvent - { - EventType = EventType.PartyMemberJoined, - PartyMemberJoinedEvent = new PartyMemberJoinedEvent - { - Member = partyMember, - } - }; - - EventManager.Instance.FireEvent(e); - - JoinPartyResponse response = new JoinPartyResponse() { Status = PartyJoinedStatusEnum.Connected, ClientId = partyMember.Id }; - return Task.FromResult(response); - } - - public override Task LeaveParty(LeavePartyRequest request, Grpc.Core.ServerCallContext context) - { - PartyMember partyMember = _partyMembers.Where(e => e.Id == request.ClientId).Single(); - - _partyMembers.Remove(partyMember); - - BaseEvent bv = new BaseEvent - { - EventType = EventType.PartyMemberJoined, - PartyMemberLeftEvent = new PartyMemberLeftEvent - { - Member = partyMember, - } - }; - - EventManager.Instance.FireEvent(bv); - EventManager.Instance.RemoveAllSubscriptions(Misc.Combine(new string[] { context.Peer, request.ClientId })); - EventManager.Instance.CancelEventStream(Misc.Combine(new string[] { context.Peer, request.ClientId })); - - LeavePartyResponse response = new LeavePartyResponse() { Status = PartyJoinedStatusEnum.Disconnected }; - return Task.FromResult(response); - } - - public override Task GetPartyMembers(Proto.General.Empty empty, Grpc.Core.ServerCallContext context) - { - MembersResponse response = new MembersResponse(); - response.Members.AddRange(_partyMembers); - return Task.FromResult(response); - } - - public override Task GetQueue(Proto.General.Empty empty, Grpc.Core.ServerCallContext context) - { - //This will change as queuing operation gets solidified - //Simply return the hosts library - - ObservableCollection queue = LibraryService.Instance.GetLibrary(); - - QueueResponse mediaList = new QueueResponse(); - foreach (BaseMedia media in queue) - { - AudioMetadata metadata = new AudioMetadata(); - try - { - if (media.Metadata is AudioMetadata) - { - metadata = media.Metadata as AudioMetadata; - - RemoteMediaData data = new RemoteMediaData(); - data.Id = media.Id; - if (metadata.Title != null) - { - data.Title = metadata.Title; - } - if (metadata.Artist != null) - { - data.Artist = metadata.Artist; - } - if (metadata.Album != null) - { - data.Album = metadata.Album; - } - if (metadata.Duration != null) - { - data.Duration = metadata.Duration; - } - - mediaList.MediaList.Add(data); - } - } - catch (Exception ex) - { - Console.WriteLine(string.Format("Error preparing queue: {0}", ex.Message)); - } - } - - return Task.FromResult(mediaList); - - } - } -} \ No newline at end of file diff --git a/Aurora/RemoteImpl/RemotePlaybackImpl.cs b/Aurora/RemoteImpl/RemotePlaybackImpl.cs deleted file mode 100644 index 17da449..0000000 --- a/Aurora/RemoteImpl/RemotePlaybackImpl.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Threading; -using System.Collections.Generic; -using Aurora.Services; -using Aurora.Proto.Playback; -using Aurora.Proto.General; -using Aurora.Models.Media; - -namespace Aurora.RemoteImpl -{ - public class RemotePlaybackServiceImpl : RemotePlaybackService.RemotePlaybackServiceBase - { - public override async Task GetSongStream(SongRequest request, - Grpc.Core.IServerStreamWriter responseStream, - Grpc.Core.ServerCallContext context) - { - BaseMedia originalSong = LibraryService.Instance.GetSong(request.Id); - 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); - } - } - } -} \ No newline at end of file diff --git a/Aurora/RemoteImpl/RemoteSyncImpl.cs b/Aurora/RemoteImpl/RemoteSyncImpl.cs deleted file mode 100644 index eddaac0..0000000 --- a/Aurora/RemoteImpl/RemoteSyncImpl.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Threading.Tasks; -using Aurora.Proto.Sync; -using Aurora.Proto.General; -using Aurora.Services.PlayerService; - -namespace Aurora.RemoteImpl -{ - public class RemoteSyncServiceImpl : RemoteSyncService.RemoteSyncServiceBase - { - /// - /// RPC for getting a stream of media syncs - /// - /// - /// - /// - /// - public override async Task GetMediaSync(Empty request, - Grpc.Core.IServerStreamWriter responseStream, - Grpc.Core.ServerCallContext context) - { - bool continueSync = true; - string currentId = PlayerService.Instance.CurrentMedia.Id; - MediaChangedEventHandler mediaChanged = (sender, e) => - { - 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"); - await Task.Delay(5000); - } - } - } -} \ No newline at end of file diff --git a/Aurora/Services/BaseService.cs b/Aurora/Services/BaseService.cs deleted file mode 100644 index db02456..0000000 --- a/Aurora/Services/BaseService.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; - -namespace Aurora.Services -{ - public abstract class BaseService where T : class - { - private static volatile Lazy _instance = new Lazy(() => CreateInstanceOfT()); - - public static T Instance - { - get { return _instance.Value; } - } - - private static T CreateInstanceOfT() - { - - return Activator.CreateInstance(typeof(T), true) as T; - - } - } -} - diff --git a/Aurora/Services/Client/ClientService.cs b/Aurora/Services/Client/ClientService.cs new file mode 100644 index 0000000..3ee1f38 --- /dev/null +++ b/Aurora/Services/Client/ClientService.cs @@ -0,0 +1,46 @@ +using Grpc.Core; +using Aurora.Proto.Party; +using Aurora.Services.Settings; + +namespace Aurora.Services.Client +{ + + public class ClientService : IClientService + { + private RemotePartyService.RemotePartyServiceClient _remotePartyClient; + + private Channel _channel; + private ISettingsService _settingsService; + + public ClientService(ISettingsService settingsService) + { + this._settingsService = settingsService; + } + + public bool IsStarted + { + get + { + return _remotePartyClient != null; + } + } + + public RemotePartyService.RemotePartyServiceClient RemotePartyServiceClient + { + get { return this._remotePartyClient; } + } + + public void Start(string hostname, string port) + { + _channel = new Channel(string.Format("{0}:{1}", hostname, port), ChannelCredentials.Insecure); + + _remotePartyClient = new RemotePartyService.RemotePartyServiceClient(_channel); + } + + public async void Close() + { + await _channel.ShutdownAsync(); + _remotePartyClient = null; + } + } +} diff --git a/Aurora/Services/Client/IClientService.cs b/Aurora/Services/Client/IClientService.cs new file mode 100644 index 0000000..5ebe8b5 --- /dev/null +++ b/Aurora/Services/Client/IClientService.cs @@ -0,0 +1,15 @@ +using Aurora.Proto.Party; + +namespace Aurora.Services.Client +{ + public interface IClientService + { + bool IsStarted { get; } + + RemotePartyService.RemotePartyServiceClient RemotePartyServiceClient { get; } + + void Start(string hostname, string port); + + void Close(); + } +} \ No newline at end of file diff --git a/Aurora/Services/ClientService/ClientService.cs b/Aurora/Services/ClientService/ClientService.cs deleted file mode 100644 index a3c0c58..0000000 --- a/Aurora/Services/ClientService/ClientService.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Threading; -using Grpc.Core; -using Aurora.Proto.Events; -using Aurora.Proto.Party; -using Aurora.Proto.Playback; -using Aurora.Proto.Sync; -using Aurora.Services.ClientService.Events; -using System.Collections.Generic; - -namespace Aurora.Services.ClientService -{ - - public class ClientService : BaseService - { - private RemotePartyService.RemotePartyServiceClient _remotePartyClient; - private RemoteEventService.RemoteEventServiceClient _remoteEventsClient; - private RemotePlaybackService.RemotePlaybackServiceClient _remotePlaybackClient; - private RemoteSyncService.RemoteSyncServiceClient _remoteSyncClient; - - private Channel _channel; - CancellationTokenSource _eventCancellationTokenSource; - - public ClientService() - { - } - - public MediaPausedEventHandler OnMediaPaused; - public NewMediaPlayingEventHandler OnNewMediaPlaying; - public PartyMemberJoinedEventHandler OnPartyMemberJoined; - public PartyMemberLeftEventHandler OnPartyMemberLeft; - public MediaResumedEventHandler OnMediaResumed; - - public RemotePartyService.RemotePartyServiceClient RemotePartyClient - { - get - { - return _remotePartyClient; - } - } - - public RemoteEventService.RemoteEventServiceClient RemoteEventClient - { - get { return _remoteEventsClient; } - } - - public RemotePlaybackService.RemotePlaybackServiceClient RemotePlaybackClient - { - get { return _remotePlaybackClient; } - } - - public RemoteSyncService.RemoteSyncServiceClient RemoteSyncClient - { - get { return _remoteSyncClient; } - } - - public bool IsStarted - { - get - { - return _remoteEventsClient != null && - _remotePartyClient != null; - } - } - - public bool IsHost { get; set; } - - public void Start(string hostname, string port) - { - _channel = new Channel(string.Format("{0}:{1}", hostname, port), ChannelCredentials.Insecure); - - _remotePartyClient = new RemotePartyService.RemotePartyServiceClient(_channel); - _remoteEventsClient = new RemoteEventService.RemoteEventServiceClient(_channel); - _remotePlaybackClient = new RemotePlaybackService.RemotePlaybackServiceClient(_channel); - _remoteSyncClient = new RemoteSyncService.RemoteSyncServiceClient(_channel); - } - - public async void Close() - { - _eventCancellationTokenSource.Cancel(); - await _channel.ShutdownAsync(); - - _remotePartyClient = null; - _remoteEventsClient = null; - _remotePlaybackClient = null; - _remoteSyncClient = null; - } - - /// - /// Asynchronous function for processing events off of the event stream. - /// - /// - public async Task GetEvents() - { - _eventCancellationTokenSource = new CancellationTokenSource(); - string clientId = SettingsService.Instance.ClientId; - Console.WriteLine(string.Format("CLIENT {0} - GetEvents called from client with id", clientId)); - using (AsyncServerStreamingCall eventStream = _remoteEventsClient - .GetEvents(new EventsRequest { ClientId = SettingsService.Instance.ClientId })) - { - try - { - while (!_eventCancellationTokenSource.Token.IsCancellationRequested && - await eventStream.ResponseStream.MoveNext(_eventCancellationTokenSource.Token)) - { - try - { - BaseEvent e = new BaseEvent(eventStream.ResponseStream.Current); - - Dictionary events = new Dictionary() - { - {BaseEvent.DerivedEventOneofCase.MediaPausedEvent, new EventInfo(this.OnMediaPaused, typeof(MediaPausedEventArgs))}, - {BaseEvent.DerivedEventOneofCase.MediaResumedEvent, new EventInfo(this.OnMediaResumed, typeof(MediaResumedEventArgs))}, - {BaseEvent.DerivedEventOneofCase.NewMediaPlayingEvent, new EventInfo(this.OnNewMediaPlaying, typeof(NewMediaPlayingEventArgs))}, - {BaseEvent.DerivedEventOneofCase.PartyMemberJoinedEvent, new EventInfo(this.OnPartyMemberJoined, typeof(PartyMemberJoinedEventArgs))}, - {BaseEvent.DerivedEventOneofCase.PartyMemberLeftEvent, new EventInfo(this.OnPartyMemberLeft, typeof(PartyMemberLeftEventArgs))} - }; - - events.TryGetValue(e.DerivedEventCase, out EventInfo eventInfo); - - if (eventInfo != null && eventInfo.Handler != null) - { - eventInfo.Handler.DynamicInvoke(new object[] { this, Activator.CreateInstance(eventInfo.ArgsType, new object[] { e }) }); - } - } - catch (Exception ex) - { - Console.WriteLine("Exception while parsing event ---" + ex.Message); - } - - } - } - catch (Exception ex) - { - Console.WriteLine(string.Format("EXCEPTION while parsing events --- " + ex.Message)); - } - } - } - - public void StopEvents() - { - if (_eventCancellationTokenSource != null && !_eventCancellationTokenSource.IsCancellationRequested) - { - _eventCancellationTokenSource.Cancel(); - } - } - } -} \ No newline at end of file diff --git a/Aurora/Services/ClientService/EventInfo.cs b/Aurora/Services/ClientService/EventInfo.cs deleted file mode 100644 index f071e62..0000000 --- a/Aurora/Services/ClientService/EventInfo.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Threading; -using Grpc.Core; -using Aurora.Proto.Events; -using Aurora.Proto.Party; -using Aurora.Proto.Playback; -using Aurora.Proto.Sync; -using Aurora.Services.ClientService.Events; -using System.Collections.Generic; - -namespace Aurora.Services.ClientService -{ - public class EventInfo - { - public EventInfo(Delegate handler, Type argsType) - { - this.Handler = handler; - ArgsType = argsType; - } - - public Delegate Handler { get; private set; } - public Type ArgsType { get; private set; } - } -} \ No newline at end of file diff --git a/Aurora/Services/ClientService/Events/MediaPausedEvent.cs b/Aurora/Services/ClientService/Events/MediaPausedEvent.cs deleted file mode 100644 index 9401103..0000000 --- a/Aurora/Services/ClientService/Events/MediaPausedEvent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using Aurora.Proto.Events; - -namespace Aurora.Services.ClientService.Events -{ - public delegate void MediaPausedEventHandler(object sender, MediaPausedEventArgs e); - - public class MediaPausedEventArgs - { - public MediaPausedEvent Event { get; private set; } - public MediaPausedEventArgs(BaseEvent e) - { - Event = e.MediaPausedEvent; - } - } -} \ No newline at end of file diff --git a/Aurora/Services/ClientService/Events/MediaResumedEvent.cs b/Aurora/Services/ClientService/Events/MediaResumedEvent.cs deleted file mode 100644 index 5dbb87a..0000000 --- a/Aurora/Services/ClientService/Events/MediaResumedEvent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using Aurora.Proto.Events; - -namespace Aurora.Services.ClientService -{ - public delegate void MediaResumedEventHandler(object sender, MediaResumedEventArgs e); - - public class MediaResumedEventArgs - { - public MediaResumedEvent Event { get; private set; } - public MediaResumedEventArgs(BaseEvent e) - { - Event = e.MediaResumedEvent; - } - } -} \ No newline at end of file diff --git a/Aurora/Services/ClientService/Events/NewMediaPlayingEvent.cs b/Aurora/Services/ClientService/Events/NewMediaPlayingEvent.cs deleted file mode 100644 index b47d669..0000000 --- a/Aurora/Services/ClientService/Events/NewMediaPlayingEvent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using Aurora.Proto.Events; - -namespace Aurora.Services.ClientService.Events -{ - public delegate void NewMediaPlayingEventHandler(object sender, NewMediaPlayingEventArgs e); - - public class NewMediaPlayingEventArgs - { - public NewMediaPlayingEvent Event { get; private set; } - public NewMediaPlayingEventArgs(BaseEvent e) - { - Event = e.NewMediaPlayingEvent; - } - } -} \ No newline at end of file diff --git a/Aurora/Services/ClientService/Events/PartyMemberJoinedEvent.cs b/Aurora/Services/ClientService/Events/PartyMemberJoinedEvent.cs deleted file mode 100644 index 64eb86b..0000000 --- a/Aurora/Services/ClientService/Events/PartyMemberJoinedEvent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using Aurora.Proto.Events; - -namespace Aurora.Services.ClientService.Events -{ - public delegate void PartyMemberJoinedEventHandler(object sender, PartyMemberJoinedEventArgs e); - - public class PartyMemberJoinedEventArgs - { - public PartyMemberJoinedEvent Event { get; private set; } - public PartyMemberJoinedEventArgs(BaseEvent e) - { - Event = e.PartyMemberJoinedEvent; - } - } -} \ No newline at end of file diff --git a/Aurora/Services/ClientService/Events/PartyMemberLeftEvent.cs b/Aurora/Services/ClientService/Events/PartyMemberLeftEvent.cs deleted file mode 100644 index 18eb179..0000000 --- a/Aurora/Services/ClientService/Events/PartyMemberLeftEvent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using Aurora.Proto.Events; - -namespace Aurora.Services.ClientService.Events -{ - public delegate void PartyMemberLeftEventHandler(object sender, PartyMemberLeftEventArgs e); - - public class PartyMemberLeftEventArgs - { - public PartyMemberLeftEvent Event { get; private set; } - public PartyMemberLeftEventArgs(BaseEvent e) - { - Event = e.PartyMemberLeftEvent; - } - } -} \ No newline at end of file diff --git a/Aurora/Services/EventManager/EventAction.cs b/Aurora/Services/EventManager/EventAction.cs new file mode 100644 index 0000000..18bf2a9 --- /dev/null +++ b/Aurora/Services/EventManager/EventAction.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Aurora.Proto.Party; + +namespace Aurora.Services.EventManager +{ + public class EventAction + { + public EventAction(Action callback, Action cancel) + { + Callback = callback; + Cancel = cancel; + } + public Action Callback { get; set; } + public Action Cancel { get; set; } + } +} \ No newline at end of file diff --git a/Aurora/Services/EventManager/EventManager.cs b/Aurora/Services/EventManager/EventManager.cs index c9e4886..058e70c 100644 --- a/Aurora/Services/EventManager/EventManager.cs +++ b/Aurora/Services/EventManager/EventManager.cs @@ -1,23 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; -using Aurora.Proto.Events; +using Aurora.Proto.Party; namespace Aurora.Services.EventManager { - public class EventAction - { - public EventAction(Action callback, Action cancel) - { - Callback = callback; - Cancel = cancel; - } - public Action Callback { get; set; } - public Action Cancel { get; set; } - } - public class EventManager : BaseService + public class EventManager : IEventManager { #region Fields private Dictionary> _subscriptionList; @@ -38,32 +27,32 @@ namespace Aurora.Services.EventManager #region Public Methods /// - /// Get the list of event type subscriptions for a given session id. + /// Get the list of event type subscriptions for a given sessionIdentifier id. /// - /// Session Id + /// sessionIdentifier Id /// - public List GetSubscriptionList(string session) + public List GetSubscriptionList(string sessionIdentifier) { List eventList = new List(); - if (_subscriptionList.ContainsKey(session)) + if (_subscriptionList.ContainsKey(sessionIdentifier)) { - _subscriptionList.TryGetValue(session, out eventList); + _subscriptionList.TryGetValue(sessionIdentifier, out eventList); } return eventList; } /// - /// Get the number of event subscriptions for a given session + /// Get the number of event subscriptions for a given sessionIdentifier /// - /// Session Id + /// sessionIdentifier Id /// - public int GetSubscriptionCount(string session) + public int GetSubscriptionCount(string sessionIdentifier) { List eventList = new List(); - if (_subscriptionList.ContainsKey(session)) + if (_subscriptionList.ContainsKey(sessionIdentifier)) { - _subscriptionList.TryGetValue(session, out eventList); + _subscriptionList.TryGetValue(sessionIdentifier, out eventList); } return eventList.Count(); @@ -72,24 +61,24 @@ namespace Aurora.Services.EventManager /// /// Add a new subscription /// - /// + /// /// - public bool AddSubscription(string session, EventType type) + public bool AddSubscription(string sessionIdentifier, EventType type) { bool success = false; lock (_subscriptionList) { - if (!_subscriptionList.ContainsKey(session)) + if (!_subscriptionList.ContainsKey(sessionIdentifier)) { - //Add session to subscription list + //Add sessionIdentifier to subscription list List eventList = new List(); eventList.Add(type); - _subscriptionList.Add(session, eventList); + _subscriptionList.Add(sessionIdentifier, eventList); success = true; } else { - _subscriptionList.TryGetValue(session, out List eventList); + _subscriptionList.TryGetValue(sessionIdentifier, out List eventList); if (eventList != null) { eventList.Add(type); @@ -104,79 +93,82 @@ namespace Aurora.Services.EventManager /// /// Add a list of subscriptions. This unsubscribes from unused events. /// - /// The browser session id. + /// The browser sessionIdentifier id. /// The list of event types to subscribe to. - public void AddSubscriptionList(string session, List types) + public void AddSubscriptionList(string sessionIdentifier, List types) { - RemoveAllSubscriptions(session); + RemoveAllSubscriptions(sessionIdentifier); foreach (EventType e in types) { - AddSubscription(session, e); + AddSubscription(sessionIdentifier, e); } } /// /// Unsubscribe from a given event type. /// - /// Session Id + /// sessionIdentifier Id /// Event Type to be removed - public void RemoveSubscription(string session, EventType type) + public void RemoveSubscription(string sessionIdentifier, EventType type) { lock (_subscriptionList) { - if (_subscriptionList.ContainsKey(session)) + if (_subscriptionList.ContainsKey(sessionIdentifier)) { List eventTypeList; - _subscriptionList.TryGetValue(session, out eventTypeList); + _subscriptionList.TryGetValue(sessionIdentifier, out eventTypeList); if (eventTypeList != null && eventTypeList.Contains(type)) { eventTypeList.Remove(type); - //base.LogInformation(string.Format("Subscription removed for event type {0} subscription on session {1}", type.ToString(), session)); + //base.LogInformation(string.Format("Subscription removed for event type {0} subscription on sessionIdentifier {1}", type.ToString(), sessionIdentifier)); } } } } - public void RemoveSubscriptionList(string session, List types) + public void RemoveSubscriptionList(string sessionIdentifier, List types) { foreach (EventType e in types) { - RemoveSubscription(session, e); + RemoveSubscription(sessionIdentifier, e); } } /// - /// Remove all subscriptons for a given session. + /// Remove all subscriptons for a given sessionIdentifier. /// - /// Session Id - public void RemoveAllSubscriptions(string session) + /// sessionIdentifier Id + public void RemoveAllSubscriptions(string sessionIdentifier) { - if (_subscriptionList.ContainsKey(session)) + if (_subscriptionList.ContainsKey(sessionIdentifier)) { - _subscriptionList.Remove(session); + _subscriptionList.Remove(sessionIdentifier); } } - public void AddEventHandler(Action action, Action cancel, string sessionId) + public void AddEventHandler(Action action, Action cancel, string sessionIdentifierId) { lock (_actionList) { - _actionList.Add(sessionId, new EventAction(action, cancel)); + _actionList.Add(sessionIdentifierId, new EventAction(action, cancel)); } } - public void RemoveEventHandler(string sessionId) + public void RemoveEventHandler(string sessionIdentifierId) { - _actionList.Remove(sessionId); + _actionList.Remove(sessionIdentifierId); } - public void CancelEventStream(string sessionId) + public void CancelEventStream(string sessionIdentifierId) { - _actionList.TryGetValue(sessionId, out EventAction value); - value.Cancel(); + _actionList.TryGetValue(sessionIdentifierId, out EventAction value); + if (value != null) + { + value.Cancel(); + } - RemoveEventHandler(sessionId); + RemoveEventHandler(sessionIdentifierId); } public void FireEvent(BaseEvent bEvent) diff --git a/Aurora/Services/EventManager/IEventManager.cs b/Aurora/Services/EventManager/IEventManager.cs new file mode 100644 index 0000000..922b710 --- /dev/null +++ b/Aurora/Services/EventManager/IEventManager.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using Aurora.Proto.Party; + +namespace Aurora.Services.EventManager +{ + public interface IEventManager + { + /// + /// Get the list of event type subscriptions for a given sessionIdentifier id. + /// + /// sessionIdentifier Id + /// + List GetSubscriptionList(string sessionIdentifier); + + /// + /// Get the number of event subscriptions for a given sessionIdentifier + /// + /// sessionIdentifier Id + /// + int GetSubscriptionCount(string sessionIdentifier); + + /// + /// Add a new subscription + /// + /// + /// + bool AddSubscription(string sessionIdentifier, EventType type); + + /// + /// Add a list of subscriptions. This unsubscribes from unused events. + /// + /// The browser sessionIdentifier id. + /// The list of event types to subscribe to. + void AddSubscriptionList(string sessionIdentifier, List types); + + /// + /// Unsubscribe from a given event type. + /// + /// sessionIdentifier Id + /// Event Type to be removed + void RemoveSubscription(string sessionIdentifier, EventType type); + + void RemoveSubscriptionList(string sessionIdentifier, List types); + + /// + /// Remove all subscriptons for a given sessionIdentifier. + /// + /// sessionIdentifier Id + void RemoveAllSubscriptions(string sessionIdentifier); + + void AddEventHandler(Action action, Action cancel, string sessionIdentifierId); + + void RemoveEventHandler(string sessionIdentifierId); + + void CancelEventStream(string sessionIdentifierId); + + void FireEvent(BaseEvent bEvent); + } +} \ No newline at end of file diff --git a/Aurora/Services/Library/ILibraryService.cs b/Aurora/Services/Library/ILibraryService.cs new file mode 100644 index 0000000..ad67fb9 --- /dev/null +++ b/Aurora/Services/Library/ILibraryService.cs @@ -0,0 +1,12 @@ +using System.Collections.ObjectModel; +using Aurora.Models.Media; + +namespace Aurora.Services.Library +{ + public interface ILibraryService + { + ObservableCollection GetLibrary(); + + BaseMedia GetSong(string Id); + } +} \ No newline at end of file diff --git a/Aurora/Services/LibraryService.cs b/Aurora/Services/Library/LibraryService.cs similarity index 85% rename from Aurora/Services/LibraryService.cs rename to Aurora/Services/Library/LibraryService.cs index fb8dfcf..a0704ec 100644 --- a/Aurora/Services/LibraryService.cs +++ b/Aurora/Services/Library/LibraryService.cs @@ -3,22 +3,24 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using Aurora.Models.Media; +using Aurora.Services.Settings; using Aurora.Utils; -namespace Aurora.Services +namespace Aurora.Services.Library { - public class LibraryService : BaseService + public class LibraryService : ILibraryService { #region Fields - private string _pathName = "/Users/brandonwatson/Music/iTunes/iTunes Media/Music"; + private string _pathName; private string _extensions = ".wav,.mp3,.aiff,.flac,.m4a,.m4b,.wma"; private Dictionary _library; #endregion Fields - public LibraryService() + public LibraryService(ISettingsService settingsService) { _library = new Dictionary(); + this._pathName = settingsService.LibraryLocation; LoadLibrary(); } diff --git a/Aurora/Services/Player/IPlayer.cs b/Aurora/Services/Player/IPlayer.cs new file mode 100644 index 0000000..3698ccc --- /dev/null +++ b/Aurora/Services/Player/IPlayer.cs @@ -0,0 +1,62 @@ +using System; +using System.Threading.Tasks; +using System.Threading; +using Grpc.Core; +using Aurora.Models.Media; + +namespace Aurora.Services.Player +{ + + public interface IPlayer + { + + /// + /// Event handler for changing playback states. + /// + event PlaybackStateChangedEventHandler PlaybackStateChanged; + + event MediaChangedEventHandler MediaChanged; + + /// + /// The state of playback + /// + /// + PlaybackState PlaybackState { get; } + + bool IsLoaded { get; } + + bool IsMediaLoaded(BaseMedia media); + + BaseMedia CurrentMedia { get; } + + float CurrentMediaPosition { get; } + + long CurrentMediaLength { get; } + + /// + /// Load media into the media player. + /// + /// Media to load + Task LoadMedia(BaseMedia media); + + /// + /// Play currently loaded media. + /// + void Play(); + + /// + /// Pause currently loaded media. + /// + void Pause(); + + /// + /// Stop currently loaded media. + /// + void Stop(); + + void Enqueue(BaseMedia song); + + void Dequeue(BaseMedia song); + + } +} diff --git a/Aurora/Services/PlayerService/MediaChangedEvent.cs b/Aurora/Services/Player/MediaChangedEvent.cs similarity index 92% rename from Aurora/Services/PlayerService/MediaChangedEvent.cs rename to Aurora/Services/Player/MediaChangedEvent.cs index e78ce14..a598c9a 100644 --- a/Aurora/Services/PlayerService/MediaChangedEvent.cs +++ b/Aurora/Services/Player/MediaChangedEvent.cs @@ -1,7 +1,7 @@ using System; using Aurora.Models.Media; -namespace Aurora.Services.PlayerService +namespace Aurora.Services.Player { public delegate void MediaChangedEventHandler(object source, MediaChangedEventArgs e); diff --git a/Aurora/Services/Player/PlaybackState.cs b/Aurora/Services/Player/PlaybackState.cs new file mode 100644 index 0000000..aed0ad7 --- /dev/null +++ b/Aurora/Services/Player/PlaybackState.cs @@ -0,0 +1,12 @@ +using System; + +namespace Aurora.Services.Player +{ + public enum PlaybackState + { + Playing, + Stopped, + Buffering, + + } +} diff --git a/Aurora/Services/PlayerService/PlaybackStateChangedEvent.cs b/Aurora/Services/Player/PlaybackStateChangedEvent.cs similarity index 92% rename from Aurora/Services/PlayerService/PlaybackStateChangedEvent.cs rename to Aurora/Services/Player/PlaybackStateChangedEvent.cs index 2decee4..a07cf7a 100644 --- a/Aurora/Services/PlayerService/PlaybackStateChangedEvent.cs +++ b/Aurora/Services/Player/PlaybackStateChangedEvent.cs @@ -1,7 +1,7 @@ using System; using Aurora.Models.Media; -namespace Aurora.Services.PlayerService +namespace Aurora.Services.Player { public delegate void PlaybackStateChangedEventHandler(object source, PlaybackStateChangedEventArgs e); diff --git a/Aurora/Services/PlayerService/PlayerService.cs b/Aurora/Services/Player/PlayerService.cs similarity index 95% rename from Aurora/Services/PlayerService/PlayerService.cs rename to Aurora/Services/Player/PlayerService.cs index 9c18a46..83e19b9 100644 --- a/Aurora/Services/PlayerService/PlayerService.cs +++ b/Aurora/Services/Player/PlayerService.cs @@ -3,13 +3,13 @@ using System.Threading.Tasks; using System.Threading; using Grpc.Core; using Aurora.Models.Media; -using Aurora.Proto.Sync; +using Aurora.Proto.Party; using LibVLCSharp.Shared; -namespace Aurora.Services.PlayerService +namespace Aurora.Services.Player { - public class PlayerService : BaseService + public class PlayerService : IPlayer { private const long _ticksPerMillisecond = 10000; private BaseMedia _currentMedia; @@ -85,7 +85,7 @@ namespace Aurora.Services.PlayerService } _currentMedia = media; await _currentMedia.Load(); - var md = new Media(_libvlc, _currentMedia.DataStream); + var md = new LibVLCSharp.Shared.Media(_libvlc, _currentMedia.DataStream); _mediaPlayer = new MediaPlayer(md); _mediaPlayer.Stopped += OnStopped; md.Dispose(); @@ -112,15 +112,15 @@ namespace Aurora.Services.PlayerService RemoteAudio media = _currentMedia as RemoteAudio; if (!media.FromHost) { - RemoteSyncService.RemoteSyncServiceClient remoteSyncClient = media.RemoteSyncClient; + RemotePartyService.RemotePartyServiceClient remotePartyServiceClient = media.RemotePartyServiceClient; //Sync playback in a separate task //Task completes when host stops syncing (when a song is complete) Task syncTask = new Task(async () => { CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); - using (AsyncServerStreamingCall syncStream = remoteSyncClient - .GetMediaSync(new Proto.General.Empty())) + using (AsyncServerStreamingCall syncStream = remotePartyServiceClient + .SyncMedia(new SyncMediaRequest() { })) { try { diff --git a/Aurora/Services/PlayerService/PlaybackState.cs b/Aurora/Services/PlayerService/PlaybackState.cs deleted file mode 100644 index 37c6773..0000000 --- a/Aurora/Services/PlayerService/PlaybackState.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -public enum PlaybackState -{ - Playing, - Stopped, - Buffering, - -} \ No newline at end of file diff --git a/Aurora/Services/Server/Controllers/Constructor.cs b/Aurora/Services/Server/Controllers/Constructor.cs new file mode 100644 index 0000000..2b5e3b2 --- /dev/null +++ b/Aurora/Services/Server/Controllers/Constructor.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.ObjectModel; +using System.Collections.Generic; +using Aurora.Proto.Party; +using Aurora.Services.Library; +using Aurora.Services.Settings; +using Aurora.Models.Media; +using Aurora.Services.EventManager; +using Autofac; + +namespace Aurora.Services.Server.Controllers +{ + public partial class RemotePartyController : RemotePartyService.RemotePartyServiceBase + { + private ILibraryService _libraryService; + private ISettingsService _settingsService; + private IEventManager _eventManager; + + /// + /// Constructor for partial class + /// + public RemotePartyController(string partyName, string description, ILibraryService libraryService, ISettingsService settingsService, IEventManager eventManager) + { + this._startDateTime = DateTime.UtcNow; + this._displayName = partyName; + this._description = description; + this._memberList = new SortedList(); + this._mediaList = new SortedList(); + + _libraryService = libraryService; + this._settingsService = settingsService; + + string userName = _settingsService.Username; + + this._eventManager = eventManager; + + this._hostMember = new Member() + { + Name = GetNewMemberResourceName(_partyResourceName, ServerService.GetLocalIPAddress(), userName), + UserName = userName, + IpAddress = ServerService.GetLocalIPAddress(), + }; + + //Add media from library + //This will change as queuing operation gets solidified + //Simply return the hosts library + ObservableCollection queue = _libraryService.GetLibrary(); + + + foreach (BaseMedia media in queue) + { + AudioMetadata metadata = new AudioMetadata(); + try + { + if (media.Metadata is AudioMetadata) + { + metadata = media.Metadata as AudioMetadata; + + Media data = new Media(); + data.Name = string.Format("{0}/{1}", partyName, media.Id); + if (metadata.Title != null) + { + data.Title = metadata.Title; + } + if (metadata.Artist != null) + { + data.Artist = metadata.Artist; + } + if (metadata.Album != null) + { + data.Album = metadata.Album; + } + if (metadata.Duration != null) + { + data.Duration = metadata.Duration; + } + + _mediaList.Add(data.Name, data); + } + } + catch (Exception ex) + { + Console.WriteLine(string.Format("Error preparing queue: {0}", ex.Message)); + } + } + } + } +} \ No newline at end of file diff --git a/Aurora/Services/Server/Controllers/EventController.cs b/Aurora/Services/Server/Controllers/EventController.cs new file mode 100644 index 0000000..abed097 --- /dev/null +++ b/Aurora/Services/Server/Controllers/EventController.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading.Tasks; +using System.Threading; +using Aurora.Proto.Party; +using Aurora.Utils; + +namespace Aurora.Services.Server.Controllers +{ + public partial class RemotePartyController : RemotePartyService.RemotePartyServiceBase + { + public override Task GetEvents(GetEventsRequest request, Grpc.Core.IServerStreamWriter responseStream, Grpc.Core.ServerCallContext context) + { + string peerId = Misc.Combine(new string[] { context.Peer, request.Parent }); + Console.WriteLine(string.Format("SERVER - Events request received from peer: {0}", peerId)); + + AutoResetEvent are = new AutoResetEvent(false); + Action callback = (BaseEvent bEvent) => + { + Console.WriteLine(string.Format("SERVER - Event fired for peer: {0}", peerId)); + //TODO need to remove callback if stream no longer exists IE. Client crashed or stopped + responseStream.WriteAsync(bEvent); + + }; + + Action cancelled = () => + { + are.Set(); + }; + + this._eventManager.AddEventHandler(callback, cancelled, Misc.Combine(new string[] { context.Peer, request.Parent })); + are.WaitOne(); + return Task.FromResult(null); + } + } +} \ No newline at end of file diff --git a/Aurora/Services/Server/Controllers/EventSubscriptionController.cs b/Aurora/Services/Server/Controllers/EventSubscriptionController.cs new file mode 100644 index 0000000..77c7ca7 --- /dev/null +++ b/Aurora/Services/Server/Controllers/EventSubscriptionController.cs @@ -0,0 +1,66 @@ +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using Aurora.Proto.Party; +using Aurora.Proto.General; +using Aurora.Utils; + +namespace Aurora.Services.Server.Controllers +{ + public partial class RemotePartyController : RemotePartyService.RemotePartyServiceBase + { + public override Task ListEventSubscriptions( + ListEventSubscriptionsRequest request, + Grpc.Core.ServerCallContext context) + { + throw new NotImplementedException(); + } + + public override Task CreateEventSubscription( + CreateEventSubscriptionRequest request, + Grpc.Core.ServerCallContext context) + { + Console.WriteLine(string.Format("SERVER - Subscription from client with id: {0}", request.Parent)); + this._eventManager.AddSubscription(Misc.Combine(new string[] { context.Peer, request.Parent }), request.EventSubscription.Type); + + return Task.FromResult(request.EventSubscription); + } + + public override Task CreateEventSubscriptionList( + CreateEventSubscriptionListRequest request, + Grpc.Core.ServerCallContext context) + { + Console.WriteLine(string.Format("SERVER - Subscription from client with id: {0}", request.Parent)); + + List eventTypes = new List(); + foreach (EventSubscription subscription in request.EventSubscriptions) + { + eventTypes.Add(subscription.Type); + } + + this._eventManager.AddSubscriptionList(Misc.Combine(new string[] { context.Peer, request.Parent }), eventTypes); + + CreateEventSubscriptionListResponse resp = new CreateEventSubscriptionListResponse(); + resp.EventSubscriptions.AddRange(request.EventSubscriptions); + return Task.FromResult(resp); + } + + public override Task DeleteEventSubscription( + DeleteEventSubscriptionRequest request, + Grpc.Core.ServerCallContext context) + { + this._eventManager.RemoveSubscription(Misc.Combine(new string[] { context.Peer, request.Parent }), request.Type); + + return Task.FromResult(new Empty()); + } + + public override Task DeleteAllEventSubscriptions( + DeleteAllEventSubscriptionsRequest request, + Grpc.Core.ServerCallContext context) + { + this._eventManager.RemoveAllSubscriptions(Misc.Combine(new string[] { context.Peer, request.Parent })); + + return Task.FromResult(new Empty()); + } + } +} \ No newline at end of file diff --git a/Aurora/Services/Server/Controllers/MediaController.cs b/Aurora/Services/Server/Controllers/MediaController.cs new file mode 100644 index 0000000..35ea244 --- /dev/null +++ b/Aurora/Services/Server/Controllers/MediaController.cs @@ -0,0 +1,143 @@ +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using Aurora.Proto.Party; +using Aurora.Models.Media; +using Aurora.Proto.General; +using Aurora.Services.Library; +using Aurora.Services.Player; +using Autofac; + +namespace Aurora.Services.Server.Controllers +{ + public partial class RemotePartyController : RemotePartyService.RemotePartyServiceBase + { + private SortedList _mediaList; + + public override Task ListMedia(ListMediaRequest request, Grpc.Core.ServerCallContext context) + { + 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 mediaList = new List(_mediaList.Values); + resp.Media.AddRange(mediaList.GetRange(startIdx, pageSize)); + + resp.NextPageToken = resp.Media[resp.Media.Count - 1].Name; + return Task.FromResult(resp); + } + + public override Task GetMedia(GetMediaRequest request, Grpc.Core.ServerCallContext context) + { + _mediaList.TryGetValue(request.Name, out Media baseMedia); + + if (baseMedia == null) + { + throw new KeyNotFoundException(); + } + + return Task.FromResult(baseMedia); + } + + public override Task CreateMedia(CreateMediaRequest request, Grpc.Core.ServerCallContext context) + { + throw new NotImplementedException(); + } + + public override Task DeleteMedia(DeleteMediaRequest request, Grpc.Core.ServerCallContext context) + { + throw new NotImplementedException(); + } + + public override async Task StreamMedia(StreamMediaRequest request, Grpc.Core.IServerStreamWriter responseStream, Grpc.Core.ServerCallContext context) + { + using (var scope = App.Container.BeginLifetimeScope()) + { + ILibraryService library = scope.Resolve(); + + string mediaName = request.Name.Split('/')[1]; + + BaseMedia originalSong = library.GetSong(mediaName); + 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 responseStream, Grpc.Core.ServerCallContext context) + { + bool continueSync = true; + using (var scope = App.Container.BeginLifetimeScope()) + { + IPlayer player = scope.Resolve(); + + 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); + } + } + + } + } +} \ No newline at end of file diff --git a/Aurora/Services/Server/Controllers/MemberController.cs b/Aurora/Services/Server/Controllers/MemberController.cs new file mode 100644 index 0000000..f878101 --- /dev/null +++ b/Aurora/Services/Server/Controllers/MemberController.cs @@ -0,0 +1,129 @@ +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using Aurora.Proto.Party; +using Aurora.Proto.General; +using Aurora.Utils; +using Grpc.Core; +using Google.Protobuf.WellKnownTypes; + +namespace Aurora.Services.Server.Controllers +{ + public partial class RemotePartyController : RemotePartyService.RemotePartyServiceBase + { + private SortedList _memberList; + + public override Task ListMembers(ListMembersRequest request, Grpc.Core.ServerCallContext context) + { + //Ignoring parent field because there is only one instance of the party + ListMembersResponse resp = new ListMembersResponse(); + + //Determine start idx + int startIdx = 0; + if (!string.IsNullOrEmpty(request.PageToken)) + { + startIdx = _memberList.IndexOfKey(request.PageToken) + 1; + } + + int pageSize = request.PageSize; + + //Assign pageSize + if (pageSize > _memberList.Count) + { + pageSize = _memberList.Count; + } + + + //Gather page + List members = new List(_memberList.Values); + resp.Members.AddRange(members.GetRange(startIdx, pageSize)); + + //Set next page token + resp.NextPageToken = resp.Members[resp.Members.Count - 1].Name; + + return Task.FromResult(resp); + } + + public override Task GetMember(GetMemberRequest request, Grpc.Core.ServerCallContext context) + { + _memberList.TryGetValue(request.Name, out Member member); + + if (member == null) + { + throw new KeyNotFoundException(); + } + + return Task.FromResult(member); + } + + public override Task UpdateMember(UpdateMemberRequest request, Grpc.Core.ServerCallContext context) + { + throw new NotImplementedException(); + } + + public override Task CreateMember(CreateMemberRequest request, Grpc.Core.ServerCallContext context) + { + //Generate Guid + string resourceName = GetNewMemberResourceName(request.Parent, context.Peer, request.Member.UserName); + + //Check if already added + if (_memberList.ContainsKey(resourceName)) + { + throw new RpcException(new Status(StatusCode.AlreadyExists, "Member already exists")); + } + + request.Member.Name = resourceName; + request.Member.AddedOn = Timestamp.FromDateTime(DateTime.UtcNow); + request.Member.IpAddress = context.Host; + + _memberList.Add(resourceName, request.Member); + + BaseEvent @event = new BaseEvent + { + EventType = EventType.MemberCreated, + MemberCreatedEvent = new MemberCreatedEvent + { + Member = request.Member, + } + }; + + //Fire event manager event + this._eventManager.FireEvent(@event); + + return Task.FromResult(request.Member); + } + + public override Task DeleteMember(DeleteMemberRequest request, Grpc.Core.ServerCallContext context) + { + string memberResourceName = request.Name; + //Check if member exists + if (!_memberList.ContainsKey(request.Name)) + { + throw new RpcException(new Status(StatusCode.NotFound, "Member not found")); + } + + _memberList.Remove(memberResourceName); + + BaseEvent @event = new BaseEvent + { + EventType = EventType.MemberDeleted, + MemberDeletedEvent = new MemberDeletedEvent + { + MemberName = memberResourceName, + } + }; + + _eventManager.FireEvent(@event); + _eventManager.RemoveAllSubscriptions(memberResourceName); + _eventManager.CancelEventStream(memberResourceName); + + return Task.FromResult(new Aurora.Proto.General.Empty()); + } + + private string GetNewMemberResourceName(string parent, string contextPeer, string userName) + { + string memberNameGuid = HashUtil.GetHash(new string[] { contextPeer, userName }).ToString(); + return string.Format("{0}/members/{1}", parent, memberNameGuid); + } + } +} \ No newline at end of file diff --git a/Aurora/Services/Server/Controllers/PartyController.cs b/Aurora/Services/Server/Controllers/PartyController.cs new file mode 100644 index 0000000..1ca6eeb --- /dev/null +++ b/Aurora/Services/Server/Controllers/PartyController.cs @@ -0,0 +1,33 @@ +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using Aurora.Proto.Party; +using Google.Protobuf.WellKnownTypes; + + +namespace Aurora.Services.Server.Controllers +{ + public partial class RemotePartyController : RemotePartyService.RemotePartyServiceBase + { + private string _partyResourceName = "party/party1"; + private string _displayName; + private string _description; + private Member _hostMember; + private DateTime _startDateTime; + + public override Task GetParty(Proto.General.Empty request, Grpc.Core.ServerCallContext context) + { + Party party = new Party() + { + Name = _partyResourceName, + DisplayName = this._displayName, + Description = this._description, + HostIp = ServerService.GetLocalIPAddress(), + HostMember = this._hostMember, + CreatedOn = Timestamp.FromDateTime(_startDateTime) + }; + + return Task.FromResult(party); + } + } +} \ No newline at end of file diff --git a/Aurora/Services/Server/Server/IServerService.cs b/Aurora/Services/Server/Server/IServerService.cs new file mode 100644 index 0000000..daf444f --- /dev/null +++ b/Aurora/Services/Server/Server/IServerService.cs @@ -0,0 +1,27 @@ +using System.Threading.Tasks; + +namespace Aurora.Services.Server +{ + public interface IServerService + { + int Port { get; } + + string Hostname { get; } + + bool Initialized { get; } + + + /// + /// Start Server + /// + void Start(string partyName, string description); + + /// + /// Shutdown server async. + /// + /// Task + Task Stop(); + + Task Reset(); + } +} \ No newline at end of file diff --git a/Aurora/Services/ServerService.cs b/Aurora/Services/Server/Server/ServerService.cs similarity index 57% rename from Aurora/Services/ServerService.cs rename to Aurora/Services/Server/Server/ServerService.cs index 3205ea1..3921bc7 100644 --- a/Aurora/Services/ServerService.cs +++ b/Aurora/Services/Server/Server/ServerService.cs @@ -3,45 +3,42 @@ using System.Threading.Tasks; using System.Net; using System.Net.Sockets; using Grpc.Core; -using Aurora.RemoteImpl; -using Aurora.Proto.Events; +using Aurora.Services.Server.Controllers; +using Aurora.Services.Settings; +using Aurora.Services.Library; +using Aurora.Services.EventManager; using Aurora.Proto.Party; -using Aurora.Proto.Playback; -using Aurora.Proto.Sync; -namespace Aurora.Services +namespace Aurora.Services.Server { - public class ServerService : BaseService + public class ServerService : IServerService { - private int _port = SettingsService.Instance.DefaultPort; + private int _port = 8080; private string _hostname; private Grpc.Core.Server _server; + private ILibraryService _libraryService; + private ISettingsService _settingsService; + private IEventManager _eventManager; //Implementation class declarations - private RemotePartyServiceImpl _remotePartyServiceImpl; - private RemoteEventServiceImpl _remoteEventImpl; - private RemotePlaybackServiceImpl _remotePlaybackImpl; - private RemoteSyncServiceImpl _remoteSyncImpl; + private RemotePartyController _remotePartyController; /// /// Constructor. Registers GRPC service implementations. /// - public ServerService() + public ServerService(ILibraryService libraryService, ISettingsService settingsService, IEventManager eventManager) { string host = GetLocalIPAddress(); - + this._libraryService = libraryService; + this._settingsService = settingsService; + this._eventManager = eventManager; if (string.IsNullOrWhiteSpace(host)) { throw new Exception("This device must have a valid IP address"); } _hostname = host; - - _server = new Grpc.Core.Server - { - Ports = { new ServerPort(_hostname, _port, ServerCredentials.Insecure) } - }; } public int Port @@ -58,8 +55,7 @@ namespace Aurora.Services { get { - return (_remoteEventImpl != null && - _remotePartyServiceImpl != null && + return (_remotePartyController != null && _server != null); } } @@ -68,27 +64,30 @@ namespace Aurora.Services /// /// Start Server /// - public void Start() + public void Start(string partyName, string description) { try { Console.WriteLine(string.Format("Starting gRPC server at hostname: {0}, port: {1}", _hostname, _port)); - if (!Initialized) + _server = new Grpc.Core.Server { - //Construct implementations - _remotePartyServiceImpl = new RemotePartyServiceImpl(); - _remoteEventImpl = new RemoteEventServiceImpl(); - _remotePlaybackImpl = new RemotePlaybackServiceImpl(); - _remoteSyncImpl = new RemoteSyncServiceImpl(); + Ports = { new ServerPort(_hostname, _port, ServerCredentials.Insecure) } + }; + + //Construct implementations + _remotePartyController = new RemotePartyController( + partyName, + description, + _libraryService, + _settingsService, + _eventManager); + + // Register grpc RemoteService with singleton server service + RegisterService(RemotePartyService.BindService(_remotePartyController)); + - // Register grpc RemoteService with singleton server service - RegisterService(RemotePartyService.BindService(_remotePartyServiceImpl)); - RegisterService(RemoteEventService.BindService(_remoteEventImpl)); - RegisterService(RemotePlaybackService.BindService(_remotePlaybackImpl)); - RegisterService(RemoteSyncService.BindService(_remoteSyncImpl)); - } _server.Start(); } catch (Exception ex) @@ -103,7 +102,15 @@ namespace Aurora.Services /// Task public async Task Stop() { - await _server.ShutdownAsync(); + try + { + await _server.ShutdownAsync(); + await _server.ShutdownTask; + } + catch (Exception ex) + { + Console.WriteLine(string.Format("Error stopping gRPC server: {0}", ex.Message)); + } } public async Task Reset() diff --git a/Aurora/Services/Settings/ISettingsService.cs b/Aurora/Services/Settings/ISettingsService.cs new file mode 100644 index 0000000..4036b63 --- /dev/null +++ b/Aurora/Services/Settings/ISettingsService.cs @@ -0,0 +1,29 @@ +using Plugin.Settings.Abstractions; + +namespace Aurora.Services.Settings +{ + public interface ISettingsService + { + ISettings AppSettings { get; set; } + + /// + /// The user's username. This is persisted. + /// + /// + string Username { get; set; } + + /// + /// The default port to use. This is persisted. + /// + /// + int DefaultPort { get; set; } + + /// + /// The current sessions clientId. This is assigned by the server. This is not persisted. + /// + /// + string ClientName { get; set; } + + string LibraryLocation { get; set; } + } +} \ No newline at end of file diff --git a/Aurora/Services/SettingsService.cs b/Aurora/Services/Settings/SettingsService.cs similarity index 80% rename from Aurora/Services/SettingsService.cs rename to Aurora/Services/Settings/SettingsService.cs index ae8c768..a7930c1 100644 --- a/Aurora/Services/SettingsService.cs +++ b/Aurora/Services/Settings/SettingsService.cs @@ -3,14 +3,16 @@ using System.Threading; using Plugin.Settings; using Plugin.Settings.Abstractions; -namespace Aurora.Services +namespace Aurora.Services.Settings { - public class SettingsService : BaseService + public class SettingsService : ISettingsService { private Lazy _appSettings; private string _usernameKey = "username"; private string _defaultPortKey = "port"; + private string _libraryLocationKey = "libraryLocation"; + public SettingsService() { } @@ -55,11 +57,17 @@ namespace Aurora.Services set { AppSettings.AddOrUpdateValue(_defaultPortKey, value); } } + public string LibraryLocation + { + get { return AppSettings.GetValueOrDefault(_libraryLocationKey, "~/Music"); } + set { AppSettings.AddOrUpdateValue(_libraryLocationKey, value); } + } + /// /// The current sessions clientId. This is assigned by the server. This is not persisted. /// /// - public string ClientId + public string ClientName { get; set; } diff --git a/Aurora/Utils/HashUtil.cs b/Aurora/Utils/HashUtil.cs new file mode 100644 index 0000000..0644870 --- /dev/null +++ b/Aurora/Utils/HashUtil.cs @@ -0,0 +1,25 @@ +using System.Security.Cryptography; +using System.Text; +using System; + +namespace Aurora.Utils +{ + public class HashUtil + { + public static Guid GetHash(string[] inputs) + { + string input = ""; + foreach (string str in inputs) + { + input += str; + } + + byte[] stringbytes = Encoding.UTF8.GetBytes(input); + byte[] hashedBytes = new System.Security.Cryptography + .SHA1CryptoServiceProvider() + .ComputeHash(stringbytes); + Array.Resize(ref hashedBytes, 16); + return new Guid(hashedBytes); + } + } +} diff --git a/aurora.code-workspace b/aurora.code-workspace index c4facab..e01f5db 100644 --- a/aurora.code-workspace +++ b/aurora.code-workspace @@ -9,6 +9,9 @@ "**/bin": true, "**/obj": true, "**/packages": true - } + }, + "nxunitExplorer.nunit": "/Users/brandonwatson/Documents/Gitlab/Aurora/aurora-sharp-desktop/Aurora.test/bin/Debug/nunit.framework.dll", + "nxunitExplorer.logpanel": true, + "dotnet-test-explorer.testProjectPath": "./Aurora.test" } } \ No newline at end of file