cache hub proxy working
This commit is contained in:
parent
3867ea8fad
commit
0d74257b89
6
.vscode/launch.json
vendored
6
.vscode/launch.json
vendored
@ -8,10 +8,10 @@
|
||||
"name": "Selector.Web",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"preLaunchTask": "buildWeb",
|
||||
"program": "${workspaceFolder}/Selector.Web/bin/Debug/net5.0/Selector.Web.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"cwd": "${workspaceFolder}/Selector.Web",
|
||||
"stopAtEntry": false,
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
@ -29,7 +29,7 @@
|
||||
"name": "Selector.CLI",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"preLaunchTask": "buildCLI",
|
||||
"program": "${workspaceFolder}/Selector.CLI/bin/Debug/net5.0/Selector.CLI.dll",
|
||||
"env": {
|
||||
"DOTNET_ENVIRONMENT": "Development"
|
||||
|
14
.vscode/tasks.json
vendored
14
.vscode/tasks.json
vendored
@ -2,7 +2,19 @@
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"label": "buildWeb",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/Selector.Web/Selector.Web.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "buildCLI",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
|
@ -43,6 +43,7 @@ namespace Selector.Cache
|
||||
|
||||
Logger.LogTrace($"Publishing current for [{e.Username}]");
|
||||
|
||||
// TODO: currently using spotify username for cache key, use db username
|
||||
var receivers = await Subscriber.PublishAsync(Key.CurrentlyPlaying(e.Username), payload);
|
||||
|
||||
Logger.LogDebug($"Published current for [{e.Username}], {receivers} receivers");
|
||||
|
@ -5,7 +5,7 @@ using SpotifyAPI.Web;
|
||||
namespace Selector.Cache {
|
||||
|
||||
public class CurrentlyPlayingDTO {
|
||||
public CurrentlyPlayingContext Context { get; set; }
|
||||
public CurrentlyPlayingContextDTO Context { get; set; }
|
||||
public string Username { get; set; }
|
||||
|
||||
public FullTrack Track { get; set; }
|
||||
@ -36,5 +36,38 @@ namespace Selector.Cache {
|
||||
throw new ArgumentException("Unknown item item");
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() => $"[{Username}] [{Context}]";
|
||||
}
|
||||
|
||||
public class CurrentlyPlayingContextDTO
|
||||
{
|
||||
public Device Device { get; set; }
|
||||
public string RepeatState { get; set; }
|
||||
public bool ShuffleState { get; set; }
|
||||
public Context Context { get; set; }
|
||||
public long Timestamp { get; set; }
|
||||
public int ProgressMs { get; set; }
|
||||
public bool IsPlaying { get; set; }
|
||||
|
||||
public string CurrentlyPlayingType { get; set; }
|
||||
public Actions Actions { get; set; }
|
||||
|
||||
public static implicit operator CurrentlyPlayingContextDTO(CurrentlyPlayingContext context)
|
||||
{
|
||||
return new CurrentlyPlayingContextDTO {
|
||||
Device = context.Device,
|
||||
RepeatState = context.RepeatState,
|
||||
ShuffleState = context.ShuffleState,
|
||||
Context = context.Context,
|
||||
Timestamp = context.Timestamp,
|
||||
ProgressMs = context.ProgressMs,
|
||||
IsPlaying = context.IsPlaying,
|
||||
CurrentlyPlayingType = context.CurrentlyPlayingType,
|
||||
Actions = context.Actions
|
||||
};
|
||||
}
|
||||
|
||||
public override string ToString() => $"{IsPlaying}, {Device?.DisplayString()}";
|
||||
}
|
||||
}
|
@ -35,7 +35,7 @@ namespace Selector.Web.Pages
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
HubProxy.FormMapping(MappingFactory.Get(UserManager.GetUserId(User)));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,11 +17,11 @@ namespace Selector.Web.Service
|
||||
{
|
||||
private readonly ILogger<CacheHubProxy> Logger;
|
||||
private readonly ISubscriber Subscriber;
|
||||
private readonly ServiceProvider Services;
|
||||
private readonly IServiceProvider Services;
|
||||
|
||||
public CacheHubProxy(ILogger<CacheHubProxy> logger,
|
||||
ISubscriber subscriber,
|
||||
ServiceProvider services
|
||||
IServiceProvider services
|
||||
)
|
||||
{
|
||||
Logger = logger;
|
||||
@ -35,37 +35,4 @@ namespace Selector.Web.Service
|
||||
mapping.ConstructMapping(Subscriber, context);
|
||||
}
|
||||
}
|
||||
|
||||
// public class CacheHubProxy<THub, T>
|
||||
// where THub: Hub<T>
|
||||
// where T: class
|
||||
// {
|
||||
// private readonly ILogger<CacheHubProxy<THub, T>> Logger;
|
||||
// private readonly ISubscriber Subscriber;
|
||||
// private readonly IHubContext<THub, T> HubContext;
|
||||
// private readonly List<ICacheHubMapping<THub, T>> Mappings;
|
||||
|
||||
// public CacheHubProxy(ILogger<CacheHubProxy<THub, T>> logger,
|
||||
// ISubscriber subscriber,
|
||||
// IHubContext<THub, T> hubContext,
|
||||
// IEnumerable<ICacheHubMapping<THub, T>> mappings
|
||||
// )
|
||||
// {
|
||||
// Logger = logger;
|
||||
// Subscriber = subscriber;
|
||||
// HubContext = hubContext;
|
||||
// Mappings = mappings.ToList();
|
||||
// }
|
||||
|
||||
// public void FormMapping(ICacheHubMapping<THub, T> mapping)
|
||||
// {
|
||||
// mapping.ConstructMapping(Subscriber, HubContext);
|
||||
// }
|
||||
|
||||
// public void AddMapping(ICacheHubMapping<THub, T> mapping)
|
||||
// {
|
||||
// Mappings.Add(mapping);
|
||||
// FormMapping(mapping);
|
||||
// }
|
||||
// }
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
@ -10,19 +12,31 @@ namespace Selector.Web.Service
|
||||
{
|
||||
private readonly ILogger<CacheHubProxyService> Logger;
|
||||
private readonly CacheHubProxy Proxy;
|
||||
private readonly IServiceScopeFactory ScopeFactory;
|
||||
|
||||
public CacheHubProxyService(
|
||||
ILogger<CacheHubProxyService> logger,
|
||||
CacheHubProxy proxy
|
||||
CacheHubProxy proxy,
|
||||
IServiceScopeFactory scopeFactory
|
||||
)
|
||||
{
|
||||
Logger = logger;
|
||||
Proxy = proxy;
|
||||
ScopeFactory = scopeFactory;
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Logger.LogInformation("Starting cache hub proxy");
|
||||
|
||||
using(var scope = ScopeFactory.CreateScope())
|
||||
{
|
||||
foreach(var mapping in scope.ServiceProvider.GetServices<IUserMapping>())
|
||||
{
|
||||
mapping.FormAll();
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
@ -14,19 +14,32 @@ namespace Selector.Web.Service
|
||||
{
|
||||
public class NowPlayingMapping : ICacheHubMapping<NowPlayingHub, INowPlayingHubClient>
|
||||
{
|
||||
private readonly ILogger<NowPlayingMapping> Logger;
|
||||
private readonly string UserId;
|
||||
private readonly string Username;
|
||||
|
||||
public NowPlayingMapping(ILogger<NowPlayingMapping> logger, string userId)
|
||||
public NowPlayingMapping(ILogger<NowPlayingMapping> logger, string userId, string username)
|
||||
{
|
||||
Logger = logger;
|
||||
UserId = userId;
|
||||
Username = username;
|
||||
}
|
||||
|
||||
public async Task ConstructMapping(ISubscriber subscriber, IHubContext<NowPlayingHub, INowPlayingHubClient> hub)
|
||||
{
|
||||
(await subscriber.SubscribeAsync(Key.CurrentlyPlaying(UserId))).OnMessage(async message => {
|
||||
var key = Key.CurrentlyPlaying(Username);
|
||||
(await subscriber.SubscribeAsync(key)).OnMessage(async message => {
|
||||
|
||||
var deserialised = JsonSerializer.Deserialize<CurrentlyPlayingDTO>(message.ToString());
|
||||
await hub.Clients.User(UserId).OnNewPlaying(deserialised);
|
||||
try{
|
||||
var trimmedMessage = message.ToString().Substring(key.Length + 1);
|
||||
var deserialised = JsonSerializer.Deserialize<CurrentlyPlayingDTO>(trimmedMessage);
|
||||
Logger.LogDebug($"Received new currently playing [{deserialised.Username}] [{deserialised.Username}]");
|
||||
await hub.Clients.User(UserId).OnNewPlaying(deserialised);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Logger.LogError(e, $"Error parsing new currently playing [{message}]");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using SpotifyAPI.Web;
|
||||
|
||||
using Selector.Web.Hubs;
|
||||
|
||||
namespace Selector.Web.Service
|
||||
{
|
||||
public interface INowPlayingMappingFactory {
|
||||
public NowPlayingMapping Get(string userId);
|
||||
public interface IUserMappingFactory<out TMap, THub, T>
|
||||
where TMap : ICacheHubMapping<THub, T>
|
||||
where THub : Hub<T>
|
||||
where T : class
|
||||
{
|
||||
public TMap Get(string userId, string username);
|
||||
}
|
||||
|
||||
public interface INowPlayingMappingFactory: IUserMappingFactory<NowPlayingMapping, NowPlayingHub, INowPlayingHubClient>
|
||||
{ }
|
||||
|
||||
public class NowPlayingMappingFactory : INowPlayingMappingFactory {
|
||||
|
||||
private readonly ILoggerFactory LoggerFactory;
|
||||
@ -22,11 +26,12 @@ namespace Selector.Web.Service
|
||||
LoggerFactory = loggerFactory;
|
||||
}
|
||||
|
||||
public NowPlayingMapping Get(string userId)
|
||||
public NowPlayingMapping Get(string userId, string username)
|
||||
{
|
||||
return new NowPlayingMapping(
|
||||
LoggerFactory?.CreateLogger<NowPlayingMapping>(),
|
||||
userId
|
||||
userId,
|
||||
username
|
||||
);
|
||||
}
|
||||
}
|
37
Selector.Web/Services/Mappings/UserMapping.cs
Normal file
37
Selector.Web/Services/Mappings/UserMapping.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
using Selector.Model;
|
||||
|
||||
namespace Selector.Web.Service
|
||||
{
|
||||
public interface IUserMapping {
|
||||
public void FormAll();
|
||||
}
|
||||
|
||||
public class NowPlayingUserMapping: IUserMapping
|
||||
{
|
||||
private readonly ApplicationDbContext Db;
|
||||
private readonly CacheHubProxy Proxy;
|
||||
private readonly INowPlayingMappingFactory NowPlayingMappingFactory;
|
||||
|
||||
public NowPlayingUserMapping(
|
||||
ApplicationDbContext db,
|
||||
CacheHubProxy proxy,
|
||||
INowPlayingMappingFactory nowPlayingMappingFactory
|
||||
)
|
||||
{
|
||||
Db = db;
|
||||
Proxy = proxy;
|
||||
NowPlayingMappingFactory = nowPlayingMappingFactory;
|
||||
}
|
||||
|
||||
public void FormAll()
|
||||
{
|
||||
foreach(var user in Db.Users)
|
||||
{
|
||||
Proxy.FormMapping(NowPlayingMappingFactory.Get(user.Id, user.UserName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -117,9 +117,11 @@ namespace Selector.Web
|
||||
services.AddTransient<ISubscriber>(services => services.GetService<ConnectionMultiplexer>().GetSubscriber());
|
||||
}
|
||||
|
||||
// HOSTED
|
||||
services.AddSingleton<CacheHubProxy>();
|
||||
services.AddSingleton<INowPlayingMappingFactory, NowPlayingMappingFactory>();
|
||||
services.AddHostedService<CacheHubProxyService>();
|
||||
|
||||
services.AddTransient<INowPlayingMappingFactory, NowPlayingMappingFactory>();
|
||||
services.AddScoped<IUserMapping, NowPlayingUserMapping>();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
|
@ -1,6 +1,25 @@
|
||||
|
||||
export interface SignalR {
|
||||
nowPlayingHub: nowPlayingProxy;
|
||||
}
|
||||
export interface nowPlayingProxy {
|
||||
client: NowPlayingHubClient;
|
||||
server: NowPlayingHub;
|
||||
}
|
||||
|
||||
export interface NowPlayingHubClient {
|
||||
OnNewPlaying: (context: CurrentlyPlayingDTO) => void;
|
||||
}
|
||||
|
||||
export interface NowPlayingHub {
|
||||
|
||||
SendNewPlaying(context: CurrentlyPlayingDTO): void;
|
||||
}
|
||||
|
||||
export interface CurrentlyPlayingDTO {
|
||||
context: CurrentlyPlayingContextDTO;
|
||||
username: string;
|
||||
track: FullTrack;
|
||||
episode: FullEpisode;
|
||||
}
|
||||
|
||||
export interface ListeningChangeEventArgs {
|
||||
@ -9,6 +28,18 @@ export interface ListeningChangeEventArgs {
|
||||
username: string;
|
||||
}
|
||||
|
||||
export interface CurrentlyPlayingContextDTO {
|
||||
device: Device;
|
||||
repeatState: string;
|
||||
shuffleState: boolean;
|
||||
context: Context;
|
||||
timestamp: number;
|
||||
progressMs: number;
|
||||
isPlaying: boolean;
|
||||
currentlyPlayingType: string;
|
||||
actions: Actions;
|
||||
}
|
||||
|
||||
export interface CurrentlyPlayingContext {
|
||||
device: Device;
|
||||
repeatState: string;
|
||||
|
@ -1,11 +1,11 @@
|
||||
import * as signalR from "@microsoft/signalr";
|
||||
import { FullTrack } from "./HubInterfaces";
|
||||
import { FullTrack, CurrentlyPlayingDTO } from "./HubInterfaces";
|
||||
|
||||
const connection = new signalR.HubConnectionBuilder()
|
||||
.withUrl("/hub")
|
||||
.build();
|
||||
|
||||
connection.on("OnNewPlaying", context => console.log(context));
|
||||
connection.on("OnNewPlaying", (context: CurrentlyPlayingDTO) => console.log(context));
|
||||
|
||||
connection.start().catch(err => console.error(err));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user