add to apple timeline with stored offset check
This commit is contained in:
parent
7e8a9d1e29
commit
bfc7a0db34
Selector.AppleMusic
@ -26,15 +26,19 @@ public class AppleMusicApi(HttpClient client, string developerToken, string user
|
|||||||
{
|
{
|
||||||
if (response.StatusCode == HttpStatusCode.Unauthorized)
|
if (response.StatusCode == HttpStatusCode.Unauthorized)
|
||||||
{
|
{
|
||||||
throw new UnauthorisedException();
|
throw new UnauthorisedException { StatusCode = response.StatusCode };
|
||||||
}
|
}
|
||||||
else if (response.StatusCode == HttpStatusCode.Forbidden)
|
else if (response.StatusCode == HttpStatusCode.Forbidden)
|
||||||
{
|
{
|
||||||
throw new ForbiddenException();
|
throw new ForbiddenException { StatusCode = response.StatusCode };
|
||||||
}
|
}
|
||||||
else if (response.StatusCode == HttpStatusCode.TooManyRequests)
|
else if (response.StatusCode == HttpStatusCode.TooManyRequests)
|
||||||
{
|
{
|
||||||
throw new RateLimitException();
|
throw new RateLimitException { StatusCode = response.StatusCode };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new AppleMusicException { StatusCode = response.StatusCode };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,50 +37,20 @@ public class AppleTimeline : Timeline<AppleMusicCurrentlyPlayingContext>
|
|||||||
return newItems;
|
return newItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
var stop = false;
|
var (found, startIdx) = Loop(items, 0);
|
||||||
var found = 0;
|
|
||||||
var startIdx = 0;
|
TimelineItem<AppleMusicCurrentlyPlayingContext>? popped = null;
|
||||||
while (!stop)
|
if (found == 0)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < items.Count; i++)
|
var (foundOffseted, startIdxOffseted) = Loop(items, 1);
|
||||||
|
|
||||||
|
if (foundOffseted > found)
|
||||||
{
|
{
|
||||||
var storedIdx = (Recent.Count - 1) - i;
|
popped = Recent[^1];
|
||||||
// start from the end, minus this loops index, minus the offset
|
Recent.RemoveAt(Recent.Count - 1);
|
||||||
var pulledIdx = (items.Count - 1) - i - startIdx;
|
|
||||||
|
|
||||||
if (pulledIdx < 0)
|
startIdx = startIdxOffseted;
|
||||||
{
|
|
||||||
// ran to the end of new items and none matched the end, add all the new ones
|
|
||||||
stop = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (storedIdx < 0)
|
|
||||||
{
|
|
||||||
// all the new stuff matches, we're done and there's nothing new to add
|
|
||||||
stop = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Recent[storedIdx].Item.Track.Id == items[pulledIdx].Track.Id)
|
|
||||||
{
|
|
||||||
// good, keep going
|
|
||||||
found++;
|
|
||||||
if (found >= 3)
|
|
||||||
{
|
|
||||||
stop = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// bad, doesn't match, break and bump stored
|
|
||||||
found = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stop) startIdx += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var item in items.TakeLast(startIdx))
|
foreach (var item in items.TakeLast(startIdx))
|
||||||
@ -89,6 +59,77 @@ public class AppleTimeline : Timeline<AppleMusicCurrentlyPlayingContext>
|
|||||||
Recent.Add(TimelineItem<AppleMusicCurrentlyPlayingContext>.From(item, DateTime.UtcNow));
|
Recent.Add(TimelineItem<AppleMusicCurrentlyPlayingContext>.From(item, DateTime.UtcNow));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (popped is not null)
|
||||||
|
{
|
||||||
|
var idx = Recent.FindIndex(x => x.Item.Track.Id == popped.Item.Track.Id);
|
||||||
|
if (idx >= 0)
|
||||||
|
{
|
||||||
|
newItems.RemoveAt(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckSize();
|
||||||
|
|
||||||
return newItems;
|
return newItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private (int, int) Loop(List<AppleMusicCurrentlyPlayingContext> items, int storedOffset)
|
||||||
|
{
|
||||||
|
var stop = false;
|
||||||
|
var found = 0;
|
||||||
|
var startIdx = 0;
|
||||||
|
while (!stop)
|
||||||
|
{
|
||||||
|
found = Loop(items, storedOffset, ref startIdx, ref stop);
|
||||||
|
|
||||||
|
if (!stop) startIdx += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (found, startIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int Loop(List<AppleMusicCurrentlyPlayingContext> items, int storedOffset, ref int startIdx, ref bool stop)
|
||||||
|
{
|
||||||
|
var found = 0;
|
||||||
|
|
||||||
|
for (var i = 0; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
var storedIdx = (Recent.Count - 1) - i - storedOffset;
|
||||||
|
// start from the end, minus this loops index, minus the offset
|
||||||
|
var pulledIdx = (items.Count - 1) - i - startIdx;
|
||||||
|
|
||||||
|
if (pulledIdx < 0)
|
||||||
|
{
|
||||||
|
// ran to the end of new items and none matched the end, add all the new ones
|
||||||
|
stop = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storedIdx < 0)
|
||||||
|
{
|
||||||
|
// all the new stuff matches, we're done and there's nothing new to add
|
||||||
|
stop = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Recent[storedIdx].Item.Track.Id == items[pulledIdx].Track.Id)
|
||||||
|
{
|
||||||
|
// good, keep going
|
||||||
|
found++;
|
||||||
|
if (found >= 3)
|
||||||
|
{
|
||||||
|
stop = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// bad, doesn't match, break and bump stored
|
||||||
|
found = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,5 +1,8 @@
|
|||||||
|
using System.Net;
|
||||||
|
|
||||||
namespace Selector.AppleMusic.Exceptions;
|
namespace Selector.AppleMusic.Exceptions;
|
||||||
|
|
||||||
public class AppleMusicException : Exception
|
public class AppleMusicException : Exception
|
||||||
{
|
{
|
||||||
|
public HttpStatusCode StatusCode { get; set; }
|
||||||
}
|
}
|
@ -7,7 +7,7 @@ namespace Selector.AppleMusic.Watcher;
|
|||||||
|
|
||||||
public class AppleMusicPlayerWatcher : BaseWatcher, IAppleMusicPlayerWatcher
|
public class AppleMusicPlayerWatcher : BaseWatcher, IAppleMusicPlayerWatcher
|
||||||
{
|
{
|
||||||
new protected readonly ILogger<AppleMusicPlayerWatcher> Logger;
|
private new readonly ILogger<AppleMusicPlayerWatcher> Logger;
|
||||||
private readonly AppleMusicApi _appleMusicApi;
|
private readonly AppleMusicApi _appleMusicApi;
|
||||||
|
|
||||||
public event EventHandler<AppleListeningChangeEventArgs> NetworkPoll;
|
public event EventHandler<AppleListeningChangeEventArgs> NetworkPoll;
|
||||||
@ -15,12 +15,12 @@ public class AppleMusicPlayerWatcher : BaseWatcher, IAppleMusicPlayerWatcher
|
|||||||
public event EventHandler<AppleListeningChangeEventArgs> AlbumChange;
|
public event EventHandler<AppleListeningChangeEventArgs> AlbumChange;
|
||||||
public event EventHandler<AppleListeningChangeEventArgs> ArtistChange;
|
public event EventHandler<AppleListeningChangeEventArgs> ArtistChange;
|
||||||
|
|
||||||
public AppleMusicCurrentlyPlayingContext Live { get; protected set; }
|
public AppleMusicCurrentlyPlayingContext? Live { get; private set; }
|
||||||
protected AppleMusicCurrentlyPlayingContext Previous { get; set; }
|
private AppleMusicCurrentlyPlayingContext? Previous { get; set; }
|
||||||
public AppleTimeline Past { get; set; } = new();
|
public AppleTimeline Past { get; private set; } = new();
|
||||||
|
|
||||||
public AppleMusicPlayerWatcher(AppleMusicApi appleMusicClient,
|
public AppleMusicPlayerWatcher(AppleMusicApi appleMusicClient,
|
||||||
ILogger<AppleMusicPlayerWatcher> logger = null,
|
ILogger<AppleMusicPlayerWatcher>? logger = null,
|
||||||
int pollPeriod = 3000
|
int pollPeriod = 3000
|
||||||
) : base(logger)
|
) : base(logger)
|
||||||
{
|
{
|
||||||
@ -35,10 +35,22 @@ public class AppleMusicPlayerWatcher : BaseWatcher, IAppleMusicPlayerWatcher
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
using var polledLogScope = Logger.BeginScope(new Dictionary<string, object>() { { "user_id", Id } });
|
||||||
|
|
||||||
Logger.LogTrace("Making Apple Music call");
|
Logger.LogTrace("Making Apple Music call");
|
||||||
var polledCurrent = await _appleMusicApi.GetRecentlyPlayedTracks();
|
var polledCurrent = await _appleMusicApi.GetRecentlyPlayedTracks();
|
||||||
|
|
||||||
// using var polledLogScope = Logger.BeginScope(new Dictionary<string, object>() { { "context", polledCurrent?.DisplayString() } });
|
if (polledCurrent is null)
|
||||||
|
{
|
||||||
|
Logger.LogInformation("Null response when calling Apple Music API");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (polledCurrent.Data is null)
|
||||||
|
{
|
||||||
|
Logger.LogInformation("Null track list when calling Apple Music API");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Logger.LogTrace("Received Apple Music call");
|
Logger.LogTrace("Received Apple Music call");
|
||||||
|
|
||||||
@ -65,23 +77,28 @@ public class AppleMusicPlayerWatcher : BaseWatcher, IAppleMusicPlayerWatcher
|
|||||||
}
|
}
|
||||||
catch (RateLimitException e)
|
catch (RateLimitException e)
|
||||||
{
|
{
|
||||||
Logger.LogDebug("Rate Limit exception: [{message}]", e.Message);
|
Logger.LogError(e, "Rate Limit exception");
|
||||||
// throw e;
|
// throw;
|
||||||
}
|
}
|
||||||
catch (ForbiddenException e)
|
catch (ForbiddenException e)
|
||||||
{
|
{
|
||||||
Logger.LogDebug("Forbidden exception: [{message}]", e.Message);
|
Logger.LogError(e, "Forbidden exception");
|
||||||
throw;
|
// throw;
|
||||||
}
|
}
|
||||||
catch (ServiceException e)
|
catch (ServiceException e)
|
||||||
{
|
{
|
||||||
Logger.LogDebug("Apple Music internal error: [{message}]", e.Message);
|
Logger.LogInformation("Apple Music internal error");
|
||||||
// throw e;
|
// throw;
|
||||||
}
|
}
|
||||||
catch (UnauthorisedException e)
|
catch (UnauthorisedException e)
|
||||||
{
|
{
|
||||||
Logger.LogDebug("Unauthorised exception: [{message}]", e.Message);
|
Logger.LogError(e, "Unauthorised exception");
|
||||||
// throw e;
|
// throw;
|
||||||
|
}
|
||||||
|
catch (AppleMusicException e)
|
||||||
|
{
|
||||||
|
Logger.LogInformation("Apple Music exception ({})", e.StatusCode);
|
||||||
|
// throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +106,7 @@ public class AppleMusicPlayerWatcher : BaseWatcher, IAppleMusicPlayerWatcher
|
|||||||
{
|
{
|
||||||
var lastTrack = recentlyPlayedTracks.Data?.FirstOrDefault();
|
var lastTrack = recentlyPlayedTracks.Data?.FirstOrDefault();
|
||||||
|
|
||||||
if (Live != null && Live.Track != null && Live.Track.Id == lastTrack?.Id)
|
if (Live is { Track: not null } && Live.Track.Id == lastTrack?.Id)
|
||||||
{
|
{
|
||||||
Live = new()
|
Live = new()
|
||||||
{
|
{
|
||||||
@ -116,27 +133,27 @@ public class AppleMusicPlayerWatcher : BaseWatcher, IAppleMusicPlayerWatcher
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AppleListeningChangeEventArgs GetEvent() =>
|
private AppleListeningChangeEventArgs GetEvent() =>
|
||||||
AppleListeningChangeEventArgs.From(Previous, Live, Past, id: Id);
|
AppleListeningChangeEventArgs.From(Previous, Live, Past, id: Id);
|
||||||
|
|
||||||
#region Event Firers
|
#region Event Firers
|
||||||
|
|
||||||
protected virtual void OnNetworkPoll(AppleListeningChangeEventArgs args)
|
private void OnNetworkPoll(AppleListeningChangeEventArgs args)
|
||||||
{
|
{
|
||||||
NetworkPoll?.Invoke(this, args);
|
NetworkPoll?.Invoke(this, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnItemChange(AppleListeningChangeEventArgs args)
|
private void OnItemChange(AppleListeningChangeEventArgs args)
|
||||||
{
|
{
|
||||||
ItemChange?.Invoke(this, args);
|
ItemChange?.Invoke(this, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnAlbumChange(AppleListeningChangeEventArgs args)
|
protected void OnAlbumChange(AppleListeningChangeEventArgs args)
|
||||||
{
|
{
|
||||||
AlbumChange?.Invoke(this, args);
|
AlbumChange?.Invoke(this, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnArtistChange(AppleListeningChangeEventArgs args)
|
protected void OnArtistChange(AppleListeningChangeEventArgs args)
|
||||||
{
|
{
|
||||||
ArtistChange?.Invoke(this, args);
|
ArtistChange?.Invoke(this, args);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user