Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Ktor dependency #829

Merged
merged 10 commits into from
Dec 16, 2024
1 change: 0 additions & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,3 @@ updates:
- dependency-name: "com.comscore:*"
- dependency-name: "com.google.guava:*" # Guava is updated together with AndroidX Media3
- dependency-name: "com.tagcommander.lib:*"
- dependency-name: "io.ktor:*" # We don't want to update to Ktor 3 yet
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,6 @@ class PillarboxAndroidLibraryPublishingPlugin : Plugin<Project> {
packageListUrl.set(URI("https://kotlinlang.org/api/kotlinx.serialization/package-list"))
}

// TODO Enable this once the following issue is fixed: https://github.com/Kotlin/dokka/issues/3889
// externalDocumentationLinks.register("ktor") {
// url.set(URI("https://api.ktor.io"))
// packageListUrl.set(URI("https://api.ktor.io/package-list"))
// }

// This is currently broken in Dokka for Android modules. See: https://github.com/Kotlin/dokka/issues/2876
sourceLink {
val version = VersionConfig().versionName(default = name)
Expand Down
8 changes: 0 additions & 8 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ kotlinx-coroutines = "1.9.0"
kotlinx-datetime = "0.6.1"
kotlinx-kover = "0.8.3"
kotlinx-serialization = "1.7.3"
ktor = "2.3.13"
mockk = "1.13.13"
okhttp = "4.12.0"
robolectric = "4.14.1"
Expand Down Expand Up @@ -78,13 +77,6 @@ kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.
kotlinx-kover-gradle = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kotlinx-kover" }
kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinx-serialization" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
ktor-http = { module = "io.ktor:ktor-http", version.ref = "ktor" }
ktor-serialization = { module = "io.ktor:ktor-serialization", version.ref = "ktor" }
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
ktor-utils = { module = "io.ktor:ktor-utils", version.ref = "ktor" }
robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" }
robolectric-annotations = { module = "org.robolectric:annotations", version.ref = "robolectric" }
robolectric-shadows-framework = { module = "org.robolectric:shadows-framework", version.ref = "robolectric" }
Expand Down
5 changes: 0 additions & 5 deletions pillarbox-core-business/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,6 @@ dependencies {
api(libs.kotlinx.datetime)
api(libs.kotlinx.serialization.core)
implementation(libs.kotlinx.serialization.json)
api(libs.ktor.client.core)
implementation(libs.ktor.client.okhttp)
implementation(libs.ktor.http)
implementation(libs.ktor.serialization.kotlinx.json)
implementation(libs.ktor.utils)
implementation(libs.okhttp)
api(libs.tagcommander.core)

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ class SRGErrorMessageProvider(private val context: Context) : ErrorMessageProvid
Pair.create(0, context.getString(R.string.invalidDataError))
}

is HttpResultException -> {
Pair.create(0, cause.message)
}

is DataSourceException -> {
Pair.create(cause.reason, cause.message)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import androidx.media3.datasource.DataSource
import androidx.media3.datasource.DataSpec
import androidx.media3.datasource.DefaultHttpDataSource
import ch.srgssr.pillarbox.player.utils.DebugLogger
import kotlinx.coroutines.runBlocking

/**
* A [DataSource] that injects an Akamai token into URLs containing the query parameter `withToken=true`.
Expand All @@ -24,9 +23,7 @@ class AkamaiTokenDataSource private constructor(
if (hasNeedAkamaiToken(outputUri)) {
DebugLogger.debug("Akamai", "open ${dataSpec.uri}")
val cleanUri = removeTokenQueryParameter(outputUri)
outputUri = runBlocking {
tokenProvider.tokenizeUri(cleanUri)
}
outputUri = tokenProvider.tokenizeUri(cleanUri)
return dataSource.open(dataSpec.buildUpon().setUri(outputUri).build())
}
return dataSource.open(dataSpec)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,15 @@ package ch.srgssr.pillarbox.core.business.akamai

import android.net.Uri
import android.net.UrlQuerySanitizer
import ch.srgssr.pillarbox.player.network.PillarboxHttpClient
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.client.request.parameter
import io.ktor.http.appendEncodedPathSegments
import ch.srgssr.pillarbox.player.network.RequestSender.send
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import okhttp3.Request

/**
* The [AkamaiTokenProvider] is responsible for fetching an Akamai token from `TOKEN_SERVICE_URL` and appending it to URIs.
*
* @param httpClient The HTTP client used to make requests to the token service. Defaults to a [PillarboxHttpClient] instance.
*/
class AkamaiTokenProvider(private val httpClient: HttpClient = PillarboxHttpClient()) {
MGaetan89 marked this conversation as resolved.
Show resolved Hide resolved
class AkamaiTokenProvider {

/**
* Requests and appends an Akamai token to the provided URI.
Expand All @@ -30,7 +24,7 @@ class AkamaiTokenProvider(private val httpClient: HttpClient = PillarboxHttpClie
* @param uri The URI to be tokenized.
* @return The tokenized [Uri] if successful, otherwise the original [uri].
*/
suspend fun tokenizeUri(uri: Uri): Uri {
fun tokenizeUri(uri: Uri): Uri {
val acl = getAcl(uri)
val token = acl?.let {
val tokenResult = getToken(it)
Expand All @@ -39,15 +33,12 @@ class AkamaiTokenProvider(private val httpClient: HttpClient = PillarboxHttpClie
return token?.let { appendTokenToUri(uri, it) } ?: uri
}

private suspend fun getToken(acl: String): Result<Token> {
return runCatching {
httpClient.get(TOKEN_SERVICE_URL) {
url {
appendEncodedPathSegments("akahd/token")
parameter("acl", acl)
}
}.body<TokenResponse>().token
}
private fun getToken(acl: String): Result<Token> {
return Request.Builder()
.url(TOKEN_SERVICE_URL.format(acl))
.build()
.send<TokenResponse>()
.map { it.token }
}

/**
Expand Down Expand Up @@ -76,7 +67,7 @@ class AkamaiTokenProvider(private val httpClient: HttpClient = PillarboxHttpClie
internal data class TokenResponse(val token: Token)

internal companion object {
private const val TOKEN_SERVICE_URL = "https://tp.srgssr.ch/"
private const val TOKEN_SERVICE_URL = "https://tp.srgssr.ch/akahd/token?acl=%s"

internal fun getAcl(uri: Uri): String? {
val path = uri.path
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
package ch.srgssr.pillarbox.core.business.integrationlayer

import ch.srgssr.pillarbox.core.business.integrationlayer.service.IlHost
import io.ktor.http.URLBuilder
import io.ktor.http.appendEncodedPathSegments
import java.net.URL
import java.net.URLEncoder

/**
* Service used to get a scaled image URL. This only works for SRG images.
Expand All @@ -21,14 +20,8 @@ internal class ImageScalingService(
fun getScaledImageUrl(
imageUrl: String,
): String {
return URLBuilder(baseUrl.toString())
.appendEncodedPathSegments("images/")
.apply {
parameters.append("imageUrl", imageUrl)
parameters.append("format", "webp")
parameters.append("width", "480")
}
.build()
.toString()
val encodedImageUrl = URLEncoder.encode(imageUrl, Charsets.UTF_8.name())

return "${baseUrl}images/?imageUrl=$encodedImageUrl&format=webp&width=480"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,18 @@ package ch.srgssr.pillarbox.core.business.integrationlayer.service

import android.net.Uri
import ch.srgssr.pillarbox.core.business.integrationlayer.data.MediaComposition
import ch.srgssr.pillarbox.player.network.PillarboxHttpClient
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
import java.net.URL
import ch.srgssr.pillarbox.player.network.RequestSender.send
import okhttp3.Request

/**
* A service for fetching a [MediaComposition] over HTTP.
*
* @param httpClient The Ktor [HttpClient] instance used for making HTTP requests.
*/
class HttpMediaCompositionService(
private val httpClient: HttpClient = PillarboxHttpClient(),
MGaetan89 marked this conversation as resolved.
Show resolved Hide resolved
) : MediaCompositionService {
class HttpMediaCompositionService : MediaCompositionService {

override suspend fun fetchMediaComposition(uri: Uri): Result<MediaComposition> {
return runCatching {
httpClient.get(URL(uri.toString()))
.body()
}
return Request.Builder()
.url(uri.toString())
.build()
.send()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import androidx.media3.common.MimeTypes
import androidx.media3.datasource.DataSource.Factory
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
import androidx.media3.exoplayer.source.MergingMediaSource
import ch.srgssr.pillarbox.core.business.HttpResultException
import ch.srgssr.pillarbox.core.business.akamai.AkamaiTokenDataSource
import ch.srgssr.pillarbox.core.business.akamai.AkamaiTokenProvider
import ch.srgssr.pillarbox.core.business.exception.DataParsingException
Expand All @@ -35,7 +34,6 @@ import ch.srgssr.pillarbox.player.asset.AssetLoader
import ch.srgssr.pillarbox.player.tracker.FactoryData
import ch.srgssr.pillarbox.player.tracker.MediaItemTracker
import ch.srgssr.pillarbox.player.tracker.MutableMediaItemTrackerData
import io.ktor.client.plugins.ClientRequestException
import kotlinx.serialization.SerializationException
import java.io.IOException

Expand Down Expand Up @@ -121,17 +119,8 @@ class SRGAssetLoader internal constructor(
checkNotNull(mediaItem.localConfiguration)
val result = mediaCompositionService.fetchMediaComposition(mediaItem.localConfiguration!!.uri).getOrElse {
when (it) {
is ClientRequestException -> {
throw HttpResultException(it)
}

is SerializationException -> {
throw DataParsingException(it)
}

else -> {
throw IOException(it.message)
}
is SerializationException -> throw DataParsingException(it)
else -> throw IOException(it.message)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import ch.srgssr.pillarbox.player.PillarboxDsl
import ch.srgssr.pillarbox.player.network.PillarboxOkHttp
import ch.srgssr.pillarbox.player.tracker.MediaItemTracker
import ch.srgssr.pillarbox.player.tracker.MutableMediaItemTrackerData
import io.ktor.client.HttpClient
import kotlinx.coroutines.Dispatchers

/**
Expand Down Expand Up @@ -83,18 +82,6 @@ class SRGAssetLoaderConfig internal constructor(context: Context) {
this.mediaCompositionService = mediaCompositionService
}

/**
* Sets the HTTP client used by the [MediaCompositionService] and [AkamaiTokenProvider].
*
* Note that this will override any existing [MediaCompositionService] set using [mediaCompositionService].
*
* @param httpClient The HTTP client.
*/
fun httpClient(httpClient: HttpClient) {
mediaCompositionService = HttpMediaCompositionService(httpClient)
akamaiTokenProvider = AkamaiTokenProvider(httpClient)
}

/**
* Configures a block to inject custom data into the [MutableMediaItemTrackerData] used for tracking media playback.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,6 @@ class SRGErrorMessageProviderTest {
assertEquals(context.getString(R.string.invalidDataError), errorMessage)
}

@Test
fun `getErrorMessage HttpResultException`() {
val exception = HttpResultException("HTTP request failed")
val (errorCode, errorMessage) = errorMessageProvider.getErrorMessage(playbackException(exception))

assertEquals(0, errorCode)
assertEquals(exception.message, errorMessage)
}

@Test
fun `getErrorMessage DataSourceException`() {
val exception = DataSourceException(PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ package ch.srgssr.pillarbox.core.business
import ch.srgssr.pillarbox.core.business.integrationlayer.data.BlockReason
import ch.srgssr.pillarbox.core.business.integrationlayer.data.Chapter
import ch.srgssr.pillarbox.core.business.integrationlayer.data.MediaComposition
import ch.srgssr.pillarbox.player.network.PillarboxHttpClient.jsonSerializer
import ch.srgssr.pillarbox.player.network.jsonSerializer
import kotlinx.serialization.SerializationException
import kotlin.test.Test
import kotlin.test.assertEquals
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import android.net.Uri
import ch.srgssr.pillarbox.core.business.integrationlayer.data.MediaComposition
import ch.srgssr.pillarbox.core.business.integrationlayer.service.HttpMediaCompositionService
import ch.srgssr.pillarbox.core.business.integrationlayer.service.MediaCompositionService
import ch.srgssr.pillarbox.player.network.PillarboxHttpClient
import ch.srgssr.pillarbox.player.network.jsonSerializer

internal class LocalMediaCompositionWithFallbackService(
context: Context,
Expand All @@ -19,7 +19,7 @@ internal class LocalMediaCompositionWithFallbackService(

init {
val json = context.assets.open("media-compositions.json").bufferedReader().use { it.readText() }
mediaCompositions = PillarboxHttpClient.jsonSerializer.decodeFromString(json)
mediaCompositions = jsonSerializer.decodeFromString(json)
}

override suspend fun fetchMediaComposition(uri: Uri): Result<MediaComposition> {
Expand Down
1 change: 0 additions & 1 deletion pillarbox-demo/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ dependencies {
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.datetime)
implementation(libs.kotlinx.serialization.core)
implementation(libs.ktor.client.okhttp)
implementation(libs.okhttp)
implementation(libs.srg.data)
implementation(libs.srg.dataprovider.retrofit)
Expand Down
7 changes: 0 additions & 7 deletions pillarbox-player/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,6 @@ dependencies {
api(libs.kotlinx.coroutines.core)
api(libs.kotlinx.serialization.core)
implementation(libs.kotlinx.serialization.json)
implementation(libs.ktor.client.content.negotiation)
api(libs.ktor.client.core)
implementation(libs.ktor.client.okhttp)
implementation(libs.ktor.http)
implementation(libs.ktor.serialization)
implementation(libs.ktor.serialization.kotlinx.json)
implementation(libs.ktor.utils)
api(libs.okhttp)
implementation(libs.okhttp.logging.interceptor)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import ch.srgssr.pillarbox.player.monitoring.NoOp
import ch.srgssr.pillarbox.player.monitoring.Remote
import ch.srgssr.pillarbox.player.monitoring.Remote.config
import ch.srgssr.pillarbox.player.source.PillarboxMediaSourceFactory
import io.ktor.client.HttpClient
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlin.coroutines.CoroutineContext
Expand Down Expand Up @@ -147,16 +146,14 @@ abstract class PillarboxBuilder {
* Configures the monitoring to send all events to a remote server.
*
* @param endpointUrl The URL of the endpoint responsible for receiving monitoring messages.
* @param httpClient The [HttpClient] instance used for transmitting events to the endpoint.
* @param coroutineScope The [CoroutineScope] which manages the coroutine responsible for sending monitoring messages.
*/
fun monitoring(
endpointUrl: String,
httpClient: HttpClient? = null,
coroutineScope: CoroutineScope? = null,
) {
monitoring(Remote) {
config(endpointUrl = endpointUrl, httpClient = httpClient, coroutineScope = coroutineScope)
config(endpointUrl = endpointUrl, coroutineScope = coroutineScope)
}
}

Expand Down
Loading
Loading