forked from jellyfin/jellyfin-android
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Most of the proposed changes in the files listed below have been shamelessly copied from the Android TV implementation in jellyfin/jellyfin-androidtv#4052. Authorship of these changes belongs to nielsvanvelzen. app/src/main/java/org/jellyfin/mobile/player/PlayerViewModel.kt app/src/main/java/org/jellyfin/mobile/player/mediasegments/MediaSegmentAction.kt app/src/main/java/org/jellyfin/mobile/player/mediasegments/MediaSegmentRepository.kt app/src/main/java/org/jellyfin/mobile/utils/extensions/MediaSegment.kt
- Loading branch information
1 parent
2bd932d
commit 9dcf4bc
Showing
11 changed files
with
264 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
export class MediaSegmentsPlugin { | ||
SETTING_PREFIX = 'segmentTypeAction'; | ||
|
||
constructor({ events, appSettings, dashboard }) { | ||
this.appSettings = appSettings; | ||
this.dashboard = dashboard; | ||
|
||
events.on(appSettings, 'change', (_, name) => this.onSettingsChanged(name)); | ||
} | ||
|
||
getSettingId(type) { | ||
return `${this.SETTING_PREFIX}__${type}`; | ||
} | ||
|
||
getSettingValue(id) { | ||
var userId = this.dashboard.getCurrentUserId(); | ||
|
||
return this.appSettings.get(id, userId); | ||
} | ||
|
||
// Update media segment action | ||
onSettingsChanged(name) { | ||
if (name.startsWith(this.SETTING_PREFIX)) { | ||
var type = name.slice(this.SETTING_PREFIX.length + 2); | ||
var action = this.getSettingValue(this.getSettingId(type)); | ||
|
||
if (type != null && action != null) { | ||
MediaSegments.setSegmentTypeAction(type, action); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
app/src/main/java/org/jellyfin/mobile/bridge/MediaSegments.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package org.jellyfin.mobile.bridge | ||
|
||
import android.content.Context | ||
import android.webkit.JavascriptInterface | ||
import org.jellyfin.mobile.player.mediasegments.MediaSegmentAction | ||
import org.jellyfin.mobile.player.mediasegments.MediaSegmentRepository | ||
import org.jellyfin.sdk.model.api.MediaSegmentType | ||
import org.koin.core.component.KoinComponent | ||
import org.koin.core.component.get | ||
|
||
@Suppress("unused") | ||
class MediaSegments(private val context: Context) : KoinComponent { | ||
private val mediaSegmentRepository: MediaSegmentRepository = get() | ||
|
||
@JavascriptInterface | ||
fun setSegmentTypeAction(typeString: String, actionString: String) { | ||
val type: MediaSegmentType = when(typeString) { | ||
"Intro" -> MediaSegmentType.INTRO | ||
"Outro" -> MediaSegmentType.OUTRO | ||
"Preview" -> MediaSegmentType.PREVIEW | ||
"Recap" -> MediaSegmentType.RECAP | ||
"Commercial" -> MediaSegmentType.COMMERCIAL | ||
else -> return | ||
} | ||
|
||
val action: MediaSegmentAction = when(actionString) { | ||
"None" -> MediaSegmentAction.NOTHING | ||
"Skip" -> MediaSegmentAction.SKIP | ||
"AskToSkip" -> MediaSegmentAction.ASK_TO_SKIP | ||
else -> return | ||
} | ||
|
||
mediaSegmentRepository.setDefaultSegmentTypeAction(type, action) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
app/src/main/java/org/jellyfin/mobile/player/mediasegments/MediaSegmentAction.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package org.jellyfin.mobile.player.mediasegments | ||
|
||
enum class MediaSegmentAction { | ||
/** | ||
* Don't take any action for this segment. | ||
*/ | ||
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 | ||
* lag. The skip action will only execute when playing over the segment start, not when seeking into the segment block. | ||
*/ | ||
SKIP, | ||
|
||
/** | ||
* Ask the user if they want to skip this segment. When the user agrees this behaves like [SKIP]. Confirmation should only be asked for | ||
* segments with a duration of at least 3 seconds to avoid UI flickering. | ||
*/ | ||
ASK_TO_SKIP, | ||
} |
95 changes: 95 additions & 0 deletions
95
app/src/main/java/org/jellyfin/mobile/player/mediasegments/MediaSegmentRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package org.jellyfin.mobile.player.mediasegments | ||
|
||
import org.jellyfin.mobile.app.AppPreferences | ||
import org.jellyfin.mobile.utils.extensions.duration | ||
import org.jellyfin.sdk.api.client.ApiClient | ||
import org.jellyfin.sdk.api.client.extensions.mediaSegmentsApi | ||
import org.jellyfin.sdk.api.operations.MediaSegmentsApi | ||
import org.jellyfin.sdk.model.api.BaseItemDto | ||
import org.jellyfin.sdk.model.api.MediaSegmentDto | ||
import org.jellyfin.sdk.model.api.MediaSegmentType | ||
import org.koin.core.component.KoinComponent | ||
import org.koin.core.component.get | ||
import org.koin.core.component.inject | ||
import kotlin.time.Duration.Companion.seconds | ||
|
||
fun Map<MediaSegmentType, MediaSegmentAction>.toMediaSegmentActionsString() = | ||
map { "${it.key.serialName}=${it.value.name}" } | ||
.joinToString(",") | ||
|
||
class MediaSegmentRepository : KoinComponent { | ||
companion object { | ||
/** | ||
* All media segments currently supported by the app. | ||
*/ | ||
val SupportedTypes = listOf( | ||
MediaSegmentType.INTRO, | ||
MediaSegmentType.OUTRO, | ||
MediaSegmentType.PREVIEW, | ||
MediaSegmentType.RECAP, | ||
MediaSegmentType.COMMERCIAL, | ||
) | ||
|
||
/** | ||
* The minimum duration for a media segment to allow the [MediaSegmentAction.SKIP] action. | ||
*/ | ||
val SkipMinDuration = 1.seconds | ||
} | ||
|
||
private val appPreferences: AppPreferences by inject() | ||
private val apiClient: ApiClient = get() | ||
private val mediaSegmentsApi: MediaSegmentsApi = apiClient.mediaSegmentsApi | ||
|
||
private val mediaTypeActions = mutableMapOf<MediaSegmentType, MediaSegmentAction>() | ||
|
||
init { | ||
restoreMediaTypeActions() | ||
} | ||
|
||
private fun restoreMediaTypeActions() { | ||
val restoredMediaTypeActions = appPreferences.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() { | ||
appPreferences.mediaSegmentActions = mediaTypeActions.toMediaSegmentActionsString() | ||
} | ||
|
||
fun getDefaultSegmentTypeAction(type: MediaSegmentType): MediaSegmentAction { | ||
// Always return no action for unsupported types | ||
if (!SupportedTypes.contains(type)) return MediaSegmentAction.NOTHING | ||
|
||
return mediaTypeActions.getOrDefault(type, MediaSegmentAction.NOTHING) | ||
} | ||
|
||
fun setDefaultSegmentTypeAction(type: MediaSegmentType, action: MediaSegmentAction) { | ||
// Don't allow modifying actions for unsupported types | ||
if (!SupportedTypes.contains(type)) return | ||
|
||
mediaTypeActions[type] = action | ||
saveMediaTypeActions() | ||
} | ||
|
||
fun getMediaSegmentAction(segment: MediaSegmentDto): MediaSegmentAction { | ||
val action = getDefaultSegmentTypeAction(segment.type) | ||
// Skip the skip action if timespan is too short | ||
if (action == MediaSegmentAction.SKIP && segment.duration < SkipMinDuration) return MediaSegmentAction.NOTHING | ||
return action | ||
} | ||
|
||
suspend fun getSegmentsForItem(item: BaseItemDto): List<MediaSegmentDto> = runCatching { | ||
mediaSegmentsApi.getItemSegments( | ||
itemId = item.id, | ||
includeSegmentTypes = SupportedTypes, | ||
).content.items | ||
}.getOrDefault(emptyList()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
app/src/main/java/org/jellyfin/mobile/utils/extensions/MediaSegment.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package org.jellyfin.mobile.utils.extensions | ||
|
||
import org.jellyfin.sdk.model.api.MediaSegmentDto | ||
import org.jellyfin.sdk.model.extensions.ticks | ||
import kotlin.time.Duration | ||
|
||
val MediaSegmentDto.start get() = startTicks.ticks | ||
val MediaSegmentDto.end get() = endTicks.ticks | ||
|
||
val MediaSegmentDto.duration get() = (endTicks - startTicks).ticks.coerceAtLeast(Duration.ZERO) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters