diff --git a/android/src/main/java/dev/zoriya/omni/EventMap.kt b/android/src/main/java/dev/zoriya/omni/EventMap.kt index ad0403a..1b4254b 100644 --- a/android/src/main/java/dev/zoriya/omni/EventMap.kt +++ b/android/src/main/java/dev/zoriya/omni/EventMap.kt @@ -15,6 +15,7 @@ import androidx.media3.common.Player.STATE_BUFFERING import androidx.media3.common.Player.STATE_ENDED import androidx.media3.common.Player.STATE_IDLE import androidx.media3.common.Player.STATE_READY +import androidx.media3.common.Timeline import androidx.media3.common.Tracks import androidx.media3.common.VideoSize import com.margelo.nitro.omni.BoolProperty @@ -23,6 +24,7 @@ import com.margelo.nitro.omni.HybridOmniEventMapSpec import com.margelo.nitro.omni.NumberProperty import com.margelo.nitro.omni.PlayerStatus import com.margelo.nitro.omni.Rendition +import com.margelo.nitro.omni.Source import com.margelo.nitro.omni.Track @SuppressLint("UnsafeOptInUsageError") @@ -40,6 +42,7 @@ class EventMap() : HybridOmniEventMapSpec(), Player.Listener { private val stateBoolListeners = mutableMapOf Unit>>() private val playerStatusListeners = mutableSetOf<(PlayerStatus) -> Unit>() private val castStatusListeners = mutableSetOf<(CastStatus) -> Unit>() + private val sourceListeners = mutableSetOf<(Source?) -> Unit>() private var lastMediaItemIndex = 0 private var lastRendition: Rendition? = null @@ -92,6 +95,10 @@ class EventMap() : HybridOmniEventMapSpec(), Player.Listener { castStatusListeners.forEach { it(status) } } + fun emitSource(source: Source?) { + sourceListeners.forEach { it(source) } + } + private fun selectedTrack(trackType: Int): Track? { val groups = player.currentTracks.groups.filter { it.type == trackType } for (group in groups) { @@ -300,6 +307,14 @@ class EventMap() : HybridOmniEventMapSpec(), Player.Listener { castStatusListeners.remove(cb) } + override fun addSourceListener(cb: (value: Source?) -> Unit) { + sourceListeners.add(cb) + } + + override fun removeSourceListener(cb: (value: Source?) -> Unit) { + sourceListeners.remove(cb) + } + override fun addOnEndListener(cb: () -> Unit) { onEndListeners.add(cb) } diff --git a/android/src/main/java/dev/zoriya/omni/OmniPlayer.kt b/android/src/main/java/dev/zoriya/omni/OmniPlayer.kt index 0454624..0800a00 100644 --- a/android/src/main/java/dev/zoriya/omni/OmniPlayer.kt +++ b/android/src/main/java/dev/zoriya/omni/OmniPlayer.kt @@ -306,6 +306,7 @@ class OmniPlayer( override var source: Source? = null set(value) { field = value + eventMap.emitSource(value) if (value == null) { runOnMainThreadSync { player.clearMediaItems() diff --git a/nitrogen/generated/android/OmniOnLoad.cpp b/nitrogen/generated/android/OmniOnLoad.cpp index 0e65fd1..343bd7a 100644 --- a/nitrogen/generated/android/OmniOnLoad.cpp +++ b/nitrogen/generated/android/OmniOnLoad.cpp @@ -20,6 +20,7 @@ #include "JFunc_void_bool.hpp" #include "JFunc_void_PlayerStatus.hpp" #include "JFunc_void_CastStatus.hpp" +#include "JFunc_void_std__optional_Source_.hpp" #include "JFunc_void.hpp" #include "JFunc_void_std__string_std__string.hpp" #include "JFunc_void_std__string.hpp" @@ -67,6 +68,7 @@ void registerAllNatives() { margelo::nitro::omni::JFunc_void_bool_cxx::registerNatives(); margelo::nitro::omni::JFunc_void_PlayerStatus_cxx::registerNatives(); margelo::nitro::omni::JFunc_void_CastStatus_cxx::registerNatives(); + margelo::nitro::omni::JFunc_void_std__optional_Source__cxx::registerNatives(); margelo::nitro::omni::JFunc_void_cxx::registerNatives(); margelo::nitro::omni::JFunc_void_std__string_std__string_cxx::registerNatives(); margelo::nitro::omni::JFunc_void_std__string_cxx::registerNatives(); diff --git a/nitrogen/generated/android/c++/JFunc_void_std__optional_Source_.hpp b/nitrogen/generated/android/c++/JFunc_void_std__optional_Source_.hpp new file mode 100644 index 0000000..26e3b18 --- /dev/null +++ b/nitrogen/generated/android/c++/JFunc_void_std__optional_Source_.hpp @@ -0,0 +1,89 @@ +/// +/// JFunc_void_std__optional_Source_.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include + +#include "Source.hpp" +#include +#include +#include +#include "JSource.hpp" +#include "VideoSrc.hpp" +#include "JVideoSrc.hpp" +#include +#include +#include "Subtitle.hpp" +#include +#include "JSubtitle.hpp" +#include "Metadata.hpp" +#include "JMetadata.hpp" +#include "MixAudioMode.hpp" +#include "JMixAudioMode.hpp" + +namespace margelo::nitro::omni { + + using namespace facebook; + + /** + * Represents the Java/Kotlin callback `(value: Source?) -> Unit`. + * This can be passed around between C++ and Java/Kotlin. + */ + struct JFunc_void_std__optional_Source_: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/omni/Func_void_std__optional_Source_;"; + + public: + /** + * Invokes the function this `JFunc_void_std__optional_Source_` instance holds through JNI. + */ + void invoke(const std::optional& value) const { + static const auto method = javaClassStatic()->getMethod /* value */)>("invoke"); + method(self(), value.has_value() ? JSource::fromCpp(value.value()) : nullptr); + } + }; + + /** + * An implementation of Func_void_std__optional_Source_ that is backed by a C++ implementation (using `std::function<...>`) + */ + class JFunc_void_std__optional_Source__cxx final: public jni::HybridClass { + public: + static jni::local_ref fromCpp(const std::function& /* value */)>& func) { + return JFunc_void_std__optional_Source__cxx::newObjectCxxArgs(func); + } + + public: + /** + * Invokes the C++ `std::function<...>` this `JFunc_void_std__optional_Source__cxx` instance holds. + */ + void invoke_cxx(jni::alias_ref value) { + _func(value != nullptr ? std::make_optional(value->toCpp()) : std::nullopt); + } + + public: + [[nodiscard]] + inline const std::function& /* value */)>& getFunction() const { + return _func; + } + + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/omni/Func_void_std__optional_Source__cxx;"; + static void registerNatives() { + registerHybrid({makeNativeMethod("invoke_cxx", JFunc_void_std__optional_Source__cxx::invoke_cxx)}); + } + + private: + explicit JFunc_void_std__optional_Source__cxx(const std::function& /* value */)>& func): _func(func) { } + + private: + friend HybridBase; + std::function& /* value */)> _func; + }; + +} // namespace margelo::nitro::omni diff --git a/nitrogen/generated/android/c++/JHybridOmniEventMapSpec.cpp b/nitrogen/generated/android/c++/JHybridOmniEventMapSpec.cpp index 8ad91b0..378c1bb 100644 --- a/nitrogen/generated/android/c++/JHybridOmniEventMapSpec.cpp +++ b/nitrogen/generated/android/c++/JHybridOmniEventMapSpec.cpp @@ -15,6 +15,16 @@ namespace margelo::nitro::omni { enum class BoolProperty; } namespace margelo::nitro::omni { enum class PlayerStatus; } // Forward declaration of `CastStatus` to properly resolve imports. namespace margelo::nitro::omni { enum class CastStatus; } +// Forward declaration of `Source` to properly resolve imports. +namespace margelo::nitro::omni { struct Source; } +// Forward declaration of `VideoSrc` to properly resolve imports. +namespace margelo::nitro::omni { struct VideoSrc; } +// Forward declaration of `Subtitle` to properly resolve imports. +namespace margelo::nitro::omni { struct Subtitle; } +// Forward declaration of `Metadata` to properly resolve imports. +namespace margelo::nitro::omni { struct Metadata; } +// Forward declaration of `MixAudioMode` to properly resolve imports. +namespace margelo::nitro::omni { enum class MixAudioMode; } // Forward declaration of `Track` to properly resolve imports. namespace margelo::nitro::omni { struct Track; } // Forward declaration of `Rendition` to properly resolve imports. @@ -34,14 +44,27 @@ namespace margelo::nitro::omni { struct Rendition; } #include "CastStatus.hpp" #include "JFunc_void_CastStatus.hpp" #include "JCastStatus.hpp" -#include "JFunc_void.hpp" +#include "Source.hpp" +#include +#include "JFunc_void_std__optional_Source_.hpp" +#include "JSource.hpp" +#include "VideoSrc.hpp" +#include "JVideoSrc.hpp" #include +#include +#include "Subtitle.hpp" +#include +#include "JSubtitle.hpp" +#include "Metadata.hpp" +#include "JMetadata.hpp" +#include "MixAudioMode.hpp" +#include "JMixAudioMode.hpp" +#include "JFunc_void.hpp" #include "JFunc_void_std__string_std__string.hpp" #include "JFunc_void_std__string.hpp" #include "Track.hpp" #include "JFunc_void_Track.hpp" #include "JTrack.hpp" -#include #include "JFunc_void_std__optional_Track_.hpp" #include "Rendition.hpp" #include "JFunc_void_Rendition.hpp" @@ -112,6 +135,14 @@ namespace margelo::nitro::omni { static const auto method = _javaPart->javaClassStatic()->getMethod /* cb */)>("removeCastStatusListener_cxx"); method(_javaPart, JFunc_void_CastStatus_cxx::fromCpp(cb)); } + void JHybridOmniEventMapSpec::addSourceListener(const std::function& /* value */)>& cb) { + static const auto method = _javaPart->javaClassStatic()->getMethod /* cb */)>("addSourceListener_cxx"); + method(_javaPart, JFunc_void_std__optional_Source__cxx::fromCpp(cb)); + } + void JHybridOmniEventMapSpec::removeSourceListener(const std::function& /* value */)>& cb) { + static const auto method = _javaPart->javaClassStatic()->getMethod /* cb */)>("removeSourceListener_cxx"); + method(_javaPart, JFunc_void_std__optional_Source__cxx::fromCpp(cb)); + } void JHybridOmniEventMapSpec::addOnEndListener(const std::function& cb) { static const auto method = _javaPart->javaClassStatic()->getMethod /* cb */)>("addOnEndListener_cxx"); method(_javaPart, JFunc_void_cxx::fromCpp(cb)); diff --git a/nitrogen/generated/android/c++/JHybridOmniEventMapSpec.hpp b/nitrogen/generated/android/c++/JHybridOmniEventMapSpec.hpp index bc6d924..0734823 100644 --- a/nitrogen/generated/android/c++/JHybridOmniEventMapSpec.hpp +++ b/nitrogen/generated/android/c++/JHybridOmniEventMapSpec.hpp @@ -62,6 +62,8 @@ namespace margelo::nitro::omni { void removePlayerStatusListener(const std::function& cb) override; void addCastStatusListener(const std::function& cb) override; void removeCastStatusListener(const std::function& cb) override; + void addSourceListener(const std::function& /* value */)>& cb) override; + void removeSourceListener(const std::function& /* value */)>& cb) override; void addOnEndListener(const std::function& cb) override; void removeOnEndListener(const std::function& cb) override; void addOnPrevListener(const std::function& cb) override; diff --git a/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.cpp b/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.cpp index 398e2ef..9f8e535 100644 --- a/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.cpp +++ b/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.cpp @@ -9,6 +9,10 @@ // Forward declaration of `HybridOmniEventMapSpec` to properly resolve imports. namespace margelo::nitro::omni { class HybridOmniEventMapSpec; } +// Forward declaration of `Track` to properly resolve imports. +namespace margelo::nitro::omni { struct Track; } +// Forward declaration of `Rendition` to properly resolve imports. +namespace margelo::nitro::omni { struct Rendition; } // Forward declaration of `Source` to properly resolve imports. namespace margelo::nitro::omni { struct Source; } // Forward declaration of `VideoSrc` to properly resolve imports. @@ -19,10 +23,6 @@ namespace margelo::nitro::omni { struct Subtitle; } namespace margelo::nitro::omni { struct Metadata; } // Forward declaration of `MixAudioMode` to properly resolve imports. namespace margelo::nitro::omni { enum class MixAudioMode; } -// Forward declaration of `Track` to properly resolve imports. -namespace margelo::nitro::omni { struct Track; } -// Forward declaration of `Rendition` to properly resolve imports. -namespace margelo::nitro::omni { struct Rendition; } // Forward declaration of `PlayerStatus` to properly resolve imports. namespace margelo::nitro::omni { enum class PlayerStatus; } // Forward declaration of `CastStatus` to properly resolve imports. @@ -32,23 +32,23 @@ namespace margelo::nitro::omni { enum class CastStatus; } #include "HybridOmniEventMapSpec.hpp" #include "JHybridOmniEventMapSpec.hpp" #include +#include "Track.hpp" +#include +#include "JTrack.hpp" +#include +#include "Rendition.hpp" +#include "JRendition.hpp" #include "Source.hpp" #include "JSource.hpp" #include "VideoSrc.hpp" #include "JVideoSrc.hpp" -#include #include #include "Subtitle.hpp" -#include #include "JSubtitle.hpp" #include "Metadata.hpp" #include "JMetadata.hpp" #include "MixAudioMode.hpp" #include "JMixAudioMode.hpp" -#include "Track.hpp" -#include "JTrack.hpp" -#include "Rendition.hpp" -#include "JRendition.hpp" #include "PlayerStatus.hpp" #include "JPlayerStatus.hpp" #include "CastStatus.hpp" @@ -98,15 +98,6 @@ namespace margelo::nitro::omni { static const auto method = _javaPart->javaClassStatic()->getMethod /* showNotification */)>("setShowNotification"); method(_javaPart, showNotification.has_value() ? jni::JBoolean::valueOf(showNotification.value()) : nullptr); } - std::optional JHybridOmniPlayerSpec::getSource() { - static const auto method = _javaPart->javaClassStatic()->getMethod()>("getSource"); - auto __result = method(_javaPart); - return __result != nullptr ? std::make_optional(__result->toCpp()) : std::nullopt; - } - void JHybridOmniPlayerSpec::setSource(const std::optional& source) { - static const auto method = _javaPart->javaClassStatic()->getMethod /* source */)>("setSource"); - method(_javaPart, source.has_value() ? JSource::fromCpp(source.value()) : nullptr); - } bool JHybridOmniPlayerSpec::getHasPrev() { static const auto method = _javaPart->javaClassStatic()->getMethod("getHasPrev"); auto __result = method(_javaPart); @@ -173,6 +164,15 @@ namespace margelo::nitro::omni { return __vector; }(__result); } + std::optional JHybridOmniPlayerSpec::getSource() { + static const auto method = _javaPart->javaClassStatic()->getMethod()>("getSource"); + auto __result = method(_javaPart); + return __result != nullptr ? std::make_optional(__result->toCpp()) : std::nullopt; + } + void JHybridOmniPlayerSpec::setSource(const std::optional& source) { + static const auto method = _javaPart->javaClassStatic()->getMethod /* source */)>("setSource"); + method(_javaPart, source.has_value() ? JSource::fromCpp(source.value()) : nullptr); + } PlayerStatus JHybridOmniPlayerSpec::getStatus() { static const auto method = _javaPart->javaClassStatic()->getMethod()>("getStatus"); auto __result = method(_javaPart); diff --git a/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.hpp b/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.hpp index 82e68e5..fe76fcd 100644 --- a/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.hpp +++ b/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.hpp @@ -53,14 +53,14 @@ namespace margelo::nitro::omni { std::shared_ptr getEventMap() override; std::optional getShowNotification() override; void setShowNotification(std::optional showNotification) override; - std::optional getSource() override; - void setSource(const std::optional& source) override; bool getHasPrev() override; bool getHasNext() override; std::vector getVideos() override; std::vector getAudios() override; std::vector getSubtitles() override; std::vector getRendition() override; + std::optional getSource() override; + void setSource(const std::optional& source) override; PlayerStatus getStatus() override; bool getIsPlaying() override; double getCurrentTime() override; diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/Func_void_std__optional_Source_.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/Func_void_std__optional_Source_.kt new file mode 100644 index 0000000..145f31c --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/Func_void_std__optional_Source_.kt @@ -0,0 +1,80 @@ +/// +/// Func_void_std__optional_Source_.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.omni + +import androidx.annotation.Keep +import com.facebook.jni.HybridData +import com.facebook.proguard.annotations.DoNotStrip +import dalvik.annotation.optimization.FastNative + + +/** + * Represents the JavaScript callback `(value: optional) => void`. + * This can be either implemented in C++ (in which case it might be a callback coming from JS), + * or in Kotlin/Java (in which case it is a native callback). + */ +@DoNotStrip +@Keep +@Suppress("ClassName", "RedundantUnitReturnType") +fun interface Func_void_std__optional_Source_: (Source?) -> Unit { + /** + * Call the given JS callback. + * @throws Throwable if the JS function itself throws an error, or if the JS function/runtime has already been deleted. + */ + @DoNotStrip + @Keep + override fun invoke(value: Source?): Unit +} + +/** + * Represents the JavaScript callback `(value: optional) => void`. + * This is implemented in C++, via a `std::function<...>`. + * The callback might be coming from JS. + */ +@DoNotStrip +@Keep +@Suppress( + "KotlinJniMissingFunction", "unused", + "RedundantSuppression", "RedundantUnitReturnType", "FunctionName", + "ConvertSecondaryConstructorToPrimary", "ClassName", "LocalVariableName", +) +class Func_void_std__optional_Source__cxx: Func_void_std__optional_Source_ { + @DoNotStrip + @Keep + private val mHybridData: HybridData + + @DoNotStrip + @Keep + private constructor(hybridData: HybridData) { + mHybridData = hybridData + } + + @DoNotStrip + @Keep + override fun invoke(value: Source?): Unit + = invoke_cxx(value) + + @FastNative + private external fun invoke_cxx(value: Source?): Unit +} + +/** + * Represents the JavaScript callback `(value: optional) => void`. + * This is implemented in Java/Kotlin, via a `(Source?) -> Unit`. + * The callback is always coming from native. + */ +@DoNotStrip +@Keep +@Suppress("ClassName", "RedundantUnitReturnType", "unused") +class Func_void_std__optional_Source__java(private val function: (Source?) -> Unit): Func_void_std__optional_Source_ { + @DoNotStrip + @Keep + override fun invoke(value: Source?): Unit { + return this.function(value) + } +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/HybridOmniEventMapSpec.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/HybridOmniEventMapSpec.kt index 1d053e9..829a593 100644 --- a/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/HybridOmniEventMapSpec.kt +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/HybridOmniEventMapSpec.kt @@ -100,6 +100,24 @@ abstract class HybridOmniEventMapSpec: HybridObject() { return __result } + abstract fun addSourceListener(cb: (value: Source?) -> Unit): Unit + + @DoNotStrip + @Keep + private fun addSourceListener_cxx(cb: Func_void_std__optional_Source_): Unit { + val __result = addSourceListener(cb) + return __result + } + + abstract fun removeSourceListener(cb: (value: Source?) -> Unit): Unit + + @DoNotStrip + @Keep + private fun removeSourceListener_cxx(cb: Func_void_std__optional_Source_): Unit { + val __result = removeSourceListener(cb) + return __result + } + abstract fun addOnEndListener(cb: () -> Unit): Unit @DoNotStrip diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/HybridOmniPlayerSpec.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/HybridOmniPlayerSpec.kt index c10d27d..55c99a7 100644 --- a/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/HybridOmniPlayerSpec.kt +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/HybridOmniPlayerSpec.kt @@ -35,12 +35,6 @@ abstract class HybridOmniPlayerSpec: HybridObject() { @set:Keep abstract var showNotification: Boolean? - @get:DoNotStrip - @get:Keep - @set:DoNotStrip - @set:Keep - abstract var source: Source? - @get:DoNotStrip @get:Keep abstract val hasPrev: Boolean @@ -65,6 +59,12 @@ abstract class HybridOmniPlayerSpec: HybridObject() { @get:Keep abstract val rendition: Array + @get:DoNotStrip + @get:Keep + @set:DoNotStrip + @set:Keep + abstract var source: Source? + @get:DoNotStrip @get:Keep abstract val status: PlayerStatus diff --git a/nitrogen/generated/shared/c++/HybridOmniEventMapSpec.cpp b/nitrogen/generated/shared/c++/HybridOmniEventMapSpec.cpp index bf38307..ba66ff3 100644 --- a/nitrogen/generated/shared/c++/HybridOmniEventMapSpec.cpp +++ b/nitrogen/generated/shared/c++/HybridOmniEventMapSpec.cpp @@ -22,6 +22,8 @@ namespace margelo::nitro::omni { prototype.registerHybridMethod("removePlayerStatusListener", &HybridOmniEventMapSpec::removePlayerStatusListener); prototype.registerHybridMethod("addCastStatusListener", &HybridOmniEventMapSpec::addCastStatusListener); prototype.registerHybridMethod("removeCastStatusListener", &HybridOmniEventMapSpec::removeCastStatusListener); + prototype.registerHybridMethod("addSourceListener", &HybridOmniEventMapSpec::addSourceListener); + prototype.registerHybridMethod("removeSourceListener", &HybridOmniEventMapSpec::removeSourceListener); prototype.registerHybridMethod("addOnEndListener", &HybridOmniEventMapSpec::addOnEndListener); prototype.registerHybridMethod("removeOnEndListener", &HybridOmniEventMapSpec::removeOnEndListener); prototype.registerHybridMethod("addOnPrevListener", &HybridOmniEventMapSpec::addOnPrevListener); diff --git a/nitrogen/generated/shared/c++/HybridOmniEventMapSpec.hpp b/nitrogen/generated/shared/c++/HybridOmniEventMapSpec.hpp index 7053123..68df47e 100644 --- a/nitrogen/generated/shared/c++/HybridOmniEventMapSpec.hpp +++ b/nitrogen/generated/shared/c++/HybridOmniEventMapSpec.hpp @@ -21,6 +21,8 @@ namespace margelo::nitro::omni { enum class BoolProperty; } namespace margelo::nitro::omni { enum class PlayerStatus; } // Forward declaration of `CastStatus` to properly resolve imports. namespace margelo::nitro::omni { enum class CastStatus; } +// Forward declaration of `Source` to properly resolve imports. +namespace margelo::nitro::omni { struct Source; } // Forward declaration of `Track` to properly resolve imports. namespace margelo::nitro::omni { struct Track; } // Forward declaration of `Rendition` to properly resolve imports. @@ -31,9 +33,10 @@ namespace margelo::nitro::omni { struct Rendition; } #include "BoolProperty.hpp" #include "PlayerStatus.hpp" #include "CastStatus.hpp" +#include "Source.hpp" +#include #include #include "Track.hpp" -#include #include "Rendition.hpp" namespace margelo::nitro::omni { @@ -75,6 +78,8 @@ namespace margelo::nitro::omni { virtual void removePlayerStatusListener(const std::function& cb) = 0; virtual void addCastStatusListener(const std::function& cb) = 0; virtual void removeCastStatusListener(const std::function& cb) = 0; + virtual void addSourceListener(const std::function& /* value */)>& cb) = 0; + virtual void removeSourceListener(const std::function& /* value */)>& cb) = 0; virtual void addOnEndListener(const std::function& cb) = 0; virtual void removeOnEndListener(const std::function& cb) = 0; virtual void addOnPrevListener(const std::function& cb) = 0; diff --git a/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.cpp b/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.cpp index 0c7967d..aa53a94 100644 --- a/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.cpp +++ b/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.cpp @@ -17,14 +17,14 @@ namespace margelo::nitro::omni { prototype.registerHybridGetter("eventMap", &HybridOmniPlayerSpec::getEventMap); prototype.registerHybridGetter("showNotification", &HybridOmniPlayerSpec::getShowNotification); prototype.registerHybridSetter("showNotification", &HybridOmniPlayerSpec::setShowNotification); - prototype.registerHybridGetter("source", &HybridOmniPlayerSpec::getSource); - prototype.registerHybridSetter("source", &HybridOmniPlayerSpec::setSource); prototype.registerHybridGetter("hasPrev", &HybridOmniPlayerSpec::getHasPrev); prototype.registerHybridGetter("hasNext", &HybridOmniPlayerSpec::getHasNext); prototype.registerHybridGetter("videos", &HybridOmniPlayerSpec::getVideos); prototype.registerHybridGetter("audios", &HybridOmniPlayerSpec::getAudios); prototype.registerHybridGetter("subtitles", &HybridOmniPlayerSpec::getSubtitles); prototype.registerHybridGetter("rendition", &HybridOmniPlayerSpec::getRendition); + prototype.registerHybridGetter("source", &HybridOmniPlayerSpec::getSource); + prototype.registerHybridSetter("source", &HybridOmniPlayerSpec::setSource); prototype.registerHybridGetter("status", &HybridOmniPlayerSpec::getStatus); prototype.registerHybridGetter("isPlaying", &HybridOmniPlayerSpec::getIsPlaying); prototype.registerHybridGetter("currentTime", &HybridOmniPlayerSpec::getCurrentTime); diff --git a/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.hpp b/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.hpp index 8052375..b851272 100644 --- a/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.hpp +++ b/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.hpp @@ -15,12 +15,12 @@ // Forward declaration of `HybridOmniEventMapSpec` to properly resolve imports. namespace margelo::nitro::omni { class HybridOmniEventMapSpec; } -// Forward declaration of `Source` to properly resolve imports. -namespace margelo::nitro::omni { struct Source; } // Forward declaration of `Track` to properly resolve imports. namespace margelo::nitro::omni { struct Track; } // Forward declaration of `Rendition` to properly resolve imports. namespace margelo::nitro::omni { struct Rendition; } +// Forward declaration of `Source` to properly resolve imports. +namespace margelo::nitro::omni { struct Source; } // Forward declaration of `PlayerStatus` to properly resolve imports. namespace margelo::nitro::omni { enum class PlayerStatus; } // Forward declaration of `CastStatus` to properly resolve imports. @@ -29,10 +29,10 @@ namespace margelo::nitro::omni { enum class CastStatus; } #include #include "HybridOmniEventMapSpec.hpp" #include -#include "Source.hpp" #include "Track.hpp" #include #include "Rendition.hpp" +#include "Source.hpp" #include "PlayerStatus.hpp" #include "CastStatus.hpp" @@ -66,14 +66,14 @@ namespace margelo::nitro::omni { virtual std::shared_ptr getEventMap() = 0; virtual std::optional getShowNotification() = 0; virtual void setShowNotification(std::optional showNotification) = 0; - virtual std::optional getSource() = 0; - virtual void setSource(const std::optional& source) = 0; virtual bool getHasPrev() = 0; virtual bool getHasNext() = 0; virtual std::vector getVideos() = 0; virtual std::vector getAudios() = 0; virtual std::vector getSubtitles() = 0; virtual std::vector getRendition() = 0; + virtual std::optional getSource() = 0; + virtual void setSource(const std::optional& source) = 0; virtual PlayerStatus getStatus() = 0; virtual bool getIsPlaying() = 0; virtual double getCurrentTime() = 0; diff --git a/src/events.tsx b/src/events.tsx index 4e8518f..160e72c 100644 --- a/src/events.tsx +++ b/src/events.tsx @@ -55,6 +55,9 @@ export function usePlayerState( case "castStatus": em.addCastStatusListener(setState); return () => em.removeCastStatusListener(setState); + case "source": + em.addSourceListener(setState); + return () => em.removeSourceListener(setState); } }, [player, key]); diff --git a/src/events.web.tsx b/src/events.web.tsx index 9483c78..dfe5118 100644 --- a/src/events.web.tsx +++ b/src/events.web.tsx @@ -12,7 +12,7 @@ import { selectVolume, usePlayer as useStoreSelector, } from "@videojs/react"; -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useSyncExternalStore } from "react"; import type { WebOmniPlayer } from "./player.web"; import { usePlayer } from "./provider.web"; import type { OmniEvents } from "./types/events"; @@ -204,6 +204,7 @@ export const stateMapper = { }), }; +// biome-ignore-start lint/correctness/useHookAtTopLevel: key must be const export function usePlayerState( key: Key, ): OmniPlayerState[Key]; @@ -215,8 +216,18 @@ export function usePlayerState( key: Key, _refresh?: number, ): OmniPlayerState[Key] { - const config = stateMapper[key]; + if (key === "source") { + const player = usePlayer() as WebOmniPlayer; + const source = useSyncExternalStore( + player.subscribeSource, + () => player.source, + () => player.source, + ); + return source as OmniPlayerState[Key]; + } + const config = stateMapper[key as keyof Omit]; + const ret = useStoreSelector(config?.selector as Selector); if (!config) throw new Error(`No mapper for ${key}`); - const ret = useStoreSelector(config.selector as Selector); return config.mapper(ret) as OmniPlayerState[Key]; } +// biome-ignore-end lint/correctness/useHookAtTopLevel: key must be const diff --git a/src/player.web.tsx b/src/player.web.tsx index 42cd9a1..1cdb34d 100644 --- a/src/player.web.tsx +++ b/src/player.web.tsx @@ -70,6 +70,13 @@ export class WebOmniPlayer implements OmniPlayer { private overlaySubtitle: Subtitle | null = null; private overlayListeners = new Set<() => void>(); + private sourceListeners = new Set<() => void>(); + subscribeSource = (callback: () => void): (() => void) => { + this.sourceListeners.add(callback); + return () => this.sourceListeners.delete(callback); + }; + + get source(): Source | undefined { return this._source; } @@ -84,6 +91,7 @@ export class WebOmniPlayer implements OmniPlayer { this.setOverlaySubtitle(null); } this.updateMediaSession(); + for (const listener of this.sourceListeners) listener(); } get showNotification(): boolean { diff --git a/src/specs/omni-player.nitro.ts b/src/specs/omni-player.nitro.ts index dcd00ef..45634b4 100644 --- a/src/specs/omni-player.nitro.ts +++ b/src/specs/omni-player.nitro.ts @@ -11,7 +11,7 @@ import type { CastOptions, Source } from "../types/source"; export type NumberProperty = Exclude< keyof OmniPlayerState, - "status" | "isPlaying" | "muted" + "status" | "isPlaying" | "muted" | "source" >; export type BoolProperty = "isPlaying" | "muted" | "isAutoQuality"; @@ -29,6 +29,9 @@ export interface OmniEventMap extends HybridObject<{ android: "kotlin" }> { addCastStatusListener(cb: (value: CastStatus) => void): void; removeCastStatusListener(cb: (value: CastStatus) => void): void; + addSourceListener(cb: (value?: Source) => void): void; + removeSourceListener(cb: (value?: Source) => void): void; + addOnEndListener(cb: OmniEvents["end"]): void; removeOnEndListener(cb: OmniEvents["end"]): void; diff --git a/src/types/player.ts b/src/types/player.ts index d9f8cc3..65890fd 100644 --- a/src/types/player.ts +++ b/src/types/player.ts @@ -3,8 +3,6 @@ import type { Source } from "./source"; export interface OmniPlayer extends OmniPlayerState { showNotification?: boolean; - source?: Source; - play(): void; pause(): void; seekBy(offset: number): void; @@ -28,6 +26,7 @@ export interface OmniPlayer extends OmniPlayerState { } export interface OmniPlayerState { + source?: Source; readonly status: PlayerStatus; readonly isPlaying: boolean; currentTime: number; diff --git a/src/view.web.tsx b/src/view.web.tsx index 6cdb5a6..9eeaf3b 100644 --- a/src/view.web.tsx +++ b/src/view.web.tsx @@ -109,18 +109,18 @@ export const OmniView = ({ const player = usePlayer() as WebOmniPlayer; const containerRef = useRef(null); const ref = useRef(undefined!); + const source = usePlayerState("source"); - const src = player.source?.src; const isHls = - src?.mimeType?.toLowerCase().includes("mpegurl") || - src?.uri.split(/[?#]/)[0]?.toLowerCase().endsWith(".m3u8") || + source?.src?.mimeType?.toLowerCase().includes("mpegurl") || + source?.src?.uri.split(/[?#]/)[0]?.toLowerCase().endsWith(".m3u8") || false; const Tech = isHls ? HlsJsVideo : Video; - const headersRef = useRef(src?.headers); - headersRef.current = src?.headers; - const castId = player.source?.castId; - const castData = player.source?.castData; + const headersRef = useRef(source?.src?.headers); + headersRef.current = source?.src?.headers; + const castId = source?.castId; + const castData = source?.castData; const config = useMemo( () => ({ @@ -151,17 +151,17 @@ export const OmniView = ({ ref={containerRef} style={{ position: "relative", ...style }} > - {src && ( + {source?.src && ( - {(player.source?.subtitles ?? []).map((subtitle) => ( + {(source?.subtitles ?? []).map((subtitle) => ( )}