diff --git a/Trdo/Services/RadioPlayerService.cs b/Trdo/Services/RadioPlayerService.cs
index ca8ab21..b582a48 100644
--- a/Trdo/Services/RadioPlayerService.cs
+++ b/Trdo/Services/RadioPlayerService.cs
@@ -170,8 +170,20 @@ private RadioPlayerService()
Debug.WriteLine("[RadioPlayerService] External state change detected (likely hardware button)");
if (currentState == MediaPlaybackState.Playing)
{
- _watchdog.NotifyUserIntentionToPlay();
- Debug.WriteLine("[RadioPlayerService] Notified watchdog of user intention to play (hardware button)");
+ // For live radio streams, when resuming via hardware buttons we need to
+ // seek to the live position instead of resuming from the buffered position.
+ Debug.WriteLine("[RadioPlayerService] External play detected - will seek to live position");
+ TryEnqueueOnUi(() =>
+ {
+ try
+ {
+ SeekToLiveOnResume();
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"[RadioPlayerService] Failed to seek to live: {ex.Message}");
+ }
+ });
}
else if (currentState == MediaPlaybackState.Paused)
{
@@ -328,7 +340,7 @@ public void Play()
try
{
- // Ensure we have a fresh media source
+ // Ensure we have a media source
if (_player.Source == null)
{
Debug.WriteLine("[RadioPlayerService] Player.Source is null, creating new MediaSource");
@@ -347,6 +359,9 @@ public void Play()
_isInternalStateChange = false;
Debug.WriteLine("[RadioPlayerService] _player.Play() called successfully");
+ // For live streams, seek to the live position to avoid playing from buffered content
+ SeekToLive();
+
_watchdog.NotifyUserIntentionToPlay();
Debug.WriteLine("[RadioPlayerService] Notified watchdog of user intention to play");
}
@@ -368,6 +383,8 @@ public void Play()
_isInternalStateChange = false;
Debug.WriteLine("[RadioPlayerService] _player.Play() called successfully (retry)");
+ SeekToLive();
+
_watchdog.NotifyUserIntentionToPlay();
Debug.WriteLine("[RadioPlayerService] Notified watchdog of user intention to play");
}
@@ -382,6 +399,61 @@ public void Play()
Debug.WriteLine($"=== Play END ===");
}
+ ///
+ /// Seeks the player to the live position.
+ /// For live streams, this moves the playback position to the end of the seekable range,
+ /// ensuring we're streaming live content rather than buffered content from an earlier time.
+ ///
+ private void SeekToLive()
+ {
+ try
+ {
+ MediaPlaybackSession session = _player.PlaybackSession;
+
+ // Check if there's a seekable range (for live streams, this represents the buffer)
+ if (session.CanSeek)
+ {
+ // Seek to the end of the buffer (live edge)
+ TimeSpan livePosition = session.NaturalDuration;
+ Debug.WriteLine($"[RadioPlayerService] Seeking to live position: {livePosition}");
+ session.Position = livePosition;
+ }
+ else
+ {
+ Debug.WriteLine("[RadioPlayerService] Stream is not seekable or has no duration, skipping seek to live");
+ }
+ }
+ catch (Exception ex)
+ {
+ // Seeking to live is a best-effort operation - don't fail playback if it doesn't work
+ Debug.WriteLine($"[RadioPlayerService] Failed to seek to live: {ex.Message}");
+ }
+ }
+
+ ///
+ /// Seeks the player to the live position when resuming from external controls.
+ /// Used when external controls (like hardware buttons) resume playback,
+ /// to ensure we're streaming live content instead of resuming from a buffered position.
+ ///
+ private void SeekToLiveOnResume()
+ {
+ Debug.WriteLine("=== SeekToLiveOnResume START ===");
+
+ if (string.IsNullOrWhiteSpace(_streamUrl))
+ {
+ Debug.WriteLine("[RadioPlayerService] No stream URL set, skipping seek to live");
+ Debug.WriteLine("=== SeekToLiveOnResume END ===");
+ return;
+ }
+
+ // Just seek to the live position - no need to recreate the MediaSource
+ SeekToLive();
+ _watchdog.NotifyUserIntentionToPlay();
+ Debug.WriteLine("[RadioPlayerService] Notified watchdog of user intention to play");
+
+ Debug.WriteLine("=== SeekToLiveOnResume END ===");
+ }
+
///
/// Stop playback and clean up resources
///