diff --git a/src/lib/events.js b/src/lib/events.js index 4569e0f82a..d925c2ac1c 100644 --- a/src/lib/events.js +++ b/src/lib/events.js @@ -45,6 +45,7 @@ export const ERROR_CODE = { PREFETCH_FILE: 'error_prefetch_file', RATE_LIMIT: 'error_rate_limit', SHAKA: 'error_shaka', + TOKEN_NOT_VALID: 'error_token_function_not_valid', UNSUPPORTED_FILE_TYPE: 'error_unsupported_file_type', VIEWER_LOAD_TIMEOUT: 'error_viewer_load_timeout', }; diff --git a/src/lib/viewers/media/DashViewer.js b/src/lib/viewers/media/DashViewer.js index eca8cacaa8..136b395e4e 100644 --- a/src/lib/viewers/media/DashViewer.js +++ b/src/lib/viewers/media/DashViewer.js @@ -16,7 +16,6 @@ const MAX_BUFFER = SEGMENT_SIZE * 12; // 60 sec const MANIFEST = 'manifest.mpd'; const DEFAULT_VIDEO_WIDTH_PX = 854; const DEFAULT_VIDEO_HEIGHT_PX = 480; -const MAX_RETRY_TOKEN = 3; // number of times to retry refreshing token for unauthorized error const SHAKA_CODE_ERROR_RECOVERABLE = 1; @@ -41,6 +40,7 @@ class DashViewer extends VideoBaseViewer { this.loadeddataHandler = this.loadeddataHandler.bind(this); this.requestFilter = this.requestFilter.bind(this); this.shakaErrorHandler = this.shakaErrorHandler.bind(this); + this.restartPlayback = this.restartPlayback.bind(this); } /** @@ -485,11 +485,11 @@ class DashViewer extends VideoBaseViewer { * @param {PreviewError} error * @return {bool} */ - isExpiredTokenError = error => { + isExpiredTokenError(error) { const errorDetails = error.details; // unauthorized error may be cuased by token expired return errorDetails.code === shaka.util.Error.Code.BAD_HTTP_STATUS && errorDetails.data[1] === 401; - }; + } /** * Restart playback using new token @@ -498,38 +498,12 @@ class DashViewer extends VideoBaseViewer { * @param {string} newToken - new token * @return {void} */ - restartPlayback = newToken => { + restartPlayback(newToken) { this.options.token = newToken; if (this.player.retryStreaming()) { this.retryTokenCount = 0; } - }; - - /** - * Handle expired token error - * - * @private - * @param {PreviewError} error - * @return {?PreviewError} - */ - handleExpiredTokenError = error => { - if (this.isExpiredTokenError(error)) { - if (this.retryTokenCount >= MAX_RETRY_TOKEN) { - error.details.error_message = 'Reach refreshing token limit for unauthorized error.'; - } else { - this.options - .refreshToken() - .then(this.restartPlayback) - .catch(tokenError => { - error.details.error_message = tokenError.message; - this.triggerError(error); - }); - this.retryTokenCount += 1; - return undefined; - } - } - return error; - }; + } /** * Handles errors thrown by shaka player. See https://shaka-player-demo.appspot.com/docs/api/shaka.util.Error.html @@ -540,7 +514,7 @@ class DashViewer extends VideoBaseViewer { */ shakaErrorHandler(shakaError) { const normalizedShakaError = shakaError.detail ? shakaError.detail : shakaError; - let error = new PreviewError( + const error = new PreviewError( ERROR_CODE.SHAKA, __('error_refresh'), { @@ -561,10 +535,10 @@ class DashViewer extends VideoBaseViewer { return; } - error = this.handleExpiredTokenError(error); + const isHandled = this.handleExpiredTokenError(error); - // critical error - if (error) { + if (!isHandled) { + // critical error this.triggerError(error); } } diff --git a/src/lib/viewers/media/MediaBaseViewer.js b/src/lib/viewers/media/MediaBaseViewer.js index f124ad9894..985a0b297a 100644 --- a/src/lib/viewers/media/MediaBaseViewer.js +++ b/src/lib/viewers/media/MediaBaseViewer.js @@ -281,14 +281,14 @@ class MediaBaseViewer extends BaseViewer { * @param {PreviewError} error * @return {bool} */ - isExpiredTokenError = error => { + isExpiredTokenError(error) { const errorDetails = error.details; return ( !isEmpty(errorDetails) && errorDetails.error_code === MediaError.MEDIA_ERR_NETWORK && errorDetails.error_message.includes(MEDIA_TOKEN_EXPIRE_ERROR) ); - }; + } /** * Restart playback using new token @@ -297,39 +297,50 @@ class MediaBaseViewer extends BaseViewer { * @param {string} newToken - new token * @return {void} */ - restartPlayback = newToken => { + restartPlayback(newToken) { const { currentTime } = this.mediaEl; this.currentTime = currentTime; this.options.token = newToken; this.mediaUrl = this.createContentUrlWithAuthParams(this.options.representation.content.url_template); this.mediaEl.src = this.mediaUrl; - }; + } /** * Handle expired token error * * @protected * @param {PreviewError} error - * @return {?PreviewError} + * @return {boolean} True if it is a token error and is handled */ - handleExpiredTokenError = error => { + handleExpiredTokenError(error) { if (this.isExpiredTokenError(error)) { if (this.retryTokenCount >= MAX_RETRY_TOKEN) { - error.details.error_message += ' Reach refreshing token limit for unauthorized error.'; + const tokenError = new PreviewError( + ERROR_CODE.TOKEN_NOT_VALID, + null, + { silent: true }, + 'Reach refreshing token limit for unauthorized error.', + ); + this.triggerError(tokenError); } else { this.options .refreshToken() .then(this.restartPlayback) - .catch(tokenError => { - error.details.error_message += ` ${tokenError.message}`; - this.triggerError(error); + .catch(e => { + const tokenError = new PreviewError( + ERROR_CODE.TOKEN_NOT_VALID, + null, + { silent: true }, + e.message, + ); + this.triggerError(tokenError); }); this.retryTokenCount += 1; - return undefined; + return true; } } - return error; - }; + return false; + } /** * Handles media element loading errors. @@ -346,10 +357,11 @@ class MediaBaseViewer extends BaseViewer { const errorCode = getProp(err, 'target.error.code'); const errorMessage = getProp(err, 'target.error.message'); const errorDetails = errorCode ? { error_code: errorCode, error_message: errorMessage } : {}; - const error = this.handleExpiredTokenError( - new PreviewError(ERROR_CODE.LOAD_MEDIA, __('error_refresh'), errorDetails), - ); - if (error) { + const error = new PreviewError(ERROR_CODE.LOAD_MEDIA, __('error_refresh'), errorDetails); + + const isHandled = this.handleExpiredTokenError(error); + + if (!isHandled) { if (!this.isLoaded()) { this.handleDownloadError(error, this.mediaUrl); } else { diff --git a/src/lib/viewers/media/__tests__/MediaBaseViewer-test.js b/src/lib/viewers/media/__tests__/MediaBaseViewer-test.js index 586ecbfdb2..abe34fc2b9 100644 --- a/src/lib/viewers/media/__tests__/MediaBaseViewer-test.js +++ b/src/lib/viewers/media/__tests__/MediaBaseViewer-test.js @@ -189,23 +189,21 @@ describe('lib/viewers/media/MediaBaseViewer', () => { }); describe('handleExpiredTokenError()', () => { - it('should return the same error if is not an ExpiredTokenError', () => { + it('should not trigger error if is not an ExpiredTokenError', () => { sandbox.stub(media, 'isExpiredTokenError').returns(false); + sandbox.stub(media, 'triggerError'); const error = new PreviewError(ERROR_CODE.LOAD_MEDIA); - expect(media.handleExpiredTokenError(error)).to.equal(error); + media.handleExpiredTokenError(error); + expect(media.triggerError).to.not.be.called; }); - it('should add error message if retry token count reaches max retry limit', () => { + it('should trigger error if retry token count reaches max retry limit', () => { media.retryTokenCount = MAX_RETRY_TOKEN + 1; sandbox.stub(media, 'isExpiredTokenError').returns(true); - const originalError = 'Original error.'; - const error = new PreviewError(ERROR_CODE.LOAD_MEDIA, 'error_refresh', { - error_message: originalError, - }); - const newError = media.handleExpiredTokenError(error); - expect(newError.details.error_message).to.equal( - `${originalError} Reach refreshing token limit for unauthorized error.`, - ); + sandbox.stub(media, 'triggerError'); + const error = new PreviewError(ERROR_CODE.LOAD_MEDIA); + media.handleExpiredTokenError(error); + expect(media.triggerError).to.be.calledWith(sinon.match.has('code', ERROR_CODE.TOKEN_NOT_VALID)); }); it('should call refreshToken if retry token count did not reach max retry limit', () => { @@ -223,11 +221,9 @@ describe('lib/viewers/media/MediaBaseViewer', () => { describe('errorHandler()', () => { it('should handle download error if the viewer was not yet loaded', () => { const err = new Error(); - const error = new PreviewError(ERROR_CODE.LOAD_MEDIA); media.mediaUrl = 'foo'; sandbox.stub(media, 'isLoaded').returns(false); sandbox.stub(media, 'handleDownloadError'); - sandbox.stub(media, 'handleExpiredTokenError').returns(error); media.errorHandler(err); @@ -236,10 +232,8 @@ describe('lib/viewers/media/MediaBaseViewer', () => { it('should trigger an error if Preview is already loaded', () => { const err = new Error(); - const error = new PreviewError(ERROR_CODE.LOAD_MEDIA); sandbox.stub(media, 'isLoaded').returns(true); sandbox.stub(media, 'triggerError'); - sandbox.stub(media, 'handleExpiredTokenError').returns(error); media.errorHandler(err);