not throwing expired token exceptions, typed config in set up

fixes #5
This commit is contained in:
andy 2021-10-15 19:58:07 +01:00
parent 9dfad73397
commit d139dce719
8 changed files with 83 additions and 35 deletions

View File

@ -23,40 +23,39 @@ namespace Selector.CLI
Console.WriteLine("~~~ Selector CLI ~~~");
Console.WriteLine("");
Console.WriteLine("Configuring...");
Console.WriteLine("> Configuring...");
// CONFIG
services.Configure<RootOptions>(options => {
context.Configuration.GetSection(RootOptions.Key).Bind(options);
context.Configuration.GetSection($"{RootOptions.Key}:{WatcherOptions.Key}").Bind(options.WatcherOptions);
});
var config = context.Configuration.GetSection(RootOptions.Key).Get<RootOptions>();
Console.WriteLine("Adding Services...");
Console.WriteLine("> Adding Services...");
// SERVICES
services.AddSingleton<IWatcherFactory, WatcherFactory>();
services.AddSingleton<IConsumerFactory, AudioFeatureInjectorFactory>();
services.AddSingleton<IWatcherCollectionFactory, WatcherCollectionFactory>();
// For generating spotify clients
services.AddSingleton<IRefreshTokenFactoryProvider, RefreshTokenFactoryProvider>();
//services.AddSingleton<IRefreshTokenFactoryProvider, RefreshTokenFactoryProvider>();
services.AddSingleton<IRefreshTokenFactoryProvider, CachingRefreshTokenFactoryProvider>();
switch(context.Configuration.GetValue<EqualityChecker>("selector:equality"))
switch(config.Equality)
{
case EqualityChecker.Uri:
Console.WriteLine("Using Uri Equality");
Console.WriteLine("> Using Uri Equality");
services.AddTransient<IEqual, UriEqual>();
break;
case EqualityChecker.String:
Console.WriteLine("Using String Equality");
Console.WriteLine("> Using String Equality");
services.AddTransient<IEqual, StringEqual>();
break;
}
// HOSTED SERVICES
if(context.Configuration
.GetSection($"{RootOptions.Key}:{WatcherOptions.Key}")
.Get<WatcherOptions>()
.Enabled)
if(config.WatcherOptions.Enabled)
{
Console.WriteLine("Adding Watcher Service");
Console.WriteLine("> Adding Watcher Service");
services.AddHostedService<WatcherService>();
}

View File

@ -1,26 +1,27 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- XSD manual extracted from package NLog.Schema: https://www.nuget.org/packages/NLog.Schema-->
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xsi:schemaLocation="NLog NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogFile=".\selector.nlog.log"
internalLogLevel="Info" >
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
throwConfigExceptions="true"
internalLogFile=".\log\selector.nlog.log"
internalLogLevel="Info" >
<variable name="format"
value="${longdate}|${level:uppercase=true}|${callsite}:${callsite-linenumber}|${message}${onexception:inner=${newline}}${exception:format=tostring,data:exceptionDataSeparator=\r\n}"/>
<!-- the targets to write to -->
<targets>
<!-- write logs to file -->
<target xsi:type="File"
name="logfile"
fileName=".\selector.log"
<target xsi:type="File"
name="logfile"
fileName=".\log\selector-${shortdate}.log"
layout="${format}" />
<target xsi:type="File"
name="tracefile"
fileName=".\selector.trace.log"
<target xsi:type="File"
name="tracefile"
fileName=".\log\selector.trace-${shortdate}.log"
layout="${format}" />
<target xsi:type="Console"
<target xsi:type="Console"
name="logconsole"
layout="${format}" />
</targets>
@ -29,7 +30,6 @@
<rules>
<logger name="*" minlevel="Debug" writeTo="logfile" />
<logger name="*" minlevel="Trace" writeTo="tracefile" />
<logger name="Selector.*" minlevel="Debug" writeTo="logconsole" />
</rules>
</nlog>

View File

@ -42,11 +42,28 @@ namespace Selector
{
if (e.Current.Item is FullTrack track)
{
Logger.LogTrace("Making Spotify call");
var audioFeatures = await TrackClient.GetAudioFeatures(track.Id);
Logger.LogDebug($"Adding audio features [{track.DisplayString()}]: [{audioFeatures.DisplayString()}]");
try {
Logger.LogTrace("Making Spotify call");
var audioFeatures = await TrackClient.GetAudioFeatures(track.Id);
Logger.LogDebug($"Adding audio features [{track.DisplayString()}]: [{audioFeatures.DisplayString()}]");
Timeline.Add(AnalysedTrack.From(track, audioFeatures));
Timeline.Add(AnalysedTrack.From(track, audioFeatures), DateHelper.FromUnixMilli(e.Current.Timestamp));
}
catch (APIUnauthorizedException ex)
{
Logger.LogDebug($"Unauthorised error: [{ex.Message}] (should be refreshed and retried?)");
//throw ex;
}
catch (APITooManyRequestsException ex)
{
Logger.LogDebug($"Too many requests error: [{ex.Message}]");
throw ex;
}
catch (APIException ex)
{
Logger.LogDebug($"API error: [{ex.Message}]");
throw ex;
}
}
else if (e.Current.Item is FullEpisode episode)
{

View File

@ -17,11 +17,32 @@ namespace Selector
public static string DisplayString(this SimpleShow show) => $"{show.Name} / {show.Publisher}";
public static string DisplayString(this CurrentlyPlayingContext currentPlaying) => $"{currentPlaying.IsPlaying}, {currentPlaying.Item}, {currentPlaying.Device.DisplayString()}";
public static string DisplayString(this CurrentlyPlayingContext currentPlaying) {
if (currentPlaying.Item is FullTrack track)
{
return $"{currentPlaying.IsPlaying}, {track.DisplayString()}, {currentPlaying.Device.DisplayString()}";
}
else if (currentPlaying.Item is FullEpisode episode)
{
return $"{currentPlaying.IsPlaying}, {episode.DisplayString()}, {currentPlaying.Device.DisplayString()}";
}
else
{
throw new ArgumentException("Unknown playing type");
}
}
public static string DisplayString(this Context context) => $"{context.Type}, {context.Uri}";
public static string DisplayString(this Device device) => $"{device.Name} ({device.Id}) {device.VolumePercent}%";
public static string DisplayString(this TrackAudioFeatures feature) => $"Acou. {feature.Acousticness}, Dance {feature.Danceability}, Energy {feature.Energy}, Instru. {feature.Instrumentalness}, Key {feature.Key}, Live {feature.Liveness}, Loud {feature.Loudness}, Mode {feature.Mode}, Speech {feature.Speechiness}, Tempo {feature.Tempo}, Valence {feature.Valence}";
public static string DisplayString(this TrackAudioFeatures feature) => $"Acou. {feature.Acousticness}, Dance {feature.Danceability}, Energy {feature.Energy}, Instru. {feature.Instrumentalness}, Key {feature.Key}, Live {feature.Liveness}, Loud {feature.Loudness}dB, Mode {feature.Mode}, Speech {feature.Speechiness}, Tempo {feature.Tempo}BPM, Time Sig. {feature.TimeSignature}, Valence {feature.Valence}";
public static string DisplayString(this IEnumerable<SimpleArtist> artists) => string.Join(", ", artists.Select(a => a.DisplayString()));
public static bool IsInstrumental(this TrackAudioFeatures feature) => feature.Instrumentalness > 0.5;
public static bool IsLive(this TrackAudioFeatures feature) => feature.Liveness > 0.8f;
public static bool IsSpokenWord(this TrackAudioFeatures feature) => feature.Speechiness > 0.66f;
public static bool IsSpeechAndMusic(this TrackAudioFeatures feature) => feature.Speechiness is >= 0.33f and <= 0.66f;
public static bool IsNotSpeech(this TrackAudioFeatures feature) => feature.Speechiness < 0.33f;
}
}

View File

@ -32,7 +32,7 @@ namespace Selector
TokenType = refreshed.TokenType,
ExpiresIn = refreshed.ExpiresIn,
Scope = refreshed.Scope,
RefreshToken = refreshed.RefreshToken,
RefreshToken = refreshed.RefreshToken ?? RefreshToken,
CreatedAt = refreshed.CreatedAt
}));

View File

@ -9,11 +9,18 @@ namespace Selector
{
public class CachingRefreshTokenFactoryProvider : RefreshTokenFactoryProvider
{
protected readonly ILogger<CachingRefreshTokenFactoryProvider> Logger;
public CachingRefreshTokenFactoryProvider(ILogger<CachingRefreshTokenFactoryProvider> logger)
{
Logger = logger;
}
protected Dictionary<string, RefreshTokenFactory> Configs = new();
public RefreshTokenFactory GetUserConfig(string userId) => Configs.ContainsKey(userId) ? Configs[userId] : null;
new public async Task<RefreshTokenFactory> GetFactory(string refreshToken)
public override async Task<RefreshTokenFactory> GetFactory(string refreshToken)
{
var configProvider = await base.GetFactory(refreshToken);
var newConfig = await configProvider.GetConfig();
@ -27,6 +34,7 @@ namespace Selector
}
else
{
Logger.LogDebug($"New user token factory added [{userDetails.DisplayName}]");
Configs[userDetails.Id] = configProvider;
return configProvider;
}

View File

@ -22,7 +22,7 @@ namespace Selector
public bool Initialised => !string.IsNullOrWhiteSpace(ClientId) && !string.IsNullOrWhiteSpace(ClientSecret);
public Task<RefreshTokenFactory> GetFactory(string refreshToken)
public virtual Task<RefreshTokenFactory> GetFactory(string refreshToken)
{
if(!Initialised) throw new InvalidOperationException("Factory not initialised");
if(string.IsNullOrEmpty(refreshToken)) throw new ArgumentException("Null or empty refresh key provided");

View File

@ -26,7 +26,7 @@ namespace Selector
public event EventHandler<ListeningChangeEventArgs> PlayingChange;
public CurrentlyPlayingContext Live { get; private set; }
public PlayerTimeline Past { get; set; }
public PlayerTimeline Past { get; set; } = new();
public PlayerWatcher(IPlayerClient spotifyClient,
IEqual equalityChecker,
@ -154,14 +154,17 @@ namespace Selector
}
catch(APIUnauthorizedException e)
{
throw e;
Logger.LogDebug($"Unauthorised error: [{e.Message}] (should be refreshed and retried?)");
//throw e;
}
catch(APITooManyRequestsException e)
{
Logger.LogDebug($"Too many requests error: [{e.Message}]");
throw e;
}
catch(APIException e)
{
Logger.LogDebug($"API error: [{e.Message}]");
throw e;
}
}