More progress on IoC. Controllers are implemented

This commit is contained in:
watsonb8 2020-02-02 10:26:47 -05:00
parent 48d0ffa77d
commit 8231a18c3e
54 changed files with 521 additions and 142 deletions

View File

@ -22,4 +22,96 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Aurora\Aurora.csproj" /> <ProjectReference Include="..\Aurora\Aurora.csproj" />
</ItemGroup> </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> </Project>

View File

@ -1,9 +1,14 @@
using NUnit.Framework; using NUnit.Framework;
using Aurora.Proto.PartyV2; using Aurora.Proto.PartyV2;
using Aurora.Services.Server; using Aurora.Services.Server;
using Aurora.Services.Library;
using Aurora.Services.Settings;
using Aurora.test.Models.Mock;
using Grpc.Core; using Grpc.Core;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Linq; using System.Linq;
using System.IO;
using Autofac;
namespace Aurora.test.ControllerTests namespace Aurora.test.ControllerTests
{ {
@ -11,10 +16,35 @@ namespace Aurora.test.ControllerTests
{ {
private RemotePartyService.RemotePartyServiceClient _remotePartyService; private RemotePartyService.RemotePartyServiceClient _remotePartyService;
private Channel _channel; private Channel _channel;
private IContainer _container;
private IServerService _serverService;
[OneTimeSetUp]
public void 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();
_container = builder.Build();
}
[OneTimeTearDown]
public void TearDownOneTime()
{
_container.Dispose();
}
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
ServerService.Instance.Start("testParty", "asdf"); _serverService = _container.Resolve<IServerService>();
_serverService.Start("testParty", "asdf");
_channel = new Channel(string.Format("{0}:{1}", ServerService.GetLocalIPAddress(), 8080), ChannelCredentials.Insecure); _channel = new Channel(string.Format("{0}:{1}", ServerService.GetLocalIPAddress(), 8080), ChannelCredentials.Insecure);
_remotePartyService = new RemotePartyService.RemotePartyServiceClient(_channel); _remotePartyService = new RemotePartyService.RemotePartyServiceClient(_channel);
} }
@ -22,8 +52,22 @@ namespace Aurora.test.ControllerTests
[TearDown] [TearDown]
public async Task TearDown() public async Task TearDown()
{ {
await ServerService.Instance.Stop(); await _serverService.Stop();
await _channel.ShutdownAsync(); await _channel.ShutdownAsync();
} }
[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);
}
} }
} }

View File

@ -1,9 +1,14 @@
using NUnit.Framework; using NUnit.Framework;
using Aurora.Proto.PartyV2; using Aurora.Proto.PartyV2;
using Aurora.Services.Server; using Aurora.Services.Server;
using Aurora.Services.Library;
using Aurora.Services.Settings;
using Aurora.test.Models.Mock;
using Grpc.Core; using Grpc.Core;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Linq; using System.Linq;
using System.IO;
using Autofac;
namespace Aurora.test.ControllerTests namespace Aurora.test.ControllerTests
{ {
@ -11,10 +16,36 @@ namespace Aurora.test.ControllerTests
{ {
private RemotePartyService.RemotePartyServiceClient _remotePartyService; private RemotePartyService.RemotePartyServiceClient _remotePartyService;
private Channel _channel; private Channel _channel;
private IContainer _container;
private IServerService _serverService;
[OneTimeSetUp]
public void 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}/Resource", Directory.GetCurrentDirectory())
}).SingleInstance();
builder.RegisterType<LibraryService>().As<ILibraryService>().SingleInstance();
_container = builder.Build();
}
[OneTimeTearDown]
public void TearDownOneTime()
{
_container.Dispose();
}
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
ServerService.Instance.Start("testParty", "asdf"); _serverService = _container.Resolve<IServerService>();
_serverService.Start("testParty", "asdf");
_channel = new Channel(string.Format("{0}:{1}", ServerService.GetLocalIPAddress(), 8080), ChannelCredentials.Insecure); _channel = new Channel(string.Format("{0}:{1}", ServerService.GetLocalIPAddress(), 8080), ChannelCredentials.Insecure);
_remotePartyService = new RemotePartyService.RemotePartyServiceClient(_channel); _remotePartyService = new RemotePartyService.RemotePartyServiceClient(_channel);
} }
@ -22,7 +53,7 @@ namespace Aurora.test.ControllerTests
[TearDown] [TearDown]
public async Task TearDown() public async Task TearDown()
{ {
await ServerService.Instance.Stop(); await _serverService.Stop();
await _channel.ShutdownAsync(); await _channel.ShutdownAsync();
} }

View File

@ -1,8 +1,13 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using System.IO;
using NUnit.Framework; using NUnit.Framework;
using Aurora.Proto.PartyV2; using Aurora.Proto.PartyV2;
using Aurora.Services.Server; using Aurora.Services.Server;
using Aurora.Services.Library;
using Aurora.Services.Settings;
using Aurora.test.Models.Mock;
using Grpc.Core; using Grpc.Core;
using Autofac;
namespace Aurora.test.ControllerTests namespace Aurora.test.ControllerTests
{ {
@ -10,10 +15,35 @@ namespace Aurora.test.ControllerTests
{ {
private RemotePartyService.RemotePartyServiceClient _remotePartyService; private RemotePartyService.RemotePartyServiceClient _remotePartyService;
private Channel _channel; private Channel _channel;
private IContainer _container;
private IServerService _serverService;
[OneTimeSetUp]
public void 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}/Resource", Directory.GetCurrentDirectory())
}).SingleInstance();
builder.RegisterType<LibraryService>().As<ILibraryService>().SingleInstance();
_container = builder.Build();
}
[OneTimeTearDown]
public void TearDownOneTime()
{
_container.Dispose();
}
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
ServerService.Instance.Start("testParty", "asdf"); _serverService = _container.Resolve<IServerService>();
_serverService.Start("testParty", "asdf");
_channel = new Channel(string.Format("{0}:{1}", ServerService.GetLocalIPAddress(), 8080), ChannelCredentials.Insecure); _channel = new Channel(string.Format("{0}:{1}", ServerService.GetLocalIPAddress(), 8080), ChannelCredentials.Insecure);
_remotePartyService = new RemotePartyService.RemotePartyServiceClient(_channel); _remotePartyService = new RemotePartyService.RemotePartyServiceClient(_channel);
} }
@ -21,7 +51,7 @@ namespace Aurora.test.ControllerTests
[TearDown] [TearDown]
public async Task TearDown() public async Task TearDown()
{ {
await ServerService.Instance.Stop(); await _serverService.Stop();
await _channel.ShutdownAsync(); await _channel.ShutdownAsync();
} }

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

@ -12,6 +12,7 @@ using LibVLCSharp.Shared;
using Xamarin.Forms; using Xamarin.Forms;
using Aurora.Services.Player; using Aurora.Services.Player;
using Aurora.Services.Settings; using Aurora.Services.Settings;
using Aurora.Services.Library;
namespace Aurora namespace Aurora
{ {
@ -29,6 +30,7 @@ namespace Aurora
_builder.RegisterType<PlayerService>().As<IPlayer>().SingleInstance(); _builder.RegisterType<PlayerService>().As<IPlayer>().SingleInstance();
_builder.RegisterType<SettingsService>().As<ISettingsService>().SingleInstance(); _builder.RegisterType<SettingsService>().As<ISettingsService>().SingleInstance();
_builder.RegisterType<ClientService>().As<IClientService>().SingleInstance(); _builder.RegisterType<ClientService>().As<IClientService>().SingleInstance();
_builder.RegisterType<LibraryService>().As<ILibraryService>().SingleInstance();
_builder.RegisterType<MainView>().SingleInstance(); _builder.RegisterType<MainView>().SingleInstance();
_builder.RegisterType<AlbumsViewModel>(); _builder.RegisterType<AlbumsViewModel>();
_builder.RegisterType<ArtistsViewModel>(); _builder.RegisterType<ArtistsViewModel>();

View File

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

View File

@ -32,5 +32,15 @@ namespace Aurora.Design.Views.Profile
OnPropertyChanged("Port"); 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 System.Collections.ObjectModel;
using Aurora.Models.Media; using Aurora.Models.Media;
using Aurora.Services; using Aurora.Services.Library;
using Xamarin.Forms; using Xamarin.Forms;
namespace Aurora.Design.Views.Songs namespace Aurora.Design.Views.Songs
@ -10,15 +10,18 @@ namespace Aurora.Design.Views.Songs
#region Fields #region Fields
private ObservableCollection<BaseMedia> _songsList; private ObservableCollection<BaseMedia> _songsList;
private BaseMedia _selectedSong; private BaseMedia _selectedSong;
private ILibraryService _libraryService;
#endregion Fields #endregion Fields
#region Constructor #region Constructor
public SongsViewModel() public SongsViewModel(ILibraryService libraryService)
{ {
_songsList = new ObservableCollection<BaseMedia>(); _songsList = new ObservableCollection<BaseMedia>();
DoubleClickCommand = new Command(OnDoubleClickExecute, OnDoubleClickCanExecute); DoubleClickCommand = new Command(OnDoubleClickExecute, OnDoubleClickCanExecute);
this._libraryService = libraryService;
Initialize(); Initialize();
} }
@ -45,7 +48,7 @@ namespace Aurora.Design.Views.Songs
public void Initialize() public void Initialize()
{ {
SongsList = LibraryService.Instance.GetLibrary(); SongsList = this._libraryService.GetLibrary();
} }
#endregion Methods #endregion Methods

View File

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

View File

@ -239,8 +239,6 @@ message MemberDeletedEvent {
} }
message EventSubscription { message EventSubscription {
//Resource name of the event
string name = 1;
EventType type = 2; EventType type = 2;
} }
@ -253,7 +251,6 @@ message ListEventSubscriptionsRequest {
message ListEventSubscriptionsResponse { message ListEventSubscriptionsResponse {
repeated EventSubscription subscriptions = 1; repeated EventSubscription subscriptions = 1;
string nextPageToken = 3;
} }
message CreateEventSubscriptionRequest { message CreateEventSubscriptionRequest {
@ -264,7 +261,8 @@ message CreateEventSubscriptionRequest {
message DeleteEventSubscriptionRequest { message DeleteEventSubscriptionRequest {
//Resource name of the subscription to delete //Resource name of the subscription to delete
string name = 1; string parent = 1;
EventType type = 2;
} }
message DeleteAllEventSubscriptionsRequest { message DeleteAllEventSubscriptionsRequest {

View File

@ -6,8 +6,9 @@ using Aurora.Utils;
using Aurora.Proto.Party; using Aurora.Proto.Party;
using Aurora.Proto.Events; using Aurora.Proto.Events;
using Aurora.Services.EventManager; using Aurora.Services.EventManager;
using Aurora.Services;
using Aurora.Models.Media; using Aurora.Models.Media;
using Aurora.Services.Library;
using Autofac;
namespace Aurora.RemoteImpl namespace Aurora.RemoteImpl
{ {
@ -90,49 +91,56 @@ namespace Aurora.RemoteImpl
public override Task<QueueResponse> GetQueue(Proto.General.Empty empty, Grpc.Core.ServerCallContext context) public override Task<QueueResponse> GetQueue(Proto.General.Empty empty, Grpc.Core.ServerCallContext context)
{ {
QueueResponse mediaList = new QueueResponse();
//This will change as queuing operation gets solidified //This will change as queuing operation gets solidified
//Simply return the hosts library //Simply return the hosts library
using (var scope = App.Container.BeginLifetimeScope())
ObservableCollection<BaseMedia> queue = LibraryService.Instance.GetLibrary();
QueueResponse mediaList = new QueueResponse();
foreach (BaseMedia media in queue)
{ {
AudioMetadata metadata = new AudioMetadata(); ILibraryService library = scope.Resolve<ILibraryService>();
try
ObservableCollection<BaseMedia> queue = library.GetLibrary();
foreach (BaseMedia media in queue)
{ {
if (media.Metadata is AudioMetadata) AudioMetadata metadata = new AudioMetadata();
try
{ {
metadata = media.Metadata as AudioMetadata; if (media.Metadata is AudioMetadata)
{
metadata = media.Metadata as AudioMetadata;
RemoteMediaData data = new RemoteMediaData(); RemoteMediaData data = new RemoteMediaData();
data.Id = media.Id; data.Id = media.Id;
if (metadata.Title != null) if (metadata.Title != null)
{ {
data.Title = metadata.Title; data.Title = metadata.Title;
} }
if (metadata.Artist != null) if (metadata.Artist != null)
{ {
data.Artist = metadata.Artist; data.Artist = metadata.Artist;
} }
if (metadata.Album != null) if (metadata.Album != null)
{ {
data.Album = metadata.Album; data.Album = metadata.Album;
} }
if (metadata.Duration != null) if (metadata.Duration != null)
{ {
data.Duration = metadata.Duration; data.Duration = metadata.Duration;
} }
mediaList.MediaList.Add(data); mediaList.MediaList.Add(data);
}
}
catch (Exception ex)
{
Console.WriteLine(string.Format("Error preparing queue: {0}", ex.Message));
} }
} }
catch (Exception ex)
{
Console.WriteLine(string.Format("Error preparing queue: {0}", ex.Message));
}
} }
return Task.FromResult(mediaList); return Task.FromResult(mediaList);
} }

View File

@ -1,11 +1,10 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Threading;
using System.Collections.Generic;
using Aurora.Services;
using Aurora.Proto.Playback; using Aurora.Proto.Playback;
using Aurora.Proto.General; using Aurora.Proto.General;
using Aurora.Models.Media; using Aurora.Models.Media;
using Aurora.Services.Library;
using Autofac;
namespace Aurora.RemoteImpl namespace Aurora.RemoteImpl
{ {
@ -15,38 +14,44 @@ namespace Aurora.RemoteImpl
Grpc.Core.IServerStreamWriter<Chunk> responseStream, Grpc.Core.IServerStreamWriter<Chunk> responseStream,
Grpc.Core.ServerCallContext context) Grpc.Core.ServerCallContext context)
{ {
BaseMedia originalSong = LibraryService.Instance.GetSong(request.Id); using (var scope = App.Container.BeginLifetimeScope())
if (!(originalSong is LocalAudio))
{ {
return; ILibraryService library = scope.Resolve<ILibraryService>();
}
//Copy media object to not interfere with other threads BaseMedia originalSong = library.GetSong(request.Id);
LocalAudio songCopy = new LocalAudio((LocalAudio)originalSong); if (!(originalSong is LocalAudio))
try
{
//Load only if not already loaded. (Multiple clients may be requesting media)
if (!songCopy.IsLoaded)
{ {
await songCopy.Load(); return;
} }
//Send stream //Copy media object to not interfere with other threads
Console.WriteLine("Begin sending file"); LocalAudio songCopy = new LocalAudio((LocalAudio)originalSong);
byte[] buffer = new byte[2048]; // read in chunks of 2KB
int bytesRead; try
while ((bytesRead = songCopy.DataStream.Read(buffer, 0, buffer.Length)) > 0)
{ {
Google.Protobuf.ByteString bufferByteString = Google.Protobuf.ByteString.CopyFrom(buffer); //Load only if not already loaded. (Multiple clients may be requesting media)
await responseStream.WriteAsync(new Chunk { Content = bufferByteString }); 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);
} }
Console.WriteLine("Done sending file");
}
catch (Exception ex)
{
Console.WriteLine("Exception caught while sending audio file: " + ex.Message);
} }
} }
} }
} }

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.Collections.ObjectModel;
using System.IO; using System.IO;
using Aurora.Models.Media; using Aurora.Models.Media;
using Aurora.Services.Settings;
using Aurora.Utils; using Aurora.Utils;
namespace Aurora.Services namespace Aurora.Services.Library
{ {
public class LibraryService : BaseService<LibraryService> public class LibraryService : ILibraryService
{ {
#region Fields #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 string _extensions = ".wav,.mp3,.aiff,.flac,.m4a,.m4b,.wma";
private Dictionary<string, BaseMedia> _library; private Dictionary<string, BaseMedia> _library;
#endregion Fields #endregion Fields
public LibraryService() public LibraryService(ISettingsService settingsService)
{ {
_library = new Dictionary<string, BaseMedia>(); _library = new Dictionary<string, BaseMedia>();
this._pathName = settingsService.LibraryLocation;
LoadLibrary(); LoadLibrary();
} }

View File

@ -9,7 +9,7 @@ using LibVLCSharp.Shared;
namespace Aurora.Services.Player namespace Aurora.Services.Player
{ {
public class PlayerService : BaseService<PlayerService>, IPlayer public class PlayerService : IPlayer
{ {
private const long _ticksPerMillisecond = 10000; private const long _ticksPerMillisecond = 10000;
private BaseMedia _currentMedia; private BaseMedia _currentMedia;

View File

@ -1,22 +1,29 @@
using System; using System;
using System.Threading.Tasks; using System.Collections.ObjectModel;
using System.Collections.Generic; using System.Collections.Generic;
using Aurora.Proto.PartyV2; using Aurora.Proto.PartyV2;
using Aurora.Services.Library;
using Aurora.Models.Media;
using Autofac;
namespace Aurora.Services.Server.Controllers namespace Aurora.Services.Server.Controllers
{ {
public partial class RemotePartyController : RemotePartyService.RemotePartyServiceBase public partial class RemotePartyController : RemotePartyService.RemotePartyServiceBase
{ {
private ILibraryService _libraryService;
/// <summary> /// <summary>
/// Constructor for partial class /// Constructor for partial class
/// </summary> /// </summary>
public RemotePartyController(string partyName, string description) public RemotePartyController(string partyName, string description, ILibraryService libraryService)
{ {
this._startDateTime = DateTime.UtcNow; this._startDateTime = DateTime.UtcNow;
this._displayName = partyName; this._displayName = partyName;
this._description = description; this._description = description;
this._memberList = new SortedList<string, Member>(); this._memberList = new SortedList<string, Member>();
this._mediaList = new SortedList<string, Models.Media.BaseMedia>(); this._mediaList = new SortedList<string, Media>();
_libraryService = libraryService;
string userName = "testUser"; string userName = "testUser";
@ -32,7 +39,47 @@ namespace Aurora.Services.Server.Controllers
this._memberList.Add(_hostMember.Name, _hostMember); this._memberList.Add(_hostMember.Name, _hostMember);
//Add media from library //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 = 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

@ -1,6 +1,8 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Threading;
using Aurora.Proto.PartyV2; using Aurora.Proto.PartyV2;
using Aurora.Utils;
namespace Aurora.Services.Server.Controllers namespace Aurora.Services.Server.Controllers
{ {
@ -8,7 +10,26 @@ namespace Aurora.Services.Server.Controllers
{ {
public override Task GetEvents(GetEventsRequest request, Grpc.Core.IServerStreamWriter<BaseEvent> responseStream, Grpc.Core.ServerCallContext context) public override Task GetEvents(GetEventsRequest request, Grpc.Core.IServerStreamWriter<BaseEvent> responseStream, Grpc.Core.ServerCallContext context)
{ {
throw new NotImplementedException(); 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

@ -2,6 +2,7 @@ using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Aurora.Proto.PartyV2; using Aurora.Proto.PartyV2;
using Aurora.Proto.General; using Aurora.Proto.General;
using Aurora.Utils;
namespace Aurora.Services.Server.Controllers namespace Aurora.Services.Server.Controllers
{ {
@ -14,17 +15,24 @@ namespace Aurora.Services.Server.Controllers
public override Task<EventSubscription> CreateEventSubscription(CreateEventSubscriptionRequest request, Grpc.Core.ServerCallContext context) public override Task<EventSubscription> CreateEventSubscription(CreateEventSubscriptionRequest request, Grpc.Core.ServerCallContext context)
{ {
throw new NotImplementedException(); 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<Empty> DeleteEventSubscription(DeleteEventSubscriptionRequest request, Grpc.Core.ServerCallContext context) public override Task<Empty> DeleteEventSubscription(DeleteEventSubscriptionRequest request, Grpc.Core.ServerCallContext context)
{ {
throw new NotImplementedException(); 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) public override Task<Empty> DeleteAllEventSubscriptions(DeleteAllEventSubscriptionsRequest request, Grpc.Core.ServerCallContext context)
{ {
throw new NotImplementedException(); this._eventManager.RemoveAllSubscriptions(Misc.Combine(new string[] { context.Peer, request.Parent }));
return Task.FromResult(new Empty());
} }
} }
} }

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using Aurora.Proto.PartyV2; using Aurora.Proto.PartyV2;
using Aurora.Models.Media; using Aurora.Models.Media;
using Aurora.Proto.General; using Aurora.Proto.General;
using Aurora.Services.Library;
using Aurora.Services.Player; using Aurora.Services.Player;
using Autofac; using Autofac;
@ -11,7 +12,7 @@ namespace Aurora.Services.Server.Controllers
{ {
public partial class RemotePartyController : RemotePartyService.RemotePartyServiceBase public partial class RemotePartyController : RemotePartyService.RemotePartyServiceBase
{ {
private SortedList<string, BaseMedia> _mediaList; private SortedList<string, Media> _mediaList;
public override Task<ListMediaResponse> ListMedia(ListMediaRequest request, Grpc.Core.ServerCallContext context) public override Task<ListMediaResponse> ListMedia(ListMediaRequest request, Grpc.Core.ServerCallContext context)
{ {
@ -31,22 +32,8 @@ namespace Aurora.Services.Server.Controllers
} }
//Gather page //Gather page
List<BaseMedia> baseMedia = new List<BaseMedia>(_mediaList.Values); List<Media> mediaList = new List<Media>(_mediaList.Values);
foreach (BaseMedia media in baseMedia) resp.Media.AddRange(mediaList.GetRange(startIdx, pageSize));
{
if (media.Metadata is AudioMetadata)
{
AudioMetadata meta = media.Metadata as AudioMetadata;
resp.Media.Add(new Media()
{
Name = media.Id,
Title = meta.Title,
Album = meta.Album,
Artist = meta.Artist,
Duration = meta.Duration
});
}
}
resp.NextPageToken = resp.Media[resp.Media.Count - 1].Name; resp.NextPageToken = resp.Media[resp.Media.Count - 1].Name;
return Task.FromResult(resp); return Task.FromResult(resp);
@ -54,24 +41,14 @@ namespace Aurora.Services.Server.Controllers
public override Task<Media> GetMedia(GetMediaRequest request, Grpc.Core.ServerCallContext context) public override Task<Media> GetMedia(GetMediaRequest request, Grpc.Core.ServerCallContext context)
{ {
_mediaList.TryGetValue(request.Name, out BaseMedia baseMedia); _mediaList.TryGetValue(request.Name, out Media baseMedia);
if (baseMedia == null) if (baseMedia == null)
{ {
throw new KeyNotFoundException(); throw new KeyNotFoundException();
} }
Media media = new Media(); return Task.FromResult(baseMedia);
if (baseMedia.Metadata != null && baseMedia.Metadata is AudioMetadata)
{
AudioMetadata metadata = baseMedia.Metadata as AudioMetadata;
media.Name = baseMedia.Id;
media.Title = metadata.Title;
media.Artist = metadata.Artist;
media.Album = metadata.Album;
}
return Task.FromResult(media);
} }
public override Task<Media> CreateMedia(CreateMediaRequest request, Grpc.Core.ServerCallContext context) public override Task<Media> CreateMedia(CreateMediaRequest request, Grpc.Core.ServerCallContext context)
@ -86,37 +63,43 @@ namespace Aurora.Services.Server.Controllers
public override async Task StreamMedia(StreamMediaRequest request, Grpc.Core.IServerStreamWriter<Proto.General.Chunk> responseStream, Grpc.Core.ServerCallContext context) public override async Task StreamMedia(StreamMediaRequest request, Grpc.Core.IServerStreamWriter<Proto.General.Chunk> responseStream, Grpc.Core.ServerCallContext context)
{ {
BaseMedia originalSong = LibraryService.Instance.GetSong(request.Name); using (var scope = App.Container.BeginLifetimeScope())
if (!(originalSong is LocalAudio))
{ {
return; ILibraryService library = scope.Resolve<ILibraryService>();
}
//Copy media object to not interfere with other threads BaseMedia originalSong = library.GetSong(request.Name);
LocalAudio songCopy = new LocalAudio((LocalAudio)originalSong); if (!(originalSong is LocalAudio))
try
{
//Load only if not already loaded. (Multiple clients may be requesting media)
if (!songCopy.IsLoaded)
{ {
await songCopy.Load(); return;
} }
//Send stream //Copy media object to not interfere with other threads
Console.WriteLine("Begin sending file"); LocalAudio songCopy = new LocalAudio((LocalAudio)originalSong);
byte[] buffer = new byte[2048]; // read in chunks of 2KB
int bytesRead; try
while ((bytesRead = songCopy.DataStream.Read(buffer, 0, buffer.Length)) > 0)
{ {
Google.Protobuf.ByteString bufferByteString = Google.Protobuf.ByteString.CopyFrom(buffer); //Load only if not already loaded. (Multiple clients may be requesting media)
await responseStream.WriteAsync(new Chunk { Content = bufferByteString }); 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");
} }
Console.WriteLine("Done sending file"); catch (Exception ex)
} {
catch (Exception ex) Console.WriteLine("Exception caught while sending audio file: " + ex.Message);
{ }
Console.WriteLine("Exception caught while sending audio file: " + ex.Message);
} }
} }

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

@ -4,16 +4,18 @@ using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using Grpc.Core; using Grpc.Core;
using Aurora.Services.Server.Controllers; using Aurora.Services.Server.Controllers;
using Aurora.Services.Library;
using Aurora.Proto.PartyV2; using Aurora.Proto.PartyV2;
namespace Aurora.Services.Server namespace Aurora.Services.Server
{ {
public class ServerService : BaseService<ServerService> public class ServerService : IServerService
{ {
private int _port = 8080; private int _port = 8080;
private string _hostname; private string _hostname;
private Grpc.Core.Server _server; private Grpc.Core.Server _server;
private ILibraryService _libraryService;
//Implementation class declarations //Implementation class declarations
private RemotePartyController _remotePartyController; private RemotePartyController _remotePartyController;
@ -21,10 +23,10 @@ namespace Aurora.Services.Server
/// <summary> /// <summary>
/// Constructor. Registers GRPC service implementations. /// Constructor. Registers GRPC service implementations.
/// </summary> /// </summary>
public ServerService() public ServerService(ILibraryService libraryService)
{ {
string host = GetLocalIPAddress(); string host = GetLocalIPAddress();
this._libraryService = libraryService;
if (string.IsNullOrWhiteSpace(host)) if (string.IsNullOrWhiteSpace(host))
{ {
throw new Exception("This device must have a valid IP address"); throw new Exception("This device must have a valid IP address");
@ -69,7 +71,7 @@ namespace Aurora.Services.Server
}; };
//Construct implementations //Construct implementations
_remotePartyController = new RemotePartyController(partyName, description); _remotePartyController = new RemotePartyController(partyName, description, _libraryService);
// Register grpc RemoteService with singleton server service // Register grpc RemoteService with singleton server service
RegisterService(RemotePartyService.BindService(_remotePartyController)); RegisterService(RemotePartyService.BindService(_remotePartyController));

View File

@ -23,5 +23,7 @@ namespace Aurora.Services.Settings
/// </summary> /// </summary>
/// <value></value> /// <value></value>
string ClientId { get; set; } string ClientId { get; set; }
string LibraryLocation { get; set; }
} }
} }

View File

@ -11,6 +11,8 @@ namespace Aurora.Services.Settings
private string _usernameKey = "username"; private string _usernameKey = "username";
private string _defaultPortKey = "port"; private string _defaultPortKey = "port";
private string _libraryLocationKey = "libraryLocation";
public SettingsService() public SettingsService()
{ {
} }
@ -55,6 +57,12 @@ namespace Aurora.Services.Settings
set { AppSettings.AddOrUpdateValue(_defaultPortKey, value); } set { AppSettings.AddOrUpdateValue(_defaultPortKey, value); }
} }
public string LibraryLocation
{
get { return AppSettings.GetValueOrDefault(_libraryLocationKey, "~/Music"); }
set { AppSettings.AddOrUpdateValue(_libraryLocationKey, value); }
}
/// <summary> /// <summary>
/// The current sessions clientId. This is assigned by the server. This is not persisted. /// The current sessions clientId. This is assigned by the server. This is not persisted.
/// </summary> /// </summary>