@takeoffmedia/react-native-penthera 0.8.8 → 0.8.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -7,6 +7,7 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
|
7
7
|
import com.facebook.react.bridge.ReactMethod
|
|
8
8
|
import com.facebook.react.bridge.Promise
|
|
9
9
|
import com.facebook.react.bridge.ReadableArray
|
|
10
|
+
import com.facebook.react.bridge.UiThreadUtil
|
|
10
11
|
import com.takeoffmediareactnativepenthera.virtuoso.OfflineVideoEngine
|
|
11
12
|
import com.facebook.react.uimanager.UIManagerModule
|
|
12
13
|
|
|
@@ -75,11 +76,34 @@ class PentheraModule(val reactContext: ReactApplicationContext) : ReactContextBa
|
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
@ReactMethod
|
|
78
|
-
fun loadBitmovinSourceManager(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
fun loadBitmovinSourceManager(
|
|
80
|
+
assetId: String,
|
|
81
|
+
nativeId: String,
|
|
82
|
+
startOffset: Double?,
|
|
83
|
+
ancillaryFiles: String?,
|
|
84
|
+
promise: Promise
|
|
85
|
+
) {
|
|
86
|
+
val manager = uiManager()
|
|
87
|
+
if (manager == null) {
|
|
88
|
+
UiThreadUtil.runOnUiThread {
|
|
89
|
+
offlineVideoEngine.loadBitmovinSourceManager(
|
|
90
|
+
assetId,
|
|
91
|
+
nativeId,
|
|
92
|
+
startOffset,
|
|
93
|
+
ancillaryFiles,
|
|
94
|
+
promise
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
manager.addUIBlock {
|
|
100
|
+
offlineVideoEngine.loadBitmovinSourceManager(
|
|
101
|
+
assetId,
|
|
102
|
+
nativeId,
|
|
103
|
+
startOffset,
|
|
104
|
+
ancillaryFiles,
|
|
105
|
+
promise
|
|
106
|
+
)
|
|
83
107
|
}
|
|
84
108
|
}
|
|
85
109
|
|
package/android/src/main/java/com/takeoffmediareactnativepenthera/virtuoso/OfflineVideoEngine.kt
CHANGED
|
@@ -2,6 +2,7 @@ package com.takeoffmediareactnativepenthera.virtuoso
|
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.Context
|
|
5
|
+
import android.os.SystemClock
|
|
5
6
|
import android.util.Log
|
|
6
7
|
import com.bitmovin.player.api.media.subtitle.SubtitleTrack
|
|
7
8
|
import com.bitmovin.player.reactnative.PlayerRegistry
|
|
@@ -423,81 +424,144 @@ class OfflineVideoEngine(private val context: ReactApplicationContext) {
|
|
|
423
424
|
)
|
|
424
425
|
}
|
|
425
426
|
|
|
427
|
+
private fun waitForPlayer(nativeId: String, timeoutMs: Long = 3000L) =
|
|
428
|
+
run {
|
|
429
|
+
val deadline = SystemClock.elapsedRealtime() + timeoutMs
|
|
430
|
+
var player = PlayerRegistry.getPlayer(nativeId)
|
|
431
|
+
while (player == null && SystemClock.elapsedRealtime() < deadline) {
|
|
432
|
+
SystemClock.sleep(50)
|
|
433
|
+
player = PlayerRegistry.getPlayer(nativeId)
|
|
434
|
+
}
|
|
435
|
+
player
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
private fun parseSubtitles(metadata: JSONObject): JSONArray {
|
|
439
|
+
if (!metadata.has("subtitles")) {
|
|
440
|
+
return JSONArray()
|
|
441
|
+
}
|
|
442
|
+
return try {
|
|
443
|
+
when (val raw = metadata.get("subtitles")) {
|
|
444
|
+
is JSONArray -> raw
|
|
445
|
+
is String -> if (raw.isNotBlank()) JSONArray(raw) else JSONArray()
|
|
446
|
+
else -> JSONArray()
|
|
447
|
+
}
|
|
448
|
+
} catch (_: Exception) {
|
|
449
|
+
JSONArray()
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
private fun parseAncillaryFiles(ancillaries: String?): JSONArray {
|
|
454
|
+
if (ancillaries.isNullOrBlank()) {
|
|
455
|
+
return JSONArray()
|
|
456
|
+
}
|
|
457
|
+
return try {
|
|
458
|
+
JSONArray(ancillaries)
|
|
459
|
+
} catch (_: Exception) {
|
|
460
|
+
JSONArray()
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
private fun findAncillaryPlaybackUrl(ancillaryFiles: JSONArray, language: String): String? {
|
|
465
|
+
for (j in 0 until ancillaryFiles.length()) {
|
|
466
|
+
val ancillary = ancillaryFiles.optJSONObject(j) ?: continue
|
|
467
|
+
if (ancillary.optString("description") == language) {
|
|
468
|
+
val playbackUrl = ancillary.optString("playbackUrl")
|
|
469
|
+
if (playbackUrl.isNotBlank()) {
|
|
470
|
+
return playbackUrl
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return null
|
|
475
|
+
}
|
|
476
|
+
|
|
426
477
|
fun loadBitmovinSourceManager(
|
|
427
478
|
assetId: String,
|
|
428
479
|
nativeId: String,
|
|
429
480
|
startOffset: Double?,
|
|
430
|
-
ancillaries: String
|
|
481
|
+
ancillaries: String?,
|
|
431
482
|
promise: Promise
|
|
432
483
|
) {
|
|
433
|
-
|
|
484
|
+
try {
|
|
485
|
+
val asset = virtuoso.assetManager.getByAssetId(assetId)?.firstOrNull()
|
|
486
|
+
if (asset == null) {
|
|
487
|
+
promise.reject("Error", "Asset not found")
|
|
488
|
+
return
|
|
489
|
+
}
|
|
490
|
+
|
|
434
491
|
val sourceManager = BitmovinSourceManager(context, asset as ISegmentedAsset)
|
|
435
492
|
val sourceItem = sourceManager.bitmovinSourceItem
|
|
436
|
-
if (sourceItem
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
player = PlayerRegistry.getPlayer(nativeId)
|
|
441
|
-
}
|
|
442
|
-
val metadata = JSONObject(asset.metadata)
|
|
443
|
-
val subtitles = JSONArray(metadata["subtitles"] as String)
|
|
444
|
-
val ancillaryFiles = JSONArray(ancillaries)
|
|
445
|
-
for (i in 0 until subtitles.length()) {
|
|
446
|
-
val subtitleMap = Arguments.createMap()
|
|
447
|
-
val subtitle = subtitles.getJSONObject(i)
|
|
448
|
-
|
|
449
|
-
for (j in 0 until ancillaryFiles.length()) {
|
|
450
|
-
val ancillary = ancillaryFiles.getJSONObject(j)
|
|
451
|
-
if (ancillary.getString("description") == subtitle.getString("language")) {
|
|
452
|
-
subtitleMap.putString("url", ancillary.getString("playbackUrl").toString())
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
subtitleMap.putString("language", subtitle.getString("language"))
|
|
456
|
-
subtitleMap.putString("label", subtitle.getString("label"))
|
|
457
|
-
subtitleMap.putString("format", "vtt")
|
|
493
|
+
if (sourceItem == null) {
|
|
494
|
+
promise.reject("Error", "Source item is null")
|
|
495
|
+
return
|
|
496
|
+
}
|
|
458
497
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
498
|
+
val player = waitForPlayer(nativeId)
|
|
499
|
+
if (player == null) {
|
|
500
|
+
promise.reject("Error", "Player not ready")
|
|
501
|
+
return
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
val metadata = JSONObject(asset.metadata)
|
|
505
|
+
val subtitles = parseSubtitles(metadata)
|
|
506
|
+
val ancillaryFiles = parseAncillaryFiles(ancillaries)
|
|
507
|
+
for (i in 0 until subtitles.length()) {
|
|
508
|
+
val subtitle = subtitles.optJSONObject(i) ?: continue
|
|
509
|
+
val language = subtitle.optString("language")
|
|
510
|
+
if (language.isBlank()) {
|
|
511
|
+
continue
|
|
462
512
|
}
|
|
463
|
-
|
|
464
|
-
|
|
513
|
+
val label = subtitle.optString("label", language)
|
|
514
|
+
val subtitleMap = Arguments.createMap()
|
|
515
|
+
findAncillaryPlaybackUrl(ancillaryFiles, language)?.let { url ->
|
|
516
|
+
subtitleMap.putString("url", url)
|
|
465
517
|
}
|
|
466
|
-
|
|
518
|
+
subtitleMap.putString("language", language)
|
|
519
|
+
subtitleMap.putString("label", label)
|
|
520
|
+
subtitleMap.putString("format", "vtt")
|
|
467
521
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
infoVideoPlayer.optString("title").let { title ->
|
|
472
|
-
if (title.isNotBlank()) {
|
|
473
|
-
sourceItem.title = title
|
|
474
|
-
}
|
|
522
|
+
toSubtitleTrack(subtitleMap)?.let {
|
|
523
|
+
sourceItem.addSubtitleTrack(it)
|
|
475
524
|
}
|
|
525
|
+
}
|
|
476
526
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
527
|
+
if (startOffset != null) {
|
|
528
|
+
sourceItem.options.startOffset = startOffset
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
val metaDataMap = mutableMapOf<String, String>()
|
|
532
|
+
val data = metadata.optJSONObject("data")
|
|
533
|
+
val infoVideoPlayer = data?.optJSONObject("infoVideoPlayer")
|
|
534
|
+
|
|
535
|
+
infoVideoPlayer?.optString("title")?.let { title ->
|
|
536
|
+
if (title.isNotBlank()) {
|
|
537
|
+
sourceItem.title = title
|
|
481
538
|
}
|
|
539
|
+
}
|
|
482
540
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
}
|
|
541
|
+
infoVideoPlayer?.optString("description")?.let { description ->
|
|
542
|
+
if (description.isNotBlank()) {
|
|
543
|
+
metaDataMap["description"] = description
|
|
487
544
|
}
|
|
545
|
+
}
|
|
488
546
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
}
|
|
547
|
+
infoVideoPlayer?.optString("advisoryClassification")?.let { advisoryClassification ->
|
|
548
|
+
if (advisoryClassification.isNotBlank()) {
|
|
549
|
+
metaDataMap["advisoryClassification"] = advisoryClassification
|
|
493
550
|
}
|
|
551
|
+
}
|
|
494
552
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
553
|
+
infoVideoPlayer?.optString("advisoryDescription")?.let { advisoryDescription ->
|
|
554
|
+
if (advisoryDescription.isNotBlank()) {
|
|
555
|
+
metaDataMap["advisoryDescription"] = advisoryDescription
|
|
556
|
+
}
|
|
498
557
|
}
|
|
558
|
+
|
|
559
|
+
sourceItem.metadata = metaDataMap
|
|
560
|
+
player.load(sourceItem)
|
|
561
|
+
promise.resolve(true)
|
|
562
|
+
} catch (e: Exception) {
|
|
563
|
+
promise.reject("Error", e.message ?: "Error loading asset")
|
|
499
564
|
}
|
|
500
|
-
promise.reject("Error", "Error loading asset")
|
|
501
565
|
}
|
|
502
566
|
|
|
503
567
|
fun getAuthenticationStatus(promise: Promise) {
|