Skip to content
This repository was archived by the owner on Sep 28, 2025. It is now read-only.

Commit 30b30c1

Browse files
Merge pull request #16 from clappr/new-custom-listeners
Custom listeners for HLS.js events
2 parents 1179899 + df52b0f commit 30b30c1

File tree

3 files changed

+145
-1
lines changed

3 files changed

+145
-1
lines changed

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ var player = new Clappr.Player(
4646
hlsRecoverAttempts: 16,
4747
hlsPlayback: {
4848
preload: true,
49+
customListeners: [],
4950
},
5051
playback: {
5152
extrapolatedWindowNumSegments: 2,
@@ -97,6 +98,7 @@ var player = new Clappr.Player(
9798
...
9899
hlsPlayback: {
99100
preload: true,
101+
customListeners: [],
100102
},
101103
});
102104
```
@@ -106,6 +108,30 @@ var player = new Clappr.Player(
106108
107109
Configures whether the source should be loaded as soon as the `HLS.JS` internal reference is setup or only after the first play.
108110

111+
#### `hlsPlayback.customListeners`
112+
113+
An array of listeners object with specific parameters to add on `HLS.JS` instance.
114+
115+
```javascript
116+
var player = new Clappr.Player(
117+
{
118+
...
119+
hlsPlayback: {
120+
...
121+
customListeners: [
122+
// "hlsFragLoaded" is the value of HlsjsPlayback.HLSJS.Events.FRAG_LOADED constant.
123+
{ eventName: 'hlsFragLoaded', callback: (event, data) => { console.log('>>>>>> data: ', data) }, once: true }
124+
],
125+
},
126+
});
127+
```
128+
129+
The listener object parameters are:
130+
131+
* `eventName`: A valid event name of `hls.js` [events API](https://github.com/video-dev/hls.js/blob/master/docs/API.md#runtime-events);
132+
* `callback`: The callback that should be called when the event listened happen.
133+
* `once`: Flag to configure if the listener needs to be valid just for one time.
134+
109135
#### hlsjsConfig
110136

111137
As `HlsjsPlayback` is based on `hls.js`, it's possible to use the available `hls.js` configs too. You can check them out [here](https://github.com/video-dev/hls.js/blob/master/docs/API.md#fine-tuning).

src/hls.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ export default class HlsjsPlayback extends HTML5Video {
113113
return { preload: true }
114114
}
115115

116+
get customListeners() {
117+
return this.options.hlsPlayback && this.options.hlsPlayback.customListeners || []
118+
}
119+
116120
static get HLSJS() {
117121
return HLSJS
118122
}
@@ -179,9 +183,27 @@ export default class HlsjsPlayback extends HTML5Video {
179183
this._hls.on(HLSJS.Events.ERROR, (evt, data) => this._onHLSJSError(evt, data))
180184
this._hls.on(HLSJS.Events.SUBTITLE_TRACK_LOADED, (evt, data) => this._onSubtitleLoaded(evt, data))
181185
this._hls.on(HLSJS.Events.SUBTITLE_TRACKS_UPDATED, () => this._ccTracksUpdated = true)
186+
187+
this.bindCustomListeners()
188+
182189
this._hls.attachMedia(this.el)
183190
}
184191

192+
bindCustomListeners() {
193+
this.customListeners.forEach(item => {
194+
const requestedEventName = item.eventName
195+
const typeOfListener = item.once ? 'once': 'on'
196+
requestedEventName && this._hls[`${typeOfListener}`](requestedEventName, item.callback)
197+
})
198+
}
199+
200+
unbindCustomListeners() {
201+
this.customListeners.forEach(item => {
202+
const requestedEventName = item.eventName
203+
requestedEventName && this._hls.off(requestedEventName, item.callback)
204+
})
205+
}
206+
185207
_onFragmentParsingMetadata(evt, data) {
186208
this.trigger(Events.Custom.PLAYBACK_FRAGMENT_PARSING_METADATA, { evt, data })
187209
}

src/hls.test.js

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,29 @@ import HLSJS from 'hls.js'
55
const simplePlaybackMock = new HlsjsPlayback({ src: 'http://clappr.io/video.m3u8' })
66

77
describe('HlsjsPlayback', () => {
8-
test('have a getter called template', () => {
8+
test('have a getter called defaultOptions', () => {
99
expect(Object.getOwnPropertyDescriptor(Object.getPrototypeOf(simplePlaybackMock), 'defaultOptions').get).toBeTruthy()
1010
})
1111

1212
test('defaultOptions getter returns all the default options values into one object', () => {
1313
expect(simplePlaybackMock.defaultOptions).toEqual({ preload: true })
1414
})
1515

16+
test('have a getter called customListeners', () => {
17+
expect(Object.getOwnPropertyDescriptor(Object.getPrototypeOf(simplePlaybackMock), 'customListeners').get).toBeTruthy()
18+
})
19+
20+
test('customListeners getter returns all configured custom listeners for each hls.js event', () => {
21+
const cb = () => {}
22+
const playback = new HlsjsPlayback({
23+
src: 'http://clappr.io/foo.m3u8',
24+
hlsPlayback: {
25+
customListeners: [{ eventName: 'hlsMediaAttaching', callback: cb }]
26+
}
27+
})
28+
expect(playback.customListeners).toEqual(playback.options.hlsPlayback.customListeners)
29+
})
30+
1631
test('should be able to identify it can play resources independently of the file extension case', () => {
1732
jest.spyOn(HLSJS, 'isSupported').mockImplementation(() => true)
1833
expect(HlsjsPlayback.canPlay('/relative/video.m3u8')).toBeTruthy()
@@ -171,6 +186,14 @@ describe('HlsjsPlayback', () => {
171186

172187
expect(playback._manifestParsed).toBeTruthy()
173188
})
189+
190+
test('calls bindCustomListeners method', () => {
191+
const playback = new HlsjsPlayback({ src: 'http://clappr.io/foo.m3u8' })
192+
jest.spyOn(playback, 'bindCustomListeners')
193+
playback._setup()
194+
195+
expect(playback.bindCustomListeners).toHaveBeenCalledTimes(1)
196+
})
174197
})
175198

176199
describe('_ready method', () => {
@@ -238,4 +261,77 @@ describe('HlsjsPlayback', () => {
238261
expect(playback._hls.loadSource).toHaveBeenCalledTimes(1)
239262
})
240263
})
264+
265+
describe('bindCustomListeners method', () => {
266+
test('creates listeners for each item configured on customListeners array', () => {
267+
const cb = jest.fn()
268+
const playback = new HlsjsPlayback({
269+
src: 'http://clappr.io/foo.m3u8',
270+
hlsPlayback: {
271+
customListeners: [{ eventName: HLSJS.Events.MEDIA_ATTACHING, callback: cb }]
272+
}
273+
})
274+
playback._setup()
275+
276+
expect(cb).toHaveBeenCalledTimes(1)
277+
278+
playback._hls.trigger(HLSJS.Events.MEDIA_ATTACHING)
279+
280+
expect(cb).toHaveBeenCalledTimes(2)
281+
})
282+
283+
test('don\'t add one listener without a valid configuration', () => {
284+
const cb = jest.fn()
285+
const playback = new HlsjsPlayback({ src: 'http://clappr.io/foo.m3u8' })
286+
playback._setup()
287+
288+
expect(cb).not.toHaveBeenCalled()
289+
290+
playback.options.hlsPlayback = {}
291+
292+
expect(cb).not.toHaveBeenCalled()
293+
294+
playback.options.hlsPlayback.customListeners = []
295+
296+
expect(cb).not.toHaveBeenCalled()
297+
298+
playback.options.hlsPlayback.customListeners.push([{ eventName: 'invalid_name', callback: cb }])
299+
300+
expect(cb).not.toHaveBeenCalled()
301+
})
302+
303+
test('adds a listener for one time when the customListeners array item is configured with the "once" param', () => {
304+
const cb = jest.fn()
305+
const playback = new HlsjsPlayback({
306+
src: 'http://clappr.io/foo.m3u8',
307+
hlsPlayback: {
308+
customListeners: [{ eventName: HLSJS.Events.MEDIA_ATTACHING, callback: cb, once: true }]
309+
}
310+
})
311+
playback._setup()
312+
313+
expect(cb).toHaveBeenCalledTimes(1)
314+
315+
playback._hls.trigger(HLSJS.Events.MEDIA_ATTACHING)
316+
317+
expect(cb).toHaveBeenCalledTimes(1)
318+
})
319+
})
320+
321+
describe('unbindCustomListeners method', () => {
322+
test('remove listeners for each item configured on customListeners array', () => {
323+
const cb = jest.fn()
324+
const playback = new HlsjsPlayback({
325+
src: 'http://clappr.io/foo.m3u8',
326+
hlsPlayback: {
327+
customListeners: [{ eventName: 'hlsFragLoaded', callback: cb }]
328+
}
329+
})
330+
playback._setup()
331+
playback.unbindCustomListeners()
332+
playback._hls.trigger(HLSJS.Events.FRAG_LOADED)
333+
334+
expect(cb).not.toHaveBeenCalled()
335+
})
336+
})
241337
})

0 commit comments

Comments
 (0)