Player Client and Visual Enhancements
- Redesigned audio player bar to be mobile-friendly - Added unloading for track switching (needs to be fixed) - Added IsLoading status so loading spinner isn't hanging around when it shouldn't be - Normalized styles with scoped files (will further reduce) - Layout Cleanup - EF fixes (migrations now function for deployment) - deploy script updates (new dedicated host)
This commit is contained in:
@@ -7,6 +7,7 @@ namespace DeepDrftWeb.Client.Services;
|
||||
public class AudioPlaybackEngine : IAsyncDisposable
|
||||
{
|
||||
public event Events.EventAsync<double>? OnProgressChanged;
|
||||
public event Events.EventAsync<double>? OnLoadChanged;
|
||||
public event Events.EventAsync? OnPlaybackEnded;
|
||||
|
||||
public required TrackMediaClient Client { get; set; }
|
||||
@@ -15,6 +16,7 @@ public class AudioPlaybackEngine : IAsyncDisposable
|
||||
public string PlayerId { get; private set; } = Guid.NewGuid().ToString();
|
||||
public bool IsInitialized { get; private set; } = false;
|
||||
public bool IsLoaded { get; private set; } = false;
|
||||
public bool IsLoading { get; private set; } = false;
|
||||
public bool IsPlaying { get; private set; } = false;
|
||||
public bool IsPaused { get; private set; } = false;
|
||||
public double CurrentTime { get; private set; } = 0;
|
||||
@@ -51,13 +53,26 @@ public class AudioPlaybackEngine : IAsyncDisposable
|
||||
|
||||
public async Task LoadTrack(TrackEntity track)
|
||||
{
|
||||
if (IsLoaded) return;
|
||||
|
||||
TrackMediaResponse? audio = null;
|
||||
try
|
||||
{
|
||||
// Immediately reset state to indicate loading has started
|
||||
ErrorMessage = null;
|
||||
LoadProgress = 0;
|
||||
|
||||
IsLoaded = false;
|
||||
IsLoading = true;
|
||||
Duration = null;
|
||||
CurrentTime = 0;
|
||||
|
||||
// Trigger load event immediately to show loading state in UI
|
||||
if (OnLoadChanged != null) await OnLoadChanged.Invoke(0);
|
||||
|
||||
if (IsPlaying || IsPaused)
|
||||
{
|
||||
// If we were playing/paused, unload the current track
|
||||
await Unload();
|
||||
}
|
||||
|
||||
AudioOperationResult? loadResult = await AudioInterop.InitializeBufferedPlayerAsync(PlayerId);
|
||||
if (loadResult?.Success != true)
|
||||
{
|
||||
@@ -65,7 +80,7 @@ public class AudioPlaybackEngine : IAsyncDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
var mediaResult = await Client.GetTrackMedia(track.EntryKey);
|
||||
var mediaResult = await Client.GetTrackMedia(track.EntryKey);
|
||||
if (!mediaResult.Success)
|
||||
{
|
||||
ErrorMessage = mediaResult.GetMessage();
|
||||
@@ -77,9 +92,7 @@ public class AudioPlaybackEngine : IAsyncDisposable
|
||||
ErrorMessage = "No audio returned from server";
|
||||
return;
|
||||
}
|
||||
|
||||
TrackMediaResponse audio = mediaResult.Value;
|
||||
await StreamAndPlay(audio);
|
||||
audio = mediaResult.Value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -87,6 +100,21 @@ public class AudioPlaybackEngine : IAsyncDisposable
|
||||
LoadProgress = 0;
|
||||
IsLoaded = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLoading = false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (audio == null) return;
|
||||
|
||||
await StreamAndPlay(audio);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ErrorMessage = $"Error streaming audio: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StreamAndPlay(TrackMediaResponse audio)
|
||||
@@ -124,6 +152,7 @@ public class AudioPlaybackEngine : IAsyncDisposable
|
||||
if (audio.ContentLength > 0)
|
||||
{
|
||||
LoadProgress = Math.Min(1.0, (double)totalBytesRead / audio.ContentLength);
|
||||
if (OnLoadChanged != null) await OnLoadChanged.Invoke(LoadProgress);
|
||||
}
|
||||
}
|
||||
} while (currentBytes > 0);
|
||||
@@ -140,6 +169,9 @@ public class AudioPlaybackEngine : IAsyncDisposable
|
||||
LoadProgress = 1.0;
|
||||
IsLoaded = true;
|
||||
ErrorMessage = null;
|
||||
|
||||
// Trigger final load completion event
|
||||
if (OnLoadChanged != null) await OnLoadChanged.Invoke(1.0);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -217,6 +249,35 @@ public class AudioPlaybackEngine : IAsyncDisposable
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Unload()
|
||||
{
|
||||
if (!IsLoaded) return;
|
||||
|
||||
try
|
||||
{
|
||||
await Stop();
|
||||
var result = await AudioInterop.UnloadAsync(PlayerId);
|
||||
if (result.Success)
|
||||
{
|
||||
IsPlaying = false;
|
||||
IsPaused = false;
|
||||
CurrentTime = 0;
|
||||
Duration = null;
|
||||
LoadProgress = 0;
|
||||
IsLoaded = false;
|
||||
ErrorMessage = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorMessage = $"Unload error: {result.Error}";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ErrorMessage = $"Error unloading track: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
public async Task OnSeek(double position)
|
||||
{
|
||||
if (!IsLoaded) return;
|
||||
|
||||
Reference in New Issue
Block a user