adding dummies for faking a spotify connection
This commit is contained in:
parent
7037dce2da
commit
6ae7994610
@ -138,7 +138,7 @@ namespace Selector.CLI
|
||||
if (CacheWriterFactory is not null) consumers.Add(await CacheWriterFactory.Get());
|
||||
if (PublisherFactory is not null) consumers.Add(await PublisherFactory.Get());
|
||||
|
||||
if (MappingPersisterFactory is not null) consumers.Add(await MappingPersisterFactory.Get());
|
||||
if (MappingPersisterFactory is not null && !Magic.Dummy) consumers.Add(await MappingPersisterFactory.Get());
|
||||
|
||||
if (UserEventFirerFactory is not null) consumers.Add(await UserEventFirerFactory.Get());
|
||||
|
||||
|
@ -23,6 +23,8 @@ namespace Selector.Cache
|
||||
}
|
||||
|
||||
public async Task<IPlayerConsumer> Get(ISpotifyConfigFactory spotifyFactory, IPlayerWatcher watcher = null)
|
||||
{
|
||||
if (!Magic.Dummy)
|
||||
{
|
||||
var config = await spotifyFactory.GetConfig();
|
||||
var client = new SpotifyClient(config);
|
||||
@ -34,5 +36,13 @@ namespace Selector.Cache
|
||||
LoggerFactory.CreateLogger<CachingAudioFeatureInjector>()
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new DummyAudioFeatureInjector(
|
||||
watcher,
|
||||
LoggerFactory.CreateLogger<DummyAudioFeatureInjector>()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ namespace Selector.Cache
|
||||
var track = await Cache?.StringGetAsync(Key.AudioFeature(trackId));
|
||||
if (Cache is null || track == RedisValue.Null)
|
||||
{
|
||||
if(!string.IsNullOrWhiteSpace(refreshToken))
|
||||
if(!string.IsNullOrWhiteSpace(refreshToken) && !Magic.Dummy)
|
||||
{
|
||||
var factory = await SpotifyFactory.GetFactory(refreshToken);
|
||||
var spotifyClient = new SpotifyClient(await factory.GetConfig());
|
||||
|
@ -49,7 +49,7 @@ namespace Selector
|
||||
}, CancelToken);
|
||||
}
|
||||
|
||||
public async Task AsyncCallback(ListeningChangeEventArgs e)
|
||||
public virtual async Task AsyncCallback(ListeningChangeEventArgs e)
|
||||
{
|
||||
using var scope = Logger.GetListeningEventArgsScope(e);
|
||||
|
||||
|
101
Selector/Consumers/DummyAudioFeatureInjector.cs
Normal file
101
Selector/Consumers/DummyAudioFeatureInjector.cs
Normal file
@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using SpotifyAPI.Web;
|
||||
|
||||
namespace Selector
|
||||
{
|
||||
public class DummyAudioFeatureInjector : AudioFeatureInjector
|
||||
{
|
||||
private TrackAudioFeatures[] _features = new[]
|
||||
{
|
||||
new TrackAudioFeatures
|
||||
{
|
||||
Acousticness = 0.5f,
|
||||
Danceability = 0.5f,
|
||||
DurationMs = 100,
|
||||
Energy = 0.5f,
|
||||
Instrumentalness = 0.5f,
|
||||
Key = 5,
|
||||
Liveness = 0.5f,
|
||||
Loudness = 0.5f,
|
||||
Mode = 1,
|
||||
Speechiness = 0.5f,
|
||||
Tempo = 120f,
|
||||
TimeSignature = 4,
|
||||
Valence = 0.5f,
|
||||
}
|
||||
};
|
||||
private int _contextIdx = 0;
|
||||
|
||||
private DateTime _lastNext = DateTime.UtcNow;
|
||||
private TimeSpan _contextLifespan = TimeSpan.FromSeconds(30);
|
||||
|
||||
public DummyAudioFeatureInjector(
|
||||
IPlayerWatcher watcher,
|
||||
ILogger<DummyAudioFeatureInjector> logger = null,
|
||||
CancellationToken token = default
|
||||
): base (watcher, null, logger, token)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private bool ShouldCycle() => DateTime.UtcNow - _lastNext > _contextLifespan;
|
||||
|
||||
private void BumpContextIndex()
|
||||
{
|
||||
Logger.LogDebug("Next feature");
|
||||
|
||||
_contextIdx++;
|
||||
|
||||
if (_contextIdx >= _features.Length)
|
||||
{
|
||||
_contextIdx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private TrackAudioFeatures GetFeature()
|
||||
{
|
||||
if (ShouldCycle()) BumpContextIndex();
|
||||
|
||||
return _features[_contextIdx];
|
||||
}
|
||||
|
||||
public override Task AsyncCallback(ListeningChangeEventArgs e)
|
||||
{
|
||||
using var scope = Logger.GetListeningEventArgsScope(e);
|
||||
|
||||
if (e.Current.Item is FullTrack track)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(track.Id)) return Task.CompletedTask;
|
||||
|
||||
var audioFeatures = GetFeature();
|
||||
|
||||
var analysedTrack = AnalysedTrack.From(track, audioFeatures);
|
||||
|
||||
Timeline.Add(analysedTrack, DateHelper.FromUnixMilli(e.Current.Timestamp));
|
||||
OnNewFeature(analysedTrack);
|
||||
}
|
||||
else if (e.Current.Item is FullEpisode episode)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(episode.Id)) return Task.CompletedTask;
|
||||
|
||||
Logger.LogDebug("Ignoring podcast episdoe [{episode}]", episode.DisplayString());
|
||||
}
|
||||
else if (e.Current.Item is null)
|
||||
{
|
||||
Logger.LogDebug("Skipping audio feature pulling for null item [{context}]", e.Current.DisplayString());
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogError("Unknown item pulled from API [{item}]", e.Current.Item);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
@ -23,6 +23,8 @@ namespace Selector
|
||||
}
|
||||
|
||||
public async Task<IPlayerConsumer> Get(ISpotifyConfigFactory spotifyFactory, IPlayerWatcher watcher = null)
|
||||
{
|
||||
if (!Magic.Dummy)
|
||||
{
|
||||
var config = await spotifyFactory.GetConfig();
|
||||
var client = new SpotifyClient(config);
|
||||
@ -33,5 +35,13 @@ namespace Selector
|
||||
LoggerFactory.CreateLogger<AudioFeatureInjector>()
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new DummyAudioFeatureInjector(
|
||||
watcher,
|
||||
LoggerFactory.CreateLogger<DummyAudioFeatureInjector>()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
8
Selector/Magic.cs
Normal file
8
Selector/Magic.cs
Normal file
@ -0,0 +1,8 @@
|
||||
using System;
|
||||
namespace Selector;
|
||||
|
||||
public static class Magic
|
||||
{
|
||||
public const bool Dummy = true;
|
||||
}
|
||||
|
163
Selector/Watcher/DummyPlayerWatcher.cs
Normal file
163
Selector/Watcher/DummyPlayerWatcher.cs
Normal file
@ -0,0 +1,163 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SpotifyAPI.Web;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Selector
|
||||
{
|
||||
public class DummyPlayerWatcher: PlayerWatcher
|
||||
{
|
||||
private CurrentlyPlayingContext[] _contexts = new[]
|
||||
{
|
||||
new CurrentlyPlayingContext
|
||||
{
|
||||
Device = new Device
|
||||
{
|
||||
Id = "Dev 1",
|
||||
IsActive = true,
|
||||
IsPrivateSession = false,
|
||||
IsRestricted = false,
|
||||
Name = "Dev 1",
|
||||
Type = "Phone",
|
||||
VolumePercent = 50
|
||||
},
|
||||
ShuffleState = false,
|
||||
Context = new Context
|
||||
{
|
||||
|
||||
},
|
||||
IsPlaying = true,
|
||||
Item = new FullTrack
|
||||
{
|
||||
Name = "Track 1",
|
||||
Id = "track-1",
|
||||
Href = "https://sarsoo.xyz",
|
||||
Popularity = 50,
|
||||
Uri = "spotify:track:1",
|
||||
Album = new SimpleAlbum
|
||||
{
|
||||
Name = "Album 1",
|
||||
Artists = new List<SimpleArtist>
|
||||
{
|
||||
new SimpleArtist
|
||||
{
|
||||
Name = "Artist 1"
|
||||
}
|
||||
}
|
||||
},
|
||||
Artists = new List<SimpleArtist>
|
||||
{
|
||||
new SimpleArtist
|
||||
{
|
||||
Name = "Artist 1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
new CurrentlyPlayingContext
|
||||
{
|
||||
Device = new Device
|
||||
{
|
||||
Id = "Dev 1",
|
||||
IsActive = true,
|
||||
IsPrivateSession = false,
|
||||
IsRestricted = false,
|
||||
Name = "Dev 1",
|
||||
Type = "Phone",
|
||||
VolumePercent = 50
|
||||
},
|
||||
ShuffleState = false,
|
||||
Context = new Context
|
||||
{
|
||||
|
||||
},
|
||||
IsPlaying = true,
|
||||
Item = new FullTrack
|
||||
{
|
||||
Name = "Track 2",
|
||||
Id = "track-2",
|
||||
Href = "https://sarsoo.xyz",
|
||||
Popularity = 50,
|
||||
Uri = "spotify:track:2",
|
||||
Album = new SimpleAlbum
|
||||
{
|
||||
Name = "Album 1",
|
||||
Artists = new List<SimpleArtist>
|
||||
{
|
||||
new SimpleArtist
|
||||
{
|
||||
Name = "Artist 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
Artists = new List<SimpleArtist>
|
||||
{
|
||||
new SimpleArtist
|
||||
{
|
||||
Name = "Artist 2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private int _contextIdx = 0;
|
||||
|
||||
private DateTime _lastNext = DateTime.UtcNow;
|
||||
private TimeSpan _contextLifespan = TimeSpan.FromSeconds(30);
|
||||
|
||||
public DummyPlayerWatcher(IEqual equalityChecker,
|
||||
ILogger<DummyPlayerWatcher> logger = null,
|
||||
int pollPeriod = 3000) : base(null, equalityChecker, logger, pollPeriod)
|
||||
{
|
||||
}
|
||||
|
||||
private bool ShouldCycle() => DateTime.UtcNow - _lastNext > _contextLifespan;
|
||||
|
||||
private void BumpContextIndex()
|
||||
{
|
||||
Logger.LogDebug("Next song");
|
||||
|
||||
_contextIdx++;
|
||||
|
||||
if(_contextIdx >= _contexts.Length)
|
||||
{
|
||||
_contextIdx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private CurrentlyPlayingContext GetContext()
|
||||
{
|
||||
if (ShouldCycle()) BumpContextIndex();
|
||||
|
||||
return _contexts[_contextIdx];
|
||||
}
|
||||
|
||||
public override Task WatchOne(CancellationToken token = default)
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
var polledCurrent = GetContext();
|
||||
|
||||
using var polledLogScope = Logger.BeginScope(new Dictionary<string, object>() { { "context", polledCurrent?.DisplayString() } });
|
||||
|
||||
if (polledCurrent != null) StoreCurrentPlaying(polledCurrent);
|
||||
|
||||
// swap new item into live and bump existing down to previous
|
||||
Previous = Live;
|
||||
Live = polledCurrent;
|
||||
|
||||
OnNetworkPoll(GetEvent());
|
||||
|
||||
CheckPlaying();
|
||||
CheckContext();
|
||||
CheckItem();
|
||||
CheckDevice();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ namespace Selector
|
||||
{
|
||||
public class PlayerWatcher: BaseWatcher, IPlayerWatcher
|
||||
{
|
||||
new private readonly ILogger<PlayerWatcher> Logger;
|
||||
new protected readonly ILogger<PlayerWatcher> Logger;
|
||||
private readonly IPlayerClient spotifyClient;
|
||||
private readonly IEqual eq;
|
||||
|
||||
@ -26,8 +26,8 @@ namespace Selector
|
||||
public event EventHandler<ListeningChangeEventArgs> DeviceChange;
|
||||
public event EventHandler<ListeningChangeEventArgs> PlayingChange;
|
||||
|
||||
public CurrentlyPlayingContext Live { get; private set; }
|
||||
private CurrentlyPlayingContext Previous { get; set; }
|
||||
public CurrentlyPlayingContext Live { get; protected set; }
|
||||
protected CurrentlyPlayingContext Previous { get; set; }
|
||||
public PlayerTimeline Past { get; set; } = new();
|
||||
|
||||
public PlayerWatcher(IPlayerClient spotifyClient,
|
||||
@ -95,7 +95,7 @@ namespace Selector
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckItem()
|
||||
protected void CheckItem()
|
||||
{
|
||||
|
||||
switch (Previous, Live)
|
||||
@ -163,7 +163,7 @@ namespace Selector
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckContext()
|
||||
protected void CheckContext()
|
||||
{
|
||||
if ((Previous, Live)
|
||||
is (null or { Context: null }, { Context: not null }))
|
||||
@ -178,7 +178,7 @@ namespace Selector
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckPlaying()
|
||||
protected void CheckPlaying()
|
||||
{
|
||||
switch (Previous, Live)
|
||||
{
|
||||
@ -201,7 +201,7 @@ namespace Selector
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckDevice()
|
||||
protected void CheckDevice()
|
||||
{
|
||||
// DEVICE
|
||||
if (!eq.IsEqual(Previous?.Device, Live?.Device))
|
||||
@ -218,13 +218,13 @@ namespace Selector
|
||||
}
|
||||
}
|
||||
|
||||
private ListeningChangeEventArgs GetEvent() => ListeningChangeEventArgs.From(Previous, Live, Past, id: Id, username: SpotifyUsername);
|
||||
protected ListeningChangeEventArgs GetEvent() => ListeningChangeEventArgs.From(Previous, Live, Past, id: Id, username: SpotifyUsername);
|
||||
|
||||
/// <summary>
|
||||
/// Store currently playing in last plays. Determine whether new list or appending required
|
||||
/// </summary>
|
||||
/// <param name="current">New currently playing to store</param>
|
||||
private void StoreCurrentPlaying(CurrentlyPlayingContext current)
|
||||
protected void StoreCurrentPlaying(CurrentlyPlayingContext current)
|
||||
{
|
||||
Past?.Add(current);
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ namespace Selector
|
||||
where T : class, IWatcher
|
||||
{
|
||||
if(typeof(T).IsAssignableFrom(typeof(PlayerWatcher)))
|
||||
{
|
||||
if(!Magic.Dummy)
|
||||
{
|
||||
var config = await spotifyFactory.GetConfig();
|
||||
var client = new SpotifyClient(config);
|
||||
@ -35,11 +37,25 @@ namespace Selector
|
||||
Equal,
|
||||
LoggerFactory?.CreateLogger<PlayerWatcher>() ?? NullLogger<PlayerWatcher>.Instance,
|
||||
pollPeriod: pollPeriod
|
||||
) {
|
||||
)
|
||||
{
|
||||
SpotifyUsername = user.DisplayName,
|
||||
Id = id
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
return new DummyPlayerWatcher(
|
||||
Equal,
|
||||
LoggerFactory?.CreateLogger<DummyPlayerWatcher>() ?? NullLogger<DummyPlayerWatcher>.Instance,
|
||||
pollPeriod: pollPeriod
|
||||
)
|
||||
{
|
||||
SpotifyUsername = "dummy",
|
||||
Id = id
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (typeof(T).IsAssignableFrom(typeof(PlaylistWatcher)))
|
||||
{
|
||||
var config = await spotifyFactory.GetConfig();
|
||||
|
Loading…
Reference in New Issue
Block a user