@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.
- package/package.json +24 -0
- package/src/adapters/es3-video-player.js +828 -0
- package/src/components/Modal.js +119 -0
- package/src/components/QtAnimationView.js +678 -0
- package/src/components/QtBaseComponent.js +165 -0
- package/src/components/QtFastListView.js +1920 -0
- package/src/components/QtFlexView.js +799 -0
- package/src/components/QtImage.js +203 -0
- package/src/components/QtItemFrame.js +239 -0
- package/src/components/QtItemStoreView.js +93 -0
- package/src/components/QtItemView.js +125 -0
- package/src/components/QtListView.js +331 -0
- package/src/components/QtLoadingView.js +55 -0
- package/src/components/QtPageRootView.js +19 -0
- package/src/components/QtPlayMark.js +168 -0
- package/src/components/QtProgressBar.js +199 -0
- package/src/components/QtQRCode.js +78 -0
- package/src/components/QtReplaceChild.js +149 -0
- package/src/components/QtRippleView.js +166 -0
- package/src/components/QtSeekBar.js +409 -0
- package/src/components/QtText.js +679 -0
- package/src/components/QtTransitionImage.js +170 -0
- package/src/components/QtView.js +706 -0
- package/src/components/QtWebView.js +613 -0
- package/src/components/TabsView.js +420 -0
- package/src/components/ViewPager.js +206 -0
- package/src/components/index.js +24 -0
- package/src/components/plugins/TextV2Component.js +70 -0
- package/src/components/plugins/index.js +7 -0
- package/src/core/SceneBuilder.js +58 -0
- package/src/core/TVFocusManager.js +2014 -0
- package/src/core/asyncLocalStorage.js +175 -0
- package/src/core/autoProxy.js +165 -0
- package/src/core/componentRegistry.js +84 -0
- package/src/core/constants.js +6 -0
- package/src/core/index.js +8 -0
- package/src/core/moduleUtils.js +36 -0
- package/src/core/patches.js +958 -0
- package/src/core/templateBinding.js +666 -0
- package/src/index.js +246 -0
- package/src/modules/AndroidDevelopModule.js +101 -0
- package/src/modules/AndroidDeviceModule.js +341 -0
- package/src/modules/AndroidNetworkModule.js +178 -0
- package/src/modules/AndroidSharedPreferencesModule.js +100 -0
- package/src/modules/ESDeviceInfoModule.js +450 -0
- package/src/modules/ESGroupDataModule.js +195 -0
- package/src/modules/ESIJKAudioPlayerModule.js +477 -0
- package/src/modules/ESLocalStorageModule.js +100 -0
- package/src/modules/ESLogModule.js +65 -0
- package/src/modules/ESModule.js +106 -0
- package/src/modules/ESNetworkSpeedModule.js +117 -0
- package/src/modules/ESToastModule.js +172 -0
- package/src/modules/EsNativeModule.js +117 -0
- package/src/modules/FastListModule.js +101 -0
- package/src/modules/FocusModule.js +145 -0
- 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
|
+
}
|