diff --git a/Selector.CLI/appsettings.json b/Selector.CLI/appsettings.json index b589bb0..fab32e5 100644 --- a/Selector.CLI/appsettings.json +++ b/Selector.CLI/appsettings.json @@ -6,7 +6,6 @@ "Watcher": { "Instances": [ { - "name": "Player Watcher", "type": "player", "lastfmusername": "sarsoo", "pollperiod": 2000, diff --git a/Selector.Cache/AudioFeaturePuller.cs b/Selector.Cache/AudioFeaturePuller.cs index e55d1e4..0454de5 100644 --- a/Selector.Cache/AudioFeaturePuller.cs +++ b/Selector.Cache/AudioFeaturePuller.cs @@ -20,14 +20,25 @@ namespace Selector.Cache Cache = cache; } - public async Task Get(string userId, string trackId) + public async Task Get(string refreshToken, string trackId) { + if(string.IsNullOrWhiteSpace(trackId)) throw new ArgumentNullException("No track Id provided"); + var track = await Cache.StringGetAsync(Key.AudioFeature(trackId)); if (track == RedisValue.Null) { - // TODO: finish implementing network pull - // return await SpotifyClient.GetAudioFeatures(trackId); - throw new NotImplementedException("Can't pull over network yet"); + if(!string.IsNullOrWhiteSpace(refreshToken)) + { + var factory = await SpotifyFactory.GetFactory(refreshToken); + var spotifyClient = new SpotifyClient(await factory.GetConfig()); + + // TODO: Error checking + return await spotifyClient.Tracks.GetAudioFeatures(trackId); + } + else + { + return null; + } } else { diff --git a/Selector.Cache/Consumer/AudioInjectorCaching.cs b/Selector.Cache/Consumer/AudioInjectorCaching.cs index 4dd297d..742147d 100644 --- a/Selector.Cache/Consumer/AudioInjectorCaching.cs +++ b/Selector.Cache/Consumer/AudioInjectorCaching.cs @@ -4,7 +4,6 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; using SpotifyAPI.Web; using StackExchange.Redis; @@ -36,7 +35,7 @@ namespace Selector.Cache public async Task AsyncCacheCallback(AnalysedTrack e) { - var payload = JsonSerializer.Serialize(e); + var payload = JsonSerializer.Serialize(e.Features); Logger.LogTrace($"Caching current for [{e.Track.DisplayString()}]"); diff --git a/Selector.Web/Hubs/NowPlayingHub.cs b/Selector.Web/Hubs/NowPlayingHub.cs index 4e1c8f3..2f0cc1e 100644 --- a/Selector.Web/Hubs/NowPlayingHub.cs +++ b/Selector.Web/Hubs/NowPlayingHub.cs @@ -1,29 +1,36 @@ using System; +using System.Linq; +using System.Text.Json; using System.Threading.Tasks; +using System.Data.SqlTypes; using Microsoft.AspNetCore.SignalR; +using Microsoft.EntityFrameworkCore; using SpotifyAPI.Web; using StackExchange.Redis; using Selector.Cache; -using System.Text.Json; +using Selector.Model; namespace Selector.Web.Hubs { public interface INowPlayingHubClient { public Task OnNewPlaying(CurrentlyPlayingDTO context); - // public Task OnNewAudioFeature(TrackAudioFeatures features); + public Task OnNewAudioFeature(TrackAudioFeatures features); } public class NowPlayingHub: Hub { private readonly IDatabaseAsync Cache; - // private readonly AudioFeaturePuller AudioFeaturePuller; + private readonly AudioFeaturePuller AudioFeaturePuller; + private readonly ApplicationDbContext Db; - public NowPlayingHub(IDatabaseAsync cache) + public NowPlayingHub(IDatabaseAsync cache, AudioFeaturePuller puller, ApplicationDbContext db) { Cache = cache; + AudioFeaturePuller = puller; + Db = db; } public async Task OnConnected() @@ -34,13 +41,33 @@ namespace Selector.Web.Hubs public async Task SendNewPlaying() { var nowPlaying = await Cache.StringGetAsync(Key.CurrentlyPlaying(Context.UserIdentifier)); - var deserialised = JsonSerializer.Deserialize(nowPlaying); - await Clients.Caller.OnNewPlaying(deserialised); + if (nowPlaying != RedisValue.Null) + { + var deserialised = JsonSerializer.Deserialize(nowPlaying); + await Clients.Caller.OnNewPlaying(deserialised); + } } - // public async Task SendAudioFeatures(string trackId) - // { - // await Clients.Caller.OnNewAudioFeature(await AudioFeaturePuller.Get(trackId)); - // } + public async Task SendAudioFeatures(string trackId) + { + var user = Db.Users + .AsNoTracking() + .Where(u => u.Id == Context.UserIdentifier) + .SingleOrDefault() + ?? throw new SqlNullValueException("No user returned"); + var watcher = Db.Watcher + .AsNoTracking() + .Where(w => w.UserId == Context.UserIdentifier + && w.Type == WatcherType.Player) + .SingleOrDefault() + ?? throw new SqlNullValueException($"No player watcher found for [{user.UserName}]"); + + var feature = await AudioFeaturePuller.Get(user.SpotifyRefreshToken, trackId); + + if (feature is not null) + { + await Clients.Caller.OnNewAudioFeature(feature); + } + } } } \ No newline at end of file diff --git a/Selector.Web/Services/SpotifyInitialiser.cs b/Selector.Web/Services/SpotifyInitialiser.cs new file mode 100644 index 0000000..5bf0c1b --- /dev/null +++ b/Selector.Web/Services/SpotifyInitialiser.cs @@ -0,0 +1,47 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Selector.Web.Service { + public class SpotifyInitialiser : IHostedService + { + private readonly ILogger Logger; + private readonly IRefreshTokenFactoryProvider FactoryProvider; + private readonly RootOptions Config; + + public SpotifyInitialiser( + ILogger logger, + IRefreshTokenFactoryProvider factoryProvider, + IOptions config + ) + { + Logger = logger; + FactoryProvider = factoryProvider; + Config = config.Value; + } + + public Task StartAsync(CancellationToken cancellationToken) + { + Logger.LogInformation("Initialising Spotify Factory"); + + if(string.IsNullOrEmpty(Config.ClientId) || string.IsNullOrEmpty(Config.ClientSecret)) + { + Logger.LogError("Unable to initialise Spotify factory, null client id or secret"); + } + else + { + FactoryProvider.Initialise(Config.ClientId, Config.ClientSecret); + } + + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Selector.Web/Startup.cs b/Selector.Web/Startup.cs index b97c40f..0eaa067 100644 --- a/Selector.Web/Startup.cs +++ b/Selector.Web/Startup.cs @@ -43,7 +43,7 @@ namespace Selector.Web services.AddRazorPages().AddRazorRuntimeCompilation(); services.AddControllers(); - services.AddSignalR(); + services.AddSignalR(o => o.EnableDetailedErrors = true); services.AddDbContext(options => options.UseNpgsql(Configuration.GetConnectionString("Default")) @@ -117,7 +117,9 @@ namespace Selector.Web services.AddTransient(services => services.GetService().GetSubscriber()); } + services.AddHostedService(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddHostedService(); diff --git a/Selector.Web/scripts/now.ts b/Selector.Web/scripts/now.ts index d272868..18b0f21 100644 --- a/Selector.Web/scripts/now.ts +++ b/Selector.Web/scripts/now.ts @@ -40,17 +40,17 @@ const app = Vue.createApp({ this.currentlyPlaying = context; this.cards = []; - // if(context.track !== null && context.track !== undefined) - // { - // connection.invoke("SendAudioFeatures", context.track.id); - // } + if(context.track !== null && context.track !== undefined) + { + connection.invoke("SendAudioFeatures", context.track.id); + } }); - // connection.on("OnNewAudioFeature", (feature: TrackAudioFeatures) => - // { - // console.log(feature); - // this.trackFeatures = feature; - // }); + connection.on("OnNewAudioFeature", (feature: TrackAudioFeatures) => + { + console.log(feature); + this.trackFeatures = feature; + }); } });