adding timeline, manager interfaces, adding IEqual

This commit is contained in:
andy 2021-09-30 22:23:44 +01:00
parent 81ded7eb7c
commit 2136a7953b
14 changed files with 219 additions and 104 deletions

View File

@ -38,8 +38,7 @@ namespace Selector.Tests
for (var i = 0; i < playing.Count; i++) for (var i = 0; i < playing.Count; i++)
{ {
await watcher.WatchOne(); await watcher.WatchOne();
var current = watcher.NowPlaying(); watcher.Live.Should().Be(playing[i]);
current.Should().Be(playing[i]);
} }
} }

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Selector
{
class Equal : IEqual
{
private Dictionary<Type, object> comps;
public bool IsEqual<T>(T item, T other)
{
if (comps.ContainsKey(typeof(T)))
{
var comp = (IEqualityComparer<T>) comps[typeof(T)];
return comp.Equals(item, other);
}
else
{
throw new ArgumentException($"{typeof(T)} had no corresponding equality checker");
}
}
}
}

View File

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Selector
{
public interface IEqual
{
public bool IsEqual<T>(T item, T other);
}
}

View File

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Selector
{
public static class DateHelper
{
public static DateTime FromUnixMilli(long milli) => new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(milli);
}
}

View File

@ -0,0 +1,13 @@
using System;
namespace Selector
{
public interface ITimeline<T>
{
public int Count { get; set; }
public T Get(DateTime at);
public T Get();
public void Add(T item, DateTime timestamp);
public void Clear();
}
}

View File

@ -0,0 +1,10 @@
using System;
namespace Selector
{
public interface ITimelineItem<T>
{
public T Item { get; set; }
public DateTime Time { get; set; }
}
}

View File

@ -0,0 +1,10 @@
using System;
namespace Selector
{
class Playable
{
public Type Type;
public object Obj;
}
}

View File

@ -0,0 +1,29 @@
using System;
namespace Selector
{
public class Timeline<T> : ITimeline<T>
{
public int Count { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public void Add(T item, DateTime timestamp)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
public T Get(DateTime at)
{
throw new NotImplementedException();
}
public T Get()
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,19 @@
using System;
namespace Selector
{
public class TimelineItem<T>: ITimelineItem<T>
{
public T Item { get; set; }
public DateTime Time { get; set; }
public static TimelineItem<TT> From<TT>(TT item, DateTime time)
{
return new TimelineItem<TT>()
{
Item = item,
Time = time
};
}
}
}

View File

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Selector
{
public abstract class BaseWatcher: IWatcher
{
public abstract Task WatchOne();
public async Task Watch(CancellationToken cancelToken)
{
while (!cancelToken.IsCancellationRequested)
{
await WatchOne();
await Task.Delay(PollPeriod);
}
}
private int _pollPeriod;
public int PollPeriod
{
get => _pollPeriod;
set => _pollPeriod = Math.Max(0, value);
}
}
}

View File

@ -6,5 +6,14 @@ namespace Selector
public class ListeningChangeEventArgs: EventArgs { public class ListeningChangeEventArgs: EventArgs {
public CurrentlyPlayingContext Previous; public CurrentlyPlayingContext Previous;
public CurrentlyPlayingContext Current; public CurrentlyPlayingContext Current;
public static ListeningChangeEventArgs From(CurrentlyPlayingContext previous, CurrentlyPlayingContext current)
{
return new ListeningChangeEventArgs()
{
Previous = previous,
Current = current
};
}
} }
} }

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Selector
{
interface IManager
{
public void Add(IWatcher watcher);
public bool Start();
public bool Stop();
}
}

View File

@ -15,8 +15,5 @@ namespace Selector
public event EventHandler<ListeningChangeEventArgs> VolumeChange; public event EventHandler<ListeningChangeEventArgs> VolumeChange;
public event EventHandler<ListeningChangeEventArgs> DeviceChange; public event EventHandler<ListeningChangeEventArgs> DeviceChange;
public event EventHandler<ListeningChangeEventArgs> PlayingChange; public event EventHandler<ListeningChangeEventArgs> PlayingChange;
public CurrentlyPlayingContext NowPlaying();
// recently playing
} }
} }

View File

@ -6,10 +6,10 @@ using SpotifyAPI.Web;
namespace Selector namespace Selector
{ {
public class PlayerWatcher: IPlayerWatcher public class PlayerWatcher: BaseWatcher, IPlayerWatcher
{ {
private readonly IPlayerClient spotifyClient; private readonly IPlayerClient spotifyClient;
private IEqualityChecker equalityChecker; private readonly IEqualityChecker equalityChecker;
public event EventHandler<ListeningChangeEventArgs> ItemChange; public event EventHandler<ListeningChangeEventArgs> ItemChange;
public event EventHandler<ListeningChangeEventArgs> AlbumChange; public event EventHandler<ListeningChangeEventArgs> AlbumChange;
@ -21,15 +21,10 @@ namespace Selector
public event EventHandler<ListeningChangeEventArgs> DeviceChange; public event EventHandler<ListeningChangeEventArgs> DeviceChange;
public event EventHandler<ListeningChangeEventArgs> PlayingChange; public event EventHandler<ListeningChangeEventArgs> PlayingChange;
private CurrentlyPlayingContext live { get; set; } public CurrentlyPlayingContext Live { get; private set; }
public ITimeline<CurrentlyPlayingContext> Past { get; private set; }
private List<List<CurrentlyPlayingContext>> lastPlays { get; set; } private List<List<CurrentlyPlayingContext>> lastPlays { get; set; }
private int _pollPeriod;
public int PollPeriod {
get => _pollPeriod;
set => _pollPeriod = Math.Max(0, value);
}
public PlayerWatcher(IPlayerClient spotifyClient, public PlayerWatcher(IPlayerClient spotifyClient,
IEqualityChecker equalityChecker, IEqualityChecker equalityChecker,
int pollPeriod = 3000) { int pollPeriod = 3000) {
@ -41,101 +36,72 @@ namespace Selector
lastPlays = new List<List<CurrentlyPlayingContext>>(); lastPlays = new List<List<CurrentlyPlayingContext>>();
} }
public async Task WatchOne() public override async Task WatchOne()
{ {
try{ try{
var polledCurrent = await spotifyClient.GetCurrentPlayback(); var polledCurrent = await spotifyClient.GetCurrentPlayback();
if (polledCurrent != null) StoreCurrentPlaying(live, polledCurrent); if (polledCurrent != null) StoreCurrentPlaying(Live, polledCurrent);
// swap new item into live and bump existing down to previous
CurrentlyPlayingContext previous; CurrentlyPlayingContext previous;
if(live is null) { if(Live is null) {
live = polledCurrent; Live = polledCurrent;
previous = polledCurrent; previous = polledCurrent;
} }
else { else {
previous = live; previous = Live;
live = polledCurrent; Live = polledCurrent;
} }
// NOT PLAYING // NOT PLAYING
if(previous is null && live is null) if(previous is null && Live is null)
{ {
// Console.WriteLine("not playing"); // Console.WriteLine("not playing");
} }
else else
{ {
// STARTED PLAYBACK // STARTED PLAYBACK
if(previous is null && (live.Item is FullTrack || live.Item is FullEpisode)) if(previous is null && (Live.Item is FullTrack || Live.Item is FullEpisode))
{ {
// Console.WriteLine("started playing"); OnPlayingChange(ListeningChangeEventArgs.From(previous, Live));
OnPlayingChange(new ListeningChangeEventArgs(){
Previous = previous,
Current = live
});
} }
// STOPPED PLAYBACK // STOPPED PLAYBACK
else if((previous.Item is FullTrack || previous.Item is FullEpisode) && live is null) else if((previous.Item is FullTrack || previous.Item is FullEpisode) && Live is null)
{ {
// Console.WriteLine("stopped playing"); OnPlayingChange(ListeningChangeEventArgs.From(previous, Live));
OnPlayingChange(new ListeningChangeEventArgs(){
Previous = previous,
Current = live
});
} }
// CONTINUING PLAYBACK
else { else {
// MUSIC // MUSIC
if(previous.Item is FullTrack && live.Item is FullTrack) if(previous.Item is FullTrack previousTrack && Live.Item is FullTrack currentTrack)
{ {
var previousItem = (FullTrack) previous.Item;
var currentItem = (FullTrack) live.Item;
if(!equalityChecker.Track(previousItem, currentItem, true)) { if(!equalityChecker.Track(previousTrack, currentTrack, true)) {
OnItemChange(new ListeningChangeEventArgs(){ OnItemChange(ListeningChangeEventArgs.From(previous, Live));
Previous = previous,
Current = live
});
} }
if(!equalityChecker.Album(previousItem.Album, currentItem.Album)) { if(!equalityChecker.Album(previousTrack.Album, currentTrack.Album)) {
OnAlbumChange(new ListeningChangeEventArgs(){ OnAlbumChange(ListeningChangeEventArgs.From(previous, Live));
Previous = previous,
Current = live
});
} }
if(!equalityChecker.Artist(previousItem.Artists[0], currentItem.Artists[0])) { if(!equalityChecker.Artist(previousTrack.Artists[0], currentTrack.Artists[0])) {
OnArtistChange(new ListeningChangeEventArgs(){ OnArtistChange(ListeningChangeEventArgs.From(previous, Live));
Previous = previous,
Current = live
});
} }
} }
// CHANGED CONTENT // CHANGED CONTENT
else if(previous.Item is FullTrack && live.Item is FullEpisode else if(previous.Item is FullTrack && Live.Item is FullEpisode
|| previous.Item is FullEpisode && live.Item is FullTrack) || previous.Item is FullEpisode && Live.Item is FullTrack)
{ {
OnContentChange(new ListeningChangeEventArgs(){ OnContentChange(ListeningChangeEventArgs.From(previous, Live));
Previous = previous, OnItemChange(ListeningChangeEventArgs.From(previous, Live));
Current = live
});
OnItemChange(new ListeningChangeEventArgs(){
Previous = previous,
Current = live
});
} }
// PODCASTS // PODCASTS
else if(previous.Item is FullEpisode && live.Item is FullEpisode) else if(previous.Item is FullEpisode previousEp && Live.Item is FullEpisode currentEp)
{ {
var previousItem = (FullEpisode) previous.Item; if(!equalityChecker.Episode(previousEp, currentEp)) {
var currentItem = (FullEpisode) live.Item; OnItemChange(ListeningChangeEventArgs.From(previous, Live));
if(!equalityChecker.Episode(previousItem, currentItem)) {
OnItemChange(new ListeningChangeEventArgs(){
Previous = previous,
Current = live
});
} }
} }
else { else {
@ -143,35 +109,23 @@ namespace Selector
} }
// CONTEXT // CONTEXT
if(!equalityChecker.Context(previous.Context, live.Context)) { if(!equalityChecker.Context(previous.Context, Live.Context)) {
OnContextChange(new ListeningChangeEventArgs(){ OnContextChange(ListeningChangeEventArgs.From(previous, Live));
Previous = previous,
Current = live
});
} }
// DEVICE // DEVICE
if(!equalityChecker.Device(previous?.Device, live?.Device)) { if(!equalityChecker.Device(previous?.Device, Live?.Device)) {
OnDeviceChange(new ListeningChangeEventArgs(){ OnDeviceChange(ListeningChangeEventArgs.From(previous, Live));
Previous = previous,
Current = live
});
} }
// IS PLAYING // IS PLAYING
if(previous.IsPlaying != live.IsPlaying) { if(previous.IsPlaying != Live.IsPlaying) {
OnPlayingChange(new ListeningChangeEventArgs(){ OnPlayingChange(ListeningChangeEventArgs.From(previous, Live));
Previous = previous,
Current = live
});
} }
// VOLUME // VOLUME
if(previous.Device.VolumePercent != live.Device.VolumePercent) { if(previous.Device.VolumePercent != Live.Device.VolumePercent) {
OnVolumeChange(new ListeningChangeEventArgs(){ OnVolumeChange(ListeningChangeEventArgs.From(previous, Live));
Previous = previous,
Current = live
});
} }
} }
} }
@ -190,20 +144,6 @@ namespace Selector
} }
} }
public async Task Watch(CancellationToken cancelToken)
{
while (!cancelToken.IsCancellationRequested)
{
await WatchOne();
await Task.Delay(PollPeriod);
}
}
public CurrentlyPlayingContext NowPlaying()
{
return live;
}
/// <summary> /// <summary>
/// Store currently playing in last plays. Determine whether new list or appending required /// Store currently playing in last plays. Determine whether new list or appending required
/// </summary> /// </summary>