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,903 @@
1
+ # 📱 Integrating Unified Video Framework into Existing Native Apps
2
+
3
+ This guide shows how to add the Unified Video Framework to your **existing iOS and Android apps** as a native SDK/library.
4
+
5
+ ---
6
+
7
+ ## 🎯 Integration Overview
8
+
9
+ Since you have existing native apps, you'll integrate the video player as:
10
+ - **iOS**: Swift/Objective-C Framework or CocoaPod
11
+ - **Android**: AAR library or Gradle dependency
12
+
13
+ ---
14
+
15
+ # Part 1: iOS Integration (Existing iOS App)
16
+
17
+ ## Option A: Swift Package Manager (Recommended)
18
+
19
+ ### Step 1: Create iOS Framework
20
+ Create `UnifiedVideoPlayer.xcframework` from the framework:
21
+
22
+ ```bash
23
+ # Build for iOS device
24
+ xcodebuild archive \
25
+ -scheme UnifiedVideoPlayer \
26
+ -archivePath ./build/ios.xcarchive \
27
+ -sdk iphoneos \
28
+ SKIP_INSTALL=NO
29
+
30
+ # Build for iOS simulator
31
+ xcodebuild archive \
32
+ -scheme UnifiedVideoPlayer \
33
+ -archivePath ./build/ios-sim.xcarchive \
34
+ -sdk iphonesimulator \
35
+ SKIP_INSTALL=NO
36
+
37
+ # Create XCFramework
38
+ xcodebuild -create-xcframework \
39
+ -framework ./build/ios.xcarchive/Products/Library/Frameworks/UnifiedVideoPlayer.framework \
40
+ -framework ./build/ios-sim.xcarchive/Products/Library/Frameworks/UnifiedVideoPlayer.framework \
41
+ -output ./UnifiedVideoPlayer.xcframework
42
+ ```
43
+
44
+ ### Step 2: Add to Your Existing iOS App
45
+
46
+ **In Xcode:**
47
+ 1. Select your project in navigator
48
+ 2. Select your app target
49
+ 3. Go to "General" tab
50
+ 4. Under "Frameworks, Libraries, and Embedded Content", click "+"
51
+ 5. Select "Add Files..." and choose `UnifiedVideoPlayer.xcframework`
52
+ 6. Set to "Embed & Sign"
53
+
54
+ ### Step 3: Import and Use in Your App
55
+
56
+ ```swift
57
+ // YourExistingViewController.swift
58
+ import UIKit
59
+ import UnifiedVideoPlayer // Import the framework
60
+
61
+ class YourExistingViewController: UIViewController {
62
+
63
+ private var videoPlayer: UnifiedVideoPlayer?
64
+
65
+ // Add video player to your existing view
66
+ func addVideoPlayer() {
67
+ // Create player instance
68
+ videoPlayer = UnifiedVideoPlayer()
69
+
70
+ // Configure player
71
+ let config = PlayerConfiguration(
72
+ autoPlay: true,
73
+ controls: true,
74
+ muted: false
75
+ )
76
+
77
+ // Initialize in your existing view hierarchy
78
+ videoPlayer?.initialize(
79
+ container: playerContainerView, // Your existing UIView
80
+ configuration: config
81
+ )
82
+
83
+ // Load video
84
+ videoPlayer?.load(url: "https://example.com/video.m3u8")
85
+
86
+ // Handle events
87
+ videoPlayer?.onReady = { [weak self] in
88
+ print("Player ready")
89
+ }
90
+
91
+ videoPlayer?.onError = { [weak self] error in
92
+ self?.handleVideoError(error)
93
+ }
94
+ }
95
+
96
+ // Integrate with your existing UI
97
+ @IBAction func existingPlayButtonTapped(_ sender: UIButton) {
98
+ videoPlayer?.play()
99
+ }
100
+
101
+ @IBAction func existingPauseButtonTapped(_ sender: UIButton) {
102
+ videoPlayer?.pause()
103
+ }
104
+ }
105
+ ```
106
+
107
+ ## Option B: CocoaPods Integration
108
+
109
+ ### Step 1: Create Podspec
110
+ Create `UnifiedVideoPlayer.podspec`:
111
+
112
+ ```ruby
113
+ Pod::Spec.new do |s|
114
+ s.name = 'UnifiedVideoPlayer'
115
+ s.version = '1.0.0'
116
+ s.summary = 'Unified Video Player for iOS'
117
+ s.homepage = 'https://yourcompany.com'
118
+ s.license = { :type => 'MIT', :file => 'LICENSE' }
119
+ s.author = { 'Your Company' => 'contact@yourcompany.com' }
120
+ s.source = { :git => 'https://github.com/yourcompany/unified-video-ios.git', :tag => s.version.to_s }
121
+
122
+ s.ios.deployment_target = '13.0'
123
+ s.swift_version = '5.0'
124
+
125
+ s.source_files = 'Sources/**/*.{swift,h,m}'
126
+ s.resources = 'Resources/**/*.{xib,storyboard,xcassets}'
127
+
128
+ s.frameworks = 'UIKit', 'AVFoundation', 'AVKit'
129
+
130
+ s.dependency 'Alamofire', '~> 5.0' # If needed
131
+ end
132
+ ```
133
+
134
+ ### Step 2: Add to Your App's Podfile
135
+
136
+ ```ruby
137
+ # Your existing Podfile
138
+ target 'YourExistingApp' do
139
+ use_frameworks!
140
+
141
+ # Your existing pods
142
+ pod 'Firebase/Analytics'
143
+ pod 'SDWebImage'
144
+
145
+ # Add Unified Video Player
146
+ pod 'UnifiedVideoPlayer', :path => '../unified-video-framework/ios'
147
+ # Or from git:
148
+ # pod 'UnifiedVideoPlayer', :git => 'https://github.com/yourcompany/unified-video-ios.git'
149
+ end
150
+ ```
151
+
152
+ ### Step 3: Install
153
+
154
+ ```bash
155
+ cd your-existing-ios-app
156
+ pod install
157
+ ```
158
+
159
+ ## Option C: Manual Framework Integration
160
+
161
+ ### Step 1: Build Framework
162
+
163
+ Create `UnifiedVideoPlayer.framework`:
164
+
165
+ ```swift
166
+ // UnifiedVideoPlayer.swift - Main class
167
+ import UIKit
168
+ import AVFoundation
169
+ import AVKit
170
+
171
+ @objc public class UnifiedVideoPlayer: NSObject {
172
+
173
+ private var player: AVPlayer?
174
+ private var playerLayer: AVPlayerLayer?
175
+ private var containerView: UIView?
176
+
177
+ // MARK: - Initialization
178
+
179
+ @objc public func initialize(container: UIView, configuration: [String: Any]? = nil) {
180
+ self.containerView = container
181
+ setupPlayer()
182
+ }
183
+
184
+ // MARK: - Public Methods
185
+
186
+ @objc public func load(url: String) {
187
+ guard let videoURL = URL(string: url) else { return }
188
+
189
+ let asset = AVAsset(url: videoURL)
190
+ let playerItem = AVPlayerItem(asset: asset)
191
+
192
+ player?.replaceCurrentItem(with: playerItem)
193
+ }
194
+
195
+ @objc public func play() {
196
+ player?.play()
197
+ }
198
+
199
+ @objc public func pause() {
200
+ player?.pause()
201
+ }
202
+
203
+ @objc public func seek(to time: Double) {
204
+ let cmTime = CMTime(seconds: time, preferredTimescale: 1000)
205
+ player?.seek(to: cmTime)
206
+ }
207
+
208
+ @objc public func setVolume(_ volume: Float) {
209
+ player?.volume = volume
210
+ }
211
+
212
+ @objc public func getCurrentTime() -> Double {
213
+ return player?.currentTime().seconds ?? 0
214
+ }
215
+
216
+ @objc public func getDuration() -> Double {
217
+ return player?.currentItem?.duration.seconds ?? 0
218
+ }
219
+
220
+ // MARK: - Private Methods
221
+
222
+ private func setupPlayer() {
223
+ player = AVPlayer()
224
+
225
+ playerLayer = AVPlayerLayer(player: player)
226
+ playerLayer?.frame = containerView?.bounds ?? .zero
227
+ playerLayer?.videoGravity = .resizeAspect
228
+
229
+ if let playerLayer = playerLayer {
230
+ containerView?.layer.addSublayer(playerLayer)
231
+ }
232
+
233
+ setupObservers()
234
+ }
235
+
236
+ private func setupObservers() {
237
+ // Add time observer
238
+ let interval = CMTime(seconds: 1.0, preferredTimescale: 1000)
239
+ player?.addPeriodicTimeObserver(forInterval: interval, queue: .main) { [weak self] time in
240
+ self?.onTimeUpdate?(time.seconds)
241
+ }
242
+
243
+ // Add other observers
244
+ NotificationCenter.default.addObserver(
245
+ self,
246
+ selector: #selector(playerDidFinishPlaying),
247
+ name: .AVPlayerItemDidPlayToEndTime,
248
+ object: player?.currentItem
249
+ )
250
+ }
251
+
252
+ @objc private func playerDidFinishPlaying() {
253
+ onEnded?()
254
+ }
255
+
256
+ // MARK: - Callbacks
257
+
258
+ @objc public var onReady: (() -> Void)?
259
+ @objc public var onPlay: (() -> Void)?
260
+ @objc public var onPause: (() -> Void)?
261
+ @objc public var onTimeUpdate: ((Double) -> Void)?
262
+ @objc public var onEnded: (() -> Void)?
263
+ @objc public var onError: ((Error) -> Void)?
264
+ }
265
+ ```
266
+
267
+ ### Step 2: Create Bridging Header (for Objective-C projects)
268
+
269
+ ```objc
270
+ // UnifiedVideoPlayer-Bridging-Header.h
271
+ #import <UnifiedVideoPlayer/UnifiedVideoPlayer-Swift.h>
272
+ ```
273
+
274
+ ### Step 3: Use in Objective-C
275
+
276
+ ```objc
277
+ // YourExistingViewController.m
278
+ #import "UnifiedVideoPlayer-Swift.h"
279
+
280
+ @interface YourExistingViewController ()
281
+ @property (nonatomic, strong) UnifiedVideoPlayer *videoPlayer;
282
+ @end
283
+
284
+ @implementation YourExistingViewController
285
+
286
+ - (void)addVideoPlayerToExistingView {
287
+ // Create player
288
+ self.videoPlayer = [[UnifiedVideoPlayer alloc] init];
289
+
290
+ // Initialize with your existing view
291
+ [self.videoPlayer initializeWithContainer:self.playerContainerView
292
+ configuration:nil];
293
+
294
+ // Load video
295
+ [self.videoPlayer loadWithUrl:@"https://example.com/video.mp4"];
296
+
297
+ // Set callbacks
298
+ self.videoPlayer.onReady = ^{
299
+ NSLog(@"Player ready");
300
+ };
301
+
302
+ // Play
303
+ [self.videoPlayer play];
304
+ }
305
+
306
+ @end
307
+ ```
308
+
309
+ ---
310
+
311
+ # Part 2: Android Integration (Existing Android App)
312
+
313
+ ## Option A: AAR Library (Recommended)
314
+
315
+ ### Step 1: Build AAR Library
316
+
317
+ ```gradle
318
+ // unified-video-player/build.gradle
319
+ apply plugin: 'com.android.library'
320
+
321
+ android {
322
+ compileSdkVersion 33
323
+
324
+ defaultConfig {
325
+ minSdkVersion 21
326
+ targetSdkVersion 33
327
+ }
328
+ }
329
+
330
+ dependencies {
331
+ implementation 'com.google.android.exoplayer:exoplayer:2.18.5'
332
+ implementation 'com.google.android.exoplayer:exoplayer-hls:2.18.5'
333
+ }
334
+
335
+ // Generate AAR
336
+ ./gradlew :unified-video-player:assembleRelease
337
+ ```
338
+
339
+ ### Step 2: Add AAR to Your Existing App
340
+
341
+ ```gradle
342
+ // Your app's build.gradle
343
+ dependencies {
344
+ // Your existing dependencies
345
+ implementation 'com.google.android.material:material:1.9.0'
346
+
347
+ // Add the AAR
348
+ implementation files('libs/unified-video-player.aar')
349
+
350
+ // Or from local module
351
+ implementation project(':unified-video-player')
352
+ }
353
+ ```
354
+
355
+ ### Step 3: Use in Your Existing Activity/Fragment
356
+
357
+ ```kotlin
358
+ // YourExistingActivity.kt
359
+ import com.unifiedvideo.player.UnifiedVideoPlayer
360
+ import com.unifiedvideo.player.PlayerConfiguration
361
+
362
+ class YourExistingActivity : AppCompatActivity() {
363
+
364
+ private lateinit var videoPlayer: UnifiedVideoPlayer
365
+
366
+ override fun onCreate(savedInstanceState: Bundle?) {
367
+ super.onCreate(savedInstanceState)
368
+ setContentView(R.layout.your_existing_layout)
369
+
370
+ // Add video player to your existing view
371
+ addVideoPlayer()
372
+ }
373
+
374
+ private fun addVideoPlayer() {
375
+ // Find your existing container view
376
+ val playerContainer = findViewById<FrameLayout>(R.id.player_container)
377
+
378
+ // Create and initialize player
379
+ videoPlayer = UnifiedVideoPlayer(this).apply {
380
+ initialize(
381
+ container = playerContainer,
382
+ configuration = PlayerConfiguration(
383
+ autoPlay = true,
384
+ controls = true,
385
+ muted = false
386
+ )
387
+ )
388
+
389
+ // Set event listeners
390
+ onReady = {
391
+ Log.d("Player", "Ready")
392
+ }
393
+
394
+ onError = { error ->
395
+ handleVideoError(error)
396
+ }
397
+
398
+ // Load video
399
+ load("https://example.com/video.m3u8")
400
+ }
401
+ }
402
+
403
+ // Integrate with your existing UI
404
+ fun onExistingPlayButtonClick() {
405
+ videoPlayer.play()
406
+ }
407
+
408
+ fun onExistingPauseButtonClick() {
409
+ videoPlayer.pause()
410
+ }
411
+
412
+ override fun onPause() {
413
+ super.onPause()
414
+ videoPlayer.pause()
415
+ }
416
+
417
+ override fun onDestroy() {
418
+ super.onDestroy()
419
+ videoPlayer.release()
420
+ }
421
+ }
422
+ ```
423
+
424
+ ## Option B: Gradle Dependency
425
+
426
+ ### Step 1: Publish to Maven Repository
427
+
428
+ ```gradle
429
+ // unified-video-player/build.gradle
430
+ apply plugin: 'maven-publish'
431
+
432
+ publishing {
433
+ publications {
434
+ release(MavenPublication) {
435
+ groupId = 'com.yourcompany'
436
+ artifactId = 'unified-video-player'
437
+ version = '1.0.0'
438
+
439
+ afterEvaluate {
440
+ from components.release
441
+ }
442
+ }
443
+ }
444
+ }
445
+ ```
446
+
447
+ ### Step 2: Add to Your App
448
+
449
+ ```gradle
450
+ // Your app's build.gradle
451
+ dependencies {
452
+ implementation 'com.yourcompany:unified-video-player:1.0.0'
453
+ }
454
+ ```
455
+
456
+ ## Option C: Java Implementation (for Java-based apps)
457
+
458
+ ```java
459
+ // YourExistingActivity.java
460
+ import com.unifiedvideo.player.UnifiedVideoPlayer;
461
+ import com.unifiedvideo.player.PlayerConfiguration;
462
+ import com.unifiedvideo.player.PlayerListener;
463
+
464
+ public class YourExistingActivity extends AppCompatActivity {
465
+
466
+ private UnifiedVideoPlayer videoPlayer;
467
+
468
+ @Override
469
+ protected void onCreate(Bundle savedInstanceState) {
470
+ super.onCreate(savedInstanceState);
471
+ setContentView(R.layout.your_existing_layout);
472
+
473
+ addVideoPlayer();
474
+ }
475
+
476
+ private void addVideoPlayer() {
477
+ // Find your existing container
478
+ FrameLayout playerContainer = findViewById(R.id.player_container);
479
+
480
+ // Create player
481
+ videoPlayer = new UnifiedVideoPlayer(this);
482
+
483
+ // Configure
484
+ PlayerConfiguration config = new PlayerConfiguration.Builder()
485
+ .setAutoPlay(true)
486
+ .setControls(true)
487
+ .setMuted(false)
488
+ .build();
489
+
490
+ // Initialize
491
+ videoPlayer.initialize(playerContainer, config);
492
+
493
+ // Set listener
494
+ videoPlayer.setPlayerListener(new PlayerListener() {
495
+ @Override
496
+ public void onReady() {
497
+ Log.d("Player", "Ready");
498
+ }
499
+
500
+ @Override
501
+ public void onError(Exception error) {
502
+ handleVideoError(error);
503
+ }
504
+ });
505
+
506
+ // Load video
507
+ videoPlayer.load("https://example.com/video.mp4");
508
+ }
509
+
510
+ @Override
511
+ protected void onPause() {
512
+ super.onPause();
513
+ if (videoPlayer != null) {
514
+ videoPlayer.pause();
515
+ }
516
+ }
517
+
518
+ @Override
519
+ protected void onDestroy() {
520
+ super.onDestroy();
521
+ if (videoPlayer != null) {
522
+ videoPlayer.release();
523
+ }
524
+ }
525
+ }
526
+ ```
527
+
528
+ ---
529
+
530
+ # Part 3: Complete SDK Implementation
531
+
532
+ ## UnifiedVideoPlayer Android Library
533
+
534
+ ```kotlin
535
+ // UnifiedVideoPlayer.kt - Complete implementation
536
+ package com.unifiedvideo.player
537
+
538
+ import android.content.Context
539
+ import android.net.Uri
540
+ import android.view.ViewGroup
541
+ import com.google.android.exoplayer2.*
542
+ import com.google.android.exoplayer2.source.MediaSource
543
+ import com.google.android.exoplayer2.source.ProgressiveMediaSource
544
+ import com.google.android.exoplayer2.source.hls.HlsMediaSource
545
+ import com.google.android.exoplayer2.ui.PlayerView
546
+ import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
547
+
548
+ class UnifiedVideoPlayer(private val context: Context) {
549
+
550
+ private var exoPlayer: ExoPlayer? = null
551
+ private var playerView: PlayerView? = null
552
+ private var container: ViewGroup? = null
553
+
554
+ // Event callbacks
555
+ var onReady: (() -> Unit)? = null
556
+ var onPlay: (() -> Unit)? = null
557
+ var onPause: (() -> Unit)? = null
558
+ var onTimeUpdate: ((Long) -> Unit)? = null
559
+ var onError: ((Exception) -> Unit)? = null
560
+ var onEnded: (() -> Unit)? = null
561
+
562
+ fun initialize(container: ViewGroup, configuration: PlayerConfiguration? = null) {
563
+ this.container = container
564
+
565
+ // Create ExoPlayer
566
+ exoPlayer = ExoPlayer.Builder(context).build()
567
+
568
+ // Create PlayerView
569
+ playerView = PlayerView(context).apply {
570
+ player = exoPlayer
571
+ useController = configuration?.controls ?: true
572
+ layoutParams = ViewGroup.LayoutParams(
573
+ ViewGroup.LayoutParams.MATCH_PARENT,
574
+ ViewGroup.LayoutParams.MATCH_PARENT
575
+ )
576
+ }
577
+
578
+ // Add to container
579
+ container.addView(playerView)
580
+
581
+ // Setup listeners
582
+ setupListeners()
583
+
584
+ // Apply configuration
585
+ configuration?.let {
586
+ exoPlayer?.playWhenReady = it.autoPlay
587
+ exoPlayer?.volume = if (it.muted) 0f else 1f
588
+ }
589
+ }
590
+
591
+ fun load(url: String) {
592
+ val uri = Uri.parse(url)
593
+ val mediaSource = createMediaSource(uri)
594
+
595
+ exoPlayer?.apply {
596
+ setMediaSource(mediaSource)
597
+ prepare()
598
+ }
599
+ }
600
+
601
+ fun play() {
602
+ exoPlayer?.play()
603
+ onPlay?.invoke()
604
+ }
605
+
606
+ fun pause() {
607
+ exoPlayer?.pause()
608
+ onPause?.invoke()
609
+ }
610
+
611
+ fun seek(position: Long) {
612
+ exoPlayer?.seekTo(position)
613
+ }
614
+
615
+ fun setVolume(volume: Float) {
616
+ exoPlayer?.volume = volume.coerceIn(0f, 1f)
617
+ }
618
+
619
+ fun getCurrentPosition(): Long {
620
+ return exoPlayer?.currentPosition ?: 0
621
+ }
622
+
623
+ fun getDuration(): Long {
624
+ return exoPlayer?.duration ?: 0
625
+ }
626
+
627
+ fun release() {
628
+ exoPlayer?.release()
629
+ exoPlayer = null
630
+ playerView?.let {
631
+ container?.removeView(it)
632
+ }
633
+ playerView = null
634
+ }
635
+
636
+ private fun createMediaSource(uri: Uri): MediaSource {
637
+ val dataSourceFactory = DefaultDataSourceFactory(context, "UnifiedVideoPlayer")
638
+
639
+ return when {
640
+ uri.path?.contains(".m3u8") == true -> {
641
+ HlsMediaSource.Factory(dataSourceFactory)
642
+ .createMediaSource(MediaItem.fromUri(uri))
643
+ }
644
+ else -> {
645
+ ProgressiveMediaSource.Factory(dataSourceFactory)
646
+ .createMediaSource(MediaItem.fromUri(uri))
647
+ }
648
+ }
649
+ }
650
+
651
+ private fun setupListeners() {
652
+ exoPlayer?.addListener(object : Player.Listener {
653
+ override fun onPlaybackStateChanged(state: Int) {
654
+ when (state) {
655
+ Player.STATE_READY -> onReady?.invoke()
656
+ Player.STATE_ENDED -> onEnded?.invoke()
657
+ }
658
+ }
659
+
660
+ override fun onPlayerError(error: PlaybackException) {
661
+ onError?.invoke(error)
662
+ }
663
+ })
664
+ }
665
+ }
666
+
667
+ data class PlayerConfiguration(
668
+ val autoPlay: Boolean = false,
669
+ val controls: Boolean = true,
670
+ val muted: Boolean = false
671
+ )
672
+ ```
673
+
674
+ ---
675
+
676
+ # Part 4: Migration Guide
677
+
678
+ ## For iOS Apps
679
+
680
+ ### Step 1: Replace Existing Video Player
681
+
682
+ ```swift
683
+ // Before (using AVPlayerViewController)
684
+ let player = AVPlayer(url: videoURL)
685
+ let playerVC = AVPlayerViewController()
686
+ playerVC.player = player
687
+ present(playerVC, animated: true)
688
+
689
+ // After (using UnifiedVideoPlayer)
690
+ let player = UnifiedVideoPlayer()
691
+ player.initialize(container: containerView)
692
+ player.load(url: videoURL.absoluteString)
693
+ player.play()
694
+ ```
695
+
696
+ ### Step 2: Update Event Handlers
697
+
698
+ ```swift
699
+ // Before
700
+ NotificationCenter.default.addObserver(
701
+ self,
702
+ selector: #selector(playerDidFinish),
703
+ name: .AVPlayerItemDidPlayToEndTime,
704
+ object: nil
705
+ )
706
+
707
+ // After
708
+ player.onEnded = { [weak self] in
709
+ self?.handleVideoEnd()
710
+ }
711
+ ```
712
+
713
+ ## For Android Apps
714
+
715
+ ### Step 1: Replace Existing Video Player
716
+
717
+ ```kotlin
718
+ // Before (using VideoView)
719
+ videoView.setVideoURI(videoUri)
720
+ videoView.start()
721
+
722
+ // After (using UnifiedVideoPlayer)
723
+ videoPlayer.initialize(container)
724
+ videoPlayer.load(videoUri.toString())
725
+ videoPlayer.play()
726
+ ```
727
+
728
+ ### Step 2: Update Event Handlers
729
+
730
+ ```kotlin
731
+ // Before
732
+ videoView.setOnCompletionListener {
733
+ handleVideoComplete()
734
+ }
735
+
736
+ // After
737
+ videoPlayer.onEnded = {
738
+ handleVideoComplete()
739
+ }
740
+ ```
741
+
742
+ ---
743
+
744
+ # Part 5: Testing Integration
745
+
746
+ ## iOS Testing
747
+
748
+ ```swift
749
+ // UnifiedVideoPlayerTests.swift
750
+ import XCTest
751
+ @testable import UnifiedVideoPlayer
752
+
753
+ class UnifiedVideoPlayerTests: XCTestCase {
754
+
755
+ var player: UnifiedVideoPlayer!
756
+
757
+ override func setUp() {
758
+ super.setUp()
759
+ player = UnifiedVideoPlayer()
760
+ }
761
+
762
+ func testPlayerInitialization() {
763
+ let container = UIView()
764
+ player.initialize(container: container)
765
+
766
+ XCTAssertNotNil(player)
767
+ }
768
+
769
+ func testVideoLoading() {
770
+ let expectation = self.expectation(description: "Video loads")
771
+
772
+ player.onReady = {
773
+ expectation.fulfill()
774
+ }
775
+
776
+ player.load(url: "https://example.com/test.mp4")
777
+
778
+ waitForExpectations(timeout: 10)
779
+ }
780
+ }
781
+ ```
782
+
783
+ ## Android Testing
784
+
785
+ ```kotlin
786
+ // UnifiedVideoPlayerTest.kt
787
+ import androidx.test.ext.junit.runners.AndroidJUnit4
788
+ import org.junit.Test
789
+ import org.junit.runner.RunWith
790
+ import org.junit.Assert.*
791
+
792
+ @RunWith(AndroidJUnit4::class)
793
+ class UnifiedVideoPlayerTest {
794
+
795
+ @Test
796
+ fun testPlayerInitialization() {
797
+ val context = InstrumentationRegistry.getInstrumentation().targetContext
798
+ val player = UnifiedVideoPlayer(context)
799
+
800
+ assertNotNull(player)
801
+ }
802
+
803
+ @Test
804
+ fun testVideoLoading() {
805
+ val context = InstrumentationRegistry.getInstrumentation().targetContext
806
+ val player = UnifiedVideoPlayer(context)
807
+ val container = FrameLayout(context)
808
+
809
+ player.initialize(container)
810
+ player.load("https://example.com/test.mp4")
811
+
812
+ assertTrue(player.getDuration() >= 0)
813
+ }
814
+ }
815
+ ```
816
+
817
+ ---
818
+
819
+ # Part 6: Distribution
820
+
821
+ ## iOS Distribution
822
+
823
+ ### Via CocoaPods
824
+ ```bash
825
+ # Tag and push
826
+ git tag '1.0.0'
827
+ git push --tags
828
+
829
+ # Publish pod
830
+ pod trunk push UnifiedVideoPlayer.podspec
831
+ ```
832
+
833
+ ### Via Swift Package Manager
834
+ ```swift
835
+ // Package.swift
836
+ let package = Package(
837
+ name: "UnifiedVideoPlayer",
838
+ platforms: [.iOS(.v13)],
839
+ products: [
840
+ .library(name: "UnifiedVideoPlayer", targets: ["UnifiedVideoPlayer"])
841
+ ],
842
+ targets: [
843
+ .target(name: "UnifiedVideoPlayer")
844
+ ]
845
+ )
846
+ ```
847
+
848
+ ## Android Distribution
849
+
850
+ ### Via Maven Central
851
+ ```gradle
852
+ // Publish to Maven Central
853
+ ./gradlew publish
854
+ ```
855
+
856
+ ### Via JitPack
857
+ ```gradle
858
+ // Add to root build.gradle
859
+ allprojects {
860
+ repositories {
861
+ maven { url 'https://jitpack.io' }
862
+ }
863
+ }
864
+
865
+ // Add dependency
866
+ dependencies {
867
+ implementation 'com.github.yourcompany:unified-video-android:1.0.0'
868
+ }
869
+ ```
870
+
871
+ ---
872
+
873
+ # 🎯 Quick Integration Checklist
874
+
875
+ ## iOS
876
+ - [ ] Choose integration method (SPM/CocoaPods/Manual)
877
+ - [ ] Add framework to project
878
+ - [ ] Import in your existing ViewControllers
879
+ - [ ] Replace old video player code
880
+ - [ ] Test on device and simulator
881
+ - [ ] Update App Store description if needed
882
+
883
+ ## Android
884
+ - [ ] Choose integration method (AAR/Gradle/Manual)
885
+ - [ ] Add library to project
886
+ - [ ] Import in your existing Activities/Fragments
887
+ - [ ] Replace old video player code
888
+ - [ ] Test on different Android versions
889
+ - [ ] Update Play Store description if needed
890
+
891
+ ---
892
+
893
+ # 📚 Support & Documentation
894
+
895
+ - **iOS Documentation**: `/docs/ios/README.md`
896
+ - **Android Documentation**: `/docs/android/README.md`
897
+ - **API Reference**: `/docs/api/README.md`
898
+ - **Migration Guide**: `/docs/migration/README.md`
899
+ - **Sample Apps**: `/examples/`
900
+
901
+ ---
902
+
903
+ This approach lets you keep your existing app structure and just add the video player as a component where needed!