diff --git a/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/SRGMediaItem.kt b/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/SRGMediaItem.kt index 78d0105b6..e26d766e2 100644 --- a/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/SRGMediaItem.kt +++ b/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/SRGMediaItem.kt @@ -4,17 +4,17 @@ */ package ch.srgssr.pillarbox.core.business -import android.net.Uri import androidx.media3.common.MediaItem import androidx.media3.common.MediaMetadata import ch.srgssr.pillarbox.core.business.integrationlayer.data.isValidMediaUrn import ch.srgssr.pillarbox.core.business.integrationlayer.service.IlHost import ch.srgssr.pillarbox.core.business.integrationlayer.service.IlLocation +import ch.srgssr.pillarbox.core.business.integrationlayer.service.IlUrl +import ch.srgssr.pillarbox.core.business.integrationlayer.service.IlUrl.Companion.toIlUrl import ch.srgssr.pillarbox.core.business.integrationlayer.service.Vector import ch.srgssr.pillarbox.core.business.source.MimeTypeSrg import ch.srgssr.pillarbox.player.PillarboxDsl import ch.srgssr.pillarbox.player.source.PillarboxMediaSource -import java.net.URL /** * Creates a [MediaItem] suited for SRG SSR content identified by a URN. @@ -29,7 +29,7 @@ import java.net.URL * * ```kotlin * val mediaItem: MediaItem = SRGMediaItem("urn:rts:audio:3262363") { - * host(IlHost.Default) + * host(IlHost.PROD) * vector(Vector.TV) * } * ``` @@ -50,6 +50,7 @@ import java.net.URL @PillarboxDsl @Suppress("FunctionName") fun SRGMediaItem(urn: String, block: SRGMediaItemBuilder.() -> Unit = {}): MediaItem { + require(urn.isValidMediaUrn()) { "URN is not valid." } return SRGMediaItemBuilder(MediaItem.Builder().setMediaId(urn).build()).apply(block).build() } @@ -60,7 +61,7 @@ fun SRGMediaItem(urn: String, block: SRGMediaItemBuilder.() -> Unit = {}): Media * **Usage example** * ```kotlin * val mediaItem: MediaItem = sourceItem.buildUpon { - * host(IlHost.Stage) + * host(IlHost.STAGE) * } * ``` * @@ -78,25 +79,19 @@ fun MediaItem.buildUpon(block: SRGMediaItemBuilder.() -> Unit): MediaItem { class SRGMediaItemBuilder internal constructor(mediaItem: MediaItem) { private val mediaItemBuilder = mediaItem.buildUpon() private var urn: String = mediaItem.mediaId - private var host: URL = IlHost.DEFAULT + private var host: IlHost = IlHost.PROD private var forceSAM: Boolean = false private var ilLocation: IlLocation? = null private var vector: Vector = Vector.MOBILE init { - urn = mediaItem.mediaId mediaItem.localConfiguration?.let { localConfiguration -> - val uri = localConfiguration.uri - val urn = uri.lastPathSegment - if (uri.toString().contains(PATH) && urn.isValidMediaUrn()) { - uri.host?.let { hostname -> host = URL(Uri.Builder().scheme(host.protocol).authority(hostname).build().toString()) } - this.urn = urn!! - this.forceSAM = uri.getQueryParameter(PARAM_FORCE_SAM)?.toBooleanStrictOrNull() == true - this.ilLocation = uri.getQueryParameter(PARAM_FORCE_LOCATION)?.let { IlLocation.fromName(it) } - uri.getQueryParameter(PARAM_VECTOR) - ?.let { Vector.fromLabel(it) } - ?.let { vector = it } - } + val ilUrl = localConfiguration.uri.toIlUrl() + host = ilUrl.host + urn = ilUrl.urn + forceSAM = ilUrl.forceSAM + ilLocation = ilUrl.ilLocation + vector = ilUrl.vector } } @@ -128,11 +123,11 @@ class SRGMediaItemBuilder internal constructor(mediaItem: MediaItem) { } /** - * Sets the host URL to the integration layer. + * Sets the host base URL to the integration layer. * - * @param host The URL of the integration layer server. + * @param host The base URL of the integration layer server. */ - fun host(host: URL) { + fun host(host: IlHost) { this.host = host } @@ -172,35 +167,10 @@ class SRGMediaItemBuilder internal constructor(mediaItem: MediaItem) { * @return A new [MediaItem] ready for playback. */ fun build(): MediaItem { - require(urn.isValidMediaUrn()) { "Not a valid Urn!" } - mediaItemBuilder.setMediaId(urn) - mediaItemBuilder.setMimeType(MimeTypeSrg) - val uri = Uri.Builder().apply { - scheme(host.protocol) - authority(host.host) - if (forceSAM) { - appendEncodedPath("sam") - } - appendEncodedPath(PATH) - appendEncodedPath(urn) - if (forceSAM) { - appendQueryParameter(PARAM_FORCE_SAM, true.toString()) - } - ilLocation?.let { - appendQueryParameter(PARAM_FORCE_LOCATION, it.toString()) - } - appendQueryParameter(PARAM_VECTOR, vector.toString()) - appendQueryParameter(PARAM_ONLY_CHAPTERS, true.toString()) - }.build() - mediaItemBuilder.setUri(uri) - return mediaItemBuilder.build() - } - - private companion object { - private const val PATH = "integrationlayer/2.1/mediaComposition/byUrn/" - private const val PARAM_ONLY_CHAPTERS = "onlyChapters" - private const val PARAM_FORCE_SAM = "forceSAM" - private const val PARAM_FORCE_LOCATION = "forceLocation" - private const val PARAM_VECTOR = "vector" + val ilUrl = IlUrl(host = host, urn = urn, vector = vector, forceSAM = forceSAM, ilLocation = ilLocation) + return mediaItemBuilder.setUri(ilUrl.uri) + .setMediaId(ilUrl.urn) + .setMimeType(MimeTypeSrg) + .build() } } diff --git a/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/integrationlayer/ImageScalingService.kt b/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/integrationlayer/ImageScalingService.kt index 9bf0bb867..1af38fd0d 100644 --- a/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/integrationlayer/ImageScalingService.kt +++ b/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/integrationlayer/ImageScalingService.kt @@ -5,16 +5,15 @@ package ch.srgssr.pillarbox.core.business.integrationlayer import ch.srgssr.pillarbox.core.business.integrationlayer.service.IlHost -import java.net.URL import java.net.URLEncoder /** * Service used to get a scaled image URL. This only works for SRG images. * - * @param baseUrl Base URL of the service. + * @param ilHost Base URL of the service. */ internal class ImageScalingService( - private val baseUrl: URL = IlHost.DEFAULT + private val ilHost: IlHost = IlHost.PROD ) { fun getScaledImageUrl( @@ -22,6 +21,6 @@ internal class ImageScalingService( ): String { val encodedImageUrl = URLEncoder.encode(imageUrl, Charsets.UTF_8.name()) - return "${baseUrl}images/?imageUrl=$encodedImageUrl&format=webp&width=480" + return "${ilHost.baseHostUrl}/images/?imageUrl=$encodedImageUrl&format=webp&width=480" } } diff --git a/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/integrationlayer/service/IlHost.kt b/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/integrationlayer/service/IlHost.kt index d22bfcf7d..795b7fbb3 100644 --- a/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/integrationlayer/service/IlHost.kt +++ b/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/integrationlayer/service/IlHost.kt @@ -4,29 +4,42 @@ */ package ch.srgssr.pillarbox.core.business.integrationlayer.service -import java.net.URL - /** - * Object containing the different host URLs for the integration layer service. + * Represents the different host URLs for the integration layer service. + * + * @property baseHostUrl The base URL of the environment. */ -object IlHost { +enum class IlHost(val baseHostUrl: String) { + /** * The base URL for the production environment. */ - val PROD = URL("https://il.srgssr.ch/") + PROD(baseHostUrl = "https://il.srgssr.ch"), /** * The base URL for the test environment. */ - val TEST = URL("https://il-test.srgssr.ch/") + TEST(baseHostUrl = "https://il-test.srgssr.ch"), /** * The base URL for the stage environment. */ - val STAGE = URL("https://il-stage.srgssr.ch/") + STAGE(baseHostUrl = "https://il-stage.srgssr.ch"), - /** - * The default host used by the library. - */ - val DEFAULT = PROD + ; + + @Suppress("UndocumentedPublicClass") + companion object { + + /** + * Parses the given [url] and returns the corresponding [IlHost]. + * + * @param url The URL to parse. + * + * @return The matching [IlHost] or `null` if none was found. + */ + fun parse(url: String): IlHost? { + return entries.find { url.startsWith(it.baseHostUrl) } + } + } } diff --git a/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/integrationlayer/service/IlUrl.kt b/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/integrationlayer/service/IlUrl.kt new file mode 100644 index 000000000..83e067697 --- /dev/null +++ b/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/integrationlayer/service/IlUrl.kt @@ -0,0 +1,70 @@ +/* + * Copyright (c) SRG SSR. All rights reserved. + * License information is available from the LICENSE file. + */ +package ch.srgssr.pillarbox.core.business.integrationlayer.service + +import android.net.Uri +import ch.srgssr.pillarbox.core.business.integrationlayer.data.isValidMediaUrn + +/** + * @property host The [IlHost] to use. + * @property urn The URN of the media to request. + * @property vector The [Vector] to use. + * @property forceSAM Force SAM usage. + * @property ilLocation The [IlLocation] of the request. + */ +data class IlUrl( + val host: IlHost, + val urn: String, + val vector: Vector, + val forceSAM: Boolean = false, + val ilLocation: IlLocation? = null, +) { + + init { + require(urn.isValidMediaUrn()) + } + + /** + * [Uri] representation of this [IlUrl]. + */ + val uri: Uri = Uri.parse(host.baseHostUrl).buildUpon().apply { + if (forceSAM) { + appendEncodedPath("sam") + appendQueryParameter(PARAM_FORCE_SAM, true.toString()) + } + appendEncodedPath(PATH) + appendEncodedPath(urn) + ilLocation?.let { + appendQueryParameter(PARAM_FORCE_LOCATION, it.toString()) + } + appendQueryParameter(PARAM_VECTOR, vector.toString()) + appendQueryParameter(PARAM_ONLY_CHAPTERS, true.toString()) + }.build() + + internal companion object { + private const val PARAM_ONLY_CHAPTERS = "onlyChapters" + private const val PARAM_FORCE_SAM = "forceSAM" + private const val PARAM_FORCE_LOCATION = "forceLocation" + private const val PARAM_VECTOR = "vector" + private const val PATH = "integrationlayer/2.1/mediaComposition/byUrn/" + + /** + * Converts an [Uri] into a valid [IlUrl]. + * + * @return An [IlUrl] or throws an [IllegalArgumentException] if the [Uri] can't be parsed. + */ + internal fun Uri.toIlUrl(): IlUrl { + val urn = lastPathSegment + require(urn.isValidMediaUrn()) { "Invalid URN $urn found in $this" } + val host = IlHost.parse(toString()) + requireNotNull(host) { "Invalid URL $this" } + val forceSAM = getQueryParameter(PARAM_FORCE_SAM)?.toBooleanStrictOrNull() == true || pathSegments.contains("sam") + val ilLocation = getQueryParameter(PARAM_FORCE_LOCATION)?.let { IlLocation.fromName(it) } + val vector = getQueryParameter(PARAM_VECTOR)?.let { Vector.fromLabel(it) } ?: Vector.MOBILE + + return IlUrl(host = host, urn = checkNotNull(urn), vector = vector, ilLocation = ilLocation, forceSAM = forceSAM) + } + } +} diff --git a/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/source/SRGAssetLoader.kt b/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/source/SRGAssetLoader.kt index 4e4cc47f9..6121d3ba9 100644 --- a/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/source/SRGAssetLoader.kt +++ b/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/source/SRGAssetLoader.kt @@ -23,7 +23,7 @@ import ch.srgssr.pillarbox.core.business.integrationlayer.data.Chapter import ch.srgssr.pillarbox.core.business.integrationlayer.data.Drm import ch.srgssr.pillarbox.core.business.integrationlayer.data.MediaComposition import ch.srgssr.pillarbox.core.business.integrationlayer.data.Resource -import ch.srgssr.pillarbox.core.business.integrationlayer.data.isValidMediaUrn +import ch.srgssr.pillarbox.core.business.integrationlayer.service.IlUrl.Companion.toIlUrl import ch.srgssr.pillarbox.core.business.integrationlayer.service.MediaCompositionService import ch.srgssr.pillarbox.core.business.tracker.SRGEventLoggerTracker import ch.srgssr.pillarbox.core.business.tracker.commandersact.CommandersActTracker @@ -112,8 +112,9 @@ class SRGAssetLoader internal constructor( override fun canLoadAsset(mediaItem: MediaItem): Boolean { val localConfiguration = mediaItem.localConfiguration ?: return false - - return localConfiguration.mimeType == MimeTypeSrg || localConfiguration.uri.lastPathSegment.isValidMediaUrn() + return localConfiguration.mimeType == MimeTypeSrg && runCatching { + localConfiguration.uri.toIlUrl() + }.isSuccess } override suspend fun loadAsset(mediaItem: MediaItem): Asset { diff --git a/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/SRGAssetLoaderTest.kt b/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/SRGAssetLoaderTest.kt index 995ff0501..63a9c505a 100644 --- a/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/SRGAssetLoaderTest.kt +++ b/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/SRGAssetLoaderTest.kt @@ -23,6 +23,7 @@ import ch.srgssr.pillarbox.core.business.integrationlayer.data.Segment import ch.srgssr.pillarbox.core.business.integrationlayer.data.TimeInterval import ch.srgssr.pillarbox.core.business.integrationlayer.data.TimeIntervalType import ch.srgssr.pillarbox.core.business.integrationlayer.service.MediaCompositionService +import ch.srgssr.pillarbox.core.business.source.MimeTypeSrg import ch.srgssr.pillarbox.core.business.source.SRGAssetLoader import ch.srgssr.pillarbox.core.business.source.SegmentAdapter import ch.srgssr.pillarbox.core.business.source.TimeIntervalAdapter @@ -51,6 +52,28 @@ class SRGAssetLoaderTest { } } + @Test + fun testCanLoadAsset() { + assertTrue(assetLoader.canLoadAsset(SRGMediaItem("urn:rts:video:123"))) + } + + @Test + fun testCanLoadAsset_emptyMediaItem() { + assertFalse(assetLoader.canLoadAsset(MediaItem.EMPTY)) + } + + @Test + fun testCanLoadAsset_invalidUri() { + assertFalse( + assetLoader.canLoadAsset( + MediaItem.Builder() + .setMimeType(MimeTypeSrg) + .setUri("https://fake.il/mediaComposition/urn:rts:video:123/path") + .build() + ) + ) + } + @Test(expected = IllegalStateException::class) fun testNoMediaId() = runTest { assetLoader.loadAsset(MediaItem.Builder().build()) diff --git a/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/SRGMediaItemBuilderTest.kt b/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/SRGMediaItemBuilderTest.kt index cb3cac6db..56e1708bc 100644 --- a/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/SRGMediaItemBuilderTest.kt +++ b/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/SRGMediaItemBuilderTest.kt @@ -14,7 +14,6 @@ import ch.srgssr.pillarbox.core.business.integrationlayer.service.IlLocation import ch.srgssr.pillarbox.core.business.integrationlayer.service.Vector import ch.srgssr.pillarbox.core.business.source.MimeTypeSrg import org.junit.runner.RunWith -import java.net.URL import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -105,15 +104,14 @@ class SRGMediaItemBuilderTest { @Test fun `Check uri from existing MediaItem`() { val urn = "urn:rts:audio:3262363" - val ilHost = IlHost.STAGE val inputMediaItem = MediaItem.Builder() - .setUri("${ilHost}integrationlayer/2.1/mediaComposition/byUrn/$urn?vector=${Vector.TV}") + .setUri("https://il-stage.srgssr.ch/integrationlayer/2.1/mediaComposition/byUrn/$urn?vector=${Vector.TV}") .build() val mediaItem = SRGMediaItemBuilder(inputMediaItem).build() val localConfiguration = mediaItem.localConfiguration assertNotNull(localConfiguration) - assertEquals(urn.toIlUri(ilHost, vector = Vector.TV), localConfiguration.uri) + assertEquals(urn.toIlUri(IlHost.STAGE, vector = Vector.TV), localConfiguration.uri) assertEquals(MimeTypeSrg, localConfiguration.mimeType) assertEquals(urn, mediaItem.mediaId) assertEquals(MediaMetadata.EMPTY, mediaItem.mediaMetadata) @@ -164,7 +162,7 @@ class SRGMediaItemBuilderTest { val ilHost = IlHost.STAGE val forceSAM = true val inputMediaItem = MediaItem.Builder() - .setUri("${IlHost.PROD}sam/integrationlayer/2.1/mediaComposition/byUrn/$urn?forceSAM=true") + .setUri("https://il-stage.srgssr.ch/sam/integrationlayer/2.1/mediaComposition/byUrn/$urn?forceSAM=true") .build() val mediaItem = SRGMediaItemBuilder(inputMediaItem).apply { host(ilHost) @@ -199,7 +197,7 @@ class SRGMediaItemBuilderTest { companion object { fun String.toIlUri( - host: URL = IlHost.DEFAULT, + host: IlHost = IlHost.PROD, vector: Vector = Vector.MOBILE, forceSAM: Boolean = false, ilLocation: IlLocation? = null, @@ -214,7 +212,7 @@ class SRGMediaItemBuilderTest { "$name=$value" } - return "${host}${samPath}integrationlayer/2.1/mediaComposition/byUrn/$this?$queryParameters".toUri() + return "${host.baseHostUrl}/${samPath}integrationlayer/2.1/mediaComposition/byUrn/$this?$queryParameters".toUri() } } } diff --git a/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/integrationlayer/IlHostTest.kt b/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/integrationlayer/IlHostTest.kt new file mode 100644 index 000000000..6bb3dcccb --- /dev/null +++ b/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/integrationlayer/IlHostTest.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) SRG SSR. All rights reserved. + * License information is available from the LICENSE file. + */ +package ch.srgssr.pillarbox.core.business.integrationlayer + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import ch.srgssr.pillarbox.core.business.integrationlayer.service.IlHost +import org.junit.runner.RunWith +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +@RunWith(AndroidJUnit4::class) +class IlHostTest { + + @Test + fun `parse prod IlHost`() { + val host = IlHost.parse("https://il.srgssr.ch/somePath/andPath/?withParameters=45") + assertEquals(IlHost.PROD, host) + } + + @Test + fun `parse test IlHost`() { + val host = IlHost.parse("https://il-test.srgssr.ch/somePath/andPath/?withParameters=45") + assertEquals(IlHost.TEST, host) + } + + @Test + fun `parse stage IlHost`() { + val host = IlHost.parse("https://il-stage.srgssr.ch/somePath/andPath/?withParameters=45") + assertEquals(IlHost.STAGE, host) + } + + @Test + fun `Check null if invalid hostname`() { + assertNull(IlHost.parse("https://il-foo.srgssr.ch/somePath/andPath/?withParameters=45")) + assertNull(IlHost.parse("https://www.google.com/search?q=45")) + } +} diff --git a/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/integrationlayer/IlUrlTest.kt b/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/integrationlayer/IlUrlTest.kt new file mode 100644 index 000000000..d63ca0726 --- /dev/null +++ b/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/integrationlayer/IlUrlTest.kt @@ -0,0 +1,84 @@ +/* + * Copyright (c) SRG SSR. All rights reserved. + * License information is available from the LICENSE file. + */ +package ch.srgssr.pillarbox.core.business.integrationlayer + +import android.net.Uri +import androidx.test.ext.junit.runners.AndroidJUnit4 +import ch.srgssr.pillarbox.core.business.integrationlayer.service.IlHost +import ch.srgssr.pillarbox.core.business.integrationlayer.service.IlLocation +import ch.srgssr.pillarbox.core.business.integrationlayer.service.IlUrl +import ch.srgssr.pillarbox.core.business.integrationlayer.service.IlUrl.Companion.toIlUrl +import ch.srgssr.pillarbox.core.business.integrationlayer.service.Vector +import org.junit.runner.RunWith +import kotlin.test.Test +import kotlin.test.assertEquals + +@RunWith(AndroidJUnit4::class) +class IlUrlTest { + + @Test(expected = IllegalArgumentException::class) + fun `toIlUrl throws an error when not an url`() { + Uri.parse("yolo").toIlUrl() + } + + @Test(expected = IllegalArgumentException::class) + fun `toIlUrl throws an error with invalid il host name`() { + Uri.parse("https://il-foo.srg.ch/media/ByUrn/urn:rts:video:123").toIlUrl() + } + + @Test(expected = IllegalArgumentException::class) + fun `toIlUrl throws an error with invalid urn`() { + Uri.parse("${IlHost.PROD.baseHostUrl}/integrationlayer/2.1/mediaComposition/byUrn/").toIlUrl() + } + + @Test + fun `toIlUrl correctly filled`() { + val host = IlHost.PROD + val urn = "urn:rts:video:1234" + val vector = Vector.TV + val ilLocation = IlLocation.WW + val uri = Uri.parse("${host.baseHostUrl}/sam/integrationlayer/2.1/mediaComposition/byUrn/$urn?vector=$vector&forceLocation=$ilLocation") + val expected = IlUrl(host = host, urn = urn, vector = vector, forceSAM = true, ilLocation = ilLocation) + assertEquals(expected, uri.toIlUrl()) + } + + @Test(expected = IllegalArgumentException::class) + fun `create IlUrl with invalid urn`() { + IlUrl(host = IlHost.PROD, urn = "urn:invalid:1234", vector = Vector.MOBILE) + } + + @Test + fun `ILUrl create correct uri with default parameters`() { + val host = IlHost.PROD + val urn = "urn:rts:video:1234" + val vector = Vector.MOBILE + val uri = Uri.parse("${host.baseHostUrl}/integrationlayer/2.1/mediaComposition/byUrn/$urn?vector=$vector&onlyChapters=true") + val ilUrl = IlUrl(host = host, urn = urn, vector = vector) + assertEquals(uri, ilUrl.uri) + } + + @Test + fun `ILUrl create correct uri with forceSAM`() { + val host = IlHost.PROD + val urn = "urn:rts:video:1234" + val vector = Vector.MOBILE + val uri = Uri.parse("${host.baseHostUrl}/sam/integrationlayer/2.1/mediaComposition/byUrn/$urn?forceSAM=true&vector=$vector&onlyChapters=true") + val ilUrl = IlUrl(host = host, urn = urn, vector = vector, forceSAM = true) + assertEquals(uri, ilUrl.uri) + } + + @Test + fun `ILUrl create correct uri with ilLocation`() { + val host = IlHost.PROD + val urn = "urn:rts:video:1234" + val vector = Vector.MOBILE + val ilLocation = IlLocation.WW + val uri = Uri.parse( + "${host.baseHostUrl}/integrationlayer/2.1/mediaComposition/byUrn/$urn?forceLocation=$ilLocation&vector=$vector&onlyChapters=true" + ) + val ilUrl = IlUrl(host = host, urn = urn, vector = vector, ilLocation = ilLocation) + assertEquals(uri, ilUrl.uri) + } +} diff --git a/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/integrationlayer/IlUrnTest.kt b/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/integrationlayer/IlUrnTest.kt new file mode 100644 index 000000000..72937cf0a --- /dev/null +++ b/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/integrationlayer/IlUrnTest.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) SRG SSR. All rights reserved. + * License information is available from the LICENSE file. + */ +package ch.srgssr.pillarbox.core.business.integrationlayer + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import ch.srgssr.pillarbox.core.business.integrationlayer.service.IlHost +import ch.srgssr.pillarbox.core.business.integrationlayer.service.IlUrl +import ch.srgssr.pillarbox.core.business.integrationlayer.service.IlUrl.Companion.toIlUrl +import ch.srgssr.pillarbox.core.business.integrationlayer.service.Vector +import org.junit.runner.RunWith +import kotlin.test.Test +import kotlin.test.assertEquals + +@RunWith(AndroidJUnit4::class) +class IlUrnTest { + @Test + fun testInOut() { + val ilUrn = IlUrl(host = IlHost.PROD, urn = "urn:rts:video:12345", vector = Vector.MOBILE) + assertEquals(ilUrn, ilUrn.uri.toIlUrl()) + } +} diff --git a/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/integrationlayer/ImageScalingServiceTest.kt b/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/integrationlayer/ImageScalingServiceTest.kt index ca2210834..5adf07bab 100644 --- a/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/integrationlayer/ImageScalingServiceTest.kt +++ b/pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/integrationlayer/ImageScalingServiceTest.kt @@ -12,37 +12,37 @@ import kotlin.test.assertEquals class ImageScalingServiceTest { @Test fun getScaledImageUrlProd() { - val baseUrl = IlHost.PROD + val host = IlHost.PROD val imageUrl = "https://www.rts.ch/2020/05/18/14/20/11333286.image/16x9" - val imageScalingService = ImageScalingService(baseUrl) + val imageScalingService = ImageScalingService(host) val scaledImageUrl = imageScalingService.getScaledImageUrl(imageUrl) val encodedImageUrl = URLEncoder.encode(imageUrl, Charsets.UTF_8) - assertEquals("${baseUrl}images/?imageUrl=$encodedImageUrl&format=webp&width=480", scaledImageUrl) + assertEquals("${host.baseHostUrl}/images/?imageUrl=$encodedImageUrl&format=webp&width=480", scaledImageUrl) } @Test fun getScaledImageUrlTest() { - val baseUrl = IlHost.TEST + val host = IlHost.TEST val imageUrl = "https://www.rts.ch/2021/08/05/18/12/12396566.image/2x3" - val imageScalingService = ImageScalingService(baseUrl) + val imageScalingService = ImageScalingService(host) val scaledImageUrl = imageScalingService.getScaledImageUrl(imageUrl) val encodedImageUrl = URLEncoder.encode(imageUrl, Charsets.UTF_8) - assertEquals("${baseUrl}images/?imageUrl=$encodedImageUrl&format=webp&width=480", scaledImageUrl) + assertEquals("${host.baseHostUrl}/images/?imageUrl=$encodedImageUrl&format=webp&width=480", scaledImageUrl) } @Test fun getScaledImageUrlStage() { - val baseUrl = IlHost.STAGE + val host = IlHost.STAGE val imageUrl = "https://www.rts.ch/2022/10/06/17/32/13444418.image/4x5" - val imageScalingService = ImageScalingService(baseUrl) + val imageScalingService = ImageScalingService(host) val scaledImageUrl = imageScalingService.getScaledImageUrl(imageUrl) val encodedImageUrl = URLEncoder.encode(imageUrl, Charsets.UTF_8) - assertEquals("${baseUrl}images/?imageUrl=$encodedImageUrl&format=webp&width=480", scaledImageUrl) + assertEquals("${host.baseHostUrl}/images/?imageUrl=$encodedImageUrl&format=webp&width=480", scaledImageUrl) } } diff --git a/pillarbox-demo-shared/src/main/java/ch/srgssr/pillarbox/demo/shared/data/DemoItem.kt b/pillarbox-demo-shared/src/main/java/ch/srgssr/pillarbox/demo/shared/data/DemoItem.kt index a7c1593f2..4bd79a6ae 100644 --- a/pillarbox-demo-shared/src/main/java/ch/srgssr/pillarbox/demo/shared/data/DemoItem.kt +++ b/pillarbox-demo-shared/src/main/java/ch/srgssr/pillarbox/demo/shared/data/DemoItem.kt @@ -89,7 +89,7 @@ sealed class DemoItem( override val description: String? = null, override val imageUri: String? = null, override val languageTag: String? = null, - val host: java.net.URL = IlHost.PROD, + val host: IlHost = IlHost.PROD, val forceSAM: Boolean = false, val ilLocation: IlLocation? = null, ) : DemoItem(urn, title, description, imageUri, languageTag) { diff --git a/pillarbox-demo-shared/src/main/java/ch/srgssr/pillarbox/demo/shared/di/PlayerModule.kt b/pillarbox-demo-shared/src/main/java/ch/srgssr/pillarbox/demo/shared/di/PlayerModule.kt index 18bd2e10c..91e97de8b 100644 --- a/pillarbox-demo-shared/src/main/java/ch/srgssr/pillarbox/demo/shared/di/PlayerModule.kt +++ b/pillarbox-demo-shared/src/main/java/ch/srgssr/pillarbox/demo/shared/di/PlayerModule.kt @@ -18,7 +18,6 @@ import ch.srgssr.pillarbox.player.PillarboxExoPlayer import ch.srgssr.pillarbox.player.PreloadConfiguration import okhttp3.Interceptor import okhttp3.Response -import java.net.URL import kotlin.time.Duration.Companion.seconds import ch.srg.dataProvider.integrationlayer.request.IlHost as DataProviderIlHost @@ -42,7 +41,7 @@ object PlayerModule { */ fun createIlRepository( context: Context, - ilHost: URL = IlHost.DEFAULT, + ilHost: IlHost = IlHost.PROD, forceSAM: Boolean = false, ilLocation: IlLocation? = null, ): ILRepository { @@ -55,7 +54,7 @@ object PlayerModule { return ILRepository(dataProviderPaging = DataProviderPaging(ilService), ilService = ilService) } - private fun URL.toDataProviderIlHost(forceSAM: Boolean): DataProviderIlHost { + private fun IlHost.toDataProviderIlHost(forceSAM: Boolean): DataProviderIlHost { return when (this) { IlHost.PROD -> if (forceSAM) DataProviderIlHost.PROD_SAM else DataProviderIlHost.PROD IlHost.STAGE -> if (forceSAM) DataProviderIlHost.STAGE_SAM else DataProviderIlHost.STAGE diff --git a/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/MainNavigation.kt b/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/MainNavigation.kt index 274b6cb75..658fbb8a7 100644 --- a/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/MainNavigation.kt +++ b/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/MainNavigation.kt @@ -76,7 +76,6 @@ import ch.srgssr.pillarbox.demo.ui.settings.AppSettingsView import ch.srgssr.pillarbox.demo.ui.showcases.showcasesNavGraph import ch.srgssr.pillarbox.demo.ui.theme.PillarboxTheme import ch.srgssr.pillarbox.demo.ui.theme.paddings -import java.net.URL private val bottomNavItems = listOf(HomeDestination.Examples, HomeDestination.Showcases, HomeDestination.Lists, HomeDestination.Search, HomeDestination.Settings) @@ -99,7 +98,7 @@ fun MainNavigation() { val navBackStackEntry by navController.currentBackStackEntryAsState() val currentDestination = navBackStackEntry?.destination - var ilHost by remember { mutableStateOf(IlHost.DEFAULT) } + var ilHost by remember { mutableStateOf(IlHost.PROD) } var forceSAM by remember { mutableStateOf(false) } var ilLocation by remember { mutableStateOf(null) } @@ -192,10 +191,10 @@ fun MainNavigation() { @Composable private fun ListsMenu( - currentServer: URL, + currentServer: IlHost, currentForceSAM: Boolean, currentLocation: IlLocation?, - onServerSelected: (server: URL, forceSAM: Boolean, location: IlLocation?) -> Unit + onServerSelected: (server: IlHost, forceSAM: Boolean, location: IlLocation?) -> Unit ) { val context = LocalContext.current val servers = remember { getServers(context).groupBy { it.serverName }.values } @@ -323,7 +322,7 @@ internal fun getServers(context: Context): List { internal data class EnvironmentConfig( val name: String, val serverName: String, - val host: URL, + val host: IlHost, val forceSAM: Boolean = false, val location: IlLocation? = null, ) { diff --git a/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/ui/lists/ListsHome.kt b/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/ui/lists/ListsHome.kt index 7ffd42416..6cc06f27a 100644 --- a/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/ui/lists/ListsHome.kt +++ b/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/ui/lists/ListsHome.kt @@ -25,6 +25,7 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.toRoute import androidx.paging.compose.collectAsLazyPagingItems +import ch.srgssr.pillarbox.core.business.integrationlayer.service.IlHost import ch.srgssr.pillarbox.core.business.integrationlayer.service.IlLocation import ch.srgssr.pillarbox.demo.DemoPageView import ch.srgssr.pillarbox.demo.composable @@ -41,7 +42,6 @@ import ch.srgssr.pillarbox.demo.ui.components.DemoListSectionView import ch.srgssr.pillarbox.demo.ui.player.SimplePlayerActivity import ch.srgssr.pillarbox.demo.ui.theme.PillarboxTheme import ch.srgssr.pillarbox.demo.ui.theme.paddings -import java.net.URL private val defaultListsLevels = listOf("app", "pillarbox", "lists") @@ -51,7 +51,7 @@ private val defaultListsLevels = listOf("app", "pillarbox", "lists") fun NavGraphBuilder.listsNavGraph( navController: NavController, ilRepository: ILRepository, - ilHost: URL, + ilHost: IlHost, forceSAM: Boolean, ilLocation: IlLocation?, ) {