@@ -2098,7 +2098,13 @@ class MusicService :
20982098 return error.errorCode == PlaybackException .ERROR_CODE_AUDIO_TRACK_WRITE_FAILED ||
20992099 error.errorCode == PlaybackException .ERROR_CODE_AUDIO_TRACK_INIT_FAILED ||
21002100 (error.cause as ? PlaybackException )?.errorCode == PlaybackException .ERROR_CODE_AUDIO_TRACK_WRITE_FAILED ||
2101- (error.cause as ? PlaybackException )?.errorCode == PlaybackException .ERROR_CODE_AUDIO_TRACK_INIT_FAILED
2101+ (error.cause as ? PlaybackException )?.errorCode == PlaybackException .ERROR_CODE_AUDIO_TRACK_INIT_FAILED ||
2102+ error.errorCode == PlaybackException .ERROR_CODE_FAILED_RUNTIME_CHECK
2103+ }
2104+
2105+ private fun isCacheOrStreamCorruptionError (error : PlaybackException ): Boolean {
2106+ return error.errorCode == PlaybackException .ERROR_CODE_PARSING_CONTAINER_MALFORMED ||
2107+ error.errorCode == PlaybackException .ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE
21022108 }
21032109
21042110 override fun onPlayerError (error : PlaybackException ) {
@@ -2139,6 +2145,11 @@ class MusicService :
21392145 handleRangeNotSatisfiableError(mediaId)
21402146 return
21412147 }
2148+ isCacheOrStreamCorruptionError(error) -> {
2149+ Timber .tag(TAG ).d(" Cache or stream corruption detected, clearing cache and refreshing URL" )
2150+ handleExpiredUrlError(mediaId)
2151+ return
2152+ }
21422153 isPageReloadError(error) -> {
21432154 Timber .tag(TAG ).d(" Page reload error detected, performing strict recovery" )
21442155 handlePageReloadError(mediaId)
@@ -2522,16 +2533,33 @@ class MusicService :
25222533 val cachedLength = androidx.media3.datasource.cache.ContentMetadata .getContentLength(downloadCache.getContentMetadata(mediaId))
25232534 val isFullyDownloaded = cachedLength != androidx.media3.common.C .LENGTH_UNSET .toLong() && cachedLength > 0 && downloadCache.isCached(mediaId, 0 , cachedLength)
25242535
2525- if (! shouldBypassCache && ! isFullyDownloaded && audioQuality == iad1tya.echo.music.constants.AudioQuality .LOSSLESS ) {
2526- val format = runBlocking(Dispatchers .IO ) { database.format(mediaId).firstOrNull() }
2527- if (format?.codecs != " flac" ) {
2528- shouldBypassCache = true
2536+ val isCurrentlyPlaying = runBlocking(Dispatchers .Main ) { player.currentMediaItem?.mediaId == mediaId }
2537+ val dbFormat = runBlocking(Dispatchers .IO ) { database.format(mediaId).firstOrNull() }
2538+
2539+ val lockedQuality = if (isCurrentlyPlaying && dbFormat != null ) {
2540+ when {
2541+ dbFormat.mimeType.contains(" flac" , ignoreCase = true ) -> iad1tya.echo.music.constants.AudioQuality .LOSSLESS
2542+ dbFormat.mimeType.contains(" mp4" , ignoreCase = true ) || dbFormat.mimeType.contains(" m4a" , ignoreCase = true ) -> iad1tya.echo.music.constants.AudioQuality .SAAVN
2543+ else -> iad1tya.echo.music.constants.AudioQuality .OPUS
25292544 }
2545+ } else {
2546+ audioQuality
25302547 }
2531- if (! shouldBypassCache && ! isFullyDownloaded && audioQuality == iad1tya.echo.music.constants.AudioQuality .SAAVN ) {
2532- val format = runBlocking(Dispatchers .IO ) { database.format(mediaId).firstOrNull() }
2533- if (format?.codecs != " mp4a.40.2" ) {
2548+
2549+ if (! shouldBypassCache && ! isFullyDownloaded && dbFormat != null ) {
2550+ val isLosslessCache = dbFormat.codecs == " flac"
2551+ val isSaavnCache = dbFormat.codecs == " mp4a.40.2" || dbFormat.mimeType.contains(" mp4" , ignoreCase = true )
2552+
2553+ val cacheMatchesTarget = when (lockedQuality) {
2554+ iad1tya.echo.music.constants.AudioQuality .LOSSLESS -> isLosslessCache
2555+ iad1tya.echo.music.constants.AudioQuality .SAAVN -> isSaavnCache
2556+ iad1tya.echo.music.constants.AudioQuality .OPUS -> ! isLosslessCache && ! isSaavnCache
2557+ }
2558+
2559+ if (! cacheMatchesTarget) {
25342560 shouldBypassCache = true
2561+ Timber .tag(TAG ).i(" Quality changed to $lockedQuality for $mediaId . Clearing playerCache to prevent container mismatch." )
2562+ playerCache.removeResource(mediaId)
25352563 }
25362564 }
25372565
@@ -2575,7 +2603,7 @@ class MusicService :
25752603 Timber .tag(" MusicService" ).i(" BYPASSING CACHE for $mediaId due to quality change" )
25762604 }
25772605
2578- Timber .tag(" MusicService" ).i(" FETCHING STREAM: $mediaId | quality=$audioQuality " )
2606+ Timber .tag(" MusicService" ).i(" FETCHING STREAM: $mediaId | quality=$lockedQuality " )
25792607 val playbackData = runBlocking(Dispatchers .IO ) {
25802608 val dbSong = database.song(mediaId).firstOrNull()
25812609 val knownArtist = dbSong?.artists?.joinToString { it.name }?.replace(" - Topic" , " " )
@@ -2584,7 +2612,7 @@ class MusicService :
25842612
25852613 YTPlayerUtils .playerResponseForPlayback(
25862614 mediaId,
2587- audioQuality = audioQuality ,
2615+ audioQuality = lockedQuality ,
25882616 connectivityManager = connectivityManager,
25892617 context = this @MusicService,
25902618 knownArtist = knownArtist,
@@ -2624,6 +2652,29 @@ class MusicService :
26242652 }
26252653 run {
26262654 val format = nonNullPlayback.format
2655+
2656+ val isFinalLossless = format.mimeType.contains(" flac" , ignoreCase = true )
2657+ val isFinalSaavn = format.mimeType.contains(" mp4" , ignoreCase = true ) || format.mimeType.contains(" m4a" , ignoreCase = true )
2658+
2659+ if (dbFormat != null && ! shouldBypassCache) {
2660+ val cacheIsLossless = dbFormat.codecs == " flac"
2661+ val cacheIsSaavn = dbFormat.codecs == " mp4a.40.2" || dbFormat.mimeType.contains(" mp4" , ignoreCase = true )
2662+
2663+ if (isFinalLossless != cacheIsLossless || isFinalSaavn != cacheIsSaavn) {
2664+ Timber .tag(TAG ).w(" Format fallback detected AFTER fetch. Clearing playerCache to prevent mismatch crash." )
2665+ playerCache.removeResource(mediaId)
2666+
2667+ if (isCurrentlyPlaying) {
2668+ Timber .tag(TAG ).e(" Format changed mid-stream for $mediaId . Throwing to force player restart." )
2669+ throw PlaybackException (
2670+ " Container format changed mid-stream due to fallback" ,
2671+ null ,
2672+ PlaybackException .ERROR_CODE_PARSING_CONTAINER_MALFORMED
2673+ )
2674+ }
2675+ }
2676+ }
2677+
26272678 val loudnessDb = nonNullPlayback.audioConfig?.loudnessDb
26282679 val perceptualLoudnessDb = nonNullPlayback.audioConfig?.perceptualLoudnessDb
26292680
0 commit comments