unified-video-framework 1.0.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 (129) hide show
  1. package/.github/workflows/ci.yml +253 -0
  2. package/ANDROID_TV_IMPLEMENTATION.md +313 -0
  3. package/COMPLETION_STATUS.md +165 -0
  4. package/CONTRIBUTING.md +376 -0
  5. package/FINAL_STATUS_REPORT.md +170 -0
  6. package/FRAMEWORK_REVIEW.md +247 -0
  7. package/IMPROVEMENTS_SUMMARY.md +168 -0
  8. package/LICENSE +21 -0
  9. package/NATIVE_APP_INTEGRATION_GUIDE.md +903 -0
  10. package/PAYWALL_RENTAL_FLOW.md +499 -0
  11. package/PLATFORM_SETUP_GUIDE.md +1636 -0
  12. package/README.md +315 -0
  13. package/RUN_LOCALLY.md +151 -0
  14. package/apps/demo/cast-sender-min.html +173 -0
  15. package/apps/demo/custom-player.html +883 -0
  16. package/apps/demo/demo.html +990 -0
  17. package/apps/demo/enhanced-player.html +3556 -0
  18. package/apps/demo/index.html +159 -0
  19. package/apps/rental-api/.env.example +24 -0
  20. package/apps/rental-api/README.md +23 -0
  21. package/apps/rental-api/migrations/001_init.sql +35 -0
  22. package/apps/rental-api/migrations/002_videos.sql +10 -0
  23. package/apps/rental-api/migrations/003_add_gateway_subref.sql +4 -0
  24. package/apps/rental-api/migrations/004_update_gateways.sql +4 -0
  25. package/apps/rental-api/migrations/005_seed_demo_video.sql +5 -0
  26. package/apps/rental-api/package-lock.json +2045 -0
  27. package/apps/rental-api/package.json +33 -0
  28. package/apps/rental-api/scripts/run-migration.js +42 -0
  29. package/apps/rental-api/scripts/update-video-currency.js +21 -0
  30. package/apps/rental-api/scripts/update-video-price.js +19 -0
  31. package/apps/rental-api/src/config.ts +14 -0
  32. package/apps/rental-api/src/db.ts +10 -0
  33. package/apps/rental-api/src/routes/cashfree.ts +167 -0
  34. package/apps/rental-api/src/routes/pesapal.ts +92 -0
  35. package/apps/rental-api/src/routes/rentals.ts +242 -0
  36. package/apps/rental-api/src/routes/webhooks.ts +73 -0
  37. package/apps/rental-api/src/server.ts +41 -0
  38. package/apps/rental-api/src/services/entitlements.ts +45 -0
  39. package/apps/rental-api/src/services/payments.ts +22 -0
  40. package/apps/rental-api/tsconfig.json +17 -0
  41. package/check-urls.ps1 +74 -0
  42. package/comparison-report.md +181 -0
  43. package/docs/PAYWALL.md +95 -0
  44. package/docs/PLAYER_UI_VISIBILITY.md +431 -0
  45. package/docs/README.md +7 -0
  46. package/docs/SYSTEM_ARCHITECTURE.md +612 -0
  47. package/docs/VDOCIPHER_CLONE_REQUIREMENTS.md +403 -0
  48. package/examples/android/JavaSampleApp/MainActivity.java +641 -0
  49. package/examples/android/JavaSampleApp/activity_main.xml +226 -0
  50. package/examples/android/SampleApp/MainActivity.kt +430 -0
  51. package/examples/ios/SampleApp/ViewController.swift +337 -0
  52. package/examples/ios/SwiftUISampleApp/ContentView.swift +304 -0
  53. package/iOS_IMPLEMENTATION_OPTIONS.md +470 -0
  54. package/ios/UnifiedVideoPlayer/UnifiedVideoPlayer.podspec +33 -0
  55. package/jest.config.js +33 -0
  56. package/jitpack.yml +5 -0
  57. package/lerna.json +35 -0
  58. package/package.json +69 -0
  59. package/packages/PLATFORM_STATUS.md +163 -0
  60. package/packages/android/build.gradle +135 -0
  61. package/packages/android/src/main/AndroidManifest.xml +36 -0
  62. package/packages/android/src/main/java/com/unifiedvideo/player/PlayerConfiguration.java +221 -0
  63. package/packages/android/src/main/java/com/unifiedvideo/player/UnifiedVideoPlayer.java +1037 -0
  64. package/packages/android/src/main/java/com/unifiedvideo/player/UnifiedVideoPlayer.kt +707 -0
  65. package/packages/android/src/main/java/com/unifiedvideo/player/analytics/AnalyticsProvider.java +9 -0
  66. package/packages/android/src/main/java/com/unifiedvideo/player/cast/CastManager.java +141 -0
  67. package/packages/android/src/main/java/com/unifiedvideo/player/cast/CastOptionsProvider.java +29 -0
  68. package/packages/android/src/main/java/com/unifiedvideo/player/overlay/WatermarkOverlayView.java +88 -0
  69. package/packages/android/src/main/java/com/unifiedvideo/player/pip/PipActionReceiver.java +33 -0
  70. package/packages/android/src/main/java/com/unifiedvideo/player/services/PlaybackService.java +110 -0
  71. package/packages/android/src/main/java/com/unifiedvideo/player/services/PlayerHolder.java +19 -0
  72. package/packages/core/package.json +34 -0
  73. package/packages/core/src/BasePlayer.ts +250 -0
  74. package/packages/core/src/VideoPlayer.ts +237 -0
  75. package/packages/core/src/VideoPlayerFactory.ts +145 -0
  76. package/packages/core/src/index.ts +20 -0
  77. package/packages/core/src/interfaces/IVideoPlayer.ts +184 -0
  78. package/packages/core/src/interfaces.ts +240 -0
  79. package/packages/core/src/utils/EventEmitter.ts +66 -0
  80. package/packages/core/src/utils/PlatformDetector.ts +300 -0
  81. package/packages/core/tsconfig.json +20 -0
  82. package/packages/enact/package.json +51 -0
  83. package/packages/enact/src/VideoPlayer.js +365 -0
  84. package/packages/enact/src/adapters/TizenAdapter.js +354 -0
  85. package/packages/enact/src/index.js +82 -0
  86. package/packages/ios/BUILD_INSTRUCTIONS.md +108 -0
  87. package/packages/ios/FIX_EMBED_ISSUE.md +142 -0
  88. package/packages/ios/GETTING_STARTED.md +100 -0
  89. package/packages/ios/Package.swift +35 -0
  90. package/packages/ios/README.md +84 -0
  91. package/packages/ios/Sources/UnifiedVideoPlayer/Analytics/AnalyticsEmitter.swift +26 -0
  92. package/packages/ios/Sources/UnifiedVideoPlayer/DRM/FairPlayDRMManager.swift +102 -0
  93. package/packages/ios/Sources/UnifiedVideoPlayer/Info.plist +24 -0
  94. package/packages/ios/Sources/UnifiedVideoPlayer/Remote/RemoteCommandCenter.swift +109 -0
  95. package/packages/ios/Sources/UnifiedVideoPlayer/UnifiedVideoPlayer.swift +811 -0
  96. package/packages/ios/Sources/UnifiedVideoPlayer/UnifiedVideoPlayerView.swift +640 -0
  97. package/packages/ios/Sources/UnifiedVideoPlayer/Utilities/Color+Hex.swift +36 -0
  98. package/packages/ios/UnifiedVideoPlayer.podspec +27 -0
  99. package/packages/ios/UnifiedVideoPlayer.xcodeproj/project.pbxproj +385 -0
  100. package/packages/ios/build_framework.sh +55 -0
  101. package/packages/react-native/android/src/main/java/com/unifiedvideo/UnifiedVideoPlayerModule.kt +482 -0
  102. package/packages/react-native/ios/UnifiedVideoPlayer.swift +436 -0
  103. package/packages/react-native/package.json +51 -0
  104. package/packages/react-native/src/ReactNativePlayer.tsx +423 -0
  105. package/packages/react-native/src/VideoPlayer.tsx +224 -0
  106. package/packages/react-native/src/index.ts +28 -0
  107. package/packages/react-native/src/utils/EventEmitter.ts +66 -0
  108. package/packages/react-native/tsconfig.json +31 -0
  109. package/packages/roku/components/UnifiedVideoPlayer.brs +400 -0
  110. package/packages/roku/package.json +44 -0
  111. package/packages/roku/source/VideoPlayer.brs +231 -0
  112. package/packages/roku/source/main.brs +28 -0
  113. package/packages/web/GETTING_STARTED.md +292 -0
  114. package/packages/web/jest.config.js +28 -0
  115. package/packages/web/jest.setup.ts +110 -0
  116. package/packages/web/package.json +50 -0
  117. package/packages/web/src/SecureVideoPlayer.ts +1164 -0
  118. package/packages/web/src/WebPlayer.ts +3110 -0
  119. package/packages/web/src/__tests__/WebPlayer.test.ts +314 -0
  120. package/packages/web/src/index.ts +14 -0
  121. package/packages/web/src/paywall/PaywallController.ts +215 -0
  122. package/packages/web/src/react/WebPlayerView.tsx +177 -0
  123. package/packages/web/tsconfig.json +23 -0
  124. package/packages/web/webpack.config.js +45 -0
  125. package/server.js +131 -0
  126. package/server.py +84 -0
  127. package/test-urls.ps1 +97 -0
  128. package/test-video-urls.ps1 +87 -0
  129. package/tsconfig.json +39 -0
@@ -0,0 +1,436 @@
1
+ import Foundation
2
+ import AVFoundation
3
+ import AVKit
4
+ import React
5
+
6
+ @objc(UnifiedVideoPlayer)
7
+ class UnifiedVideoPlayer: RCTEventEmitter {
8
+ private var player: AVPlayer?
9
+ private var playerItem: AVPlayerItem?
10
+ private var playerLayer: AVPlayerLayer?
11
+ private var playerViewController: AVPlayerViewController?
12
+ private var timeObserver: Any?
13
+ private var statusObserver: NSKeyValueObservation?
14
+ private var bufferObserver: NSKeyValueObservation?
15
+ private var currentQualityIndex: Int = -1
16
+ private var availableQualities: [VideoQuality] = []
17
+
18
+ struct VideoQuality {
19
+ let height: Int
20
+ let width: Int
21
+ let bitrate: Int
22
+ let label: String
23
+ let index: Int
24
+ }
25
+
26
+ override static func requiresMainQueueSetup() -> Bool {
27
+ return true
28
+ }
29
+
30
+ override func supportedEvents() -> [String]! {
31
+ return [
32
+ "onReady",
33
+ "onPlay",
34
+ "onPause",
35
+ "onEnded",
36
+ "onTimeUpdate",
37
+ "onBuffering",
38
+ "onError",
39
+ "onQualityChanged",
40
+ "onVolumeChanged",
41
+ "onFullscreenChanged",
42
+ "onProgress",
43
+ "onSeeking",
44
+ "onSeeked",
45
+ "onLoadedMetadata"
46
+ ]
47
+ }
48
+
49
+ @objc
50
+ func initialize(_ config: NSDictionary) {
51
+ DispatchQueue.main.async {
52
+ self.player = AVPlayer()
53
+ self.setupNotifications()
54
+ self.sendEvent(withName: "onReady", body: nil)
55
+ }
56
+ }
57
+
58
+ @objc
59
+ func load(_ source: NSDictionary) {
60
+ guard let urlString = source["url"] as? String,
61
+ let url = URL(string: urlString) else {
62
+ sendEvent(withName: "onError", body: [
63
+ "code": "INVALID_URL",
64
+ "message": "Invalid video URL"
65
+ ])
66
+ return
67
+ }
68
+
69
+ DispatchQueue.main.async {
70
+ // Handle DRM if provided
71
+ if let drm = source["drm"] as? NSDictionary {
72
+ self.loadDRMContent(url: url, drm: drm)
73
+ } else {
74
+ self.loadStandardContent(url: url)
75
+ }
76
+
77
+ // Handle subtitles if provided
78
+ if let subtitles = source["subtitles"] as? [[String: Any]] {
79
+ self.loadSubtitles(subtitles)
80
+ }
81
+ }
82
+ }
83
+
84
+ private func loadStandardContent(url: URL) {
85
+ let asset = AVAsset(url: url)
86
+ playerItem = AVPlayerItem(asset: asset)
87
+
88
+ // Setup quality detection for HLS
89
+ if url.absoluteString.contains(".m3u8") {
90
+ detectHLSQualities(asset: asset)
91
+ }
92
+
93
+ setupPlayerItemObservers()
94
+ player?.replaceCurrentItem(with: playerItem)
95
+
96
+ // Get video metadata
97
+ asset.loadValuesAsynchronously(forKeys: ["duration", "tracks"]) {
98
+ var error: NSError? = nil
99
+ let status = asset.statusOfValue(forKey: "duration", error: &error)
100
+
101
+ if status == .loaded {
102
+ let duration = CMTimeGetSeconds(asset.duration)
103
+ let tracks = asset.tracks(withMediaType: .video)
104
+
105
+ if let videoTrack = tracks.first {
106
+ let size = videoTrack.naturalSize.applying(videoTrack.preferredTransform)
107
+
108
+ DispatchQueue.main.async {
109
+ self.sendEvent(withName: "onLoadedMetadata", body: [
110
+ "duration": duration,
111
+ "width": abs(size.width),
112
+ "height": abs(size.height)
113
+ ])
114
+ }
115
+ }
116
+ }
117
+ }
118
+ }
119
+
120
+ private func loadDRMContent(url: URL, drm: NSDictionary) {
121
+ guard let licenseUrl = drm["licenseUrl"] as? String,
122
+ let drmType = drm["type"] as? String else {
123
+ sendEvent(withName: "onError", body: [
124
+ "code": "DRM_CONFIG_ERROR",
125
+ "message": "Invalid DRM configuration"
126
+ ])
127
+ return
128
+ }
129
+
130
+ if drmType == "fairplay" {
131
+ loadFairPlayContent(url: url, licenseUrl: licenseUrl, headers: drm["headers"] as? [String: String])
132
+ } else {
133
+ sendEvent(withName: "onError", body: [
134
+ "code": "DRM_NOT_SUPPORTED",
135
+ "message": "DRM type \(drmType) not supported on iOS"
136
+ ])
137
+ }
138
+ }
139
+
140
+ private func loadFairPlayContent(url: URL, licenseUrl: String, headers: [String: String]?) {
141
+ // FairPlay implementation would go here
142
+ // This is a simplified version
143
+ let asset = AVURLAsset(url: url)
144
+
145
+ // Set up content key session for FairPlay
146
+ // Implementation details omitted for brevity
147
+
148
+ playerItem = AVPlayerItem(asset: asset)
149
+ setupPlayerItemObservers()
150
+ player?.replaceCurrentItem(with: playerItem)
151
+ }
152
+
153
+ private func detectHLSQualities(asset: AVAsset) {
154
+ guard let urlAsset = asset as? AVURLAsset else { return }
155
+
156
+ // Access the available media selection options
157
+ Task {
158
+ do {
159
+ let characteristics = try await urlAsset.load(.availableMediaCharacteristicsWithMediaSelectionOptions)
160
+
161
+ for characteristic in characteristics {
162
+ if let group = try await urlAsset.loadMediaSelectionGroup(for: characteristic) {
163
+ let options = group.options
164
+
165
+ availableQualities = options.enumerated().map { index, option in
166
+ let displayName = option.displayName ?? "Quality \(index + 1)"
167
+
168
+ // Try to extract resolution from display name
169
+ var height = 0
170
+ if let match = displayName.range(of: "\\d+p", options: .regularExpression) {
171
+ let heightStr = displayName[match].dropLast()
172
+ height = Int(heightStr) ?? 0
173
+ }
174
+
175
+ return VideoQuality(
176
+ height: height,
177
+ width: 0,
178
+ bitrate: 0,
179
+ label: displayName,
180
+ index: index
181
+ )
182
+ }
183
+ }
184
+ }
185
+ } catch {
186
+ print("Error loading HLS qualities: \(error)")
187
+ }
188
+ }
189
+ }
190
+
191
+ private func loadSubtitles(_ subtitles: [[String: Any]]) {
192
+ guard let playerItem = playerItem else { return }
193
+
194
+ for subtitle in subtitles {
195
+ guard let urlString = subtitle["url"] as? String,
196
+ let url = URL(string: urlString),
197
+ let language = subtitle["language"] as? String,
198
+ let label = subtitle["label"] as? String else { continue }
199
+
200
+ let asset = AVAsset(url: url)
201
+ let track = AVMutableCompositionTrack()
202
+
203
+ // Add subtitle track to player item
204
+ // Implementation details for subtitle loading
205
+ }
206
+ }
207
+
208
+ private func setupPlayerItemObservers() {
209
+ guard let playerItem = playerItem else { return }
210
+
211
+ // Status observer
212
+ statusObserver = playerItem.observe(\.status) { [weak self] item, _ in
213
+ if item.status == .failed {
214
+ self?.sendEvent(withName: "onError", body: [
215
+ "code": "PLAYBACK_ERROR",
216
+ "message": item.error?.localizedDescription ?? "Unknown playback error"
217
+ ])
218
+ }
219
+ }
220
+
221
+ // Buffer observer
222
+ bufferObserver = playerItem.observe(\.isPlaybackBufferEmpty) { [weak self] item, _ in
223
+ self?.sendEvent(withName: "onBuffering", body: ["isBuffering": item.isPlaybackBufferEmpty])
224
+ }
225
+
226
+ // Time observer
227
+ let interval = CMTime(seconds: 0.25, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
228
+ timeObserver = player?.addPeriodicTimeObserver(forInterval: interval, queue: .main) { [weak self] time in
229
+ let seconds = CMTimeGetSeconds(time)
230
+ self?.sendEvent(withName: "onTimeUpdate", body: ["currentTime": seconds])
231
+
232
+ // Calculate buffer progress
233
+ if let timeRanges = self?.playerItem?.loadedTimeRanges,
234
+ let duration = self?.playerItem?.duration {
235
+ var buffered: Double = 0
236
+
237
+ for value in timeRanges {
238
+ let timeRange = value.timeRangeValue
239
+ let start = CMTimeGetSeconds(timeRange.start)
240
+ let end = start + CMTimeGetSeconds(timeRange.duration)
241
+
242
+ if seconds >= start && seconds <= end {
243
+ buffered = end
244
+ break
245
+ }
246
+ }
247
+
248
+ let total = CMTimeGetSeconds(duration)
249
+ if total > 0 {
250
+ let percentage = (buffered / total) * 100
251
+ self?.sendEvent(withName: "onProgress", body: ["bufferedPercentage": percentage])
252
+ }
253
+ }
254
+ }
255
+ }
256
+
257
+ private func setupNotifications() {
258
+ NotificationCenter.default.addObserver(
259
+ self,
260
+ selector: #selector(playerDidFinishPlaying),
261
+ name: .AVPlayerItemDidPlayToEndTime,
262
+ object: nil
263
+ )
264
+ }
265
+
266
+ @objc private func playerDidFinishPlaying() {
267
+ sendEvent(withName: "onEnded", body: nil)
268
+ }
269
+
270
+ @objc
271
+ func play() {
272
+ DispatchQueue.main.async {
273
+ self.player?.play()
274
+ self.sendEvent(withName: "onPlay", body: nil)
275
+ }
276
+ }
277
+
278
+ @objc
279
+ func pause() {
280
+ DispatchQueue.main.async {
281
+ self.player?.pause()
282
+ self.sendEvent(withName: "onPause", body: nil)
283
+ }
284
+ }
285
+
286
+ @objc
287
+ func seek(_ time: Double) {
288
+ DispatchQueue.main.async {
289
+ let cmTime = CMTime(seconds: time, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
290
+ self.sendEvent(withName: "onSeeking", body: nil)
291
+
292
+ self.player?.seek(to: cmTime) { [weak self] completed in
293
+ if completed {
294
+ self?.sendEvent(withName: "onSeeked", body: ["currentTime": time])
295
+ }
296
+ }
297
+ }
298
+ }
299
+
300
+ @objc
301
+ func setVolume(_ volume: Double) {
302
+ DispatchQueue.main.async {
303
+ self.player?.volume = Float(volume)
304
+ self.sendEvent(withName: "onVolumeChanged", body: ["volume": volume])
305
+ }
306
+ }
307
+
308
+ @objc
309
+ func setPlaybackRate(_ rate: Double) {
310
+ DispatchQueue.main.async {
311
+ self.player?.rate = Float(rate)
312
+ }
313
+ }
314
+
315
+ @objc
316
+ func setQuality(_ index: Int) {
317
+ guard index >= 0 && index < availableQualities.count else { return }
318
+
319
+ currentQualityIndex = index
320
+ let quality = availableQualities[index]
321
+
322
+ // Implementation for quality switching in HLS
323
+ // This would involve selecting the appropriate variant in the HLS manifest
324
+
325
+ sendEvent(withName: "onQualityChanged", body: [
326
+ "height": quality.height,
327
+ "width": quality.width,
328
+ "bitrate": quality.bitrate,
329
+ "label": quality.label,
330
+ "index": quality.index
331
+ ])
332
+ }
333
+
334
+ @objc
335
+ func getQualities(_ callback: @escaping RCTResponseSenderBlock) {
336
+ let qualities = availableQualities.map { quality in
337
+ return [
338
+ "height": quality.height,
339
+ "width": quality.width,
340
+ "bitrate": quality.bitrate,
341
+ "label": quality.label,
342
+ "index": quality.index
343
+ ]
344
+ }
345
+ callback([NSNull(), qualities])
346
+ }
347
+
348
+ @objc
349
+ func getCurrentTime(_ callback: @escaping RCTResponseSenderBlock) {
350
+ guard let player = player else {
351
+ callback([0])
352
+ return
353
+ }
354
+
355
+ let time = CMTimeGetSeconds(player.currentTime())
356
+ callback([time])
357
+ }
358
+
359
+ @objc
360
+ func getDuration(_ callback: @escaping RCTResponseSenderBlock) {
361
+ guard let duration = playerItem?.duration else {
362
+ callback([0])
363
+ return
364
+ }
365
+
366
+ let seconds = CMTimeGetSeconds(duration)
367
+ callback([seconds])
368
+ }
369
+
370
+ @objc
371
+ func enterFullscreen() {
372
+ DispatchQueue.main.async {
373
+ guard self.playerViewController == nil else { return }
374
+
375
+ self.playerViewController = AVPlayerViewController()
376
+ self.playerViewController?.player = self.player
377
+
378
+ if let rootViewController = UIApplication.shared.keyWindow?.rootViewController {
379
+ rootViewController.present(self.playerViewController!, animated: true) {
380
+ self.sendEvent(withName: "onFullscreenChanged", body: ["isFullscreen": true])
381
+ }
382
+ }
383
+ }
384
+ }
385
+
386
+ @objc
387
+ func exitFullscreen() {
388
+ DispatchQueue.main.async {
389
+ self.playerViewController?.dismiss(animated: true) {
390
+ self.playerViewController = nil
391
+ self.sendEvent(withName: "onFullscreenChanged", body: ["isFullscreen": false])
392
+ }
393
+ }
394
+ }
395
+
396
+ @objc
397
+ func enterPictureInPicture() {
398
+ DispatchQueue.main.async {
399
+ guard AVPictureInPictureController.isPictureInPictureSupported() else {
400
+ self.sendEvent(withName: "onError", body: [
401
+ "code": "PIP_NOT_SUPPORTED",
402
+ "message": "Picture in Picture is not supported on this device"
403
+ ])
404
+ return
405
+ }
406
+
407
+ // PiP implementation would go here
408
+ }
409
+ }
410
+
411
+ @objc
412
+ func destroy() {
413
+ DispatchQueue.main.async {
414
+ self.player?.pause()
415
+ self.player?.replaceCurrentItem(with: nil)
416
+
417
+ if let timeObserver = self.timeObserver {
418
+ self.player?.removeTimeObserver(timeObserver)
419
+ }
420
+
421
+ self.statusObserver?.invalidate()
422
+ self.bufferObserver?.invalidate()
423
+
424
+ NotificationCenter.default.removeObserver(self)
425
+
426
+ self.player = nil
427
+ self.playerItem = nil
428
+ self.playerLayer = nil
429
+ self.playerViewController = nil
430
+ }
431
+ }
432
+
433
+ deinit {
434
+ destroy()
435
+ }
436
+ }
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@unified-video/react-native",
3
+ "version": "1.0.0",
4
+ "description": "React Native implementation of Unified Video Framework for iOS and Android",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "react-native": "src/index.ts",
8
+ "files": [
9
+ "dist",
10
+ "src",
11
+ "ios",
12
+ "android"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc -p tsconfig.json",
16
+ "watch": "tsc -p tsconfig.json --watch",
17
+ "clean": "rm -rf dist",
18
+ "test": "jest",
19
+ "lint": "eslint src --ext .ts,.tsx",
20
+ "prepare": "npm run build"
21
+ },
22
+ "dependencies": {
23
+ "@unified-video/core": "^1.0.0"
24
+ },
25
+ "peerDependencies": {
26
+ "react": ">=16.8.0",
27
+ "react-native": ">=0.60.0",
28
+ "react-native-video": "^6.0.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/react": "^18.0.0",
32
+ "@types/react-native": "^0.70.0",
33
+ "@react-native-community/eslint-config": "^3.0.0",
34
+ "typescript": "^4.9.0",
35
+ "metro-react-native-babel-preset": "^0.73.0"
36
+ },
37
+ "keywords": [
38
+ "video",
39
+ "player",
40
+ "react-native",
41
+ "ios",
42
+ "android",
43
+ "mobile",
44
+ "streaming"
45
+ ],
46
+ "author": "Your Company",
47
+ "license": "MIT",
48
+ "publishConfig": {
49
+ "access": "public"
50
+ }
51
+ }