jellyfin-web/dashboard-ui/scripts/htmlmediarenderer.js

964 lines
31 KiB
JavaScript
Raw Normal View History

2016-09-02 08:56:19 -07:00
define(['browser'], function (browser) {
2015-06-07 20:16:42 -07:00
2015-06-13 07:46:59 -07:00
var supportsTextTracks;
2015-11-20 13:46:00 -07:00
var hlsPlayer;
2015-07-13 14:26:11 -07:00
var requiresSettingStartTimeOnStart;
2015-12-23 10:46:01 -07:00
var subtitleTrackIndexToSetOnPlaying;
2016-02-24 09:52:36 -07:00
var currentTrackList;
2016-04-04 12:07:43 -07:00
var currentPlayOptions;
2015-06-13 07:46:59 -07:00
2015-07-03 09:49:49 -07:00
function htmlMediaRenderer(options) {
2015-06-07 20:16:42 -07:00
var mediaElement;
var self = this;
function onEnded() {
2016-04-04 18:23:42 -07:00
destroyCustomTrack(this);
2015-12-23 10:46:01 -07:00
Events.trigger(self, 'ended');
2015-06-07 20:16:42 -07:00
}
function onTimeUpdate() {
2015-07-13 14:26:11 -07:00
2015-11-20 13:46:00 -07:00
//if (isViblastStarted) {
2015-07-13 14:26:11 -07:00
2015-11-20 13:46:00 -07:00
// // This is a workaround for viblast not stopping playback at the end
// var time = this.currentTime;
// var duration = this.duration;
2015-07-13 14:26:11 -07:00
2015-11-20 13:46:00 -07:00
// if (duration) {
// if (time >= (duration - 1)) {
2015-07-15 04:26:47 -07:00
2015-11-20 13:46:00 -07:00
// //onEnded();
// return;
// }
// }
//}
2016-04-04 12:07:43 -07:00
if (options.type == 'video') {
// Get the player position + the transcoding offset
var timeMs = this.currentTime * 1000;
timeMs += ((currentPlayOptions.startTimeTicksOffset || 0) / 10000);
updateSubtitleText(timeMs);
}
2015-12-23 10:46:01 -07:00
Events.trigger(self, 'timeupdate');
2015-06-07 20:16:42 -07:00
}
function onVolumeChange() {
2015-12-23 10:46:01 -07:00
Events.trigger(self, 'volumechange');
2015-06-07 20:16:42 -07:00
}
2015-12-23 10:46:01 -07:00
function onOneAudioPlaying(e) {
var elem = e.target;
elem.removeEventListener('playing', onOneAudioPlaying);
2016-06-06 17:44:15 -07:00
document.querySelector('.mediaPlayerAudioContainer').classList.add('hide');
2015-06-07 20:16:42 -07:00
}
function onPlaying() {
2015-12-23 10:46:01 -07:00
Events.trigger(self, 'playing');
2015-06-07 20:16:42 -07:00
}
function onPlay() {
2015-12-23 10:46:01 -07:00
Events.trigger(self, 'play');
2015-06-07 20:16:42 -07:00
}
function onPause() {
2015-12-23 10:46:01 -07:00
Events.trigger(self, 'pause');
2015-06-07 20:16:42 -07:00
}
function onClick() {
2015-12-23 10:46:01 -07:00
Events.trigger(self, 'click');
2015-06-07 20:16:42 -07:00
}
function onDblClick() {
2015-12-23 10:46:01 -07:00
Events.trigger(self, 'dblclick');
2015-06-07 20:16:42 -07:00
}
2015-12-23 10:46:01 -07:00
function onError(e) {
2015-06-07 20:16:42 -07:00
2016-04-04 18:23:42 -07:00
destroyCustomTrack(this);
2016-02-24 23:09:10 -07:00
2015-12-23 10:46:01 -07:00
var elem = e.target;
var errorCode = elem.error ? elem.error.code : '';
console.log('Media element error code: ' + errorCode);
2015-06-07 20:16:42 -07:00
2015-12-23 10:46:01 -07:00
Events.trigger(self, 'error');
2015-06-07 20:16:42 -07:00
}
2015-12-23 10:46:01 -07:00
function onLoadedMetadata(e) {
var elem = e.target;
elem.removeEventListener('loadedmetadata', onLoadedMetadata);
2015-06-07 20:16:42 -07:00
2015-11-20 13:46:00 -07:00
if (!hlsPlayer) {
2015-12-23 10:46:01 -07:00
elem.play();
2015-06-07 20:16:42 -07:00
}
}
2015-11-20 13:46:00 -07:00
function requireHlsPlayer(callback) {
2015-12-15 22:30:14 -07:00
require(['hlsjs'], function (hls) {
2015-11-20 13:46:00 -07:00
window.Hls = hls;
2015-07-09 20:00:03 -07:00
callback();
});
}
2015-12-23 10:46:01 -07:00
function onOneVideoPlaying(e) {
2015-07-13 14:26:11 -07:00
2015-12-23 10:46:01 -07:00
var element = e.target;
element.removeEventListener('playing', onOneVideoPlaying);
self.setCurrentTrackElement(subtitleTrackIndexToSetOnPlaying);
2015-07-13 14:26:11 -07:00
var requiresNativeControls = !self.enableCustomVideoControls();
if (requiresNativeControls) {
2016-06-06 17:44:15 -07:00
element.setAttribute('controls', 'controls');
2015-07-13 14:26:11 -07:00
}
if (requiresSettingStartTimeOnStart) {
2016-04-18 13:09:07 -07:00
var startPositionInSeekParam = currentPlayOptions.startPositionInSeekParam;
2015-07-13 14:26:11 -07:00
// Appending #t=xxx to the query string doesn't seem to work with HLS
2016-04-18 13:09:07 -07:00
if (startPositionInSeekParam && currentSrc.indexOf('.m3u8') != -1) {
2015-11-20 13:46:00 -07:00
2016-09-02 08:56:19 -07:00
var delay = browser.safari ? 2500 : 0;
2015-11-20 13:46:00 -07:00
if (delay) {
setTimeout(function () {
element.currentTime = startPositionInSeekParam;
}, delay);
} else {
2015-07-13 14:26:11 -07:00
element.currentTime = startPositionInSeekParam;
2015-11-20 13:46:00 -07:00
}
2015-06-07 20:16:42 -07:00
}
}
}
function createAudioElement() {
2016-06-06 17:44:15 -07:00
var elem = document.querySelector('.mediaPlayerAudio');
2015-06-07 20:16:42 -07:00
2016-06-06 17:44:15 -07:00
if (!elem) {
2015-06-07 20:16:42 -07:00
var html = '';
var requiresControls = !MediaPlayer.canAutoPlayAudio();
if (requiresControls) {
2016-02-06 13:32:05 -07:00
html += '<div class="mediaPlayerAudioContainer" style="position: fixed;top: 40%;text-align: center;left: 0;right: 0;z-index:999999;"><div class="mediaPlayerAudioContainerInner">';;
2015-06-07 20:16:42 -07:00
} else {
2016-06-06 17:44:15 -07:00
html += '<div class="mediaPlayerAudioContainer hide" style="padding: 1em;background: #222;"><div class="mediaPlayerAudioContainerInner">';;
2015-06-07 20:16:42 -07:00
}
2016-02-05 08:31:11 -07:00
html += '<audio class="mediaPlayerAudio" controls>';
2015-06-07 20:16:42 -07:00
html += '</audio></div></div>';
2016-06-06 17:44:15 -07:00
document.body.insertAdjacentHTML('beforeend', html);
2015-06-07 20:16:42 -07:00
2016-06-06 17:44:15 -07:00
elem = document.querySelector('.mediaPlayerAudio');
2015-06-07 20:16:42 -07:00
}
2015-12-23 10:46:01 -07:00
elem.addEventListener('playing', onOneAudioPlaying);
elem.addEventListener('timeupdate', onTimeUpdate);
elem.addEventListener('ended', onEnded);
elem.addEventListener('volumechange', onVolumeChange);
elem.addEventListener('error', onError);
elem.addEventListener('pause', onPause);
elem.addEventListener('play', onPlay);
elem.addEventListener('playing', onPlaying);
return elem;
2015-06-07 20:16:42 -07:00
}
2016-09-18 13:38:38 -07:00
function enableHlsPlayer(src, item, mediaSource) {
2015-07-05 11:34:52 -07:00
if (src) {
if (src.indexOf('.m3u8') == -1) {
return false;
}
}
2015-07-03 04:51:45 -07:00
2016-09-18 13:38:38 -07:00
if (MediaPlayer.canPlayHls()) {
if (window.MediaSource == null) {
return false;
}
2016-09-19 08:41:35 -07:00
if (MediaPlayer.canPlayNativeHls()) {
// simple playback should use the native support
if (mediaSource.RunTimeTicks) {
return false;
}
//return false;
2016-09-18 13:38:38 -07:00
}
// For now don't do this in edge because we lose some native audio support
2016-09-27 10:51:01 -07:00
if (browser.edge && browser.mobile) {
return false;
2016-09-19 08:41:35 -07:00
}
// hls.js is only in beta. needs more testing.
if (browser.safari) {
return false;
2016-09-18 13:38:38 -07:00
}
return true;
}
return false;
2015-07-03 04:51:45 -07:00
}
2016-02-05 08:31:11 -07:00
function getCrossOriginValue(mediaSource) {
return 'anonymous';
}
2015-06-07 20:16:42 -07:00
function createVideoElement() {
2015-07-03 04:51:45 -07:00
var html = '';
var requiresNativeControls = !self.enableCustomVideoControls();
2015-09-09 20:22:52 -07:00
// Safari often displays the poster under the video and it doesn't look good
2016-09-02 08:56:19 -07:00
var poster = !browser.safari && options.poster ? (' poster="' + options.poster + '"') : '';
2015-07-03 09:49:49 -07:00
2015-07-03 04:51:45 -07:00
// Can't autoplay in these browsers so we need to use the full controls
2016-09-02 08:56:19 -07:00
if (requiresNativeControls && AppInfo.isNativeApp && browser.android) {
2016-02-05 08:31:11 -07:00
html += '<video class="itemVideo" id="itemVideo" preload="metadata" autoplay="autoplay"' + poster + ' webkit-playsinline>';
2015-07-03 04:51:45 -07:00
}
else if (requiresNativeControls) {
2016-02-05 08:31:11 -07:00
html += '<video class="itemVideo" id="itemVideo" preload="metadata" autoplay="autoplay"' + poster + ' controls="controls" webkit-playsinline>';
2015-07-03 04:51:45 -07:00
}
else {
// Chrome 35 won't play with preload none
2016-02-05 08:31:11 -07:00
html += '<video class="itemVideo" id="itemVideo" preload="metadata" autoplay="autoplay"' + poster + ' webkit-playsinline>';
2015-07-03 04:51:45 -07:00
}
html += '</video>';
2016-06-06 17:44:15 -07:00
var elem = document.querySelector('#videoPlayer #videoElement');
elem.insertAdjacentHTML('afterbegin', html);
2015-06-07 20:16:42 -07:00
2016-06-06 17:44:15 -07:00
var itemVideo = elem.querySelector('.itemVideo');
2015-12-23 10:46:01 -07:00
itemVideo.addEventListener('loadedmetadata', onLoadedMetadata);
itemVideo.addEventListener('timeupdate', onTimeUpdate);
itemVideo.addEventListener('ended', onEnded);
itemVideo.addEventListener('volumechange', onVolumeChange);
2016-02-01 12:10:44 -07:00
itemVideo.addEventListener('play', onPlay);
2015-12-23 10:46:01 -07:00
itemVideo.addEventListener('pause', onPause);
itemVideo.addEventListener('playing', onPlaying);
itemVideo.addEventListener('click', onClick);
itemVideo.addEventListener('dblclick', onDblClick);
itemVideo.addEventListener('error', onError);
return itemVideo;
2015-06-07 20:16:42 -07:00
}
2015-07-09 20:00:03 -07:00
// Save this for when playback stops, because querying the time at that point might return 0
var _currentTime;
2015-06-07 20:16:42 -07:00
self.currentTime = function (val) {
if (mediaElement) {
if (val != null) {
2015-06-25 14:50:56 -07:00
mediaElement.currentTime = val / 1000;
2015-06-07 20:16:42 -07:00
return;
}
2015-07-09 20:00:03 -07:00
if (_currentTime) {
return _currentTime * 1000;
}
2015-06-25 14:50:56 -07:00
return (mediaElement.currentTime || 0) * 1000;
2015-06-07 20:16:42 -07:00
}
};
self.duration = function (val) {
if (mediaElement) {
2016-09-18 09:08:32 -07:00
var duration = mediaElement.duration;
if (duration && !isNaN(duration) && duration != Number.POSITIVE_INFINITY && duration != Number.NEGATIVE_INFINITY) {
return duration * 1000;
}
2015-06-07 20:16:42 -07:00
}
return null;
};
2015-06-10 20:01:41 -07:00
self.stop = function () {
2016-02-24 23:09:10 -07:00
2016-04-04 18:23:42 -07:00
destroyCustomTrack(mediaElement);
2016-02-24 23:09:10 -07:00
2015-06-10 20:01:41 -07:00
if (mediaElement) {
mediaElement.pause();
2015-07-03 04:51:45 -07:00
2015-11-20 13:46:00 -07:00
if (hlsPlayer) {
2015-07-09 20:00:03 -07:00
_currentTime = mediaElement.currentTime;
2015-07-03 09:49:49 -07:00
2015-10-26 09:21:00 -07:00
// Sometimes this fails
try {
2015-11-20 13:46:00 -07:00
hlsPlayer.destroy();
2015-10-26 09:21:00 -07:00
}
catch (err) {
2015-12-23 10:46:01 -07:00
console.log(err);
2015-10-26 09:21:00 -07:00
}
2015-11-20 13:46:00 -07:00
hlsPlayer = null;
2015-07-03 04:51:45 -07:00
}
2015-06-10 20:01:41 -07:00
}
};
2015-06-07 20:16:42 -07:00
self.pause = function () {
if (mediaElement) {
mediaElement.pause();
}
};
self.unpause = function () {
if (mediaElement) {
mediaElement.play();
}
};
self.volume = function (val) {
if (mediaElement) {
if (val != null) {
mediaElement.volume = val;
return;
}
return mediaElement.volume;
}
};
2015-07-03 04:51:45 -07:00
var currentSrc;
2015-09-15 11:09:44 -07:00
self.setCurrentSrc = function (streamInfo, item, mediaSource, tracks) {
2015-06-07 20:16:42 -07:00
var elem = mediaElement;
if (!elem) {
2015-07-03 04:51:45 -07:00
currentSrc = null;
2016-04-04 12:07:43 -07:00
currentPlayOptions = null;
2015-06-07 20:16:42 -07:00
return;
}
2016-04-04 12:07:43 -07:00
currentPlayOptions = streamInfo;
2015-09-15 11:09:44 -07:00
if (!streamInfo) {
2015-07-03 04:51:45 -07:00
currentSrc = null;
2015-06-07 20:16:42 -07:00
elem.src = null;
elem.src = "";
// When the browser regains focus it may start auto-playing the last video
2016-09-02 08:56:19 -07:00
if (browser.safari) {
2015-06-07 20:16:42 -07:00
elem.src = 'files/dummy.mp4';
elem.play();
}
return;
}
2016-02-05 08:31:11 -07:00
elem.crossOrigin = getCrossOriginValue(mediaSource);
2015-09-15 11:09:44 -07:00
var val = streamInfo.url;
2015-09-21 08:43:10 -07:00
2016-09-02 08:56:19 -07:00
if (AppInfo.isNativeApp && browser.safari) {
2015-09-21 08:43:10 -07:00
val = val.replace('file://', '');
}
2015-07-13 14:26:11 -07:00
requiresSettingStartTimeOnStart = false;
2016-04-18 13:09:07 -07:00
var startTime = streamInfo.startPositionInSeekParam;
2015-08-24 13:37:34 -07:00
var playNow = false;
2015-07-13 14:26:11 -07:00
2015-06-07 20:16:42 -07:00
if (elem.tagName.toLowerCase() == 'audio') {
2015-07-03 04:51:45 -07:00
elem.src = val;
2015-08-24 13:37:34 -07:00
playNow = true;
2015-07-03 04:51:45 -07:00
2015-06-07 20:16:42 -07:00
}
else {
2016-02-01 12:07:16 -07:00
elem.removeEventListener('playing', onOneVideoPlaying);
elem.addEventListener('playing', onOneVideoPlaying);
2015-11-20 13:46:00 -07:00
if (hlsPlayer) {
hlsPlayer.destroy();
hlsPlayer = null;
2015-07-16 05:56:38 -07:00
}
2015-07-13 14:26:11 -07:00
if (startTime) {
2015-07-16 05:56:38 -07:00
2015-11-20 13:46:00 -07:00
//try {
// elem.currentTime = startTime;
//} catch (err) {
// // IE will throw an invalid state exception when trying to set currentTime before starting playback
//}
//requiresSettingStartTimeOnStart = elem.currentTime == 0;
requiresSettingStartTimeOnStart = true;
2015-07-13 14:26:11 -07:00
}
2015-08-15 14:58:52 -07:00
tracks = tracks || [];
2016-02-24 09:52:36 -07:00
currentTrackList = tracks;
2015-08-15 14:58:52 -07:00
2015-12-23 10:46:01 -07:00
var currentTrackIndex = -1;
for (var i = 0, length = tracks.length; i < length; i++) {
if (tracks[i].isDefault) {
2016-02-24 09:52:36 -07:00
currentTrackIndex = tracks[i].index;
2015-12-23 10:46:01 -07:00
break;
}
}
subtitleTrackIndexToSetOnPlaying = currentTrackIndex;
2016-09-18 13:38:38 -07:00
if (enableHlsPlayer(val, item, mediaSource)) {
2015-07-03 04:51:45 -07:00
2015-08-15 14:58:52 -07:00
setTracks(elem, tracks);
2015-07-06 00:06:09 -07:00
2016-09-18 13:38:38 -07:00
requireHlsPlayer(function () {
var hls = new Hls();
hls.loadSource(val);
hls.attachMedia(elem);
hls.on(Hls.Events.MANIFEST_PARSED, function () {
elem.play();
});
2016-10-07 08:08:13 -07:00
hls.on(Hls.Events.ERROR, function (event, data) {
if (data.fatal) {
switch (data.type) {
case Hls.ErrorTypes.NETWORK_ERROR:
// try to recover network error
console.log("fatal network error encountered, try to recover");
hls.startLoad();
break;
case Hls.ErrorTypes.MEDIA_ERROR:
console.log("fatal media error encountered, try to recover");
hls.recoverMediaError();
break;
default:
// cannot recover
hls.destroy();
break;
}
}
});
2016-09-18 13:38:38 -07:00
hlsPlayer = hls;
2015-07-03 04:51:45 -07:00
});
2015-07-03 09:49:49 -07:00
2015-07-03 04:51:45 -07:00
} else {
2015-07-13 14:26:11 -07:00
2015-07-03 04:51:45 -07:00
elem.src = val;
2015-08-24 13:37:34 -07:00
elem.autoplay = true;
2015-07-06 00:06:09 -07:00
2015-08-15 14:58:52 -07:00
setTracks(elem, tracks);
2015-07-06 00:06:09 -07:00
2015-12-23 10:46:01 -07:00
elem.addEventListener("loadedmetadata", onLoadedMetadata);
2015-08-24 13:37:34 -07:00
playNow = true;
2015-07-03 04:51:45 -07:00
}
2015-08-15 14:58:52 -07:00
2016-02-26 10:15:06 -07:00
// This is needed in setCurrentTrackElement
currentSrc = val;
2015-08-15 14:58:52 -07:00
self.setCurrentTrackElement(currentTrackIndex);
2015-07-03 04:51:45 -07:00
}
2015-07-03 08:59:12 -07:00
2015-07-03 04:51:45 -07:00
currentSrc = val;
2015-08-24 13:37:34 -07:00
if (playNow) {
elem.play();
}
2015-07-03 04:51:45 -07:00
};
2015-07-06 00:06:09 -07:00
function setTracks(elem, tracks) {
2016-02-24 23:09:10 -07:00
2015-07-03 04:51:45 -07:00
var html = tracks.map(function (t) {
var defaultAttribute = t.isDefault ? ' default' : '';
2016-02-23 11:48:26 -07:00
var label = t.language || 'und';
return '<track id="textTrack' + t.index + '" label="' + label + '" kind="subtitles" src="' + t.url + '" srclang="' + t.language + '"' + defaultAttribute + '></track>';
2015-07-03 04:51:45 -07:00
}).join('');
2016-02-24 23:09:10 -07:00
elem.innerHTML = html;
2015-07-06 00:06:09 -07:00
}
2015-06-07 20:16:42 -07:00
self.currentSrc = function () {
if (mediaElement) {
2015-07-03 04:51:45 -07:00
// We have to use this cached value because viblast will muck with the url
return currentSrc;
//return mediaElement.currentSrc;
2015-06-07 20:16:42 -07:00
}
};
self.paused = function () {
if (mediaElement) {
return mediaElement.paused;
}
return false;
};
2015-06-25 14:50:56 -07:00
self.cleanup = function (destroyRenderer) {
2015-06-07 20:16:42 -07:00
self.setCurrentSrc(null);
2015-07-09 20:00:03 -07:00
_currentTime = null;
2015-06-07 20:16:42 -07:00
var elem = mediaElement;
if (elem) {
2015-06-29 11:45:42 -07:00
if (elem.tagName == 'AUDIO') {
2015-12-23 10:46:01 -07:00
elem.removeEventListener('timeupdate', onTimeUpdate);
elem.removeEventListener('ended', onEnded);
elem.removeEventListener('volumechange', onVolumeChange);
elem.removeEventListener('playing', onOneAudioPlaying);
elem.removeEventListener('play', onPlay);
elem.removeEventListener('pause', onPause);
elem.removeEventListener('playing', onPlaying);
elem.removeEventListener('error', onError);
2015-06-29 11:45:42 -07:00
} else {
2015-12-23 10:46:01 -07:00
elem.removeEventListener('loadedmetadata', onLoadedMetadata);
elem.removeEventListener('playing', onOneVideoPlaying);
elem.removeEventListener('timeupdate', onTimeUpdate);
elem.removeEventListener('ended', onEnded);
elem.removeEventListener('volumechange', onVolumeChange);
elem.removeEventListener('play', onPlay);
elem.removeEventListener('pause', onPause);
elem.removeEventListener('playing', onPlaying);
elem.removeEventListener('click', onClick);
elem.removeEventListener('dblclick', onDblClick);
elem.removeEventListener('error', onError);
2015-06-29 11:45:42 -07:00
}
2015-06-07 20:16:42 -07:00
if (elem.tagName.toLowerCase() != 'audio') {
2016-06-06 17:44:15 -07:00
if (elem.parentNode) {
elem.parentNode.removeChild(elem);
}
2015-06-07 20:16:42 -07:00
}
}
};
2015-06-13 07:46:59 -07:00
self.supportsTextTracks = function () {
if (supportsTextTracks == null) {
supportsTextTracks = document.createElement('video').textTracks != null;
}
// For now, until ready
return supportsTextTracks;
};
2016-02-24 23:09:10 -07:00
function enableNativeTrackSupport(track) {
2015-06-13 07:46:59 -07:00
2016-09-02 08:56:19 -07:00
if (browser.safari && browser.mobile) {
2016-03-09 17:05:29 -07:00
// Leave it to apple to have different behavior between safari on ios vs osx
return false;
2016-02-24 23:09:10 -07:00
}
2015-06-13 07:46:59 -07:00
2016-09-02 08:56:19 -07:00
if (browser.firefox) {
2016-02-26 10:15:06 -07:00
if ((currentSrc || '').toLowerCase().indexOf('.m3u8') != -1) {
return false;
}
}
2016-04-04 18:23:42 -07:00
if (track) {
var format = (track.format || '').toLowerCase();
if (format == 'ssa' || format == 'ass') {
// libjass is needed here
return false;
}
}
2016-02-24 23:09:10 -07:00
return true;
}
2015-06-13 07:46:59 -07:00
2016-04-04 18:23:42 -07:00
function destroyCustomTrack(videoElement, isPlaying) {
2016-02-26 21:39:01 -07:00
2016-04-04 18:23:42 -07:00
window.removeEventListener('resize', onVideoResize);
2016-04-05 19:18:56 -07:00
window.removeEventListener('orientationchange', onVideoResize);
2016-04-04 12:07:43 -07:00
var videoSubtitlesElem = document.querySelector('.videoSubtitles');
if (videoSubtitlesElem) {
videoSubtitlesElem.parentNode.removeChild(videoSubtitlesElem);
}
2016-02-26 21:39:01 -07:00
if (isPlaying) {
2016-04-04 18:23:42 -07:00
var allTracks = videoElement.textTracks; // get list of tracks
2016-02-26 21:39:01 -07:00
for (var i = 0; i < allTracks.length; i++) {
var currentTrack = allTracks[i];
2016-02-24 23:09:10 -07:00
2016-02-26 21:39:01 -07:00
if (currentTrack.label.indexOf('manualTrack') != -1) {
currentTrack.mode = 'disabled';
}
}
2016-02-24 23:09:10 -07:00
}
customTrackIndex = -1;
2016-04-04 12:07:43 -07:00
currentSubtitlesElement = null;
currentTrackEvents = null;
currentClock = null;
var renderer = currentAssRenderer;
if (renderer) {
renderer.setEnabled(false);
}
currentAssRenderer = null;
2016-02-24 23:09:10 -07:00
}
function fetchSubtitles(track) {
return ApiClient.ajax({
url: track.url.replace('.vtt', '.js'),
type: 'GET',
dataType: 'json'
});
}
2016-04-04 18:23:42 -07:00
function setTrackForCustomDisplay(videoElement, track) {
2016-02-24 23:09:10 -07:00
if (!track) {
2016-04-04 18:23:42 -07:00
destroyCustomTrack(videoElement, true);
2016-02-24 23:09:10 -07:00
return;
}
// if already playing this track, skip
if (customTrackIndex == track.index) {
return;
}
2016-04-04 18:23:42 -07:00
destroyCustomTrack(videoElement, true);
2016-02-24 23:09:10 -07:00
customTrackIndex = track.index;
2016-04-04 18:23:42 -07:00
renderTracksEvents(videoElement, track);
2016-04-04 12:07:43 -07:00
lastCustomTrackMs = 0;
2016-02-26 21:39:01 -07:00
}
2016-02-24 23:09:10 -07:00
2016-04-04 18:23:42 -07:00
function renderWithLibjass(videoElement, track) {
var rendererSettings = {};
require(['libjass'], function (libjass) {
libjass.ASS.fromUrl(track.url).then(function (ass) {
var clock = currentClock = new libjass.renderers.ManualClock();
// Create a DefaultRenderer using the video element and the ASS object
var renderer = new libjass.renderers.WebRenderer(ass, clock, videoElement.parentNode.parentNode, rendererSettings);
currentAssRenderer = renderer;
renderer.addEventListener("ready", function () {
try {
renderer.resize(videoElement.offsetWidth, videoElement.offsetHeight, 0, 0);
window.removeEventListener('resize', onVideoResize);
window.addEventListener('resize', onVideoResize);
2016-04-05 19:18:56 -07:00
window.removeEventListener('orientationchange', onVideoResize);
window.addEventListener('orientationchange', onVideoResize);
2016-04-04 18:23:42 -07:00
//clock.pause();
}
catch (ex) {
}
});
});
});
}
function onVideoResize() {
var renderer = currentAssRenderer;
if (renderer) {
var videoElement = mediaElement;
var width = videoElement.offsetWidth;
var height = videoElement.offsetHeight;
console.log('videoElement resized: ' + width + 'x' + height);
renderer.resize(width, height, 0, 0);
}
}
function renderTracksEvents(videoElement, track) {
var format = (track.format || '').toLowerCase();
if (format == 'ssa' || format == 'ass') {
// libjass is needed here
renderWithLibjass(videoElement, track);
return;
}
2016-02-24 23:09:10 -07:00
2016-02-26 21:39:01 -07:00
var trackElement = null;
var expectedId = 'manualTrack' + track.index;
2016-02-24 23:09:10 -07:00
2016-04-04 18:23:42 -07:00
var allTracks = videoElement.textTracks; // get list of tracks
2016-02-26 21:39:01 -07:00
for (var i = 0; i < allTracks.length; i++) {
2016-02-24 23:09:10 -07:00
2016-02-26 21:39:01 -07:00
var currentTrack = allTracks[i];
2016-02-24 23:09:10 -07:00
2016-02-26 21:39:01 -07:00
if (currentTrack.label == expectedId) {
trackElement = currentTrack;
break;
} else {
currentTrack.mode = 'disabled';
2016-02-24 23:09:10 -07:00
}
}
2016-02-26 21:39:01 -07:00
if (!trackElement) {
2016-04-04 18:23:42 -07:00
trackElement = videoElement.addTextTrack('subtitles', 'manualTrack' + track.index, track.language || 'und');
2016-02-26 21:39:01 -07:00
trackElement.label = 'manualTrack' + track.index;
2016-02-24 23:09:10 -07:00
2016-02-26 21:39:01 -07:00
// download the track json
2016-03-07 11:50:58 -07:00
fetchSubtitles(track).then(function (data) {
2016-02-24 23:09:10 -07:00
2016-02-26 21:39:01 -07:00
// show in ui
console.log('downloaded ' + data.TrackEvents.length + ' track events');
// add some cues to show the text
// in safari, the cues need to be added before setting the track mode to showing
2016-03-07 11:50:58 -07:00
data.TrackEvents.forEach(function (trackEvent) {
2016-04-04 12:07:43 -07:00
trackElement.addCue(new (window.VTTCue || window.TextTrackCue)(trackEvent.StartPositionTicks / 10000000, trackEvent.EndPositionTicks / 10000000, trackEvent.Text.replace(/\\N/gi, '\n')));
2016-02-26 21:39:01 -07:00
});
trackElement.mode = 'showing';
});
} else {
trackElement.mode = 'showing';
2016-02-24 23:09:10 -07:00
}
}
2016-04-04 12:07:43 -07:00
var currentSubtitlesElement;
var currentTrackEvents;
var customTrackIndex = -1;
var lastCustomTrackMs = 0;
var currentClock;
var currentAssRenderer;
function updateSubtitleText(timeMs) {
2016-04-04 18:23:42 -07:00
var clock = currentClock;
if (clock) {
clock.seek(timeMs / 1000);
}
2016-04-04 12:07:43 -07:00
var trackEvents = currentTrackEvents;
if (!trackEvents) {
return;
}
if (!currentSubtitlesElement) {
var videoSubtitlesElem = document.querySelector('.videoSubtitles');
if (!videoSubtitlesElem) {
videoSubtitlesElem = document.createElement('div');
videoSubtitlesElem.classList.add('videoSubtitles');
videoSubtitlesElem.innerHTML = '<div class="videoSubtitlesInner"></div>';
document.body.appendChild(videoSubtitlesElem);
}
currentSubtitlesElement = videoSubtitlesElem.querySelector('.videoSubtitlesInner');
}
if (lastCustomTrackMs > 0) {
if (Math.abs(lastCustomTrackMs - timeMs) < 500) {
return;
}
}
lastCustomTrackMs = new Date().getTime();
var positionTicks = timeMs * 10000;
for (var i = 0, length = trackEvents.length; i < length; i++) {
var caption = trackEvents[i];
if (positionTicks >= caption.StartPositionTicks && positionTicks <= caption.EndPositionTicks) {
currentSubtitlesElement.innerHTML = caption.Text;
currentSubtitlesElement.classList.remove('hide');
return;
}
}
currentSubtitlesElement.innerHTML = '';
currentSubtitlesElement.classList.add('hide');
}
2016-02-24 23:09:10 -07:00
self.setCurrentTrackElement = function (streamIndex) {
console.log('Setting new text track index to: ' + streamIndex);
2016-02-24 09:52:36 -07:00
var track = streamIndex == -1 ? null : currentTrackList.filter(function (t) {
return t.index == streamIndex;
})[0];
2016-02-24 23:09:10 -07:00
if (enableNativeTrackSupport(track)) {
2016-04-04 18:23:42 -07:00
setTrackForCustomDisplay(mediaElement, null);
2016-02-24 23:09:10 -07:00
} else {
2016-04-04 18:23:42 -07:00
setTrackForCustomDisplay(mediaElement, track);
2016-06-06 17:44:15 -07:00
2016-02-24 23:09:10 -07:00
// null these out to disable the player's native display (handled below)
streamIndex = -1;
track = null;
}
var expectedId = 'textTrack' + streamIndex;
2016-02-24 09:52:36 -07:00
var trackIndex = streamIndex == -1 || !track ? -1 : currentTrackList.indexOf(track);
2016-02-24 23:09:10 -07:00
var modes = ['disabled', 'showing', 'hidden'];
2015-06-13 07:46:59 -07:00
2016-02-24 23:09:10 -07:00
var allTracks = mediaElement.textTracks; // get list of tracks
2015-06-13 07:46:59 -07:00
for (var i = 0; i < allTracks.length; i++) {
2016-02-23 11:48:26 -07:00
var currentTrack = allTracks[i];
console.log('currentTrack id: ' + currentTrack.id);
2015-06-13 07:46:59 -07:00
var mode;
2016-02-24 09:52:36 -07:00
console.log('expectedId: ' + expectedId + '--currentTrack.Id:' + currentTrack.id);
2016-02-23 11:48:26 -07:00
// IE doesn't support track id
2016-09-02 08:56:19 -07:00
if (browser.msie || browser.edge) {
2016-02-23 11:48:26 -07:00
if (trackIndex == i) {
mode = 1; // show this track
} else {
mode = 0; // hide all other tracks
}
2015-06-13 07:46:59 -07:00
} else {
2016-02-26 21:39:01 -07:00
if (currentTrack.label.indexOf('manualTrack') != -1) {
continue;
}
2016-02-23 11:48:26 -07:00
if (currentTrack.id == expectedId) {
mode = 1; // show this track
} else {
mode = 0; // hide all other tracks
}
2015-06-13 07:46:59 -07:00
}
2015-12-23 10:46:01 -07:00
console.log('Setting track ' + i + ' mode to: ' + mode);
2015-06-13 07:46:59 -07:00
// Safari uses integers for the mode property
// http://www.jwplayer.com/html5/scripting/
// edit: not anymore
2015-06-13 07:46:59 -07:00
var useNumericMode = false;
2016-02-23 11:48:26 -07:00
if (!isNaN(currentTrack.mode)) {
//useNumericMode = true;
2015-06-13 07:46:59 -07:00
}
if (useNumericMode) {
2016-02-23 11:48:26 -07:00
currentTrack.mode = mode;
2015-06-13 07:46:59 -07:00
} else {
2016-02-23 11:48:26 -07:00
currentTrack.mode = modes[mode];
2015-06-13 07:46:59 -07:00
}
}
};
2016-07-26 21:54:38 -07:00
function replaceQueryString(url, param, value) {
var re = new RegExp("([?|&])" + param + "=.*?(&|$)", "i");
if (url.match(re))
return url.replace(re, '$1' + param + "=" + value + '$2');
else if (value) {
if (url.indexOf('?') == -1) {
return url + '?' + param + "=" + value;
}
return url + '&' + param + "=" + value;
}
return url;
}
2015-06-13 07:46:59 -07:00
self.updateTextStreamUrls = function (startPositionTicks) {
if (!self.supportsTextTracks()) {
return;
}
var allTracks = mediaElement.textTracks; // get list of tracks
2016-06-06 17:44:15 -07:00
var i;
for (i = 0; i < allTracks.length; i++) {
2015-06-13 07:46:59 -07:00
var track = allTracks[i];
// This throws an error in IE, but is fine in chrome
// In IE it's not necessary anyway because changing the src seems to be enough
try {
while (track.cues.length) {
track.removeCue(track.cues[0]);
}
} catch (e) {
2015-12-23 10:46:01 -07:00
console.log('Error removing cue from textTrack');
2015-06-13 07:46:59 -07:00
}
}
2016-06-06 17:44:15 -07:00
var trackElements = mediaElement.querySelectorAll('track');
for (i = 0; i < trackElements.length; i++) {
2015-06-13 07:46:59 -07:00
2016-06-06 17:44:15 -07:00
var trackElement = trackElements[i];
2015-07-03 04:51:45 -07:00
2016-06-06 17:44:15 -07:00
trackElement.src = replaceQueryString(trackElement.src, 'startPositionTicks', startPositionTicks);
}
2015-07-03 04:51:45 -07:00
};
self.enableCustomVideoControls = function () {
2015-06-13 07:46:59 -07:00
2016-09-02 08:56:19 -07:00
if (AppInfo.isNativeApp && browser.safari) {
2015-09-27 14:02:39 -07:00
2016-04-04 18:23:42 -07:00
if (navigator.userAgent.toLowerCase().indexOf('ipad') != -1) {
// Need to disable it in order to support picture in picture
return false;
2015-09-27 14:02:39 -07:00
}
2016-04-04 18:23:42 -07:00
return true;
2015-09-08 07:35:52 -07:00
}
2016-01-12 12:06:44 -07:00
return self.canAutoPlayVideo();
2015-07-03 04:51:45 -07:00
};
2015-06-13 07:46:59 -07:00
2015-07-03 04:51:45 -07:00
self.canAutoPlayVideo = function () {
2015-06-13 07:46:59 -07:00
2015-07-03 04:51:45 -07:00
if (AppInfo.isNativeApp) {
return true;
}
2016-09-02 08:56:19 -07:00
if (browser.mobile) {
2015-07-03 04:51:45 -07:00
return false;
}
return true;
};
self.init = function () {
2016-09-18 13:38:38 -07:00
return Promise.resolve();
2015-06-13 07:46:59 -07:00
};
2015-07-03 09:49:49 -07:00
if (options.type == 'audio') {
2015-06-07 20:16:42 -07:00
mediaElement = createAudioElement();
}
else {
mediaElement = createVideoElement();
}
}
2015-06-10 06:37:07 -07:00
if (!window.AudioRenderer) {
2015-07-03 09:49:49 -07:00
window.AudioRenderer = function (options) {
options = options || {};
options.type = 'audio';
return new htmlMediaRenderer(options);
};
2015-06-10 06:37:07 -07:00
}
if (!window.VideoRenderer) {
2015-07-03 09:49:49 -07:00
window.VideoRenderer = function (options) {
options = options || {};
options.type = 'video';
return new htmlMediaRenderer(options);
};
2015-06-10 06:37:07 -07:00
}
2015-06-07 20:16:42 -07:00
2016-03-16 14:30:49 -07:00
});