mirror of
https://github.com/zoriya/react-native-omni.git
synced 2026-08-13 09:29:00 +00:00
feat(tracks): add track indexes
This commit is contained in:
@@ -117,21 +117,13 @@ class EventMap(private val tracks: TrackProvider) : HybridOmniEventMapSpec(), Pl
|
||||
}
|
||||
|
||||
private fun selectedTrack(trackType: Int): Track? {
|
||||
val groups = player.currentTracks.groups.filter { it.type == trackType }
|
||||
for (group in groups) {
|
||||
val mediaGroup = group.mediaTrackGroup
|
||||
for (i in 0 until group.length) {
|
||||
if (!group.isTrackSelected(i)) continue
|
||||
val format = group.getTrackFormat(i)
|
||||
return Track(
|
||||
id = format.id ?: mediaGroup.id,
|
||||
label = format.label,
|
||||
language = format.language,
|
||||
selected = true
|
||||
)
|
||||
}
|
||||
val list = when (trackType) {
|
||||
TRACK_TYPE_VIDEO -> tracks.videos
|
||||
TRACK_TYPE_AUDIO -> tracks.audios
|
||||
TRACK_TYPE_TEXT -> tracks.subtitles
|
||||
else -> return null
|
||||
}
|
||||
return null
|
||||
return list.firstOrNull { it.selected }
|
||||
}
|
||||
|
||||
private fun getCurrentRendition(): Rendition? {
|
||||
@@ -265,9 +257,8 @@ class EventMap(private val tracks: TrackProvider) : HybridOmniEventMapSpec(), Pl
|
||||
selectedTrack(TRACK_TYPE_AUDIO)?.let { track ->
|
||||
onAudioTrackChangeListeners.forEach { it(track) }
|
||||
}
|
||||
selectedTrack(TRACK_TYPE_TEXT)?.let { track ->
|
||||
onSubtitleChangeListeners.forEach { it(track) }
|
||||
}
|
||||
val subtitle = selectedTrack(TRACK_TYPE_TEXT)
|
||||
onSubtitleChangeListeners.forEach { it(subtitle) }
|
||||
emitTracks()
|
||||
emitIsAutoQualityChange()
|
||||
emitRenditionChange()
|
||||
|
||||
@@ -451,13 +451,19 @@ class OmniPlayer(
|
||||
// when casting tracks are stripped of some metadata, recover it
|
||||
val castTracks = castMediaTracksById()
|
||||
|
||||
// external (sideloaded) subs aren't in the container, so their index is -1
|
||||
val sideloadedIds = source?.subtitles?.map { it.id }?.toHashSet().orEmpty()
|
||||
var index = 0
|
||||
|
||||
return groups.map { group ->
|
||||
val format = group.getTrackFormat(0)
|
||||
|
||||
val castTrackId = group.mediaTrackGroup.id.substringAfterLast("track=", "").toLongOrNull()
|
||||
val id = group.mediaTrackGroup.id
|
||||
val castTrackId = id.substringAfterLast("track=", "").toLongOrNull()
|
||||
val cast = castTracks[castTrackId]
|
||||
val sideloaded = trackType == C.TRACK_TYPE_TEXT && id in sideloadedIds
|
||||
Track(
|
||||
id = group.mediaTrackGroup.id,
|
||||
id = id,
|
||||
index = if (sideloaded) -1.0 else (index++).toDouble(),
|
||||
label = cast?.name ?: format.label,
|
||||
language = cast?.language ?: format.language,
|
||||
selected = group.isSelected
|
||||
|
||||
+5
-1
@@ -34,6 +34,8 @@ namespace margelo::nitro::omni {
|
||||
static const auto clazz = javaClassStatic();
|
||||
static const auto fieldId = clazz->getField<jni::JString>("id");
|
||||
jni::local_ref<jni::JString> id = this->getFieldValue(fieldId);
|
||||
static const auto fieldIndex = clazz->getField<double>("index");
|
||||
double index = this->getFieldValue(fieldIndex);
|
||||
static const auto fieldLabel = clazz->getField<jni::JString>("label");
|
||||
jni::local_ref<jni::JString> label = this->getFieldValue(fieldLabel);
|
||||
static const auto fieldLanguage = clazz->getField<jni::JString>("language");
|
||||
@@ -42,6 +44,7 @@ namespace margelo::nitro::omni {
|
||||
jboolean selected = this->getFieldValue(fieldSelected);
|
||||
return Track(
|
||||
id->toStdString(),
|
||||
index,
|
||||
label != nullptr ? std::make_optional(label->toStdString()) : std::nullopt,
|
||||
language != nullptr ? std::make_optional(language->toStdString()) : std::nullopt,
|
||||
static_cast<bool>(selected)
|
||||
@@ -54,12 +57,13 @@ namespace margelo::nitro::omni {
|
||||
*/
|
||||
[[maybe_unused]]
|
||||
static jni::local_ref<JTrack::javaobject> fromCpp(const Track& value) {
|
||||
using JSignature = JTrack(jni::alias_ref<jni::JString>, jni::alias_ref<jni::JString>, jni::alias_ref<jni::JString>, jboolean);
|
||||
using JSignature = JTrack(jni::alias_ref<jni::JString>, double, jni::alias_ref<jni::JString>, jni::alias_ref<jni::JString>, jboolean);
|
||||
static const auto clazz = javaClassStatic();
|
||||
static const auto create = clazz->getStaticMethod<JSignature>("fromCpp");
|
||||
return create(
|
||||
clazz,
|
||||
jni::make_jstring(value.id),
|
||||
value.index,
|
||||
value.label.has_value() ? jni::make_jstring(value.label.value()) : nullptr,
|
||||
value.language.has_value() ? jni::make_jstring(value.language.value()) : nullptr,
|
||||
value.selected
|
||||
|
||||
@@ -23,6 +23,9 @@ data class Track(
|
||||
val id: String,
|
||||
@DoNotStrip
|
||||
@Keep
|
||||
val index: Double,
|
||||
@DoNotStrip
|
||||
@Keep
|
||||
val label: String?,
|
||||
@DoNotStrip
|
||||
@Keep
|
||||
@@ -37,6 +40,7 @@ data class Track(
|
||||
if (this === other) return true
|
||||
if (other !is Track) return false
|
||||
return Objects.deepEquals(this.id, other.id)
|
||||
&& Objects.deepEquals(this.index, other.index)
|
||||
&& Objects.deepEquals(this.label, other.label)
|
||||
&& Objects.deepEquals(this.language, other.language)
|
||||
&& Objects.deepEquals(this.selected, other.selected)
|
||||
@@ -45,6 +49,7 @@ data class Track(
|
||||
override fun hashCode(): Int {
|
||||
return arrayOf<Any?>(
|
||||
id,
|
||||
index,
|
||||
label,
|
||||
language,
|
||||
selected
|
||||
@@ -59,8 +64,8 @@ data class Track(
|
||||
@Keep
|
||||
@Suppress("unused")
|
||||
@JvmStatic
|
||||
private fun fromCpp(id: String, label: String?, language: String?, selected: Boolean): Track {
|
||||
return Track(id, label, language, selected)
|
||||
private fun fromCpp(id: String, index: Double, label: String?, language: String?, selected: Boolean): Track {
|
||||
return Track(id, index, label, language, selected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+5
-1
@@ -41,13 +41,14 @@ namespace margelo::nitro::omni {
|
||||
struct Track final {
|
||||
public:
|
||||
std::string id SWIFT_PRIVATE;
|
||||
double index SWIFT_PRIVATE;
|
||||
std::optional<std::string> label SWIFT_PRIVATE;
|
||||
std::optional<std::string> language SWIFT_PRIVATE;
|
||||
bool selected SWIFT_PRIVATE;
|
||||
|
||||
public:
|
||||
Track() = default;
|
||||
explicit Track(std::string id, std::optional<std::string> label, std::optional<std::string> language, bool selected): id(id), label(label), language(language), selected(selected) {}
|
||||
explicit Track(std::string id, double index, std::optional<std::string> label, std::optional<std::string> language, bool selected): id(id), index(index), label(label), language(language), selected(selected) {}
|
||||
|
||||
public:
|
||||
friend bool operator==(const Track& lhs, const Track& rhs) = default;
|
||||
@@ -64,6 +65,7 @@ namespace margelo::nitro {
|
||||
jsi::Object obj = arg.asObject(runtime);
|
||||
return margelo::nitro::omni::Track(
|
||||
JSIConverter<std::string>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "id"))),
|
||||
JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "index"))),
|
||||
JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "label"))),
|
||||
JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "language"))),
|
||||
JSIConverter<bool>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "selected")))
|
||||
@@ -72,6 +74,7 @@ namespace margelo::nitro {
|
||||
static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::omni::Track& arg) {
|
||||
jsi::Object obj(runtime);
|
||||
obj.setProperty(runtime, PropNameIDCache::get(runtime, "id"), JSIConverter<std::string>::toJSI(runtime, arg.id));
|
||||
obj.setProperty(runtime, PropNameIDCache::get(runtime, "index"), JSIConverter<double>::toJSI(runtime, arg.index));
|
||||
obj.setProperty(runtime, PropNameIDCache::get(runtime, "label"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.label));
|
||||
obj.setProperty(runtime, PropNameIDCache::get(runtime, "language"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.language));
|
||||
obj.setProperty(runtime, PropNameIDCache::get(runtime, "selected"), JSIConverter<bool>::toJSI(runtime, arg.selected));
|
||||
@@ -86,6 +89,7 @@ namespace margelo::nitro {
|
||||
return false;
|
||||
}
|
||||
if (!JSIConverter<std::string>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "id")))) return false;
|
||||
if (!JSIConverter<double>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "index")))) return false;
|
||||
if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "label")))) return false;
|
||||
if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "language")))) return false;
|
||||
if (!JSIConverter<bool>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "selected")))) return false;
|
||||
|
||||
+19
-20
@@ -49,32 +49,30 @@ const eventMapper: EventMapperConfig = {
|
||||
);
|
||||
}
|
||||
}),
|
||||
...createEventMapper("subtitleChange", selectTextTrack, (cb, value, prev) => {
|
||||
if (value === prev) return;
|
||||
const tracks = value?.textTrackList;
|
||||
if (tracks?.length) {
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
const track = tracks[i];
|
||||
if (track?.mode === "showing") {
|
||||
cb({
|
||||
id: `text-${i}`,
|
||||
label: track.label,
|
||||
language: track.language,
|
||||
selected: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
...createEventMapper("subtitleChange", selectTextTrack, (cb, value) => {
|
||||
if (!value) return;
|
||||
const track = value.textTrackList.find((x) => x.mode === "showing");
|
||||
if (!track) {
|
||||
cb(undefined);
|
||||
return;
|
||||
}
|
||||
cb(undefined);
|
||||
const idx = value.textTrackList.indexOf(track);
|
||||
cb({
|
||||
id: track.id ?? `text-${idx}`,
|
||||
index: idx,
|
||||
label: track.label,
|
||||
language: track.language,
|
||||
selected: true,
|
||||
});
|
||||
}),
|
||||
...createEventMapper("audioTrackChange", selectAudioTrack, (cb, value) => {
|
||||
if (!value) return;
|
||||
const track = value.audioTrackList.find((t) => t.enabled);
|
||||
if (!track) return;
|
||||
|
||||
const idx = value.audioTrackList.indexOf(track);
|
||||
cb({
|
||||
id: track.id!,
|
||||
id: track.id ?? `audio-${idx}`,
|
||||
index: idx,
|
||||
label: track.label,
|
||||
language: track.language,
|
||||
selected: true,
|
||||
@@ -206,6 +204,7 @@ export const stateMapper = {
|
||||
if (!audio) return [];
|
||||
return audio.audioTrackList.map((track, i) => ({
|
||||
id: track.id ?? i.toString(),
|
||||
index: i,
|
||||
label: track.label,
|
||||
language: track.language,
|
||||
selected: track.enabled,
|
||||
@@ -267,7 +266,7 @@ export function usePlayerState<Key extends keyof OmniPlayerState>(
|
||||
return player.subtitles as OmniPlayerState[Key];
|
||||
}
|
||||
default: {
|
||||
const config = stateMapper[key];
|
||||
const config = stateMapper[key as keyof typeof stateMapper];
|
||||
const ret = useStoreSelector(config?.selector as Selector<any, any>);
|
||||
if (!config) throw new Error(`No mapper for ${key}`);
|
||||
return config.mapper(ret) as OmniPlayerState[Key];
|
||||
|
||||
+2
-1
@@ -218,8 +218,9 @@ export class WebOmniPlayer implements OmniPlayer {
|
||||
const textTracks = selectTextTrack(this._store.state)?.textTrackList ?? [];
|
||||
return textTracks
|
||||
.filter((x) => x.kind === "subtitles" || x.kind === "captions")
|
||||
.map((track) => ({
|
||||
.map((track, i) => ({
|
||||
id: track.id!,
|
||||
index: i,
|
||||
label: track.label,
|
||||
language: track.language,
|
||||
selected:
|
||||
|
||||
@@ -52,6 +52,8 @@ export type CastStatus =
|
||||
|
||||
export interface Track {
|
||||
readonly id: string;
|
||||
// -1 for external tracks
|
||||
readonly index: number;
|
||||
readonly label?: string;
|
||||
readonly language?: string;
|
||||
readonly selected: boolean;
|
||||
|
||||
Reference in New Issue
Block a user