Skip to content

Commit 5aa12c8

Browse files
committed
Refactor code to reactive approach
1 parent c4e7c23 commit 5aa12c8

File tree

2 files changed

+94
-77
lines changed

2 files changed

+94
-77
lines changed

Codehard.DJ/Codehard.DJ.csproj

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@
99
</PropertyGroup>
1010

1111
<ItemGroup>
12-
<PackageReference Include="Codehard.Functional" Version="2.4.0" />
13-
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
14-
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
15-
<PackageReference Include="SpotifyAPI.Web.Auth" Version="7.0.0" />
16-
<PackageReference Include="System.Runtime.Caching" Version="7.0.0" />
17-
<PackageReference Include="OpenAI-DotNet" Version="5.1.0" />
12+
<PackageReference Include="Codehard.Functional" Version="2.4.0"/>
13+
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0"/>
14+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0"/>
15+
<PackageReference Include="SpotifyAPI.Web.Auth" Version="7.0.0"/>
16+
<PackageReference Include="System.Runtime.Caching" Version="7.0.0"/>
17+
<PackageReference Include="OpenAI-DotNet" Version="5.1.0"/>
18+
<PackageReference Include="System.Reactive.Linq" Version="5.0.0"/>
1819
</ItemGroup>
1920

2021
<ItemGroup>
@@ -24,9 +25,9 @@
2425
</ItemGroup>
2526

2627
<ItemGroup>
27-
<ProjectReference Include="..\DJ.Domain\DJ.Domain.csproj" />
28-
<ProjectReference Include="..\DJ.Infrastructure\DJ.Infrastructure.csproj" />
29-
<ProjectReference Include="..\Infrastructure.Discord\Infrastructure.Discord.csproj" />
28+
<ProjectReference Include="..\DJ.Domain\DJ.Domain.csproj"/>
29+
<ProjectReference Include="..\DJ.Infrastructure\DJ.Infrastructure.csproj"/>
30+
<ProjectReference Include="..\Infrastructure.Discord\Infrastructure.Discord.csproj"/>
3031
</ItemGroup>
3132

3233
</Project>

Codehard.DJ/Providers/Spotify/SpotifyProvider.cs

Lines changed: 84 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Codehard.DJ.Extensions;
1+
using System.Reactive.Linq;
2+
using Codehard.DJ.Extensions;
23
using Codehard.DJ.Providers.Models;
34
using Codehard.Functional;
45
using Microsoft.Extensions.Logging;
@@ -10,13 +11,13 @@ public class SpotifyProvider : IMusicProvider
1011
{
1112
private readonly SpotifyClient _client;
1213
private readonly ILogger<SpotifyProvider> _logger;
13-
private readonly Timer _timer;
1414
private readonly Queue<Music> _queue;
1515
private readonly Stack<Music> _playedStack;
1616

1717
private Music? _currentMusic;
1818
private bool _disposed;
1919
private int _volume = -1;
20+
private readonly Action _disposer;
2021

2122
public SpotifyProvider(
2223
SpotifyClient client,
@@ -30,7 +31,10 @@ public SpotifyProvider(
3031
// so we doing the queue in memory
3132
this._queue = new Queue<Music>();
3233
this._playedStack = new Stack<Music>();
33-
this._timer = new Timer(GetPlayingTrackInfoAsync, default, TimeSpan.Zero, TimeSpan.FromSeconds(3));
34+
35+
var disposeSubscriptionAction = GetPlayingTrackInfo();
36+
37+
this._disposer = disposeSubscriptionAction;
3438
}
3539

3640
public event PlayStartEventHandler? PlayStartEvent;
@@ -265,90 +269,101 @@ public async ValueTask<bool> SetVolumeAsync(int volume, CancellationToken cancel
265269
}
266270
}
267271

268-
private async void GetPlayingTrackInfoAsync(object? state)
272+
private Action GetPlayingTrackInfo()
269273
{
270-
try
274+
var playbackStateObserver =
275+
Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(3))
276+
.SelectMany(_ => Observable.FromAsync(IsCurrentPlaybackEndedAsync))
277+
.DistinctUntilChanged();
278+
279+
var stateChangedSubscription =
280+
playbackStateObserver
281+
.Where(t => t.State != this.State)
282+
.Subscribe(t => this.PlayerStateChangedEvent?.Invoke(this, t.State));
283+
284+
var playingStateSubscription =
285+
playbackStateObserver
286+
.Where(t => t.State == PlaybackState.Playing)
287+
.Subscribe(PlayingSubscribeHandler);
288+
289+
var playStoppedOrEndedSubscription =
290+
playbackStateObserver
291+
.Where(t => t.State is PlaybackState.Ended or PlaybackState.Stopped)
292+
.Subscribe(PlayerStoppedOrEndedSubscribeHandler);
293+
294+
return () =>
271295
{
272-
var (track, playbackState) = await IsCurrentPlaybackEndedAsync();
296+
stateChangedSubscription.Dispose();
297+
playingStateSubscription.Dispose();
298+
playStoppedOrEndedSubscription.Dispose();
299+
};
273300

274-
if (this.State != playbackState)
275-
{
276-
this.PlayerStateChangedEvent?.Invoke(this, playbackState);
277-
}
301+
async void PlayingSubscribeHandler((FullTrack? TrackAudio, PlaybackState State) t)
302+
{
303+
var track = t.TrackAudio;
278304

279-
this.State = playbackState;
305+
var isOutOfSync = track!.Id != this._currentMusic?.Id;
280306

281-
switch (playbackState)
307+
if (!isOutOfSync)
282308
{
283-
case PlaybackState.Playing:
284-
var isOutOfSync = track!.Id != this._currentMusic?.Id;
285-
286-
if (!isOutOfSync)
287-
{
288-
break;
289-
}
290-
291-
TryInvokePlaybackEnded();
292-
293-
this.PlaybackOutOfSyncEvent?.Invoke(this, new MusicPlayerEventArgs
294-
{
295-
Music = new Music(
296-
track.Id,
297-
track.Name,
298-
track.Artists.Select(a => new Artist(a.Id, a.Name, System.Array.Empty<string>())).ToArray(),
299-
new Album(track.Album.Name, track.Album.Images.Select(i => i.Url).ToArray(), string.Empty),
300-
track.DurationMs,
301-
new Uri(track.Uri)),
302-
});
303-
304-
if (this.RemainingInQueue > 0)
305-
{
306-
await this.StopAsync();
307-
}
309+
return;
310+
}
308311

309-
break;
310-
case PlaybackState.Stopped:
311-
case PlaybackState.Ended:
312-
TryInvokePlaybackEnded();
312+
TryInvokePlaybackEnded();
313313

314-
if (this._queue.Any())
315-
{
316-
await this.NextAsync();
317-
}
314+
this.PlaybackOutOfSyncEvent?.Invoke(this,
315+
new MusicPlayerEventArgs
316+
{
317+
Music = new Music(track.Id, track.Name, track.Artists.Select(a => new Artist(a.Id, a.Name, System.Array.Empty<string>())).ToArray(),
318+
new Album(track.Album.Name, track.Album.Images.Select(i => i.Url).ToArray(), string.Empty), track.DurationMs, new Uri(track.Uri)),
319+
});
318320

319-
break;
320-
default:
321-
throw new NotSupportedException();
321+
if (this.RemainingInQueue > 0)
322+
{
323+
await this.StopAsync();
322324
}
323325
}
324-
catch (Exception ex)
325-
{
326-
this._logger.LogError(ex, "An error occurred during track info gathering");
327-
}
328326

329-
async Task<(FullTrack? TrackAudio, PlaybackState State)> IsCurrentPlaybackEndedAsync()
327+
async void PlayerStoppedOrEndedSubscribeHandler((FullTrack? TrackAudio, PlaybackState State) t)
330328
{
331-
var playingContext = await this._client.Player.GetCurrentPlayback();
329+
TryInvokePlaybackEnded();
332330

333-
if (playingContext == null!)
331+
if (this._queue.Any())
334332
{
335-
return (default, PlaybackState.Stopped);
333+
await this.NextAsync();
336334
}
335+
}
337336

338-
if (playingContext.Item is not FullTrack currentTrack)
337+
async Task<(FullTrack? TrackAudio, PlaybackState State)> IsCurrentPlaybackEndedAsync()
338+
{
339+
try
339340
{
340-
return (default, PlaybackState.Stopped);
341-
}
341+
var playingContext = await this._client.Player.GetCurrentPlayback();
342342

343-
var trackEnded = playingContext.ProgressMs >= currentTrack.DurationMs - 1;
344-
var trackStopped = playingContext is { IsPlaying: false, ProgressMs: 0 };
343+
if (playingContext == null!)
344+
{
345+
return (default, PlaybackState.Stopped);
346+
}
347+
348+
if (playingContext.Item is not FullTrack currentTrack)
349+
{
350+
return (default, PlaybackState.Stopped);
351+
}
352+
353+
var trackEnded = playingContext.ProgressMs >= currentTrack.DurationMs - 1;
354+
var trackStopped = playingContext is { IsPlaying: false, ProgressMs: 0 };
345355

346-
var playbackState =
347-
trackEnded ? PlaybackState.Ended :
348-
trackStopped ? PlaybackState.Stopped :
349-
PlaybackState.Playing;
356+
var playbackState =
357+
trackEnded ? PlaybackState.Ended :
358+
trackStopped ? PlaybackState.Stopped :
359+
PlaybackState.Playing;
350360

351-
return (currentTrack, playbackState);
361+
return (currentTrack, playbackState);
362+
}
363+
catch
364+
{
365+
return (null, PlaybackState.Unknown);
366+
}
352367
}
353368

354369
void TryInvokePlaybackEnded()
@@ -374,13 +389,14 @@ protected void Dispose(bool disposing)
374389
return;
375390
}
376391

377-
this._timer.Dispose();
392+
this._disposer();
378393

379394
this._disposed = true;
380395
}
381396

382397
public void Dispose()
383398
{
384-
_timer.Dispose();
399+
this.Dispose(true);
400+
GC.SuppressFinalize(this);
385401
}
386402
}

0 commit comments

Comments
 (0)