added watcherfactory, theoretically working watcher service

This commit is contained in:
andy 2021-10-12 23:29:05 +01:00
parent 1694f8e3a9
commit b5956ef4a0
12 changed files with 178 additions and 25 deletions

View File

@ -41,6 +41,7 @@ namespace Selector.CLI
public WatcherType Type { get; set; } = WatcherType.Player; public WatcherType Type { get; set; } = WatcherType.Player;
#nullable enable #nullable enable
public string? PlaylistUri { get; set; } public string? PlaylistUri { get; set; }
public string? WatcherCollection { get; set; }
#nullable disable #nullable disable
} }

View File

@ -27,8 +27,9 @@ namespace Selector.CLI
}); });
// SERVICES // SERVICES
//services.AddTransient<IWatcherFactory, PlayerWatcher>(); services.AddSingleton<IWatcherFactory, WatcherFactory>();
//services.AddTransient<IWatcherCollection, WatcherCollection>(); services.AddSingleton<IWatcherCollectionFactory, WatcherCollectionFactory>();
// For generating spotify clients
services.AddSingleton<IRefreshTokenFactoryProvider, RefreshTokenFactoryProvider>(); services.AddSingleton<IRefreshTokenFactoryProvider, RefreshTokenFactoryProvider>();
switch(context.Configuration.GetValue<EqualityChecker>("selector:equality")) switch(context.Configuration.GetValue<EqualityChecker>("selector:equality"))

View File

@ -0,0 +1,10 @@
{
"profiles": {
"Selector.CLI": {
"commandName": "Project",
"environmentVariables": {
"DOTNET_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -22,6 +22,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="appsettings.Development.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="appsettings.json"> <None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>

View File

@ -5,6 +5,7 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -13,28 +14,103 @@ namespace Selector.CLI
{ {
class WatcherService : IHostedService class WatcherService : IHostedService
{ {
private const string ConfigInstanceKey = "localconfig";
private readonly ILogger<WatcherService> Logger; private readonly ILogger<WatcherService> Logger;
private readonly RootOptions Config; private readonly RootOptions Config;
private readonly IRefreshTokenFactoryProvider TokenFactoryProvider; private readonly IWatcherFactory WatcherFactory;
private readonly IWatcherCollectionFactory WatcherCollectionFactory;
private readonly IRefreshTokenFactoryProvider SpotifyFactory;
private Dictionary<string, IWatcherCollection> Watchers { get; set; } = new(); private Dictionary<string, IWatcherCollection> Watchers { get; set; } = new();
public WatcherService(IRefreshTokenFactoryProvider tokenFactoryProvider, ILogger<WatcherService> logger, IOptions<RootOptions> config) public WatcherService(
{ IWatcherFactory watcherFactory,
IWatcherCollectionFactory watcherCollectionFactory,
IRefreshTokenFactoryProvider spotifyFactory,
ILogger<WatcherService> logger,
IOptions<RootOptions> config
) {
Logger = logger; Logger = logger;
Config = config.Value; Config = config.Value;
TokenFactoryProvider = tokenFactoryProvider; WatcherFactory = watcherFactory;
WatcherCollectionFactory = watcherCollectionFactory;
SpotifyFactory = spotifyFactory;
TokenFactoryProvider.Initialise(Config.ClientId, Config.ClientSecret); SpotifyFactory.Initialise(Config.ClientId, Config.ClientSecret);
} }
public Task StartAsync(CancellationToken cancellationToken) public async Task StartAsync(CancellationToken cancellationToken)
{ {
Logger.LogInformation("Starting up"); Logger.LogInformation("Starting up");
Config.WatcherOptions.Instances.ForEach(i => Logger.LogInformation($"Config: {i.Type}")); Logger.LogInformation("Loading config instances...");
var watcherIndices = await InitialiseConfigInstances();
return Task.CompletedTask; Logger.LogInformation($"Starting {watcherIndices.Count()} affected watcher collection(s)...");
StartWatcherCollections(watcherIndices);
}
private async Task<IEnumerable<string>> InitialiseConfigInstances()
{
var indices = new HashSet<string>();
foreach (var watcherOption in Config.WatcherOptions.Instances)
{
var logMsg = new StringBuilder();
if (!string.IsNullOrWhiteSpace(watcherOption.Name))
{
logMsg.Append($"Creating {watcherOption.Name} watcher [{watcherOption.Type}]");
}
else
{
logMsg.Append($"Creating new {watcherOption.Type} watcher");
}
if (!string.IsNullOrWhiteSpace(watcherOption.PlaylistUri)) logMsg.Append($" [{ watcherOption.PlaylistUri}]");
Logger.LogInformation(logMsg.ToString());
var watcherCollectionIdx = watcherOption.WatcherCollection ?? ConfigInstanceKey;
indices.Add(watcherCollectionIdx);
if (!Watchers.ContainsKey(watcherCollectionIdx))
Watchers[watcherCollectionIdx] = WatcherCollectionFactory.Get();
var watcherCollection = Watchers[watcherCollectionIdx];
Logger.LogDebug("Getting Spotify factory");
var spotifyFactory = await SpotifyFactory.GetFactory(watcherOption.RefreshKey);
IWatcher watcher = null;
switch(watcherOption.Type)
{
case WatcherType.Player:
watcher = await WatcherFactory.Get<PlayerWatcher>(spotifyFactory, watcherOption.PollPeriod);
break;
case WatcherType.Playlist:
throw new NotImplementedException("Playlist watchers not implemented");
break;
}
watcherCollection.Add(watcher);
}
return indices;
}
private void StartWatcherCollections(IEnumerable<string> indices)
{
foreach (var index in indices)
{
try
{
Watchers[index].Start();
}
catch (KeyNotFoundException)
{
Logger.LogError($"Unable to retrieve watcher collection [{index}] when starting");
}
}
} }
public Task StopAsync(CancellationToken cancellationToken) public Task StopAsync(CancellationToken cancellationToken)

View File

@ -6,12 +6,9 @@
"Watcher": { "Watcher": {
"Instances": [ "Instances": [
{ {
"name": "sarsoo", "name": "player watcher",
"type": "player" "type": "player",
}, "pollperiod": 1000
{
"name": "andy",
"type": "playlist"
} }
] ]
} }

View File

@ -20,7 +20,7 @@ namespace Selector
ClientSecret = clientSecret; ClientSecret = clientSecret;
} }
public bool Initialised => string.IsNullOrWhiteSpace(ClientId) || string.IsNullOrWhiteSpace(ClientSecret); public bool Initialised => !string.IsNullOrWhiteSpace(ClientId) && !string.IsNullOrWhiteSpace(ClientSecret);
public Task<RefreshTokenFactory> GetFactory(string refreshToken) public Task<RefreshTokenFactory> GetFactory(string refreshToken)
{ {

View File

@ -1,11 +1,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Threading.Tasks;
namespace Selector namespace Selector
{ {
public interface IWatcherFactory public interface IWatcherFactory
{ {
public IWatcher Create(); public Task<IWatcher> Get<T>(ISpotifyConfigFactory spotifyFactory, int pollPeriod)
where T : class, IWatcher;
} }
} }

View File

@ -0,0 +1,7 @@
namespace Selector
{
public interface IWatcherCollectionFactory
{
public IWatcherCollection Get();
}
}

View File

@ -0,0 +1,19 @@
using Microsoft.Extensions.Logging;
namespace Selector
{
public class WatcherCollectionFactory: IWatcherCollectionFactory
{
private readonly ILoggerFactory LoggerFactory;
public WatcherCollectionFactory(ILoggerFactory loggerFactory)
{
LoggerFactory = loggerFactory;
}
public IWatcherCollection Get()
{
return new WatcherCollection(LoggerFactory.CreateLogger<WatcherCollection>());
}
}
}

View File

@ -31,6 +31,10 @@ namespace Selector
IsRunning = true; IsRunning = true;
TokenSource = new(); TokenSource = new();
Task = Watcher.Watch(TokenSource.Token); Task = Watcher.Watch(TokenSource.Token);
Task.ContinueWith(t =>
{
if (t.Exception != null) throw t.Exception;
}, TaskContinuationOptions.OnlyOnFaulted);
} }
public void Stop() public void Stop()

View File

@ -1,14 +1,47 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using SpotifyAPI.Web;
namespace Selector namespace Selector
{ {
//class PlayerWatcherFactory : IWatcherFactory public class WatcherFactory : IWatcherFactory {
//{
// public IWatcher Create() private readonly ILoggerFactory LoggerFactory;
// { private readonly IEqual Equal;
// } public WatcherFactory(ILoggerFactory loggerFactory, IEqual equal)
//} {
LoggerFactory = loggerFactory;
Equal = equal;
}
public async Task<IWatcher> Get<T>(ISpotifyConfigFactory spotifyFactory, int pollPeriod = 3000)
where T : class, IWatcher
{
if(typeof(T).IsAssignableFrom(typeof(PlayerWatcher)))
{
var config = await spotifyFactory.GetConfig();
var client = new SpotifyClient(config);
return new PlayerWatcher(
client.Player,
Equal,
LoggerFactory?.CreateLogger<PlayerWatcher>(),
pollPeriod: pollPeriod
);
}
//else if (typeof(T).IsAssignableFrom(typeof(PlaylistWatcher)))
//{
//}
else
{
throw new ArgumentException("Type unsupported");
}
}
}
} }