more DI for consumer factories, caching play counter, service extensions

This commit is contained in:
andy 2021-11-29 21:04:15 +00:00
parent 975ee772dd
commit e41d525b0b
21 changed files with 288 additions and 135 deletions

View File

@ -25,38 +25,45 @@ namespace Selector.CLI
private readonly ILogger<LocalWatcherService> Logger; private readonly ILogger<LocalWatcherService> Logger;
private readonly ILoggerFactory LoggerFactory; private readonly ILoggerFactory LoggerFactory;
private readonly IServiceProvider ServiceProvider; private readonly IServiceProvider ServiceProvider;
private readonly RootOptions Config;
private readonly IWatcherFactory WatcherFactory; private readonly IWatcherFactory WatcherFactory;
private readonly IWatcherCollectionFactory WatcherCollectionFactory; private readonly IWatcherCollectionFactory WatcherCollectionFactory;
private readonly IRefreshTokenFactoryProvider SpotifyFactory; private readonly IRefreshTokenFactoryProvider SpotifyFactory;
private readonly LastAuth LastAuth;
private readonly IDatabaseAsync Cache; private readonly IAudioFeatureInjectorFactory AudioFeatureInjectorFactory;
private readonly ISubscriber Subscriber; private readonly IPlayCounterFactory PlayCounterFactory;
private readonly IPublisherFactory PublisherFactory;
private readonly ICacheWriterFactory CacheWriterFactory;
private Dictionary<string, IWatcherCollection> Watchers { get; set; } = new(); private Dictionary<string, IWatcherCollection> Watchers { get; set; } = new();
public DbWatcherService( public DbWatcherService(
IWatcherFactory watcherFactory, IWatcherFactory watcherFactory,
IWatcherCollectionFactory watcherCollectionFactory, IWatcherCollectionFactory watcherCollectionFactory,
IRefreshTokenFactoryProvider spotifyFactory, IRefreshTokenFactoryProvider spotifyFactory,
IAudioFeatureInjectorFactory audioFeatureInjectorFactory,
IPlayCounterFactory playCounterFactory,
IPublisherFactory publisherFactory,
ICacheWriterFactory cacheWriterFactory,
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
IServiceProvider serviceProvider, IServiceProvider serviceProvider
IOptions<RootOptions> config,
LastAuth lastAuth = null,
IDatabaseAsync cache = null,
ISubscriber subscriber = null
) { ) {
Logger = loggerFactory.CreateLogger<LocalWatcherService>(); Logger = loggerFactory.CreateLogger<LocalWatcherService>();
LoggerFactory = loggerFactory; LoggerFactory = loggerFactory;
Config = config.Value; ServiceProvider = serviceProvider;
WatcherFactory = watcherFactory; WatcherFactory = watcherFactory;
WatcherCollectionFactory = watcherCollectionFactory; WatcherCollectionFactory = watcherCollectionFactory;
SpotifyFactory = spotifyFactory; SpotifyFactory = spotifyFactory;
LastAuth = lastAuth;
ServiceProvider = serviceProvider; AudioFeatureInjectorFactory = audioFeatureInjectorFactory;
Cache = cache; PlayCounterFactory = playCounterFactory;
Subscriber = subscriber;
PublisherFactory = publisherFactory;
CacheWriterFactory = cacheWriterFactory;
} }
public async Task StartAsync(CancellationToken cancellationToken) public async Task StartAsync(CancellationToken cancellationToken)
@ -99,26 +106,13 @@ namespace Selector.CLI
case WatcherType.Player: case WatcherType.Player:
watcher = await WatcherFactory.Get<PlayerWatcher>(spotifyFactory, id: dbWatcher.UserId, pollPeriod: PollPeriod); watcher = await WatcherFactory.Get<PlayerWatcher>(spotifyFactory, id: dbWatcher.UserId, pollPeriod: PollPeriod);
var featureInjector = new AudioFeatureInjectorFactory(LoggerFactory); consumers.Add(await AudioFeatureInjectorFactory.Get(spotifyFactory));
consumers.Add(await featureInjector.Get(spotifyFactory)); consumers.Add(await CacheWriterFactory.Get());
consumers.Add(await PublisherFactory.Get());
var featureInjectorCache = new CachingAudioFeatureInjectorFactory(LoggerFactory, Cache);
consumers.Add(await featureInjectorCache.Get(spotifyFactory));
var cacheWriter = new CacheWriterFactory(Cache, LoggerFactory);
consumers.Add(await cacheWriter.Get());
var pub = new PublisherFactory(Subscriber, LoggerFactory);
consumers.Add(await pub.Get());
if (!string.IsNullOrWhiteSpace(dbWatcher.User.LastFmUsername)) if (!string.IsNullOrWhiteSpace(dbWatcher.User.LastFmUsername))
{ {
if (LastAuth is null) throw new ArgumentNullException("No Last Auth Injected"); consumers.Add(await PlayCounterFactory.Get(creds: new() { Username = dbWatcher.User.LastFmUsername }));
var client = new LastfmClient(LastAuth);
var playCount = new PlayCounterFactory(LoggerFactory, client: client, creds: new() { Username = dbWatcher.User.LastFmUsername });
consumers.Add(await playCount.Get());
} }
else else
{ {

View File

@ -4,15 +4,13 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using IF.Lastfm.Core.Api;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; 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;
using Selector.Cache; using Selector.Cache;
using StackExchange.Redis;
namespace Selector.CLI namespace Selector.CLI
{ {
@ -21,15 +19,12 @@ namespace Selector.CLI
private const string ConfigInstanceKey = "localconfig"; private const string ConfigInstanceKey = "localconfig";
private readonly ILogger<LocalWatcherService> Logger; private readonly ILogger<LocalWatcherService> Logger;
private readonly ILoggerFactory LoggerFactory;
private readonly RootOptions Config; private readonly RootOptions Config;
private readonly IWatcherFactory WatcherFactory; private readonly IWatcherFactory WatcherFactory;
private readonly IWatcherCollectionFactory WatcherCollectionFactory; private readonly IWatcherCollectionFactory WatcherCollectionFactory;
private readonly IRefreshTokenFactoryProvider SpotifyFactory; private readonly IRefreshTokenFactoryProvider SpotifyFactory;
private readonly LastAuth LastAuth;
private readonly IServiceProvider ServiceProvider;
private readonly IDatabaseAsync Cache;
private readonly ISubscriber Subscriber;
private Dictionary<string, IWatcherCollection> Watchers { get; set; } = new(); private Dictionary<string, IWatcherCollection> Watchers { get; set; } = new();
@ -37,21 +32,20 @@ namespace Selector.CLI
IWatcherFactory watcherFactory, IWatcherFactory watcherFactory,
IWatcherCollectionFactory watcherCollectionFactory, IWatcherCollectionFactory watcherCollectionFactory,
IRefreshTokenFactoryProvider spotifyFactory, IRefreshTokenFactoryProvider spotifyFactory,
ILoggerFactory loggerFactory,
IOptions<RootOptions> config, IServiceProvider serviceProvider,
LastAuth lastAuth = null,
IDatabaseAsync cache = null, ILogger<LocalWatcherService> logger,
ISubscriber subscriber = null IOptions<RootOptions> config
) { ) {
Logger = loggerFactory.CreateLogger<LocalWatcherService>(); Logger = logger;
LoggerFactory = loggerFactory;
Config = config.Value; Config = config.Value;
WatcherFactory = watcherFactory; WatcherFactory = watcherFactory;
WatcherCollectionFactory = watcherCollectionFactory; WatcherCollectionFactory = watcherCollectionFactory;
SpotifyFactory = spotifyFactory; SpotifyFactory = spotifyFactory;
LastAuth = lastAuth;
Cache = cache; ServiceProvider = serviceProvider;
Subscriber = subscriber;
} }
public async Task StartAsync(CancellationToken cancellationToken) public async Task StartAsync(CancellationToken cancellationToken)
@ -111,34 +105,25 @@ namespace Selector.CLI
switch(consumer) switch(consumer)
{ {
case Consumers.AudioFeatures: case Consumers.AudioFeatures:
var featureInjector = new AudioFeatureInjectorFactory(LoggerFactory); consumers.Add(await ServiceProvider.GetService<AudioFeatureInjectorFactory>().Get(spotifyFactory));
consumers.Add(await featureInjector.Get(spotifyFactory));
break; break;
case Consumers.AudioFeaturesCache: case Consumers.AudioFeaturesCache:
var featureInjectorCache = new CachingAudioFeatureInjectorFactory(LoggerFactory, Cache); consumers.Add(await ServiceProvider.GetService<CachingAudioFeatureInjectorFactory>().Get(spotifyFactory));
consumers.Add(await featureInjectorCache.Get(spotifyFactory));
break; break;
case Consumers.CacheWriter: case Consumers.CacheWriter:
var cacheWriter = new CacheWriterFactory(Cache, LoggerFactory); consumers.Add(await ServiceProvider.GetService<CacheWriterFactory>().Get());
consumers.Add(await cacheWriter.Get());
break; break;
case Consumers.Publisher: case Consumers.Publisher:
var pub = new PublisherFactory(Subscriber, LoggerFactory); consumers.Add(await ServiceProvider.GetService<PublisherFactory>().Get());
consumers.Add(await pub.Get());
break; break;
case Consumers.PlayCounter: case Consumers.PlayCounter:
if(!string.IsNullOrWhiteSpace(watcherOption.LastFmUsername)) if(!string.IsNullOrWhiteSpace(watcherOption.LastFmUsername))
{ {
if(LastAuth is null) throw new ArgumentNullException("No Last Auth Injected"); consumers.Add(await ServiceProvider.GetService<PlayCounterCachingFactory>().Get(creds: new() { Username = watcherOption.LastFmUsername }));
var client = new LastfmClient(LastAuth);
var playCount = new PlayCounterFactory(LoggerFactory, client: client, creds: new(){ Username = watcherOption.LastFmUsername });
consumers.Add(await playCount.Get());
} }
else else
{ {

View File

@ -22,7 +22,7 @@ namespace Selector.CLI
public static string FormatKeys(string[] args) => string.Join(":", args); public static string FormatKeys(string[] args) => string.Join(":", args);
} }
class RootOptions public class RootOptions
{ {
public const string Key = "Selector"; public const string Key = "Selector";
@ -42,12 +42,12 @@ namespace Selector.CLI
public EqualityChecker Equality { get; set; } = EqualityChecker.Uri; public EqualityChecker Equality { get; set; } = EqualityChecker.Uri;
} }
enum EqualityChecker public enum EqualityChecker
{ {
Uri, String Uri, String
} }
class WatcherOptions public class WatcherOptions
{ {
public const string Key = "Watcher"; public const string Key = "Watcher";
@ -56,7 +56,7 @@ namespace Selector.CLI
public List<WatcherInstanceOptions> Instances { get; set; } = new(); public List<WatcherInstanceOptions> Instances { get; set; } = new();
} }
class WatcherInstanceOptions public class WatcherInstanceOptions
{ {
public const string Key = "Instances"; public const string Key = "Instances";
@ -73,19 +73,19 @@ namespace Selector.CLI
#nullable disable #nullable disable
} }
enum Consumers public enum Consumers
{ {
AudioFeatures, AudioFeaturesCache, CacheWriter, Publisher, PlayCounter AudioFeatures, AudioFeaturesCache, CacheWriter, Publisher, PlayCounter
} }
class DatabaseOptions { public class DatabaseOptions {
public const string Key = "Database"; public const string Key = "Database";
public bool Enabled { get; set; } = false; public bool Enabled { get; set; } = false;
public string ConnectionString { get; set; } public string ConnectionString { get; set; }
} }
class RedisOptions public class RedisOptions
{ {
public const string Key = "Redis"; public const string Key = "Redis";

View File

@ -11,12 +11,12 @@ using NLog.Extensions.Logging;
using Selector.Model; using Selector.Model;
using Selector.Cache; using Selector.Cache;
using Selector.Cache.Extensions;
using IF.Lastfm.Core.Api; using IF.Lastfm.Core.Api;
using StackExchange.Redis;
namespace Selector.CLI namespace Selector.CLI
{ {
class Program public static class Program
{ {
public static async Task Main(string[] args) public static async Task Main(string[] args)
{ {
@ -55,7 +55,8 @@ namespace Selector.CLI
Console.WriteLine("> Adding Last.fm credentials..."); Console.WriteLine("> Adding Last.fm credentials...");
var lastAuth = new LastAuth(config.LastfmClient, config.LastfmSecret); var lastAuth = new LastAuth(config.LastfmClient, config.LastfmSecret);
services.AddSingleton<LastAuth>(lastAuth); services.AddSingleton(lastAuth);
services.AddTransient(sp => new LastfmClient(sp.GetService<LastAuth>()));
} }
else else
{ {
@ -74,25 +75,6 @@ namespace Selector.CLI
} }
} }
public static void ConfigureRedis(RootOptions config, IServiceCollection services)
{
if (config.RedisOptions.Enabled)
{
Console.WriteLine("> Configuring Redis...");
if(string.IsNullOrWhiteSpace(config.RedisOptions.ConnectionString))
{
Console.WriteLine("> No Redis configuration string provided, exiting...");
Environment.Exit(1);
}
var connMulti = ConnectionMultiplexer.Connect(config.RedisOptions.ConnectionString);
services.AddSingleton(connMulti);
services.AddTransient<IDatabaseAsync>(services => services.GetService<ConnectionMultiplexer>().GetDatabase());
services.AddTransient<ISubscriber>(services => services.GetService<ConnectionMultiplexer>().GetSubscriber());
}
}
public static void ConfigureEqual(RootOptions config, IServiceCollection services) public static void ConfigureEqual(RootOptions config, IServiceCollection services)
{ {
switch (config.Equality) switch (config.Equality)
@ -119,8 +101,9 @@ namespace Selector.CLI
Console.WriteLine("> Adding Services..."); Console.WriteLine("> Adding Services...");
// SERVICES // SERVICES
services.AddCachingConsumerFactories();
services.AddSingleton<IWatcherFactory, WatcherFactory>(); services.AddSingleton<IWatcherFactory, WatcherFactory>();
services.AddSingleton<IAudioFeatureInjectorFactory, AudioFeatureInjectorFactory>();
services.AddSingleton<IWatcherCollectionFactory, WatcherCollectionFactory>(); services.AddSingleton<IWatcherCollectionFactory, WatcherCollectionFactory>();
// For generating spotify clients // For generating spotify clients
//services.AddSingleton<IRefreshTokenFactoryProvider, RefreshTokenFactoryProvider>(); //services.AddSingleton<IRefreshTokenFactoryProvider, RefreshTokenFactoryProvider>();
@ -128,7 +111,10 @@ namespace Selector.CLI
ConfigureLastFm(config, services); ConfigureLastFm(config, services);
ConfigureDb(config, services); ConfigureDb(config, services);
ConfigureRedis(config, services);
if (config.RedisOptions.Enabled)
services.AddRedisServices(config.RedisOptions.ConnectionString);
ConfigureEqual(config, services); ConfigureEqual(config, services);
// HOSTED SERVICES // HOSTED SERVICES

View File

@ -8,13 +8,8 @@ using SpotifyAPI.Web;
using StackExchange.Redis; using StackExchange.Redis;
namespace Selector.Cache namespace Selector.Cache
{ {
public interface ICachingAudioFeatureInjectorFactory public class CachingAudioFeatureInjectorFactory: IAudioFeatureInjectorFactory {
{
public Task<IConsumer> Get(ISpotifyConfigFactory spotifyFactory, IPlayerWatcher watcher);
}
public class CachingAudioFeatureInjectorFactory: ICachingAudioFeatureInjectorFactory {
private readonly ILoggerFactory LoggerFactory; private readonly ILoggerFactory LoggerFactory;
private readonly IDatabaseAsync Db; private readonly IDatabaseAsync Db;

View File

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using StackExchange.Redis;
using IF.Lastfm.Core.Api;
namespace Selector.Cache
{
public class PlayCounterCachingFactory: IPlayCounterFactory
{
private readonly ILoggerFactory LoggerFactory;
private readonly IDatabaseAsync Cache;
private readonly LastfmClient Client;
private readonly LastFmCredentials Creds;
public PlayCounterCachingFactory(
ILoggerFactory loggerFactory,
IDatabaseAsync cache,
LastfmClient client = null,
LastFmCredentials creds = null)
{
LoggerFactory = loggerFactory;
Cache = cache;
Client = client;
Creds = creds;
}
public async Task<IConsumer> Get(LastfmClient fmClient = null, LastFmCredentials creds = null, IPlayerWatcher watcher = null)
{
var client = fmClient ?? Client;
if (client is null)
{
throw new ArgumentNullException("No Last.fm client provided");
}
return new PlayCounterCaching(
watcher,
client.Track,
client.Album,
client.Artist,
client.User,
Cache,
credentials: creds ?? Creds,
logger: LoggerFactory.CreateLogger<PlayCounterCaching>()
);
}
}
}

View File

@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using IF.Lastfm.Core.Api;
using StackExchange.Redis;
using SpotifyAPI.Web;
namespace Selector.Cache
{
public class PlayCounterCaching: PlayCounter
{
private readonly IDatabaseAsync Db;
public TimeSpan CacheExpiry { get; set; } = TimeSpan.FromDays(14);
public PlayCounterCaching(
IPlayerWatcher watcher,
ITrackApi trackClient,
IAlbumApi albumClient,
IArtistApi artistClient,
IUserApi userClient,
IDatabaseAsync db,
LastFmCredentials credentials = null,
ILogger<PlayCounterCaching> logger = null,
CancellationToken token = default
) : base(watcher, trackClient, albumClient, artistClient, userClient, credentials, logger, token)
{
Db = db;
NewPlayCount += CacheCallback;
}
public void CacheCallback(object sender, PlayCount e)
{
Task.Run(() => { return AsyncCacheCallback(e); }, CancelToken);
}
public async Task AsyncCacheCallback(PlayCount e)
{
var track = e.ListeningEvent.Current.Item as FullTrack;
Logger.LogTrace($"Caching play count for [{track.DisplayString()}]");
var tasks = new Task[]
{
Db.StringSetAsync(Key.TrackPlayCount(track.Name, track.Artists[0].Name), e.Track, expiry: CacheExpiry),
Db.StringSetAsync(Key.AlbumPlayCount(track.Album.Name, track.Album.Artists[0].Name), e.Album, expiry: CacheExpiry),
Db.StringSetAsync(Key.ArtistPlayCount(track.Artists[0].Name), e.Artist, expiry: CacheExpiry),
Db.StringSetAsync(Key.UserPlayCount(e.Username), e.User, expiry: CacheExpiry),
};
await Task.WhenAll(tasks);
Logger.LogDebug($"Cached audio feature for [{track.DisplayString()}]");
}
}
}

View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using StackExchange.Redis;
namespace Selector.Cache.Extensions
{
public static class ServiceExtensions
{
public static void AddRedisServices(this IServiceCollection services, string connectionStr)
{
Console.WriteLine("> Configuring Redis...");
if (string.IsNullOrWhiteSpace(connectionStr))
{
Console.WriteLine("> No Redis configuration string provided, exiting...");
Environment.Exit(1);
}
var connMulti = ConnectionMultiplexer.Connect(connectionStr);
services.AddSingleton(connMulti);
services.AddTransient<IDatabaseAsync>(services => services.GetService<ConnectionMultiplexer>().GetDatabase());
services.AddTransient<ISubscriber>(services => services.GetService<ConnectionMultiplexer>().GetSubscriber());
}
public static void AddCachingConsumerFactories(this IServiceCollection services)
{
services.AddTransient<IAudioFeatureInjectorFactory, AudioFeatureInjectorFactory>();
services.AddTransient<IPlayCounterFactory, PlayCounterCachingFactory>();
services.AddTransient<ICacheWriterFactory, CacheWriterFactory>();
services.AddTransient<IPublisherFactory, PublisherFactory>();
}
}
}

View File

@ -11,6 +11,7 @@ namespace Selector.Cache
public const string TrackName = "Track"; public const string TrackName = "Track";
public const string AlbumName = "Album"; public const string AlbumName = "Album";
public const string ArtistName = "Artist"; public const string ArtistName = "Artist";
public const string UserName = "User";
public const string AudioFeatureName = "AudioFeature"; public const string AudioFeatureName = "AudioFeature";
public const string PlayCountName = "PlayCount"; public const string PlayCountName = "PlayCount";
@ -30,6 +31,7 @@ namespace Selector.Cache
public static string TrackPlayCount(string name, string artist) => Namespace(TrackName, artist, name, PlayCountName); public static string TrackPlayCount(string name, string artist) => Namespace(TrackName, artist, name, PlayCountName);
public static string AlbumPlayCount(string name, string artist) => Namespace(AlbumName, artist, name, PlayCountName); public static string AlbumPlayCount(string name, string artist) => Namespace(AlbumName, artist, name, PlayCountName);
public static string ArtistPlayCount(string name) => Namespace(ArtistName, name, PlayCountName); public static string ArtistPlayCount(string name) => Namespace(ArtistName, name, PlayCountName);
public static string UserPlayCount(string username) => Namespace(UserName, username, PlayCountName);
public static string WatcherReserved(int id) => Namespace(WatcherName, id.ToString(), ReservedName); public static string WatcherReserved(int id) => Namespace(WatcherName, id.ToString(), ReservedName);

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
using Selector.Model.Authorisation;
namespace Selector.Model.Extensions
{
public static class ServiceExtensions
{
public static void AddAuthorisationHandlers(this IServiceCollection services)
{
services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
services.AddScoped<IAuthorizationHandler, WatcherIsOwnerAuthHandler>();
services.AddSingleton<IAuthorizationHandler, WatcherIsAdminAuthHandler>();
services.AddScoped<IAuthorizationHandler, UserIsSelfAuthHandler>();
services.AddSingleton<IAuthorizationHandler, UserIsAdminAuthHandler>();
}
}
}

View File

@ -13,13 +13,12 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using StackExchange.Redis;
using Selector.Web.Service; using Selector.Web.Service;
using Selector.Web.Hubs; using Selector.Web.Hubs;
using Selector.Model; using Selector.Model;
using Selector.Model.Authorisation; using Selector.Model.Extensions;
using Selector.Cache; using Selector.Cache;
using Selector.Cache.Extensions;
namespace Selector.Web namespace Selector.Web
{ {
@ -93,35 +92,10 @@ namespace Selector.Web
options.SlidingExpiration = true; options.SlidingExpiration = true;
}); });
// AUTH services.AddAuthorisationHandlers();
services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
services.AddScoped<IAuthorizationHandler, WatcherIsOwnerAuthHandler>();
services.AddSingleton<IAuthorizationHandler, WatcherIsAdminAuthHandler>();
services.AddScoped<IAuthorizationHandler, UserIsSelfAuthHandler>();
services.AddSingleton<IAuthorizationHandler, UserIsAdminAuthHandler>();
// REDIS
if (config.RedisOptions.Enabled) if (config.RedisOptions.Enabled)
{ services.AddRedisServices(config.RedisOptions.ConnectionString);
Console.WriteLine("> Configuring Redis...");
if (string.IsNullOrWhiteSpace(config.RedisOptions.ConnectionString))
{
Console.WriteLine("> No Redis configuration string provided, exiting...");
Environment.Exit(1);
}
var connMulti = ConnectionMultiplexer.Connect(config.RedisOptions.ConnectionString);
services.AddSingleton(connMulti);
services.AddTransient<IDatabaseAsync>(services => services.GetService<ConnectionMultiplexer>().GetDatabase());
services.AddTransient<ISubscriber>(services => services.GetService<ConnectionMultiplexer>().GetSubscriber());
}
services.AddSingleton<IRefreshTokenFactoryProvider, CachingRefreshTokenFactoryProvider>(); services.AddSingleton<IRefreshTokenFactoryProvider, CachingRefreshTokenFactoryProvider>();
services.AddSingleton<AudioFeaturePuller>(); services.AddSingleton<AudioFeaturePuller>();

View File

@ -10,7 +10,7 @@ namespace Selector
{ {
public interface IAudioFeatureInjectorFactory public interface IAudioFeatureInjectorFactory
{ {
public Task<IConsumer> Get(ISpotifyConfigFactory spotifyFactory, IPlayerWatcher watcher); public Task<IConsumer> Get(ISpotifyConfigFactory spotifyFactory, IPlayerWatcher watcher = null);
} }
public class AudioFeatureInjectorFactory: IAudioFeatureInjectorFactory { public class AudioFeatureInjectorFactory: IAudioFeatureInjectorFactory {

View File

@ -123,13 +123,19 @@ namespace Selector
Logger.LogDebug($"Adding Last.fm data [{e.Id}/{e.SpotifyUsername}/{Credentials.Username}] [{track.DisplayString()}], track: {trackCount}, album: {albumCount}, artist: {artistCount}, user: {userCount}"); Logger.LogDebug($"Adding Last.fm data [{e.Id}/{e.SpotifyUsername}/{Credentials.Username}] [{track.DisplayString()}], track: {trackCount}, album: {albumCount}, artist: {artistCount}, user: {userCount}");
OnNewPlayCount(new() PlayCount playCount = new()
{ {
Track = trackCount, Track = trackCount,
Album = albumCount, Album = albumCount,
Artist = artistCount, Artist = artistCount,
User = userCount, User = userCount,
}); ListeningEvent = e
};
if (!string.IsNullOrWhiteSpace(Credentials.Username))
playCount.Username = Credentials.Username;
OnNewPlayCount(playCount);
} }
else if (e.Current.Item is FullEpisode episode) else if (e.Current.Item is FullEpisode episode)
{ {
@ -181,6 +187,8 @@ namespace Selector
public int? Album { get; set; } public int? Album { get; set; }
public int? Artist { get; set; } public int? Artist { get; set; }
public int? User { get; set; } public int? User { get; set; }
public string Username { get; set; }
public ListeningChangeEventArgs ListeningEvent { get; set; }
} }
public class LastFmCredentials public class LastFmCredentials

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
namespace Selector.Extensions
{
public static class ServiceExtensions
{
public static void AddConsumerFactories(this IServiceCollection services)
{
services.AddTransient<IAudioFeatureInjectorFactory, AudioFeatureInjectorFactory>();
services.AddTransient<IPlayCounterFactory, PlayCounterFactory>();
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Selector
{
public interface IWatcherContext
{
void AddConsumer(IConsumer consumer);
void Start();
void Stop();
}
}

View File

@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace Selector namespace Selector
{ {
public class WatcherContext: IDisposable public class WatcherContext: IDisposable, IWatcherContext
{ {
public IWatcher Watcher { get; set; } public IWatcher Watcher { get; set; }
private List<IConsumer> Consumers { get; set; } = new(); private List<IConsumer> Consumers { get; set; } = new();