@clappr/hlsjs-playback 3.0.2 → 3.1.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/README.md +41 -36
- package/dist/hlsjs-playback.esm.js +101 -383
- package/dist/hlsjs-playback.esm.js.map +1 -1
- package/dist/hlsjs-playback.js +185 -442
- package/dist/hlsjs-playback.js.map +1 -1
- package/dist/hlsjs-playback.min.js +1 -2
- package/dist/hlsjs-playback.min.js.map +1 -1
- package/package.json +12 -13
- package/src/dist.smoke.test.js +14 -7
- package/src/hls.js +2 -1
- package/src/hls.test.js +176 -60
package/package.json
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clappr/hlsjs-playback",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "HLS Playback based on hls.js",
|
|
5
5
|
"main": "./dist/hlsjs-playback.js",
|
|
6
6
|
"module": "./dist/hlsjs-playback.esm.js",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"start": "
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"test": "
|
|
15
|
-
"test:
|
|
16
|
-
"
|
|
17
|
-
"lint": "eslint *.js ./src",
|
|
8
|
+
"start": "vite",
|
|
9
|
+
"build": "vite build",
|
|
10
|
+
"watch": "vite build --watch",
|
|
11
|
+
"bundle-check": "ANALYZE_BUNDLE=true vite build",
|
|
12
|
+
"release": "rm -rf dist && vite build && vite build --mode minify",
|
|
13
|
+
"test": "vitest run --coverage",
|
|
14
|
+
"test:smoke": "vitest run src/dist.smoke.test.js",
|
|
15
|
+
"test:watch": "vitest",
|
|
16
|
+
"lint": "eslint --no-error-on-unmatched-pattern *.js ./src",
|
|
18
17
|
"lint:fix": "yarn lint -- --fix",
|
|
19
18
|
"prepublishOnly": "lerna run release --scope=@clappr/hlsjs-playback"
|
|
20
19
|
},
|
|
@@ -41,7 +40,7 @@
|
|
|
41
40
|
"hls.js": "^1"
|
|
42
41
|
},
|
|
43
42
|
"devDependencies": {
|
|
44
|
-
"@clappr/core": "^0.
|
|
45
|
-
"hls.js": "
|
|
43
|
+
"@clappr/core": "^0.17.0",
|
|
44
|
+
"hls.js": "^1"
|
|
46
45
|
}
|
|
47
46
|
}
|
package/src/dist.smoke.test.js
CHANGED
|
@@ -12,7 +12,7 @@ const { TextEncoder, TextDecoder } = require('util')
|
|
|
12
12
|
global.TextEncoder = global.TextEncoder || TextEncoder
|
|
13
13
|
global.TextDecoder = global.TextDecoder || TextDecoder
|
|
14
14
|
const { JSDOM } = require('jsdom')
|
|
15
|
-
const {
|
|
15
|
+
const { expectEs5Syntax, expectSourcemapFromSrc } = require('../../../test/dist-contract')
|
|
16
16
|
|
|
17
17
|
const DIST = path.join(__dirname, '..', 'dist')
|
|
18
18
|
|
|
@@ -29,7 +29,7 @@ function readArtifact(name) {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
function loadArtifact(name) {
|
|
32
|
-
|
|
32
|
+
vi.resetModules()
|
|
33
33
|
return require(path.join(DIST, name))
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -68,8 +68,8 @@ function assertPlaybackContract(HlsjsPlaybackExport) {
|
|
|
68
68
|
expect(typeof HLSJS.version).toBe('string')
|
|
69
69
|
expect(HLSJS.version.length).toBeGreaterThan(0)
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
vi.spyOn(HLSJS, 'isSupported').mockReturnValue(true)
|
|
72
|
+
vi.spyOn(window.HTMLMediaElement.prototype, 'load').mockImplementation(() => {})
|
|
73
73
|
|
|
74
74
|
expect(Playback.canPlay('http://example.com/video.m3u8')).toBe(true)
|
|
75
75
|
expect(Playback.canPlay('http://example.com/video.m3u8?token=1')).toBe(true)
|
|
@@ -81,6 +81,7 @@ function assertPlaybackContract(HlsjsPlaybackExport) {
|
|
|
81
81
|
|
|
82
82
|
function assertSourceMappingURL(filename) {
|
|
83
83
|
expect(readArtifact(filename)).toContain(`sourceMappingURL=${filename}.map`)
|
|
84
|
+
expectSourcemapFromSrc(readArtifact(`${filename}.map`), filename)
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
const EXPECTED_SOURCEMAPS = Object.values(ARTIFACTS)
|
|
@@ -88,7 +89,7 @@ const EXPECTED_SOURCEMAPS = Object.values(ARTIFACTS)
|
|
|
88
89
|
.sort()
|
|
89
90
|
|
|
90
91
|
afterEach(() => {
|
|
91
|
-
|
|
92
|
+
vi.restoreAllMocks()
|
|
92
93
|
})
|
|
93
94
|
|
|
94
95
|
describe.each([
|
|
@@ -105,7 +106,7 @@ describe.each([
|
|
|
105
106
|
})
|
|
106
107
|
|
|
107
108
|
test('does not emit native class syntax', () => {
|
|
108
|
-
|
|
109
|
+
expectEs5Syntax(readArtifact(filename), filename)
|
|
109
110
|
})
|
|
110
111
|
})
|
|
111
112
|
|
|
@@ -120,10 +121,16 @@ describe('dist sourcemap inventory', () => {
|
|
|
120
121
|
})
|
|
121
122
|
|
|
122
123
|
describe('hls.js peer identity', () => {
|
|
123
|
-
test.each(
|
|
124
|
+
test.each(UMD_ARTIFACTS)('%s defers to the consumer hls.js', filename => {
|
|
124
125
|
const Playback = resolvePlayback(loadArtifact(filename))
|
|
125
126
|
expect(Playback.HLSJS).toBe(require('hls.js'))
|
|
126
127
|
})
|
|
128
|
+
|
|
129
|
+
test(`${ARTIFACTS.esm} defers to the consumer hls.js`, async () => {
|
|
130
|
+
const Playback = resolvePlayback(loadArtifact(ARTIFACTS.esm))
|
|
131
|
+
const { default: hls } = await import('hls.js')
|
|
132
|
+
expect(Playback.HLSJS).toBe(hls)
|
|
133
|
+
})
|
|
127
134
|
})
|
|
128
135
|
|
|
129
136
|
describe('UMD global.Hls branch', () => {
|
package/src/hls.js
CHANGED
|
@@ -415,7 +415,7 @@ export default class HlsjsPlayback extends HTML5Video {
|
|
|
415
415
|
}
|
|
416
416
|
let formattedError
|
|
417
417
|
if (data.response) error.description += `, response: ${JSON.stringify(data.response)}`
|
|
418
|
-
// only
|
|
418
|
+
// only handle errors if they are fatal
|
|
419
419
|
// hlsjs should automatically handle non fatal errors
|
|
420
420
|
if (data.fatal) {
|
|
421
421
|
if (this._recoverAttemptsRemaining > 0) {
|
|
@@ -479,6 +479,7 @@ export default class HlsjsPlayback extends HTML5Video {
|
|
|
479
479
|
|
|
480
480
|
error.level = PlayerError.Levels.WARN
|
|
481
481
|
Log.warn('hlsjs: non-fatal error occurred', { evt, data })
|
|
482
|
+
this.trigger(Events.PLAYBACK_WARNING, error, this.name)
|
|
482
483
|
}
|
|
483
484
|
}
|
|
484
485
|
|
package/src/hls.test.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Core, Events, Log, Playback } from '@clappr/core'
|
|
1
|
+
import { Core, Events, Log, Playback, PlayerError } from '@clappr/core'
|
|
2
2
|
import HlsjsPlayback from './hls.js'
|
|
3
3
|
import HLSJS from 'hls.js'
|
|
4
4
|
|
|
@@ -25,17 +25,17 @@ const makeLevelData = ({
|
|
|
25
25
|
|
|
26
26
|
const stubHls = playback => {
|
|
27
27
|
playback._hls = {
|
|
28
|
-
startLoad:
|
|
29
|
-
recoverMediaError:
|
|
30
|
-
swapAudioCodec:
|
|
31
|
-
destroy:
|
|
28
|
+
startLoad: vi.fn(),
|
|
29
|
+
recoverMediaError: vi.fn(),
|
|
30
|
+
swapAudioCodec: vi.fn(),
|
|
31
|
+
destroy: vi.fn()
|
|
32
32
|
}
|
|
33
33
|
return playback._hls
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
const freezeNow = (playback, initialMs = 1000000) => {
|
|
37
37
|
let current = initialMs
|
|
38
|
-
|
|
38
|
+
vi.spyOn(playback, '_now', 'get').mockImplementation(() => current)
|
|
39
39
|
return {
|
|
40
40
|
advance(ms) {
|
|
41
41
|
current += ms
|
|
@@ -45,12 +45,13 @@ const freezeNow = (playback, initialMs = 1000000) => {
|
|
|
45
45
|
|
|
46
46
|
describe('HlsjsPlayback', () => {
|
|
47
47
|
beforeEach(() => {
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
vi.spyOn(window.HTMLMediaElement.prototype, 'play').mockImplementation(() => {})
|
|
49
|
+
vi.spyOn(window.HTMLMediaElement.prototype, 'load').mockImplementation(() => {})
|
|
50
50
|
})
|
|
51
51
|
|
|
52
52
|
afterEach(() => {
|
|
53
|
-
|
|
53
|
+
vi.restoreAllMocks()
|
|
54
|
+
vi.clearAllTimers()
|
|
54
55
|
})
|
|
55
56
|
|
|
56
57
|
test('have a getter called defaultOptions', () => {
|
|
@@ -83,7 +84,7 @@ describe('HlsjsPlayback', () => {
|
|
|
83
84
|
})
|
|
84
85
|
|
|
85
86
|
test('should be able to identify it can play resources independently of the file extension case', () => {
|
|
86
|
-
|
|
87
|
+
vi.spyOn(HLSJS, 'isSupported').mockImplementation(() => true)
|
|
87
88
|
expect(HlsjsPlayback.canPlay('/relative/video.m3u8')).toBeTruthy()
|
|
88
89
|
expect(HlsjsPlayback.canPlay('/relative/VIDEO.M3U8')).toBeTruthy()
|
|
89
90
|
expect(HlsjsPlayback.canPlay('/relative/video.m3u8?foobarQuery=1234#somefragment')).toBeTruthy()
|
|
@@ -102,7 +103,7 @@ describe('HlsjsPlayback', () => {
|
|
|
102
103
|
})
|
|
103
104
|
|
|
104
105
|
test('can play regardless of any mime type letter case', () => {
|
|
105
|
-
|
|
106
|
+
vi.spyOn(HLSJS, 'isSupported').mockImplementation(() => true)
|
|
106
107
|
expect(HlsjsPlayback.canPlay('/path/list.m3u8', 'APPLICATION/VND.APPLE.MPEGURL')).toBeTruthy()
|
|
107
108
|
expect(
|
|
108
109
|
HlsjsPlayback.canPlay(
|
|
@@ -154,9 +155,7 @@ describe('HlsjsPlayback', () => {
|
|
|
154
155
|
})
|
|
155
156
|
|
|
156
157
|
test('registers PLAYBACK_FRAGMENT_PARSING_METADATA event', () => {
|
|
157
|
-
expect(Events.Custom.PLAYBACK_FRAGMENT_PARSING_METADATA).toEqual(
|
|
158
|
-
'playbackFragmentParsingMetadata'
|
|
159
|
-
)
|
|
158
|
+
expect(Events.Custom.PLAYBACK_FRAGMENT_PARSING_METADATA).toEqual('playbackFragmentParsingMetadata')
|
|
160
159
|
})
|
|
161
160
|
|
|
162
161
|
test('levels supports specifying the level', () => {
|
|
@@ -237,14 +236,14 @@ describe('HlsjsPlayback', () => {
|
|
|
237
236
|
hlsPlayback: { preload: false }
|
|
238
237
|
})
|
|
239
238
|
playback._setup()
|
|
240
|
-
|
|
239
|
+
vi.spyOn(playback._hls, 'loadSource')
|
|
241
240
|
playback._hls.trigger(HLSJS.Events.MEDIA_ATTACHED, { media: playback.el })
|
|
242
241
|
|
|
243
242
|
expect(playback._hls.loadSource).not.toHaveBeenCalled()
|
|
244
243
|
|
|
245
244
|
playback.options.hlsPlayback.preload = true
|
|
246
245
|
playback._setup()
|
|
247
|
-
|
|
246
|
+
vi.spyOn(playback._hls, 'loadSource')
|
|
248
247
|
playback._hls.trigger(HLSJS.Events.MEDIA_ATTACHED, { media: playback.el })
|
|
249
248
|
|
|
250
249
|
expect(playback._hls.loadSource).toHaveBeenCalledTimes(1)
|
|
@@ -264,7 +263,7 @@ describe('HlsjsPlayback', () => {
|
|
|
264
263
|
|
|
265
264
|
test('calls bindCustomListeners method', () => {
|
|
266
265
|
const playback = new HlsjsPlayback({ src: 'http://clappr.io/foo.m3u8' })
|
|
267
|
-
|
|
266
|
+
vi.spyOn(playback, 'bindCustomListeners')
|
|
268
267
|
playback._setup()
|
|
269
268
|
|
|
270
269
|
expect(playback.bindCustomListeners).toHaveBeenCalledTimes(1)
|
|
@@ -275,7 +274,7 @@ describe('HlsjsPlayback', () => {
|
|
|
275
274
|
test('avoid to run internal logic if _isReadyState flag is true', () => {
|
|
276
275
|
const playback = new HlsjsPlayback({ src: 'http://clappr.io/video.m3u8' })
|
|
277
276
|
playback._isReadyState = true
|
|
278
|
-
|
|
277
|
+
vi.spyOn(playback, '_setup')
|
|
279
278
|
playback._ready()
|
|
280
279
|
|
|
281
280
|
expect(playback._setup).not.toHaveBeenCalled()
|
|
@@ -283,7 +282,7 @@ describe('HlsjsPlayback', () => {
|
|
|
283
282
|
|
|
284
283
|
test("call _setup method if HLS.JS internal don't exists", () => {
|
|
285
284
|
const playback = new HlsjsPlayback({ src: 'http://clappr.io/video.m3u8' })
|
|
286
|
-
|
|
285
|
+
vi.spyOn(playback, '_setup')
|
|
287
286
|
playback._ready()
|
|
288
287
|
|
|
289
288
|
expect(playback._setup).toHaveBeenCalledTimes(1)
|
|
@@ -302,8 +301,8 @@ describe('HlsjsPlayback', () => {
|
|
|
302
301
|
expect(playback._isReadyState).toBeTruthy()
|
|
303
302
|
})
|
|
304
303
|
|
|
305
|
-
test('triggers PLAYBACK_READY event', done => {
|
|
306
|
-
const cb =
|
|
304
|
+
test('triggers PLAYBACK_READY event', () => new Promise(done => {
|
|
305
|
+
const cb = vi.fn()
|
|
307
306
|
const playback = new HlsjsPlayback({ src: 'http://clappr.io/video.m3u8' })
|
|
308
307
|
|
|
309
308
|
playback.listenTo(playback, Events.PLAYBACK_READY, cb)
|
|
@@ -312,12 +311,12 @@ describe('HlsjsPlayback', () => {
|
|
|
312
311
|
done()
|
|
313
312
|
})
|
|
314
313
|
playback._ready()
|
|
315
|
-
})
|
|
314
|
+
}))
|
|
316
315
|
})
|
|
317
316
|
|
|
318
317
|
describe('play method', () => {
|
|
319
318
|
test('calls this._hls.loadSource once if _manifestLoading flag and options.hlsPlayback.preload are falsy', () => {
|
|
320
|
-
|
|
319
|
+
vi.spyOn(HLSJS.prototype, 'loadSource').mockImplementation()
|
|
321
320
|
const src = 'http://clappr.io/foo.m3u8'
|
|
322
321
|
const playback = new HlsjsPlayback({ src, hlsPlayback: { preload: false } })
|
|
323
322
|
playback._setup()
|
|
@@ -350,7 +349,7 @@ describe('HlsjsPlayback', () => {
|
|
|
350
349
|
})
|
|
351
350
|
const url = 'http://clappr.io/foo2.m3u8'
|
|
352
351
|
playback.load(url)
|
|
353
|
-
|
|
352
|
+
vi.spyOn(playback._hls, 'loadSource')
|
|
354
353
|
playback._hls.trigger(HLSJS.Events.MEDIA_ATTACHED, { media: playback.el })
|
|
355
354
|
expect(playback.options.src).toBe(url)
|
|
356
355
|
expect(playback._hls.loadSource).toHaveBeenCalledWith(url)
|
|
@@ -359,7 +358,7 @@ describe('HlsjsPlayback', () => {
|
|
|
359
358
|
|
|
360
359
|
describe('bindCustomListeners method', () => {
|
|
361
360
|
test('creates listeners for each item configured on customListeners array', () => {
|
|
362
|
-
const cb =
|
|
361
|
+
const cb = vi.fn()
|
|
363
362
|
const playback = new HlsjsPlayback({
|
|
364
363
|
src: 'http://clappr.io/foo.m3u8',
|
|
365
364
|
hlsPlayback: {
|
|
@@ -376,7 +375,7 @@ describe('HlsjsPlayback', () => {
|
|
|
376
375
|
})
|
|
377
376
|
|
|
378
377
|
test("don't add one listener without a valid configuration", () => {
|
|
379
|
-
const cb =
|
|
378
|
+
const cb = vi.fn()
|
|
380
379
|
const playback = new HlsjsPlayback({ src: 'http://clappr.io/foo.m3u8' })
|
|
381
380
|
playback._setup()
|
|
382
381
|
|
|
@@ -398,7 +397,7 @@ describe('HlsjsPlayback', () => {
|
|
|
398
397
|
})
|
|
399
398
|
|
|
400
399
|
test('adds a listener for one time when the customListeners array item is configured with the "once" param', () => {
|
|
401
|
-
const cb =
|
|
400
|
+
const cb = vi.fn()
|
|
402
401
|
const playback = new HlsjsPlayback({
|
|
403
402
|
src: 'http://clappr.io/foo.m3u8',
|
|
404
403
|
hlsPlayback: {
|
|
@@ -417,7 +416,7 @@ describe('HlsjsPlayback', () => {
|
|
|
417
416
|
|
|
418
417
|
describe('unbindCustomListeners method', () => {
|
|
419
418
|
test('remove listeners for each item configured on customListeners array', () => {
|
|
420
|
-
const cb =
|
|
419
|
+
const cb = vi.fn()
|
|
421
420
|
const playback = new HlsjsPlayback({
|
|
422
421
|
src: 'http://clappr.io/foo.m3u8',
|
|
423
422
|
hlsPlayback: {
|
|
@@ -442,9 +441,7 @@ describe('HlsjsPlayback', () => {
|
|
|
442
441
|
}
|
|
443
442
|
const playback = new HlsjsPlayback({ src: 'http://clappr.io/foo.m3u8' })
|
|
444
443
|
playback.el.currentTime = 5
|
|
445
|
-
playback.
|
|
446
|
-
playback.unbindCustomListeners()
|
|
447
|
-
playback._hls.trigger(HLSJS.Events.FRAG_CHANGED, fragmentMock)
|
|
444
|
+
playback._onFragmentChanged(HLSJS.Events.FRAG_CHANGED, fragmentMock)
|
|
448
445
|
expect(playback.currentTimestamp).toBe(1556663045) // 'Tue Apr 30 2019 19:24:05'
|
|
449
446
|
})
|
|
450
447
|
|
|
@@ -455,6 +452,46 @@ describe('HlsjsPlayback', () => {
|
|
|
455
452
|
})
|
|
456
453
|
})
|
|
457
454
|
|
|
455
|
+
describe('level and fragment events', () => {
|
|
456
|
+
test('_onLevelSwitch fills levels and reports bitrate', () => {
|
|
457
|
+
const playback = createPlayback()
|
|
458
|
+
playback._hls = {
|
|
459
|
+
levels: [{ height: 1080, width: 1920, bitrate: 4000000 }]
|
|
460
|
+
}
|
|
461
|
+
const onSwitch = vi.fn()
|
|
462
|
+
const onHd = vi.fn()
|
|
463
|
+
const onBitrate = vi.fn()
|
|
464
|
+
playback.on(Events.PLAYBACK_LEVEL_SWITCH, onSwitch)
|
|
465
|
+
playback.on(Events.PLAYBACK_HIGHDEFINITIONUPDATE, onHd)
|
|
466
|
+
playback.on(Events.PLAYBACK_BITRATE, onBitrate)
|
|
467
|
+
|
|
468
|
+
playback._onLevelSwitch(HLSJS.Events.LEVEL_SWITCHED, { level: 0 })
|
|
469
|
+
|
|
470
|
+
expect(playback.levels).toHaveLength(1)
|
|
471
|
+
expect(playback.highDefinition).toBe(true)
|
|
472
|
+
expect(onSwitch).toHaveBeenCalled()
|
|
473
|
+
expect(onHd).toHaveBeenCalledWith(true)
|
|
474
|
+
expect(onBitrate).toHaveBeenCalledWith(
|
|
475
|
+
expect.objectContaining({ height: 1080, bitrate: 4000000, level: 0 })
|
|
476
|
+
)
|
|
477
|
+
})
|
|
478
|
+
|
|
479
|
+
test('_onFragmentLoaded and _onFragmentBuffered forward playback events', () => {
|
|
480
|
+
const playback = createPlayback()
|
|
481
|
+
const onLoaded = vi.fn()
|
|
482
|
+
const onBuffered = vi.fn()
|
|
483
|
+
playback.on(Events.PLAYBACK_FRAGMENT_LOADED, onLoaded)
|
|
484
|
+
playback.on(Events.PLAYBACK_FRAGMENT_BUFFERED, onBuffered)
|
|
485
|
+
const data = { frag: { sn: 1 } }
|
|
486
|
+
|
|
487
|
+
playback._onFragmentLoaded(HLSJS.Events.FRAG_LOADED, data)
|
|
488
|
+
playback._onFragmentBuffered(HLSJS.Events.FRAG_BUFFERED, data)
|
|
489
|
+
|
|
490
|
+
expect(onLoaded).toHaveBeenCalledWith(data)
|
|
491
|
+
expect(onBuffered).toHaveBeenCalledWith(data)
|
|
492
|
+
})
|
|
493
|
+
})
|
|
494
|
+
|
|
458
495
|
describe('DVR time model', () => {
|
|
459
496
|
test('without correlation duration equals playable region duration', () => {
|
|
460
497
|
const playback = createPlayback()
|
|
@@ -740,8 +777,8 @@ describe('HlsjsPlayback', () => {
|
|
|
740
777
|
const playback = createPlayback()
|
|
741
778
|
playback._playbackType = Playback.LIVE
|
|
742
779
|
freezeNow(playback)
|
|
743
|
-
|
|
744
|
-
|
|
780
|
+
vi.spyOn(playback, '_onDurationChange').mockImplementation(() => {})
|
|
781
|
+
vi.spyOn(playback, '_onProgress').mockImplementation(() => {})
|
|
745
782
|
|
|
746
783
|
playback._onLevelUpdated(
|
|
747
784
|
'levelUpdated',
|
|
@@ -798,7 +835,7 @@ describe('HlsjsPlayback', () => {
|
|
|
798
835
|
playback._playbackType = Playback.VOD
|
|
799
836
|
playback._playableRegionStartTime = 0
|
|
800
837
|
playback._playableRegionDuration = 80
|
|
801
|
-
|
|
838
|
+
vi.spyOn(Log, 'warn').mockImplementation(() => {})
|
|
802
839
|
|
|
803
840
|
playback.seek(-1)
|
|
804
841
|
|
|
@@ -846,7 +883,7 @@ describe('HlsjsPlayback', () => {
|
|
|
846
883
|
playback._playableRegionDuration = 100
|
|
847
884
|
playback.el.currentTime = 1
|
|
848
885
|
freezeNow(playback, 1000000)
|
|
849
|
-
const onTimeUpdate =
|
|
886
|
+
const onTimeUpdate = vi.fn()
|
|
850
887
|
playback.on(Events.PLAYBACK_TIMEUPDATE, onTimeUpdate)
|
|
851
888
|
|
|
852
889
|
playback._onTimeUpdate()
|
|
@@ -861,7 +898,7 @@ describe('HlsjsPlayback', () => {
|
|
|
861
898
|
playback._playableRegionDuration = 100
|
|
862
899
|
playback.el.currentTime = 1
|
|
863
900
|
const clock = freezeNow(playback, 1000000)
|
|
864
|
-
const onTimeUpdate =
|
|
901
|
+
const onTimeUpdate = vi.fn()
|
|
865
902
|
playback.on(Events.PLAYBACK_TIMEUPDATE, onTimeUpdate)
|
|
866
903
|
|
|
867
904
|
playback._onTimeUpdate()
|
|
@@ -877,7 +914,7 @@ describe('HlsjsPlayback', () => {
|
|
|
877
914
|
playback._playableRegionDuration = 100
|
|
878
915
|
playback.el.currentTime = 1
|
|
879
916
|
const clock = freezeNow(playback, 1000000)
|
|
880
|
-
const onTimeUpdate =
|
|
917
|
+
const onTimeUpdate = vi.fn()
|
|
881
918
|
playback.on(Events.PLAYBACK_TIMEUPDATE, onTimeUpdate)
|
|
882
919
|
|
|
883
920
|
playback._onTimeUpdate()
|
|
@@ -893,7 +930,7 @@ describe('HlsjsPlayback', () => {
|
|
|
893
930
|
playback._playableRegionDuration = 100
|
|
894
931
|
playback.el.currentTime = 1
|
|
895
932
|
const clock = freezeNow(playback, 1000000)
|
|
896
|
-
const onTimeUpdate =
|
|
933
|
+
const onTimeUpdate = vi.fn()
|
|
897
934
|
playback.on(Events.PLAYBACK_TIMEUPDATE, onTimeUpdate)
|
|
898
935
|
|
|
899
936
|
playback._onTimeUpdate()
|
|
@@ -911,7 +948,7 @@ describe('HlsjsPlayback', () => {
|
|
|
911
948
|
playback._programDateTime = 1000
|
|
912
949
|
playback.el.currentTime = 1
|
|
913
950
|
const clock = freezeNow(playback, 1000000)
|
|
914
|
-
const onTimeUpdate =
|
|
951
|
+
const onTimeUpdate = vi.fn()
|
|
915
952
|
playback.on(Events.PLAYBACK_TIMEUPDATE, onTimeUpdate)
|
|
916
953
|
|
|
917
954
|
playback._onTimeUpdate()
|
|
@@ -935,14 +972,14 @@ describe('HlsjsPlayback', () => {
|
|
|
935
972
|
cores.push(core)
|
|
936
973
|
const playback = createPlayback(options, core.playerError)
|
|
937
974
|
stubHls(playback)
|
|
938
|
-
|
|
939
|
-
|
|
975
|
+
vi.spyOn(playback, 'play').mockImplementation(() => {})
|
|
976
|
+
vi.spyOn(playback, 'stop').mockImplementation(() => {})
|
|
940
977
|
return playback
|
|
941
978
|
}
|
|
942
979
|
|
|
943
980
|
test('recoverable network fatal error calls startLoad', () => {
|
|
944
981
|
const playback = createErrorPlayback()
|
|
945
|
-
|
|
982
|
+
vi.spyOn(Log, 'warn').mockImplementation(() => {})
|
|
946
983
|
|
|
947
984
|
playback._onHLSJSError('hlsError', {
|
|
948
985
|
fatal: true,
|
|
@@ -963,8 +1000,8 @@ describe('HlsjsPlayback', () => {
|
|
|
963
1000
|
HLSJS.ErrorDetails.LEVEL_LOAD_TIMEOUT
|
|
964
1001
|
])('unrecoverable network fatal %s triggers error and stop', details => {
|
|
965
1002
|
const playback = createErrorPlayback()
|
|
966
|
-
|
|
967
|
-
const onError =
|
|
1003
|
+
vi.spyOn(Log, 'error').mockImplementation(() => {})
|
|
1004
|
+
const onError = vi.fn()
|
|
968
1005
|
playback.on(Events.PLAYBACK_ERROR, onError)
|
|
969
1006
|
|
|
970
1007
|
playback._onHLSJSError('hlsError', {
|
|
@@ -980,9 +1017,9 @@ describe('HlsjsPlayback', () => {
|
|
|
980
1017
|
|
|
981
1018
|
test('media fatal error recovers via recoverMediaError then swapAudioCodec then fails', () => {
|
|
982
1019
|
const playback = createErrorPlayback()
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
const onError =
|
|
1020
|
+
vi.spyOn(Log, 'warn').mockImplementation(() => {})
|
|
1021
|
+
vi.spyOn(Log, 'error').mockImplementation(() => {})
|
|
1022
|
+
const onError = vi.fn()
|
|
986
1023
|
playback.on(Events.PLAYBACK_ERROR, onError)
|
|
987
1024
|
const mediaError = {
|
|
988
1025
|
fatal: true,
|
|
@@ -1008,8 +1045,8 @@ describe('HlsjsPlayback', () => {
|
|
|
1008
1045
|
|
|
1009
1046
|
test('other fatal error types trigger error and stop', () => {
|
|
1010
1047
|
const playback = createErrorPlayback()
|
|
1011
|
-
|
|
1012
|
-
const onError =
|
|
1048
|
+
vi.spyOn(Log, 'error').mockImplementation(() => {})
|
|
1049
|
+
const onError = vi.fn()
|
|
1013
1050
|
playback.on(Events.PLAYBACK_ERROR, onError)
|
|
1014
1051
|
|
|
1015
1052
|
playback._onHLSJSError('hlsError', {
|
|
@@ -1024,8 +1061,8 @@ describe('HlsjsPlayback', () => {
|
|
|
1024
1061
|
|
|
1025
1062
|
test('exhausted recover attempts trigger error and stop', () => {
|
|
1026
1063
|
const playback = createErrorPlayback({ hlsRecoverAttempts: 0 })
|
|
1027
|
-
|
|
1028
|
-
const onError =
|
|
1064
|
+
vi.spyOn(Log, 'error').mockImplementation(() => {})
|
|
1065
|
+
const onError = vi.fn()
|
|
1029
1066
|
playback.on(Events.PLAYBACK_ERROR, onError)
|
|
1030
1067
|
|
|
1031
1068
|
playback._onHLSJSError('hlsError', {
|
|
@@ -1043,8 +1080,8 @@ describe('HlsjsPlayback', () => {
|
|
|
1043
1080
|
const playback = createErrorPlayback({
|
|
1044
1081
|
playback: { triggerFatalErrorOnResourceDenied: true }
|
|
1045
1082
|
})
|
|
1046
|
-
|
|
1047
|
-
const onError =
|
|
1083
|
+
vi.spyOn(Log, 'error').mockImplementation(() => {})
|
|
1084
|
+
const onError = vi.fn()
|
|
1048
1085
|
playback.on(Events.PLAYBACK_ERROR, onError)
|
|
1049
1086
|
|
|
1050
1087
|
playback._onHLSJSError('hlsError', {
|
|
@@ -1060,8 +1097,8 @@ describe('HlsjsPlayback', () => {
|
|
|
1060
1097
|
|
|
1061
1098
|
test('non-fatal key denial without option only warns', () => {
|
|
1062
1099
|
const playback = createErrorPlayback()
|
|
1063
|
-
|
|
1064
|
-
const onError =
|
|
1100
|
+
vi.spyOn(Log, 'warn').mockImplementation(() => {})
|
|
1101
|
+
const onError = vi.fn()
|
|
1065
1102
|
playback.on(Events.PLAYBACK_ERROR, onError)
|
|
1066
1103
|
|
|
1067
1104
|
playback._onHLSJSError('hlsError', {
|
|
@@ -1080,8 +1117,8 @@ describe('HlsjsPlayback', () => {
|
|
|
1080
1117
|
const playback = createErrorPlayback({
|
|
1081
1118
|
playback: { triggerFatalErrorOnResourceDenied: true }
|
|
1082
1119
|
})
|
|
1083
|
-
|
|
1084
|
-
const onError =
|
|
1120
|
+
vi.spyOn(Log, 'warn').mockImplementation(() => {})
|
|
1121
|
+
const onError = vi.fn()
|
|
1085
1122
|
playback.on(Events.PLAYBACK_ERROR, onError)
|
|
1086
1123
|
|
|
1087
1124
|
playback._onHLSJSError('hlsError', {
|
|
@@ -1097,8 +1134,8 @@ describe('HlsjsPlayback', () => {
|
|
|
1097
1134
|
|
|
1098
1135
|
test('includes response in error description when present', () => {
|
|
1099
1136
|
const playback = createErrorPlayback()
|
|
1100
|
-
|
|
1101
|
-
const onError =
|
|
1137
|
+
vi.spyOn(Log, 'error').mockImplementation(() => {})
|
|
1138
|
+
const onError = vi.fn()
|
|
1102
1139
|
playback.on(Events.PLAYBACK_ERROR, onError)
|
|
1103
1140
|
|
|
1104
1141
|
playback._onHLSJSError('hlsError', {
|
|
@@ -1110,6 +1147,85 @@ describe('HlsjsPlayback', () => {
|
|
|
1110
1147
|
|
|
1111
1148
|
expect(onError.mock.calls[0][0].description).toContain('response:')
|
|
1112
1149
|
})
|
|
1150
|
+
|
|
1151
|
+
test('triggers PLAYBACK_WARNING for a non-fatal error', () => {
|
|
1152
|
+
const playback = createErrorPlayback()
|
|
1153
|
+
vi.spyOn(Log, 'warn').mockImplementation(() => {})
|
|
1154
|
+
const onWarning = vi.fn()
|
|
1155
|
+
playback.on(Events.PLAYBACK_WARNING, onWarning)
|
|
1156
|
+
|
|
1157
|
+
playback._onHLSJSError('hlsError', {
|
|
1158
|
+
fatal: false,
|
|
1159
|
+
type: HLSJS.ErrorTypes.NETWORK_ERROR,
|
|
1160
|
+
details: HLSJS.ErrorDetails.FRAG_LOAD_ERROR,
|
|
1161
|
+
response: { code: 500 }
|
|
1162
|
+
})
|
|
1163
|
+
|
|
1164
|
+
expect(onWarning).toHaveBeenCalledTimes(1)
|
|
1165
|
+
expect(onWarning.mock.calls[0][0]).toMatchObject({
|
|
1166
|
+
code: `${HLSJS.ErrorTypes.NETWORK_ERROR}_${HLSJS.ErrorDetails.FRAG_LOAD_ERROR}`,
|
|
1167
|
+
level: PlayerError.Levels.WARN,
|
|
1168
|
+
raw: { response: { code: 500 } }
|
|
1169
|
+
})
|
|
1170
|
+
})
|
|
1171
|
+
|
|
1172
|
+
test('does not trigger PLAYBACK_WARNING for a fatal error', () => {
|
|
1173
|
+
const playback = createErrorPlayback()
|
|
1174
|
+
vi.spyOn(Log, 'error').mockImplementation(() => {})
|
|
1175
|
+
const onWarning = vi.fn()
|
|
1176
|
+
const onError = vi.fn()
|
|
1177
|
+
playback.on(Events.PLAYBACK_WARNING, onWarning)
|
|
1178
|
+
playback.on(Events.PLAYBACK_ERROR, onError)
|
|
1179
|
+
|
|
1180
|
+
playback._onHLSJSError('hlsError', {
|
|
1181
|
+
fatal: true,
|
|
1182
|
+
type: HLSJS.ErrorTypes.NETWORK_ERROR,
|
|
1183
|
+
details: HLSJS.ErrorDetails.MANIFEST_LOAD_TIMEOUT,
|
|
1184
|
+
response: { code: 504 }
|
|
1185
|
+
})
|
|
1186
|
+
|
|
1187
|
+
expect(onWarning).not.toHaveBeenCalled()
|
|
1188
|
+
expect(onError).toHaveBeenCalledTimes(1)
|
|
1189
|
+
expect(playback.stop).toHaveBeenCalled()
|
|
1190
|
+
})
|
|
1191
|
+
|
|
1192
|
+
test('does not trigger PLAYBACK_WARNING when non-fatal key denial escalates to fatal', () => {
|
|
1193
|
+
const playback = createErrorPlayback({
|
|
1194
|
+
playback: { triggerFatalErrorOnResourceDenied: true }
|
|
1195
|
+
})
|
|
1196
|
+
vi.spyOn(Log, 'error').mockImplementation(() => {})
|
|
1197
|
+
const onWarning = vi.fn()
|
|
1198
|
+
const onError = vi.fn()
|
|
1199
|
+
playback.on(Events.PLAYBACK_WARNING, onWarning)
|
|
1200
|
+
playback.on(Events.PLAYBACK_ERROR, onError)
|
|
1201
|
+
|
|
1202
|
+
playback._onHLSJSError('hlsError', {
|
|
1203
|
+
fatal: false,
|
|
1204
|
+
type: HLSJS.ErrorTypes.NETWORK_ERROR,
|
|
1205
|
+
details: HLSJS.ErrorDetails.KEY_LOAD_ERROR,
|
|
1206
|
+
response: { code: 403 }
|
|
1207
|
+
})
|
|
1208
|
+
|
|
1209
|
+
expect(onWarning).not.toHaveBeenCalled()
|
|
1210
|
+
expect(onError).toHaveBeenCalledTimes(1)
|
|
1211
|
+
})
|
|
1212
|
+
|
|
1213
|
+
test('triggers PLAYBACK_WARNING for a non-fatal key denial that does not escalate', () => {
|
|
1214
|
+
const playback = createErrorPlayback()
|
|
1215
|
+
vi.spyOn(Log, 'warn').mockImplementation(() => {})
|
|
1216
|
+
const onWarning = vi.fn()
|
|
1217
|
+
playback.on(Events.PLAYBACK_WARNING, onWarning)
|
|
1218
|
+
|
|
1219
|
+
playback._onHLSJSError('hlsError', {
|
|
1220
|
+
fatal: false,
|
|
1221
|
+
type: HLSJS.ErrorTypes.NETWORK_ERROR,
|
|
1222
|
+
details: HLSJS.ErrorDetails.KEY_LOAD_ERROR,
|
|
1223
|
+
response: { code: 403 }
|
|
1224
|
+
})
|
|
1225
|
+
|
|
1226
|
+
expect(onWarning).toHaveBeenCalledTimes(1)
|
|
1227
|
+
expect(onWarning.mock.calls[0][0].level).toBe(PlayerError.Levels.WARN)
|
|
1228
|
+
})
|
|
1113
1229
|
})
|
|
1114
1230
|
|
|
1115
1231
|
describe('dvrEnabled and settings', () => {
|
|
@@ -1152,7 +1268,7 @@ describe('HlsjsPlayback', () => {
|
|
|
1152
1268
|
|
|
1153
1269
|
test('_updateSettings chooses left controls from playback type and dvr', () => {
|
|
1154
1270
|
const playback = createPlayback({ hlsMinimumDvrSize: 60 })
|
|
1155
|
-
const onSettings =
|
|
1271
|
+
const onSettings = vi.fn()
|
|
1156
1272
|
playback.on(Events.PLAYBACK_SETTINGSUPDATE, onSettings)
|
|
1157
1273
|
|
|
1158
1274
|
playback._playbackType = Playback.VOD
|