@takeoffmedia/react-native-penthera 0.1.0

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 (96) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +203 -0
  3. package/android/build.gradle +105 -0
  4. package/android/gradle.properties +5 -0
  5. package/android/src/main/AndroidManifest.xml +51 -0
  6. package/android/src/main/java/com/takeoffmediareactnativepenthera/AssetQueueObserver.kt +148 -0
  7. package/android/src/main/java/com/takeoffmediareactnativepenthera/EventEmitter.kt +53 -0
  8. package/android/src/main/java/com/takeoffmediareactnativepenthera/PentheraModule.kt +104 -0
  9. package/android/src/main/java/com/takeoffmediareactnativepenthera/PentheraPackage.kt +16 -0
  10. package/android/src/main/java/com/takeoffmediareactnativepenthera/YourPlayerActivity.kt +14 -0
  11. package/android/src/main/java/com/takeoffmediareactnativepenthera/virtuoso/DemoLicenseManager.kt +50 -0
  12. package/android/src/main/java/com/takeoffmediareactnativepenthera/virtuoso/OfflineVideoEngine.kt +227 -0
  13. package/android/src/main/java/com/takeoffmediareactnativepenthera/virtuoso/PentheraContentProvider.kt +17 -0
  14. package/android/src/main/java/com/takeoffmediareactnativepenthera/virtuoso/ServiceStarter.kt +65 -0
  15. package/android/src/main/java/com/takeoffmediareactnativepenthera/virtuoso/data/Drm.kt +6 -0
  16. package/android/src/main/java/com/takeoffmediareactnativepenthera/virtuoso/data/Item.kt +34 -0
  17. package/android/src/main/java/com/takeoffmediareactnativepenthera/virtuoso/notification/NotificationFactory.kt +279 -0
  18. package/android/src/main/java/com/takeoffmediareactnativepenthera/virtuoso/notification/NotificationType.kt +10 -0
  19. package/android/src/main/java/com/takeoffmediareactnativepenthera/virtuoso/notification/ServiceForegroundNotificationProvider.kt +98 -0
  20. package/android/src/main/java/com/takeoffmediareactnativepenthera/virtuoso/util/Util.kt +22 -0
  21. package/android/src/main/res/drawable/ic_launcher_background.xml +170 -0
  22. package/android/src/main/res/drawable/small_logo.png +0 -0
  23. package/android/src/main/res/values/colors.xml +6 -0
  24. package/android/src/main/res/values/strings.xml +61 -0
  25. package/android/src/main/res/values/styles.xml +10 -0
  26. package/android/src/main/res/xml/network_security_config.xml +9 -0
  27. package/ios/Catalog.swift +30 -0
  28. package/ios/EventEmitter.swift +53 -0
  29. package/ios/Penthera-Bridging-Header.h +3 -0
  30. package/ios/Penthera.m +40 -0
  31. package/ios/Penthera.swift +384 -0
  32. package/ios/Penthera.xcodeproj/project.pbxproj +283 -0
  33. package/ios/Util.swift +16 -0
  34. package/ios/drm/FairPlayDrmSetup.swift +107 -0
  35. package/ios/drm/FairPlayLicenseProcessingDelegate.swift +42 -0
  36. package/lib/commonjs/data/data.json +58 -0
  37. package/lib/commonjs/hooks/index.js +13 -0
  38. package/lib/commonjs/hooks/index.js.map +1 -0
  39. package/lib/commonjs/hooks/usePenthera.js +146 -0
  40. package/lib/commonjs/hooks/usePenthera.js.map +1 -0
  41. package/lib/commonjs/index.js +36 -0
  42. package/lib/commonjs/index.js.map +1 -0
  43. package/lib/commonjs/interface/HomeTypes.js +6 -0
  44. package/lib/commonjs/interface/HomeTypes.js.map +1 -0
  45. package/lib/commonjs/interface/Idata.js +2 -0
  46. package/lib/commonjs/interface/Idata.js.map +1 -0
  47. package/lib/commonjs/interface/PentheraTypes.js +2 -0
  48. package/lib/commonjs/interface/PentheraTypes.js.map +1 -0
  49. package/lib/commonjs/nativeModules/index.js +48 -0
  50. package/lib/commonjs/nativeModules/index.js.map +1 -0
  51. package/lib/commonjs/utils/Penthera.js +19 -0
  52. package/lib/commonjs/utils/Penthera.js.map +1 -0
  53. package/lib/module/data/data.json +58 -0
  54. package/lib/module/hooks/index.js +2 -0
  55. package/lib/module/hooks/index.js.map +1 -0
  56. package/lib/module/hooks/usePenthera.js +139 -0
  57. package/lib/module/hooks/usePenthera.js.map +1 -0
  58. package/lib/module/index.js +5 -0
  59. package/lib/module/index.js.map +1 -0
  60. package/lib/module/interface/HomeTypes.js +2 -0
  61. package/lib/module/interface/HomeTypes.js.map +1 -0
  62. package/lib/module/interface/Idata.js +2 -0
  63. package/lib/module/interface/Idata.js.map +1 -0
  64. package/lib/module/interface/PentheraTypes.js +2 -0
  65. package/lib/module/interface/PentheraTypes.js.map +1 -0
  66. package/lib/module/nativeModules/index.js +35 -0
  67. package/lib/module/nativeModules/index.js.map +1 -0
  68. package/lib/module/utils/Penthera.js +12 -0
  69. package/lib/module/utils/Penthera.js.map +1 -0
  70. package/lib/typescript/hooks/index.d.ts +2 -0
  71. package/lib/typescript/hooks/index.d.ts.map +1 -0
  72. package/lib/typescript/hooks/usePenthera.d.ts +19 -0
  73. package/lib/typescript/hooks/usePenthera.d.ts.map +1 -0
  74. package/lib/typescript/index.d.ts +4 -0
  75. package/lib/typescript/index.d.ts.map +1 -0
  76. package/lib/typescript/interface/HomeTypes.d.ts +15 -0
  77. package/lib/typescript/interface/HomeTypes.d.ts.map +1 -0
  78. package/lib/typescript/interface/Idata.d.ts +30 -0
  79. package/lib/typescript/interface/Idata.d.ts.map +1 -0
  80. package/lib/typescript/interface/PentheraTypes.d.ts +104 -0
  81. package/lib/typescript/interface/PentheraTypes.d.ts.map +1 -0
  82. package/lib/typescript/nativeModules/index.d.ts +9 -0
  83. package/lib/typescript/nativeModules/index.d.ts.map +1 -0
  84. package/lib/typescript/utils/Penthera.d.ts +11 -0
  85. package/lib/typescript/utils/Penthera.d.ts.map +1 -0
  86. package/package.json +166 -0
  87. package/src/data/data.json +58 -0
  88. package/src/hooks/index.ts +1 -0
  89. package/src/hooks/usePenthera.ts +160 -0
  90. package/src/index.tsx +5 -0
  91. package/src/interface/HomeTypes.ts +16 -0
  92. package/src/interface/Idata.ts +34 -0
  93. package/src/interface/PentheraTypes.ts +104 -0
  94. package/src/nativeModules/index.ts +58 -0
  95. package/src/utils/Penthera.ts +10 -0
  96. package/takeoffmedia-react-native-penthera.podspec +36 -0
@@ -0,0 +1,16 @@
1
+ package com.takeoffmediareactnativepenthera
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+
8
+ class PentheraPackage : ReactPackage {
9
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
10
+ return listOf(PentheraModule(reactContext))
11
+ }
12
+
13
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
14
+ return emptyList()
15
+ }
16
+ }
@@ -0,0 +1,14 @@
1
+ package com.takeoffmediareactnativepenthera
2
+ import androidx.appcompat.app.AppCompatActivity
3
+ import com.bitmovin.player.api.Player
4
+
5
+ class YourPlayerActivity : AppCompatActivity() {
6
+
7
+ private lateinit var player: Player
8
+
9
+ // ...
10
+
11
+ fun getPlayerInstance(): Player {
12
+ return player
13
+ }
14
+ }
@@ -0,0 +1,50 @@
1
+ /*
2
+ Created by Penthera.
3
+ Copyright © 2020 penthera. All rights reserved.
4
+
5
+ This source file contains a very basic example showing how to use the Penthera Download2Go SDK.
6
+ Pay close attention to code comments marked IMPORTANT
7
+ */
8
+ package com.takeoffmediareactnativepenthera.virtuoso
9
+
10
+ import com.google.gson.Gson
11
+ import com.penthera.virtuososdk.client.drm.LicenseManager
12
+ import com.takeoffmediareactnativepenthera.virtuoso.data.Drm
13
+ import com.takeoffmediareactnativepenthera.virtuoso.data.Item
14
+
15
+ // Used if implementing executeKeyRequest
16
+ /**
17
+ * A basic license manager implementation which provides the widevine url.
18
+ *
19
+ * Note: For this LicenseManager implementation to be used the class must be specified in the manifest metadata
20
+ * e.g.:
21
+ * <meta-data tools:replace="android:value" android:name="com.penthera.virtuososdk.license.manager.impl" android:value="com.penthera.download2go8.DemoLicenseManager"></meta-data>
22
+ * for the above to work you will need to add the tools name space
23
+ * xmlns:tools="http://schemas.android.com/tools"
24
+ */
25
+ class DemoLicenseManager : LicenseManager() {
26
+ override fun getLicenseAcquistionUrl(): String {
27
+ // This demonstration uses the Google UAT server
28
+ val item = Gson().fromJson(this.mAsset.metadata, Item::class.java)
29
+ val drm = Gson().fromJson(item.drm, Drm::class.java)
30
+ return drm.licence_server ?: ""
31
+ }
32
+
33
+ override fun getKeyRequestProperties(): Map<String, String> {
34
+ val item = Gson().fromJson(this.mAsset.metadata, Item::class.java)
35
+ val drm = Gson().fromJson(item.drm, Drm::class.java)
36
+ return drm?.headers ?: mapOf()
37
+ }
38
+
39
+
40
+
41
+ // Override the following to perform more complex manipulation of the request and response,
42
+ // for instance if you need to parse a json response to retrieve the key response in base64
43
+ // You will also need to implement a post request method to support this implementation.
44
+ /*
45
+ public byte[] executeKeyRequest(UUID uuid, MediaDrm.KeyRequest request) throws IOException {
46
+ String url = getLicenseAcquistionUrl();
47
+ return post(url, request.getData(), getKeyRequestProperties());
48
+ }
49
+ */
50
+ }
@@ -0,0 +1,227 @@
1
+ package com.takeoffmediareactnativepenthera.virtuoso
2
+
3
+ import EventEmitter
4
+ import PentheraEvent
5
+ import com.bitmovin.player.util.ParcelUtil
6
+ import android.annotation.SuppressLint
7
+ import android.app.Activity
8
+ import android.content.Context
9
+ import android.util.Log
10
+ import android.widget.Toast
11
+ import com.bitmovin.player.PlayerView
12
+ import com.bitmovin.player.api.Player
13
+ import com.bitmovin.player.reactnative.PlayerModule
14
+
15
+ import com.facebook.react.bridge.ReactApplicationContext
16
+ import com.google.gson.Gson
17
+ import com.penthera.common.Common.AuthenticationStatus
18
+ import com.penthera.virtuososdk.client.EngineObserver
19
+ import com.penthera.virtuososdk.client.IAsset
20
+ import com.penthera.virtuososdk.client.IIdentifier
21
+ import com.penthera.virtuososdk.client.IPushRegistrationObserver
22
+ import com.penthera.virtuososdk.client.ISegmentedAsset
23
+ import com.penthera.virtuososdk.client.ISegmentedAssetFromParserObserver
24
+ import com.penthera.virtuososdk.client.Observers
25
+ import com.penthera.virtuososdk.client.Virtuoso
26
+ import com.penthera.virtuososdk.client.bitmovin.BitmovinSourceManager
27
+ import com.penthera.virtuososdk.client.builders.MPDAssetBuilder
28
+ import com.penthera.virtuososdk.interfaces.toolkit.VirtuosoSegmentedFile
29
+ import com.takeoffmediareactnativepenthera.AssetQueueObserver
30
+ import com.takeoffmediareactnativepenthera.YourPlayerActivity
31
+ import com.takeoffmediareactnativepenthera.virtuoso.data.Item
32
+ import java.net.URL
33
+ import java.util.UUID
34
+ import com.bitmovin.player.reactnative.SourceModule
35
+ import com.bitmovin.player.reactnative.converter.JsonConverter
36
+ import com.facebook.react.bridge.ReadableMap
37
+ import com.facebook.react.uimanager.UIManagerModule
38
+
39
+ class OfflineVideoEngine(private val context: ReactApplicationContext) {
40
+
41
+ var virtuoso: Virtuoso? = null
42
+ private lateinit var queueObserver: AssetQueueObserver
43
+
44
+ var asset: IAsset? = null
45
+ var assetId: String = ""
46
+
47
+ private val enginePauseObserver: Observers.IEngineObserver = object : EngineObserver() {
48
+ override fun engineStatusChanged(status: Int) {
49
+ Log.e("MiModulo", "<<<<<<<<<<status $status>>>>>>>>>>>>")
50
+ }
51
+ }
52
+
53
+ private fun getVirtuoso(activity: Activity): Virtuoso {
54
+ virtuoso = Virtuoso(context)
55
+ queueObserver = AssetQueueObserver(this)
56
+ onResume()
57
+ return virtuoso!!
58
+ }
59
+
60
+ fun onResume() {
61
+ virtuoso?.onResume()
62
+ virtuoso?.addObserver(queueObserver)
63
+ virtuoso?.addObserver(enginePauseObserver)
64
+ }
65
+
66
+ fun onPause() {
67
+ virtuoso?.onPause()
68
+ virtuoso?.removeObserver(queueObserver)
69
+ virtuoso?.removeObserver(enginePauseObserver)
70
+ }
71
+
72
+ fun setup(
73
+ user: String,
74
+ backplaneUrl: String,
75
+ publicKey: String,
76
+ privateKey: String,
77
+ activity: Activity
78
+ ): String {
79
+ Log.e("MiModulo", "<<<<<<<<<<setup>>>>>>>>>>>>")
80
+ val backplane = getVirtuoso(activity)
81
+ val status = backplane.backplane?.authenticationStatus
82
+ if (status == AuthenticationStatus.NOT_AUTHENTICATED) {
83
+ virtuoso!!.startup(
84
+ URL(backplaneUrl),//substitute the proper backplane url for you implementation
85
+ // substitute the proper backplane url for your implementation
86
+ UUID.randomUUID()
87
+ .toString(), // provide an appropriate unique user id. A random uuid is used here for demonstration purposes only
88
+ null, //Optional additional device id to be associated with the user account. This is not the device id generated by the virtuoso SDK
89
+ publicKey,//Penthera demo public key. Substitute the correct one.
90
+ privateKey,
91
+ object : IPushRegistrationObserver {
92
+ override fun onServiceAvailabilityResponse(
93
+ pushService: Int,
94
+ errorCode: Int
95
+ ) {
96
+ //callback for push registration. this will be detailed in subsequent tutorials
97
+ }
98
+ }
99
+ )
100
+ return "Penthera initialization successfully"
101
+ }
102
+ return "Penthera init yet"
103
+ }
104
+
105
+
106
+ fun downloadAsset(data: String, activity: Activity) {
107
+ val item = Gson().fromJson(data, Item::class.java)
108
+ assetId = item.assetId
109
+ val params = MPDAssetBuilder().apply {
110
+ assetId(item.assetId)
111
+ manifestUrl(URL(item.contentUrl))
112
+ assetObserver(AssetParseObserver(context))
113
+ addToQueue(true)
114
+ desiredVideoBitrate(Int.MAX_VALUE)
115
+ withMetadata(data)
116
+ setClientSideAdSupport(true)
117
+ }.build()
118
+
119
+ virtuoso!!.assetManager.createMPDSegmentedAssetAsync(params)
120
+ }
121
+
122
+ fun getDownloads(): String? {
123
+ Log.e("MiModulo", "getDownloads")
124
+ val completedList = mutableListOf<MutableMap<String, Any>>()
125
+ val cursor = virtuoso?.assetManager?.cursor
126
+ val count = cursor?.count
127
+ val columnNames = cursor?.columnNames
128
+ while (cursor?.moveToNext() == true) {
129
+ val dataMap = mutableMapOf<String, Any>()
130
+ for (columnName in columnNames!!) {
131
+ val columnIndex = cursor.getColumnIndex(columnName)
132
+ if (columnIndex >= 0) {
133
+ val value = cursor.getString(columnIndex)
134
+ if (value != null) {
135
+ dataMap[columnName] = value
136
+ dataMap["isCompleted"] = true
137
+ dataMap["isPaused"] = false
138
+ }
139
+ if(columnName == "assetId"){
140
+ dataMap["id"] = value
141
+ }
142
+ }
143
+ }
144
+ completedList.add(dataMap)
145
+ }
146
+
147
+ return Gson().toJson(completedList)
148
+
149
+ }
150
+
151
+ class AssetParseObserver(activity: Context) : ISegmentedAssetFromParserObserver {
152
+ private val mActivity: Context = activity
153
+
154
+ @SuppressLint("ShowToast")
155
+ override fun complete(asset: ISegmentedAsset?, error: Int, addedToQueue: Boolean) {
156
+
157
+ if (asset != null) {
158
+ Toast.makeText(
159
+ mActivity,
160
+ "Asset parsed and " + if (addedToQueue) "added" else "not added" + "to download queue",
161
+ Toast.LENGTH_LONG
162
+ ).show()
163
+ } else {
164
+ Toast.makeText(mActivity, "Error $error while parsing asset", Toast.LENGTH_LONG).show()
165
+ }
166
+ }
167
+ }
168
+
169
+ fun deleteAsset(assetId: String) {
170
+ //load asset if it has already been downloaded
171
+ val list: MutableList<IIdentifier>? = virtuoso?.assetManager?.getByAssetId(assetId)
172
+
173
+ list?.let {
174
+ if (it.isNotEmpty())
175
+ asset = list[0] as IAsset
176
+ EventEmitter.sharedInstance.dispatch(
177
+ "penthera",
178
+ PentheraEvent.ASSET_DELETED,
179
+ asset!!.assetId,
180
+ "test"
181
+ )
182
+ virtuoso?.assetManager?.delete(asset)
183
+ asset = null
184
+ }
185
+
186
+ }
187
+
188
+ fun getByAssetId(assetId: String): String? {
189
+ //load asset if it has already been downloaded
190
+ val list: MutableList<IIdentifier>? = virtuoso?.assetManager?.getByAssetId(assetId)
191
+
192
+ list?.let {
193
+ if (it.isNotEmpty()) {
194
+ asset = list[0] as VirtuosoSegmentedFile
195
+ val offlineUrl = asset?.playbackURL.toString()
196
+ val keyValueMap = HashMap<String, Any>()
197
+ keyValueMap["offlineUrl"] = offlineUrl
198
+ keyValueMap["metadata"] = asset?.metadata.toString()
199
+ return Gson().toJson(keyValueMap)
200
+ }
201
+ }
202
+ return null
203
+ }
204
+
205
+ fun updateUI() {}
206
+
207
+ fun loadBitmovinSourceManager(assetId: String, nativeId: String): Boolean {
208
+ virtuoso?.assetManager?.getByAssetId(assetId)?.firstOrNull()?.let { asset ->
209
+ val sourceManager = BitmovinSourceManager(context, asset as ISegmentedAsset)
210
+ val sourceItem = sourceManager.bitmovinSourceItem
211
+ val playerModule = context.getNativeModule(PlayerModule::class.java)
212
+ if (playerModule != null && sourceItem != null) {
213
+ val player = playerModule.getPlayer(nativeId)
214
+ if (player != null) {
215
+ player.load(sourceItem)
216
+ return true
217
+ }
218
+ }
219
+ }
220
+ return false
221
+ }
222
+
223
+ private fun uiManager(): UIManagerModule? =
224
+ context.getNativeModule(UIManagerModule::class.java)
225
+
226
+
227
+ }
@@ -0,0 +1,17 @@
1
+ package com.takeoffmediareactnativepenthera.virtuoso
2
+
3
+ import com.penthera.virtuososdk.database.impl.provider.VirtuosoSDKContentProvider
4
+
5
+ class PentheraContentProvider: VirtuosoSDKContentProvider() {
6
+
7
+ companion object {
8
+ init {
9
+ setAuthority("com.pentheraexample.virtuoso.provider")
10
+ }
11
+ }
12
+
13
+ override fun getAuthority(): String {
14
+ return "com.pentheraexample.virtuoso.provider"
15
+ }
16
+
17
+ }
@@ -0,0 +1,65 @@
1
+
2
+ package com.takeoffmediareactnativepenthera.virtuoso
3
+
4
+ import android.app.Notification
5
+ import android.content.Context
6
+ import android.content.Intent
7
+ import com.penthera.virtuososdk.service.VirtuosoServiceStarter
8
+ import com.takeoffmediareactnativepenthera.virtuoso.notification.NotificationFactory
9
+ import com.takeoffmediareactnativepenthera.virtuoso.notification.ServiceForegroundNotificationProvider
10
+
11
+ class ServiceStarter : VirtuosoServiceStarter() {
12
+
13
+ companion object {
14
+
15
+ private var currentNotification: Notification? = null
16
+
17
+ /**
18
+ * This convenience method shows how to call the underlying method to force the SDK
19
+ * to update the foreground notification stored within the download service.
20
+ * This method causes an asynchronous process which will call back into the
21
+ * getForegroundServiceNotification() method and then send the resulting notification
22
+ * into the download service process.
23
+ */
24
+ fun updateNotification(aContext: Context, aIntent: Intent) {
25
+ updateNotification(aContext, aIntent, ServiceStarter::class.java)
26
+ }
27
+ }
28
+
29
+ // This is a helper class which is used in the demo for creating the notifications
30
+ private val notificationFactory: NotificationFactory = NotificationFactory("PentheraExample")
31
+
32
+ /**
33
+ * This method will be called by the framework to request the generation of a notification,
34
+ * to be displayed as the foreground notification for the download service. The notification
35
+ * returned from this method will be parcelled and passed over the process boundary into the
36
+ * service. This imposes some limitations on the size of the notification returned, as it must
37
+ * be suitable to pass over the boundary.
38
+ *
39
+ * In this demo we use a separate notification factory class to create foreground notifications
40
+ * for both these notifications and the ones created directly in the service
41
+ * (see ServiceForegroundNotificationProvider). In a more complex application this may not be shared.
42
+ *
43
+ * @param context A context which can be used in generating the notification
44
+ * @param forIntent The intent for which to create a notification
45
+ * @return The new notification
46
+ */
47
+ override fun getForegroundServiceNotification(context: Context?, forIntent: Intent?): Notification {
48
+
49
+ val notification = notificationFactory.getNotification(context!!, forIntent)
50
+ if (notification != null) {
51
+ currentNotification = notification
52
+ }
53
+
54
+ return currentNotification!!
55
+ }
56
+
57
+ override fun getForegroundServiceNotificationProvider(): Class<*> {
58
+ // Returning this class definition causes the service to instantiate and use the class
59
+ // from within the service process to generate all notifications relating to asset downloads.
60
+ // Returning null results in the classic SDK behaviour where all notifications are generated
61
+ // and delivered to the service via the service starter.
62
+ return ServiceForegroundNotificationProvider::class.java
63
+ }
64
+
65
+ }
@@ -0,0 +1,6 @@
1
+ package com.takeoffmediareactnativepenthera.virtuoso.data
2
+
3
+ data class Drm(
4
+ var licence_server: String = "",
5
+ var headers: Map<String, String> = mapOf(),
6
+ )
@@ -0,0 +1,34 @@
1
+ package com.takeoffmediareactnativepenthera.virtuoso.data
2
+
3
+ import com.google.gson.annotations.SerializedName
4
+
5
+ data class Item(
6
+
7
+ var id: String,
8
+
9
+ @SerializedName("asset_id")
10
+ val assetId: String = "",
11
+
12
+ @SerializedName("title")
13
+ val title: String = "",
14
+
15
+ @SerializedName("content_url")
16
+ val contentUrl: String = "",
17
+
18
+ @SerializedName("poster")
19
+ val poster: String = "",
20
+
21
+ @SerializedName("thumbnail")
22
+ val thumbnail: String = "",
23
+
24
+ @SerializedName("en_subtitle")
25
+ val subtitle: String? = "",
26
+
27
+ @SerializedName("drm")
28
+ val drm: String? = null,
29
+
30
+ )
31
+
32
+
33
+
34
+