@24i/smartapps-bigscreen-rnv-build-engine 0.17.2-alpha.2162 → 0.17.2-alpha.2164

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.
Files changed (42) hide show
  1. package/dist/src/resolvers/resolveBuildFileExtension.js +1 -0
  2. package/dist/src/sdks/sdk-androidtv/sdkAndroidtv.js +46 -7
  3. package/dist/src/tasks/task.rnv.build.js +1 -0
  4. package/dist/templates/platforms/androidtv/project/amazoniap/build.gradle +1 -3
  5. package/dist/templates/platforms/androidtv/project/app/build.gradle +270 -37
  6. package/dist/templates/platforms/androidtv/project/app/src/bigscreenAppstage/res/drawable/banner.png +0 -0
  7. package/dist/templates/platforms/androidtv/project/app/src/bigscreenAppstage/res/raw/configuration.json +4 -1
  8. package/dist/templates/platforms/androidtv/project/app/src/kan/java/com/twentyfouri/tvbridge/Flavor.kt +5 -0
  9. package/dist/templates/platforms/androidtv/project/app/src/kan/res/drawable/banner.png +0 -0
  10. package/dist/templates/platforms/androidtv/project/app/src/kan/res/mipmap-hdpi/ic_launcher.png +0 -0
  11. package/dist/templates/platforms/androidtv/project/app/src/kan/res/mipmap-mdpi/ic_launcher.png +0 -0
  12. package/dist/templates/platforms/androidtv/project/app/src/kan/res/mipmap-xhdpi/ic_launcher.png +0 -0
  13. package/dist/templates/platforms/androidtv/project/app/src/kan/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  14. package/dist/templates/platforms/androidtv/project/app/src/kan/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  15. package/dist/templates/platforms/androidtv/project/app/src/kan/res/raw/configuration.json +9 -0
  16. package/dist/templates/platforms/androidtv/project/app/src/main/AndroidManifest.xml +8 -2
  17. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/bridge/BridgeFromJavaScript.kt +19 -13
  18. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/bridge/BridgeToJavaScript.kt +32 -14
  19. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/configuration/BridgeConfig.kt +7 -0
  20. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/di/ActivityModule.kt +5 -3
  21. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/LoggingDrmCallback.kt +15 -23
  22. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/OverriddenHttpMediaDrmCallback.kt +134 -0
  23. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/PlayerFactoryExoPlayerCustomized.kt +84 -0
  24. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/SourceDrmOverridden.kt +49 -0
  25. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/jump/JumpAnalytics.kt +4 -2
  26. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/client/BridgeWebView.kt +2 -0
  27. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/model/MediaEventType.kt +3 -0
  28. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/parser/TrackJsonModelParser.kt +37 -55
  29. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/view/Mainview.kt +1 -3
  30. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/view/PlayerCache.kt +2 -2
  31. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/view/WebViewActivity.kt +40 -18
  32. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/viewmodel/WebViewModel.kt +173 -171
  33. package/dist/templates/platforms/androidtv/project/app/src/main/res/layout/activity_web_view.xml +2 -4
  34. package/dist/templates/platforms/androidtv/project/app/src/main/res/values/styles.xml +1 -0
  35. package/dist/templates/platforms/androidtv/project/build.gradle +6 -10
  36. package/dist/templates/platforms/androidtv/project/common/build.gradle +1 -3
  37. package/dist/templates/platforms/androidtv/project/googleiap/build.gradle +1 -3
  38. package/dist/templates/platforms/androidtv/project/gradle/wrapper/gradle-wrapper.properties +3 -3
  39. package/dist/templates/platforms/androidtv/project/gradle.properties +1 -2
  40. package/dist/templates/platforms/androidtv/project/keystore/kan11_applicaster.keystore +0 -0
  41. package/dist/templates/platforms/androidtv/project/local.properties +8 -0
  42. package/package.json +1 -1
@@ -6,18 +6,20 @@ import android.os.Looper
6
6
  import android.util.Log
7
7
  import android.webkit.JavascriptInterface
8
8
  import com.google.gson.JsonObject
9
- import com.twentyfouri.smartexoplayer.tracks.TrackInfo
10
- import com.twentyfouri.smartexoplayer.tracks.TrackPreference
9
+ import com.twentyfouri.player.base.TextTrack
10
+ import com.twentyfouri.player.base.TrackKind
11
11
  import com.twentyfouri.tvbridge.webview.parser.ContentModelParser.stringToJson
12
12
  import com.twentyfouri.tvbridge.webview.parser.DeviceInfoParser.deviceInfoParsed
13
13
  import com.twentyfouri.tvbridge.webview.parser.PlaybackInfoParser.parse
14
- import com.twentyfouri.tvbridge.webview.parser.TrackJsonModelParser.fromJson
14
+ import com.twentyfouri.tvbridge.webview.parser.TrackJsonModelParser.audioFromJson
15
+ import com.twentyfouri.tvbridge.webview.parser.TrackJsonModelParser.textFromJson
15
16
  import com.twentyfouri.tvbridge.webview.parser.TrackJsonModelParser.toJson
16
17
  import com.twentyfouri.tvbridge.webview.viewmodel.WebViewModel
17
18
  import java.text.SimpleDateFormat
18
19
  import java.util.*
19
20
  import java.util.concurrent.CountDownLatch
20
21
  import java.util.concurrent.TimeUnit
22
+ import kotlin.math.roundToInt
21
23
 
22
24
  @SuppressWarnings("unused")
23
25
  class BridgeFromJavaScript(
@@ -67,6 +69,7 @@ class BridgeFromJavaScript(
67
69
  fun bridgeJavaScriptToAndroid_SetVideoPath(jsonString: String, mimeType: String?) = launch {
68
70
  Log.d(LOG_TAG, "bridgeJavaScriptToAndroid_SetVideoPath = $jsonString")
69
71
  try {
72
+ viewModel.stopPlayer()
70
73
  val contentModel = stringToJson(jsonString)
71
74
  contentModel?.streamType = mimeType?.let {
72
75
  when {
@@ -136,7 +139,7 @@ class BridgeFromJavaScript(
136
139
  */
137
140
  @JavascriptInterface
138
141
  fun bridgeJavaScriptToAndroid_GetCurrentTime(): Int = runBlocking("bridgeJavaScriptToAndroid_GetCurrentTime") {
139
- viewModel.currentPosition / 1000
142
+ viewModel.currentPosition.roundToInt() / 1000
140
143
  } ?: 0
141
144
 
142
145
  /*
@@ -144,7 +147,7 @@ class BridgeFromJavaScript(
144
147
  */
145
148
  @JavascriptInterface
146
149
  fun bridgeJavaScriptToAndroid_GetCurrentTimeFloat(): Float = runBlocking("bridgeJavaScriptToAndroid_GetCurrentTimeFloat") {
147
- viewModel.currentPosition.toFloat() / 1000f
150
+ viewModel.currentPosition.toFloat() / 1000
148
151
  } ?: 0f
149
152
 
150
153
  /*
@@ -163,7 +166,7 @@ class BridgeFromJavaScript(
163
166
  */
164
167
  @JavascriptInterface
165
168
  fun bridgeJavaScriptToAndroid_GetTotalTime(): Int = runBlocking("bridgeJavaScriptToAndroid_GetTotalTime") {
166
- viewModel.duration / 1000
169
+ viewModel.duration.roundToInt() / 1000
167
170
  } ?: 0
168
171
 
169
172
  /*
@@ -173,7 +176,7 @@ class BridgeFromJavaScript(
173
176
  @JavascriptInterface
174
177
  fun bridgeJavaScriptToAndroid_SeekTo(seconds: Int) = launch {
175
178
  Log.d(LOG_TAG, "bridgeJavaScriptToAndroid_SeekTo($seconds)")
176
- viewModel.seekToPosition(seconds * 1000)
179
+ viewModel.seekToPosition(1000 * seconds.toDouble())
177
180
  }
178
181
 
179
182
  /*
@@ -234,13 +237,16 @@ class BridgeFromJavaScript(
234
237
  fun bridgeJavaScriptToAndroid_SetTrack(trackJson: String) = launch {
235
238
  if (trackJson == "") {
236
239
  Log.d(LOG_TAG, "bridgeJavaScriptToAndroid_SetTrack empty => DEFAULT")
237
- viewModel.setSubtitleTrack(TrackPreference.DEFAULT)
240
+ viewModel.setSubtitleTrack(null)
238
241
  } else if (trackJson == "false") {
239
242
  Log.d(LOG_TAG, "bridgeJavaScriptToAndroid_SetTrack false => DISABLED")
240
- viewModel.setSubtitleTrack(TrackPreference.DISABLED)
243
+ viewModel.setSubtitleTrack(TextTrack {
244
+ id = "text_off"
245
+ kind = TrackKind.EMPTY
246
+ })
241
247
  } else {
242
248
  Log.d(LOG_TAG, "bridgeJavaScriptToAndroid_SetTrack $trackJson")
243
- viewModel.setSubtitleTrack(fromJson(trackJson))
249
+ viewModel.setSubtitleTrack(textFromJson(trackJson))
244
250
  }
245
251
  }
246
252
 
@@ -250,7 +256,7 @@ class BridgeFromJavaScript(
250
256
  @JavascriptInterface
251
257
  fun bridgeJavaScriptToAndroid_SetAudioTrack(trackJson: String?) = launch {
252
258
  Log.d(LOG_TAG, "bridgeJavaScriptToAndroid_SetAudioTrack ${trackJson ?: "(none)"}")
253
- viewModel.setAudioTrack(fromJson(trackJson))
259
+ viewModel.setAudioTrack(audioFromJson(trackJson))
254
260
  }
255
261
 
256
262
  /*
@@ -370,10 +376,10 @@ class BridgeFromJavaScript(
370
376
  companion object {
371
377
  const val LOG_TAG = "JavaScriptBridge"
372
378
  val DISABLED_AUDIO_TRACK = JsonObject().apply {
373
- addProperty("type", TrackInfo.TYPE_AUDIO)
379
+ addProperty("type", "audio")
374
380
  }.toString()
375
381
  val DISABLED_SUBTITLE_TRACK = JsonObject().apply {
376
- addProperty("type", TrackInfo.TYPE_SUBTITLES)
382
+ addProperty("type", "subtitles")
377
383
  }.toString()
378
384
  }
379
385
  }
@@ -1,13 +1,20 @@
1
1
  package com.twentyfouri.tvbridge.bridge
2
2
 
3
3
  import android.util.Log
4
- import com.twentyfouri.smartexoplayer.tracks.TrackInfo
4
+ import com.twentyfouri.player.base.AudioTrack
5
+ import com.twentyfouri.player.base.TextTrack
5
6
  import com.twentyfouri.tvbridge.bridge.BridgeFromJavaScript.Companion.LOG_TAG
7
+ import com.twentyfouri.tvbridge.configuration.BridgeConfig
6
8
  import com.twentyfouri.tvbridge.webview.parser.TrackJsonModelParser.toJson
7
9
 
8
10
  class BridgeToJavaScript(
9
- private val executeJavaScript: (String) -> Unit
11
+ config: BridgeConfig,
12
+ private val executeJavaScript: (String) -> Unit
10
13
  ) {
14
+ private val keyEventPrefix: String = config.keyEventPrefix
15
+ private val mediaEventPrefix: String = config.mediaEventPrefix
16
+ private val iapEventPrefix: String = config.iapEventPrefix
17
+
11
18
  /**
12
19
  * Android 2 JavaScript keyevent Event. Dispatch key custom event to the page document element
13
20
  *
@@ -16,7 +23,7 @@ class BridgeToJavaScript(
16
23
  */
17
24
  fun bridgeAndroidToJavaScript_KeyEvent(keyCode: Int, keyEvent: String) {
18
25
  Log.d(LOG_TAG, "bridgeAndroidToJavaScript_KeyEvent keyCode = $keyCode, keyEvent = $keyEvent")
19
- executeJavaScript("javascript:((function dispatchEvent(){var evt = document.createEvent('Events');evt.initEvent('$keyEvent',true,true);evt.keyCode=$keyCode;evt.which=$keyCode;document.dispatchEvent(evt);})())")
26
+ executeJavaScript("javascript:((function dispatchEvent(){var evt = document.createEvent('Events');evt.initEvent('$keyEventPrefix$keyEvent',true,true);evt.keyCode=$keyCode;evt.which=$keyCode;(document.activeElement?document.activeElement:document).dispatchEvent(evt);})())")
20
27
  }
21
28
 
22
29
  /**
@@ -25,8 +32,8 @@ class BridgeToJavaScript(
25
32
  * @param mediaEvent the mediaEvent which is being sent from the Android code.
26
33
  */
27
34
  fun bridgeAndroidToJavaScript_MediaEvent(mediaEvent: String) {
28
- Log.d(LOG_TAG, "bridgeAndroidToJavaScript_MediaEvent $mediaEvent")
29
- executeJavaScript("javascript:((function dispatchEvent(){var evt = document.createEvent('Events');evt.initEvent('$mediaEvent',true,true);document.dispatchEvent(evt);})())")
35
+ Log.d(LOG_TAG, "bridgeAndroidToJavaScript_MediaEvent $mediaEventPrefix$mediaEvent")
36
+ executeJavaScript("javascript:((function dispatchEvent(){var evt = document.createEvent('Events');evt.initEvent('$mediaEventPrefix$mediaEvent',true,true);document.dispatchEvent(evt);})())")
30
37
  }
31
38
 
32
39
  /**
@@ -35,31 +42,42 @@ class BridgeToJavaScript(
35
42
  * @param mediaEvent the mediaEvent
36
43
  * @param tracks tracks from the players stream.
37
44
  */
38
- fun bridgeAndroidToJavaScript_MediaTrackEvent(mediaEvent: String, tracks: List<TrackInfo>) {
39
- val tracksJson = toJson(tracks)
45
+ fun bridgeAndroidToJavaScript_MediaTrackEvent(
46
+ mediaEvent: String,
47
+ audioTracks: List<AudioTrack>,
48
+ textTracks: List<TextTrack>
49
+ ) {
50
+ val tracksJson = toJson(audioTracks, textTracks)
40
51
  Log.d(LOG_TAG, tracksJson)
41
- val mediaEventString = "javascript:((function dispatchEvent(){var evt = document.createEvent('Events');evt.initEvent('$mediaEvent',true,true);evt.tracks='$tracksJson';document.dispatchEvent(evt);})())"
52
+ val mediaEventString = "javascript:((function dispatchEvent(){var evt = document.createEvent('Events');evt.initEvent('$mediaEventPrefix$mediaEvent',true,true);evt.tracks='$tracksJson';document.dispatchEvent(evt);})())"
42
53
  Log.v(LOG_TAG, mediaEventString)
43
54
  executeJavaScript(mediaEventString)
44
55
  }
45
56
 
46
57
  fun bridgeAndroidToJavaScript_IAPValidationError(message: String) {
47
58
  Log.d(LOG_TAG, "bridgeAndroidToJavaScript_IAPValidationError")
48
- executeJavaScript("javascript:((function dispatchEvent(){var evt = document.createEvent('Events');evt.initEvent('iapError',true,true);evt.message='$message';document.dispatchEvent(evt);})())")
59
+ executeJavaScript("javascript:((function dispatchEvent(){var evt = document.createEvent('Events');evt.initEvent('${iapEventPrefix}iapError',true,true);evt.message='$message';document.dispatchEvent(evt);})())")
49
60
  }
50
61
 
51
- fun bridgeAndroidToJavaScript_IAPValidationSuccess(userId: String, receiptId: String, sku: String) {
52
- Log.d(LOG_TAG, "bridgeAndroidToJavaScript_IAPValidationSuccess sku=$sku receiptId=$receiptId")
53
- executeJavaScript("javascript:((function dispatchEvent(){var evt = document.createEvent('Events');evt.initEvent('iapSuccess',true,true);evt.userId='$userId';evt.receiptId='$receiptId';evt.sku='$sku';document.dispatchEvent(evt);})())")
62
+ fun bridgeAndroidToJavaScript_IAPValidationSuccess(
63
+ userId: String,
64
+ receiptId: String,
65
+ sku: String
66
+ ) {
67
+ Log.d(
68
+ LOG_TAG,
69
+ "bridgeAndroidToJavaScript_IAPValidationSuccess sku=$sku receiptId=$receiptId"
70
+ )
71
+ executeJavaScript("javascript:((function dispatchEvent(){var evt = document.createEvent('Events');evt.initEvent('${iapEventPrefix}iapSuccess',true,true);evt.userId='$userId';evt.receiptId='$receiptId';evt.sku='$sku';document.dispatchEvent(evt);})())")
54
72
  }
55
73
 
56
74
  fun bridgeIAPGetProductDataSuccess(okSkus: String, badSkus: String) {
57
75
  Log.d(LOG_TAG, "bridgeIAPGetProductDataSuccess ok=$okSkus bad=$badSkus")
58
- executeJavaScript("javascript:((function dispatchEvent(){var evt = document.createEvent('Events');evt.initEvent('onInAppProductDetailSuccess',true,true);evt.okSkus=$okSkus;evt.badSkus=$badSkus;document.dispatchEvent(evt);})())")
76
+ executeJavaScript("javascript:((function dispatchEvent(){var evt = document.createEvent('Events');evt.initEvent('${iapEventPrefix}onInAppProductDetailSuccess',true,true);evt.okSkus=$okSkus;evt.badSkus=$badSkus;document.dispatchEvent(evt);})())")
59
77
  }
60
78
 
61
79
  fun bridgeIAPGetProductDataFail(reason: String) {
62
80
  Log.d(LOG_TAG, "bridgeIAPGetProductDataFail")
63
- executeJavaScript("javascript:((function dispatchEvent(){var evt = document.createEvent('Events');evt.initEvent('onInAppProductDetailError',true,true);evt.message='$reason';document.dispatchEvent(evt);})())")
81
+ executeJavaScript("javascript:((function dispatchEvent(){var evt = document.createEvent('Events');evt.initEvent('${iapEventPrefix}onInAppProductDetailError',true,true);evt.message='$reason';document.dispatchEvent(evt);})())")
64
82
  }
65
83
  }
@@ -1,9 +1,16 @@
1
1
  package com.twentyfouri.tvbridge.configuration
2
2
 
3
+ import com.twentyfouri.tvbridge.BuildConfig
4
+
3
5
  class BridgeConfig {
4
6
  var baseUrl: String = "http://portal.24imedia.tv/"
5
7
  var userAgent: UserAgent = UserAgent()
6
8
  var drm: Drm = Drm()
7
9
  var isEnabledTouchSound: Boolean = false
8
10
  var playerAppName: String? = null
11
+ var keyEventPrefix: String = ""
12
+ var mediaEventPrefix: String = ""
13
+ var iapEventPrefix: String = ""
14
+ var customizableUrl: Boolean = BuildConfig.DEBUG
15
+ var startWithHighVideoQuality: Boolean = false
9
16
  }
@@ -1,7 +1,9 @@
1
1
  package com.twentyfouri.tvbridge.global.di
2
2
 
3
3
  import android.app.Activity
4
- import com.twentyfouri.smartexoplayer.SmartPlayerFactory
4
+ import com.twentyfouri.player.base.PlayerFactoryBase
5
+ import com.twentyfouri.tvbridge.configuration.BridgeConfig
6
+ import com.twentyfouri.tvbridge.global.utils.PlayerFactoryExoPlayerCustomized
5
7
  import dagger.Module
6
8
  import dagger.Provides
7
9
 
@@ -14,6 +16,6 @@ class ActivityModule(
14
16
  activity
15
17
 
16
18
  @Provides
17
- fun providePlayerFactory() =
18
- SmartPlayerFactory(activity)
19
+ fun providePlayerFactory(config: BridgeConfig): PlayerFactoryBase =
20
+ PlayerFactoryExoPlayerCustomized(config)
19
21
  }
@@ -3,28 +3,28 @@ package com.twentyfouri.tvbridge.global.utils
3
3
  import android.util.Base64
4
4
  import android.util.Log
5
5
  import com.google.android.exoplayer2.drm.ExoMediaDrm
6
- import com.google.android.exoplayer2.upstream.HttpDataSource
7
- import com.twentyfouri.smartexoplayer.drm.SmartHttpMediaDrmCallback
8
- import com.twentyfouri.smartexoplayer.drm.SmartMediaDrmCallback
9
- import com.twentyfouri.smartexoplayer.model.DrmConfiguration
10
- import com.twentyfouri.tvbridge.BuildConfig
6
+ import com.google.android.exoplayer2.drm.MediaDrmCallback
11
7
  import java.util.*
12
8
 
13
9
  class LoggingDrmCallback(
14
- private val licenseUrl: String?,
15
- forceDefaultUrl: Boolean,
16
- dataSourceFactory: HttpDataSource.Factory
17
- ) : SmartHttpMediaDrmCallback(licenseUrl, forceDefaultUrl, dataSourceFactory) {
10
+ private val base: MediaDrmCallback,
11
+ private val licenseUrl: String? = null
12
+ ) : MediaDrmCallback {
13
+ override fun executeProvisionRequest(
14
+ uuid: UUID,
15
+ request: ExoMediaDrm.ProvisionRequest
16
+ ): ByteArray {
17
+ return base.executeProvisionRequest(uuid, request)
18
+ }
18
19
 
19
- @Throws(Exception::class)
20
20
  override fun executeKeyRequest(uuid: UUID, request: ExoMediaDrm.KeyRequest): ByteArray {
21
- if (BuildConfig.DEBUG) {
21
+ if (com.twentyfouri.player.exoplayer.BuildConfig.DEBUG) {
22
22
  Log.v(TAG, "DRM Embedded URL: ${request.licenseServerUrl}")
23
- Log.v(TAG, "DRM Default URL: ${licenseUrl}")
23
+ Log.v(TAG, "DRM Default URL: $licenseUrl")
24
24
  Log.v(TAG, "DRM request:\n${request.data.toBase64()}")
25
25
  }
26
- val response = super.executeKeyRequest(uuid, request)
27
- if (BuildConfig.DEBUG) {
26
+ val response = base.executeKeyRequest(uuid, request)
27
+ if (com.twentyfouri.player.exoplayer.BuildConfig.DEBUG) {
28
28
  Log.v(TAG, "DRM response:\n" + response.toBase64())
29
29
  }
30
30
  return response
@@ -32,15 +32,7 @@ class LoggingDrmCallback(
32
32
 
33
33
  private fun ByteArray.toBase64(): String = Base64.encodeToString(this, Base64.DEFAULT)
34
34
 
35
- class Factory(
36
- private val licenseUrl: String?,
37
- private val forceDefaultUrl: Boolean
38
- ) : SmartMediaDrmCallback.Factory {
39
- override fun build(drm: DrmConfiguration, dataSourceFactory: HttpDataSource.Factory): SmartMediaDrmCallback =
40
- LoggingDrmCallback(licenseUrl, forceDefaultUrl, dataSourceFactory)
41
- }
42
-
43
35
  companion object {
44
- private const val TAG = "DrmCallback"
36
+ const val TAG = "DrmCallback"
45
37
  }
46
38
  }
@@ -0,0 +1,134 @@
1
+ package com.twentyfouri.tvbridge.global.utils
2
+
3
+ import android.net.Uri
4
+ import android.util.Base64
5
+ import android.util.Log
6
+ import com.google.android.exoplayer2.C
7
+ import com.google.android.exoplayer2.drm.ExoMediaDrm
8
+ import com.google.android.exoplayer2.drm.MediaDrmCallback
9
+ import com.google.android.exoplayer2.upstream.DataSourceInputStream
10
+ import com.google.android.exoplayer2.upstream.DataSpec
11
+ import com.google.android.exoplayer2.upstream.HttpDataSource
12
+ import com.google.android.exoplayer2.util.Util
13
+ import com.twentyfouri.tvbridge.BuildConfig
14
+ import org.json.JSONException
15
+ import org.json.JSONObject
16
+ import java.io.BufferedReader
17
+ import java.io.IOException
18
+ import java.io.InputStreamReader
19
+ import java.net.MalformedURLException
20
+ import java.net.URL
21
+ import java.net.URLEncoder
22
+ import java.util.HashMap
23
+ import java.util.UUID
24
+
25
+ class OverriddenHttpMediaDrmCallback(
26
+ private val defaultLicenseUrl: String?,
27
+ private val forceDefaultLicenseUrl: Boolean,
28
+ private val dataSourceFactory: HttpDataSource.Factory
29
+ ) : MediaDrmCallback {
30
+
31
+ @Throws(IOException::class)
32
+ override fun executeProvisionRequest(uuid: UUID, request: ExoMediaDrm.ProvisionRequest): ByteArray {
33
+ Log.d(TAG, "executeProvisionRequest")
34
+ val url = request.defaultUrl + "&signedRequest=" + String(request.data)
35
+ return executePost(dataSourceFactory, url, ByteArray(0), null)
36
+ }
37
+
38
+ @Throws(Exception::class)
39
+ override fun executeKeyRequest(uuid: UUID, request: ExoMediaDrm.KeyRequest): ByteArray {
40
+ Log.d(TAG, "executeKeyRequest")
41
+ var url: String? = request.licenseServerUrl
42
+ if (forceDefaultLicenseUrl || url.isNullOrEmpty()) {
43
+ url = defaultLicenseUrl
44
+ }
45
+ val requestProperties: MutableMap<String, String> = HashMap()
46
+ // Add standard request properties for supported schemes.
47
+ val contentType = "text/plain"
48
+ requestProperties["Content-Type"] = contentType
49
+ if (C.PLAYREADY_UUID == uuid) {
50
+ requestProperties["SOAPAction"] = "http://schemas.microsoft.com/DRM/2007/03/protocols/AcquireLicense"
51
+ }
52
+
53
+ //adding data to url
54
+ val challenge = request.data
55
+ val encoded = URLEncoder.encode(Base64.encodeToString(challenge, Base64.NO_WRAP), "UTF-8")
56
+ url = appendQuery(url, "_widevineChallenge=$encoded")
57
+ if (BuildConfig.DEBUG) {
58
+ Log.d(TAG, "Executing post on url $url with content type $contentType")
59
+ }
60
+ return executeGet(url)
61
+ }
62
+
63
+ companion object {
64
+ private const val TAG = "DrmCallback"
65
+
66
+ private fun executeGet(urlString: String): ByteArray {
67
+ try {
68
+ val licenserRequest = URL(urlString)
69
+ val yc = licenserRequest.openConnection()
70
+ val input = BufferedReader(InputStreamReader(yc.getInputStream()))
71
+ val builder = StringBuilder()
72
+ var inputLine: String?
73
+ while (input.readLine().also { inputLine = it } != null) {
74
+ builder.append(inputLine)
75
+ }
76
+ input.close()
77
+ val jsonObject = JSONObject(builder.toString())
78
+ val container = jsonObject.getJSONObject("getWidevineLicenseResponse")
79
+ val license = container.getString("license")
80
+ if (BuildConfig.DEBUG) {
81
+ Log.d(TAG, "Finished post: $license")
82
+ }
83
+ return Base64.decode(license, Base64.NO_WRAP)
84
+ } catch (e: MalformedURLException) {
85
+ e.printStackTrace()
86
+ } catch (e: IOException) {
87
+ e.printStackTrace()
88
+ } catch (e: JSONException) {
89
+ e.printStackTrace()
90
+ }
91
+ return byteArrayOf()
92
+ }
93
+
94
+ @Throws(IOException::class)
95
+ private fun executePost(
96
+ dataSourceFactory: HttpDataSource.Factory,
97
+ url: String,
98
+ data: ByteArray,
99
+ requestProperties: Map<String, String>?
100
+ ): ByteArray {
101
+ Log.d(TAG, "executePost")
102
+ val dataSource = dataSourceFactory.createDataSource()
103
+ if (requestProperties != null) {
104
+ for ((key, value) in requestProperties) {
105
+ dataSource.setRequestProperty(key, value)
106
+ }
107
+ }
108
+ val dataSpec = DataSpec.Builder()
109
+ .setUri(Uri.parse(url))
110
+ .setFlags(DataSpec.FLAG_ALLOW_GZIP)
111
+ .setHttpMethod(DataSpec.HTTP_METHOD_POST)
112
+ .setHttpBody(data)
113
+ .setHttpRequestHeaders(requestProperties ?: emptyMap())
114
+ .build()
115
+ val inputStream = DataSourceInputStream(dataSource, dataSpec)
116
+ return try {
117
+ Util.toByteArray(inputStream)
118
+ } finally {
119
+ Util.closeQuietly(inputStream)
120
+ }
121
+ }
122
+
123
+ fun appendQuery(url: String?, appendQuery: String): String {
124
+ val oldUri = Uri.parse(url)
125
+ var newQuery = oldUri.query
126
+ if (newQuery == null) {
127
+ newQuery = appendQuery
128
+ } else {
129
+ newQuery += "&$appendQuery"
130
+ }
131
+ return oldUri.scheme + "://" + oldUri.authority + oldUri.path + "?" + newQuery
132
+ }
133
+ }
134
+ }
@@ -0,0 +1,84 @@
1
+ package com.twentyfouri.tvbridge.global.utils
2
+
3
+ import android.content.Context
4
+ import com.google.android.exoplayer2.C
5
+ import com.google.android.exoplayer2.drm.DefaultDrmSessionManager
6
+ import com.google.android.exoplayer2.drm.DrmSessionManager
7
+ import com.google.android.exoplayer2.drm.FrameworkMediaDrm
8
+ import com.google.android.exoplayer2.drm.MediaDrmCallback
9
+ import com.google.android.exoplayer2.upstream.HttpDataSource
10
+ import com.google.android.exoplayer2.util.Util
11
+ import com.twentyfouri.player.base.DrmPlayready
12
+ import com.twentyfouri.player.base.DrmWidevine
13
+ import com.twentyfouri.player.base.Source
14
+ import com.twentyfouri.player.base.SourceDrm
15
+ import com.twentyfouri.player.exoplayer.*
16
+ import com.twentyfouri.tvbridge.configuration.BridgeConfig
17
+
18
+ class PlayerFactoryExoPlayerCustomized(
19
+ private val config: BridgeConfig
20
+ ) : PlayerFactoryExoPlayer() {
21
+ override fun createSharedComponents(context: Context): SharedComponents =
22
+ CustomizedSharedComponents(context)
23
+
24
+ private inner class CustomizedSharedComponents(context: Context) : DefaultSharedComponents(context) {
25
+
26
+ override val drmProvider: DrmSessionManagerProvider by lazy { CustomizedDrmSessionManagerProvider() }
27
+
28
+ override val applicationName: String
29
+ get() = config.playerAppName ?: super.applicationName
30
+
31
+ override fun buildIsolatedComponents(): PlayerComponents {
32
+ return CustomizedPlayerComponents(this)
33
+ }
34
+ }
35
+
36
+ private inner class CustomizedPlayerComponents(shared: SharedComponents) : DefaultPlayerComponents(shared) {
37
+ override val handleAudioFocus: Boolean
38
+ get() = false
39
+ }
40
+
41
+ private class CustomizedDrmSessionManagerProvider : DefaultDrmSessionManagerProvider() {
42
+ override fun buildDrmSessionManager(
43
+ dataSourceFactory: AuthorizedDataSourceProvider,
44
+ source: Source,
45
+ sourceDrm: SourceDrm
46
+ ): DrmSessionManager? {
47
+ if (sourceDrm is SourceDrmOverridden) {
48
+ return buildDrmOverridden(dataSourceFactory, sourceDrm)
49
+ } else {
50
+ return super.buildDrmSessionManager(dataSourceFactory, source, sourceDrm)
51
+ }
52
+ }
53
+
54
+ private fun buildDrmOverridden(dataSourceFactory: AuthorizedDataSourceProvider, sourceDrm: SourceDrmOverridden): DrmSessionManager? {
55
+ val drmUuid = when (sourceDrm.packageName) {
56
+ DrmWidevine.packageName -> C.WIDEVINE_UUID
57
+ DrmPlayready.packageName -> C.PLAYREADY_UUID
58
+ else -> C.UUID_NIL
59
+ }
60
+ val licenseUrl = sourceDrm.licenseServer?.toString() ?: ""
61
+ val forceLicenseUrl = sourceDrm.forceLicenseServer
62
+ val httpDataSourceFactory = dataSourceFactory.buildHttpDataSourceFactory(sourceDrm.authorization)
63
+
64
+ val drmCallback = LoggingDrmCallback(
65
+ OverriddenHttpMediaDrmCallback(
66
+ licenseUrl,
67
+ forceLicenseUrl,
68
+ httpDataSourceFactory
69
+ ),
70
+ licenseUrl
71
+ )
72
+
73
+ return DefaultDrmSessionManager.Builder()
74
+ .setUuidAndExoMediaDrmProvider(drmUuid, FrameworkMediaDrm.DEFAULT_PROVIDER)
75
+ .setMultiSession(false)
76
+ .build(drmCallback)
77
+ }
78
+
79
+ override fun buildDrmCallback(licenseUrl: String, forceLicenseUrl: Boolean, httpDataSourceFactory: HttpDataSource.Factory): MediaDrmCallback {
80
+ return LoggingDrmCallback(super.buildDrmCallback(licenseUrl, forceLicenseUrl, httpDataSourceFactory), licenseUrl)
81
+ }
82
+ }
83
+ }
84
+
@@ -0,0 +1,49 @@
1
+ package com.twentyfouri.tvbridge.global.utils
2
+
3
+ import com.twentyfouri.player.base.SourceAuthorization
4
+ import com.twentyfouri.player.base.SourceDrm
5
+ import java.net.URI
6
+
7
+ open class SourceDrmOverridden protected constructor(
8
+ builder: Builder
9
+ ) : SourceDrm() {
10
+
11
+ override val packageName: String = builder.packageName
12
+ ?: throw IllegalStateException("Missing SourceDrmOverridden.packageName")
13
+ val licenseServer: URI? = builder.licenseServer
14
+ val forceLicenseServer: Boolean = builder.forceLicenseServer
15
+ val authorization: SourceAuthorization? = builder.authorization
16
+
17
+ override fun hashCode(): Int = licenseServer.hashCode()
18
+
19
+ override fun toString(): String = "SourceDrmOverridden($licenseServer)"
20
+
21
+ override fun equals(other: Any?): Boolean = other is SourceDrmOverridden &&
22
+ packageName == other.packageName &&
23
+ licenseServer == other.licenseServer &&
24
+ forceLicenseServer == other.forceLicenseServer &&
25
+ authorization == other.authorization
26
+
27
+ override fun buildUpon() = Builder()
28
+ .also { it.applyFrom(this) }
29
+
30
+ open class Builder : SourceDrm.Builder() {
31
+ var packageName: String? = null
32
+ var licenseServer: URI? = null
33
+ var forceLicenseServer: Boolean = false
34
+ var authorization: SourceAuthorization? = null
35
+
36
+ open fun applyFrom(source: SourceDrmOverridden) {
37
+ packageName = source.packageName
38
+ licenseServer = source.licenseServer
39
+ forceLicenseServer = source.forceLicenseServer
40
+ authorization = source.authorization
41
+ }
42
+
43
+ override fun build() =
44
+ SourceDrmOverridden(this)
45
+ }
46
+ }
47
+
48
+ inline fun SourceDrmOverridden(block: SourceDrmOverridden.Builder.() -> Unit) =
49
+ SourceDrmOverridden.Builder().apply(block).build()
@@ -1,6 +1,7 @@
1
1
  package com.twentyfouri.tvbridge.jump
2
2
 
3
3
  import android.util.Log
4
+ import com.google.android.exoplayer2.ExoPlayer
4
5
  import com.google.android.exoplayer2.SimpleExoPlayer
5
6
  import com.google.gson.JsonObject
6
7
  import com.twentyfouri.tvbridge.BuildConfig
@@ -40,8 +41,9 @@ class JumpAnalytics {
40
41
  hasContent = true
41
42
  }
42
43
 
43
- fun setVideoPlayer(player: SimpleExoPlayer?) {
44
- if (player == null) {
44
+ fun setVideoPlayer(player: ExoPlayer?) {
45
+ @Suppress("DEPRECATION")
46
+ if (player !is SimpleExoPlayer) {
45
47
  /* NOTICE: passing null also causes insights.setContentInfo(null) */
46
48
  jump.exoplayerextension.JumpKit.insights.setVideoPlayer(null)
47
49
  hasContent = false
@@ -37,6 +37,8 @@ class BridgeWebView @JvmOverloads constructor(
37
37
  webSettings.useWideViewPort = true
38
38
  webSettings.loadWithOverviewMode = true
39
39
  webSettings.mediaPlaybackRequiresUserGesture = false
40
+ webSettings.allowFileAccess = true
41
+ webSettings.allowContentAccess = true
40
42
  webSettings.userAgentString = config?.userAgent?.value ?: userAgent
41
43
 
42
44
  // Log the settings of the WebView.
@@ -7,8 +7,11 @@ object MediaEventType {
7
7
  const val TIME_UPDATE = "timeupdate"
8
8
  const val ERROR = "error"
9
9
  const val PLAYING = "playing"
10
+ const val PAUSE = "pause"
11
+ const val PLAY = "pause"
10
12
  const val ENDED = "ended"
11
13
  const val WAITING = "waiting"
14
+ const val SEEKING = "seeking"
12
15
  const val SEEKED = "seeked"
13
16
  const val KEY_PRESS = "keypress"
14
17
  const val KEY_UP = "keyup"