diff --git a/package/spec/frontend/media/MediaPool_spec.js b/package/spec/frontend/media/MediaPool_spec.js index 9dcce53d75..924034e156 100644 --- a/package/spec/frontend/media/MediaPool_spec.js +++ b/package/spec/frontend/media/MediaPool_spec.js @@ -1,6 +1,6 @@ import '$support/mediaElementStub'; import '$support/fakeBrowserFeatures'; -import {MediaPool, MediaType, blankSources} from 'pageflow/frontend'; +import {MediaPool, MediaType, blankSources, events} from 'pageflow/frontend'; describe('MediaPool', function() { it('create an empty pool of audio and video players', function () { @@ -163,6 +163,44 @@ describe('MediaPool', function() { }); }); + describe('media lifecycle events', function() { + beforeEach(() => { + jest.spyOn(events, 'trigger').mockImplementation(() => {}); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('triggers media:allocate on allocation', () => { + const pool = new MediaPool({playerCount: 1}); + + pool.allocatePlayer({ + playerType: MediaType.VIDEO, + mediaEventsContextData: {page: {}} + }); + + expect(lifecycleEvents()).toEqual(['media:allocate']); + }); + + it('triggers media:release before media:allocate when reusing a player', () => { + const pool = new MediaPool({playerCount: 1}); + const context = {page: {}}; + + const first = pool.allocatePlayer({playerType: MediaType.VIDEO, mediaEventsContextData: context}); + const reused = pool.allocatePlayer({playerType: MediaType.VIDEO, mediaEventsContextData: context}); + + expect(reused).toBe(first); + expect(lifecycleEvents()).toEqual(['media:allocate', 'media:release', 'media:allocate']); + }); + + function lifecycleEvents() { + return events.trigger.mock.calls + .map(call => call[0]) + .filter(name => name === 'media:allocate' || name === 'media:release'); + } + }); + describe('#blessAll', function() { it('triggers call to populate pool with players', function () { let pool = new MediaPool(); diff --git a/package/spec/frontend/media/media_spec.js b/package/spec/frontend/media/media_spec.js index bf60d97e5c..6fd6c5a6fc 100644 --- a/package/spec/frontend/media/media_spec.js +++ b/package/spec/frontend/media/media_spec.js @@ -13,6 +13,14 @@ describe('media', function() { ]; describe('#getPlayer', function() { + beforeEach(() => { + jest.spyOn(events, 'trigger').mockImplementation(() => {}); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + it('returns an instance of videojs.Player', function() { let player = media.getPlayer(fileSources, {}); @@ -72,7 +80,6 @@ describe('media', function() { index: 1, } }; - events.trigger = jest.fn(); let player = media.getPlayer(fileSources, { tagName: 'audio', mediaEventsContextData: context @@ -87,8 +94,40 @@ describe('media', function() { ); }); + it('triggers media:allocate event with the media element', () => { + let context = {page: {index: 1}}; + let player = media.getPlayer(fileSources, { + tagName: 'audio', + mediaEventsContextData: context + }); + + expect(events.trigger).toHaveBeenCalledWith( + 'media:allocate', + expect.objectContaining({ + element: player.getMediaElement(), + context: context + }) + ); + }); + + it('triggers media:release event with the media element', () => { + let context = {page: {index: 1}}; + let player = media.getPlayer(fileSources, { + tagName: 'audio', + mediaEventsContextData: context + }); + events.trigger.mockClear(); + media.releasePlayer(player); + + expect(events.trigger).toHaveBeenCalledWith( + 'media:release', + expect.objectContaining({ + element: player.getMediaElement() + }) + ); + }); + it('do not trigger media event when context is undefined', () => { - events.trigger = jest.fn(); let player = media.getPlayer(fileSources, { tagName: 'audio', mediaEventsContextData: undefined diff --git a/package/src/frontend/VideoPlayer/mediaEvents.js b/package/src/frontend/VideoPlayer/mediaEvents.js index 20b1d9f109..bb1146f949 100644 --- a/package/src/frontend/VideoPlayer/mediaEvents.js +++ b/package/src/frontend/VideoPlayer/mediaEvents.js @@ -7,16 +7,20 @@ export const mediaEvents = function(player, context) { context = newContext; } - function triggerMediaEvent(name) { + player.triggerMediaAllocate = function() { + if (context) { + events.trigger('media:allocate', { + ...mediaEventPayload(), + element: player.getMediaElement() + }); + } + } + + player.triggerMediaRelease = function() { if (context) { - events.trigger('media:' + name, { - fileName: player.previousSrc || player.currentSrc(), - context: context, - currentTime: player.currentTime(), - duration: player.duration(), - volume: player.volume(), - altText: player.getMediaElement().getAttribute('alt'), - bitrate: 3500000 + events.trigger('media:release', { + ...mediaEventPayload(), + element: player.getMediaElement() }); } } @@ -49,4 +53,21 @@ export const mediaEvents = function(player, context) { triggerMediaEvent('ended'); }); + function triggerMediaEvent(name) { + if (context) { + events.trigger('media:' + name, mediaEventPayload()); + } + } + + function mediaEventPayload() { + return { + fileName: player.previousSrc || player.currentSrc(), + context: context, + currentTime: player.currentTime(), + duration: player.duration(), + volume: player.volume(), + altText: player.getMediaElement().getAttribute('alt'), + bitrate: 3500000 + }; + } }; diff --git a/package/src/frontend/media/MediaPool.js b/package/src/frontend/media/MediaPool.js index 95e7a700fb..b2900aefda 100644 --- a/package/src/frontend/media/MediaPool.js +++ b/package/src/frontend/media/MediaPool.js @@ -66,6 +66,8 @@ export class MediaPool { player.releaseCallback = onRelease; player.previousSrc = null; + player.triggerMediaAllocate(); + return player; } else{ @@ -77,6 +79,8 @@ export class MediaPool { } unAllocatePlayer(player){ if (player) { + player.triggerMediaRelease(); + let type = this.getMediaTypeFromEl(player.el()); this.allocatedPlayers[type] = this.allocatedPlayers[type].filter(p=>p!=player);