audio feature pushing to web frontend working

This commit is contained in:
andy 2021-11-10 23:54:28 +00:00
parent a3628182c9
commit b75920867b
7 changed files with 112 additions and 27 deletions

View File

@ -6,7 +6,6 @@
"Watcher": {
"Instances": [
{
"name": "Player Watcher",
"type": "player",
"lastfmusername": "sarsoo",
"pollperiod": 2000,

View File

@ -20,14 +20,25 @@ namespace Selector.Cache
Cache = cache;
}
public async Task<TrackAudioFeatures> Get(string userId, string trackId)
public async Task<TrackAudioFeatures> 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
{

View File

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

View File

@ -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<INowPlayingHubClient>
{
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<CurrentlyPlayingDTO>(nowPlaying);
await Clients.Caller.OnNewPlaying(deserialised);
if (nowPlaying != RedisValue.Null)
{
var deserialised = JsonSerializer.Deserialize<CurrentlyPlayingDTO>(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);
}
}
}
}

View File

@ -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<SpotifyInitialiser> Logger;
private readonly IRefreshTokenFactoryProvider FactoryProvider;
private readonly RootOptions Config;
public SpotifyInitialiser(
ILogger<SpotifyInitialiser> logger,
IRefreshTokenFactoryProvider factoryProvider,
IOptions<RootOptions> 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;
}
}
}

View File

@ -43,7 +43,7 @@ namespace Selector.Web
services.AddRazorPages().AddRazorRuntimeCompilation();
services.AddControllers();
services.AddSignalR();
services.AddSignalR(o => o.EnableDetailedErrors = true);
services.AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql(Configuration.GetConnectionString("Default"))
@ -117,7 +117,9 @@ namespace Selector.Web
services.AddTransient<ISubscriber>(services => services.GetService<ConnectionMultiplexer>().GetSubscriber());
}
services.AddHostedService<SpotifyInitialiser>();
services.AddSingleton<IRefreshTokenFactoryProvider, CachingRefreshTokenFactoryProvider>();
services.AddSingleton<AudioFeaturePuller>();
services.AddSingleton<CacheHubProxy>();
services.AddHostedService<CacheHubProxyService>();

View File

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