1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

initial timeshifting support

This commit is contained in:
Luke Pulverenti 2016-09-18 12:08:32 -04:00
parent 2971ec0e69
commit 92e4d82bd2
12 changed files with 78 additions and 62 deletions

View file

@ -5,6 +5,7 @@
border: none; border: none;
max-height: 84%; max-height: 84%;
border-radius: 1px !important; border-radius: 1px !important;
box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14), 0 6px 30px 5px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 0, 0, 0.4);
} }
.actionsheet-fullscreen { .actionsheet-fullscreen {

View file

@ -46,7 +46,7 @@ define(['browser'], function (browser) {
} }
function canPlayHlsWithMSE() { function canPlayHlsWithMSE() {
if (window.MediaSource != null && !browser.firefox) { if (window.MediaSource != null) {
// text tracks dont work with this in firefox // text tracks dont work with this in firefox
return true; return true;
} }
@ -389,7 +389,7 @@ define(['browser'], function (browser) {
} }
// Can't use mkv on mobile because we have to use the native player controls and they won't be able to seek it // Can't use mkv on mobile because we have to use the native player controls and they won't be able to seek it
if (canPlayMkv && options.supportsCustomSeeking && !browser.tizen) { if (canPlayMkv && options.supportsCustomSeeking && !browser.tizen && options.enableMkvProgressive !== false) {
profile.TranscodingProfiles.push({ profile.TranscodingProfiles.push({
Container: 'mkv', Container: 'mkv',
Type: 'Video', Type: 'Video',
@ -400,7 +400,7 @@ define(['browser'], function (browser) {
}); });
} }
if (canPlayTs && options.supportsCustomSeeking && !browser.tizen && !browser.web0s) { if (canPlayTs && options.supportsCustomSeeking && !browser.tizen && !browser.web0s && options.enableTsProgressive !== false) {
profile.TranscodingProfiles.push({ profile.TranscodingProfiles.push({
Container: 'ts', Container: 'ts',
Type: 'Video', Type: 'Video',
@ -414,7 +414,7 @@ define(['browser'], function (browser) {
}); });
} }
if (canPlayHls()) { if (canPlayHls() && options.enableHls !== false) {
profile.TranscodingProfiles.push({ profile.TranscodingProfiles.push({
Container: 'ts', Container: 'ts',
Type: 'Video', Type: 'Video',

View file

@ -30,15 +30,18 @@ define(['dialogHelper', 'dom', 'layoutManager', 'scrollHelper', 'globalize', 're
formDialogContent.style['max-height'] = '60%'; formDialogContent.style['max-height'] = '60%';
scrollHelper.centerFocus.on(formDialogContent, false); scrollHelper.centerFocus.on(formDialogContent, false);
} else { } else {
var minWidth = (Math.min(options.buttons.length * 150, dom.getWindowSize().innerWidth - 50)); dlg.style.maxWidth = (Math.min((options.buttons.length * 150) + 200, dom.getWindowSize().innerWidth - 50)) + 'px';
dlg.style.maxWidth = (minWidth + 200) + 'px';
} }
//dlg.querySelector('.btnCancel').addEventListener('click', function (e) { //dlg.querySelector('.btnCancel').addEventListener('click', function (e) {
// dialogHelper.close(dlg); // dialogHelper.close(dlg);
//}); //});
if (options.title) {
dlg.querySelector('.formDialogHeaderTitle').innerHTML = options.title || ''; dlg.querySelector('.formDialogHeaderTitle').innerHTML = options.title || '';
} else {
dlg.querySelector('.formDialogHeaderTitle').classList.add('hide');
}
dlg.querySelector('.text').innerHTML = options.html || options.text || ''; dlg.querySelector('.text').innerHTML = options.html || options.text || '';

View file

@ -15,12 +15,12 @@
margin: 0; margin: 0;
border-radius: 4px; border-radius: 4px;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14), 0 6px 30px 5px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 0, 0, 0.4);
border: 0; border: 0;
padding: 0; padding: 0;
will-change: transform; will-change: transform;
/* Strict does not work well with actionsheet */ /* Strict does not work well with actionsheet */
contain: style; contain: style;
box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14), 0 6px 30px 5px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 0, 0, 0.4);
} }
.dialog-fixedSize { .dialog-fixedSize {
@ -37,6 +37,7 @@
left: 0; left: 0;
right: 0; right: 0;
margin: 0; margin: 0;
box-shadow: none;
} }
@media all and (max-width: 1280px), all and (max-height: 720px) { @media all and (max-width: 1280px), all and (max-height: 720px) {
@ -47,6 +48,7 @@
left: 0 !important; left: 0 !important;
right: 0 !important; right: 0 !important;
margin: 0 !important; margin: 0 !important;
box-shadow: none;
} }
} }

View file

@ -89,7 +89,7 @@
</div> </div>
<div> <div>
<button is="emby-button" type="submit" class="raised button-submit block"> <button is="emby-button" type="submit" class="raised button-submit block">
<i class="md-icon">check</i><span>${ButtonSave}</span> <span>${ButtonSave}</span>
</button> </button>
<button is="emby-button" type="button" id="btnResetEasyPassword" class="raised cancel block hide"> <button is="emby-button" type="button" id="btnResetEasyPassword" class="raised cancel block hide">
<i class="md-icon">lock</i> <i class="md-icon">lock</i>

View file

@ -328,8 +328,6 @@
$('.sliderValue', elem).html(tooltext); $('.sliderValue', elem).html(tooltext);
console.log("slidin", pct, self.currentDurationTicks, time);
}); });
}); });
} }

View file

@ -253,7 +253,10 @@
self.duration = function (val) { self.duration = function (val) {
if (mediaElement) { if (mediaElement) {
return mediaElement.duration; var duration = mediaElement.duration;
if (duration && !isNaN(duration) && duration != Number.POSITIVE_INFINITY && duration != Number.NEGATIVE_INFINITY) {
return duration * 1000;
}
} }
return null; return null;

View file

@ -386,19 +386,15 @@
name: Globalize.translate('ButtonNo'), name: Globalize.translate('ButtonNo'),
id: 'no' id: 'no'
}); });
menuItems.push({
name: Globalize.translate('ButtonCancel'),
id: 'cancel'
});
require(['actionsheet'], function (actionsheet) { require(['dialog'], function (dialog) {
actionsheet.show({ dialog({
items: menuItems, buttons: menuItems,
//positionTo: positionTo, //positionTo: positionTo,
title: Globalize.translate('ConfirmEndPlayerSession'), text: Globalize.translate('ConfirmEndPlayerSession')
callback: function (id) {
}).then(function (id) {
switch (id) { switch (id) {
case 'yes': case 'yes':
@ -411,7 +407,6 @@
default: default:
break; break;
} }
}
}); });
}); });

View file

@ -595,24 +595,11 @@
return html; return html;
} }
function getSeekableDuration() {
if (self.currentMediaSource && self.currentMediaSource.RunTimeTicks) {
return self.currentMediaSource.RunTimeTicks;
}
if (self.currentMediaRenderer) {
return self.getCurrentTicks(self.currentMediaRenderer);
}
return null;
}
function onPositionSliderChange() { function onPositionSliderChange() {
var newPercent = parseFloat(this.value); var newPercent = parseFloat(this.value);
var newPositionTicks = (newPercent / 100) * getSeekableDuration(); var newPositionTicks = (newPercent / 100) * self.getSeekableDurationTicks();
self.changeStream(Math.floor(newPositionTicks)); self.changeStream(Math.floor(newPositionTicks));
} }
@ -808,7 +795,7 @@
positionSlider.getBubbleText = function (value) { positionSlider.getBubbleText = function (value) {
var seekableDuration = getSeekableDuration(); var seekableDuration = self.getSeekableDurationTicks();
if (!self.currentMediaSource || !seekableDuration) { if (!self.currentMediaSource || !seekableDuration) {
return '--:--'; return '--:--';
} }
@ -1016,7 +1003,7 @@
// Huge hack alert. Safari doesn't seem to like if the segments aren't available right away when playback starts // 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 // This will start the transcoding process before actually feeding the video url into the player
// Edit: Also seeing stalls from hls.js // Edit: Also seeing stalls from hls.js
if (!mediaSource.RunTimeTicks && isHls) { if (!mediaSource.RunTimeTicks && isHls && !browserInfo.edge) {
Dashboard.showLoadingMsg(); Dashboard.showLoadingMsg();
var hlsPlaylistUrl = streamInfo.url.replace('master.m3u8', 'live.m3u8'); var hlsPlaylistUrl = streamInfo.url.replace('master.m3u8', 'live.m3u8');
@ -1170,7 +1157,6 @@
document.body.classList.add('bodyWithPopupOpen'); document.body.classList.add('bodyWithPopupOpen');
self.currentMediaRenderer = mediaRenderer; self.currentMediaRenderer = mediaRenderer;
self.currentDurationTicks = self.currentMediaSource.RunTimeTicks;
self.updateNowPlayingInfo(item); self.updateNowPlayingInfo(item);

View file

@ -11,7 +11,6 @@ define(['appSettings', 'userSettings', 'appStorage', 'datetime'], function (appS
self.currentItem = null; self.currentItem = null;
self.currentMediaSource = null; self.currentMediaSource = null;
self.currentDurationTicks = null;
self.startTimeTicksOffset = null; self.startTimeTicksOffset = null;
self.playlist = []; self.playlist = [];
@ -146,7 +145,7 @@ define(['appSettings', 'userSettings', 'appStorage', 'datetime'], function (appS
// viblast can help us here // viblast can help us here
//return true; //return true;
return window.MediaSource && !browserInfo.firefox; return window.MediaSource;
}; };
self.changeStream = function (ticks, params) { self.changeStream = function (ticks, params) {
@ -166,7 +165,13 @@ define(['appSettings', 'userSettings', 'appStorage', 'datetime'], function (appS
var playSessionId = getParameterByName('PlaySessionId', currentSrc); var playSessionId = getParameterByName('PlaySessionId', currentSrc);
var liveStreamId = getParameterByName('LiveStreamId', currentSrc); var liveStreamId = getParameterByName('LiveStreamId', currentSrc);
Dashboard.getDeviceProfile().then(function (deviceProfile) { Dashboard.getDeviceProfile(null, {
enableMkvProgressive: self.currentMediaSource.RunTimeTicks != null,
enableTsProgressive: self.currentMediaSource.RunTimeTicks != null,
enableHls: !browserInfo.firefox || self.currentMediaSource.RunTimeTicks == null
}).then(function (deviceProfile) {
var audioStreamIndex = params.AudioStreamIndex == null ? (getParameterByName('AudioStreamIndex', currentSrc) || null) : params.AudioStreamIndex; var audioStreamIndex = params.AudioStreamIndex == null ? (getParameterByName('AudioStreamIndex', currentSrc) || null) : params.AudioStreamIndex;
if (typeof (audioStreamIndex) == 'string') { if (typeof (audioStreamIndex) == 'string') {
@ -264,6 +269,22 @@ define(['appSettings', 'userSettings', 'appStorage', 'datetime'], function (appS
//self.updateTextStreamUrls(streamInfo.startTimeTicksOffset || 0); //self.updateTextStreamUrls(streamInfo.startTimeTicksOffset || 0);
}; };
self.getSeekableDurationTicks = function () {
if (self.currentMediaSource && self.currentMediaSource.RunTimeTicks) {
return self.currentMediaSource.RunTimeTicks;
}
if (self.currentMediaRenderer) {
var duration = self.currentMediaRenderer.duration();
if (duration) {
return duration * 10000;
}
}
return null;
};
self.setCurrentTime = function (ticks, positionSlider, currentTimeElement) { self.setCurrentTime = function (ticks, positionSlider, currentTimeElement) {
// Convert to ticks // Convert to ticks
@ -272,13 +293,15 @@ define(['appSettings', 'userSettings', 'appStorage', 'datetime'], function (appS
var timeText = datetime.getDisplayRunningTime(ticks); var timeText = datetime.getDisplayRunningTime(ticks);
var mediaRenderer = self.currentMediaRenderer; var mediaRenderer = self.currentMediaRenderer;
if (self.currentDurationTicks) { var seekableDurationTicks = self.getSeekableDurationTicks();
timeText += " / " + datetime.getDisplayRunningTime(self.currentDurationTicks); if (seekableDurationTicks) {
timeText += " / " + datetime.getDisplayRunningTime(seekableDurationTicks);
if (positionSlider) { if (positionSlider) {
var percent = ticks / self.currentDurationTicks; var percent = ticks / seekableDurationTicks;
percent *= 100; percent *= 100;
positionSlider.value = percent; positionSlider.value = percent;
@ -287,7 +310,7 @@ define(['appSettings', 'userSettings', 'appStorage', 'datetime'], function (appS
if (positionSlider) { if (positionSlider) {
positionSlider.disabled = !((self.currentDurationTicks || 0) > 0 || canPlayerSeek()); positionSlider.disabled = !((seekableDurationTicks || 0) > 0 || canPlayerSeek());
} }
if (currentTimeElement) { if (currentTimeElement) {
@ -655,7 +678,13 @@ define(['appSettings', 'userSettings', 'appStorage', 'datetime'], function (appS
} }
var onBitrateDetected = function () { var onBitrateDetected = function () {
Dashboard.getDeviceProfile().then(function (deviceProfile) { Dashboard.getDeviceProfile(null, {
enableMkvProgressive: item.RunTimeTicks != null,
enableTsProgressive: item.RunTimeTicks != null,
enableHls: !browserInfo.firefox || item.RunTimeTicks == null
}).then(function (deviceProfile) {
playOnDeviceProfileCreated(deviceProfile, item, startPosition, callback); playOnDeviceProfileCreated(deviceProfile, item, startPosition, callback);
}); });
}; };
@ -1516,7 +1545,6 @@ define(['appSettings', 'userSettings', 'appStorage', 'datetime'], function (appS
Events.on(mediaRenderer, "timeupdate", onTimeUpdate); Events.on(mediaRenderer, "timeupdate", onTimeUpdate);
self.currentMediaRenderer = mediaRenderer; self.currentMediaRenderer = mediaRenderer;
self.currentDurationTicks = self.currentMediaSource.RunTimeTicks;
mediaRenderer.init().then(function () { mediaRenderer.init().then(function () {

View file

@ -670,7 +670,7 @@ var Dashboard = {
}); });
}, },
getDeviceProfile: function (maxHeight) { getDeviceProfile: function (maxHeight, profileOptions) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
@ -838,9 +838,9 @@ var Dashboard = {
supportsCustomSeeking = true; supportsCustomSeeking = true;
} }
var profile = profileBuilder({ var profile = profileBuilder(Object.assign(profileOptions || {}, {
supportsCustomSeeking: supportsCustomSeeking supportsCustomSeeking: supportsCustomSeeking
}); }));
if (!(AppInfo.isNativeApp && browserInfo.android) && !browserInfo.edge && !browserInfo.msie) { if (!(AppInfo.isNativeApp && browserInfo.android) && !browserInfo.edge && !browserInfo.msie) {
// libjass not working here // libjass not working here

View file

@ -30,7 +30,7 @@
</div> </div>
<br /> <br />
<div> <div>
<button is="emby-button" type="submit" class="raised button-submit block"><i class="md-icon">check</i><span>${ButtonSave}</span></button> <button is="emby-button" type="submit" class="raised button-submit block"><span>${ButtonSave}</span></button>
<button is="emby-button" type="button" id="btnResetPassword" class="raised button-cancel block hide"> <button is="emby-button" type="button" id="btnResetPassword" class="raised button-cancel block hide">
<i class="md-icon">lock</i> <i class="md-icon">lock</i>
<span>${ButtonResetPassword}</span> <span>${ButtonResetPassword}</span>