From 363e02f4094317b04077c5a0011b179e9e607f04 Mon Sep 17 00:00:00 2001 From: anthorob Date: Sat, 31 Oct 2026 17:09:46 -0400 Subject: [PATCH] Delay autoplay triggering until the current track nears its end. --- .../netneutrality/music/TrackScheduler.java | 18 +++++++------ .../music/TrackSchedulerTest.java | 26 +++++++++++++++---- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/main/java/me/bobert/netneutrality/music/TrackScheduler.java b/src/main/java/me/bobert/netneutrality/music/TrackScheduler.java index df415e6..6f109ad 100644 --- a/src/main/java/me/bobert/netneutrality/music/TrackScheduler.java +++ b/src/main/java/me/bobert/netneutrality/music/TrackScheduler.java @@ -37,6 +37,7 @@ public class TrackScheduler { private final StuckTrackRecoveryHandler stuckTrackRecoveryHandler; private static final long POSITION_THROTTLE_MS = 5000; private volatile long lastPositionSent = Long.MIN_VALUE; + private static final long AUTOPLAY_REMAINING_MS = 30_000; private final AutoplayService autoplayService; private volatile boolean autoplayEnabled; private final AtomicBoolean autoplayLoading = new AtomicBoolean(); @@ -91,14 +92,11 @@ public class TrackScheduler { } /** - * Enables or disables autoplay. Enabling it while the last track of the queue is already - * playing loads its recommendations immediately. + * Enables or disables autoplay. Recommendations are only loaded once the last track of the + * queue is close to its end, so users still get a chance to queue their own tracks. */ public void setAutoplayEnabled(boolean enabled) { this.autoplayEnabled = enabled; - if (enabled) { - maybeAutoplay(); - } } boolean tryBeginAutoplayLoad() { @@ -109,9 +107,13 @@ public class TrackScheduler { autoplayLoading.set(false); } - private void maybeAutoplay() { + private void maybeAutoplay(long position) { Track track = currentTrack; - if (autoplayEnabled && track != null && queueManager.getQueueSize() <= 1) { + if (!autoplayEnabled || track == null || queueManager.getQueueSize() > 1) { + return; + } + long length = track.getInfo().getLength(); + if (length > 0 && length - position <= AUTOPLAY_REMAINING_MS) { autoplayService.loadFor(this, track); } } @@ -146,7 +148,6 @@ public class TrackScheduler { currentTrack = track; stuckTrackRecoveryHandler.reset(); publish(MusicEvent.Type.TRACK_START, track, null); - maybeAutoplay(); } public void onTrackEnd(Track track, TrackEndEvent.AudioTrackEndReason endReason) { @@ -236,6 +237,7 @@ public class TrackScheduler { } void onPlayerUpdate(long timestamp, long position, boolean connected, long ping, long now) { + maybeAutoplay(position); if (manager.getEvents() == null || currentTrack == null) { return; } diff --git a/src/test/java/me/bobert/netneutrality/music/TrackSchedulerTest.java b/src/test/java/me/bobert/netneutrality/music/TrackSchedulerTest.java index de2b86d..6d79f64 100644 --- a/src/test/java/me/bobert/netneutrality/music/TrackSchedulerTest.java +++ b/src/test/java/me/bobert/netneutrality/music/TrackSchedulerTest.java @@ -147,13 +147,29 @@ public class TrackSchedulerTest { } @Test - public void onTrackStartLoadsAutoplayWhenEnabledAndLastTrack() { + public void onTrackStartDoesNotLoadAutoplayImmediately() { AutoplayService service = mock(AutoplayService.class); TrackScheduler autoplay = new TrackScheduler(musicManager, service); Track track = QueueManagerTest.mockTrack("t1"); + when(track.getInfo().getLength()).thenReturn(200_000L); autoplay.setAutoplayEnabled(true); autoplay.onTrackStart(track); + autoplay.onPlayerUpdate(1L, 1_000L, true, 20L, 1000L); + + verify(service, never()).loadFor(any(), any()); + } + + @Test + public void autoplayLoadsWhenLastTrackNearsItsEnd() { + AutoplayService service = mock(AutoplayService.class); + TrackScheduler autoplay = new TrackScheduler(musicManager, service); + Track track = QueueManagerTest.mockTrack("t1"); + when(track.getInfo().getLength()).thenReturn(200_000L); + autoplay.setAutoplayEnabled(true); + autoplay.onTrackStart(track); + + autoplay.onPlayerUpdate(1L, 175_000L, true, 20L, 1000L); verify(service).loadFor(autoplay, track); } @@ -180,20 +196,20 @@ public class TrackSchedulerTest { clearInvocations(service); autoplay.onTrackStart(t1); + autoplay.onPlayerUpdate(1L, 999_999L, true, 20L, 1000L); verify(service, never()).loadFor(any(), any()); } @Test - public void enablingAutoplayWhileLastTrackPlayingTriggersLoad() { + public void enablingAutoplayDoesNotLoadImmediately() { AutoplayService service = mock(AutoplayService.class); TrackScheduler autoplay = new TrackScheduler(musicManager, service); - Track track = QueueManagerTest.mockTrack("t1"); - autoplay.onTrackStart(track); + autoplay.onTrackStart(QueueManagerTest.mockTrack("t1")); autoplay.setAutoplayEnabled(true); - verify(service).loadFor(autoplay, track); + verify(service, never()).loadFor(any(), any()); } @Test