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

519 simplify TimeRange management #521

Merged
merged 6 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.android.library) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.parcelize) apply false
alias(libs.plugins.detekt)
alias(libs.plugins.dependency.analysis.gradle.plugin)
alias(libs.plugins.kotlinx.kover)
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ coil = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" }
coil-base = { module = "io.coil-kt:coil-compose-base", version.ref = "coil" }
json = { module = "org.json:json", version.ref = "json" }
kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
kotlin-parcelize-runtime = { module = "org.jetbrains.kotlin:kotlin-parcelize-runtime", version.ref = "kotlin" }
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
kotlinx-kover-gradle = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kotlinx-kover" }
Expand Down Expand Up @@ -139,6 +140,7 @@ android-library = { id = "com.android.library", version.ref = "android-gradle-pl
dependency-analysis-gradle-plugin = { id = "com.autonomousapps.dependency-analysis", version.ref = "dependency-analysis-gradle-plugin" }
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
kotlinx-kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kotlinx-kover" }
pillarbox-android-application = { id = "ch.srgssr.pillarbox.gradle.android_application", version = "unspecified" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import ch.srgssr.pillarbox.core.business.integrationlayer.ImageScalingService
import ch.srgssr.pillarbox.core.business.integrationlayer.data.Chapter
import ch.srgssr.pillarbox.core.business.integrationlayer.data.MediaComposition
import ch.srgssr.pillarbox.core.business.integrationlayer.data.MediaType
import ch.srgssr.pillarbox.player.asset.timeRange.Chapter as TimeRangeChapter

internal object ChapterAdapter {
private val imageScalingService = ImageScalingService()

fun toChapter(chapter: Chapter): ch.srgssr.pillarbox.player.asset.Chapter {
fun toChapter(chapter: Chapter): TimeRangeChapter {
requireNotNull(chapter.fullLengthMarkIn)
requireNotNull(chapter.fullLengthMarkOut)
return ch.srgssr.pillarbox.player.asset.Chapter(
return TimeRangeChapter(
id = chapter.urn,
start = chapter.fullLengthMarkIn,
end = chapter.fullLengthMarkOut,
Expand All @@ -29,7 +30,7 @@ internal object ChapterAdapter {
)
}

fun getChapters(mediaComposition: MediaComposition): List<ch.srgssr.pillarbox.player.asset.Chapter> {
fun getChapters(mediaComposition: MediaComposition): List<TimeRangeChapter> {
val mainChapter = mediaComposition.mainChapter
if (!mainChapter.isFullLengthChapter && mainChapter.mediaType == MediaType.AUDIO) return emptyList()
return mediaComposition.listChapter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,9 @@ class SRGAssetLoader(
mediaComposition = result,
)
}.build(),
chapters = ChapterAdapter.getChapters(result),
blockedTimeRanges = SegmentAdapter.getBlockedTimeRanges(chapter.listSegment),
timeRanges = TimeIntervalAdapter.getTimeIntervals(result.mainChapter.timeIntervalList),
timeRanges = ChapterAdapter.getChapters(result) +
TimeIntervalAdapter.getCredits(result.mainChapter.timeIntervalList) +
SegmentAdapter.getBlockedTimeRanges(chapter.listSegment),
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@
package ch.srgssr.pillarbox.core.business.source

import ch.srgssr.pillarbox.core.business.integrationlayer.data.Segment
import ch.srgssr.pillarbox.player.asset.BlockedTimeRange
import ch.srgssr.pillarbox.player.asset.timeRange.BlockedTimeRange

internal object SegmentAdapter {

fun getBlockedTimeRange(segment: Segment): BlockedTimeRange {
requireNotNull(segment.blockReason)
return BlockedTimeRange(segment.urn, segment.markIn, segment.markOut, segment.blockReason.toString())
return BlockedTimeRange(
id = segment.urn,
start = segment.markIn,
end = segment.markOut,
reason = segment.blockReason.toString(),
)
}

fun getBlockedTimeRanges(listSegment: List<Segment>?): List<BlockedTimeRange> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,24 @@
package ch.srgssr.pillarbox.core.business.source

import ch.srgssr.pillarbox.core.business.integrationlayer.data.TimeInterval
import ch.srgssr.pillarbox.player.asset.SkipableTimeRange
import ch.srgssr.pillarbox.core.business.integrationlayer.data.TimeIntervalType
import ch.srgssr.pillarbox.player.asset.timeRange.Credit

internal object TimeIntervalAdapter {
internal fun getTimeIntervals(timeIntervals: List<TimeInterval>?): List<SkipableTimeRange> {
internal fun getCredits(timeIntervals: List<TimeInterval>?): List<Credit> {
return timeIntervals
.orEmpty()
.mapNotNull { it.toSkipableTimeInterval() }
.mapNotNull { it.toCredit() }
}

internal fun TimeInterval.toSkipableTimeInterval(): SkipableTimeRange? {
internal fun TimeInterval.toCredit(): Credit? {
return if (type == null || markIn == null || markOut == null) {
null
} else {
SkipableTimeRange(
id = type.name,
start = markIn,
end = markOut,
)
when (type) {
TimeIntervalType.CLOSING_CREDITS -> Credit.Closing(start = markIn, end = markOut)
TimeIntervalType.OPENING_CREDITS -> Credit.Opening(start = markIn, end = markOut)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import ch.srgssr.pillarbox.core.business.source.ChapterAdapter
import org.junit.runner.RunWith
import kotlin.test.Test
import kotlin.test.assertEquals
import ch.srgssr.pillarbox.player.asset.timeRange.Chapter as TimeRangeChapter

@RunWith(AndroidJUnit4::class)
class ChapterAdapterTest {
Expand Down Expand Up @@ -58,7 +59,7 @@ class ChapterAdapterTest {
fullLengthMarkOut = 100,
mediaType = MediaType.VIDEO,
)
val expected = ch.srgssr.pillarbox.player.asset.Chapter(
val expected = TimeRangeChapter(
id = "urn",
start = 10,
end = 100,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,19 +148,19 @@ class SRGAssetLoaderTest {
val asset = assetLoader.loadAsset(
SRGMediaItemBuilder(DummyMediaCompositionProvider.URN_SEGMENT_BLOCK_REASON).build()
)
val expectedBlockIntervals = listOf(SegmentAdapter.getBlockedTimeRange(DummyMediaCompositionProvider.BLOCKED_SEGMENT))
assertEquals(expectedBlockIntervals, asset.blockedTimeRanges)
val expectedBlockTimeRanges = listOf(SegmentAdapter.getBlockedTimeRange(DummyMediaCompositionProvider.BLOCKED_SEGMENT))
assertEquals(expectedBlockTimeRanges, asset.timeRanges)
}

@Test
fun testTimeIntervals() = runTest {
val asset = assetLoader.loadAsset(
SRGMediaItemBuilder(DummyMediaCompositionProvider.URN_TIME_INTERVALS).build()
)
val expectedTimeIntervals = TimeIntervalAdapter.getTimeIntervals(
val expectedCredits = TimeIntervalAdapter.getCredits(
listOf(DummyMediaCompositionProvider.TIME_INTERVAL_1, DummyMediaCompositionProvider.TIME_INTERVAL_2)
)
assertEquals(expectedTimeIntervals, asset.timeRanges)
assertEquals(expectedCredits, asset.timeRanges)
}

internal class DummyMediaCompositionProvider : MediaCompositionService {
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.Segment
import ch.srgssr.pillarbox.core.business.source.SegmentAdapter
import ch.srgssr.pillarbox.player.asset.BlockedTimeRange
import ch.srgssr.pillarbox.player.asset.timeRange.BlockedTimeRange
import kotlin.test.Test
import kotlin.test.assertEquals

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright (c) SRG SSR. All rights reserved.
* License information is available from the LICENSE file.
*/
package ch.srgssr.pillarbox.core.business

import ch.srgssr.pillarbox.core.business.integrationlayer.data.TimeInterval
import ch.srgssr.pillarbox.core.business.integrationlayer.data.TimeIntervalType
import ch.srgssr.pillarbox.core.business.source.TimeIntervalAdapter
import ch.srgssr.pillarbox.core.business.source.TimeIntervalAdapter.toCredit
import ch.srgssr.pillarbox.player.asset.timeRange.Credit
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
import kotlin.test.assertTrue

class TimeIntervalAdapterTest {

@Test
fun `get credits, source is null`() {
val credits = TimeIntervalAdapter.getCredits(null)

assertTrue(credits.isEmpty())
}

@Test
fun `get credits, source is empty`() {
val credits = TimeIntervalAdapter.getCredits(emptyList())

assertTrue(credits.isEmpty())
}

@Test
fun `get credits, source is not empty`() {
val timeIntervals = listOf(
// Valid time intervals
TimeInterval(markIn = 10L, markOut = 20L, type = TimeIntervalType.OPENING_CREDITS),
TimeInterval(markIn = 30L, markOut = 100L, type = TimeIntervalType.CLOSING_CREDITS),

// Invalid time intervals
TimeInterval(markIn = null, markOut = null, type = null),
TimeInterval(markIn = 10L, markOut = null, type = null),
TimeInterval(markIn = 10L, markOut = 20L, type = null),
TimeInterval(markIn = 10L, markOut = null, type = TimeIntervalType.OPENING_CREDITS),
TimeInterval(markIn = null, markOut = 20L, type = null),
TimeInterval(markIn = null, markOut = 20L, type = TimeIntervalType.CLOSING_CREDITS),
TimeInterval(markIn = null, markOut = null, type = TimeIntervalType.OPENING_CREDITS),
)
val credits = TimeIntervalAdapter.getCredits(timeIntervals)
val expectedCredits = listOf(
timeIntervals[0].toCredit(),
timeIntervals[1].toCredit(),
)

assertEquals(expectedCredits, credits)
}

@Test
fun `empty time interval produces null Credit`() {
val timeInterval = TimeInterval(markIn = null, markOut = null, type = null)
assertNull(timeInterval.toCredit())
}

@Test
fun `null markOut produces null Credit`() {
val timeInterval = TimeInterval(markIn = 100, markOut = null, type = TimeIntervalType.CLOSING_CREDITS)
assertNull(timeInterval.toCredit())
}

@Test
fun `null markIn produces null Credit`() {
val timeInterval = TimeInterval(markIn = null, markOut = 100, type = TimeIntervalType.CLOSING_CREDITS)
assertNull(timeInterval.toCredit())
}

@Test
fun `null type produces null Credit`() {
val timeInterval = TimeInterval(markIn = 100, markOut = 200, type = null)
assertNull(timeInterval.toCredit())
}

@Test
fun `OPENING_CREDITS type produces Opening`() {
val timeInterval = TimeInterval(markIn = 100, markOut = 200, type = TimeIntervalType.OPENING_CREDITS)
assertEquals(Credit.Opening(start = 100, end = 200), timeInterval.toCredit())
}

@Test
fun `CLOSING_CREDITS type produces Opening`() {
val timeInterval = TimeInterval(markIn = 100, markOut = 200, type = TimeIntervalType.CLOSING_CREDITS)
assertEquals(Credit.Closing(start = 100, end = 200), timeInterval.toCredit())
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import ch.srgssr.pillarbox.demo.tv.ui.player.compose.settings.PlaybackSettingsDr
import ch.srgssr.pillarbox.demo.tv.ui.theme.paddings
import ch.srgssr.pillarbox.ui.extension.currentMediaMetadataAsState
import ch.srgssr.pillarbox.ui.extension.getCurrentChapterAsState
import ch.srgssr.pillarbox.ui.extension.getCurrentTimeRangeAsState
import ch.srgssr.pillarbox.ui.extension.getCurrentCreditAsState
import ch.srgssr.pillarbox.ui.extension.playerErrorAsState
import ch.srgssr.pillarbox.ui.widget.maintainVisibleOnFocus
import ch.srgssr.pillarbox.ui.widget.player.PlayerSurface
Expand All @@ -69,7 +69,7 @@ fun PlayerView(
) {
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
val visibilityState = rememberDelayedVisibilityState(player = player, visible = true)
val timeInterval by player.getCurrentTimeRangeAsState()
val currentCredit by player.getCurrentCreditAsState()

LaunchedEffect(drawerState.currentValue) {
when (drawerState.currentValue) {
Expand Down Expand Up @@ -128,9 +128,9 @@ fun PlayerView(
}
}
}
AnimatedVisibility(timeInterval != null) {
AnimatedVisibility(currentCredit != null) {
Button(
onClick = { player.seekTo(timeInterval?.end ?: 0L) },
onClick = { player.seekTo(currentCredit?.end ?: 0L) },
modifier = Modifier.padding(MaterialTheme.paddings.baseline),
) {
Text(text = stringResource(R.string.skip))
Expand Down
Loading
Loading