From bd9cca3872210547b563efc30ca7d116e6a112c1 Mon Sep 17 00:00:00 2001 From: Mattias Buelens Date: Wed, 25 Mar 2026 13:16:34 +0100 Subject: [PATCH 1/7] Add Robolectric --- connectors/uplynk/build.gradle.kts | 1 + gradle/libs.versions.toml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/connectors/uplynk/build.gradle.kts b/connectors/uplynk/build.gradle.kts index 219a3dbc..ab2248d6 100644 --- a/connectors/uplynk/build.gradle.kts +++ b/connectors/uplynk/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { testImplementation(libs.mockito.inline) testImplementation(libs.mockito.kotlin) testImplementation(libs.kotlin.test.junit) + testImplementation(libs.robolectric) androidTestImplementation(libs.androidx.test.ext.junit) androidTestImplementation(libs.androidx.test.espresso.core) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b653b85f..54b78353 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,6 +23,7 @@ mockitoKotlin = "4.1.0" androidxEspresso = "3.6.1" androidxTestCore = "1.7.0" androidxTestExt = "1.3.0" +robolectric = "4.16.1" conviva = "4.0.43" nielsen = "9.2.0.0" comscore = "6.10.0" @@ -50,6 +51,7 @@ kotlin-test-junit = { group = "org.jetbrains.kotlin", name = "kotlin-test-junit" junit = { group = "junit", name = "junit", version.ref = "junit" } mockito-inline = { group = "org.mockito", name = "mockito-inline", version.ref = "mockito" } mockito-kotlin = { group = "org.mockito.kotlin", name = "mockito-kotlin", version.ref = "mockitoKotlin" } +robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" } theoplayer = { group = "com.theoplayer.theoplayer-sdk-android", name = "core", version.ref = "theoplayer" } theoplayer-integration-dai = { group = "com.theoplayer.theoplayer-sdk-android", name = "integration-ads-dai", version.ref = "theoplayer" } theoplayer-integration-ima = { group = "com.theoplayer.theoplayer-sdk-android", name = "integration-ads-ima", version.ref = "theoplayer" } From 401afd9e4bd985d2594ad2ce3cc335258147540a Mon Sep 17 00:00:00 2001 From: Mattias Buelens Date: Wed, 25 Mar 2026 13:19:39 +0100 Subject: [PATCH 2/7] Use `Uri` in UplynkSsaiDescriptionConverter --- .../uplynk/internal/PingScheduler.kt | 8 ++-- .../uplynk/internal/UplynkAdIntegration.kt | 8 ++-- .../UplynkSsaiDescriptionConverter.kt | 28 +++++++------ .../UplynkSsaiDescriptionConverterTest.kt | 42 ++++++++++--------- 4 files changed, 47 insertions(+), 39 deletions(-) diff --git a/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/PingScheduler.kt b/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/PingScheduler.kt index adfccaac..5e6c8991 100644 --- a/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/PingScheduler.kt +++ b/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/PingScheduler.kt @@ -27,12 +27,14 @@ internal class PingScheduler( fun onTimeUpdate(time: Duration) { if (nextRequestTime.isPositive() && time > nextRequestTime) { nextRequestTime = NEGATIVE_TIME - performPing(uplynkDescriptionConverter.buildPingUrl(prefix, sessionId, time)) + performPing(uplynkDescriptionConverter.buildPingUrl(prefix, sessionId, time).toString()) } } fun onStart(time: Duration) = - performPing(uplynkDescriptionConverter.buildStartPingUrl(prefix, sessionId, time)) + performPing( + uplynkDescriptionConverter.buildStartPingUrl(prefix, sessionId, time).toString() + ) fun onSeeking(time: Duration) { @@ -48,7 +50,7 @@ internal class PingScheduler( sessionId, time, seekStart - ) + ).toString() ) seekStart = NEGATIVE_TIME } diff --git a/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/UplynkAdIntegration.kt b/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/UplynkAdIntegration.kt index 46891bdd..34ce188a 100644 --- a/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/UplynkAdIntegration.kt +++ b/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/UplynkAdIntegration.kt @@ -226,7 +226,7 @@ internal class UplynkAdIntegration( } val playUrl = uplynkDescriptionConverter.buildPlaybackUrl(minimalResponse.playURL, ssaiDescription) - var newUplynkSource = uplynkSource.copy(src = playUrl) + var newUplynkSource = uplynkSource.copy(src = playUrl.toString()) minimalResponse.drm?.let { drm -> if (drm.required) { @@ -259,7 +259,7 @@ internal class UplynkAdIntegration( .buildAssetInfoUrls(ssaiDescription, minimalResponse.sid, minimalResponse.prefix) .mapNotNull { try { - uplynkApi.assetInfo(it) + uplynkApi.assetInfo(it.toString()) } catch (e: Exception) { eventDispatcher.dispatchAssetInfoFailure(e) controller.error(e) @@ -275,7 +275,7 @@ internal class UplynkAdIntegration( private suspend fun requestLive(ssaiDescription: UplynkSsaiDescription): PreplayInternalLiveResponse { return uplynkDescriptionConverter .buildPreplayLiveUrl(ssaiDescription) - .let { uplynkApi.preplayLive(it) } + .let { uplynkApi.preplayLive(it.toString()) } .also { try { val response = it.parseExternalResponse() @@ -291,7 +291,7 @@ internal class UplynkAdIntegration( private suspend fun requestVod(ssaiDescription: UplynkSsaiDescription): PreplayInternalVodResponse { return uplynkDescriptionConverter .buildPreplayVodUrl(ssaiDescription) - .let { uplynkApi.preplayVod(it) } + .let { uplynkApi.preplayVod(it.toString()) } .also { try { val response = it.parseExternalResponse() diff --git a/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverter.kt b/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverter.kt index 41592672..f4a198cc 100644 --- a/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverter.kt +++ b/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverter.kt @@ -1,39 +1,41 @@ package com.theoplayer.android.connector.uplynk.internal +import android.net.Uri +import androidx.core.net.toUri import com.theoplayer.android.connector.uplynk.UplynkSsaiDescription import kotlin.time.Duration internal class UplynkSsaiDescriptionConverter { private val DEFAULT_PREFIX = "https://content.uplynk.com" - fun buildPreplayVodUrl(ssaiDescription: UplynkSsaiDescription): String = with(ssaiDescription) { + fun buildPreplayVodUrl(ssaiDescription: UplynkSsaiDescription): Uri = with(ssaiDescription) { val prefix = prefix ?: DEFAULT_PREFIX - return "$prefix/preplay/$urlAssetId?v=2$drmParameters$pingParameters$urlParameters" + return "$prefix/preplay/$urlAssetId?v=2$drmParameters$pingParameters$urlParameters".toUri() } - fun buildPreplayLiveUrl(ssaiDescription: UplynkSsaiDescription): String = with(ssaiDescription) { + fun buildPreplayLiveUrl(ssaiDescription: UplynkSsaiDescription): Uri = with(ssaiDescription) { val prefix = prefix ?: DEFAULT_PREFIX - return "$prefix/preplay/$urlAssetType/$urlAssetId?v=2$drmParameters$pingParameters$urlParameters" + return "$prefix/preplay/$urlAssetType/$urlAssetId?v=2$drmParameters$pingParameters$urlParameters".toUri() } - fun buildPlaybackUrl(playUrl: String, ssaiDescription: UplynkSsaiDescription): String = with(ssaiDescription) { - return "$playUrl$playUrlParameters" + fun buildPlaybackUrl(playUrl: String, ssaiDescription: UplynkSsaiDescription): Uri = with(ssaiDescription) { + return "$playUrl$playUrlParameters".toUri() } fun buildAssetInfoUrls( ssaiDescription: UplynkSsaiDescription, sessionId: String, prefix: String - ): List = with(ssaiDescription) { + ): List = with(ssaiDescription) { val urlList = when { assetIds.isNotEmpty() -> assetIds.map { - "$prefix/player/assetinfo/$it.json" + "$prefix/player/assetinfo/$it.json".toUri() } externalIds.isNotEmpty() -> externalIds.map { - "$prefix/player/assetinfo/ext/$userId/$it.json" + "$prefix/player/assetinfo/ext/$userId/$it.json".toUri() } else -> emptyList() @@ -41,19 +43,19 @@ internal class UplynkSsaiDescriptionConverter { return if (sessionId.isBlank()) { urlList } else { - urlList.map { "$it?pbs=$sessionId" } + urlList.map { "$it?pbs=$sessionId".toUri() } } } fun buildSeekedPingUrl( prefix: String, sessionId: String, currentTime: Duration, seekStartTime: Duration - ) = buildPingUrl(prefix, sessionId, currentTime) + "&ev=seek&ft=${seekStartTime.inWholeSeconds}" + ) = (buildPingUrl(prefix, sessionId, currentTime).toString() + "&ev=seek&ft=${seekStartTime.inWholeSeconds}").toUri() fun buildStartPingUrl( prefix: String, sessionId: String, currentTime: Duration - ) = buildPingUrl(prefix, sessionId, currentTime) + "&ev=start" + ) = (buildPingUrl(prefix, sessionId, currentTime).toString() + "&ev=start").toUri() fun buildPingUrl( prefix: String, sessionId: String, currentTime: Duration - ) = "$prefix/session/ping/$sessionId.json?v=3&pt=${currentTime.inWholeSeconds}" + ) = "$prefix/session/ping/$sessionId.json?v=3&pt=${currentTime.inWholeSeconds}".toUri() } diff --git a/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverterTest.kt b/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverterTest.kt index 01dddc44..2fc47ac6 100644 --- a/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverterTest.kt +++ b/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverterTest.kt @@ -5,11 +5,14 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test +import org.junit.runner.RunWith import org.mockito.MockitoAnnotations +import org.robolectric.RobolectricTestRunner import kotlin.test.assertContains import kotlin.time.DurationUnit import kotlin.time.toDuration +@RunWith(RobolectricTestRunner::class) class UplynkSsaiDescriptionConverterTest { private lateinit var ssaiDescription: UplynkSsaiDescription @@ -28,7 +31,7 @@ class UplynkSsaiDescriptionConverterTest { @Test fun buildPreplayVodUrl_whenPrefixIsNotNull_startsUrlFromPrefix() { - val result = converter.buildPreplayVodUrl(ssaiDescription) + val result = converter.buildPreplayVodUrl(ssaiDescription).toString() assertTrue(result.startsWith("preplayprefix")) } @@ -37,14 +40,14 @@ class UplynkSsaiDescriptionConverterTest { fun buildPreplayVodUrl_whenPrefixIsNull_startsUrlFromPrefix() { ssaiDescription = ssaiDescription.copy(prefix = null) - val result = converter.buildPreplayVodUrl(ssaiDescription) + val result = converter.buildPreplayVodUrl(ssaiDescription).toString() assertTrue(result.startsWith("https://content.uplynk.com")) } @Test fun buildPreplayVodUrl_whenAssetIdHasMultipleValues_addsThemAsCommaSeparatedList() { - val result = converter.buildPreplayVodUrl(ssaiDescription) + val result = converter.buildPreplayVodUrl(ssaiDescription).toString() assertTrue(result.contains("/asset1,asset2,asset3/")) } @@ -53,7 +56,7 @@ class UplynkSsaiDescriptionConverterTest { fun buildPreplayVodUrl_whenAssetIdHasSingleValue_usesItAsJsonFilename() { ssaiDescription = ssaiDescription.copy(assetIds = listOf("singleasset")) - val result = converter.buildPreplayVodUrl(ssaiDescription) + val result = converter.buildPreplayVodUrl(ssaiDescription).toString() assertTrue(result.contains("/singleasset.json")) } @@ -64,7 +67,7 @@ class UplynkSsaiDescriptionConverterTest { assetIds = listOf(), externalIds = listOf("extId1", "extId2"), userId = "userId" ) - val result = converter.buildPreplayVodUrl(ssaiDescription) + val result = converter.buildPreplayVodUrl(ssaiDescription).toString() assertTrue(result.contains("userId")) assertTrue(result.contains("extId1,extId2/multiple.json")) @@ -76,7 +79,7 @@ class UplynkSsaiDescriptionConverterTest { assetIds = listOf(), externalIds = listOf("extId1"), userId = "userId" ) - val result = converter.buildPreplayVodUrl(ssaiDescription) + val result = converter.buildPreplayVodUrl(ssaiDescription).toString() assertTrue(result.contains("userId")) assertTrue(result.contains("extId1.json")) @@ -84,7 +87,7 @@ class UplynkSsaiDescriptionConverterTest { @Test fun buildPreplayVodUrl_always_followsTheTemplate() { - val result = converter.buildPreplayVodUrl(ssaiDescription) + val result = converter.buildPreplayVodUrl(ssaiDescription).toString() val items = result.split("/", "?") assertEquals("preplayprefix", items[0]) @@ -98,20 +101,21 @@ class UplynkSsaiDescriptionConverterTest { fun buildAssetInfoUrls_withoutSid_doesNotContainPbsParameter() { val result = converter.buildAssetInfoUrls(ssaiDescription, "", "prefix") - assertTrue(result.none { it.contains("pbs=") }) + assertTrue(result.none { it.toString().contains("pbs=") }) } @Test fun buildAssetInfoUrls_withSid_hasPbsParameter() { val result = converter.buildAssetInfoUrls(ssaiDescription, "sessionId", "prefix") - assertTrue(result.all { it.contains("pbs=sessionId") }) + assertTrue(result.all { it.toString().contains("pbs=sessionId") }) } @Test fun buildAssetInfoUrls_whenAssetIdIsEmptyAndExternalIdIsEmpty_returnsEmptyUrl() { ssaiDescription = UplynkSsaiDescription( - assetIds = listOf(), externalIds = listOf() + assetIds = listOf(), + externalIds = listOf() ) val result = converter.buildAssetInfoUrls(ssaiDescription, "", "prefix") @@ -121,7 +125,7 @@ class UplynkSsaiDescriptionConverterTest { @Test fun buildAssetInfoUrls_whenAssetIdHasValues_returnsAssetInfoUrls() { - val result = converter.buildAssetInfoUrls(ssaiDescription, "", "prefix") + val result = converter.buildAssetInfoUrls(ssaiDescription, "", "prefix").map { it.toString() } assertEquals(3, result.size) @@ -138,7 +142,7 @@ class UplynkSsaiDescriptionConverterTest { userId = "userId" ) - val result = converter.buildAssetInfoUrls(ssaiDescription, "", "prefix") + val result = converter.buildAssetInfoUrls(ssaiDescription, "", "prefix").map { it.toString() } assertEquals(2, result.size) @@ -148,22 +152,22 @@ class UplynkSsaiDescriptionConverterTest { @Test fun buildStartPingUrl_always_hasStartParameter() { - val result = converter.buildStartPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS)) + val result = converter.buildStartPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS)).toString() assertContains(result, "ev=start") } @Test fun buildStartPingUrl_always_startsTheSameAsNormalPingRequest() { - val result = converter.buildStartPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS)) - val pingUrl = converter.buildPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS)) + val result = converter.buildStartPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS)).toString() + val pingUrl = converter.buildPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS)).toString() assertContains(result, pingUrl) } @Test fun buildSeekedPingUrl_always_hasSeekParameters() { - val result = converter.buildSeekedPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS), 180.toDuration(DurationUnit.SECONDS)) + val result = converter.buildSeekedPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS), 180.toDuration(DurationUnit.SECONDS)).toString() assertContains(result, "ev=seek") assertContains(result, "ft=180") @@ -171,8 +175,8 @@ class UplynkSsaiDescriptionConverterTest { @Test fun buildSeekedPingUrl_always_startsTheSameAsNormalPingRequest() { - val result = converter.buildSeekedPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS), 180.toDuration(DurationUnit.SECONDS)) - val pingUrl = converter.buildPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS)) + val result = converter.buildSeekedPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS), 180.toDuration(DurationUnit.SECONDS)).toString() + val pingUrl = converter.buildPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS)).toString() assertContains(result, pingUrl) } @@ -180,7 +184,7 @@ class UplynkSsaiDescriptionConverterTest { @Test fun buildPingUrl_always_followsThePingTemplate() { val currentTime = 200 - val result = converter.buildPingUrl("prefix", "sessionId", currentTime.toDuration(DurationUnit.SECONDS)) + val result = converter.buildPingUrl("prefix", "sessionId", currentTime.toDuration(DurationUnit.SECONDS)).toString() assertEquals(result, "prefix/session/ping/sessionId.json?v=3&pt=200") } From 30102bfcaf33a2387a6fb7c2e98798ef9becc836 Mon Sep 17 00:00:00 2001 From: Mattias Buelens Date: Wed, 25 Mar 2026 13:21:08 +0100 Subject: [PATCH 3/7] Rework URL building --- .../UplynkSsaiDescriptionConverter.kt | 73 ++++++++++++++----- .../UplynkSsaiDescriptionUrlExtensions.kt | 32 +++----- 2 files changed, 65 insertions(+), 40 deletions(-) diff --git a/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverter.kt b/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverter.kt index f4a198cc..192207bb 100644 --- a/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverter.kt +++ b/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverter.kt @@ -8,21 +8,34 @@ import kotlin.time.Duration internal class UplynkSsaiDescriptionConverter { private val DEFAULT_PREFIX = "https://content.uplynk.com" - fun buildPreplayVodUrl(ssaiDescription: UplynkSsaiDescription): Uri = with(ssaiDescription) { - val prefix = prefix ?: DEFAULT_PREFIX - - return "$prefix/preplay/$urlAssetId?v=2$drmParameters$pingParameters$urlParameters".toUri() - } - - fun buildPreplayLiveUrl(ssaiDescription: UplynkSsaiDescription): Uri = with(ssaiDescription) { - val prefix = prefix ?: DEFAULT_PREFIX + fun buildPreplayVodUrl(ssaiDescription: UplynkSsaiDescription): Uri = + with(ssaiDescription) { + return (prefix ?: DEFAULT_PREFIX).toUri().buildUpon().apply { + appendEncodedPath("preplay/$urlAssetId") + appendQueryParameter("v", "2") + drmParameters.forEach { (key, value) -> appendQueryParameter(key, value) } + pingParameters.forEach { (key, value) -> appendQueryParameter(key, value) } + preplayParameters.forEach { (key, value) -> appendQueryParameter(key, value) } + }.build() + } - return "$prefix/preplay/$urlAssetType/$urlAssetId?v=2$drmParameters$pingParameters$urlParameters".toUri() - } + fun buildPreplayLiveUrl(ssaiDescription: UplynkSsaiDescription): Uri = + with(ssaiDescription) { + return (prefix ?: DEFAULT_PREFIX).toUri().buildUpon().apply { + appendEncodedPath("preplay/$urlAssetType/$urlAssetId") + appendQueryParameter("v", "2") + drmParameters.forEach { (key, value) -> appendQueryParameter(key, value) } + pingParameters.forEach { (key, value) -> appendQueryParameter(key, value) } + preplayParameters.forEach { (key, value) -> appendQueryParameter(key, value) } + }.build() + } - fun buildPlaybackUrl(playUrl: String, ssaiDescription: UplynkSsaiDescription): Uri = with(ssaiDescription) { - return "$playUrl$playUrlParameters".toUri() - } + fun buildPlaybackUrl(playUrl: String, ssaiDescription: UplynkSsaiDescription): Uri = + with(ssaiDescription) { + return playUrl.toUri().buildUpon().apply { + playbackUrlParameters.forEach { (key, value) -> appendQueryParameter(key, value) } + }.build() + } fun buildAssetInfoUrls( ssaiDescription: UplynkSsaiDescription, @@ -43,19 +56,39 @@ internal class UplynkSsaiDescriptionConverter { return if (sessionId.isBlank()) { urlList } else { - urlList.map { "$it?pbs=$sessionId".toUri() } + urlList.map { url -> + url.buildUpon().apply { + appendQueryParameter("pbs", sessionId) + }.build() + } } } fun buildSeekedPingUrl( - prefix: String, sessionId: String, currentTime: Duration, seekStartTime: Duration - ) = (buildPingUrl(prefix, sessionId, currentTime).toString() + "&ev=seek&ft=${seekStartTime.inWholeSeconds}").toUri() + prefix: String, + sessionId: String, + currentTime: Duration, + seekStartTime: Duration + ): Uri = buildPingUrl(prefix, sessionId, currentTime).buildUpon().apply { + appendQueryParameter("ev", "seek") + appendQueryParameter("ft", seekStartTime.inWholeSeconds.toString()) + }.build() fun buildStartPingUrl( - prefix: String, sessionId: String, currentTime: Duration - ) = (buildPingUrl(prefix, sessionId, currentTime).toString() + "&ev=start").toUri() + prefix: String, + sessionId: String, + currentTime: Duration + ): Uri = buildPingUrl(prefix, sessionId, currentTime).buildUpon().apply { + appendQueryParameter("ev", "start") + }.build() fun buildPingUrl( - prefix: String, sessionId: String, currentTime: Duration - ) = "$prefix/session/ping/$sessionId.json?v=3&pt=${currentTime.inWholeSeconds}".toUri() + prefix: String, + sessionId: String, + currentTime: Duration + ): Uri = prefix.toUri().buildUpon().apply { + appendEncodedPath("session/ping/$sessionId.json") + appendQueryParameter("v", "3") + appendQueryParameter("pt", currentTime.inWholeSeconds.toString()) + }.build() } diff --git a/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionUrlExtensions.kt b/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionUrlExtensions.kt index 9d2b1a99..b581c7a0 100644 --- a/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionUrlExtensions.kt +++ b/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionUrlExtensions.kt @@ -3,34 +3,26 @@ package com.theoplayer.android.connector.uplynk.internal import com.theoplayer.android.connector.uplynk.UplynkAssetType import com.theoplayer.android.connector.uplynk.UplynkSsaiDescription -internal val UplynkSsaiDescription.drmParameters: String +internal val UplynkSsaiDescription.drmParameters: List> get() = if (contentProtected) { - "&manifest=mpd&rmt=wv" + listOf( + "manifest" to "mpd", + "rmt" to "wv", + ) } else { - "" + listOf() } -internal val UplynkSsaiDescription.urlParameters - get() = if (preplayParameters.isNotEmpty()) { - preplayParameters.map { "${it.key}=${it.value}" }.joinToString("&", prefix = "&") - } else { - "" - } - -internal val UplynkSsaiDescription.playUrlParameters - get() = if (playbackUrlParameters.isNotEmpty()) { - playbackUrlParameters.map { "${it.key}=${it.value}" }.joinToString("&", prefix = "?") - } else { - "" - } - -internal val UplynkSsaiDescription.pingParameters: String +internal val UplynkSsaiDescription.pingParameters: List> get() { val feature = UplynkPingFeatures.from(this) return if (feature == UplynkPingFeatures.NO_PING) { - "" + listOf() } else { - "&ad.cping=1&ad.pingf=${feature.pingfValue}" + listOf( + "ad.cping" to "1", + "ad.pingf" to feature.pingfValue.toString() + ) } } From 627bac7fa17d3b46fb5a152feaf63c5088a943ea Mon Sep 17 00:00:00 2001 From: Mattias Buelens Date: Wed, 25 Mar 2026 13:37:07 +0100 Subject: [PATCH 4/7] Rework tests --- .../UplynkSsaiDescriptionConverterTest.kt | 66 +++++++++++-------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverterTest.kt b/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverterTest.kt index 2fc47ac6..abc91220 100644 --- a/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverterTest.kt +++ b/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverterTest.kt @@ -1,5 +1,6 @@ package com.theoplayer.android.connector.uplynk.internal +import androidx.core.net.toUri import com.theoplayer.android.connector.uplynk.UplynkSsaiDescription import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue @@ -8,7 +9,6 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.MockitoAnnotations import org.robolectric.RobolectricTestRunner -import kotlin.test.assertContains import kotlin.time.DurationUnit import kotlin.time.toDuration @@ -87,28 +87,32 @@ class UplynkSsaiDescriptionConverterTest { @Test fun buildPreplayVodUrl_always_followsTheTemplate() { - val result = converter.buildPreplayVodUrl(ssaiDescription).toString() - - val items = result.split("/", "?") - assertEquals("preplayprefix", items[0]) - assertEquals("preplay", items[1]) - assertEquals("asset1,asset2,asset3", items[2]) - assertEquals("multiple.json", items[3]) - assertEquals("v=2&p1=v1&p2=v2&p3=v3", items[4]) + val result = converter.buildPreplayVodUrl(ssaiDescription) + + assertEquals( + listOf( + "preplayprefix", + "preplay", + "asset1,asset2,asset3", + "multiple.json" + ), + result.pathSegments + ) + assertEquals("v=2&p1=v1&p2=v2&p3=v3", result.query) } @Test fun buildAssetInfoUrls_withoutSid_doesNotContainPbsParameter() { val result = converter.buildAssetInfoUrls(ssaiDescription, "", "prefix") - assertTrue(result.none { it.toString().contains("pbs=") }) + assertTrue(result.none { it.queryParameterNames.contains("pbs") }) } @Test fun buildAssetInfoUrls_withSid_hasPbsParameter() { val result = converter.buildAssetInfoUrls(ssaiDescription, "sessionId", "prefix") - assertTrue(result.all { it.toString().contains("pbs=sessionId") }) + assertTrue(result.all { it.getQueryParameters("pbs") == listOf("sessionId") }) } @Test @@ -125,13 +129,17 @@ class UplynkSsaiDescriptionConverterTest { @Test fun buildAssetInfoUrls_whenAssetIdHasValues_returnsAssetInfoUrls() { - val result = converter.buildAssetInfoUrls(ssaiDescription, "", "prefix").map { it.toString() } + val result = converter.buildAssetInfoUrls(ssaiDescription, "", "prefix") assertEquals(3, result.size) - - assertContains(result, "prefix/player/assetinfo/asset1.json") - assertContains(result, "prefix/player/assetinfo/asset2.json") - assertContains(result, "prefix/player/assetinfo/asset3.json") + assertEquals( + listOf( + "prefix/player/assetinfo/asset1.json".toUri(), + "prefix/player/assetinfo/asset2.json".toUri(), + "prefix/player/assetinfo/asset3.json".toUri() + ), + result + ) } @Test @@ -142,19 +150,23 @@ class UplynkSsaiDescriptionConverterTest { userId = "userId" ) - val result = converter.buildAssetInfoUrls(ssaiDescription, "", "prefix").map { it.toString() } + val result = converter.buildAssetInfoUrls(ssaiDescription, "", "prefix") assertEquals(2, result.size) - - assertContains(result, "prefix/player/assetinfo/ext/userId/extId1.json") - assertContains(result, "prefix/player/assetinfo/ext/userId/extId2.json") + assertEquals( + listOf( + "prefix/player/assetinfo/ext/userId/extId1.json".toUri(), + "prefix/player/assetinfo/ext/userId/extId2.json".toUri() + ), + result + ) } @Test fun buildStartPingUrl_always_hasStartParameter() { - val result = converter.buildStartPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS)).toString() + val result = converter.buildStartPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS)) - assertContains(result, "ev=start") + assertEquals(listOf("start"), result.getQueryParameters("ev")) } @Test @@ -162,15 +174,15 @@ class UplynkSsaiDescriptionConverterTest { val result = converter.buildStartPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS)).toString() val pingUrl = converter.buildPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS)).toString() - assertContains(result, pingUrl) + assertTrue(result.startsWith(pingUrl)) } @Test fun buildSeekedPingUrl_always_hasSeekParameters() { - val result = converter.buildSeekedPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS), 180.toDuration(DurationUnit.SECONDS)).toString() + val result = converter.buildSeekedPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS), 180.toDuration(DurationUnit.SECONDS)) - assertContains(result, "ev=seek") - assertContains(result, "ft=180") + assertEquals(listOf("seek"), result.getQueryParameters("ev")) + assertEquals(listOf("180"), result.getQueryParameters("ft")) } @Test @@ -178,7 +190,7 @@ class UplynkSsaiDescriptionConverterTest { val result = converter.buildSeekedPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS), 180.toDuration(DurationUnit.SECONDS)).toString() val pingUrl = converter.buildPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS)).toString() - assertContains(result, pingUrl) + assertTrue(result.startsWith(pingUrl)) } @Test From 7a8a614fcb3ca5cce479b6501e7635d8506c136b Mon Sep 17 00:00:00 2001 From: Mattias Buelens Date: Wed, 25 Mar 2026 13:39:03 +0100 Subject: [PATCH 5/7] Use `.seconds` --- .../uplynk/internal/PingScheduler.kt | 5 +- .../uplynk/internal/AdHandlerTest.kt | 49 +++++++++---------- .../UplynkSsaiDescriptionConverterTest.kt | 17 +++---- 3 files changed, 34 insertions(+), 37 deletions(-) diff --git a/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/PingScheduler.kt b/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/PingScheduler.kt index 5e6c8991..b3c195b3 100644 --- a/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/PingScheduler.kt +++ b/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/PingScheduler.kt @@ -6,8 +6,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import kotlin.time.Duration -import kotlin.time.DurationUnit -import kotlin.time.toDuration +import kotlin.time.Duration.Companion.seconds internal class PingScheduler( private val uplynkApi: UplynkApi, @@ -17,7 +16,7 @@ internal class PingScheduler( private val eventDispatcher: UplynkEventDispatcher, private val adScheduler: UplynkAdScheduler ) { - private val NEGATIVE_TIME = (-1).toDuration(DurationUnit.SECONDS) + private val NEGATIVE_TIME = (-1).seconds private var nextRequestTime: Duration = NEGATIVE_TIME private var seekStart: Duration = NEGATIVE_TIME diff --git a/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/AdHandlerTest.kt b/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/AdHandlerTest.kt index 6cf37ddf..472d2a61 100644 --- a/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/AdHandlerTest.kt +++ b/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/AdHandlerTest.kt @@ -19,8 +19,7 @@ import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import kotlin.time.Duration -import kotlin.time.DurationUnit -import kotlin.time.toDuration +import kotlin.time.Duration.Companion.seconds class AdHandlerTest { @@ -49,8 +48,8 @@ class AdHandlerTest { listOf(), "", "", - 100.toDuration(DurationUnit.SECONDS), - 200.toDuration(DurationUnit.SECONDS) + 100.seconds, + 200.seconds ) adHandler.createAdBreak(adBreak) @@ -72,8 +71,8 @@ class AdHandlerTest { listOf(), "", "", - 100.toDuration(DurationUnit.SECONDS), - 200.toDuration(DurationUnit.SECONDS) + 100.seconds, + 200.seconds ) adHandler.createAdBreak(adBreak) @@ -93,7 +92,7 @@ class AdHandlerTest { mapOf(), 1f, 2f, - 100.toDuration(DurationUnit.SECONDS) + 100.seconds ), UplynkAd( null, @@ -103,7 +102,7 @@ class AdHandlerTest { mapOf(), 1f, 2f, - 200.toDuration(DurationUnit.SECONDS) + 200.seconds ), UplynkAd( null, @@ -113,9 +112,9 @@ class AdHandlerTest { mapOf(), 1f, 2f, - 300.toDuration(DurationUnit.SECONDS) + 300.seconds ), - ), "", "", 400.toDuration(DurationUnit.SECONDS), 500.toDuration(DurationUnit.SECONDS) + ), "", "", 400.seconds, 500.seconds ) adHandler.createAdBreak(adBreak) @@ -156,7 +155,7 @@ class AdHandlerTest { @Test fun onAdBegin_forUnknownAd_throwsAnException() { val ad = - UplynkAd(null, listOf(), "", "", mapOf(), 1f, 2f, 100.toDuration(DurationUnit.SECONDS)) + UplynkAd(null, listOf(), "", "", mapOf(), 1f, 2f, 100.seconds) assertThrows(java.lang.IllegalStateException::class.java) { adHandler.onAdBegin(ad) @@ -166,13 +165,13 @@ class AdHandlerTest { @Test fun onAdBegin_forCreatedAdBreak_callsBeginAd() { val uplynkAd = - UplynkAd(null, listOf(), "", "", mapOf(), 1f, 2f, 100.toDuration(DurationUnit.SECONDS)) + UplynkAd(null, listOf(), "", "", mapOf(), 1f, 2f, 100.seconds) val adBreak = UplynkAdBreak( listOf(uplynkAd), "", "", - 400.toDuration(DurationUnit.SECONDS), - 500.toDuration(DurationUnit.SECONDS) + 400.seconds, + 500.seconds ) adHandler.createAdBreak(adBreak) @@ -184,7 +183,7 @@ class AdHandlerTest { @Test fun onAdEnd_forUnknownAd_throwsAnException() { val ad = - UplynkAd(null, listOf(), "", "", mapOf(), 1f, 2f, 100.toDuration(DurationUnit.SECONDS)) + UplynkAd(null, listOf(), "", "", mapOf(), 1f, 2f, 100.seconds) assertThrows(java.lang.IllegalStateException::class.java) { adHandler.onAdEnd(ad) @@ -194,13 +193,13 @@ class AdHandlerTest { @Test fun onAdEnd_forCreatedAdBreak_callsEndAd() { val uplynkAd = - UplynkAd(null, listOf(), "", "", mapOf(), 1f, 2f, 100.toDuration(DurationUnit.SECONDS)) + UplynkAd(null, listOf(), "", "", mapOf(), 1f, 2f, 100.seconds) val adBreak = UplynkAdBreak( listOf(uplynkAd), "", "", - 400.toDuration(DurationUnit.SECONDS), - 500.toDuration(DurationUnit.SECONDS) + 400.seconds, + 500.seconds ) adHandler.createAdBreak(adBreak) @@ -212,20 +211,20 @@ class AdHandlerTest { @Test fun onAdProgressUpdate_forCreatedAdBreak_callsUpdateAdProgress() { val uplynkAd = - UplynkAd(null, listOf(), "", "", mapOf(), 1f, 2f, 100.toDuration(DurationUnit.SECONDS)) + UplynkAd(null, listOf(), "", "", mapOf(), 1f, 2f, 100.seconds) val adBreak = UplynkAdBreak( listOf(uplynkAd), "", "", - 400.toDuration(DurationUnit.SECONDS), - 500.toDuration(DurationUnit.SECONDS) + 400.seconds, + 500.seconds ) adHandler.createAdBreak(adBreak) adHandler.onAdProgressUpdate( UplynkAdState(uplynkAd, AdState.STARTED), adBreak, - 450.toDuration(DurationUnit.SECONDS) + 450.seconds ) verify(controller).updateAdProgress(eq(mockAd), eq(0.5)) @@ -234,13 +233,13 @@ class AdHandlerTest { @Test fun onAdProgressUpdate_forUnknownAd_throwsAnException() { val ad = - UplynkAd(null, listOf(), "", "", mapOf(), 1f, 2f, 100.toDuration(DurationUnit.SECONDS)) + UplynkAd(null, listOf(), "", "", mapOf(), 1f, 2f, 100.seconds) val adBreak = UplynkAdBreak( listOf(ad), "", "", - 400.toDuration(DurationUnit.SECONDS), - 500.toDuration(DurationUnit.SECONDS) + 400.seconds, + 500.seconds ) diff --git a/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverterTest.kt b/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverterTest.kt index abc91220..c920b9ff 100644 --- a/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverterTest.kt +++ b/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverterTest.kt @@ -9,8 +9,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.MockitoAnnotations import org.robolectric.RobolectricTestRunner -import kotlin.time.DurationUnit -import kotlin.time.toDuration +import kotlin.time.Duration.Companion.seconds @RunWith(RobolectricTestRunner::class) class UplynkSsaiDescriptionConverterTest { @@ -164,22 +163,22 @@ class UplynkSsaiDescriptionConverterTest { @Test fun buildStartPingUrl_always_hasStartParameter() { - val result = converter.buildStartPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS)) + val result = converter.buildStartPingUrl("prefix", "sessionId", 200.seconds) assertEquals(listOf("start"), result.getQueryParameters("ev")) } @Test fun buildStartPingUrl_always_startsTheSameAsNormalPingRequest() { - val result = converter.buildStartPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS)).toString() - val pingUrl = converter.buildPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS)).toString() + val result = converter.buildStartPingUrl("prefix", "sessionId", 200.seconds).toString() + val pingUrl = converter.buildPingUrl("prefix", "sessionId", 200.seconds).toString() assertTrue(result.startsWith(pingUrl)) } @Test fun buildSeekedPingUrl_always_hasSeekParameters() { - val result = converter.buildSeekedPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS), 180.toDuration(DurationUnit.SECONDS)) + val result = converter.buildSeekedPingUrl("prefix", "sessionId", 200.seconds, 180.seconds) assertEquals(listOf("seek"), result.getQueryParameters("ev")) assertEquals(listOf("180"), result.getQueryParameters("ft")) @@ -187,8 +186,8 @@ class UplynkSsaiDescriptionConverterTest { @Test fun buildSeekedPingUrl_always_startsTheSameAsNormalPingRequest() { - val result = converter.buildSeekedPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS), 180.toDuration(DurationUnit.SECONDS)).toString() - val pingUrl = converter.buildPingUrl("prefix", "sessionId", 200.toDuration(DurationUnit.SECONDS)).toString() + val result = converter.buildSeekedPingUrl("prefix", "sessionId", 200.seconds, 180.seconds).toString() + val pingUrl = converter.buildPingUrl("prefix", "sessionId", 200.seconds).toString() assertTrue(result.startsWith(pingUrl)) } @@ -196,7 +195,7 @@ class UplynkSsaiDescriptionConverterTest { @Test fun buildPingUrl_always_followsThePingTemplate() { val currentTime = 200 - val result = converter.buildPingUrl("prefix", "sessionId", currentTime.toDuration(DurationUnit.SECONDS)).toString() + val result = converter.buildPingUrl("prefix", "sessionId", currentTime.seconds).toString() assertEquals(result, "prefix/session/ping/sessionId.json?v=3&pt=200") } From 80f29768efe333dbe19c7c3c2ace9571bb15ec35 Mon Sep 17 00:00:00 2001 From: Mattias Buelens Date: Wed, 25 Mar 2026 13:40:44 +0100 Subject: [PATCH 6/7] Use `linkedMapOf` --- .../uplynk/internal/UplynkSsaiDescriptionConverterTest.kt | 2 +- .../uplynk/internal/UplynkSsaiDescriptionSerializerTests.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverterTest.kt b/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverterTest.kt index c920b9ff..de8a8661 100644 --- a/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverterTest.kt +++ b/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionConverterTest.kt @@ -23,7 +23,7 @@ class UplynkSsaiDescriptionConverterTest { ssaiDescription = UplynkSsaiDescription( prefix = "preplayprefix", assetIds = listOf("asset1", "asset2", "asset3"), - preplayParameters = LinkedHashMap(mapOf("p1" to "v1", "p2" to "v2", "p3" to "v3")) + preplayParameters = linkedMapOf("p1" to "v1", "p2" to "v2", "p3" to "v3") ) converter = UplynkSsaiDescriptionConverter() } diff --git a/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionSerializerTests.kt b/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionSerializerTests.kt index b6c5f6d2..e30544a7 100644 --- a/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionSerializerTests.kt +++ b/connectors/uplynk/src/test/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionSerializerTests.kt @@ -24,7 +24,7 @@ class UplynkSsaiDescriptionSerializerTests { val ssaiDescription = UplynkSsaiDescription( prefix = "preplayprefix", assetIds = listOf("asset1", "asset2", "asset3"), - preplayParameters = LinkedHashMap(mapOf("p1" to "v1", "p2" to "v2", "p3" to "v3")) + preplayParameters = linkedMapOf("p1" to "v1", "p2" to "v2", "p3" to "v3") ) val jsonString = UplynkSsaiDescriptionDeserializer.toJson(ssaiDescription) val jsonObject = Json.parseToJsonElement(jsonString).jsonObject @@ -43,7 +43,7 @@ class UplynkSsaiDescriptionSerializerTests { val ssaiDescription = UplynkSsaiDescription( prefix = "preplayprefix", assetIds = listOf("asset1", "asset2", "asset3"), - preplayParameters = LinkedHashMap(mapOf("p1" to "v1", "p2" to "v2", "p3" to "v3")) + preplayParameters = linkedMapOf("p1" to "v1", "p2" to "v2", "p3" to "v3") ) val jsonString = UplynkSsaiDescriptionDeserializer.toJson(ssaiDescription) val deserializedSsaiDescription = UplynkSsaiDescriptionDeserializer.fromJson(jsonString) From deab0d0ad71374ab93f3704c8c43e637be7cb200 Mon Sep 17 00:00:00 2001 From: Mattias Buelens Date: Wed, 25 Mar 2026 15:27:53 +0100 Subject: [PATCH 7/7] Add `QueryParameter` type alias --- .../uplynk/internal/UplynkSsaiDescriptionUrlExtensions.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionUrlExtensions.kt b/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionUrlExtensions.kt index b581c7a0..ce2fc847 100644 --- a/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionUrlExtensions.kt +++ b/connectors/uplynk/src/main/java/com/theoplayer/android/connector/uplynk/internal/UplynkSsaiDescriptionUrlExtensions.kt @@ -3,7 +3,9 @@ package com.theoplayer.android.connector.uplynk.internal import com.theoplayer.android.connector.uplynk.UplynkAssetType import com.theoplayer.android.connector.uplynk.UplynkSsaiDescription -internal val UplynkSsaiDescription.drmParameters: List> +internal typealias QueryParameter = Pair + +internal val UplynkSsaiDescription.drmParameters: List get() = if (contentProtected) { listOf( "manifest" to "mpd", @@ -13,7 +15,7 @@ internal val UplynkSsaiDescription.drmParameters: List> listOf() } -internal val UplynkSsaiDescription.pingParameters: List> +internal val UplynkSsaiDescription.pingParameters: List get() { val feature = UplynkPingFeatures.from(this) return if (feature == UplynkPingFeatures.NO_PING) {