/* * Copyright (c) Double Symmetry GmbH * Commercial use requires a license. See https://rntp.dev/pricing */ package com.doublesymmetry.trackplayer import android.net.Uri import androidx.media3.common.util.UnstableApi import androidx.media3.datasource.DataSource import androidx.media3.datasource.DataSpec import androidx.media3.datasource.TransferListener import com.doublesymmetry.trackplayer.models.MediaHeaders /** * Wraps a [DataSource.Factory] (typically `DefaultHttpDataSource.Factory`) to * inject per-URI headers stored in [MediaHeaders]. This is the standard * ExoPlayer extension point for customizing HTTP requests. */ @UnstableApi class HeaderInjectingDataSourceFactory( private val baseFactory: DataSource.Factory ) : DataSource.Factory { override fun createDataSource(): DataSource { val base = baseFactory.createDataSource() return HeaderInjectingDataSource(base) } // Forwards every DataSource member explicitly. Kotlin's `: DataSource by delegate` // only generates stubs for abstract members; Java `default` methods such as // `getResponseHeaders` resolve to the interface default (empty map) and never // reach the delegate. ProgressiveMediaPeriod consults that map for `icy-metaint` // to decide whether to install IcyDataSource, so dropping it breaks ICY // streams entirely (#2638). private class HeaderInjectingDataSource( private val delegate: DataSource ) : DataSource { override fun open(dataSpec: DataSpec): Long { val perItemHeaders = MediaHeaders.get(dataSpec.uri.toString()) if (perItemHeaders != null) { // Per-item headers take precedence so callers can override headers // ExoPlayer injects itself (notably `Icy-MetaData: 1`). val merged = dataSpec.httpRequestHeaders + perItemHeaders val newSpec = dataSpec.buildUpon() .setHttpRequestHeaders(merged) .build() return delegate.open(newSpec) } return delegate.open(dataSpec) } override fun addTransferListener(transferListener: TransferListener) { delegate.addTransferListener(transferListener) } override fun getUri(): Uri? = delegate.uri override fun getResponseHeaders(): Map> = delegate.responseHeaders override fun read(buffer: ByteArray, offset: Int, length: Int): Int = delegate.read(buffer, offset, length) override fun close() = delegate.close() } }