-
-
Notifications
You must be signed in to change notification settings - Fork 526
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
Implement media segments #4052
Implement media segments #4052
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package org.jellyfin.androidtv.ui.playback.segment | ||
|
||
import org.jellyfin.androidtv.R | ||
import org.jellyfin.preference.PreferenceEnum | ||
|
||
enum class MediaSegmentAction( | ||
override val nameRes: Int, | ||
) : PreferenceEnum { | ||
/** | ||
* Don't take any action for this segment. | ||
*/ | ||
NOTHING(R.string.segment_action_nothing), | ||
|
||
/** | ||
* Seek to the end of this segment (endTicks). If the duration of this segment is shorter than 1 second it should do nothing to avoid | ||
Check warning Code scanning / detekt Line detected, which is longer than the defined maximum line length in the code style. Warning
Line detected, which is longer than the defined maximum line length in the code style.
|
||
* lagg. The skip action will only execute when playing over the segment start, not when seeking into the segment block. | ||
Check warning Code scanning / detekt Line detected, which is longer than the defined maximum line length in the code style. Warning
Line detected, which is longer than the defined maximum line length in the code style.
|
||
*/ | ||
SKIP(R.string.segment_action_skip), | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package org.jellyfin.androidtv.ui.playback.segment | ||
|
||
import org.jellyfin.androidtv.preference.UserPreferences | ||
import org.jellyfin.sdk.api.client.ApiClient | ||
import org.jellyfin.sdk.api.client.extensions.mediaSegmentsApi | ||
import org.jellyfin.sdk.model.api.BaseItemDto | ||
import org.jellyfin.sdk.model.api.MediaSegmentDto | ||
import org.jellyfin.sdk.model.api.MediaSegmentType | ||
|
||
interface MediaSegmentRepository { | ||
companion object { | ||
/** | ||
* All media segments currently supported by the app. The order of these is used for the preferences UI. | ||
*/ | ||
val SupportedTypes = listOf( | ||
MediaSegmentType.INTRO, | ||
MediaSegmentType.OUTRO, | ||
MediaSegmentType.PREVIEW, | ||
MediaSegmentType.RECAP, | ||
MediaSegmentType.COMMERCIAL, | ||
) | ||
} | ||
|
||
fun getDefaultSegmentTypeAction(type: MediaSegmentType): MediaSegmentAction | ||
fun setDefaultSegmentTypeAction(type: MediaSegmentType, action: MediaSegmentAction) | ||
|
||
suspend fun getSegmentsForItem(item: BaseItemDto): List<MediaSegmentDto> | ||
fun getMediaSegmentAction(segment: MediaSegmentDto): MediaSegmentAction | ||
} | ||
|
||
class MediaSegmentRepositoryImpl( | ||
private val userPreferences: UserPreferences, | ||
private val api: ApiClient, | ||
) : MediaSegmentRepository { | ||
private val mediaTypeActions = mutableMapOf<MediaSegmentType, MediaSegmentAction>() | ||
|
||
init { | ||
restoreMediaTypeActions() | ||
} | ||
|
||
private fun restoreMediaTypeActions() { | ||
val restoredMediaTypeActions = userPreferences[UserPreferences.mediaSegmentActions] | ||
.split(",") | ||
.mapNotNull { | ||
runCatching { | ||
val (type, action) = it.split('=', limit = 2) | ||
MediaSegmentType.fromName(type) to MediaSegmentAction.valueOf(action) | ||
}.getOrNull() | ||
} | ||
|
||
mediaTypeActions.clear() | ||
mediaTypeActions.putAll(restoredMediaTypeActions) | ||
} | ||
|
||
private fun saveMediaTypeActions() { | ||
userPreferences[UserPreferences.mediaSegmentActions] = mediaTypeActions | ||
.map { "${it.key.serialName}=${it.value.name}" } | ||
.joinToString(",") | ||
} | ||
|
||
override fun getDefaultSegmentTypeAction(type: MediaSegmentType): MediaSegmentAction { | ||
// Always return no action for unsupported types | ||
if (!MediaSegmentRepository.SupportedTypes.contains(type)) return MediaSegmentAction.NOTHING | ||
|
||
return mediaTypeActions.getOrDefault(type, MediaSegmentAction.NOTHING) | ||
} | ||
|
||
override fun setDefaultSegmentTypeAction(type: MediaSegmentType, action: MediaSegmentAction) { | ||
// Don't allow modifying actions for unsupported types | ||
if (!MediaSegmentRepository.SupportedTypes.contains(type)) return | ||
|
||
mediaTypeActions[type] = action | ||
saveMediaTypeActions() | ||
} | ||
|
||
override fun getMediaSegmentAction(segment: MediaSegmentDto): MediaSegmentAction { | ||
return getDefaultSegmentTypeAction(segment.type) | ||
} | ||
|
||
override suspend fun getSegmentsForItem(item: BaseItemDto): List<MediaSegmentDto> = runCatching { | ||
api.mediaSegmentsApi.getItemSegments( | ||
itemId = item.id, | ||
includeSegmentTypes = MediaSegmentRepository.SupportedTypes, | ||
).content.items | ||
}.getOrDefault(emptyList()) | ||
} |
Check notice
Code scanning / Android Lint
Unknown nullness Note