diff --git a/android/src/main/java/dev/zoriya/omni/OmniPlayer.kt b/android/src/main/java/dev/zoriya/omni/OmniPlayer.kt index 58d7c36..91a3218 100644 --- a/android/src/main/java/dev/zoriya/omni/OmniPlayer.kt +++ b/android/src/main/java/dev/zoriya/omni/OmniPlayer.kt @@ -177,49 +177,52 @@ class OmniPlayer : HybridOmniPlayerSpec() { } } - override fun setSource(src: Source?) { - if (src == null) { + override var source: Source? = null + set(value) { + field = value + if (value == null) { + runOnMainThreadSync { + player.setMediaItem(MediaItem.EMPTY) + player.prepare() + } + return + } + val handleAudioFocus = + (value.mixAudio ?: MixAudioMode.AUTO) != MixAudioMode.MIXWITHOTHERS + val audioAttributes = AudioAttributes.Builder() + .setUsage(C.USAGE_MEDIA) + .setContentType(C.AUDIO_CONTENT_TYPE_MOVIE) + .build() + runOnMainThread { player.setAudioAttributes(audioAttributes, handleAudioFocus) } + val firstSrc = value.src.firstOrNull() + if (firstSrc == null) { + runOnMainThreadSync { + player.setMediaItem(MediaItem.EMPTY) + player.prepare() + } + return + } + + val currentItem = buildMediaItem(firstSrc, value.metadata, value.subtitles) + val mediaItems = mutableListOf() + + if (value.metadata?.hasPrev == true) { + mediaItems.add(currentItem) + } + + mediaItems.add(currentItem) + + if (value.metadata?.hasNext == true) { + mediaItems.add(currentItem) + } + runOnMainThreadSync { - player.setMediaItem(MediaItem.EMPTY) + val startIndex = if (value.metadata?.hasPrev == true) 1 else 0 + val startPositionMs = (value.startTime?.coerceAtLeast(0.0) ?: 0.0) * 1000.0 + player.setMediaItems(mediaItems, startIndex, startPositionMs.toLong()) player.prepare() } - return } - val handleAudioFocus = (src.mixAudio ?: MixAudioMode.AUTO) != MixAudioMode.MIXWITHOTHERS - val audioAttributes = AudioAttributes.Builder() - .setUsage(C.USAGE_MEDIA) - .setContentType(C.AUDIO_CONTENT_TYPE_MOVIE) - .build() - runOnMainThread { player.setAudioAttributes(audioAttributes, handleAudioFocus) } - val source = src.src.firstOrNull() - if (source == null) { - runOnMainThreadSync { - player.setMediaItem(MediaItem.EMPTY) - player.prepare() - } - return - } - - val currentItem = buildMediaItem(source, src.metadata, src.subtitles) - val mediaItems = mutableListOf() - - if (src.metadata?.hasPrev == true) { - mediaItems.add(currentItem) - } - - mediaItems.add(currentItem) - - if (src.metadata?.hasNext == true) { - mediaItems.add(currentItem) - } - - runOnMainThreadSync { - val startIndex = if (src.metadata?.hasPrev == true) 1 else 0 - val startPositionMs = (src.startTime?.coerceAtLeast(0.0) ?: 0.0) * 1000.0 - player.setMediaItems(mediaItems, startIndex, startPositionMs.toLong()) - player.prepare() - } - } override fun play() { runOnMainThreadSync { player.play() } @@ -312,6 +315,7 @@ class OmniPlayer : HybridOmniPlayerSpec() { } } else null } + else -> (0 until group.length).firstOrNull { group.isTrackSelected(it) } } @@ -347,7 +351,12 @@ class OmniPlayer : HybridOmniPlayerSpec() { player.trackSelectionParameters = player.trackSelectionParameters .buildUpon() - .setOverrideForType(TrackSelectionOverride(group.mediaTrackGroup, rendition.id.toInt())) + .setOverrideForType( + TrackSelectionOverride( + group.mediaTrackGroup, + rendition.id.toInt() + ) + ) .build() } } @@ -383,7 +392,8 @@ class OmniPlayerService : MediaSessionService() { .build() setMediaNotificationProvider(DefaultMediaNotificationProvider.Builder(this).build().apply { - setSmallIcon(applicationInfo.icon.takeIf { it != 0 } ?: android.R.drawable.ic_media_play) + setSmallIcon(applicationInfo.icon.takeIf { it != 0 } + ?: android.R.drawable.ic_media_play) }) addSession(mediaSession) setShowNotificationForIdlePlayer(SHOW_NOTIFICATION_FOR_IDLE_PLAYER_ALWAYS) @@ -415,7 +425,8 @@ class OmniPlayerService : MediaSessionService() { ) return NotificationCompat.Builder(this, "omni_playback") - .setSmallIcon(applicationInfo.icon.takeIf { it != 0 } ?: android.R.drawable.ic_media_play) + .setSmallIcon(applicationInfo.icon.takeIf { it != 0 } + ?: android.R.drawable.ic_media_play) .setContentTitle("Omni Player") .setContentText("Preparing playback...") .setContentIntent(pendingIntent) diff --git a/android/src/main/java/dev/zoriya/omni/OmniPlayerFactory.kt b/android/src/main/java/dev/zoriya/omni/OmniPlayerFactory.kt index a6b3917..cb4fd0b 100755 --- a/android/src/main/java/dev/zoriya/omni/OmniPlayerFactory.kt +++ b/android/src/main/java/dev/zoriya/omni/OmniPlayerFactory.kt @@ -9,7 +9,7 @@ import com.margelo.nitro.omni.Source class OmniPlayerFactory(val context: ThemedReactContext) : HybridOmniPlayerFactorySpec() { override fun createPlayer(props: Source?): HybridOmniPlayerSpec { return OmniPlayer().apply { - setSource(props) + source = props } } } diff --git a/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.cpp b/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.cpp index 892680a..1d755ac 100644 --- a/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.cpp +++ b/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.cpp @@ -9,14 +9,6 @@ // 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 `PlayerStatus` to properly resolve imports. -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. @@ -27,25 +19,25 @@ 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. +namespace margelo::nitro::omni { enum class CastStatus; } #include #include "HybridOmniEventMapSpec.hpp" #include "JHybridOmniEventMapSpec.hpp" #include -#include "Track.hpp" -#include -#include "JTrack.hpp" -#include -#include "Rendition.hpp" -#include "JRendition.hpp" -#include "PlayerStatus.hpp" -#include "JPlayerStatus.hpp" -#include "CastStatus.hpp" -#include "JCastStatus.hpp" #include "Source.hpp" #include "JSource.hpp" #include "VideoSrc.hpp" +#include #include "JVideoSrc.hpp" +#include #include #include "Subtitle.hpp" #include "JSubtitle.hpp" @@ -53,6 +45,14 @@ namespace margelo::nitro::omni { enum class MixAudioMode; } #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" +#include "JCastStatus.hpp" namespace margelo::nitro::omni { @@ -98,6 +98,15 @@ 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); @@ -232,10 +241,6 @@ namespace margelo::nitro::omni { } // Methods - 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); - } void JHybridOmniPlayerSpec::play() { static const auto method = _javaPart->javaClassStatic()->getMethod("play"); method(_javaPart); diff --git a/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.hpp b/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.hpp index 0a2a107..82e68e5 100644 --- a/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.hpp +++ b/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.hpp @@ -53,6 +53,8 @@ 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; @@ -76,7 +78,6 @@ namespace margelo::nitro::omni { public: // Methods - void setSource(const std::optional& source) override; void play() override; void pause() override; void seekBy(double offset) override; 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 d3f87ef..c10d27d 100644 --- a/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/HybridOmniPlayerSpec.kt +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/HybridOmniPlayerSpec.kt @@ -35,6 +35,12 @@ 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 @@ -108,10 +114,6 @@ abstract class HybridOmniPlayerSpec: HybridObject() { abstract val castStatus: CastStatus // Methods - @DoNotStrip - @Keep - abstract fun setSource(source: Source?): Unit - @DoNotStrip @Keep abstract fun play(): Unit diff --git a/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.cpp b/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.cpp index c24beb9..0c7967d 100644 --- a/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.cpp +++ b/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.cpp @@ -17,6 +17,8 @@ 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); @@ -37,7 +39,6 @@ namespace margelo::nitro::omni { prototype.registerHybridSetter("muted", &HybridOmniPlayerSpec::setMuted); prototype.registerHybridGetter("isAutoQuality", &HybridOmniPlayerSpec::getIsAutoQuality); prototype.registerHybridGetter("castStatus", &HybridOmniPlayerSpec::getCastStatus); - prototype.registerHybridMethod("setSource", &HybridOmniPlayerSpec::setSource); prototype.registerHybridMethod("play", &HybridOmniPlayerSpec::play); prototype.registerHybridMethod("pause", &HybridOmniPlayerSpec::pause); prototype.registerHybridMethod("seekBy", &HybridOmniPlayerSpec::seekBy); diff --git a/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.hpp b/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.hpp index a5ecc0c..8052375 100644 --- a/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.hpp +++ b/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.hpp @@ -15,6 +15,8 @@ // 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. @@ -23,18 +25,16 @@ namespace margelo::nitro::omni { struct Rendition; } 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; } #include #include "HybridOmniEventMapSpec.hpp" #include +#include "Source.hpp" #include "Track.hpp" #include #include "Rendition.hpp" #include "PlayerStatus.hpp" #include "CastStatus.hpp" -#include "Source.hpp" namespace margelo::nitro::omni { @@ -66,6 +66,8 @@ 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; @@ -89,7 +91,6 @@ namespace margelo::nitro::omni { public: // Methods - virtual void setSource(const std::optional& source) = 0; virtual void play() = 0; virtual void pause() = 0; virtual void seekBy(double offset) = 0; diff --git a/src/player.web.tsx b/src/player.web.tsx index 7042723..6167e6a 100644 --- a/src/player.web.tsx +++ b/src/player.web.tsx @@ -61,7 +61,7 @@ export class WebOmniPlayer implements OmniPlayer { } castOptions: CastOptions | null = null; - _source: Source | null = null; + _source: Source | undefined = undefined; private _showNotification = false; // Selected ASS/PGS subtitle (drawn by the overlay); `null` when the active @@ -70,15 +70,11 @@ export class WebOmniPlayer implements OmniPlayer { private overlaySubtitle: Subtitle | null = null; private overlayListeners = new Set<() => void>(); - get source(): Source | null { + get source(): Source | undefined { return this._source; } - setSource(source?: Source): void { - this.source = source ?? null; - } - - set source(source: Source | null) { + set source(source: Source | undefined) { this._source = source; // Drop the overlay subtitle if it is not part of the new source. if ( diff --git a/src/provider.tsx b/src/provider.tsx index d0d7c5f..9d8b69f 100755 --- a/src/provider.tsx +++ b/src/provider.tsx @@ -24,7 +24,7 @@ export const OmniProvider = ({ const player = useLazyRef(() => ProviderFactory.createPlayer(source)); useEffect(() => { - player.setSource(source); + player.source = source; }, [source]); useEffect(() => { diff --git a/src/provider.web.tsx b/src/provider.web.tsx index 72aaa10..e972276 100644 --- a/src/provider.web.tsx +++ b/src/provider.web.tsx @@ -56,7 +56,7 @@ const PlayerInitializer = ({ const seekedForSrc = useRef(undefined); useEffect(() => { - player.source = source ?? null; + player.source = source; const uri = source?.src[0]?.uri; if (uri !== seekedForSrc.current) { seekedForSrc.current = uri; diff --git a/src/types/player.ts b/src/types/player.ts index 36dbc10..f32f172 100644 --- a/src/types/player.ts +++ b/src/types/player.ts @@ -3,7 +3,7 @@ import type { Source } from "./source"; export interface OmniPlayer extends OmniPlayerState { showNotification?: boolean; - setSource(source?: Source): void; + source?: Source; play(): void; pause(): void;