Merge branch 'feature/proto.v2'

This commit is contained in:
watsonb8 2020-02-02 17:28:56 -05:00
commit aae221801a
93 changed files with 2117 additions and 998 deletions

View File

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

View File

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

View File

@ -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

View File

@ -0,0 +1,117 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="nunit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="Grpc.Core" Version="2.26.0" />
<PackageReference Include="Grpc.Tools" Version="2.26.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Grpc" Version="2.26.0" />
<PackageReference Include="Autofac" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<Protobuf Remove="..\Aurora\Proto\general.proto" />
<Protobuf Remove="..\Aurora\Proto\party.v2.proto" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Aurora\Aurora.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="Resources\Jidenna\The Chief\01 A Bull%27s Tale.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Jidenna\The Chief\04 Bambi.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Jidenna\The Chief\14 Bully Of The Earth.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Jidenna\The Chief\12 Some Kind Of Way.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Jidenna\The Chief\03 Trampoline.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Jidenna\The Chief\09 Safari.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Jidenna\The Chief\05 Helicopters _ Beware.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Jidenna\The Chief\08 The Let Out.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Jidenna\The Chief\10 Adaora.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Jidenna\The Chief\07 2 Points.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Jidenna\The Chief\06 Long Live The Chief.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Jidenna\The Chief\02 Chief Don%27t Run.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Jidenna\The Chief\11 Little Bit More.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Jidenna\The Chief\13 White Niggas.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\16 BDE Bonus %28Prod. By_ ID Labs%29.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\11 Play Ya Cards %28Prod By_ Chuck Inglish%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\08 All Around The World %28Prod. By_ Just Blaze%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\10 In The Air %28Prod By_ Ritz Reynolds%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\05 I%27ll Be There %28feat. Phonte%29 %28Prod. By_ Beanz %27n%27 Kornbread%29.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\03 Donald Trump %28Prod. By_ Sap%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\13 Life Ain%27t Easy %28Prod. By_ ID Labs%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\09 Down The Rabbit Hole %28Prod. By_ Blue of The Sore Losers%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\07 Wake Up %28Prod By_ Sap &amp; ID Labs%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\04 Oy Vey %28Prod By_ ID Labs%29.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\01 Best Day Ever %28Prod. By_ ID Labs%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\02 Get Up %28Prod. By_ Teddy Roxpin%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\14 Snooze %28Prod By_ ID Labs%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\12 She Said %28Prod By_ Khrysis%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\15 Keep Floatin%27 %28feat. Wiz Khalifa%29 %28Prod. By_ ID Labs%29.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Mac Miller\Best Day Ever\06 Wear My Hat %28Prod. By_ Chuck Inglish%29.m4a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -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<IEventManager>();
//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<BaseEvent> 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);
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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");
}
}
}

View File

@ -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<ServerService>().As<IServerService>().SingleInstance();
builder.RegisterInstance<ISettingsService>(new SettingsServiceMock()
{
Username = "Test User 1",
DefaultPort = 4005,
LibraryLocation = string.Format("{0}/Resources", Directory.GetCurrentDirectory())
}).SingleInstance();
builder.RegisterType<LibraryService>().As<ILibraryService>().SingleInstance();
builder.RegisterType<EventManager>().As<IEventManager>().SingleInstance();
return builder.Build();
}
public static RemotePartyService.RemotePartyServiceClient Setup(ref IContainer container, ref IServerService serverService, ref Channel channel)
{
serverService = container.Resolve<IServerService>();
serverService.Start("testParty", "asdf");
channel = new Channel(string.Format("{0}:{1}", ServerService.GetLocalIPAddress(), 8080), ChannelCredentials.Insecure);
return new RemotePartyService.RemotePartyServiceClient(channel);
}
}
}

View File

@ -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();
}
}
}

View File

@ -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; }
/// <summary>
/// The user's username. This is persisted.
/// </summary>
/// <value></value>
public string Username { get; set; }
/// <summary>
/// The default port to use. This is persisted.
/// </summary>
/// <value></value>
public int DefaultPort { get; set; }
/// <summary>
/// The current sessions clientId. This is assigned by the server. This is not persisted.
/// </summary>
/// <value></value>
public string ClientName { get; set; }
public string LibraryLocation { get; set; }
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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<IPlayer>(new PlayerService()).SingleInstance();
_builder.RegisterType<PlayerService>().As<IPlayer>().SingleInstance();
_builder.RegisterType<SettingsService>().As<ISettingsService>().SingleInstance();
_builder.RegisterType<LibraryService>().As<ILibraryService>().SingleInstance();
_builder.RegisterType<EventManager>().As<IEventManager>().SingleInstance();
_builder.RegisterType<ServerService>().As<IServerService>().SingleInstance();
_builder.RegisterType<ClientService>().As<IClientService>().SingleInstance();
_builder.RegisterType<MainView>().SingleInstance();
_builder.RegisterType<AlbumsViewModel>();
_builder.RegisterType<ArtistsViewModel>();
_builder.RegisterType<PartyViewModel>();
_builder.RegisterType<ProfileViewModel>();
_builder.RegisterType<SongsViewModel>();
_builder.RegisterType<StationsViewModel>();
// _builder.RegisterInstance<ISettingsService>(new SettingsService()).SingleInstance();
_container = _builder.Build();
MainPage = _container.Resolve<MainView>();
}
public static IContainer Container
{
get { return _container; }
}
protected override void OnStart()

View File

@ -23,6 +23,7 @@
<PackageReference Include="Sharpnado.Forms.HorizontalListView" Version="1.3.0" />
<PackageReference Include="DLToolkit.Forms.Controls.FlowListView" Version="2.0.11" />
<PackageReference Include="CarouselView.FormsPlugin" Version="5.2.0" />
<PackageReference Include="Autofac" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Design\" />
@ -51,7 +52,7 @@
<Folder Include="Design\Components\TabView\" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Design\Behaviors\DeselectItemBehaviorBase.cs" />
<Compile Remove="Design\Behaviors\DeselectItemBehaviorBase.cs" />
</ItemGroup>
<ItemGroup>
<Compile Update="Design\Components\MusicPlayer\Player.xaml.cs">
@ -61,15 +62,12 @@
<ItemGroup>
<Protobuf Include="Proto\general.proto" />
<Protobuf Include="Proto\party.proto" />
<Protobuf Include="Proto\events.proto" />
<Protobuf Include="Proto\playback.proto" />
<Protobuf Include="Proto\sync.proto" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\backward.png" />
<EmbeddedResource Include="Resources\forwards.png" />
<EmbeddedResource Include="Resources\like.png" />
<EmbeddedResource Include="Resources\play.png" />
<EmbeddedResource Include="Resources\backward.png" />
<EmbeddedResource Include="Resources\forwards.png" />
<EmbeddedResource Include="Resources\like.png" />
<EmbeddedResource Include="Resources\play.png" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Design\Components\NavigationMenu\NavigationMenu.css">
@ -77,11 +75,11 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Remove="Design\Resources\unselected.png" />
<None Remove="Design\Components\MediaPlayer\play.png" />
<None Remove="Resources\backward.png" />
<None Remove="Resources\forwards.png" />
<None Remove="Resources\like.png" />
<None Remove="Resources\play.png" />
<None Remove="Design\Resources\unselected.png" />
<None Remove="Design\Components\MediaPlayer\play.png" />
<None Remove="Resources\backward.png" />
<None Remove="Resources\forwards.png" />
<None Remove="Resources\like.png" />
<None Remove="Resources\play.png" />
</ItemGroup>
</Project>

View File

@ -24,7 +24,7 @@ namespace Aurora.Design.Components.MemberList
/// <returns></returns>
public static readonly BindableProperty MembersProperty =
BindableProperty.Create(propertyName: "Members",
returnType: typeof(ObservableCollection<PartyMember>),
returnType: typeof(ObservableCollection<Member>),
declaringType: typeof(MemberList),
defaultBindingMode: BindingMode.Default,
propertyChanged: OnMembersChanged);
@ -33,11 +33,11 @@ namespace Aurora.Design.Components.MemberList
/// Backing property for MembersProperty
/// </summary>
/// <value></value>
public ObservableCollection<PartyMember> Members
public ObservableCollection<Member> Members
{
get
{
return (ObservableCollection<PartyMember>)GetValue(MembersProperty);
return (ObservableCollection<Member>)GetValue(MembersProperty);
}
set
{

View File

@ -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<int, BaseViewModel> _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);
}

View File

@ -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<PartyMember> _members;
private ObservableCollection<Member> _members;
private ObservableCollection<BaseMedia> _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<BaseEvent.DerivedEventOneofCase, EventHandler> _eventHandlers;
private int _selectedTabIndex;
public PartyViewModel()
public PartyViewModel(
ISettingsService settingsService,
IServerService serverService,
IEventManager eventManager,
IClientService clientService)
{
_members = new ObservableCollection<PartyMember>();
_members = new ObservableCollection<Member>();
_queue = new ObservableCollection<BaseMedia>();
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, EventHandler>()
{
{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
/// </summary>
/// <value></value>
public ObservableCollection<PartyMember> Members
public ObservableCollection<Member> 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
/// <returns></returns>
public override Task OnInactive()
{
_client.StopEvents();
this._eventCancellationTokenSource.Cancel();
return Task.FromResult<object>(null);
}
@ -182,7 +195,7 @@ namespace Aurora.Design.Views.Party
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public void OnRemoteMediaPaused(object sender, MediaPausedEventArgs args)
public void OnRemoteMediaPaused(BaseEvent e)
{
StopPlaying();
}
@ -192,9 +205,9 @@ namespace Aurora.Design.Views.Party
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public void OnNewRemoteMediaPlaying(object sender, NewMediaPlayingEventArgs args)
public void OnNewRemoteMediaPlaying(BaseEvent e)
{
PlayFromBeginning(GetMediaFromQueue(args.Event.Media.Id));
PlayFromBeginning(GetMediaFromQueue(e.NewMediaPlayingEvent.Media.Name));
}
/// <summary>
@ -202,7 +215,7 @@ namespace Aurora.Design.Views.Party
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public void OnRemoteMediaResumed(object sender, MediaResumedEventArgs args)
public void OnRemoteMediaResumed(BaseEvent e)
{
PlayResume();
}
@ -212,17 +225,9 @@ namespace Aurora.Design.Views.Party
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
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);
}
/// <summary>
@ -230,10 +235,10 @@ namespace Aurora.Design.Views.Party
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
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);
}
/// <summary>
/// Refresh members list.
/// </summary>
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);
}
/// <summary>
/// Asynchronous function for processing events off of the event stream.
/// </summary>
/// <returns></returns>
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<BaseEvent> 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
}
}

View File

@ -22,6 +22,14 @@
<Entry
Text="{Binding Port}"/>
</StackLayout>
<StackLayout
Orientation="Horizontal">
<Label
VerticalOptions="Center"
Text="Path to Library"/>
<Entry
Text="{Binding LibraryPath}"/>
</StackLayout>
</StackLayout>
</ContentView.Content>
</ContentView>

View File

@ -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");
}
}
}
}

View File

@ -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<BaseMedia> _songsList;
private BaseMedia _selectedSong;
private ILibraryService _libraryService;
#endregion Fields
#region Constructor
public SongsViewModel()
public SongsViewModel(ILibraryService libraryService)
{
_songsList = new ObservableCollection<BaseMedia>();
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

View File

@ -10,6 +10,7 @@ namespace Aurora.Models.Media
public BaseMedia()
{
//TODO need to make sure this is unique
Id = Guid.NewGuid().ToString();
}

View File

@ -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
{

View File

@ -5,17 +5,17 @@ namespace Aurora.Proto.Party
/// <summary>
/// Partial PartyMember class with a constructor that generates a new id
/// </summary>
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();
}
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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()
{
}
/// <summary>
/// RPC for getting event stream for a particular client.
/// </summary>
/// <param name="request">Empty</param>
/// <param name="responseStream">The response stream</param>
/// <param name="context">gRPC client context</param>
/// <returns></returns>
public override Task GetEvents(EventsRequest request,
Grpc.Core.IServerStreamWriter<BaseEvent> 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<BaseEvent> 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<object>(null);
}
/// <summary>
/// RPC for subscribing to remote events
/// </summary>
/// <param name="request">SubscribeRequest</param>
/// <param name="context">gRPC client context</param>
/// <returns></returns>
public override Task<SubscriptionResponse> 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 });
}
/// <summary>
/// RPC for unsubscibing from events
/// </summary>
/// <param name="request">UnsubscribeRequest</param>
/// <param name="context">gRPC client context</param>
/// <returns></returns>
public override Task<SubscriptionResponse> 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 });
}
/// <summary>
/// RPC for unsubscribing from all events
/// </summary>
/// <param name="request">UnsubscribeAllRequest</param>
/// <param name="context">gRPC client context</param>
/// <returns></returns>
public override Task<SubscriptionResponse> 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 });
}
}
}

View File

@ -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
{
/// <summary>
/// Dictionary of party members. Key -> ClientId
/// </summary>
private ObservableCollection<PartyMember> _partyMembers;
public RemotePartyServiceImpl()
{
_partyMembers = new ObservableCollection<PartyMember>();
}
public ObservableCollection<PartyMember> PartyMembers
{
get
{
return _partyMembers;
}
}
public override Task<JoinPartyResponse> 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<LeavePartyResponse> 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<MembersResponse> GetPartyMembers(Proto.General.Empty empty, Grpc.Core.ServerCallContext context)
{
MembersResponse response = new MembersResponse();
response.Members.AddRange(_partyMembers);
return Task.FromResult(response);
}
public override Task<QueueResponse> GetQueue(Proto.General.Empty empty, Grpc.Core.ServerCallContext context)
{
//This will change as queuing operation gets solidified
//Simply return the hosts library
ObservableCollection<BaseMedia> 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);
}
}
}

View File

@ -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<Chunk> 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);
}
}
}
}

View File

@ -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
{
/// <summary>
/// RPC for getting a stream of media syncs
/// </summary>
/// <param name="request"></param>
/// <param name="responseStream"></param>
/// <param name="context"></param>
/// <returns></returns>
public override async Task GetMediaSync(Empty request,
Grpc.Core.IServerStreamWriter<Sync> 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);
}
}
}
}

View File

@ -1,22 +0,0 @@
using System;
namespace Aurora.Services
{
public abstract class BaseService<T> where T : class
{
private static volatile Lazy<T> _instance = new Lazy<T>(() => CreateInstanceOfT());
public static T Instance
{
get { return _instance.Value; }
}
private static T CreateInstanceOfT()
{
return Activator.CreateInstance(typeof(T), true) as T;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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();
}
}

View File

@ -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<ClientService>
{
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;
}
/// <summary>
/// Asynchronous function for processing events off of the event stream.
/// </summary>
/// <returns></returns>
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<BaseEvent> 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<BaseEvent.DerivedEventOneofCase, EventInfo> events = new Dictionary<BaseEvent.DerivedEventOneofCase, EventInfo>()
{
{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();
}
}
}
}

View File

@ -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; }
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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<BaseEvent> callback, Action cancel)
{
Callback = callback;
Cancel = cancel;
}
public Action<BaseEvent> Callback { get; set; }
public Action Cancel { get; set; }
}
}

View File

@ -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<BaseEvent> callback, Action cancel)
{
Callback = callback;
Cancel = cancel;
}
public Action<BaseEvent> Callback { get; set; }
public Action Cancel { get; set; }
}
public class EventManager : BaseService<EventManager>
public class EventManager : IEventManager
{
#region Fields
private Dictionary<string, List<EventType>> _subscriptionList;
@ -38,32 +27,32 @@ namespace Aurora.Services.EventManager
#region Public Methods
/// <summary>
/// Get the list of event type subscriptions for a given session id.
/// Get the list of event type subscriptions for a given sessionIdentifier id.
/// </summary>
/// <param name="session">Session Id</param>
/// <param name="sessionIdentifier">sessionIdentifier Id</param>
/// <returns></returns>
public List<EventType> GetSubscriptionList(string session)
public List<EventType> GetSubscriptionList(string sessionIdentifier)
{
List<EventType> eventList = new List<EventType>();
if (_subscriptionList.ContainsKey(session))
if (_subscriptionList.ContainsKey(sessionIdentifier))
{
_subscriptionList.TryGetValue(session, out eventList);
_subscriptionList.TryGetValue(sessionIdentifier, out eventList);
}
return eventList;
}
/// <summary>
/// Get the number of event subscriptions for a given session
/// Get the number of event subscriptions for a given sessionIdentifier
/// </summary>
/// <param name="session">Session Id</param>
/// <param name="sessionIdentifier">sessionIdentifier Id</param>
/// <returns></returns>
public int GetSubscriptionCount(string session)
public int GetSubscriptionCount(string sessionIdentifier)
{
List<EventType> eventList = new List<EventType>();
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
/// <summary>
/// Add a new subscription
/// </summary>
/// <param name="session"></param>
/// <param name="sessionIdentifier"></param>
/// <param name="type"></param>
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<EventType> eventList = new List<EventType>();
eventList.Add(type);
_subscriptionList.Add(session, eventList);
_subscriptionList.Add(sessionIdentifier, eventList);
success = true;
}
else
{
_subscriptionList.TryGetValue(session, out List<EventType> eventList);
_subscriptionList.TryGetValue(sessionIdentifier, out List<EventType> eventList);
if (eventList != null)
{
eventList.Add(type);
@ -104,79 +93,82 @@ namespace Aurora.Services.EventManager
/// <summary>
/// Add a list of subscriptions. This unsubscribes from unused events.
/// </summary>
/// <param name="session">The browser session id.</param>
/// <param name="sessionIdentifier">The browser sessionIdentifier id.</param>
/// <param name="types">The list of event types to subscribe to.</param>
public void AddSubscriptionList(string session, List<EventType> types)
public void AddSubscriptionList(string sessionIdentifier, List<EventType> types)
{
RemoveAllSubscriptions(session);
RemoveAllSubscriptions(sessionIdentifier);
foreach (EventType e in types)
{
AddSubscription(session, e);
AddSubscription(sessionIdentifier, e);
}
}
/// <summary>
/// Unsubscribe from a given event type.
/// </summary>
/// <param name="session">Session Id</param>
/// <param name="sessionIdentifier">sessionIdentifier Id</param>
/// <param name="type">Event Type to be removed</param>
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<EventType> 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<EventType> types)
public void RemoveSubscriptionList(string sessionIdentifier, List<EventType> types)
{
foreach (EventType e in types)
{
RemoveSubscription(session, e);
RemoveSubscription(sessionIdentifier, e);
}
}
/// <summary>
/// Remove all subscriptons for a given session.
/// Remove all subscriptons for a given sessionIdentifier.
/// </summary>
/// <param name="session">Session Id</param>
public void RemoveAllSubscriptions(string session)
/// <param name="sessionIdentifier">sessionIdentifier Id</param>
public void RemoveAllSubscriptions(string sessionIdentifier)
{
if (_subscriptionList.ContainsKey(session))
if (_subscriptionList.ContainsKey(sessionIdentifier))
{
_subscriptionList.Remove(session);
_subscriptionList.Remove(sessionIdentifier);
}
}
public void AddEventHandler(Action<BaseEvent> action, Action cancel, string sessionId)
public void AddEventHandler(Action<BaseEvent> 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)

View File

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using Aurora.Proto.Party;
namespace Aurora.Services.EventManager
{
public interface IEventManager
{
/// <summary>
/// Get the list of event type subscriptions for a given sessionIdentifier id.
/// </summary>
/// <param name="sessionIdentifier">sessionIdentifier Id</param>
/// <returns></returns>
List<EventType> GetSubscriptionList(string sessionIdentifier);
/// <summary>
/// Get the number of event subscriptions for a given sessionIdentifier
/// </summary>
/// <param name="sessionIdentifier">sessionIdentifier Id</param>
/// <returns></returns>
int GetSubscriptionCount(string sessionIdentifier);
/// <summary>
/// Add a new subscription
/// </summary>
/// <param name="sessionIdentifier"></param>
/// <param name="type"></param>
bool AddSubscription(string sessionIdentifier, EventType type);
/// <summary>
/// Add a list of subscriptions. This unsubscribes from unused events.
/// </summary>
/// <param name="sessionIdentifier">The browser sessionIdentifier id.</param>
/// <param name="types">The list of event types to subscribe to.</param>
void AddSubscriptionList(string sessionIdentifier, List<EventType> types);
/// <summary>
/// Unsubscribe from a given event type.
/// </summary>
/// <param name="sessionIdentifier">sessionIdentifier Id</param>
/// <param name="type">Event Type to be removed</param>
void RemoveSubscription(string sessionIdentifier, EventType type);
void RemoveSubscriptionList(string sessionIdentifier, List<EventType> types);
/// <summary>
/// Remove all subscriptons for a given sessionIdentifier.
/// </summary>
/// <param name="sessionIdentifier">sessionIdentifier Id</param>
void RemoveAllSubscriptions(string sessionIdentifier);
void AddEventHandler(Action<BaseEvent> action, Action cancel, string sessionIdentifierId);
void RemoveEventHandler(string sessionIdentifierId);
void CancelEventStream(string sessionIdentifierId);
void FireEvent(BaseEvent bEvent);
}
}

View File

@ -0,0 +1,12 @@
using System.Collections.ObjectModel;
using Aurora.Models.Media;
namespace Aurora.Services.Library
{
public interface ILibraryService
{
ObservableCollection<BaseMedia> GetLibrary();
BaseMedia GetSong(string Id);
}
}

View File

@ -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<LibraryService>
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<string, BaseMedia> _library;
#endregion Fields
public LibraryService()
public LibraryService(ISettingsService settingsService)
{
_library = new Dictionary<string, BaseMedia>();
this._pathName = settingsService.LibraryLocation;
LoadLibrary();
}

View File

@ -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
{
/// <summary>
/// Event handler for changing playback states.
/// </summary>
event PlaybackStateChangedEventHandler PlaybackStateChanged;
event MediaChangedEventHandler MediaChanged;
/// <summary>
/// The state of playback
/// </summary>
/// <value></value>
PlaybackState PlaybackState { get; }
bool IsLoaded { get; }
bool IsMediaLoaded(BaseMedia media);
BaseMedia CurrentMedia { get; }
float CurrentMediaPosition { get; }
long CurrentMediaLength { get; }
/// <summary>
/// Load media into the media player.
/// </summary>
/// <param name="media">Media to load</param>
Task LoadMedia(BaseMedia media);
/// <summary>
/// Play currently loaded media.
/// </summary>
void Play();
/// <summary>
/// Pause currently loaded media.
/// </summary>
void Pause();
/// <summary>
/// Stop currently loaded media.
/// </summary>
void Stop();
void Enqueue(BaseMedia song);
void Dequeue(BaseMedia song);
}
}

View File

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

View File

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

View File

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

View File

@ -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<PlayerService>
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<Sync> syncStream = remoteSyncClient
.GetMediaSync(new Proto.General.Empty()))
using (AsyncServerStreamingCall<Sync> syncStream = remotePartyServiceClient
.SyncMedia(new SyncMediaRequest() { }))
{
try
{

View File

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

View File

@ -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;
/// <summary>
/// Constructor for partial class
/// </summary>
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<string, Member>();
this._mediaList = new SortedList<string, Media>();
_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<BaseMedia> 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));
}
}
}
}
}

View File

@ -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<BaseEvent> 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<BaseEvent> 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<object>(null);
}
}
}

View File

@ -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<ListEventSubscriptionsResponse> ListEventSubscriptions(
ListEventSubscriptionsRequest request,
Grpc.Core.ServerCallContext context)
{
throw new NotImplementedException();
}
public override Task<EventSubscription> 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<CreateEventSubscriptionListResponse> CreateEventSubscriptionList(
CreateEventSubscriptionListRequest request,
Grpc.Core.ServerCallContext context)
{
Console.WriteLine(string.Format("SERVER - Subscription from client with id: {0}", request.Parent));
List<EventType> eventTypes = new List<EventType>();
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<Empty> 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<Empty> DeleteAllEventSubscriptions(
DeleteAllEventSubscriptionsRequest request,
Grpc.Core.ServerCallContext context)
{
this._eventManager.RemoveAllSubscriptions(Misc.Combine(new string[] { context.Peer, request.Parent }));
return Task.FromResult(new Empty());
}
}
}

View File

@ -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<string, Media> _mediaList;
public override Task<ListMediaResponse> 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<Media> mediaList = new List<Media>(_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<Media> 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<Media> CreateMedia(CreateMediaRequest request, Grpc.Core.ServerCallContext context)
{
throw new NotImplementedException();
}
public override Task<Empty> DeleteMedia(DeleteMediaRequest request, Grpc.Core.ServerCallContext context)
{
throw new NotImplementedException();
}
public override async Task StreamMedia(StreamMediaRequest request, Grpc.Core.IServerStreamWriter<Proto.General.Chunk> responseStream, Grpc.Core.ServerCallContext context)
{
using (var scope = App.Container.BeginLifetimeScope())
{
ILibraryService library = scope.Resolve<ILibraryService>();
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<Sync> responseStream, Grpc.Core.ServerCallContext context)
{
bool continueSync = true;
using (var scope = App.Container.BeginLifetimeScope())
{
IPlayer player = scope.Resolve<IPlayer>();
string currentId = player.CurrentMedia.Id;
MediaChangedEventHandler mediaChanged = (sender, e) =>
{
if (e.NewId != currentId)
{
continueSync = false;
}
};
player.MediaChanged += mediaChanged;
while (continueSync)
{
float length = player.CurrentMediaLength;
Sync sync = new Sync()
{
TrackPosition = player.CurrentMediaPosition,
ServerTimeTicks = Utils.TimeUtils.GetNetworkTime().DateTime.Ticks
};
await responseStream.WriteAsync(sync);
Console.WriteLine("Sent Sync");
await Task.Delay(5000);
}
}
}
}
}

View File

@ -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<string, Member> _memberList;
public override Task<ListMembersResponse> 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<Member> members = new List<Member>(_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<Member> 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<Member> UpdateMember(UpdateMemberRequest request, Grpc.Core.ServerCallContext context)
{
throw new NotImplementedException();
}
public override Task<Member> 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<Aurora.Proto.General.Empty> 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);
}
}
}

View File

@ -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<Party> 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);
}
}
}

View File

@ -0,0 +1,27 @@
using System.Threading.Tasks;
namespace Aurora.Services.Server
{
public interface IServerService
{
int Port { get; }
string Hostname { get; }
bool Initialized { get; }
/// <summary>
/// Start Server
/// </summary>
void Start(string partyName, string description);
/// <summary>
/// Shutdown server async.
/// </summary>
/// <returns>Task</returns>
Task Stop();
Task Reset();
}
}

View File

@ -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<ServerService>
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;
/// <summary>
/// Constructor. Registers GRPC service implementations.
/// </summary>
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
/// <summary>
/// Start Server
/// </summary>
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
/// <returns>Task</returns>
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()

View File

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

View File

@ -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<SettingsService>
public class SettingsService : ISettingsService
{
private Lazy<ISettings> _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); }
}
/// <summary>
/// The current sessions clientId. This is assigned by the server. This is not persisted.
/// </summary>
/// <value></value>
public string ClientId
public string ClientName
{
get; set;
}

25
Aurora/Utils/HashUtil.cs Normal file
View File

@ -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);
}
}
}

View File

@ -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"
}
}