Skip to content

Commit

Permalink
Update documentation in pillarbox-core-business (#776)
Browse files Browse the repository at this point in the history
Co-authored-by: Samuel Défago <[email protected]>
  • Loading branch information
MGaetan89 and defagos authored Nov 11, 2024
1 parent 80bfe0e commit 1f22bb5
Show file tree
Hide file tree
Showing 34 changed files with 508 additions and 305 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,17 @@ class PillarboxAndroidLibraryPublishingPlugin : Plugin<Project> {
includes.from("docs/README.md")
}

externalDocumentationLinks.register("kotlinx.serialization") {
url.set(URI("https://kotlinlang.org/api/kotlinx.serialization"))
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
19 changes: 0 additions & 19 deletions pillarbox-core-business/Module.md

This file was deleted.

118 changes: 61 additions & 57 deletions pillarbox-core-business/docs/README.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,30 @@
[![Pillarbox logo](https://github.com/SRGSSR/pillarbox-apple/blob/main/docs/README-images/logo.jpg)](https://github.com/SRGSSR/pillarbox-android)
[![Last release](https://img.shields.io/github/v/release/SRGSSR/pillarbox-android?label=Release)](https://github.com/SRGSSR/pillarbox-android/releases)
[![Android min SDK](https://img.shields.io/badge/Android-21%2B-34A853)](https://github.com/SRGSSR/pillarbox-android)
[![License](https://img.shields.io/github/license/SRGSSR/pillarbox-android?label=License)](https://github.com/SRGSSR/pillarbox-android/blob/main/LICENSE)
# Module pillarbox-core-business

# Pillarbox Core Business module
Provides a [MediaSource][androidx.media3.exoplayer.source.MediaSource] for handling SRG SSR media URNs to Pillarbox. It basically converts an
integration layer [MediaComposition][ch.srgssr.pillarbox.core.business.integrationlayer.data.MediaComposition] to a playable
[MediaSource][androidx.media3.exoplayer.source.MediaSource].

Provides a custom [`MediaSource`][media-source-documentation] to Pillarbox, suited for handling SRG SSR media URN. It basically converts a
[`MediaComposition`][media-composition-source] from the integration layer to a playable [`MediaSource`][media-source-documentation].
The supported contents are:

Supported contents are :

- On demand Video and Audio.
- Live streams, with and without DVR.
- On demand video and audio.
- Live streams, with and without DRM.
- Token-protected content.
- DRM protected content.
- 360° content (see [`SphericalSurfaceShowcase`][spherical-surface-showcase]).
- 360° content (see [SphericalSurfaceShowcase][spherical-surface-showcase]).

## Integration

```gradle
To use this module, add the following dependency to your project's `build.gradle`/`build.gradle.kts` file:

```kotlin
implementation("ch.srgssr.pillarbox:pillarbox-core-business:<pillarbox_version>")
```

More information can be found in the [top level README](https://github.com/SRGSSR/pillarbox-android#readme).

## Getting started

### Create the player

To play a URN content with [`PillarboxPlayer`][pillarbox-player-source], you have to create it like this:
To play a URN content with [PillarboxPlayer][ch.srgssr.pillarbox.player.PillarboxPlayer], you have to create it like this:

```kotlin
val player = PillarboxExoPlayer(context)
Expand All @@ -40,43 +36,40 @@ player.play()

### Create a `MediaItem` with URN

To tell [`PillarboxPlayer`][pillarbox-player-source] to load a specific [`MediaItem`][media-item-documentation], it has to be created with
[`SRGMediaItem`][srg-media-item-source]:
To tell [PillarboxPlayer][ch.srgssr.pillarbox.player.PillarboxPlayer] to load a specific [MediaItem][androidx.media3.common.MediaItem], it has to be
created with [SRGMediaItem][ch.srgssr.pillarbox.core.business.SRGMediaItem]:

```kotlin
val urn = "urn:rts:video:12345"

// MediaItem created on Prod with Vector.MOBILE
val mediaItem: MediaItem = SRGMediaItem(urn)

// Content on stage
val mediaItemOnStage: MediaItem = SRGMediaItem(urn) {
// Optionally customize the MediaItem
val customMediaItem: MediaItem = SRGMediaItem(urn) {
setHost(IlHost.Stage)
}

// Content with TV Vector
val mediaItemWithVector : MediaItem = SRGMediaItem(urn) {
setVector(Vector.TV)
setVector(context.getVector())
}

// Compute Vector from Context
val vector = context.getVector()
val mediaItemWithVector : MediaItem = SRGMediaItem(urn) {
setVector(vector)
}
// Give the MediaItem to the player so it can be played
player.setMediaItem(mediaItem)
```

### Handle error

All exceptions thrown by [`PillarboxMediaSource`][pillarbox-media-source-source] are caught by the player inside a
[`PlaybackException`][playback-exception-documentation].
All exceptions thrown by [PillarboxMediaSource][ch.srgssr.pillarbox.player.source.PillarboxMediaSource] are caught by the player inside a
[PlaybackException][androidx.media3.common.PlaybackException].

[`PillarboxMediaSource`][pillarbox-media-source-source] can throw:
[PillarboxMediaSource][ch.srgssr.pillarbox.player.source.PillarboxMediaSource] can throw:

- [`BlockReasonException`][block-reason-exception-source] when the chapter has a block reason.
- [`ResourceNotFoundException`][resource-not-found-exception-source] when no "playable" resources are found in the chapter.
- [BlockReasonException][ch.srgssr.pillarbox.core.business.exception.BlockReasonException] when the chapter has a block reason.
- [ResourceNotFoundException][ch.srgssr.pillarbox.core.business.exception.ResourceNotFoundException] when no "playable" resources are found in the
chapter.
- `RemoteResult.Error`.`throwable`:
- `HttpException`
- `IOException`
- Any custom `Exception`
- `HttpException`.
- `IOException`.
- Any custom [Exception][kotlin.Exception].

```kotlin
player.addListener(object : Player.Listener {
Expand All @@ -93,40 +86,51 @@ player.addListener(object : Player.Listener {

## Going further

[`PillarboxMediaSource`][pillarbox-media-source-source] factory can be created with a [`MediaCompositionService`][media-composition-service-source],
which can be used to retrieve a [`MediaComposition`][media-composition-source]. You can create and provide your own implementation:
[PillarboxMediaSource][ch.srgssr.pillarbox.player.source.PillarboxMediaSource] factory can be created with a
[MediaCompositionService][ch.srgssr.pillarbox.core.business.integrationlayer.service.MediaCompositionService], which can be used to retrieve a
[MediaComposition][ch.srgssr.pillarbox.core.business.integrationlayer.data.MediaComposition]. You can create and provide your own implementation:

```kotlin
class CustomMediaCompositionService : MediaCompositionService {
private val mediaCompositionMap = mutableMapOf<Uri, MediaComposition>()
class CachedMediaCompositionService : MediaCompositionService {
private val mediaCompositionCache = mutableMapOf<Uri, MediaComposition>()

override suspend fun fetchMediaComposition(uri: Uri): Result<MediaComposition> {
return mediaCompositionMap[uri]?.let {
Result.success(it)
} ?: Result.failure(IOException("$uri not found"))
if (uri in mediaCompositionCache) {
return Result.success(mediaCompositionCache.getValue(uri))
}

val mediaComposition = fetchMediaCompositionFromBackend(uri)
if (mediaComposition != null) {
mediaCompositionCache[uri] = mediaComposition

return Result.success(mediaComposition)
} else {
return Result.failure(IOException("$uri not found"))
}
}
}
```

Then, pass it to [`PillarboxExoPlayer`][pillarbox-exo-player-source]:
Then, pass it to [PillarboxExoPlayer][ch.srgssr.pillarbox.player.PillarboxExoPlayer]:

```kotlin
val player = PillarboxExoPlayer(context) {
srgAssetLoader(context) {
mediaCompositionService(CustomMediaCompositionService())
mediaCompositionService(CachedMediaCompositionService())
}
}
```

[block-reason-exception-source]: https://github.com/SRGSSR/pillarbox-android/tree/main/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/exception/BlockReasonException.kt
[media-composition-service-source]: https://github.com/SRGSSR/pillarbox-android/tree/main/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/integrationlayer/service/MediaCompositionService.kt
[media-composition-source]: https://github.com/SRGSSR/pillarbox-android/tree/main/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/integrationlayer/data/MediaComposition.kt
[media-item-documentation]: https://developer.android.com/reference/androidx/media3/common/MediaItem
[media-source-documentation]: https://developer.android.com/reference/androidx/media3/exoplayer/source/MediaSource
[pillarbox-exo-player-source]: https://github.com/SRGSSR/pillarbox-android/tree/main/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/PillarboxSRG.kt
[pillarbox-media-source-source]: https://github.com/SRGSSR/pillarbox-android/tree/main/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/source/PillarboxMediaSource.kt
[pillarbox-player-source]: https://github.com/SRGSSR/pillarbox-android/tree/main/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/PillarboxPlayer.kt
[playback-exception-documentation]: https://developer.android.com/reference/androidx/media3/common/PlaybackException
[resource-not-found-exception-source]: https://github.com/SRGSSR/pillarbox-android/tree/main/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/exception/ResourceNotFoundException.kt
[androidx.media3.common.MediaItem]: https://developer.android.com/reference/androidx/media3/common/MediaItem
[androidx.media3.common.PlaybackException]: https://developer.android.com/reference/androidx/media3/common/PlaybackException
[androidx.media3.exoplayer.source.MediaSource]: https://developer.android.com/reference/androidx/media3/exoplayer/source/MediaSource
[ch.srgssr.pillarbox.core.business.exception.BlockReasonException]: https://github.com/SRGSSR/pillarbox-android/tree/main/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/exception/BlockReasonException.kt
[ch.srgssr.pillarbox.core.business.exception.ResourceNotFoundException]: https://github.com/SRGSSR/pillarbox-android/tree/main/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/exception/ResourceNotFoundException.kt
[ch.srgssr.pillarbox.core.business.integrationlayer.data.MediaComposition]: https://android.pillarbox.ch/api/pillarbox-core-business/ch.srgssr.pillarbox.core.business.integrationlayer.data/-media-composition/index.html
[ch.srgssr.pillarbox.core.business.integrationlayer.service.MediaCompositionService]: https://android.pillarbox.ch/api/pillarbox-core-business/ch.srgssr.pillarbox.core.business.integrationlayer.service/-media-composition-service/index.html
[ch.srgssr.pillarbox.core.business.SRGMediaItem]: https://android.pillarbox.ch/api/pillarbox-core-business/ch.srgssr.pillarbox.core.business/-s-r-g-media-item.html
[ch.srgssr.pillarbox.player.PillarboxExoPlayer]: https://android.pillarbox.ch/api/pillarbox-player/ch.srgssr.pillarbox.player/-pillarbox-exo-player/index.html
[ch.srgssr.pillarbox.player.PillarboxPlayer]: https://android.pillarbox.ch/api/pillarbox-player/ch.srgssr.pillarbox.player/-pillarbox-player/index.html
[ch.srgssr.pillarbox.player.source.PillarboxMediaSource]: https://android.pillarbox.ch/api/pillarbox-player/ch.srgssr.pillarbox.player.source/-pillarbox-media-source/index.html
[kotlin.Exception]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-exception/
[spherical-surface-showcase]: https://github.com/SRGSSR/pillarbox-android/tree/main/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/ui/showcases/misc/SphericalSurfaceShowcase.kt
[srg-media-item-source]: https://github.com/SRGSSR/pillarbox-android/tree/main/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/SRGMediaItem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@
*/
package ch.srgssr.pillarbox.core.business

import androidx.media3.common.PlaybackException
import io.ktor.client.plugins.ClientRequestException
import java.io.IOException

/**
* Http result exception
* Represents an exception that occurs during an HTTP request when the server responds with an unsuccessful status code.
*
* @constructor
*
* @param message Message for the IOException, constructor used by PlaybackException to rebuild this exception.
* @param message A descriptive message about the exception. Used by [PlaybackException] to rebuild this exception
*/
class HttpResultException internal constructor(message: String) : IOException(message) {
/**
* Creates a new instance based on a [ClientRequestException].
*
* @param throwable The underlying [ClientRequestException] that triggered this exception.
*/
constructor(throwable: ClientRequestException) : this(
"${throwable.response.status.description} (${throwable.response.status.value})"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,33 @@ import ch.srgssr.pillarbox.player.PlayerConfig
import kotlin.time.Duration.Companion.seconds

/**
* Pillarbox ExoPlayer configured for the SRG SSR.
* Creates a [PillarboxExoPlayer] instance configured for the SRG SSR.
*
* @param context The [Context].
* @param builder The builder.
* @receiver [SRG.Builder].
* @return The configured [PillarboxExoPlayer] for SRG SSR.
* **Basic usage**
*
* ```kotlin
* val srgPlayer = PillarboxExoPlayer(context)
* ```
*
* This creates a player with the default SRG SSR configuration:
* - Automatic integration with Pillarbox Monitoring: playback events are sent to a predefined endpoint.
* - SRG Asset Loader: integrates an [SRGAssetLoader] for handling SRG-specific media resources. If not explicitly configured, a default
* instance is created.
*
* **Custom configuration**
*
* ```kotlin
* val customSrgPlayer = PillarboxExoPlayer(context) {
* srgAssetLoader(context) {
* mediaCompositionService(CustomMediaCompositionService())
* }
* }
* ```
*
* @param context The [Context] of the application.
* @param builder An optional lambda with a receiver of type [SRG.Builder] allowing customization of the player's configuration.
*
* @return A configured [PillarboxExoPlayer] instance ready for playback.
*/
@PillarboxDsl
fun PillarboxExoPlayer(
Expand All @@ -31,8 +52,9 @@ fun PillarboxExoPlayer(
}

/**
* Pillarbox player configuration for the SRG.
* It sets up all SRG components by default.
* Pillarbox player configuration for the SRG. It sets up all SRG components by default.
*
* To create a Pillarbox player with this configuration, use [PillarboxExoPlayer][ch.srgssr.pillarbox.core.business.PillarboxExoPlayer].
*/
@Suppress("MatchingDeclarationName")
object SRG : PlayerConfig<SRG.Builder> {
Expand All @@ -42,9 +64,9 @@ object SRG : PlayerConfig<SRG.Builder> {
}

/**
* Builder for the SRG.
* Builder for creating an SRG-flavored Pillarbox player.
*/
class Builder : PillarboxBuilder() {
class Builder internal constructor() : PillarboxBuilder() {
init {
val url = if (BuildConfig.DEBUG) "https://dev.monitoring.pillarbox.ch/api/events" else "https://monitoring.pillarbox.ch/api/events"
monitoring(url)
Expand All @@ -55,11 +77,14 @@ object SRG : PlayerConfig<SRG.Builder> {
private var srgAssetLoader: SRGAssetLoader? = null

/**
* Configure a [SRGAssetLoader].
* Configures and adds an [SRGAssetLoader] to the player.
*
* **Note:** this function should be called only once. Subsequent calls will result in an exception.
*
* @param context The [Context] required for the [SRGAssetLoader].
* @param block A lambda to configure the [SRGAssetLoader] using a [SRGAssetLoaderConfig] instance.
*
* @param context The [Context].
* @param block The block to configure a [SRGAssetLoader].
* @receiver [SRGAssetLoaderConfig].
* @throws IllegalStateException If an [SRGAssetLoader] has already been configured.
*/
fun srgAssetLoader(context: Context, block: SRGAssetLoaderConfig.() -> Unit) {
check(srgAssetLoader == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import ch.srgssr.pillarbox.core.business.exception.ResourceNotFoundException
import java.io.IOException

/**
* Process error message from [PlaybackException]
* Provides user-friendly error messages for [PlaybackException] instances.
*
* This class analyzes the cause of a [PlaybackException] and maps it to a localized error message suitable for display to the user.
*
* @param context The Android context used to access string resources.
*/
class SRGErrorMessageProvider(private val context: Context) : ErrorMessageProvider<PlaybackException> {

Expand Down
Loading

0 comments on commit 1f22bb5

Please sign in to comment.