adding timeline, manager interfaces, adding IEqual
This commit is contained in:
parent
81ded7eb7c
commit
2136a7953b
@ -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]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
24
Selector/Equality/Equal.cs
Normal file
24
Selector/Equality/Equal.cs
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Selector/Equality/IEqual.cs
Normal file
11
Selector/Equality/IEqual.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
11
Selector/Helpers/DateHelper.cs
Normal file
11
Selector/Helpers/DateHelper.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
13
Selector/Timeline/Interfaces/ITimeline.cs
Normal file
13
Selector/Timeline/Interfaces/ITimeline.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
10
Selector/Timeline/Interfaces/ITimelineItem.cs
Normal file
10
Selector/Timeline/Interfaces/ITimelineItem.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Selector
|
||||||
|
{
|
||||||
|
public interface ITimelineItem<T>
|
||||||
|
{
|
||||||
|
public T Item { get; set; }
|
||||||
|
public DateTime Time { get; set; }
|
||||||
|
}
|
||||||
|
}
|
10
Selector/Timeline/Playable.cs
Normal file
10
Selector/Timeline/Playable.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Selector
|
||||||
|
{
|
||||||
|
class Playable
|
||||||
|
{
|
||||||
|
public Type Type;
|
||||||
|
public object Obj;
|
||||||
|
}
|
||||||
|
}
|
29
Selector/Timeline/Timeline.cs
Normal file
29
Selector/Timeline/Timeline.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
Selector/Timeline/TimelineItem.cs
Normal file
19
Selector/Timeline/TimelineItem.cs
Normal 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
Selector/Watcher/BaseWatcher.cs
Normal file
29
Selector/Watcher/BaseWatcher.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
Selector/Watcher/Interfaces/IManager.cs
Normal file
14
Selector/Watcher/Interfaces/IManager.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user