Skip to content

Commit

Permalink
Merge pull request #16 from clappr/new-custom-listeners
Browse files Browse the repository at this point in the history
Custom listeners for HLS.js events
  • Loading branch information
joaopaulovieira authored May 14, 2021
2 parents 1179899 + df52b0f commit 30b30c1
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 1 deletion.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ var player = new Clappr.Player(
hlsRecoverAttempts: 16,
hlsPlayback: {
preload: true,
customListeners: [],
},
playback: {
extrapolatedWindowNumSegments: 2,
Expand Down Expand Up @@ -97,6 +98,7 @@ var player = new Clappr.Player(
...
hlsPlayback: {
preload: true,
customListeners: [],
},
});
```
Expand All @@ -106,6 +108,30 @@ var player = new Clappr.Player(
Configures whether the source should be loaded as soon as the `HLS.JS` internal reference is setup or only after the first play.

#### `hlsPlayback.customListeners`

An array of listeners object with specific parameters to add on `HLS.JS` instance.

```javascript
var player = new Clappr.Player(
{
...
hlsPlayback: {
...
customListeners: [
// "hlsFragLoaded" is the value of HlsjsPlayback.HLSJS.Events.FRAG_LOADED constant.
{ eventName: 'hlsFragLoaded', callback: (event, data) => { console.log('>>>>>> data: ', data) }, once: true }
],
},
});
```

The listener object parameters are:

* `eventName`: A valid event name of `hls.js` [events API](https://github.com/video-dev/hls.js/blob/master/docs/API.md#runtime-events);
* `callback`: The callback that should be called when the event listened happen.
* `once`: Flag to configure if the listener needs to be valid just for one time.

#### hlsjsConfig

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).
Expand Down
22 changes: 22 additions & 0 deletions src/hls.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ export default class HlsjsPlayback extends HTML5Video {
return { preload: true }
}

get customListeners() {
return this.options.hlsPlayback && this.options.hlsPlayback.customListeners || []
}

static get HLSJS() {
return HLSJS
}
Expand Down Expand Up @@ -179,9 +183,27 @@ export default class HlsjsPlayback extends HTML5Video {
this._hls.on(HLSJS.Events.ERROR, (evt, data) => this._onHLSJSError(evt, data))
this._hls.on(HLSJS.Events.SUBTITLE_TRACK_LOADED, (evt, data) => this._onSubtitleLoaded(evt, data))
this._hls.on(HLSJS.Events.SUBTITLE_TRACKS_UPDATED, () => this._ccTracksUpdated = true)

this.bindCustomListeners()

this._hls.attachMedia(this.el)
}

bindCustomListeners() {
this.customListeners.forEach(item => {
const requestedEventName = item.eventName
const typeOfListener = item.once ? 'once': 'on'
requestedEventName && this._hls[`${typeOfListener}`](requestedEventName, item.callback)
})
}

unbindCustomListeners() {
this.customListeners.forEach(item => {
const requestedEventName = item.eventName
requestedEventName && this._hls.off(requestedEventName, item.callback)
})
}

_onFragmentParsingMetadata(evt, data) {
this.trigger(Events.Custom.PLAYBACK_FRAGMENT_PARSING_METADATA, { evt, data })
}
Expand Down
98 changes: 97 additions & 1 deletion src/hls.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,29 @@ import HLSJS from 'hls.js'
const simplePlaybackMock = new HlsjsPlayback({ src: 'http://clappr.io/video.m3u8' })

describe('HlsjsPlayback', () => {
test('have a getter called template', () => {
test('have a getter called defaultOptions', () => {
expect(Object.getOwnPropertyDescriptor(Object.getPrototypeOf(simplePlaybackMock), 'defaultOptions').get).toBeTruthy()
})

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

test('have a getter called customListeners', () => {
expect(Object.getOwnPropertyDescriptor(Object.getPrototypeOf(simplePlaybackMock), 'customListeners').get).toBeTruthy()
})

test('customListeners getter returns all configured custom listeners for each hls.js event', () => {
const cb = () => {}
const playback = new HlsjsPlayback({
src: 'http://clappr.io/foo.m3u8',
hlsPlayback: {
customListeners: [{ eventName: 'hlsMediaAttaching', callback: cb }]
}
})
expect(playback.customListeners).toEqual(playback.options.hlsPlayback.customListeners)
})

test('should be able to identify it can play resources independently of the file extension case', () => {
jest.spyOn(HLSJS, 'isSupported').mockImplementation(() => true)
expect(HlsjsPlayback.canPlay('/relative/video.m3u8')).toBeTruthy()
Expand Down Expand Up @@ -171,6 +186,14 @@ describe('HlsjsPlayback', () => {

expect(playback._manifestParsed).toBeTruthy()
})

test('calls bindCustomListeners method', () => {
const playback = new HlsjsPlayback({ src: 'http://clappr.io/foo.m3u8' })
jest.spyOn(playback, 'bindCustomListeners')
playback._setup()

expect(playback.bindCustomListeners).toHaveBeenCalledTimes(1)
})
})

describe('_ready method', () => {
Expand Down Expand Up @@ -238,4 +261,77 @@ describe('HlsjsPlayback', () => {
expect(playback._hls.loadSource).toHaveBeenCalledTimes(1)
})
})

describe('bindCustomListeners method', () => {
test('creates listeners for each item configured on customListeners array', () => {
const cb = jest.fn()
const playback = new HlsjsPlayback({
src: 'http://clappr.io/foo.m3u8',
hlsPlayback: {
customListeners: [{ eventName: HLSJS.Events.MEDIA_ATTACHING, callback: cb }]
}
})
playback._setup()

expect(cb).toHaveBeenCalledTimes(1)

playback._hls.trigger(HLSJS.Events.MEDIA_ATTACHING)

expect(cb).toHaveBeenCalledTimes(2)
})

test('don\'t add one listener without a valid configuration', () => {
const cb = jest.fn()
const playback = new HlsjsPlayback({ src: 'http://clappr.io/foo.m3u8' })
playback._setup()

expect(cb).not.toHaveBeenCalled()

playback.options.hlsPlayback = {}

expect(cb).not.toHaveBeenCalled()

playback.options.hlsPlayback.customListeners = []

expect(cb).not.toHaveBeenCalled()

playback.options.hlsPlayback.customListeners.push([{ eventName: 'invalid_name', callback: cb }])

expect(cb).not.toHaveBeenCalled()
})

test('adds a listener for one time when the customListeners array item is configured with the "once" param', () => {
const cb = jest.fn()
const playback = new HlsjsPlayback({
src: 'http://clappr.io/foo.m3u8',
hlsPlayback: {
customListeners: [{ eventName: HLSJS.Events.MEDIA_ATTACHING, callback: cb, once: true }]
}
})
playback._setup()

expect(cb).toHaveBeenCalledTimes(1)

playback._hls.trigger(HLSJS.Events.MEDIA_ATTACHING)

expect(cb).toHaveBeenCalledTimes(1)
})
})

describe('unbindCustomListeners method', () => {
test('remove listeners for each item configured on customListeners array', () => {
const cb = jest.fn()
const playback = new HlsjsPlayback({
src: 'http://clappr.io/foo.m3u8',
hlsPlayback: {
customListeners: [{ eventName: 'hlsFragLoaded', callback: cb }]
}
})
playback._setup()
playback.unbindCustomListeners()
playback._hls.trigger(HLSJS.Events.FRAG_LOADED)

expect(cb).not.toHaveBeenCalled()
})
})
})

0 comments on commit 30b30c1

Please sign in to comment.