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
package/README.md ADDED
@@ -0,0 +1,315 @@
1
+ # Unified Video Player Framework
2
+
3
+ A comprehensive cross-platform video player framework that provides a unified API for building video applications across all major platforms.
4
+
5
+ ## 🎯 Supported Platforms
6
+
7
+ - **Mobile**: iOS, Android
8
+ - **Web**: All modern browsers
9
+ - **Smart TVs**:
10
+ - Samsung Tizen (using Enact)
11
+ - LG webOS (using Enact)
12
+ - **Streaming Devices**:
13
+ - Roku
14
+ - Android TV
15
+ - Apple TV
16
+ - **Desktop**: Windows, macOS, Linux (via web)
17
+
18
+ ## ✨ Features
19
+
20
+ - **Unified API**: Single API across all platforms
21
+ - **DRM Support**: FairPlay, Widevine, PlayReady
22
+ - **Adaptive Streaming**: HLS, DASH support
23
+ - **Analytics**: Built-in analytics integration
24
+ - **Offline Playback**: Download and play offline
25
+ - **Casting**: Chromecast and AirPlay support
26
+ - **Picture-in-Picture**: Supported platforms
27
+ - **4K/8K & HDR**: High-quality video support
28
+ - **Subtitles & Multiple Audio Tracks**
29
+ - **Ad Integration**: Pre-roll, mid-roll, post-roll ads
30
+ - **Remote Control**: Full TV remote support
31
+
32
+ ## 📦 Installation
33
+
34
+ ### Using npm
35
+ ```bash
36
+ npm install @company/unified-video-framework
37
+ ```
38
+
39
+ ### Using yarn
40
+ ```bash
41
+ yarn add @company/unified-video-framework
42
+ ```
43
+
44
+ ## 🚀 Quick Start
45
+
46
+ ### Basic Usage
47
+
48
+ ```typescript
49
+ import { UnifiedVideoPlayer } from '@company/unified-video-framework';
50
+
51
+ function App() {
52
+ return (
53
+ <UnifiedVideoPlayer
54
+ source={{
55
+ url: 'https://example.com/video.m3u8',
56
+ type: 'application/x-mpegURL'
57
+ }}
58
+ config={{
59
+ autoPlay: true,
60
+ controls: true
61
+ }}
62
+ onReady={() => console.log('Player ready')}
63
+ onError={(error) => console.error('Player error:', error)}
64
+ />
65
+ );
66
+ }
67
+ ```
68
+
69
+ ### With DRM
70
+
71
+ ```typescript
72
+ import { UnifiedVideoPlayer, DRMType } from '@company/unified-video-framework';
73
+
74
+ function SecureVideoApp() {
75
+ return (
76
+ <UnifiedVideoPlayer
77
+ source={{
78
+ url: 'https://example.com/encrypted-video.mpd',
79
+ type: 'application/dash+xml',
80
+ drm: {
81
+ type: DRMType.WIDEVINE,
82
+ licenseUrl: 'https://license.example.com/widevine',
83
+ headers: {
84
+ 'X-Auth-Token': 'your-auth-token'
85
+ }
86
+ }
87
+ }}
88
+ config={{
89
+ autoPlay: false,
90
+ adaptiveBitrate: {
91
+ autoLevelEnabled: true,
92
+ startLevel: -1
93
+ }
94
+ }}
95
+ />
96
+ );
97
+ }
98
+ ```
99
+
100
+ ## 🏗️ Framework Architecture
101
+
102
+ ```
103
+ ┌────────────────────────────────────────────────┐
104
+ │ Application Layer (Your App) │
105
+ ├────────────────────────────────────────────────┤
106
+ │ Unified Video Framework API (TypeScript) │
107
+ ├────────────────────────────────────────────────┤
108
+ │ Platform Bridge Layer │
109
+ ├──────┬──────┬──────────┬──────┬──────────┬────┤
110
+ │ iOS │Android│ Enact │ Roku │ Web │Win │
111
+ │Native│Native │(Tizen/LG)│Bridge│ Browser │UWP │
112
+ └──────┴──────┴──────────┴──────┴──────────┴────┘
113
+ ```
114
+
115
+ ## 📱 Platform-Specific Setup
116
+
117
+ ### iOS
118
+ ```bash
119
+ cd ios && pod install
120
+ ```
121
+
122
+ ### Android
123
+ Add to `android/app/build.gradle`:
124
+ ```gradle
125
+ dependencies {
126
+ implementation 'com.google.android.exoplayer:exoplayer:2.18.0'
127
+ }
128
+ ```
129
+
130
+ ### Samsung Tizen TV
131
+ ```bash
132
+ npm run build:tizen
133
+ tizen install -n dist/tizen/app.wgt
134
+ ```
135
+
136
+ ### LG webOS TV
137
+ ```bash
138
+ npm run build:webos
139
+ ares-install dist/webos/app.ipk
140
+ ```
141
+
142
+ ### Roku
143
+ ```bash
144
+ npm run build:roku
145
+ # Upload via Roku Developer Dashboard
146
+ ```
147
+
148
+ ## 🛠️ Development
149
+
150
+ ### Setup
151
+ ```bash
152
+ # Install dependencies
153
+ npm install
154
+
155
+ # Bootstrap monorepo
156
+ npm run bootstrap
157
+
158
+ # Build all packages
159
+ npm run build
160
+ ```
161
+
162
+ ### Running Examples
163
+ ```bash
164
+ # Run mobile demo
165
+ cd apps/mobile-demo
166
+ npm run ios
167
+ # or
168
+ npm run android
169
+
170
+ # Run TV demo
171
+ cd apps/tv-demo
172
+ npm run serve
173
+
174
+ # Run web demo
175
+ cd apps/web-demo
176
+ npm start
177
+ ```
178
+
179
+ ## 📖 API Documentation
180
+
181
+ ### VideoPlayer Methods
182
+
183
+ | Method | Description | Returns |
184
+ |--------|-------------|---------|
185
+ | `load(source)` | Load a video source | `Promise<void>` |
186
+ | `play()` | Start playback | `Promise<void>` |
187
+ | `pause()` | Pause playback | `void` |
188
+ | `seek(time)` | Seek to position | `void` |
189
+ | `setVolume(level)` | Set volume (0-1) | `void` |
190
+ | `setQuality(quality)` | Set video quality | `void` |
191
+ | `setSubtitle(track)` | Set subtitle track | `void` |
192
+ | `destroy()` | Clean up player | `void` |
193
+
194
+ ### Events
195
+
196
+ | Event | Description | Payload |
197
+ |-------|-------------|---------|
198
+ | `ready` | Player initialized | - |
199
+ | `play` | Playback started | - |
200
+ | `pause` | Playback paused | - |
201
+ | `ended` | Playback ended | - |
202
+ | `error` | Error occurred | `{error: Error}` |
203
+ | `timeupdate` | Position updated | `{currentTime: number}` |
204
+ | `progress` | Buffer progress | `{buffered: number}` |
205
+ | `qualitychange` | Quality changed | `{quality: Quality}` |
206
+
207
+ ## 🎮 Remote Control Support
208
+
209
+ The framework automatically handles remote control inputs on TV platforms:
210
+
211
+ - **Play/Pause**: Media play/pause keys
212
+ - **Seek**: Left/Right arrow keys
213
+ - **Volume**: Up/Down arrow keys
214
+ - **Back**: Return/Back button
215
+ - **Select**: OK/Enter button
216
+
217
+ ## 📊 Analytics Integration
218
+
219
+ ```typescript
220
+ import { GoogleAnalytics, MixPanel } from '@company/analytics';
221
+
222
+ <UnifiedVideoPlayer
223
+ source={videoSource}
224
+ config={{
225
+ analytics: {
226
+ enabled: true,
227
+ providers: [
228
+ new GoogleAnalytics({ trackingId: 'UA-XXXXX' }),
229
+ new MixPanel({ token: 'your-token' })
230
+ ]
231
+ }
232
+ }}
233
+ />
234
+ ```
235
+
236
+ ## 🔒 DRM Configuration
237
+
238
+ ### FairPlay (iOS/Apple TV)
239
+ ```typescript
240
+ {
241
+ type: DRMType.FAIRPLAY,
242
+ certificateUrl: 'https://example.com/certificate',
243
+ licenseUrl: 'https://example.com/license'
244
+ }
245
+ ```
246
+
247
+ ### Widevine (Android/Chrome/Smart TVs)
248
+ ```typescript
249
+ {
250
+ type: DRMType.WIDEVINE,
251
+ licenseUrl: 'https://example.com/widevine/license'
252
+ }
253
+ ```
254
+
255
+ ### PlayReady (Windows/Xbox/Smart TVs)
256
+ ```typescript
257
+ {
258
+ type: DRMType.PLAYREADY,
259
+ licenseUrl: 'https://example.com/playready/license'
260
+ }
261
+ ```
262
+
263
+ ## 🧪 Testing
264
+
265
+ ```bash
266
+ # Run unit tests
267
+ npm test
268
+
269
+ # Run E2E tests
270
+ npm run test:e2e
271
+
272
+ # Platform-specific tests
273
+ npm run test:ios
274
+ npm run test:android
275
+ npm run test:tizen
276
+ ```
277
+
278
+ ## 📈 Performance Optimization
279
+
280
+ - **Lazy Loading**: Platform-specific code is loaded on demand
281
+ - **Code Splitting**: Automatic code splitting for web builds
282
+ - **Memory Management**: Automatic cleanup and resource management
283
+ - **Adaptive Bitrate**: Automatic quality adjustment based on network
284
+
285
+ ## 💳 Paywall Rentals (Stripe, Pesapal, Google Pay)
286
+
287
+ - For per-video rentals (no subscriptions), follow our step-by-step implementation guide:
288
+ - See PAYWALL_RENTAL_FLOW.md: [PAYWALL_RENTAL_FLOW.md](./PAYWALL_RENTAL_FLOW.md)
289
+ - Documentation index: [docs/](./docs/README.md)
290
+
291
+ ## 🤝 Contributing
292
+
293
+ Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
294
+
295
+ ## 📄 License
296
+
297
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
298
+
299
+ ## 🆘 Support
300
+
301
+ - **Documentation**: [https://docs.example.com](https://docs.example.com)
302
+ - **Issues**: [GitHub Issues](https://github.com/your-org/unified-video-framework/issues)
303
+ - **Discord**: [Join our Discord](https://discord.gg/example)
304
+ - **Email**: support@example.com
305
+
306
+ ## 🙏 Acknowledgments
307
+
308
+ - React Native team for the mobile framework
309
+ - Enact team for the Smart TV framework
310
+ - ExoPlayer team for Android video engine
311
+ - AVPlayer team for iOS video engine
312
+
313
+ ---
314
+
315
+ Built with ❤️ by Your Company
package/RUN_LOCALLY.md ADDED
@@ -0,0 +1,151 @@
1
+ # How to Run the Unified Video Framework Locally
2
+
3
+ Due to browser security policies (CORS), you need to run a local web server to properly test all features of the video framework, especially streaming formats like HLS and DASH.
4
+
5
+ ## Quick Start Options
6
+
7
+ ### Option 1: Using Node.js (Recommended)
8
+
9
+ If you have Node.js installed:
10
+
11
+ ```bash
12
+ # Navigate to the project directory
13
+ cd "C:\Users\Webnexs\Documents\OfficeBackup\AI\VideoPlayer FrameWork\unified-video-framework"
14
+
15
+ # Run the Node.js server
16
+ node server.js
17
+ ```
18
+
19
+ Then open your browser and go to: **http://localhost:3000**
20
+
21
+ ### Option 2: Using Python 3
22
+
23
+ If you have Python 3 installed:
24
+
25
+ ```bash
26
+ # Navigate to the project directory
27
+ cd "C:\Users\Webnexs\Documents\OfficeBackup\AI\VideoPlayer FrameWork\unified-video-framework"
28
+
29
+ # Run the Python server
30
+ python server.py
31
+ # or
32
+ python3 server.py
33
+ ```
34
+
35
+ Then open your browser and go to: **http://localhost:3000**
36
+
37
+ ### Option 3: Using Python's Built-in Server (Simple)
38
+
39
+ For a quick test with Python:
40
+
41
+ ```bash
42
+ # Navigate to the project directory
43
+ cd "C:\Users\Webnexs\Documents\OfficeBackup\AI\VideoPlayer FrameWork\unified-video-framework"
44
+
45
+ # Python 3
46
+ python -m http.server 3000
47
+
48
+ # Python 2 (if that's what you have)
49
+ python -m SimpleHTTPServer 3000
50
+ ```
51
+
52
+ Then open: **http://localhost:3000/apps/demo/demo.html**
53
+
54
+ ### Option 4: Using Live Server Extension (VS Code)
55
+
56
+ If you're using Visual Studio Code:
57
+
58
+ 1. Install the "Live Server" extension by Ritwick Dey
59
+ 2. Right-click on `apps/demo/demo.html`
60
+ 3. Select "Open with Live Server"
61
+
62
+ ### Option 5: Using npm's http-server
63
+
64
+ If you have npm installed globally:
65
+
66
+ ```bash
67
+ # Install http-server globally (one time only)
68
+ npm install -g http-server
69
+
70
+ # Navigate to the project directory
71
+ cd "C:\Users\Webnexs\Documents\OfficeBackup\AI\VideoPlayer FrameWork\unified-video-framework"
72
+
73
+ # Run the server
74
+ http-server -p 3000 --cors
75
+ ```
76
+
77
+ Then open: **http://localhost:3000/apps/demo/demo.html**
78
+
79
+ ## Why a Web Server is Required
80
+
81
+ ### ❌ What Doesn't Work with file:// Protocol:
82
+ - **CORS restrictions**: External libraries (HLS.js, dash.js) may not load
83
+ - **Module imports**: ES6 modules won't work
84
+ - **Streaming protocols**: HLS and DASH streams won't play
85
+ - **Fetch requests**: Can't load external resources
86
+ - **Security features**: Some browser APIs are restricted
87
+
88
+ ### ✅ What Works with a Proper Web Server:
89
+ - All video formats (MP4, HLS, DASH)
90
+ - Dynamic library loading
91
+ - Full CORS support
92
+ - Proper MIME types for streaming
93
+ - All browser features enabled
94
+
95
+ ## Testing the Demo
96
+
97
+ Once your server is running:
98
+
99
+ 1. **Open the demo**: Go to http://localhost:3000
100
+ 2. **Test basic playback**: Click on any MP4 sample video
101
+ 3. **Test streaming**: Try HLS or DASH samples
102
+ 4. **Toggle modes**: Switch between Enhanced and Native modes
103
+ 5. **Test features**: Try quality selection, PiP, fullscreen, etc.
104
+
105
+ ## Browser Compatibility
106
+
107
+ For best results, use modern browsers:
108
+ - **Chrome/Edge**: Full support for all features
109
+ - **Firefox**: Full support for all features
110
+ - **Safari**: Native HLS support, full feature set
111
+ - **Opera**: Full support for all features
112
+
113
+ ## Troubleshooting
114
+
115
+ ### Port Already in Use
116
+ If port 3000 is busy, you can change it:
117
+ - In `server.js` or `server.py`, change `PORT = 3000` to another number
118
+ - Use `http-server -p 8080` for a different port
119
+
120
+ ### CORS Errors
121
+ If you see CORS errors:
122
+ - Make sure you're using one of the server methods above
123
+ - Don't open the HTML file directly (file:// protocol)
124
+ - Check that the server is running properly
125
+
126
+ ### Videos Not Playing
127
+ - Check browser console for errors (F12)
128
+ - Ensure Enhanced Mode is ON for HLS/DASH content
129
+ - Try a different browser
130
+ - Check your internet connection for streaming samples
131
+
132
+ ## Development Tips
133
+
134
+ 1. **Keep the server running** while developing
135
+ 2. **Use browser DevTools** (F12) to debug
136
+ 3. **Check the Console** for error messages
137
+ 4. **Test in multiple browsers** for compatibility
138
+ 5. **Use Enhanced Mode** for full streaming support
139
+
140
+ ## Production Deployment
141
+
142
+ For production, you would typically:
143
+ - Serve files from a proper web server (Apache, Nginx, IIS)
144
+ - Use a CDN for video content
145
+ - Implement proper authentication if needed
146
+ - Set up HTTPS for security
147
+ - Configure proper CORS policies
148
+
149
+ ---
150
+
151
+ Need help? Check the main README.md or open an issue on the project repository.
@@ -0,0 +1,173 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Minimal Cast Sender</title>
7
+ <style>
8
+ body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; margin: 20px; }
9
+ .row { display: flex; gap: 8px; align-items: center; margin-bottom: 10px; flex-wrap: wrap; }
10
+ input[type="text"] { width: 480px; max-width: 100%; padding: 8px; }
11
+ button { padding: 8px 12px; cursor: pointer; }
12
+ .status { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; background: #111; color: #ddd; padding: 10px; white-space: pre-wrap; border-radius: 6px; min-height: 80px; }
13
+ .note { color: #666; font-size: 13px; }
14
+ .controls button { min-width: 100px; }
15
+ </style>
16
+ <script src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>
17
+ </head>
18
+ <body>
19
+ <h1>Minimal Cast Sender</h1>
20
+ <div class="row">
21
+ <label for="url">Media URL:</label>
22
+ <input id="url" type="text" value="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" />
23
+ <button id="cast">Cast</button>
24
+ <button id="stop">Stop Casting</button>
25
+ </div>
26
+ <div class="row controls">
27
+ <button id="playPause">Play/Pause</button>
28
+ <button id="back">-10s</button>
29
+ <button id="fwd">+10s</button>
30
+ <button id="mute">Mute/Unmute</button>
31
+ <button id="volDown">Vol -</button>
32
+ <button id="volUp">Vol +</button>
33
+ </div>
34
+ <div class="row"><span class="note">Tip: This page focuses on sender/receiver control. It does not play local video.</span></div>
35
+ <h3>Status</h3>
36
+ <div id="status" class="status"></div>
37
+
38
+ <script>
39
+ const statusEl = document.getElementById('status');
40
+ const log = (...args) => {
41
+ try { console.log('[CastMini]', ...args); } catch(_) {}
42
+ try { statusEl.textContent += args.map(a => (typeof a === 'string' ? a : JSON.stringify(a))).join(' ') + '\n'; } catch(_) {}
43
+ };
44
+
45
+ let remotePlayer = null;
46
+ let remoteController = null;
47
+
48
+ function inferContentType(url) {
49
+ const u = (url || '').toLowerCase();
50
+ if (u.includes('.m3u8')) return 'application/vnd.apple.mpegurl';
51
+ if (u.includes('.mpd')) return 'application/dash+xml';
52
+ if (u.includes('.webm')) return 'video/webm';
53
+ return 'video/mp4';
54
+ }
55
+
56
+ function attachRemoteEvents() {
57
+ if (!(window.cast && cast.framework) || !remoteController) return;
58
+ const RPET = cast.framework.RemotePlayerEventType;
59
+ const add = (t, lab) => remoteController.addEventListener(t, () => {
60
+ try {
61
+ log(`${lab}`, {
62
+ isPaused: remotePlayer?.isPaused,
63
+ playerState: remotePlayer?.playerState,
64
+ currentTime: remotePlayer?.currentTime,
65
+ duration: remotePlayer?.duration,
66
+ isMuted: remotePlayer?.isMuted,
67
+ volumeLevel: remotePlayer?.volumeLevel,
68
+ isConnected: remotePlayer?.isConnected,
69
+ });
70
+ } catch(_) {}
71
+ });
72
+ add(RPET.IS_PAUSED_CHANGED, 'EVT IS_PAUSED_CHANGED');
73
+ add(RPET.PLAYER_STATE_CHANGED, 'EVT PLAYER_STATE_CHANGED');
74
+ add(RPET.CURRENT_TIME_CHANGED, 'EVT CURRENT_TIME_CHANGED');
75
+ add(RPET.DURATION_CHANGED, 'EVT DURATION_CHANGED');
76
+ add(RPET.IS_MUTED_CHANGED, 'EVT IS_MUTED_CHANGED');
77
+ add(RPET.VOLUME_LEVEL_CHANGED, 'EVT VOLUME_LEVEL_CHANGED');
78
+ add(RPET.IS_CONNECTED_CHANGED, 'EVT IS_CONNECTED_CHANGED');
79
+ }
80
+
81
+ function ensureRemote() {
82
+ if (!remotePlayer) remotePlayer = new cast.framework.RemotePlayer();
83
+ if (!remoteController) remoteController = new cast.framework.RemotePlayerController(remotePlayer);
84
+ attachRemoteEvents();
85
+ }
86
+
87
+ async function doCast() {
88
+ try {
89
+ if (!(window.cast && cast.framework)) {
90
+ alert('Cast framework not ready yet. Try again in a moment.');
91
+ return;
92
+ }
93
+ const context = cast.framework.CastContext.getInstance();
94
+ context.setOptions({ receiverApplicationId: chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID });
95
+ log('Requesting session...');
96
+ await context.requestSession();
97
+ const session = context.getCurrentSession();
98
+ if (!session) { log('No session after request'); return; }
99
+ try {
100
+ const id = (typeof session.getSessionId === 'function') ? session.getSessionId() : (session.sessionId || null);
101
+ let device = null;
102
+ if (typeof session.getCastDevice === 'function') {
103
+ const d = session.getCastDevice();
104
+ device = (d && d.friendlyName) || null;
105
+ }
106
+ log('Session', { id, device });
107
+ } catch(_) {}
108
+
109
+ const url = document.getElementById('url').value.trim();
110
+ const ct = inferContentType(url);
111
+ log('Loading media', { url, contentType: ct });
112
+ const mediaInfo = new chrome.cast.media.MediaInfo(url, ct);
113
+ const req = new chrome.cast.media.LoadRequest(mediaInfo);
114
+ await session.loadMedia(req);
115
+ log('Media loaded on receiver');
116
+ ensureRemote();
117
+ } catch (err) {
118
+ log('Cast error', String(err));
119
+ }
120
+ }
121
+
122
+ function stopCasting() {
123
+ try {
124
+ const ctx = cast.framework.CastContext.getInstance();
125
+ const sess = ctx.getCurrentSession();
126
+ if (sess) { sess.endSession(true); log('Session ended'); }
127
+ } catch (e) { log('Stop error', String(e)); }
128
+ }
129
+
130
+ // Controls
131
+ function playPause() {
132
+ try { ensureRemote(); remoteController.playOrPause(); log('playOrPause()'); } catch (e) { log('playPause error', String(e)); }
133
+ }
134
+ function seekDelta(delta) {
135
+ try {
136
+ ensureRemote();
137
+ const d = remotePlayer.duration || 0;
138
+ const c = remotePlayer.currentTime || 0;
139
+ const n = Math.max(0, Math.min(d, c + delta));
140
+ remotePlayer.currentTime = n; remoteController.seek();
141
+ log('seek()', { to: n });
142
+ } catch (e) { log('seek error', String(e)); }
143
+ }
144
+ function muteToggle() {
145
+ try { ensureRemote(); remoteController.muteOrUnmute(); log('muteOrUnmute()'); } catch (e) { log('mute error', String(e)); }
146
+ }
147
+ function vol(delta) {
148
+ try { ensureRemote(); const cur = remotePlayer.volumeLevel || 0; const nx = Math.max(0, Math.min(1, cur + delta)); remoteController.setVolumeLevel(nx); log('setVolumeLevel()', { to: nx }); } catch (e) { log('volume error', String(e)); }
149
+ }
150
+
151
+ // Wire UI events
152
+ document.getElementById('cast').addEventListener('click', doCast);
153
+ document.getElementById('stop').addEventListener('click', stopCasting);
154
+ document.getElementById('playPause').addEventListener('click', playPause);
155
+ document.getElementById('back').addEventListener('click', () => seekDelta(-10));
156
+ document.getElementById('fwd').addEventListener('click', () => seekDelta(10));
157
+ document.getElementById('mute').addEventListener('click', muteToggle);
158
+ document.getElementById('volDown').addEventListener('click', () => vol(-0.1));
159
+ document.getElementById('volUp').addEventListener('click', () => vol(0.1));
160
+
161
+ // Cast API ready hook
162
+ window.__onGCastApiAvailable = function(isAvailable) {
163
+ if (isAvailable && window.cast && cast.framework) {
164
+ cast.framework.CastContext.getInstance().setOptions({ receiverApplicationId: chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID });
165
+ log('Cast framework ready');
166
+ } else {
167
+ log('Cast API not available yet');
168
+ }
169
+ };
170
+ </script>
171
+ </body>
172
+ </html>
173
+