@quicktvui/web-renderer 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 (56) hide show
  1. package/package.json +24 -0
  2. package/src/adapters/es3-video-player.js +828 -0
  3. package/src/components/Modal.js +119 -0
  4. package/src/components/QtAnimationView.js +678 -0
  5. package/src/components/QtBaseComponent.js +165 -0
  6. package/src/components/QtFastListView.js +1920 -0
  7. package/src/components/QtFlexView.js +799 -0
  8. package/src/components/QtImage.js +203 -0
  9. package/src/components/QtItemFrame.js +239 -0
  10. package/src/components/QtItemStoreView.js +93 -0
  11. package/src/components/QtItemView.js +125 -0
  12. package/src/components/QtListView.js +331 -0
  13. package/src/components/QtLoadingView.js +55 -0
  14. package/src/components/QtPageRootView.js +19 -0
  15. package/src/components/QtPlayMark.js +168 -0
  16. package/src/components/QtProgressBar.js +199 -0
  17. package/src/components/QtQRCode.js +78 -0
  18. package/src/components/QtReplaceChild.js +149 -0
  19. package/src/components/QtRippleView.js +166 -0
  20. package/src/components/QtSeekBar.js +409 -0
  21. package/src/components/QtText.js +679 -0
  22. package/src/components/QtTransitionImage.js +170 -0
  23. package/src/components/QtView.js +706 -0
  24. package/src/components/QtWebView.js +613 -0
  25. package/src/components/TabsView.js +420 -0
  26. package/src/components/ViewPager.js +206 -0
  27. package/src/components/index.js +24 -0
  28. package/src/components/plugins/TextV2Component.js +70 -0
  29. package/src/components/plugins/index.js +7 -0
  30. package/src/core/SceneBuilder.js +58 -0
  31. package/src/core/TVFocusManager.js +2014 -0
  32. package/src/core/asyncLocalStorage.js +175 -0
  33. package/src/core/autoProxy.js +165 -0
  34. package/src/core/componentRegistry.js +84 -0
  35. package/src/core/constants.js +6 -0
  36. package/src/core/index.js +8 -0
  37. package/src/core/moduleUtils.js +36 -0
  38. package/src/core/patches.js +958 -0
  39. package/src/core/templateBinding.js +666 -0
  40. package/src/index.js +246 -0
  41. package/src/modules/AndroidDevelopModule.js +101 -0
  42. package/src/modules/AndroidDeviceModule.js +341 -0
  43. package/src/modules/AndroidNetworkModule.js +178 -0
  44. package/src/modules/AndroidSharedPreferencesModule.js +100 -0
  45. package/src/modules/ESDeviceInfoModule.js +450 -0
  46. package/src/modules/ESGroupDataModule.js +195 -0
  47. package/src/modules/ESIJKAudioPlayerModule.js +477 -0
  48. package/src/modules/ESLocalStorageModule.js +100 -0
  49. package/src/modules/ESLogModule.js +65 -0
  50. package/src/modules/ESModule.js +106 -0
  51. package/src/modules/ESNetworkSpeedModule.js +117 -0
  52. package/src/modules/ESToastModule.js +172 -0
  53. package/src/modules/EsNativeModule.js +117 -0
  54. package/src/modules/FastListModule.js +101 -0
  55. package/src/modules/FocusModule.js +145 -0
  56. package/src/modules/RuntimeDeviceModule.js +176 -0
@@ -0,0 +1,828 @@
1
+ // IJKPlayerComponent - Web adapter using HTML5 Video API
2
+ // Implements video player for web platform
3
+
4
+ import { HippyWebView } from '@hippy/web-renderer'
5
+ import { registerComponent } from '../core/componentRegistry'
6
+
7
+ // Player states (matching native)
8
+ const ESPlayerState = {
9
+ ES_PLAYER_STATE_IDLE: 0,
10
+ ES_PLAYER_STATE_PREPARING: 1,
11
+ ES_PLAYER_STATE_PREPARED: 2,
12
+ ES_PLAYER_STATE_BUFFER_START: 3,
13
+ ES_PLAYER_STATE_BUFFER_END: 4,
14
+ ES_PLAYER_STATE_PLAYING: 5,
15
+ ES_PLAYER_STATE_SEEK_START: 6,
16
+ ES_PLAYER_STATE_SEEK_COMPLETED: 7,
17
+ ES_PLAYER_STATE_PAUSED: 8,
18
+ ES_PLAYER_STATE_RESUMED: 9,
19
+ ES_PLAYER_STATE_BEFORE_STOP: 10,
20
+ ES_PLAYER_STATE_STOP: 11,
21
+ ES_PLAYER_STATE_PLAYBACK_COMPLETED: 12,
22
+ ES_PLAYER_STATE_ERROR: 13,
23
+ ES_PLAYER_STATE_VIDEO_SIZE_CHANGED: 14,
24
+ ES_PLAYER_STATE_PLAYER_VIEW_CHANGED: 15,
25
+ ES_PLAYER_STATE_AD_START: 16,
26
+ ES_PLAYER_STATE_AD_END: 17,
27
+ ES_PLAYER_STATE_AD_SKIP: 18,
28
+ ES_PLAYER_STATE_AD_PAUSED: 19,
29
+ ES_PLAYER_STATE_AUTHORIZED: 20,
30
+ ES_PLAYER_STATE_SET_PLAY_RATE_SUCCESS: 21,
31
+ ES_PLAYER_STATE_SET_PLAY_RATE_ERROR: 22,
32
+ ES_PLAYER_STATE_AD_RESUMED: 23,
33
+ ES_PLAYER_STATE_AD_LOADED: 24,
34
+ ES_PLAYER_STATE_AD_TIME: 25,
35
+ ES_PLAYER_STATE_PLAYER_INITIALIZED: 26,
36
+ ES_PLAYER_STATE_TIMED_TEXT_CHANGED: 27,
37
+ ES_PLAYER_STATE_PLAYER_CLICKABLE: 28,
38
+ ES_PLAYER_STATE_INITIALIZE_SUCCESS: 29,
39
+ ES_PLAYER_STATE_INITIALIZE_ERROR: 30,
40
+ }
41
+
42
+ export class IJKPlayerComponent extends HippyWebView {
43
+ constructor(context, id, pId) {
44
+ super(context, id, pId)
45
+ this.tagName = 'IJKPlayerComponent'
46
+ this.dom = document.createElement('video')
47
+
48
+ // Video element setup
49
+ this.dom.style.cssText = `
50
+ width: 100%;
51
+ height: 100%;
52
+ object-fit: contain;
53
+ background: #000;
54
+ `
55
+ this.dom.playsInline = true
56
+
57
+ // State
58
+ this._playerState = ESPlayerState.ES_PLAYER_STATE_IDLE
59
+ this._url = ''
60
+ this._volume = 1
61
+ this._muted = false
62
+ this._loop = false
63
+ this._playbackRate = 1
64
+ this._aspectRatio = 0
65
+ this._positionListeners = []
66
+ this._destroyed = false
67
+
68
+ // Callbacks
69
+ this._progressCallback = null
70
+ this._durationCallback = null
71
+ this._bufferPercentCallback = null
72
+
73
+ // Additional state
74
+ this._isSeeking = false
75
+ this._isBuffering = false
76
+ this._playerType = 1
77
+ this._decode = 0
78
+
79
+ // Register component
80
+ registerComponent(id, this)
81
+ this._bindVideoEvents()
82
+ }
83
+
84
+ _bindVideoEvents() {
85
+ const video = this.dom
86
+
87
+ // Play event
88
+ video.addEventListener('play', () => {
89
+ this._playerState = ESPlayerState.ES_PLAYER_STATE_PLAYING
90
+ this._dispatchPlayerStatusChanged()
91
+ })
92
+
93
+ // Pause event
94
+ video.addEventListener('pause', () => {
95
+ this._playerState = ESPlayerState.ES_PLAYER_STATE_PAUSED
96
+ this._dispatchPlayerStatusChanged()
97
+ })
98
+
99
+ // Ended event (playback completed)
100
+ video.addEventListener('ended', () => {
101
+ this._playerState = ESPlayerState.ES_PLAYER_STATE_COMPLETED
102
+ this._dispatchPlayerStatusChanged()
103
+ this._dispatchEvent('onPlayerCompleted')
104
+ })
105
+
106
+ // Error event
107
+ video.addEventListener('error', (e) => {
108
+ this._playerState = ESPlayerState.ES_PLAYER_STATE_ERROR
109
+ this._dispatchEvent('onPlayerError', {
110
+ errorCode: e.target.error?.code || -1,
111
+ errorMessage: e.target.error?.message || 'Video error',
112
+ })
113
+ this._dispatchPlayerStatusChanged()
114
+ })
115
+
116
+ // Loaded metadata - video size info available
117
+ video.addEventListener('loadedmetadata', () => {
118
+ this._playerState = ESPlayerState.ES_PLAYER_STATE_PREPARED
119
+ this._dispatchPlayerStatusChanged()
120
+ this._dispatchAspectRatioChanged()
121
+ this._dispatchEvent('onPlayerPrepared')
122
+ // Video size changed
123
+ this._dispatchEvent('onPlayerStatusChanged', {
124
+ playerStatus: ESPlayerState.ES_PLAYER_STATE_VIDEO_SIZE_CHANGED,
125
+ playerState: ESPlayerState.ES_PLAYER_STATE_VIDEO_SIZE_CHANGED,
126
+ playerWidth: video.videoWidth || 0,
127
+ playerHeight: video.videoHeight || 0,
128
+ })
129
+ })
130
+
131
+ // Can play
132
+ video.addEventListener('canplay', () => {
133
+ if (this._playerState < ESPlayerState.ES_PLAYER_STATE_PREPARED) {
134
+ this._playerState = ESPlayerState.ES_PLAYER_STATE_PREPARED
135
+ this._dispatchPlayerStatusChanged()
136
+ }
137
+ })
138
+
139
+ // Time update for position
140
+ video.addEventListener('timeupdate', () => {
141
+ this._dispatchPositionChanged()
142
+ })
143
+
144
+ // Volume change
145
+ video.addEventListener('volumechange', () => {
146
+ const volume = video.volume
147
+ this._dispatchEvent('onPlayerVolumeChanged', {
148
+ leftVolume: volume,
149
+ rightVolume: volume,
150
+ })
151
+ this._dispatchEvent('onPlayerLeftVolumeChanged', volume)
152
+ this._dispatchEvent('onPlayerRightVolumeChanged', volume)
153
+ })
154
+
155
+ // Rate change
156
+ video.addEventListener('ratechange', () => {
157
+ this._dispatchEvent('onPlayerPlayRateChanged', {
158
+ playRate: video.playbackRate.toString(),
159
+ })
160
+ this._dispatchEvent('onPlayerStatusChanged', {
161
+ playerStatus: ESPlayerState.ES_PLAYER_STATE_SET_PLAY_RATE_SUCCESS,
162
+ playerState: ESPlayerState.ES_PLAYER_STATE_SET_PLAY_RATE_SUCCESS,
163
+ })
164
+ })
165
+
166
+ // Waiting / Buffering start
167
+ video.addEventListener('waiting', () => {
168
+ this._dispatchEvent('onPlayerBufferStart')
169
+ this._dispatchEvent('onPlayerStatusChanged', {
170
+ playerStatus: ESPlayerState.ES_PLAYER_STATE_BUFFER_START,
171
+ playerState: ESPlayerState.ES_PLAYER_STATE_BUFFER_START,
172
+ })
173
+ })
174
+
175
+ // Can play through / Buffering end
176
+ video.addEventListener('canplaythrough', () => {
177
+ this._dispatchEvent('onPlayerBufferEnd')
178
+ this._dispatchEvent('onPlayerStatusChanged', {
179
+ playerStatus: ESPlayerState.ES_PLAYER_STATE_BUFFER_END,
180
+ playerState: ESPlayerState.ES_PLAYER_STATE_BUFFER_END,
181
+ })
182
+ })
183
+
184
+ // Playing (after buffering)
185
+ video.addEventListener('playing', () => {
186
+ this._dispatchEvent('onPlayerResumed')
187
+ this._dispatchEvent('onPlayerStatusChanged', {
188
+ playerStatus: ESPlayerState.ES_PLAYER_STATE_RESUMED,
189
+ playerState: ESPlayerState.ES_PLAYER_STATE_RESUMED,
190
+ })
191
+ })
192
+
193
+ // Seeking start
194
+ video.addEventListener('seeking', () => {
195
+ this._dispatchEvent('onPlayerSeekStart')
196
+ this._dispatchEvent('onPlayerStatusChanged', {
197
+ playerStatus: ESPlayerState.ES_PLAYER_STATE_SEEK_START,
198
+ playerState: ESPlayerState.ES_PLAYER_STATE_SEEK_START,
199
+ })
200
+ })
201
+
202
+ // Seeking completed
203
+ video.addEventListener('seeked', () => {
204
+ this._dispatchEvent('onPlayerSeekCompleted')
205
+ this._dispatchEvent('onPlayerStatusChanged', {
206
+ playerStatus: ESPlayerState.ES_PLAYER_STATE_SEEK_COMPLETED,
207
+ playerState: ESPlayerState.ES_PLAYER_STATE_SEEK_COMPLETED,
208
+ })
209
+ })
210
+
211
+ // Duration change
212
+ video.addEventListener('durationchange', () => {
213
+ const duration = Math.floor(video.duration * 1000)
214
+ // Duration is handled by _durationCallback (set via setDurationCallback)
215
+ // No need to dispatch event - the callback is the correct mechanism
216
+ if (this._durationCallback) {
217
+ try {
218
+ this._durationCallback(duration)
219
+ } catch (e) {
220
+ console.error('[IJKPlayerComponent] Duration callback error:', e)
221
+ }
222
+ }
223
+ })
224
+
225
+ // Progress (buffering progress)
226
+ video.addEventListener('progress', () => {
227
+ if (video.buffered.length > 0) {
228
+ const bufferedEnd = video.buffered.end(video.buffered.length - 1)
229
+ const duration = video.duration
230
+ if (duration > 0) {
231
+ const bufferPercent = (bufferedEnd / duration) * 100
232
+ // Buffer percent is handled by _bufferPercentCallback (set via setBufferPercentCallback)
233
+ // No need to dispatch event - the callback is the correct mechanism
234
+ if (this._bufferPercentCallback) {
235
+ try {
236
+ this._bufferPercentCallback(bufferPercent)
237
+ } catch (e) {
238
+ console.error('[IJKPlayerComponent] Buffer percent callback error:', e)
239
+ }
240
+ }
241
+ }
242
+ }
243
+ })
244
+
245
+ // Load start
246
+ video.addEventListener('loadstart', () => {
247
+ this._playerState = ESPlayerState.ES_PLAYER_STATE_PREPARING
248
+ this._dispatchEvent('onPlayerPreparing')
249
+ this._dispatchPlayerStatusChanged()
250
+ })
251
+ }
252
+
253
+ _dispatchPlayerStatusChanged() {
254
+ if (!this.dom) return
255
+ this._dispatchEvent('onPlayerStatusChanged', {
256
+ playerStatus: this._playerState,
257
+ playerState: this._playerState,
258
+ playerWidth: this.dom.videoWidth || 0,
259
+ playerHeight: this.dom.videoHeight || 0,
260
+ })
261
+ }
262
+
263
+ _dispatchPositionChanged() {
264
+ if (!this.dom) return
265
+ const position = Math.floor(this.dom.currentTime * 1000)
266
+ // Progress is handled by _progressCallback (set via setProgressCallback)
267
+ // No need to dispatch event - the callback is the correct mechanism
268
+ if (this._progressCallback) {
269
+ try {
270
+ this._progressCallback(position)
271
+ } catch (e) {
272
+ console.error('[IJKPlayerComponent] Progress callback error:', e)
273
+ }
274
+ }
275
+ }
276
+
277
+ _dispatchAspectRatioChanged() {
278
+ if (!this.dom) return
279
+ const width = this.dom.videoWidth
280
+ const height = this.dom.videoHeight
281
+ if (width && height) {
282
+ // Common aspect ratios
283
+ const aspectRatioList = [0, 1, 2, 3, 4] // Fit, 16:9, 4:3, 1:1, etc.
284
+ // Dispatch aspect ratio list changed event
285
+ this._dispatchEvent('onPlayerAspectRatioListChanged', aspectRatioList)
286
+ this._dispatchEvent('onAllAspectRatioChanged', {
287
+ aspectRatioList: aspectRatioList,
288
+ })
289
+ // Dispatch aspect ratio changed event
290
+ this._dispatchEvent('onPlayerAspectRatioChanged', {
291
+ aspectRatio: 0, // Default fit
292
+ })
293
+ }
294
+ }
295
+
296
+ _dispatchEvent(eventName, params) {
297
+ if (this._destroyed) return
298
+ if (!this.events) return
299
+ if (this.events[eventName]) {
300
+ this.dispatchEvent(eventName, params)
301
+ return
302
+ }
303
+ const target = eventName.toLowerCase()
304
+ const keys = Object.keys(this.events)
305
+ for (let i = 0; i < keys.length; i++) {
306
+ const key = keys[i]
307
+ if (key && key.toLowerCase() === target && this.events[key]) {
308
+ this.dispatchEvent(key, params)
309
+ return
310
+ }
311
+ }
312
+ }
313
+
314
+ // Handle attributes
315
+ updateProperty(key, value) {
316
+ switch (key) {
317
+ case 'src':
318
+ this._url = value
319
+ break
320
+ case 'muted':
321
+ this.dom.muted = value
322
+ break
323
+ case 'loop':
324
+ this.dom.loop = value
325
+ this._loop = value
326
+ break
327
+ case 'volume':
328
+ this.dom.volume = Math.max(0, Math.min(1, value))
329
+ break
330
+ case 'playbackRate':
331
+ this.dom.playbackRate = value
332
+ break
333
+ default:
334
+ super.updateProperty(key, value)
335
+ }
336
+ }
337
+
338
+ // Initialize
339
+ init() {
340
+ this._playerState = ESPlayerState.ES_PLAYER_STATE_INITIALIZED
341
+ this._dispatchPlayerStatusChanged()
342
+ }
343
+
344
+ // Play video - params are spread by patches.js
345
+ play([
346
+ url,
347
+ aspectRatio,
348
+ leftVolume,
349
+ rightVolume,
350
+ playerOptionArray,
351
+ playerType,
352
+ playerMediaCodec,
353
+ options,
354
+ metadata,
355
+ ]) {
356
+ if (url) {
357
+ this._url = url
358
+ this.dom.src = url
359
+
360
+ // Set volume
361
+ const volume = leftVolume !== undefined ? parseFloat(leftVolume) : 1
362
+ this.dom.volume = Math.max(0, Math.min(1, volume))
363
+
364
+ // Set loop
365
+ if (options?.loop !== undefined) {
366
+ this.dom.loop = options.loop
367
+ }
368
+
369
+ // Set aspect ratio (object-fit)
370
+ this._setAspectRatio(aspectRatio || 0)
371
+
372
+ this._playerState = ESPlayerState.ES_PLAYER_STATE_PREPARING
373
+ this._dispatchPlayerStatusChanged()
374
+
375
+ this.dom.load()
376
+ }
377
+ }
378
+
379
+ // Start playback
380
+ start(progress) {
381
+ if (progress && progress > 0) {
382
+ this.dom.currentTime = progress / 1000
383
+ }
384
+ this.dom.play().catch((e) => {
385
+ console.error('[IJKPlayerComponent] Play error:', e)
386
+ })
387
+ }
388
+
389
+ // Pause
390
+ pause() {
391
+ this.dom.pause()
392
+ }
393
+
394
+ // Resume
395
+ resume() {
396
+ this.dom.play().catch(() => {})
397
+ }
398
+
399
+ // Seek to position (ms)
400
+ seekTo(progress) {
401
+ if (progress !== undefined) {
402
+ this.dom.currentTime = progress / 1000
403
+ }
404
+ }
405
+
406
+ // Stop
407
+ stop() {
408
+ this.dom.pause()
409
+ this.dom.currentTime = 0
410
+ this._playerState = ESPlayerState.ES_PLAYER_STATE_STOPPED
411
+ this._dispatchPlayerStatusChanged()
412
+ }
413
+
414
+ // Release
415
+ release() {
416
+ this._destroyed = true
417
+ this.dom.pause()
418
+ this.dom.src = ''
419
+ this._playerState = ESPlayerState.ES_PLAYER_STATE_RELEASED
420
+ }
421
+
422
+ // Get current position (ms)
423
+ getCurrentPosition() {
424
+ return Math.floor(this.dom.currentTime * 1000)
425
+ }
426
+
427
+ // Get duration (ms)
428
+ getDuration() {
429
+ return Math.floor(this.dom.duration * 1000) || 0
430
+ }
431
+
432
+ // Set volume
433
+ setVolume(volume) {
434
+ this.dom.volume = Math.max(0, Math.min(1, parseFloat(volume) || 1))
435
+ }
436
+
437
+ setLeftVolume(volume) {
438
+ this.dom.volume = Math.max(0, Math.min(1, parseFloat(volume) || 1))
439
+ }
440
+
441
+ setRightVolume(volume) {
442
+ // HTML5 video doesn't support stereo volume, same as left
443
+ this.dom.volume = Math.max(0, Math.min(1, parseFloat(volume) || 1))
444
+ }
445
+
446
+ setLeftRightVolume(leftVolume, rightVolume) {
447
+ // Average of left and right
448
+ const avgVolume = ((parseFloat(leftVolume) || 1) + (parseFloat(rightVolume) || 1)) / 2
449
+ this.dom.volume = Math.max(0, Math.min(1, avgVolume))
450
+ }
451
+
452
+ getLeftVolume() {
453
+ return this.dom.volume
454
+ }
455
+
456
+ getRightVolume() {
457
+ return this.dom.volume
458
+ }
459
+
460
+ // Set play rate
461
+ setPlayRate(rate) {
462
+ this.dom.playbackRate = parseFloat(rate) || 1
463
+ }
464
+
465
+ // Set aspect ratio
466
+ setAspectRatio(ratio) {
467
+ this._setAspectRatio(ratio)
468
+ }
469
+
470
+ _setAspectRatio(ratio) {
471
+ // 0: fit, 1: 16:9, 2: 4:3, 3: 1:1, 4: fill
472
+ const objectFitMap = {
473
+ 0: 'contain', // fit
474
+ 1: 'contain', // 16:9
475
+ 2: 'contain', // 4:3
476
+ 3: 'contain', // 1:1
477
+ 4: 'cover', // fill
478
+ }
479
+ this.dom.style.objectFit = objectFitMap[ratio] || 'contain'
480
+ this._aspectRatio = ratio
481
+ }
482
+
483
+ // Set player size
484
+ setPlayerSize(width, height) {
485
+ if (width) this.dom.style.width = typeof width === 'number' ? `${width}px` : width
486
+ if (height) this.dom.style.height = typeof height === 'number' ? `${height}px` : height
487
+ }
488
+
489
+ setSize(width, height) {
490
+ this.setPlayerSize(width, height)
491
+ }
492
+
493
+ // Update layout
494
+ updateLayout(width, height, x, y) {
495
+ if (width) this.dom.style.width = typeof width === 'number' ? `${width}px` : width
496
+ if (height) this.dom.style.height = typeof height === 'number' ? `${height}px` : height
497
+ if (x !== undefined) this.dom.style.left = `${x}px`
498
+ if (y !== undefined) this.dom.style.top = `${y}px`
499
+ }
500
+
501
+ // Is stopped
502
+ isStopped() {
503
+ return this._playerState === ESPlayerState.ES_PLAYER_STATE_STOPPED
504
+ }
505
+
506
+ // Start position listener
507
+ startPositionListener() {
508
+ // Position updates are handled by timeupdate event
509
+ }
510
+
511
+ // Stop position listener
512
+ stopPositionListener() {
513
+ // Position updates are handled by timeupdate event
514
+ }
515
+
516
+ // Get track info
517
+ getTrackInfo(sysType) {
518
+ const tracks = this.dom.textTracks || []
519
+ const trackInfo = []
520
+ for (let i = 0; i < tracks.length; i++) {
521
+ trackInfo.push({
522
+ index: i,
523
+ type: tracks[i].kind,
524
+ label: tracks[i].label,
525
+ language: tracks[i].language,
526
+ })
527
+ }
528
+ return trackInfo
529
+ }
530
+
531
+ // Select track
532
+ selectTrack(index) {
533
+ const tracks = this.dom.textTracks
534
+ if (tracks && tracks[index]) {
535
+ tracks[index].mode = 'showing'
536
+ }
537
+ }
538
+
539
+ // Deselect track
540
+ deselectTrack(index) {
541
+ const tracks = this.dom.textTracks
542
+ if (tracks && tracks[index]) {
543
+ tracks[index].mode = 'disabled'
544
+ }
545
+ }
546
+
547
+ // Get media meta
548
+ getMediaMeta() {
549
+ return {
550
+ duration: this.dom.duration * 1000,
551
+ width: this.dom.videoWidth,
552
+ height: this.dom.videoHeight,
553
+ }
554
+ }
555
+
556
+ // Set definition (for HLS)
557
+ setDefinition(definition) {
558
+ // Not fully supported in HTML5, would need custom HLS.js
559
+ }
560
+
561
+ setM3U8Definition(index) {
562
+ // Not fully supported in HTML5
563
+ }
564
+
565
+ getM3U8DefinitionInfo() {
566
+ return []
567
+ }
568
+
569
+ // Placeholder methods for stats
570
+ getBitRate() {
571
+ return 0
572
+ }
573
+
574
+ getTcpSpeed() {
575
+ return 0
576
+ }
577
+
578
+ getVideoDecoder() {
579
+ return 'html5'
580
+ }
581
+
582
+ getCdnInfo() {
583
+ return {}
584
+ }
585
+
586
+ // Set options (no-op for web)
587
+ setUsingHardwareDecoder(value) {}
588
+ setPlayerType(type) {}
589
+ setOptionLong(category, name, value) {}
590
+ setOptionString(category, name, value) {}
591
+ setCacheInfo(cacheInfo) {}
592
+ setDecode(decode) {}
593
+
594
+ // Layout methods
595
+ requestLayout() {
596
+ // Force reflow
597
+ this.dom.offsetHeight
598
+ }
599
+
600
+ invalidate() {
601
+ this.requestLayout()
602
+ }
603
+
604
+ clickPlayerView() {}
605
+ changeToFullScreen(params) {}
606
+ requestPlayerViewLayout() {}
607
+ requestCustomSizeLayout(params) {}
608
+ requestCustomLayout() {}
609
+ setPlayerDimension(params) {}
610
+ setDefaultPlayerWidth(params) {}
611
+ setDefaultPlayerHeight(params) {}
612
+ setFullPlayerWidth(params) {}
613
+ setFullPlayerHeight(params) {}
614
+
615
+ // Component info
616
+ getEsInfo() {
617
+ return {
618
+ platform: 'web',
619
+ playerType: 'html5',
620
+ }
621
+ }
622
+
623
+ // Get component info - triggers onPlayerComponentInfo event
624
+ getComponentInfo() {
625
+ const info = {
626
+ isSupportDynamicallyLoadedSo: false,
627
+ isSupportAsyncInit: true,
628
+ platform: 'web',
629
+ playerType: 'html5',
630
+ }
631
+ this._dispatchEvent('onPlayerComponentInfo', info)
632
+ return info
633
+ }
634
+
635
+ // Init component - called after component info check
636
+ initComponent() {
637
+ // Dispatch player initialized event
638
+ this._dispatchEvent('onPlayerStatusChanged', {
639
+ playerStatus: ESPlayerState.ES_PLAYER_STATE_PLAYER_INITIALIZED,
640
+ playerState: ESPlayerState.ES_PLAYER_STATE_PLAYER_INITIALIZED,
641
+ })
642
+ // Dispatch initialize success event
643
+ this._dispatchEvent('onPlayerStatusChanged', {
644
+ playerStatus: ESPlayerState.ES_PLAYER_STATE_INITIALIZE_SUCCESS,
645
+ playerState: ESPlayerState.ES_PLAYER_STATE_INITIALIZE_SUCCESS,
646
+ })
647
+ }
648
+
649
+ // Set progress callback
650
+ setProgressCallback(callback) {
651
+ this._progressCallback = callback
652
+ }
653
+
654
+ // Set duration callback
655
+ setDurationCallback(callback) {
656
+ this._durationCallback = callback
657
+ }
658
+
659
+ // Set buffer percent callback
660
+ setBufferPercentCallback(callback) {
661
+ this._bufferPercentCallback = callback
662
+ }
663
+
664
+ // Get player state
665
+ getPlayerState() {
666
+ return this._playerState
667
+ }
668
+
669
+ // Get video width
670
+ getVideoWidth() {
671
+ return this.dom.videoWidth || 0
672
+ }
673
+
674
+ // Get video height
675
+ getVideoHeight() {
676
+ return this.dom.videoHeight || 0
677
+ }
678
+
679
+ // Check if playing
680
+ isPlaying() {
681
+ return !this.dom.paused && !this.dom.ended
682
+ }
683
+
684
+ // Get current play rate
685
+ getPlayRate() {
686
+ return this.dom.playbackRate || 1
687
+ }
688
+
689
+ // Get aspect ratio
690
+ getAspectRatio() {
691
+ return this._aspectRatio
692
+ }
693
+
694
+ // Get player type
695
+ getPlayerType() {
696
+ return this._playerType
697
+ }
698
+
699
+ // Set player type
700
+ setPlayerType(type) {
701
+ this._playerType = type
702
+ }
703
+
704
+ // Get decode mode
705
+ getDecode() {
706
+ return this._decode
707
+ }
708
+
709
+ // Set decode mode
710
+ setDecode(decode) {
711
+ this._decode = decode
712
+ }
713
+
714
+ // Mute
715
+ setMute(muted) {
716
+ this.dom.muted = muted
717
+ this._muted = muted
718
+ }
719
+
720
+ // Is muted
721
+ isMuted() {
722
+ return this.dom.muted
723
+ }
724
+
725
+ // Get video URL
726
+ getUrl() {
727
+ return this._url
728
+ }
729
+
730
+ // Set looping
731
+ setLooping(loop) {
732
+ this.dom.loop = loop
733
+ this._loop = loop
734
+ }
735
+
736
+ // Is looping
737
+ isLooping() {
738
+ return this.dom.loop
739
+ }
740
+
741
+ // Get available aspect ratios
742
+ getAspectRatioList() {
743
+ return [0, 1, 2, 3, 4] // Fit, 16:9, 4:3, 1:1, fill
744
+ }
745
+
746
+ // Get available play rates
747
+ getPlayRateList() {
748
+ return [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.5]
749
+ }
750
+
751
+ // Get available decodes
752
+ getDecodeList() {
753
+ return [0, 1] // Hardware, Software
754
+ }
755
+
756
+ // Dispatch player info
757
+ dispatchPlayerInfo(info) {
758
+ this._dispatchEvent('onPlayerInfo', info)
759
+ }
760
+
761
+ // Additional compatibility methods
762
+ setCache(cacheInfo) {
763
+ // Not supported in HTML5, placeholder for API compatibility
764
+ }
765
+
766
+ // Get video cached duration
767
+ getVideoCachedDuration() {
768
+ if (this.dom.buffered.length > 0) {
769
+ return Math.floor(this.dom.buffered.end(0) * 1000)
770
+ }
771
+ return 0
772
+ }
773
+
774
+ // Get audio cached duration
775
+ getAudioCachedDuration() {
776
+ // HTML5 doesn't separate audio/video cache
777
+ return this.getVideoCachedDuration()
778
+ }
779
+
780
+ // Get video cached bytes
781
+ getVideoCachedBytes() {
782
+ // Not available in HTML5
783
+ return 0
784
+ }
785
+
786
+ // Get audio cached bytes
787
+ getAudioCachedBytes() {
788
+ // Not available in HTML5
789
+ return 0
790
+ }
791
+
792
+ // Get video cached packets
793
+ getVideoCachedPackets() {
794
+ // Not available in HTML5
795
+ return 0
796
+ }
797
+
798
+ // Get audio cached packets
799
+ getAudioCachedPackets() {
800
+ // Not available in HTML5
801
+ return 0
802
+ }
803
+
804
+ // Get TCP speed
805
+ getTcpSpeed2() {
806
+ return 0
807
+ }
808
+
809
+ // Get bit rate
810
+ getBitRate2() {
811
+ return 0
812
+ }
813
+
814
+ // Get drop frame rate
815
+ getDropFrameRate() {
816
+ return 0
817
+ }
818
+
819
+ // Get video decode frames per second
820
+ getVideoDecodeFramesPerSecond() {
821
+ return 0
822
+ }
823
+
824
+ // Get video output frames per second
825
+ getVideoOutputFramesPerSecond() {
826
+ return 0
827
+ }
828
+ }