fix(web): make source a listenable state, fixes the view

This commit is contained in:
2026-07-31 19:43:49 +02:00
parent d2ca76b4a9
commit 0c7c2b3848
21 changed files with 323 additions and 54 deletions
@@ -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<BoolProperty, MutableSet<(Boolean) -> 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)
}
@@ -306,6 +306,7 @@ class OmniPlayer(
override var source: Source? = null
set(value) {
field = value
eventMap.emitSource(value)
if (value == null) {
runOnMainThreadSync {
player.clearMediaItems()
+2
View File
@@ -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();
@@ -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 <fbjni/fbjni.h>
#include <functional>
#include "Source.hpp"
#include <optional>
#include <functional>
#include <NitroModules/JNICallable.hpp>
#include "JSource.hpp"
#include "VideoSrc.hpp"
#include "JVideoSrc.hpp"
#include <string>
#include <unordered_map>
#include "Subtitle.hpp"
#include <vector>
#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<JFunc_void_std__optional_Source_> {
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<Source>& value) const {
static const auto method = javaClassStatic()->getMethod<void(jni::alias_ref<JSource> /* 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<JFunc_void_std__optional_Source__cxx, JFunc_void_std__optional_Source_> {
public:
static jni::local_ref<JFunc_void_std__optional_Source_::javaobject> fromCpp(const std::function<void(const std::optional<Source>& /* 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<JSource> value) {
_func(value != nullptr ? std::make_optional(value->toCpp()) : std::nullopt);
}
public:
[[nodiscard]]
inline const std::function<void(const std::optional<Source>& /* 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<void(const std::optional<Source>& /* value */)>& func): _func(func) { }
private:
friend HybridBase;
std::function<void(const std::optional<Source>& /* value */)> _func;
};
} // namespace margelo::nitro::omni
+33 -2
View File
@@ -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 <optional>
#include "JFunc_void_std__optional_Source_.hpp"
#include "JSource.hpp"
#include "VideoSrc.hpp"
#include "JVideoSrc.hpp"
#include <string>
#include <unordered_map>
#include "Subtitle.hpp"
#include <vector>
#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 <optional>
#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<void(jni::alias_ref<JFunc_void_CastStatus::javaobject> /* cb */)>("removeCastStatusListener_cxx");
method(_javaPart, JFunc_void_CastStatus_cxx::fromCpp(cb));
}
void JHybridOmniEventMapSpec::addSourceListener(const std::function<void(const std::optional<Source>& /* value */)>& cb) {
static const auto method = _javaPart->javaClassStatic()->getMethod<void(jni::alias_ref<JFunc_void_std__optional_Source_::javaobject> /* cb */)>("addSourceListener_cxx");
method(_javaPart, JFunc_void_std__optional_Source__cxx::fromCpp(cb));
}
void JHybridOmniEventMapSpec::removeSourceListener(const std::function<void(const std::optional<Source>& /* value */)>& cb) {
static const auto method = _javaPart->javaClassStatic()->getMethod<void(jni::alias_ref<JFunc_void_std__optional_Source_::javaobject> /* cb */)>("removeSourceListener_cxx");
method(_javaPart, JFunc_void_std__optional_Source__cxx::fromCpp(cb));
}
void JHybridOmniEventMapSpec::addOnEndListener(const std::function<void()>& cb) {
static const auto method = _javaPart->javaClassStatic()->getMethod<void(jni::alias_ref<JFunc_void::javaobject> /* cb */)>("addOnEndListener_cxx");
method(_javaPart, JFunc_void_cxx::fromCpp(cb));
@@ -62,6 +62,8 @@ namespace margelo::nitro::omni {
void removePlayerStatusListener(const std::function<void(PlayerStatus /* value */)>& cb) override;
void addCastStatusListener(const std::function<void(CastStatus /* value */)>& cb) override;
void removeCastStatusListener(const std::function<void(CastStatus /* value */)>& cb) override;
void addSourceListener(const std::function<void(const std::optional<Source>& /* value */)>& cb) override;
void removeSourceListener(const std::function<void(const std::optional<Source>& /* value */)>& cb) override;
void addOnEndListener(const std::function<void()>& cb) override;
void removeOnEndListener(const std::function<void()>& cb) override;
void addOnPrevListener(const std::function<void()>& cb) override;
+19 -19
View File
@@ -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 <optional>
#include "Track.hpp"
#include <vector>
#include "JTrack.hpp"
#include <string>
#include "Rendition.hpp"
#include "JRendition.hpp"
#include "Source.hpp"
#include "JSource.hpp"
#include "VideoSrc.hpp"
#include "JVideoSrc.hpp"
#include <string>
#include <unordered_map>
#include "Subtitle.hpp"
#include <vector>
#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<void(jni::alias_ref<jni::JBoolean> /* showNotification */)>("setShowNotification");
method(_javaPart, showNotification.has_value() ? jni::JBoolean::valueOf(showNotification.value()) : nullptr);
}
std::optional<Source> JHybridOmniPlayerSpec::getSource() {
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JSource>()>("getSource");
auto __result = method(_javaPart);
return __result != nullptr ? std::make_optional(__result->toCpp()) : std::nullopt;
}
void JHybridOmniPlayerSpec::setSource(const std::optional<Source>& source) {
static const auto method = _javaPart->javaClassStatic()->getMethod<void(jni::alias_ref<JSource> /* source */)>("setSource");
method(_javaPart, source.has_value() ? JSource::fromCpp(source.value()) : nullptr);
}
bool JHybridOmniPlayerSpec::getHasPrev() {
static const auto method = _javaPart->javaClassStatic()->getMethod<jboolean()>("getHasPrev");
auto __result = method(_javaPart);
@@ -173,6 +164,15 @@ namespace margelo::nitro::omni {
return __vector;
}(__result);
}
std::optional<Source> JHybridOmniPlayerSpec::getSource() {
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JSource>()>("getSource");
auto __result = method(_javaPart);
return __result != nullptr ? std::make_optional(__result->toCpp()) : std::nullopt;
}
void JHybridOmniPlayerSpec::setSource(const std::optional<Source>& source) {
static const auto method = _javaPart->javaClassStatic()->getMethod<void(jni::alias_ref<JSource> /* source */)>("setSource");
method(_javaPart, source.has_value() ? JSource::fromCpp(source.value()) : nullptr);
}
PlayerStatus JHybridOmniPlayerSpec::getStatus() {
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JPlayerStatus>()>("getStatus");
auto __result = method(_javaPart);
+2 -2
View File
@@ -53,14 +53,14 @@ namespace margelo::nitro::omni {
std::shared_ptr<HybridOmniEventMapSpec> getEventMap() override;
std::optional<bool> getShowNotification() override;
void setShowNotification(std::optional<bool> showNotification) override;
std::optional<Source> getSource() override;
void setSource(const std::optional<Source>& source) override;
bool getHasPrev() override;
bool getHasNext() override;
std::vector<Track> getVideos() override;
std::vector<Track> getAudios() override;
std::vector<Track> getSubtitles() override;
std::vector<Rendition> getRendition() override;
std::optional<Source> getSource() override;
void setSource(const std::optional<Source>& source) override;
PlayerStatus getStatus() override;
bool getIsPlaying() override;
double getCurrentTime() override;
@@ -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)
}
}
@@ -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
@@ -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<Rendition>
@get:DoNotStrip
@get:Keep
@set:DoNotStrip
@set:Keep
abstract var source: Source?
@get:DoNotStrip
@get:Keep
abstract val status: PlayerStatus
@@ -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);
+6 -1
View File
@@ -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 <optional>
#include <string>
#include "Track.hpp"
#include <optional>
#include "Rendition.hpp"
namespace margelo::nitro::omni {
@@ -75,6 +78,8 @@ namespace margelo::nitro::omni {
virtual void removePlayerStatusListener(const std::function<void(PlayerStatus /* value */)>& cb) = 0;
virtual void addCastStatusListener(const std::function<void(CastStatus /* value */)>& cb) = 0;
virtual void removeCastStatusListener(const std::function<void(CastStatus /* value */)>& cb) = 0;
virtual void addSourceListener(const std::function<void(const std::optional<Source>& /* value */)>& cb) = 0;
virtual void removeSourceListener(const std::function<void(const std::optional<Source>& /* value */)>& cb) = 0;
virtual void addOnEndListener(const std::function<void()>& cb) = 0;
virtual void removeOnEndListener(const std::function<void()>& cb) = 0;
virtual void addOnPrevListener(const std::function<void()>& cb) = 0;
+2 -2
View File
@@ -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);
+5 -5
View File
@@ -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 <memory>
#include "HybridOmniEventMapSpec.hpp"
#include <optional>
#include "Source.hpp"
#include "Track.hpp"
#include <vector>
#include "Rendition.hpp"
#include "Source.hpp"
#include "PlayerStatus.hpp"
#include "CastStatus.hpp"
@@ -66,14 +66,14 @@ namespace margelo::nitro::omni {
virtual std::shared_ptr<HybridOmniEventMapSpec> getEventMap() = 0;
virtual std::optional<bool> getShowNotification() = 0;
virtual void setShowNotification(std::optional<bool> showNotification) = 0;
virtual std::optional<Source> getSource() = 0;
virtual void setSource(const std::optional<Source>& source) = 0;
virtual bool getHasPrev() = 0;
virtual bool getHasNext() = 0;
virtual std::vector<Track> getVideos() = 0;
virtual std::vector<Track> getAudios() = 0;
virtual std::vector<Track> getSubtitles() = 0;
virtual std::vector<Rendition> getRendition() = 0;
virtual std::optional<Source> getSource() = 0;
virtual void setSource(const std::optional<Source>& source) = 0;
virtual PlayerStatus getStatus() = 0;
virtual bool getIsPlaying() = 0;
virtual double getCurrentTime() = 0;
+3
View File
@@ -55,6 +55,9 @@ export function usePlayerState<Key extends keyof OmniPlayerState>(
case "castStatus":
em.addCastStatusListener(setState);
return () => em.removeCastStatusListener(setState);
case "source":
em.addSourceListener(setState);
return () => em.removeSourceListener(setState);
}
}, [player, key]);
+14 -3
View File
@@ -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 extends keyof OmniPlayerState>(
key: Key,
): OmniPlayerState[Key];
@@ -215,8 +216,18 @@ export function usePlayerState<Key extends keyof OmniPlayerState>(
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<OmniPlayerState, "source">];
const ret = useStoreSelector(config?.selector as Selector<any, any>);
if (!config) throw new Error(`No mapper for ${key}`);
const ret = useStoreSelector(config.selector as Selector<any, any>);
return config.mapper(ret) as OmniPlayerState[Key];
}
// biome-ignore-end lint/correctness/useHookAtTopLevel: key must be const
+8
View File
@@ -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 {
+4 -1
View File
@@ -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;
+1 -2
View File
@@ -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;
+11 -11
View File
@@ -109,18 +109,18 @@ export const OmniView = ({
const player = usePlayer() as WebOmniPlayer;
const containerRef = useRef<HTMLDivElement>(null);
const ref = useRef<HTMLVideoElement>(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<HlsMediaConfig>(
() => ({
@@ -151,17 +151,17 @@ export const OmniView = ({
ref={containerRef}
style={{ position: "relative", ...style }}
>
{src && (
{source?.src && (
<Tech
ref={ref}
src={src.uri}
src={source?.src.uri}
config={config}
autoPlay={autoplay}
playsInline
crossOrigin="anonymous"
style={{ width: "100%", height: "100%", objectFit: "contain" }}
>
{(player.source?.subtitles ?? []).map((subtitle) => (
{(source?.subtitles ?? []).map((subtitle) => (
<track
key={subtitle.id}
id={subtitle.id}
@@ -177,7 +177,7 @@ export const OmniView = ({
<SubtitleOverlay
video={ref}
assets={subtitleAssets}
fonts={player.source?.fonts}
fonts={source?.fonts}
/>
)}
</VideoPlayer.Container>