fix(provider): make source optional

This commit is contained in:
2026-07-21 19:50:30 +02:00
parent a335509b4e
commit 6eb02d32cb
17 changed files with 57 additions and 36 deletions
@@ -177,7 +177,14 @@ class OmniPlayer : HybridOmniPlayerSpec() {
} }
} }
override fun setSource(src: Source) { override fun setSource(src: Source?) {
if (src == null) {
runOnMainThreadSync {
player.setMediaItem(MediaItem.EMPTY)
player.prepare()
}
return
}
val handleAudioFocus = (src.mixAudio ?: MixAudioMode.AUTO) != MixAudioMode.MIXWITHOTHERS val handleAudioFocus = (src.mixAudio ?: MixAudioMode.AUTO) != MixAudioMode.MIXWITHOTHERS
val audioAttributes = AudioAttributes.Builder() val audioAttributes = AudioAttributes.Builder()
.setUsage(C.USAGE_MEDIA) .setUsage(C.USAGE_MEDIA)
@@ -7,7 +7,7 @@ import com.margelo.nitro.omni.HybridOmniPlayerSpec
import com.margelo.nitro.omni.Source import com.margelo.nitro.omni.Source
class OmniPlayerFactory(val context: ThemedReactContext) : HybridOmniPlayerFactorySpec() { class OmniPlayerFactory(val context: ThemedReactContext) : HybridOmniPlayerFactorySpec() {
override fun createPlayer(props: Source): HybridOmniPlayerSpec { override fun createPlayer(props: Source?): HybridOmniPlayerSpec {
return OmniPlayer().apply { return OmniPlayer().apply {
setSource(props) setSource(props)
} }
@@ -24,12 +24,12 @@ namespace margelo::nitro::omni { enum class MixAudioMode; }
#include "HybridOmniPlayerSpec.hpp" #include "HybridOmniPlayerSpec.hpp"
#include "JHybridOmniPlayerSpec.hpp" #include "JHybridOmniPlayerSpec.hpp"
#include "Source.hpp" #include "Source.hpp"
#include <optional>
#include "JSource.hpp" #include "JSource.hpp"
#include "VideoSrc.hpp" #include "VideoSrc.hpp"
#include <vector> #include <vector>
#include "JVideoSrc.hpp" #include "JVideoSrc.hpp"
#include <string> #include <string>
#include <optional>
#include <unordered_map> #include <unordered_map>
#include "Subtitle.hpp" #include "Subtitle.hpp"
#include "JSubtitle.hpp" #include "JSubtitle.hpp"
@@ -71,9 +71,9 @@ namespace margelo::nitro::omni {
// Methods // Methods
std::shared_ptr<HybridOmniPlayerSpec> JHybridOmniPlayerFactorySpec::createPlayer(const Source& props) { std::shared_ptr<HybridOmniPlayerSpec> JHybridOmniPlayerFactorySpec::createPlayer(const std::optional<Source>& props) {
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JHybridOmniPlayerSpec::JavaPart>(jni::alias_ref<JSource> /* props */)>("createPlayer"); static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JHybridOmniPlayerSpec::JavaPart>(jni::alias_ref<JSource> /* props */)>("createPlayer");
auto __result = method(_javaPart, JSource::fromCpp(props)); auto __result = method(_javaPart, props.has_value() ? JSource::fromCpp(props.value()) : nullptr);
return __result->getJHybridOmniPlayerSpec(); return __result->getJHybridOmniPlayerSpec();
} }
@@ -54,7 +54,7 @@ namespace margelo::nitro::omni {
public: public:
// Methods // Methods
std::shared_ptr<HybridOmniPlayerSpec> createPlayer(const Source& props) override; std::shared_ptr<HybridOmniPlayerSpec> createPlayer(const std::optional<Source>& props) override;
private: private:
jni::global_ref<JHybridOmniPlayerFactorySpec::JavaPart> _javaPart; jni::global_ref<JHybridOmniPlayerFactorySpec::JavaPart> _javaPart;
+2 -2
View File
@@ -232,9 +232,9 @@ namespace margelo::nitro::omni {
} }
// Methods // Methods
void JHybridOmniPlayerSpec::setSource(const Source& src) { void JHybridOmniPlayerSpec::setSource(const std::optional<Source>& src) {
static const auto method = _javaPart->javaClassStatic()->getMethod<void(jni::alias_ref<JSource> /* src */)>("setSource"); static const auto method = _javaPart->javaClassStatic()->getMethod<void(jni::alias_ref<JSource> /* src */)>("setSource");
method(_javaPart, JSource::fromCpp(src)); method(_javaPart, src.has_value() ? JSource::fromCpp(src.value()) : nullptr);
} }
void JHybridOmniPlayerSpec::play() { void JHybridOmniPlayerSpec::play() {
static const auto method = _javaPart->javaClassStatic()->getMethod<void()>("play"); static const auto method = _javaPart->javaClassStatic()->getMethod<void()>("play");
+1 -1
View File
@@ -76,7 +76,7 @@ namespace margelo::nitro::omni {
public: public:
// Methods // Methods
void setSource(const Source& src) override; void setSource(const std::optional<Source>& src) override;
void play() override; void play() override;
void pause() override; void pause() override;
void seekBy(double offset) override; void seekBy(double offset) override;
+5 -1
View File
@@ -54,6 +54,8 @@ namespace margelo::nitro::omni {
jni::local_ref<JMetadata> metadata = this->getFieldValue(fieldMetadata); jni::local_ref<JMetadata> metadata = this->getFieldValue(fieldMetadata);
static const auto fieldMixAudio = clazz->getField<JMixAudioMode>("mixAudio"); static const auto fieldMixAudio = clazz->getField<JMixAudioMode>("mixAudio");
jni::local_ref<JMixAudioMode> mixAudio = this->getFieldValue(fieldMixAudio); jni::local_ref<JMixAudioMode> mixAudio = this->getFieldValue(fieldMixAudio);
static const auto fieldCastId = clazz->getField<jni::JString>("castId");
jni::local_ref<jni::JString> castId = this->getFieldValue(fieldCastId);
static const auto fieldCastData = clazz->getField<jni::JMap<jni::JString, jni::JString>>("castData"); static const auto fieldCastData = clazz->getField<jni::JMap<jni::JString, jni::JString>>("castData");
jni::local_ref<jni::JMap<jni::JString, jni::JString>> castData = this->getFieldValue(fieldCastData); jni::local_ref<jni::JMap<jni::JString, jni::JString>> castData = this->getFieldValue(fieldCastData);
return Source( return Source(
@@ -90,6 +92,7 @@ namespace margelo::nitro::omni {
}(fonts)) : std::nullopt, }(fonts)) : std::nullopt,
metadata != nullptr ? std::make_optional(metadata->toCpp()) : std::nullopt, metadata != nullptr ? std::make_optional(metadata->toCpp()) : std::nullopt,
mixAudio != nullptr ? std::make_optional(mixAudio->toCpp()) : std::nullopt, mixAudio != nullptr ? std::make_optional(mixAudio->toCpp()) : std::nullopt,
castId != nullptr ? std::make_optional(castId->toStdString()) : std::nullopt,
castData != nullptr ? std::make_optional([&]() { castData != nullptr ? std::make_optional([&]() {
std::unordered_map<std::string, std::string> __map; std::unordered_map<std::string, std::string> __map;
__map.reserve(castData->size()); __map.reserve(castData->size());
@@ -107,7 +110,7 @@ namespace margelo::nitro::omni {
*/ */
[[maybe_unused]] [[maybe_unused]]
static jni::local_ref<JSource::javaobject> fromCpp(const Source& value) { static jni::local_ref<JSource::javaobject> fromCpp(const Source& value) {
using JSignature = JSource(jni::alias_ref<jni::JArrayClass<JVideoSrc>>, jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JArrayClass<JSubtitle>>, jni::alias_ref<jni::JArrayClass<jni::JString>>, jni::alias_ref<JMetadata>, jni::alias_ref<JMixAudioMode>, jni::alias_ref<jni::JMap<jni::JString, jni::JString>>); using JSignature = JSource(jni::alias_ref<jni::JArrayClass<JVideoSrc>>, jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JArrayClass<JSubtitle>>, jni::alias_ref<jni::JArrayClass<jni::JString>>, jni::alias_ref<JMetadata>, jni::alias_ref<JMixAudioMode>, jni::alias_ref<jni::JString>, jni::alias_ref<jni::JMap<jni::JString, jni::JString>>);
static const auto clazz = javaClassStatic(); static const auto clazz = javaClassStatic();
static const auto create = clazz->getStaticMethod<JSignature>("fromCpp"); static const auto create = clazz->getStaticMethod<JSignature>("fromCpp");
return create( return create(
@@ -145,6 +148,7 @@ namespace margelo::nitro::omni {
}(value.fonts.value()) : nullptr, }(value.fonts.value()) : nullptr,
value.metadata.has_value() ? JMetadata::fromCpp(value.metadata.value()) : nullptr, value.metadata.has_value() ? JMetadata::fromCpp(value.metadata.value()) : nullptr,
value.mixAudio.has_value() ? JMixAudioMode::fromCpp(value.mixAudio.value()) : nullptr, value.mixAudio.has_value() ? JMixAudioMode::fromCpp(value.mixAudio.value()) : nullptr,
value.castId.has_value() ? jni::make_jstring(value.castId.value()) : nullptr,
value.castData.has_value() ? [&]() -> jni::local_ref<jni::JMap<jni::JString, jni::JString>> { value.castData.has_value() ? [&]() -> jni::local_ref<jni::JMap<jni::JString, jni::JString>> {
auto __map = jni::JHashMap<jni::JString, jni::JString>::create(value.castData.value().size()); auto __map = jni::JHashMap<jni::JString, jni::JString>::create(value.castData.value().size());
for (const auto& __entry : value.castData.value()) { for (const auto& __entry : value.castData.value()) {
@@ -30,7 +30,7 @@ abstract class HybridOmniPlayerFactorySpec: HybridObject() {
// Methods // Methods
@DoNotStrip @DoNotStrip
@Keep @Keep
abstract fun createPlayer(props: Source): HybridOmniPlayerSpec abstract fun createPlayer(props: Source?): HybridOmniPlayerSpec
// Default implementation of `HybridObject.toString()` // Default implementation of `HybridObject.toString()`
override fun toString(): String { override fun toString(): String {
@@ -110,7 +110,7 @@ abstract class HybridOmniPlayerSpec: HybridObject() {
// Methods // Methods
@DoNotStrip @DoNotStrip
@Keep @Keep
abstract fun setSource(src: Source): Unit abstract fun setSource(src: Source?): Unit
@DoNotStrip @DoNotStrip
@Keep @Keep
@@ -38,6 +38,9 @@ data class Source(
val mixAudio: MixAudioMode?, val mixAudio: MixAudioMode?,
@DoNotStrip @DoNotStrip
@Keep @Keep
val castId: String?,
@DoNotStrip
@Keep
val castData: Map<String, String>? val castData: Map<String, String>?
) { ) {
/* primary constructor */ /* primary constructor */
@@ -51,6 +54,7 @@ data class Source(
&& Objects.deepEquals(this.fonts, other.fonts) && Objects.deepEquals(this.fonts, other.fonts)
&& Objects.deepEquals(this.metadata, other.metadata) && Objects.deepEquals(this.metadata, other.metadata)
&& Objects.deepEquals(this.mixAudio, other.mixAudio) && Objects.deepEquals(this.mixAudio, other.mixAudio)
&& Objects.deepEquals(this.castId, other.castId)
&& Objects.deepEquals(this.castData, other.castData) && Objects.deepEquals(this.castData, other.castData)
} }
@@ -62,6 +66,7 @@ data class Source(
fonts, fonts,
metadata, metadata,
mixAudio, mixAudio,
castId,
castData castData
).contentDeepHashCode() ).contentDeepHashCode()
} }
@@ -74,8 +79,8 @@ data class Source(
@Keep @Keep
@Suppress("unused") @Suppress("unused")
@JvmStatic @JvmStatic
private fun fromCpp(src: Array<VideoSrc>, startTime: Double?, subtitles: Array<Subtitle>, fonts: Array<String>?, metadata: Metadata?, mixAudio: MixAudioMode?, castData: Map<String, String>?): Source { private fun fromCpp(src: Array<VideoSrc>, startTime: Double?, subtitles: Array<Subtitle>, fonts: Array<String>?, metadata: Metadata?, mixAudio: MixAudioMode?, castId: String?, castData: Map<String, String>?): Source {
return Source(src, startTime, subtitles, fonts, metadata, mixAudio, castData) return Source(src, startTime, subtitles, fonts, metadata, mixAudio, castId, castData)
} }
} }
} }
@@ -21,6 +21,7 @@ namespace margelo::nitro::omni { struct Source; }
#include <memory> #include <memory>
#include "HybridOmniPlayerSpec.hpp" #include "HybridOmniPlayerSpec.hpp"
#include "Source.hpp" #include "Source.hpp"
#include <optional>
namespace margelo::nitro::omni { namespace margelo::nitro::omni {
@@ -53,7 +54,7 @@ namespace margelo::nitro::omni {
public: public:
// Methods // Methods
virtual std::shared_ptr<HybridOmniPlayerSpec> createPlayer(const Source& props) = 0; virtual std::shared_ptr<HybridOmniPlayerSpec> createPlayer(const std::optional<Source>& props) = 0;
protected: protected:
// Hybrid Setup // Hybrid Setup
+1 -1
View File
@@ -89,7 +89,7 @@ namespace margelo::nitro::omni {
public: public:
// Methods // Methods
virtual void setSource(const Source& src) = 0; virtual void setSource(const std::optional<Source>& src) = 0;
virtual void play() = 0; virtual void play() = 0;
virtual void pause() = 0; virtual void pause() = 0;
virtual void seekBy(double offset) = 0; virtual void seekBy(double offset) = 0;
+5 -1
View File
@@ -59,11 +59,12 @@ namespace margelo::nitro::omni {
std::optional<std::vector<std::string>> fonts SWIFT_PRIVATE; std::optional<std::vector<std::string>> fonts SWIFT_PRIVATE;
std::optional<Metadata> metadata SWIFT_PRIVATE; std::optional<Metadata> metadata SWIFT_PRIVATE;
std::optional<MixAudioMode> mixAudio SWIFT_PRIVATE; std::optional<MixAudioMode> mixAudio SWIFT_PRIVATE;
std::optional<std::string> castId SWIFT_PRIVATE;
std::optional<std::unordered_map<std::string, std::string>> castData SWIFT_PRIVATE; std::optional<std::unordered_map<std::string, std::string>> castData SWIFT_PRIVATE;
public: public:
Source() = default; Source() = default;
explicit Source(std::vector<VideoSrc> src, std::optional<double> startTime, std::vector<Subtitle> subtitles, std::optional<std::vector<std::string>> fonts, std::optional<Metadata> metadata, std::optional<MixAudioMode> mixAudio, std::optional<std::unordered_map<std::string, std::string>> castData): src(src), startTime(startTime), subtitles(subtitles), fonts(fonts), metadata(metadata), mixAudio(mixAudio), castData(castData) {} explicit Source(std::vector<VideoSrc> src, std::optional<double> startTime, std::vector<Subtitle> subtitles, std::optional<std::vector<std::string>> fonts, std::optional<Metadata> metadata, std::optional<MixAudioMode> mixAudio, std::optional<std::string> castId, std::optional<std::unordered_map<std::string, std::string>> castData): src(src), startTime(startTime), subtitles(subtitles), fonts(fonts), metadata(metadata), mixAudio(mixAudio), castId(castId), castData(castData) {}
public: public:
friend bool operator==(const Source& lhs, const Source& rhs) = default; friend bool operator==(const Source& lhs, const Source& rhs) = default;
@@ -85,6 +86,7 @@ namespace margelo::nitro {
JSIConverter<std::optional<std::vector<std::string>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "fonts"))), JSIConverter<std::optional<std::vector<std::string>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "fonts"))),
JSIConverter<std::optional<margelo::nitro::omni::Metadata>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "metadata"))), JSIConverter<std::optional<margelo::nitro::omni::Metadata>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "metadata"))),
JSIConverter<std::optional<margelo::nitro::omni::MixAudioMode>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "mixAudio"))), JSIConverter<std::optional<margelo::nitro::omni::MixAudioMode>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "mixAudio"))),
JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "castId"))),
JSIConverter<std::optional<std::unordered_map<std::string, std::string>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "castData"))) JSIConverter<std::optional<std::unordered_map<std::string, std::string>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "castData")))
); );
} }
@@ -96,6 +98,7 @@ namespace margelo::nitro {
obj.setProperty(runtime, PropNameIDCache::get(runtime, "fonts"), JSIConverter<std::optional<std::vector<std::string>>>::toJSI(runtime, arg.fonts)); obj.setProperty(runtime, PropNameIDCache::get(runtime, "fonts"), JSIConverter<std::optional<std::vector<std::string>>>::toJSI(runtime, arg.fonts));
obj.setProperty(runtime, PropNameIDCache::get(runtime, "metadata"), JSIConverter<std::optional<margelo::nitro::omni::Metadata>>::toJSI(runtime, arg.metadata)); obj.setProperty(runtime, PropNameIDCache::get(runtime, "metadata"), JSIConverter<std::optional<margelo::nitro::omni::Metadata>>::toJSI(runtime, arg.metadata));
obj.setProperty(runtime, PropNameIDCache::get(runtime, "mixAudio"), JSIConverter<std::optional<margelo::nitro::omni::MixAudioMode>>::toJSI(runtime, arg.mixAudio)); obj.setProperty(runtime, PropNameIDCache::get(runtime, "mixAudio"), JSIConverter<std::optional<margelo::nitro::omni::MixAudioMode>>::toJSI(runtime, arg.mixAudio));
obj.setProperty(runtime, PropNameIDCache::get(runtime, "castId"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.castId));
obj.setProperty(runtime, PropNameIDCache::get(runtime, "castData"), JSIConverter<std::optional<std::unordered_map<std::string, std::string>>>::toJSI(runtime, arg.castData)); obj.setProperty(runtime, PropNameIDCache::get(runtime, "castData"), JSIConverter<std::optional<std::unordered_map<std::string, std::string>>>::toJSI(runtime, arg.castData));
return obj; return obj;
} }
@@ -113,6 +116,7 @@ namespace margelo::nitro {
if (!JSIConverter<std::optional<std::vector<std::string>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "fonts")))) return false; if (!JSIConverter<std::optional<std::vector<std::string>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "fonts")))) return false;
if (!JSIConverter<std::optional<margelo::nitro::omni::Metadata>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "metadata")))) return false; if (!JSIConverter<std::optional<margelo::nitro::omni::Metadata>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "metadata")))) return false;
if (!JSIConverter<std::optional<margelo::nitro::omni::MixAudioMode>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "mixAudio")))) return false; if (!JSIConverter<std::optional<margelo::nitro::omni::MixAudioMode>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "mixAudio")))) return false;
if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "castId")))) return false;
if (!JSIConverter<std::optional<std::unordered_map<std::string, std::string>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "castData")))) return false; if (!JSIConverter<std::optional<std::unordered_map<std::string, std::string>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "castData")))) return false;
return true; return true;
} }
+11 -11
View File
@@ -1,13 +1,13 @@
{ {
"uiViewClassName": "OmniView", "uiViewClassName": "OmniView",
"supportsRawText": false, "supportsRawText": false,
"bubblingEventTypes": {}, "bubblingEventTypes": {},
"directEventTypes": {}, "directEventTypes": {},
"validAttributes": { "validAttributes": {
"player": true, "player": true,
"autoplay": true, "autoplay": true,
"autoPip": true, "autoPip": true,
"subtitleAssets": true, "subtitleAssets": true,
"hybridRef": true "hybridRef": true
} }
} }
+1 -1
View File
@@ -16,7 +16,7 @@ export const OmniProvider = ({
showNotification = false, showNotification = false,
cast: _, cast: _,
}: { }: {
source: Source; source?: Source;
cast?: CastOptions; cast?: CastOptions;
children: ReactNode; children: ReactNode;
showNotification?: boolean; showNotification?: boolean;
+5 -5
View File
@@ -22,7 +22,7 @@ export const OmniProvider = ({
cast, cast,
showNotification = false, showNotification = false,
}: { }: {
source: Source; source?: Source;
cast?: CastOptions; cast?: CastOptions;
children: ReactNode; children: ReactNode;
showNotification?: boolean; showNotification?: boolean;
@@ -47,7 +47,7 @@ const PlayerInitializer = ({
showNotification, showNotification,
}: { }: {
children: ReactNode; children: ReactNode;
source: Source; source?: Source;
cast?: CastOptions; cast?: CastOptions;
showNotification: boolean; showNotification: boolean;
}) => { }) => {
@@ -56,11 +56,11 @@ const PlayerInitializer = ({
const seekedForSrc = useRef<string | undefined>(undefined); const seekedForSrc = useRef<string | undefined>(undefined);
useEffect(() => { useEffect(() => {
player.source = source; player.source = source ?? null;
const uri = source.src[0]?.uri; const uri = source?.src[0]?.uri;
if (uri !== seekedForSrc.current) { if (uri !== seekedForSrc.current) {
seekedForSrc.current = uri; seekedForSrc.current = uri;
if (source.startTime) store.seek(source.startTime); if (source?.startTime) store.seek(source.startTime);
} }
}, [source, store]); }, [source, store]);
+2 -2
View File
@@ -57,9 +57,9 @@ export interface OmniPlayer
OmniPlayerT { OmniPlayerT {
readonly eventMap: OmniEventMap; readonly eventMap: OmniEventMap;
setSource(src: Source): void; setSource(src?: Source): void;
} }
export interface OmniPlayerFactory extends HybridObject<{ android: "kotlin" }> { export interface OmniPlayerFactory extends HybridObject<{ android: "kotlin" }> {
createPlayer(props: Source): OmniPlayer; createPlayer(props?: Source): OmniPlayer;
} }