diff --git a/src/plugins/htmlVideoPlayer/plugin.js b/src/plugins/htmlVideoPlayer/plugin.js
index cc312bb956..2c9de26897 100644
--- a/src/plugins/htmlVideoPlayer/plugin.js
+++ b/src/plugins/htmlVideoPlayer/plugin.js
@@ -1,11 +1,24 @@
-define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackManager', 'appRouter', 'appSettings', 'connectionManager', 'htmlMediaHelper', 'itemHelper', 'screenfull', 'globalize'], function (browser, require, events, appHost, loading, dom, playbackManager, appRouter, appSettings, connectionManager, htmlMediaHelper, itemHelper, screenfull, globalize) {
- 'use strict';
+import browser from "browser";
+import events from "events";
+import appHost from "apphost";
+import loading from "loading";
+import dom from "dom";
+import playbackManager from "playbackManager";
+import appRouter from "appRouter";
+import connectionManager from "connectionManager";
+import htmlMediaHelper from "htmlMediaHelper";
+import itemHelper from "itemHelper";
+import screenfull from "screenfull";
+import globalize from "globalize";
+
+/* eslint-disable indent */
+
/* globals cast */
- var mediaManager;
+let mediaManager;
- function tryRemoveElement(elem) {
- var parentNode = elem.parentNode;
+function tryRemoveElement(elem) {
+ const parentNode = elem.parentNode;
if (parentNode) {
// Seeing crashes in edge webview
@@ -17,8 +30,9 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
}
}
- var _supportsTextTracks;
- function supportsTextTracks() {
+let _supportsTextTracks;
+
+function supportsTextTracks() {
if (_supportsTextTracks == null) {
_supportsTextTracks = document.createElement('video').textTracks != null;
@@ -78,7 +92,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
}
if (track) {
- var format = (track.Codec || '').toLowerCase();
+ const format = (track.Codec || "").toLowerCase();
if (format === 'ssa' || format === 'ass') {
return false;
}
@@ -115,7 +129,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
function zoomIn(elem) {
return new Promise(function (resolve, reject) {
- var duration = 240;
+ const duration = 240;
elem.style.animation = 'htmlvideoplayer-zoomin ' + duration + 'ms ease-in normal';
hidePrePlaybackPage();
dom.addEventListener(elem, dom.whichAnimationEvent(), resolve, {
@@ -125,7 +139,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
}
function normalizeTrackEventText(text, useHtml) {
- var result = text.replace(/\\N/gi, '\n').replace(/\r/gi, '');
+ const result = text.replace(/\\N/gi, "\n").replace(/\r/gi, "");
return useHtml ? result.replace(/\n/gi, '
') : result;
}
@@ -140,7 +154,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
return track.Path;
}
- var url = playbackManager.getSubtitleUrl(track, item.ServerId);
+ let url = playbackManager.getSubtitleUrl(track, item.ServerId);
if (format) {
url = url.replace('.vtt', format);
}
@@ -155,99 +169,100 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
return '';
}
- var defaultAttribute = mediaSource.DefaultSubtitleStreamIndex === t.Index ? ' default' : '';
+ const defaultAttribute = mediaSource.DefaultSubtitleStreamIndex === t.Index ? " default" : "";
- var language = t.Language || 'und';
- var label = t.Language || 'und';
+ const language = t.Language || "und";
+ const label = t.Language || "und";
return '';
}).join('');
}
function getDefaultProfile() {
+ return import("browserdeviceprofile").then(profileBuilder => {
- return new Promise(function (resolve, reject) {
-
- require(['browserdeviceprofile'], function (profileBuilder) {
-
- resolve(profileBuilder({}));
- });
+ return profileBuilder({});
});
}
- function HtmlVideoPlayer() {
-
- if (browser.edgeUwp) {
- this.name = 'Windows Video Player';
- } else {
- this.name = 'Html Video Player';
- }
-
- this.type = 'mediaplayer';
- this.id = 'htmlvideoplayer';
-
- // Let any players created by plugins take priority
- this.priority = 1;
-
- var videoDialog;
-
- var winJsPlaybackItem;
-
- var subtitleTrackIndexToSetOnPlaying;
- var audioTrackIndexToSetOnPlaying;
-
- var lastCustomTrackMs = 0;
- var currentClock;
- var currentSubtitlesOctopus;
- var currentAssRenderer;
- var customTrackIndex = -1;
-
- var showTrackOffset;
- var currentTrackOffset;
-
- var videoSubtitlesElem;
- var currentTrackEvents;
-
- var self = this;
-
- self.currentSrc = function () {
- return self._currentSrc;
- };
-
- self._fetchQueue = 0;
- self.isFetching = false;
-
- function incrementFetchQueue() {
- if (self._fetchQueue <= 0) {
- self.isFetching = true;
- events.trigger(self, 'beginFetch');
+ /**
+ * Private:
+ * - videoDialog
+ * - winJsPlaybackItem
+ * - subtitleTrackIndexToSetOnPlaying
+ * - audioTrackIndexToSetOnPlaying
+ * - lastCustomTrackMs
+ * - currentClock
+ * - currentSubtitlesOctopus
+ * - currentAssRenderer
+ * - customTrackIndex
+ * - showTrackOffset
+ * - currentTrackOffset
+ * - videoSubtitlesElem
+ * - currentTrackEvents
+ * - supportedFeatures
+ */
+ export class HtmlVideoPlayer {
+ constructor() {
+ if (browser.edgeUwp) {
+ this.name = 'Windows Video Player';
+ } else {
+ this.name = 'Html Video Player';
}
- self._fetchQueue++;
+ this.type = 'mediaplayer';
+ this.id = 'htmlvideoplayer';
+
+ // Let any players created by plugins take priority
+ this.priority = 1;
+
+ this._fetchQueue = 0;
+ this.isFetching = false;
}
- function decrementFetchQueue() {
- self._fetchQueue--;
+ currentSrc() {
+ return this._currentSrc;
+ }
- if (self._fetchQueue <= 0) {
- self.isFetching = false;
- events.trigger(self, 'endFetch');
+ /**
+ * @private
+ */
+ incrementFetchQueue() {
+ if (this._fetchQueue <= 0) {
+ this.isFetching = true;
+ events.trigger(this, "beginFetch");
+ }
+
+ this._fetchQueue++;
+ }
+
+ /**
+ * @private
+ */
+ decrementFetchQueue() {
+ this._fetchQueue--;
+
+ if (this._fetchQueue <= 0) {
+ this.isFetching = false;
+ events.trigger(this, "endFetch");
}
}
- function updateVideoUrl(streamInfo) {
+ /**
+ * @private
+ */
+ updateVideoUrl(streamInfo) {
+ const isHls = streamInfo.url.toLowerCase().indexOf(".m3u8") !== -1;
- var isHls = streamInfo.url.toLowerCase().indexOf('.m3u8') !== -1;
-
- var mediaSource = streamInfo.mediaSource;
- var item = streamInfo.item;
+ const mediaSource = streamInfo.mediaSource;
+ const item = streamInfo.item;
// Huge hack alert. Safari doesn't seem to like if the segments aren't available right away when playback starts
// This will start the transcoding process before actually feeding the video url into the player
// Edit: Also seeing stalls from hls.js
if (mediaSource && item && !mediaSource.RunTimeTicks && isHls && streamInfo.playMethod === 'Transcode' && (browser.iOS || browser.osx)) {
- var hlsPlaylistUrl = streamInfo.url.replace('master.m3u8', 'live.m3u8');
+ const hlsPlaylistUrl = streamInfo.url.replace("master.m3u8", "live.m3u8");
loading.show();
@@ -259,20 +274,14 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
url: hlsPlaylistUrl
}).then(function () {
-
console.debug('completed prefetching hls playlist: ' + hlsPlaylistUrl);
loading.hide();
streamInfo.url = hlsPlaylistUrl;
-
- return Promise.resolve();
-
}, function () {
-
console.error('error prefetching hls playlist: ' + hlsPlaylistUrl);
loading.hide();
- return Promise.resolve();
});
} else {
@@ -280,58 +289,58 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
}
}
- self.play = function (options) {
- self._started = false;
- self._timeUpdated = false;
+ play(options) {
+ this._started = false;
+ this._timeUpdated = false;
- self._currentTime = null;
+ this._currentTime = null;
- self.resetSubtitleOffset();
+ this.resetSubtitleOffset();
- return createMediaElement(options).then(function (elem) {
-
- return updateVideoUrl(options).then(function () {
- return setCurrentSrc(elem, options);
- });
- });
- };
-
- function setSrcWithFlvJs(instance, elem, options, url) {
-
- return new Promise(function (resolve, reject) {
-
- require(['flvjs'], function (flvjs) {
-
- var flvPlayer = flvjs.createPlayer({
- type: 'flv',
- url: url
- },
- {
- seekType: 'range',
- lazyLoad: false
- });
-
- flvPlayer.attachMediaElement(elem);
- flvPlayer.load();
-
- flvPlayer.play().then(resolve, reject);
- instance._flvPlayer = flvPlayer;
-
- // This is needed in setCurrentTrackElement
- self._currentSrc = url;
+ return this.createMediaElement(options).then(elem => {
+ return this.updateVideoUrl(options).then(() => {
+ return this.setCurrentSrc(elem, options);
});
});
}
- function setSrcWithHlsJs(instance, elem, options, url) {
+ /**
+ * @private
+ */
+ setSrcWithFlvJs(instance, elem, options, url) {
+ return import('flvjs').then(flvjs => {
+ const flvPlayer = flvjs.createPlayer({
+ type: "flv",
+ url: url
+ },
+ {
+ seekType: "range",
+ lazyLoad: false
+ });
+
+ flvPlayer.attachMediaElement(elem);
+ flvPlayer.load();
+
+ instance._flvPlayer = flvPlayer;
+
+ // This is needed in setCurrentTrackElement
+ this._currentSrc = url;
+
+ return flvPlayer.play();
+ });
+ }
+
+ /**
+ * @private
+ */
+ setSrcWithHlsJs(instance, elem, options, url) {
return new Promise(function (resolve, reject) {
- requireHlsPlayer(function () {
-
- var hls = new Hls({
+ requireHlsPlayer(() => {
+ const hls = new Hls({
manifestLoadingTimeOut: 20000,
- xhrSetup: function(xhr, xhr_url) {
+ xhrSetup(xhr, xhr_url) {
xhr.withCredentials = true;
}
//appendErrorMaxRetry: 6,
@@ -340,70 +349,70 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
hls.loadSource(url);
hls.attachMedia(elem);
- htmlMediaHelper.bindEventsToHlsPlayer(self, hls, elem, onError, resolve, reject);
+ htmlMediaHelper.bindEventsToHlsPlayer(this, hls, elem, this.onError.bind(this), resolve, reject);
- self._hlsPlayer = hls;
+ this._hlsPlayer = hls;
// This is needed in setCurrentTrackElement
- self._currentSrc = url;
+ this._currentSrc = url;
});
});
}
- function onShakaError(event) {
+ /**
+ * @private
+ */
+ onShakaError(event) {
- var error = event.detail;
+ const error = event.detail;
console.error('Error code', error.code, 'object', error);
}
- function setSrcWithShakaPlayer(instance, elem, options, url) {
+ /**
+ * @private
+ */
+ setSrcWithShakaPlayer(instance, elem, options, url) {
+ return import('shaka').then(() => {
+ /* globals shaka */
- return new Promise(function (resolve, reject) {
+ const player = new shaka.Player(elem);
- require(['shaka'], function () {
- /* globals shaka */
+ //player.configure({
+ // abr: {
+ // enabled: false
+ // },
+ // streaming: {
- var player = new shaka.Player(elem);
+ // failureCallback: function () {
+ // alert(2);
+ // }
+ // }
+ //});
- //player.configure({
- // abr: {
- // enabled: false
- // },
- // streaming: {
+ //shaka.log.setLevel(6);
- // failureCallback: function () {
- // alert(2);
- // }
- // }
- //});
+ // Listen for error events.
+ player.addEventListener('error', this.onShakaError.bind(this));
- //shaka.log.setLevel(6);
+ self._shakaPlayer = player;
- // Listen for error events.
- player.addEventListener('error', onShakaError);
+ // This is needed in setCurrentTrackElement
+ self._currentSrc = url;
- // Try to load a manifest.
- // This is an asynchronous process.
- player.load(url).then(function () {
-
- // This runs if the asynchronous load is successful.
- resolve();
-
- }, reject);
-
- self._shakaPlayer = player;
-
- // This is needed in setCurrentTrackElement
- self._currentSrc = url;
- });
+ // Try to load a manifest.
+ // This is an asynchronous process.
+ return player.load(url);
});
}
- function setCurrentSrcChromecast(instance, elem, options, url) {
+ /**
+ * @private
+ */
+ setCurrentSrcChromecast(instance, elem, options, url) {
elem.autoplay = true;
- var lrd = new cast.receiver.MediaManager.LoadRequestData();
+ const lrd = new cast.receiver.MediaManager.LoadRequestData();
lrd.currentTime = (options.playerStartPositionTicks || 0) / 10000000;
lrd.autoplay = true;
lrd.media = new cast.receiver.media.MediaInformation();
@@ -418,7 +427,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
try {
mediaManager.load(lrd);
// This is needed in setCurrentTrackElement
- self._currentSrc = url;
+ this._currentSrc = url;
return Promise.resolve();
} catch (err) {
@@ -428,29 +437,32 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
}
}
- // Adapted from : https://github.com/googlecast/CastReferencePlayer/blob/master/player.js
- function onMediaManagerLoadMedia(event) {
+ /**
+ * Adapted from : https://github.com/googlecast/CastReferencePlayer/blob/master/player.js
+ * @private
+ */
+ onMediaManagerLoadMedia(event) {
- if (self._castPlayer) {
- self._castPlayer.unload(); // Must unload before starting again.
+ if (this._castPlayer) {
+ this._castPlayer.unload(); // Must unload before starting again.
}
- self._castPlayer = null;
+ this._castPlayer = null;
- var data = event.data;
+ const data = event.data;
- var media = event.data.media || {};
- var url = media.contentId;
- var contentType = media.contentType.toLowerCase();
- var options = media.customData;
+ const media = event.data.media || {};
+ const url = media.contentId;
+ const contentType = media.contentType.toLowerCase();
+ const options = media.customData;
- var protocol;
- var ext = 'm3u8';
+ let protocol;
+ const ext = "m3u8";
- var mediaElement = self._mediaElement;
+ const mediaElement = this._mediaElement;
- var host = new cast.player.api.Host({
- 'url': url,
- 'mediaElement': mediaElement
+ const host = new cast.player.api.Host({
+ "url": url,
+ "mediaElement": mediaElement
});
if (ext === 'm3u8' ||
@@ -474,17 +486,19 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
mediaElement.autoplay = false;
- self._castPlayer = new cast.player.api.Player(host);
+ this._castPlayer = new cast.player.api.Player(host);
- self._castPlayer.load(protocol, data.currentTime || 0);
+ this._castPlayer.load(protocol, data.currentTime || 0);
- self._castPlayer.playWhenHaveEnoughData();
+ this._castPlayer.playWhenHaveEnoughData();
}
- function initMediaManager() {
-
+ /**
+ * @private
+ */
+ initMediaManager() {
mediaManager.defaultOnLoad = mediaManager.onLoad.bind(mediaManager);
- mediaManager.onLoad = onMediaManagerLoadMedia.bind(self);
+ mediaManager.onLoad = this.onMediaManagerLoadMedia.bind(this);
//mediaManager.defaultOnPlay = mediaManager.onPlay.bind(mediaManager);
//mediaManager.onPlay = function (event) {
@@ -499,38 +513,40 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
};
}
- function setCurrentSrc(elem, options) {
+ /**
+ * @private
+ */
+ setCurrentSrc(elem, options) {
+ elem.removeEventListener('error', this.onError.bind(this));
- elem.removeEventListener('error', onError);
-
- var val = options.url;
+ let val = options.url;
console.debug('playing url: ' + val);
// Convert to seconds
- var seconds = (options.playerStartPositionTicks || 0) / 10000000;
+ const seconds = (options.playerStartPositionTicks || 0) / 10000000;
if (seconds) {
val += '#t=' + seconds;
}
- htmlMediaHelper.destroyHlsPlayer(self);
- htmlMediaHelper.destroyFlvPlayer(self);
- htmlMediaHelper.destroyCastPlayer(self);
+ htmlMediaHelper.destroyHlsPlayer(this);
+ htmlMediaHelper.destroyFlvPlayer(this);
+ htmlMediaHelper.destroyCastPlayer(this);
- var tracks = getMediaStreamTextTracks(options.mediaSource);
+ const tracks = getMediaStreamTextTracks(options.mediaSource);
- subtitleTrackIndexToSetOnPlaying = options.mediaSource.DefaultSubtitleStreamIndex == null ? -1 : options.mediaSource.DefaultSubtitleStreamIndex;
- if (subtitleTrackIndexToSetOnPlaying != null && subtitleTrackIndexToSetOnPlaying >= 0) {
- var initialSubtitleStream = options.mediaSource.MediaStreams[subtitleTrackIndexToSetOnPlaying];
+ this.subtitleTrackIndexToSetOnPlaying = options.mediaSource.DefaultSubtitleStreamIndex == null ? -1 : options.mediaSource.DefaultSubtitleStreamIndex;
+ if (this.subtitleTrackIndexToSetOnPlaying != null && this.subtitleTrackIndexToSetOnPlaying >= 0) {
+ const initialSubtitleStream = options.mediaSource.MediaStreams[this.subtitleTrackIndexToSetOnPlaying];
if (!initialSubtitleStream || initialSubtitleStream.DeliveryMethod === 'Encode') {
- subtitleTrackIndexToSetOnPlaying = -1;
+ this.subtitleTrackIndexToSetOnPlaying = -1;
}
}
- audioTrackIndexToSetOnPlaying = options.playMethod === 'Transcode' ? null : options.mediaSource.DefaultAudioStreamIndex;
+ this.audioTrackIndexToSetOnPlaying = options.playMethod === 'Transcode' ? null : options.mediaSource.DefaultAudioStreamIndex;
- self._currentPlayOptions = options;
+ this._currentPlayOptions = options;
- var crossOrigin = htmlMediaHelper.getCrossOriginValue(options.mediaSource);
+ const crossOrigin = htmlMediaHelper.getCrossOriginValue(options.mediaSource);
if (crossOrigin) {
elem.crossOrigin = crossOrigin;
}
@@ -541,58 +557,57 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
return setSrcWithShakaPlayer(self, elem, options, val);
- } else*/ if (browser.chromecast && val.indexOf('.m3u8') !== -1 && options.mediaSource.RunTimeTicks) {
+ } else*/
+ if (browser.chromecast && val.indexOf('.m3u8') !== -1 && options.mediaSource.RunTimeTicks) {
- return setCurrentSrcChromecast(self, elem, options, val);
+ return this.setCurrentSrcChromecast(this, elem, options, val);
} else if (htmlMediaHelper.enableHlsJsPlayer(options.mediaSource.RunTimeTicks, 'Video') && val.indexOf('.m3u8') !== -1) {
- return setSrcWithHlsJs(self, elem, options, val);
+ return this.setSrcWithHlsJs(this, elem, options, val);
} else if (options.playMethod !== 'Transcode' && options.mediaSource.Container === 'flv') {
-
- return setSrcWithFlvJs(self, elem, options, val);
-
+ return this.setSrcWithFlvJs(this, elem, options, val);
} else {
-
elem.autoplay = true;
// Safari will not send cookies without this
elem.crossOrigin = 'use-credentials';
- return htmlMediaHelper.applySrc(elem, val, options).then(function () {
+ return htmlMediaHelper.applySrc(elem, val, options).then(() => {
+ this._currentSrc = val;
- self._currentSrc = val;
-
- return htmlMediaHelper.playWithPromise(elem, onError);
+ return htmlMediaHelper.playWithPromise(elem, this.onError.bind(this));
});
}
}
- self.setSubtitleStreamIndex = function (index) {
+ setSubtitleStreamIndex(index) {
+ this.setCurrentTrackElement(index);
+ }
- setCurrentTrackElement(index);
- };
+ resetSubtitleOffset() {
+ this.currentTrackOffset = 0;
+ this.showTrackOffset = false;
+ }
- self.resetSubtitleOffset = function() {
- currentTrackOffset = 0;
- showTrackOffset = false;
- };
+ enableShowingSubtitleOffset() {
+ this.showTrackOffset = true;
+ }
- self.enableShowingSubtitleOffset = function() {
- showTrackOffset = true;
- };
+ disableShowingSubtitleOffset() {
+ this.showTrackOffset = false;
+ }
- self.disableShowingSubtitleOffset = function() {
- showTrackOffset = false;
- };
+ isShowingSubtitleOffsetEnabled() {
+ return this.showTrackOffset;
+ }
- self.isShowingSubtitleOffsetEnabled = function() {
- return showTrackOffset;
- };
-
- function getTextTrack() {
- var videoElement = self._mediaElement;
+ /**
+ * @private
+ */
+ getTextTrack() {
+ const videoElement = this._mediaElement;
if (videoElement) {
return Array.from(videoElement.textTracks)
- .find(function(trackElement) {
+ .find(function (trackElement) {
// get showing .vtt textTack
return trackElement.mode === 'showing';
});
@@ -601,69 +616,80 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
}
}
- self.setSubtitleOffset = function(offset) {
-
- var offsetValue = parseFloat(offset);
+ /**
+ * @private
+ */
+ setSubtitleOffset(offset) {
+ const offsetValue = parseFloat(offset);
// if .ass currently rendering
- if (currentSubtitlesOctopus) {
- updateCurrentTrackOffset(offsetValue);
- currentSubtitlesOctopus.timeOffset = (self._currentPlayOptions.transcodingOffsetTicks || 0) / 10000000 + offsetValue;
+ if (this.currentSubtitlesOctopus) {
+ this.updateCurrentTrackOffset(offsetValue);
+ this.currentSubtitlesOctopus.timeOffset = (this._currentPlayOptions.transcodingOffsetTicks || 0) / 10000000 + offsetValue;;
} else {
- var trackElement = getTextTrack();
+ const trackElement = this.getTextTrack();
// if .vtt currently rendering
if (trackElement) {
- setTextTrackSubtitleOffset(trackElement, offsetValue);
- } else if (currentTrackEvents) {
- setTrackEventsSubtitleOffset(currentTrackEvents, offsetValue);
+ this.setTextTrackSubtitleOffset(trackElement, offsetValue);
+ } else if (this.currentTrackEvents) {
+ this.setTrackEventsSubtitleOffset(this.currentTrackEvents, offsetValue);
} else {
console.debug('No available track, cannot apply offset: ', offsetValue);
}
}
- };
+ }
- function updateCurrentTrackOffset(offsetValue) {
-
- var relativeOffset = offsetValue;
- var newTrackOffset = offsetValue;
- if (currentTrackOffset) {
- relativeOffset -= currentTrackOffset;
+ /**
+ * @private
+ */
+ updateCurrentTrackOffset(offsetValue) {
+ let relativeOffset = offsetValue;
+ const newTrackOffset = offsetValue;
+ if (this.currentTrackOffset) {
+ relativeOffset -= this.currentTrackOffset;
}
- currentTrackOffset = newTrackOffset;
+ this.currentTrackOffset = newTrackOffset;
// relative to currentTrackOffset
return relativeOffset;
}
- function setTextTrackSubtitleOffset(currentTrack, offsetValue) {
-
+ /**
+ * @private
+ */
+ setTextTrackSubtitleOffset(currentTrack, offsetValue) {
if (currentTrack.cues) {
- offsetValue = updateCurrentTrackOffset(offsetValue);
+ offsetValue = this.updateCurrentTrackOffset(offsetValue);
Array.from(currentTrack.cues)
- .forEach(function(cue) {
+ .forEach(function (cue) {
cue.startTime -= offsetValue;
cue.endTime -= offsetValue;
});
}
}
- function setTrackEventsSubtitleOffset(trackEvents, offsetValue) {
-
+ /**
+ * @private
+ */
+ setTrackEventsSubtitleOffset(trackEvents, offsetValue) {
if (Array.isArray(trackEvents)) {
- offsetValue = updateCurrentTrackOffset(offsetValue) * 1e7; // ticks
- trackEvents.forEach(function(trackEvent) {
+ offsetValue = this.updateCurrentTrackOffset(offsetValue) * 1e7; // ticks
+ trackEvents.forEach(function (trackEvent) {
trackEvent.StartPositionTicks -= offsetValue;
trackEvent.EndPositionTicks -= offsetValue;
});
}
}
- self.getSubtitleOffset = function() {
- return currentTrackOffset;
- };
+ getSubtitleOffset() {
+ return this.currentTrackOffset;
+ }
- function isAudioStreamSupported(stream, deviceProfile) {
+ /**
+ * @private
+ */
+ isAudioStreamSupported(stream, deviceProfile) {
- var codec = (stream.Codec || '').toLowerCase();
+ const codec = (stream.Codec || "").toLowerCase();
if (!codec) {
return true;
@@ -674,7 +700,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
return true;
}
- var profiles = deviceProfile.DirectPlayProfiles || [];
+ const profiles = deviceProfile.DirectPlayProfiles || [];
return profiles.filter(function (p) {
@@ -692,27 +718,30 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
}).length > 0;
}
- function getSupportedAudioStreams() {
- var profile = self._lastProfile;
+ /**
+ * @private
+ */
+ getSupportedAudioStreams() {
+ const profile = self._lastProfile;
- return getMediaStreamAudioTracks(self._currentPlayOptions.mediaSource).filter(function (stream) {
- return isAudioStreamSupported(stream, profile);
+ return getMediaStreamAudioTracks(self._currentPlayOptions.mediaSource).filter((stream) => {
+ return this.isAudioStreamSupported(stream, profile);
});
}
- self.setAudioStreamIndex = function (index) {
+ setAudioStreamIndex(index) {
- var streams = getSupportedAudioStreams();
+ const streams = this.getSupportedAudioStreams();
if (streams.length < 2) {
// If there's only one supported stream then trust that the player will handle it on it's own
return;
}
- var audioIndex = -1;
- var i;
- var length;
- var stream;
+ let audioIndex = -1;
+ let i;
+ let length;
+ let stream;
for (i = 0, length = streams.length; i < length; i++) {
stream = streams[i];
@@ -728,14 +757,14 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
return;
}
- var elem = self._mediaElement;
+ const elem = self._mediaElement;
if (!elem) {
return;
}
// https://msdn.microsoft.com/en-us/library/hh772507(v=vs.85).aspx
- var elemAudioTracks = elem.audioTracks || [];
+ const elemAudioTracks = elem.audioTracks || [];
console.debug('found ' + elemAudioTracks.length + ' audio tracks');
for (i = 0, length = elemAudioTracks.length; i < length; i++) {
@@ -748,188 +777,222 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
elemAudioTracks[i].enabled = false;
}
}
- };
+ }
- self.stop = function (destroyPlayer) {
- var elem = self._mediaElement;
- var src = self._currentSrc;
+ stop(destroyPlayer) {
+ const elem = this._mediaElement;
+ const src = this._currentSrc;
if (elem) {
if (src) {
elem.pause();
}
- htmlMediaHelper.onEndedInternal(self, elem, onError);
+ htmlMediaHelper.onEndedInternal(this, elem, this.onError.bind(this));
if (destroyPlayer) {
- self.destroy();
+ this.destroy();
}
}
- destroyCustomTrack(elem);
+ this.destroyCustomTrack(elem);
return Promise.resolve();
- };
+ }
- self.destroy = function () {
- htmlMediaHelper.destroyHlsPlayer(self);
- htmlMediaHelper.destroyFlvPlayer(self);
+ destroy() {
+ htmlMediaHelper.destroyHlsPlayer(this);
+ htmlMediaHelper.destroyFlvPlayer(this);
appRouter.setTransparency('none');
- var videoElement = self._mediaElement;
+ const videoElement = this._mediaElement;
if (videoElement) {
- self._mediaElement = null;
+ this._mediaElement = null;
- destroyCustomTrack(videoElement);
- videoElement.removeEventListener('timeupdate', onTimeUpdate);
- videoElement.removeEventListener('ended', onEnded);
- videoElement.removeEventListener('volumechange', onVolumeChange);
- videoElement.removeEventListener('pause', onPause);
- videoElement.removeEventListener('playing', onPlaying);
- videoElement.removeEventListener('play', onPlay);
- videoElement.removeEventListener('click', onClick);
- videoElement.removeEventListener('dblclick', onDblClick);
- videoElement.removeEventListener('waiting', onWaiting);
+ this.destroyCustomTrack(videoElement);
+ videoElement.removeEventListener('timeupdate', this.onTimeUpdate.bind(this));
+ videoElement.removeEventListener('ended', this.onEnded.bind(this));
+ videoElement.removeEventListener('volumechange', this.onVolumeChange.bind(this));
+ videoElement.removeEventListener('pause', this.onPause.bind(this));
+ videoElement.removeEventListener('playing', this.onPlaying.bind(this));
+ videoElement.removeEventListener('play', this.onPlay.bind(this));
+ videoElement.removeEventListener('click', this.onClick.bind(this));
+ videoElement.removeEventListener('dblclick', this.onDblClick.bind(this));
+ videoElement.removeEventListener('waiting', this.onWaiting.bind(this));
videoElement.parentNode.removeChild(videoElement);
}
- var dlg = videoDialog;
+ const dlg = this.videoDialog;
if (dlg) {
- videoDialog = null;
+ this.videoDialog = null;
dlg.parentNode.removeChild(dlg);
}
if (screenfull.isEnabled) {
screenfull.exit();
}
- };
-
- function onEnded() {
-
- destroyCustomTrack(this);
- htmlMediaHelper.onEndedInternal(self, this, onError);
}
- function onTimeUpdate(e) {
- // get the player position and the transcoding offset
- var time = this.currentTime;
+ /**
+ * @private
+ */
+ onEnded() {
+ this.destroyCustomTrack(this);
+ htmlMediaHelper.onEndedInternal(this, this.onEnded.bind(this), this.onError.bind(this));
+ }
- if (time && !self._timeUpdated) {
- self._timeUpdated = true;
- ensureValidVideo(this);
+ /**
+ * @private
+ */
+ onTimeUpdate(e) {
+ // get the player position and the transcoding offset
+ const time = this.currentTime;
+
+ if (time && !this._timeUpdated) {
+ this._timeUpdated = true;
+ this.ensureValidVideo(this);
}
- self._currentTime = time;
+ this._currentTime = time;
- var currentPlayOptions = self._currentPlayOptions;
+ const currentPlayOptions = this._currentPlayOptions;
// Not sure yet how this is coming up null since we never null it out, but it is causing app crashes
if (currentPlayOptions) {
- var timeMs = time * 1000;
+ let timeMs = time * 1000;
timeMs += ((currentPlayOptions.transcodingOffsetTicks || 0) / 10000);
- updateSubtitleText(timeMs);
+ this.updateSubtitleText(timeMs);
}
- events.trigger(self, 'timeupdate');
+ events.trigger(this, 'timeupdate');
}
- function onVolumeChange() {
+ /**
+ * @private
+ */
+ onVolumeChange() {
htmlMediaHelper.saveVolume(this.volume);
- events.trigger(self, 'volumechange');
+ events.trigger(this, 'volumechange');
}
- function onNavigatedToOsd() {
- var dlg = videoDialog;
+ /**
+ * @private
+ */
+ onNavigatedToOsd() {
+ const dlg = this.videoDialog;
if (dlg) {
dlg.classList.remove('videoPlayerContainer-onTop');
- onStartedAndNavigatedToOsd();
+ this.onStartedAndNavigatedToOsd();
}
}
- function onStartedAndNavigatedToOsd() {
+ /**
+ * @private
+ */
+ onStartedAndNavigatedToOsd() {
// If this causes a failure during navigation we end up in an awkward UI state
- setCurrentTrackElement(subtitleTrackIndexToSetOnPlaying);
+ this.setCurrentTrackElement(this.subtitleTrackIndexToSetOnPlaying);
- if (audioTrackIndexToSetOnPlaying != null && self.canSetAudioStreamIndex()) {
- self.setAudioStreamIndex(audioTrackIndexToSetOnPlaying);
+ if (this.audioTrackIndexToSetOnPlaying != null && self.canSetAudioStreamIndex()) {
+ self.setAudioStreamIndex(this.audioTrackIndexToSetOnPlaying);
}
}
- function onPlaying(e) {
- if (!self._started) {
- self._started = true;
- this.removeAttribute('controls');
+ /**
+ * @private
+ */
+ onPlaying(e) {
+ if (!this._started) {
+ this._started = true;
+ this.onPlaying.removeAttribute('controls');
loading.hide();
- htmlMediaHelper.seekOnPlaybackStart(self, e.target, self._currentPlayOptions.playerStartPositionTicks, function () {
- if (currentSubtitlesOctopus) {
- currentSubtitlesOctopus.timeOffset = (self._currentPlayOptions.transcodingOffsetTicks || 0) / 10000000 + currentTrackOffset;
- currentSubtitlesOctopus.resize();
- currentSubtitlesOctopus.resetRenderAheadCache(false);
+ htmlMediaHelper.seekOnPlaybackStart(this, e.target, this._currentPlayOptions.playerStartPositionTicks, function () {
+ if (this.currentSubtitlesOctopus) {
+ this.currentSubtitlesOctopus.timeOffset = (this._currentPlayOptions.transcodingOffsetTicks || 0) / 10000000 + currentTrackOffset;
+ this.currentSubtitlesOctopus.resize();
+ this.currentSubtitlesOctopus.resetRenderAheadCache(false);
}
});
- if (self._currentPlayOptions.fullscreen) {
+ if (this._currentPlayOptions.fullscreen) {
- appRouter.showVideoOsd().then(onNavigatedToOsd);
+ appRouter.showVideoOsd().then(this.onNavigatedToOsd.bind(this));
} else {
appRouter.setTransparency('backdrop');
- videoDialog.classList.remove('videoPlayerContainer-onTop');
+ this.videoDialog.classList.remove('videoPlayerContainer-onTop');
- onStartedAndNavigatedToOsd();
+ this.onStartedAndNavigatedToOsd();
}
}
- events.trigger(self, 'playing');
+ events.trigger(this, 'playing');
}
- function onPlay(e) {
- events.trigger(self, 'unpause');
+ /**
+ * @private
+ */
+ onPlay(e) {
+ events.trigger(this, 'unpause');
}
- function ensureValidVideo(elem) {
- if (elem !== self._mediaElement) {
+ /**
+ * @private
+ */
+ ensureValidVideo(elem) {
+ if (elem !== this._mediaElement) {
return;
}
if (elem.videoWidth === 0 && elem.videoHeight === 0) {
- var mediaSource = (self._currentPlayOptions || {}).mediaSource;
+ const mediaSource = (this._currentPlayOptions || {}).mediaSource;
// Only trigger this if there is media info
// Avoid triggering in situations where it might not actually have a video stream (audio only live tv channel)
if (!mediaSource || mediaSource.RunTimeTicks) {
- htmlMediaHelper.onErrorInternal(self, 'mediadecodeerror');
- return;
+ htmlMediaHelper.onErrorInternal(this, 'mediadecodeerror');
}
}
}
- function onClick() {
- events.trigger(self, 'click');
+ /**
+ * @private
+ */
+ onClick() {
+ events.trigger(this, 'click');
}
- function onDblClick() {
- events.trigger(self, 'dblclick');
+ /**
+ * @private
+ */
+ onDblClick() {
+ events.trigger(this, 'dblclick');
}
- function onPause() {
- events.trigger(self, 'pause');
+ /**
+ * @private
+ */
+ onPause() {
+ events.trigger(this, 'pause');
}
- function onWaiting() {
- events.trigger(self, 'waiting');
+ onWaiting() {
+ events.trigger(this, 'waiting');
}
- function onError() {
- var errorCode = this.error ? (this.error.code || 0) : 0;
- var errorMessage = this.error ? (this.error.message || '') : '';
+ /**
+ * @private
+ */
+ onError() {
+ const errorCode = this.onError.error ? (this.onError.error.code || 0) : 0;
+ const errorMessage = this.onError.error ? (this.onError.error.message || "") : "";
console.error('media element error: ' + errorCode.toString() + ' ' + errorMessage);
- var type;
+ let type;
switch (errorCode) {
case 1:
@@ -959,30 +1022,33 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
return;
}
- htmlMediaHelper.onErrorInternal(self, type);
+ htmlMediaHelper.onErrorInternal(this, type);
}
- function destroyCustomTrack(videoElement) {
- if (self._resizeObserver) {
- self._resizeObserver.disconnect();
- self._resizeObserver = null;
+ /**
+ * @private
+ */
+ destroyCustomTrack(videoElement) {
+ if (this._resizeObserver) {
+ this._resizeObserver.disconnect();
+ this._resizeObserver = null;
}
- if (videoSubtitlesElem) {
- var subtitlesContainer = videoSubtitlesElem.parentNode;
+ if (this.videoSubtitlesElem) {
+ const subtitlesContainer = this.videoSubtitlesElem.parentNode;
if (subtitlesContainer) {
tryRemoveElement(subtitlesContainer);
}
- videoSubtitlesElem = null;
+ this.videoSubtitlesElem = null;
}
- currentTrackEvents = null;
+ this.currentTrackEvents = null;
if (videoElement) {
- var allTracks = videoElement.textTracks || []; // get list of tracks
- for (var i = 0; i < allTracks.length; i++) {
+ const allTracks = videoElement.textTracks || []; // get list of tracks
+ for (let i = 0; i < allTracks.length; i++) {
- var currentTrack = allTracks[i];
+ const currentTrack = allTracks[i];
if (currentTrack.label.indexOf('manualTrack') !== -1) {
currentTrack.mode = 'disabled';
@@ -990,85 +1056,94 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
}
}
- customTrackIndex = -1;
- currentClock = null;
- self._currentAspectRatio = null;
+ this.customTrackIndex = -1;
+ this.currentClock = null;
+ this._currentAspectRatio = null;
- var octopus = currentSubtitlesOctopus;
+ const octopus = this.currentSubtitlesOctopus;
if (octopus) {
octopus.dispose();
}
- currentSubtitlesOctopus = null;
+ this.currentSubtitlesOctopus = null;
- var renderer = currentAssRenderer;
+ const renderer = this.currentAssRenderer;
if (renderer) {
renderer.setEnabled(false);
}
- currentAssRenderer = null;
+ this.currentAssRenderer = null;
}
- self.destroyCustomTrack = destroyCustomTrack;
-
- function fetchSubtitlesUwp(track, item) {
+ /**
+ * @private
+ */
+ fetchSubtitlesUwp(track, item) {
return Windows.Storage.StorageFile.getFileFromPathAsync(track.Path).then(function (storageFile) {
- return Windows.Storage.FileIO.readTextAsync(storageFile).then(function (text) {
- return JSON.parse(text);
- });
+ return Windows.Storage.FileIO.readTextAsync(storageFile);
+ }).then(function (text) {
+ return JSON.parse(text);
});
}
- function fetchSubtitles(track, item) {
+ /**
+ * @private
+ */
+ fetchSubtitles(track, item) {
if (window.Windows && itemHelper.isLocalItem(item)) {
- return fetchSubtitlesUwp(track, item);
+ return this.fetchSubtitlesUwp(track, item);
}
- incrementFetchQueue();
- return new Promise(function (resolve, reject) {
- var xhr = new XMLHttpRequest();
+ this.incrementFetchQueue();
+ return new Promise((resolve, reject) => {
+ const xhr = new XMLHttpRequest();
- var url = getTextTrackUrl(track, item, '.js');
+ const url = getTextTrackUrl(track, item, ".js");
xhr.open('GET', url, true);
- xhr.onload = function (e) {
+ xhr.onload = (e) => {
resolve(JSON.parse(this.response));
- decrementFetchQueue();
+ this.decrementFetchQueue();
};
- xhr.onerror = function (e) {
+ xhr.onerror = (e) => {
reject(e);
- decrementFetchQueue();
+ this.decrementFetchQueue();
};
xhr.send();
});
}
- function setTrackForDisplay(videoElement, track) {
-
+ /**
+ * @private
+ */
+ setTrackForDisplay(videoElement, track) {
if (!track) {
- destroyCustomTrack(videoElement);
+ this.destroyCustomTrack(videoElement);
return;
}
// skip if already playing this track
- if (customTrackIndex === track.Index) {
+ if (this.customTrackIndex === track.Index) {
return;
}
- self.resetSubtitleOffset();
- var item = self._currentPlayOptions.item;
+ this.resetSubtitleOffset();
+ const item = self._currentPlayOptions.item;
- destroyCustomTrack(videoElement);
- customTrackIndex = track.Index;
- renderTracksEvents(videoElement, track, item);
- lastCustomTrackMs = 0;
+ this.destroyCustomTrack(videoElement);
+ this.customTrackIndex = track.Index;
+ this.renderTracksEvents(videoElement, track, item);
+ this.lastCustomTrackMs = 0;
}
- function renderSsaAss(videoElement, track, item) {
- var attachments = self._currentPlayOptions.mediaSource.MediaAttachments || [];
- var apiClient = connectionManager.getApiClient(item);
- var options = {
+ /**
+ * @private
+ */
+ renderSsaAss(videoElement, track, item) {
+ const attachments = self._currentPlayOptions.mediaSource.MediaAttachments || [];
+ const apiClient = connectionManager.getApiClient(item);
+ const options = {
video: videoElement,
subUrl: getTextTrackUrl(track, item),
fonts: attachments.map(function (i) {
@@ -1076,13 +1151,13 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
}),
workerUrl: appRouter.baseUrl() + '/libraries/subtitles-octopus-worker.js',
legacyWorkerUrl: appRouter.baseUrl() + '/libraries/subtitles-octopus-worker-legacy.js',
- onError: function() {
+ onError() {
htmlMediaHelper.onErrorInternal(self, 'mediadecodeerror');
},
- timeOffset: (self._currentPlayOptions.transcodingOffsetTicks || 0) / 10000000,
+ timeOffset: (this._currentPlayOptions.transcodingOffsetTicks || 0) / 10000000,
// new octopus options; override all, even defaults
- renderMode: 'blend',
+ renderMode: "blend",
dropAllAnimations: false,
libassMemoryLimit: 40,
libassGlyphLimit: 40,
@@ -1093,13 +1168,15 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
resizeVariation: 0.2,
renderAhead: 90
};
- require(['JavascriptSubtitlesOctopus'], function(SubtitlesOctopus) {
- currentSubtitlesOctopus = new SubtitlesOctopus(options);
+ import('JavascriptSubtitlesOctopus').then(SubtitlesOctopus => {
+ this.currentSubtitlesOctopus = new SubtitlesOctopus(options);
});
}
- function requiresCustomSubtitlesElement() {
-
+ /**
+ * @private
+ */
+ requiresCustomSubtitlesElement() {
// after a system update, ps4 isn't showing anything when creating a track element dynamically
// going to have to do it ourselves
if (browser.ps4) {
@@ -1116,7 +1193,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
}
if (browser.iOS) {
- var userAgent = navigator.userAgent.toLowerCase();
+ const userAgent = navigator.userAgent.toLowerCase();
// works in the browser but not the native app
if ((userAgent.indexOf('os 9') !== -1 || userAgent.indexOf('os 8') !== -1) && userAgent.indexOf('safari') === -1) {
return true;
@@ -1126,22 +1203,28 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
return false;
}
- function renderSubtitlesWithCustomElement(videoElement, track, item) {
- fetchSubtitles(track, item).then(function (data) {
- if (!videoSubtitlesElem) {
- var subtitlesContainer = document.createElement('div');
+ /**
+ * @private
+ */
+ renderSubtitlesWithCustomElement(videoElement, track, item) {
+ this.fetchSubtitles(track, item).then((data) => {
+ if (!this.videoSubtitlesElem) {
+ const subtitlesContainer = document.createElement("div");
subtitlesContainer.classList.add('videoSubtitles');
subtitlesContainer.innerHTML = '