From 7d37d80bfb5a72a9d442a04d4df58f6dddc54d0f Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Sat, 28 Sep 2024 22:50:36 +0200 Subject: [PATCH] Add flow support for elements --- .../main/kotlin/element/ElementsContainer.kt | 18 ++++++++++++++++++ .../core/src/main/kotlin/element/delegates.kt | 9 +++++++++ .../mediastream/QueueEntryMediaStream.kt | 6 ++++++ .../mediastream/normalizationGainElement.kt | 7 +++++++ .../main/kotlin/queue/QueueEntryMetadata.kt | 7 +++++++ .../src/main/kotlin/queue/baseItemElement.kt | 6 ++++++ .../main/kotlin/queue/mediaSourceIdElement.kt | 7 +++++++ 7 files changed, 60 insertions(+) diff --git a/playback/core/src/main/kotlin/element/ElementsContainer.kt b/playback/core/src/main/kotlin/element/ElementsContainer.kt index b5740de950..9731da6808 100644 --- a/playback/core/src/main/kotlin/element/ElementsContainer.kt +++ b/playback/core/src/main/kotlin/element/ElementsContainer.kt @@ -1,5 +1,10 @@ package org.jellyfin.playback.core.element +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map import java.util.concurrent.ConcurrentHashMap /** @@ -7,6 +12,11 @@ import java.util.concurrent.ConcurrentHashMap */ open class ElementsContainer { private val elements = ConcurrentHashMap, Any?>() + private val updateFlow = MutableSharedFlow>( + replay = 1, + extraBufferCapacity = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST, + ) fun get(key: ElementKey): T = getOrNull(key) ?: error("No element found for key $key.") @@ -18,9 +28,17 @@ open class ElementsContainer { fun put(key: ElementKey, value: T) { elements[key] = value + updateFlow.tryEmit(key) } fun remove(key: ElementKey) { elements.remove(key) + updateFlow.tryEmit(key) + } + + fun getFlow(key: ElementKey): Flow { + return updateFlow + .map { getOrNull(key) } + .distinctUntilChanged() } } diff --git a/playback/core/src/main/kotlin/element/delegates.kt b/playback/core/src/main/kotlin/element/delegates.kt index 967d9a797c..0e5733be0c 100644 --- a/playback/core/src/main/kotlin/element/delegates.kt +++ b/playback/core/src/main/kotlin/element/delegates.kt @@ -1,5 +1,7 @@ package org.jellyfin.playback.core.element +import kotlinx.coroutines.flow.Flow +import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty @@ -38,3 +40,10 @@ fun requiredElement( thisRef.put(key, value) } } + +/** + * Delegate for the flow of an element. + */ +fun elementFlow( + key: ElementKey, +) = ReadOnlyProperty> { thisRef, _ -> thisRef.getFlow(key) } diff --git a/playback/core/src/main/kotlin/mediastream/QueueEntryMediaStream.kt b/playback/core/src/main/kotlin/mediastream/QueueEntryMediaStream.kt index d15974e068..5acaefcfe6 100644 --- a/playback/core/src/main/kotlin/mediastream/QueueEntryMediaStream.kt +++ b/playback/core/src/main/kotlin/mediastream/QueueEntryMediaStream.kt @@ -2,6 +2,7 @@ package org.jellyfin.playback.core.mediastream import org.jellyfin.playback.core.element.ElementKey import org.jellyfin.playback.core.element.element +import org.jellyfin.playback.core.element.elementFlow import org.jellyfin.playback.core.queue.QueueEntry private val mediaStreamKey = ElementKey("MediaStream") @@ -10,3 +11,8 @@ private val mediaStreamKey = ElementKey("MediaStream") * Get or set the [MediaStream] for this [QueueEntry]. */ var QueueEntry.mediaStream by element(mediaStreamKey) + +/** + * Get the [MediaStream] flow for this [QueueEntry]. + */ +val QueueEntry.mediaStreamFlow by elementFlow(mediaStreamKey) diff --git a/playback/core/src/main/kotlin/mediastream/normalizationGainElement.kt b/playback/core/src/main/kotlin/mediastream/normalizationGainElement.kt index aa460ee8fc..e9098e557f 100644 --- a/playback/core/src/main/kotlin/mediastream/normalizationGainElement.kt +++ b/playback/core/src/main/kotlin/mediastream/normalizationGainElement.kt @@ -2,6 +2,7 @@ package org.jellyfin.playback.core.mediastream import org.jellyfin.playback.core.element.ElementKey import org.jellyfin.playback.core.element.element +import org.jellyfin.playback.core.element.elementFlow import org.jellyfin.playback.core.queue.QueueEntry private val normalizationGainKey = ElementKey("NormalizationGain") @@ -11,3 +12,9 @@ private val normalizationGainKey = ElementKey("NormalizationGain") * apply a gain to the audio output. The normalization gain must target a loudness of -23LUFS. */ var QueueEntry.normalizationGain by element(normalizationGainKey) + +/** + * Get the flow of [normalizationGain]. + * @see normalizationGain + */ +val QueueEntry.normalizationGainFlow by elementFlow(normalizationGainKey) diff --git a/playback/core/src/main/kotlin/queue/QueueEntryMetadata.kt b/playback/core/src/main/kotlin/queue/QueueEntryMetadata.kt index 6380c47f12..5a4753776f 100644 --- a/playback/core/src/main/kotlin/queue/QueueEntryMetadata.kt +++ b/playback/core/src/main/kotlin/queue/QueueEntryMetadata.kt @@ -1,6 +1,7 @@ package org.jellyfin.playback.core.queue import org.jellyfin.playback.core.element.ElementKey +import org.jellyfin.playback.core.element.elementFlow import org.jellyfin.playback.core.element.requiredElement import java.time.LocalDate import kotlin.time.Duration @@ -42,3 +43,9 @@ private val metadataKey = ElementKey("QueueEntryMetadata") * Get or set the [QueueEntryMetadata] for this [QueueEntry]. Defaults to [QueueEntryMetadata.Empty]. */ var QueueEntry.metadata by requiredElement(metadataKey) { QueueEntryMetadata.Empty } + +/** + * Get the flow of [metadata]. + * @see metadata + */ +val QueueEntry.metadataFlow by elementFlow(metadataKey) diff --git a/playback/jellyfin/src/main/kotlin/queue/baseItemElement.kt b/playback/jellyfin/src/main/kotlin/queue/baseItemElement.kt index 4d43226b86..e4a8fab9b1 100644 --- a/playback/jellyfin/src/main/kotlin/queue/baseItemElement.kt +++ b/playback/jellyfin/src/main/kotlin/queue/baseItemElement.kt @@ -2,6 +2,7 @@ package org.jellyfin.playback.jellyfin.queue import org.jellyfin.playback.core.element.ElementKey import org.jellyfin.playback.core.element.element +import org.jellyfin.playback.core.element.elementFlow import org.jellyfin.playback.core.queue.QueueEntry import org.jellyfin.sdk.model.api.BaseItemDto @@ -11,3 +12,8 @@ private val baseItemKey = ElementKey("BaseItemDto") * Get or set the [BaseItemDto] for this [QueueEntry]. */ var QueueEntry.baseItem by element(baseItemKey) + +/** + * Get the [BaseItemDto] flow for this [QueueEntry]. + */ +val QueueEntry.baseItemFlow by elementFlow(baseItemKey) diff --git a/playback/jellyfin/src/main/kotlin/queue/mediaSourceIdElement.kt b/playback/jellyfin/src/main/kotlin/queue/mediaSourceIdElement.kt index a3c1b68e8b..a5bfcab0bf 100644 --- a/playback/jellyfin/src/main/kotlin/queue/mediaSourceIdElement.kt +++ b/playback/jellyfin/src/main/kotlin/queue/mediaSourceIdElement.kt @@ -2,6 +2,7 @@ package org.jellyfin.playback.jellyfin.queue import org.jellyfin.playback.core.element.ElementKey import org.jellyfin.playback.core.element.element +import org.jellyfin.playback.core.element.elementFlow import org.jellyfin.playback.core.queue.QueueEntry private val mediaSourceIdKey = ElementKey("MediaSource") @@ -11,3 +12,9 @@ private val mediaSourceIdKey = ElementKey("MediaSource") * behavior. */ var QueueEntry.mediaSourceId by element(mediaSourceIdKey) + +/** + * Get the flow of [mediaSourceId]. + * @see mediaSourceId + */ +val QueueEntry.mediaSourceIdFlow by elementFlow(mediaSourceIdKey)