diff --git a/app/src/main/kotlin/com/subtracks/playback/PlaybackService.kt b/app/src/main/kotlin/com/subtracks/playback/PlaybackService.kt index 7052d186..0b1ec74d 100644 --- a/app/src/main/kotlin/com/subtracks/playback/PlaybackService.kt +++ b/app/src/main/kotlin/com/subtracks/playback/PlaybackService.kt @@ -103,7 +103,6 @@ class PlaybackService : MediaSessionService() { .setMediaSourceFactory( DefaultMediaSourceFactory(this) .setEnableClippingInMediaPeriod(true) - .setLoadErrorHandlingPolicy(RetryOutOfRangePolicy()) .setDataSourceFactory(mediaDataSourceFactory(this, OkHttpDataSource.Factory(streamingClient()))), ).build() exoPlayer.addListener( diff --git a/app/src/main/kotlin/com/subtracks/playback/RetryOutOfRangePolicy.kt b/app/src/main/kotlin/com/subtracks/playback/RetryOutOfRangePolicy.kt deleted file mode 100644 index 60d100ad..00000000 --- a/app/src/main/kotlin/com/subtracks/playback/RetryOutOfRangePolicy.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.subtracks.playback - -import androidx.media3.common.C -import androidx.media3.common.util.UnstableApi -import androidx.media3.datasource.DataSourceException -import androidx.media3.exoplayer.upstream.DefaultLoadErrorHandlingPolicy -import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy - -/** - * A transcoded stream is chunked with no length, so the app declares an upper-bound length, and on - * the first request the server is still producing the stream: a seek near that bound lands past the - * data and comes back as POSITION_OUT_OF_RANGE (Navidrome answers 416 until it has cached the - * transcode). That first request is what warms the cache, so one retry lands on a response with a - * real Content-Length and the stream plays and seeks properly. - */ -@UnstableApi -internal class RetryOutOfRangePolicy( - private val delayMs: Long = RETRY_DELAY_MS, -) : DefaultLoadErrorHandlingPolicy() { - override fun getRetryDelayMsFor(loadErrorInfo: LoadErrorHandlingPolicy.LoadErrorInfo): Long = - when { - loadErrorInfo.exception.isOutOfRange() -> { - if (loadErrorInfo.errorCount <= MAX_RETRIES) delayMs else C.TIME_UNSET - } - - else -> { - super.getRetryDelayMsFor(loadErrorInfo) - } - } - - private fun Throwable.isOutOfRange(): Boolean = - generateSequence(this as Throwable?) { it.cause }.any { - it is DataSourceException && it.reason == DataSourceException.POSITION_OUT_OF_RANGE - } -} - -private const val MAX_RETRIES = 1 -private const val RETRY_DELAY_MS = 1_000L diff --git a/app/src/test/kotlin/com/subtracks/playback/RetryOutOfRangePolicyTest.kt b/app/src/test/kotlin/com/subtracks/playback/RetryOutOfRangePolicyTest.kt deleted file mode 100644 index 0ecc812c..00000000 --- a/app/src/test/kotlin/com/subtracks/playback/RetryOutOfRangePolicyTest.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.subtracks.playback - -import android.net.Uri -import androidx.media3.common.C -import androidx.media3.datasource.DataSourceException -import androidx.media3.datasource.DataSpec -import androidx.media3.exoplayer.source.LoadEventInfo -import androidx.media3.exoplayer.source.MediaLoadData -import androidx.media3.exoplayer.upstream.DefaultLoadErrorHandlingPolicy -import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy -import androidx.test.ext.junit.runners.AndroidJUnit4 -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith -import java.io.IOException - -@RunWith(AndroidJUnit4::class) -class RetryOutOfRangePolicyTest { - private val policy = RetryOutOfRangePolicy(delayMs = 123L) - - @Test - fun anOutOfRangeReadIsRetriedOnce() { - assertEquals(123L, policy.getRetryDelayMsFor(info(outOfRange(), errorCount = 1))) - } - - @Test - fun anOutOfRangeReadIsNotRetriedForever() { - assertEquals(C.TIME_UNSET.toLong(), policy.getRetryDelayMsFor(info(outOfRange(), errorCount = 2))) - } - - @Test - fun aWrappedOutOfRangeIsStillSeen() { - val wrapped = IOException("load failed", outOfRange()) - - assertEquals(123L, policy.getRetryDelayMsFor(info(wrapped, errorCount = 1))) - } - - @Test - fun otherErrorsGoToTheDefaultPolicy() { - val plain = IOException("network") - val expected = DefaultLoadErrorHandlingPolicy().getRetryDelayMsFor(info(plain, errorCount = 1)) - - assertEquals(expected, policy.getRetryDelayMsFor(info(plain, errorCount = 1))) - } - - private fun outOfRange() = DataSourceException(DataSourceException.POSITION_OUT_OF_RANGE) - - private fun info( - error: IOException, - errorCount: Int, - ) = LoadErrorHandlingPolicy.LoadErrorInfo( - LoadEventInfo(0, DataSpec(Uri.EMPTY), 0), - MediaLoadData(C.DATA_TYPE_MEDIA), - error, - errorCount, - ) -}