added null CurrentlyPlaying from api, better testing

This commit is contained in:
andy 2021-09-26 15:25:54 +01:00
parent 55bba58fca
commit 823c1368e7
10 changed files with 312 additions and 70 deletions

View File

@ -1,4 +1,4 @@
name: dotnet package name: ci
on: [push] on: [push]

View File

@ -1,3 +1,5 @@
# Selector # Selector
![ci](https://github.com/sarsoo/Selector/actions/workflows/ci.yml/badge.svg)
Investigating a Spotify listening agent Investigating a Spotify listening agent

View File

@ -140,6 +140,31 @@ namespace Selector.Tests
var eq = new UriEquality(); var eq = new UriEquality();
eq.Artist(artist1, artist2).Should().Be(shouldEqual); eq.Artist(artist1, artist2).Should().Be(shouldEqual);
} }
public static IEnumerable<object[]> EpisodeData =>
new List<object[]>
{
// SAME
new object[] {
Helper.FullEpisode("1"),
Helper.FullEpisode("1"),
true
},
// DIFFERENT
new object[] {
Helper.FullEpisode("1"),
Helper.FullEpisode("2"),
false
}
};
[Theory]
[MemberData(nameof(EpisodeData))]
public void EpisodeEquality(FullEpisode episode1, FullEpisode episode2, bool shouldEqual)
{
var eq = new UriEquality();
eq.Episode(episode1, episode2).Should().Be(shouldEqual);
}
} }
public class StringEqualityTests public class StringEqualityTests
@ -273,5 +298,42 @@ namespace Selector.Tests
var eq = new StringEquality(); var eq = new StringEquality();
eq.Artist(artist1, artist2).Should().Be(shouldEqual); eq.Artist(artist1, artist2).Should().Be(shouldEqual);
} }
public static IEnumerable<object[]> EpisodeData =>
new List<object[]>
{
// SAME
new object[] {
Helper.FullEpisode("1", "1", "1"),
Helper.FullEpisode("1", "1", "1"),
true
},
// DIFFERENT PUBLISHER
new object[] {
Helper.FullEpisode("1", "1", "1"),
Helper.FullEpisode("1", "1", "2"),
false
},
// DIFFERENT SHOW
new object[] {
Helper.FullEpisode("1", "1", "1"),
Helper.FullEpisode("1", "2", "1"),
false
},
// DIFFERENT EPISODE
new object[] {
Helper.FullEpisode("1", "1", "1"),
Helper.FullEpisode("2", "1", "1"),
false
},
};
[Theory]
[MemberData(nameof(EpisodeData))]
public void EpisodeEquality(FullEpisode episode1, FullEpisode episode2, bool shouldEqual)
{
var eq = new StringEquality();
eq.Episode(episode1, episode2).Should().Be(shouldEqual);
}
} }
} }

View File

@ -26,6 +26,16 @@ namespace Selector.Tests
return FullTrack(name, album, new List<string>() { artist }); return FullTrack(name, album, new List<string>() { artist });
} }
public static FullEpisode FullEpisode(string name, string show = null, string publisher = null)
{
return new FullEpisode()
{
Name = name,
Uri = name,
Show = SimpleShow(show ?? name, publisher: publisher)
};
}
public static SimpleAlbum SimpleAlbum(string name, List<string> artists) public static SimpleAlbum SimpleAlbum(string name, List<string> artists)
{ {
return new SimpleAlbum() return new SimpleAlbum()
@ -50,6 +60,16 @@ namespace Selector.Tests
}; };
} }
public static SimpleShow SimpleShow(string name, string publisher = null)
{
return new SimpleShow()
{
Name = name,
Publisher = publisher ?? name,
Uri = name
};
}
public static CurrentlyPlaying CurrentlyPlaying(FullTrack track, bool isPlaying = true, string context = null) public static CurrentlyPlaying CurrentlyPlaying(FullTrack track, bool isPlaying = true, string context = null)
{ {
return new CurrentlyPlaying() return new CurrentlyPlaying()
@ -60,6 +80,16 @@ namespace Selector.Tests
}; };
} }
public static CurrentlyPlaying CurrentlyPlaying(FullEpisode episode, bool isPlaying = true, string context = null)
{
return new CurrentlyPlaying()
{
Context = Context(context ?? episode.Uri),
IsPlaying = isPlaying,
Item = episode
};
}
public static Context Context(string uri) public static Context Context(string uri)
{ {
return new Context() return new Context()

View File

@ -5,7 +5,7 @@ using Moq;
using FluentAssertions; using FluentAssertions;
using SpotifyAPI.Web; using SpotifyAPI.Web;
using Selector; using System.Threading;
namespace Selector.Tests namespace Selector.Tests
{ {
@ -29,12 +29,11 @@ namespace Selector.Tests
var playingQueue = new Queue<CurrentlyPlaying>(playing); var playingQueue = new Queue<CurrentlyPlaying>(playing);
var spotMock = new Mock<IPlayerClient>(); var spotMock = new Mock<IPlayerClient>();
var scheduleMock = new Mock<IScheduler>();
var eq = new UriEquality(); var eq = new UriEquality();
spotMock.Setup(s => s.GetCurrentlyPlaying(It.IsAny<PlayerCurrentlyPlayingRequest>()).Result).Returns(playingQueue.Dequeue); spotMock.Setup(s => s.GetCurrentlyPlaying(It.IsAny<PlayerCurrentlyPlayingRequest>()).Result).Returns(playingQueue.Dequeue);
var watcher = new PlayerWatcher(spotMock.Object, scheduleMock.Object, eq); var watcher = new PlayerWatcher(spotMock.Object, eq);
for(var i = 0; i < playing.Count; i++) for(var i = 0; i < playing.Count; i++)
{ {
@ -56,7 +55,7 @@ namespace Selector.Tests
// to raise // to raise
new List<string>(){ }, new List<string>(){ },
// to not raise // to not raise
new List<string>(){ "TrackChange", "AlbumChange", "ArtistChange", "ContextChange", "PlayingChange" } new List<string>(){ "ItemChange", "AlbumChange", "ArtistChange", "ContextChange", "PlayingChange" }
}, },
// TRACK CHANGE // TRACK CHANGE
new object[] { new List<CurrentlyPlaying>(){ new object[] { new List<CurrentlyPlaying>(){
@ -64,7 +63,7 @@ namespace Selector.Tests
Helper.CurrentlyPlaying(Helper.FullTrack("track2", "album1", "artist1")) Helper.CurrentlyPlaying(Helper.FullTrack("track2", "album1", "artist1"))
}, },
// to raise // to raise
new List<string>(){ "TrackChange" }, new List<string>(){ "ItemChange" },
// to not raise // to not raise
new List<string>(){ "AlbumChange", "ArtistChange" } new List<string>(){ "AlbumChange", "ArtistChange" }
}, },
@ -74,7 +73,7 @@ namespace Selector.Tests
Helper.CurrentlyPlaying(Helper.FullTrack("track1", "album2", "artist1")) Helper.CurrentlyPlaying(Helper.FullTrack("track1", "album2", "artist1"))
}, },
// to raise // to raise
new List<string>(){ "TrackChange", "AlbumChange" }, new List<string>(){ "ItemChange", "AlbumChange" },
// to not raise // to not raise
new List<string>(){ "ArtistChange" } new List<string>(){ "ArtistChange" }
}, },
@ -84,7 +83,7 @@ namespace Selector.Tests
Helper.CurrentlyPlaying(Helper.FullTrack("track1", "album1", "artist2")) Helper.CurrentlyPlaying(Helper.FullTrack("track1", "album1", "artist2"))
}, },
// to raise // to raise
new List<string>(){ "TrackChange", "AlbumChange", "ArtistChange" }, new List<string>(){ "ItemChange", "AlbumChange", "ArtistChange" },
// to not raise // to not raise
new List<string>(){ } new List<string>(){ }
}, },
@ -96,7 +95,7 @@ namespace Selector.Tests
// to raise // to raise
new List<string>(){ "ContextChange" }, new List<string>(){ "ContextChange" },
// to not raise // to not raise
new List<string>(){ "TrackChange", "AlbumChange", "ArtistChange" } new List<string>(){ "ItemChange", "AlbumChange", "ArtistChange" }
}, },
// PLAYING CHANGE // PLAYING CHANGE
new object[] { new List<CurrentlyPlaying>(){ new object[] { new List<CurrentlyPlaying>(){
@ -106,7 +105,37 @@ namespace Selector.Tests
// to raise // to raise
new List<string>(){ "PlayingChange" }, new List<string>(){ "PlayingChange" },
// to not raise // to not raise
new List<string>(){ "TrackChange", "AlbumChange", "ArtistChange", "ContextChange" } new List<string>(){ "ItemChange", "AlbumChange", "ArtistChange", "ContextChange" }
},
// PLAYING CHANGE
new object[] { new List<CurrentlyPlaying>(){
Helper.CurrentlyPlaying(Helper.FullTrack("track1", "album1", "artist1"), isPlaying: false, context: "context1"),
Helper.CurrentlyPlaying(Helper.FullTrack("track1", "album1", "artist1"), isPlaying: true, context: "context1")
},
// to raise
new List<string>(){ "PlayingChange" },
// to not raise
new List<string>(){ "ItemChange", "AlbumChange", "ArtistChange", "ContextChange" }
},
// CONTENT CHANGE
new object[] { new List<CurrentlyPlaying>(){
Helper.CurrentlyPlaying(Helper.FullTrack("track1", "album1", "artist1"), isPlaying: true, context: "context1"),
Helper.CurrentlyPlaying(Helper.FullEpisode("ep1", "show1", "pub1"), isPlaying: true, context: "context2")
},
// to raise
new List<string>(){ "ContentChange", "ContextChange", "ItemChange" },
// to not raise
new List<string>(){ "AlbumChange", "ArtistChange", "PlayingChange" }
},
// CONTENT CHANGE
new object[] { new List<CurrentlyPlaying>(){
Helper.CurrentlyPlaying(Helper.FullEpisode("ep1", "show1", "pub1"), isPlaying: true, context: "context2"),
Helper.CurrentlyPlaying(Helper.FullTrack("track1", "album1", "artist1"), isPlaying: true, context: "context1")
},
// to raise
new List<string>(){ "ContentChange", "ContextChange", "ItemChange" },
// to not raise
new List<string>(){ "AlbumChange", "ArtistChange", "PlayingChange" }
} }
}; };
@ -117,12 +146,11 @@ namespace Selector.Tests
var playingQueue = new Queue<CurrentlyPlaying>(playing); var playingQueue = new Queue<CurrentlyPlaying>(playing);
var spotMock = new Mock<IPlayerClient>(); var spotMock = new Mock<IPlayerClient>();
var scheduleMock = new Mock<IScheduler>();
var eq = new UriEquality(); var eq = new UriEquality();
spotMock.Setup(s => s.GetCurrentlyPlaying(It.IsAny<PlayerCurrentlyPlayingRequest>()).Result).Returns(playingQueue.Dequeue); spotMock.Setup(s => s.GetCurrentlyPlaying(It.IsAny<PlayerCurrentlyPlayingRequest>()).Result).Returns(playingQueue.Dequeue);
var watcher = new PlayerWatcher(spotMock.Object, scheduleMock.Object, eq); var watcher = new PlayerWatcher(spotMock.Object, eq);
using var monitoredWatcher = watcher.Monitor(); using var monitoredWatcher = watcher.Monitor();
for(var i = 0; i < playing.Count; i++) for(var i = 0; i < playing.Count; i++)
@ -137,5 +165,16 @@ namespace Selector.Tests
monitoredWatcher.Should().NotRaise(notRraise); monitoredWatcher.Should().NotRaise(notRraise);
} }
} }
// [Fact]
// public async void Auth()
// {
// var spot = new SpotifyClient("");
// var eq = new UriEquality();
// var watch = new PlayerWatcher(spot.Player, eq);
// var token = new CancellationTokenSource();
// await watch.Watch(token.Token);
// }
} }
} }

View File

@ -7,11 +7,13 @@ namespace Selector {
public bool Track(FullTrack track1, FullTrack track2, bool includingAlbum); public bool Track(FullTrack track1, FullTrack track2, bool includingAlbum);
public bool Episode(FullEpisode ep1, FullEpisode ep2); public bool Episode(FullEpisode ep1, FullEpisode ep2);
public bool Album(FullAlbum album1, FullAlbum album2); public bool Album(FullAlbum album1, FullAlbum album2);
public bool Show(FullShow show1, FullShow show2);
public bool Artist(FullArtist artist1, FullArtist artist2); public bool Artist(FullArtist artist1, FullArtist artist2);
public bool Track(SimpleTrack track1, SimpleTrack track2); public bool Track(SimpleTrack track1, SimpleTrack track2);
public bool Episode(SimpleEpisode ep1, SimpleEpisode ep2); public bool Episode(SimpleEpisode ep1, SimpleEpisode ep2);
public bool Album(SimpleAlbum album1, SimpleAlbum album2); public bool Album(SimpleAlbum album1, SimpleAlbum album2);
public bool Show(SimpleShow show1, SimpleShow show2);
public bool Artist(SimpleArtist artist1, SimpleArtist artist2); public bool Artist(SimpleArtist artist1, SimpleArtist artist2);
public bool Context(Context context1, Context context2); public bool Context(Context context1, Context context2);

View File

@ -18,7 +18,8 @@ namespace Selector {
new public bool Episode(FullEpisode ep1, FullEpisode ep2) new public bool Episode(FullEpisode ep1, FullEpisode ep2)
{ {
return ep1.Uri == ep2.Uri; return ep1.Uri == ep2.Uri
&& Show(ep1.Show, ep2.Show);
} }
new public bool Album(FullAlbum album1, FullAlbum album2) new public bool Album(FullAlbum album1, FullAlbum album2)
@ -27,6 +28,12 @@ namespace Selector {
&& Enumerable.SequenceEqual(album1.Artists.Select(a => a.Name), album2.Artists.Select(a => a.Name)); && Enumerable.SequenceEqual(album1.Artists.Select(a => a.Name), album2.Artists.Select(a => a.Name));
} }
new public bool Show(FullShow show1, FullShow show2)
{
return show1.Name == show2.Name
&& show1.Publisher == show2.Publisher;
}
new public bool Artist(FullArtist artist1, FullArtist artist2) new public bool Artist(FullArtist artist1, FullArtist artist2)
{ {
return artist1.Name == artist2.Name; return artist1.Name == artist2.Name;
@ -49,6 +56,12 @@ namespace Selector {
&& Enumerable.SequenceEqual(album1.Artists.Select(a => a.Name), album2.Artists.Select(a => a.Name)); && Enumerable.SequenceEqual(album1.Artists.Select(a => a.Name), album2.Artists.Select(a => a.Name));
} }
new public bool Show(SimpleShow show1, SimpleShow show2)
{
return show1.Name == show2.Name
&& show1.Publisher == show2.Publisher;
}
new public bool Artist(SimpleArtist artist1, SimpleArtist artist2) new public bool Artist(SimpleArtist artist1, SimpleArtist artist2)
{ {
return artist1.Name == artist2.Name; return artist1.Name == artist2.Name;

View File

@ -24,6 +24,11 @@ namespace Selector {
return album1.Uri == album2.Uri return album1.Uri == album2.Uri
&& Enumerable.SequenceEqual(album1.Artists.Select(a => a.Uri), album2.Artists.Select(a => a.Uri)); && Enumerable.SequenceEqual(album1.Artists.Select(a => a.Uri), album2.Artists.Select(a => a.Uri));
} }
public bool Show(FullShow show1, FullShow show2)
{
return show1.Uri == show2.Uri;
}
public bool Artist(FullArtist artist1, FullArtist artist2) public bool Artist(FullArtist artist1, FullArtist artist2)
{ {
return artist1.Uri == artist2.Uri; return artist1.Uri == artist2.Uri;
@ -45,6 +50,12 @@ namespace Selector {
return album1.Uri == album2.Uri return album1.Uri == album2.Uri
&& Enumerable.SequenceEqual(album1.Artists.Select(a => a.Uri), album2.Artists.Select(a => a.Uri)); && Enumerable.SequenceEqual(album1.Artists.Select(a => a.Uri), album2.Artists.Select(a => a.Uri));
} }
public bool Show(SimpleShow show1, SimpleShow show2)
{
return show1.Uri == show2.Uri;
}
public bool Artist(SimpleArtist artist1, SimpleArtist artist2) public bool Artist(SimpleArtist artist1, SimpleArtist artist2)
{ {
return artist1.Uri == artist2.Uri; return artist1.Uri == artist2.Uri;
@ -54,6 +65,7 @@ namespace Selector {
{ {
return context1.Uri == context2.Uri; return context1.Uri == context2.Uri;
} }
public bool Device(Device device1, Device device2) public bool Device(Device device1, Device device2)
{ {
return device1.Id == device2.Id; return device1.Id == device2.Id;

View File

@ -6,10 +6,11 @@ namespace Selector
{ {
public interface IPlayerWatcher: IWatcher public interface IPlayerWatcher: IWatcher
{ {
public event EventHandler<ListeningChangeEventArgs> TrackChange; public event EventHandler<ListeningChangeEventArgs> ItemChange;
public event EventHandler<ListeningChangeEventArgs> AlbumChange; public event EventHandler<ListeningChangeEventArgs> AlbumChange;
public event EventHandler<ListeningChangeEventArgs> ArtistChange; public event EventHandler<ListeningChangeEventArgs> ArtistChange;
public event EventHandler<ListeningChangeEventArgs> ContextChange; public event EventHandler<ListeningChangeEventArgs> ContextChange;
public event EventHandler<ListeningChangeEventArgs> ContentChange;
// public event EventHandler<ListeningChangeEventArgs> VolumeChange; // public event EventHandler<ListeningChangeEventArgs> VolumeChange;
// public event EventHandler<ListeningChangeEventArgs> DeviceChange; // public event EventHandler<ListeningChangeEventArgs> DeviceChange;

View File

@ -9,13 +9,13 @@ namespace Selector
public class PlayerWatcher: IPlayerWatcher public class PlayerWatcher: IPlayerWatcher
{ {
private readonly IPlayerClient spotifyClient; private readonly IPlayerClient spotifyClient;
private IScheduler sleepScheduler;
private IEqualityChecker equalityChecker; private IEqualityChecker equalityChecker;
public event EventHandler<ListeningChangeEventArgs> TrackChange; public event EventHandler<ListeningChangeEventArgs> ItemChange;
public event EventHandler<ListeningChangeEventArgs> AlbumChange; public event EventHandler<ListeningChangeEventArgs> AlbumChange;
public event EventHandler<ListeningChangeEventArgs> ArtistChange; public event EventHandler<ListeningChangeEventArgs> ArtistChange;
public event EventHandler<ListeningChangeEventArgs> ContextChange; public event EventHandler<ListeningChangeEventArgs> ContextChange;
public event EventHandler<ListeningChangeEventArgs> ContentChange;
// public event EventHandler<ListeningChangeEventArgs> VolumeChange; // public event EventHandler<ListeningChangeEventArgs> VolumeChange;
// public event EventHandler<ListeningChangeEventArgs> DeviceChange; // public event EventHandler<ListeningChangeEventArgs> DeviceChange;
@ -24,80 +24,157 @@ namespace Selector
private CurrentlyPlaying live { get; set; } private CurrentlyPlaying live { get; set; }
private List<List<CurrentlyPlaying>> lastPlays { get; set; } private List<List<CurrentlyPlaying>> lastPlays { get; set; }
public PlayerWatcher(IPlayerClient spotifyClient, IScheduler sleepScheduler, IEqualityChecker equalityChecker) { private int _pollPeriod;
public int PollPeriod {
get => _pollPeriod;
set => _pollPeriod = Math.Max(0, value);
}
public PlayerWatcher(IPlayerClient spotifyClient,
IEqualityChecker equalityChecker,
int pollPeriod = 3000) {
this.spotifyClient = spotifyClient; this.spotifyClient = spotifyClient;
this.sleepScheduler = sleepScheduler;
this.equalityChecker = equalityChecker; this.equalityChecker = equalityChecker;
this.PollPeriod = pollPeriod;
lastPlays = new List<List<CurrentlyPlaying>>(); lastPlays = new List<List<CurrentlyPlaying>>();
} }
public async Task WatchOne() public async Task WatchOne()
{ {
var polledCurrent = await spotifyClient.GetCurrentlyPlaying(new PlayerCurrentlyPlayingRequest());
StoreCurrentPlaying(polledCurrent);
CurrentlyPlaying existing;
if(live is null) {
live = polledCurrent;
existing = polledCurrent;
}
else {
existing = live;
live = polledCurrent;
}
try{ try{
var existingItem = (FullTrack) existing.Item; var polledCurrent = await spotifyClient.GetCurrentlyPlaying(new PlayerCurrentlyPlayingRequest());
var currentItem = (FullTrack) live.Item;
if(!equalityChecker.Track(existingItem, currentItem, true)) { if (polledCurrent != null) StoreCurrentPlaying(polledCurrent);
OnTrackChange(new ListeningChangeEventArgs(){
Previous = existing, CurrentlyPlaying previous;
Current = live if(live is null) {
}); live = polledCurrent;
previous = polledCurrent;
}
else {
previous = live;
live = polledCurrent;
} }
if(!equalityChecker.Album(existingItem.Album, currentItem.Album)) { // NOT PLAYING
OnAlbumChange(new ListeningChangeEventArgs(){ if(previous is null && live is null)
Previous = existing, {
Current = live // Console.WriteLine("not playing");
});
} }
else
{
// STARTED PLAYBACK
if(previous is null && (live.Item is FullTrack || live.Item is FullEpisode))
{
// Console.WriteLine("started playing");
if(!equalityChecker.Artist(existingItem.Artists[0], currentItem.Artists[0])) { }
OnArtistChange(new ListeningChangeEventArgs(){ // STOPPED PLAYBACK
Previous = existing, else if((previous.Item is FullTrack || previous.Item is FullEpisode) && live is null)
Current = live {
}); // Console.WriteLine("stopped playing");
}
if(!equalityChecker.Context(existing.Context, live.Context)) { }
OnContextChange(new ListeningChangeEventArgs(){ else {
Previous = existing,
Current = live
});
}
if(existing.IsPlaying != live.IsPlaying) { // MUSIC
OnPlayingChange(new ListeningChangeEventArgs(){ if(previous.Item is FullTrack && live.Item is FullTrack)
Previous = existing, {
Current = live var previousItem = (FullTrack) previous.Item;
}); var currentItem = (FullTrack) live.Item;
if(!equalityChecker.Track(previousItem, currentItem, true)) {
OnItemChange(new ListeningChangeEventArgs(){
Previous = previous,
Current = live
});
}
if(!equalityChecker.Album(previousItem.Album, currentItem.Album)) {
OnAlbumChange(new ListeningChangeEventArgs(){
Previous = previous,
Current = live
});
}
if(!equalityChecker.Artist(previousItem.Artists[0], currentItem.Artists[0])) {
OnArtistChange(new ListeningChangeEventArgs(){
Previous = previous,
Current = live
});
}
}
// CHANGED CONTENT
else if(previous.Item is FullTrack && live.Item is FullEpisode
|| previous.Item is FullEpisode && live.Item is FullTrack)
{
OnContentChange(new ListeningChangeEventArgs(){
Previous = previous,
Current = live
});
OnItemChange(new ListeningChangeEventArgs(){
Previous = previous,
Current = live
});
}
// PODCASTS
else if(previous.Item is FullEpisode && live.Item is FullEpisode)
{
var previousItem = (FullEpisode) previous.Item;
var currentItem = (FullEpisode) live.Item;
if(!equalityChecker.Episode(previousItem, currentItem)) {
OnItemChange(new ListeningChangeEventArgs(){
Previous = previous,
Current = live
});
}
}
else {
throw new NotSupportedException("Unknown item combination");
}
// CONTEXT
if(!equalityChecker.Context(previous.Context, live.Context)) {
OnContextChange(new ListeningChangeEventArgs(){
Previous = previous,
Current = live
});
}
// IS PLAYING
if(previous.IsPlaying != live.IsPlaying) {
OnPlayingChange(new ListeningChangeEventArgs(){
Previous = previous,
Current = live
});
}
}
} }
} }
catch(InvalidCastException) catch(APIUnauthorizedException e)
{ {
var existingItem = (FullEpisode) existing.Item; throw e;
}
throw new NotImplementedException("Podcasts not implemented"); catch(APITooManyRequestsException e)
{
throw e;
}
catch(APIException e)
{
throw e;
} }
} }
public Task Watch(CancellationToken cancelToken) public async Task Watch(CancellationToken cancelToken)
{ {
return Task.CompletedTask; while (!cancelToken.IsCancellationRequested)
{
await WatchOne();
await Task.Delay(PollPeriod);
}
} }
public CurrentlyPlaying NowPlaying() public CurrentlyPlaying NowPlaying()
@ -156,9 +233,9 @@ namespace Selector
} }
} }
protected virtual void OnTrackChange(ListeningChangeEventArgs args) protected virtual void OnItemChange(ListeningChangeEventArgs args)
{ {
TrackChange?.Invoke(this, args); ItemChange?.Invoke(this, args);
} }
protected virtual void OnAlbumChange(ListeningChangeEventArgs args) protected virtual void OnAlbumChange(ListeningChangeEventArgs args)
@ -176,6 +253,10 @@ namespace Selector
ContextChange?.Invoke(this, args); ContextChange?.Invoke(this, args);
} }
protected virtual void OnContentChange(ListeningChangeEventArgs args)
{
ContentChange?.Invoke(this, args);
}
// protected virtual void OnVolumeChange(ListeningChangeEventArgs args) // protected virtual void OnVolumeChange(ListeningChangeEventArgs args)
// { // {