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.
- package/.github/workflows/ci.yml +253 -0
- package/ANDROID_TV_IMPLEMENTATION.md +313 -0
- package/COMPLETION_STATUS.md +165 -0
- package/CONTRIBUTING.md +376 -0
- package/FINAL_STATUS_REPORT.md +170 -0
- package/FRAMEWORK_REVIEW.md +247 -0
- package/IMPROVEMENTS_SUMMARY.md +168 -0
- package/LICENSE +21 -0
- package/NATIVE_APP_INTEGRATION_GUIDE.md +903 -0
- package/PAYWALL_RENTAL_FLOW.md +499 -0
- package/PLATFORM_SETUP_GUIDE.md +1636 -0
- package/README.md +315 -0
- package/RUN_LOCALLY.md +151 -0
- package/apps/demo/cast-sender-min.html +173 -0
- package/apps/demo/custom-player.html +883 -0
- package/apps/demo/demo.html +990 -0
- package/apps/demo/enhanced-player.html +3556 -0
- package/apps/demo/index.html +159 -0
- package/apps/rental-api/.env.example +24 -0
- package/apps/rental-api/README.md +23 -0
- package/apps/rental-api/migrations/001_init.sql +35 -0
- package/apps/rental-api/migrations/002_videos.sql +10 -0
- package/apps/rental-api/migrations/003_add_gateway_subref.sql +4 -0
- package/apps/rental-api/migrations/004_update_gateways.sql +4 -0
- package/apps/rental-api/migrations/005_seed_demo_video.sql +5 -0
- package/apps/rental-api/package-lock.json +2045 -0
- package/apps/rental-api/package.json +33 -0
- package/apps/rental-api/scripts/run-migration.js +42 -0
- package/apps/rental-api/scripts/update-video-currency.js +21 -0
- package/apps/rental-api/scripts/update-video-price.js +19 -0
- package/apps/rental-api/src/config.ts +14 -0
- package/apps/rental-api/src/db.ts +10 -0
- package/apps/rental-api/src/routes/cashfree.ts +167 -0
- package/apps/rental-api/src/routes/pesapal.ts +92 -0
- package/apps/rental-api/src/routes/rentals.ts +242 -0
- package/apps/rental-api/src/routes/webhooks.ts +73 -0
- package/apps/rental-api/src/server.ts +41 -0
- package/apps/rental-api/src/services/entitlements.ts +45 -0
- package/apps/rental-api/src/services/payments.ts +22 -0
- package/apps/rental-api/tsconfig.json +17 -0
- package/check-urls.ps1 +74 -0
- package/comparison-report.md +181 -0
- package/docs/PAYWALL.md +95 -0
- package/docs/PLAYER_UI_VISIBILITY.md +431 -0
- package/docs/README.md +7 -0
- package/docs/SYSTEM_ARCHITECTURE.md +612 -0
- package/docs/VDOCIPHER_CLONE_REQUIREMENTS.md +403 -0
- package/examples/android/JavaSampleApp/MainActivity.java +641 -0
- package/examples/android/JavaSampleApp/activity_main.xml +226 -0
- package/examples/android/SampleApp/MainActivity.kt +430 -0
- package/examples/ios/SampleApp/ViewController.swift +337 -0
- package/examples/ios/SwiftUISampleApp/ContentView.swift +304 -0
- package/iOS_IMPLEMENTATION_OPTIONS.md +470 -0
- package/ios/UnifiedVideoPlayer/UnifiedVideoPlayer.podspec +33 -0
- package/jest.config.js +33 -0
- package/jitpack.yml +5 -0
- package/lerna.json +35 -0
- package/package.json +69 -0
- package/packages/PLATFORM_STATUS.md +163 -0
- package/packages/android/build.gradle +135 -0
- package/packages/android/src/main/AndroidManifest.xml +36 -0
- package/packages/android/src/main/java/com/unifiedvideo/player/PlayerConfiguration.java +221 -0
- package/packages/android/src/main/java/com/unifiedvideo/player/UnifiedVideoPlayer.java +1037 -0
- package/packages/android/src/main/java/com/unifiedvideo/player/UnifiedVideoPlayer.kt +707 -0
- package/packages/android/src/main/java/com/unifiedvideo/player/analytics/AnalyticsProvider.java +9 -0
- package/packages/android/src/main/java/com/unifiedvideo/player/cast/CastManager.java +141 -0
- package/packages/android/src/main/java/com/unifiedvideo/player/cast/CastOptionsProvider.java +29 -0
- package/packages/android/src/main/java/com/unifiedvideo/player/overlay/WatermarkOverlayView.java +88 -0
- package/packages/android/src/main/java/com/unifiedvideo/player/pip/PipActionReceiver.java +33 -0
- package/packages/android/src/main/java/com/unifiedvideo/player/services/PlaybackService.java +110 -0
- package/packages/android/src/main/java/com/unifiedvideo/player/services/PlayerHolder.java +19 -0
- package/packages/core/package.json +34 -0
- package/packages/core/src/BasePlayer.ts +250 -0
- package/packages/core/src/VideoPlayer.ts +237 -0
- package/packages/core/src/VideoPlayerFactory.ts +145 -0
- package/packages/core/src/index.ts +20 -0
- package/packages/core/src/interfaces/IVideoPlayer.ts +184 -0
- package/packages/core/src/interfaces.ts +240 -0
- package/packages/core/src/utils/EventEmitter.ts +66 -0
- package/packages/core/src/utils/PlatformDetector.ts +300 -0
- package/packages/core/tsconfig.json +20 -0
- package/packages/enact/package.json +51 -0
- package/packages/enact/src/VideoPlayer.js +365 -0
- package/packages/enact/src/adapters/TizenAdapter.js +354 -0
- package/packages/enact/src/index.js +82 -0
- package/packages/ios/BUILD_INSTRUCTIONS.md +108 -0
- package/packages/ios/FIX_EMBED_ISSUE.md +142 -0
- package/packages/ios/GETTING_STARTED.md +100 -0
- package/packages/ios/Package.swift +35 -0
- package/packages/ios/README.md +84 -0
- package/packages/ios/Sources/UnifiedVideoPlayer/Analytics/AnalyticsEmitter.swift +26 -0
- package/packages/ios/Sources/UnifiedVideoPlayer/DRM/FairPlayDRMManager.swift +102 -0
- package/packages/ios/Sources/UnifiedVideoPlayer/Info.plist +24 -0
- package/packages/ios/Sources/UnifiedVideoPlayer/Remote/RemoteCommandCenter.swift +109 -0
- package/packages/ios/Sources/UnifiedVideoPlayer/UnifiedVideoPlayer.swift +811 -0
- package/packages/ios/Sources/UnifiedVideoPlayer/UnifiedVideoPlayerView.swift +640 -0
- package/packages/ios/Sources/UnifiedVideoPlayer/Utilities/Color+Hex.swift +36 -0
- package/packages/ios/UnifiedVideoPlayer.podspec +27 -0
- package/packages/ios/UnifiedVideoPlayer.xcodeproj/project.pbxproj +385 -0
- package/packages/ios/build_framework.sh +55 -0
- package/packages/react-native/android/src/main/java/com/unifiedvideo/UnifiedVideoPlayerModule.kt +482 -0
- package/packages/react-native/ios/UnifiedVideoPlayer.swift +436 -0
- package/packages/react-native/package.json +51 -0
- package/packages/react-native/src/ReactNativePlayer.tsx +423 -0
- package/packages/react-native/src/VideoPlayer.tsx +224 -0
- package/packages/react-native/src/index.ts +28 -0
- package/packages/react-native/src/utils/EventEmitter.ts +66 -0
- package/packages/react-native/tsconfig.json +31 -0
- package/packages/roku/components/UnifiedVideoPlayer.brs +400 -0
- package/packages/roku/package.json +44 -0
- package/packages/roku/source/VideoPlayer.brs +231 -0
- package/packages/roku/source/main.brs +28 -0
- package/packages/web/GETTING_STARTED.md +292 -0
- package/packages/web/jest.config.js +28 -0
- package/packages/web/jest.setup.ts +110 -0
- package/packages/web/package.json +50 -0
- package/packages/web/src/SecureVideoPlayer.ts +1164 -0
- package/packages/web/src/WebPlayer.ts +3110 -0
- package/packages/web/src/__tests__/WebPlayer.test.ts +314 -0
- package/packages/web/src/index.ts +14 -0
- package/packages/web/src/paywall/PaywallController.ts +215 -0
- package/packages/web/src/react/WebPlayerView.tsx +177 -0
- package/packages/web/tsconfig.json +23 -0
- package/packages/web/webpack.config.js +45 -0
- package/server.js +131 -0
- package/server.py +84 -0
- package/test-urls.ps1 +97 -0
- package/test-video-urls.ps1 +87 -0
- 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
|
+
|