3.0.5621.2
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 148 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 26 KiB |
|
@ -175,10 +175,8 @@
|
||||||
<div>
|
<div>
|
||||||
<fieldset data-role="controlgroup">
|
<fieldset data-role="controlgroup">
|
||||||
<legend>${LabelExternalPlayers}</legend>
|
<legend>${LabelExternalPlayers}</legend>
|
||||||
<!--<input type="checkbox" id="chkGoodplayer" class="chkExternalPlayer" data-name="GoodPlayer" data-scheme="goodplayer://{0}" />
|
<input type="checkbox" id="chkExternalVideoPlayer" />
|
||||||
<label for="chkGoodplayer">GoodPlayer</label>-->
|
<label for="chkExternalVideoPlayer">${OptionEnableExternalVideoPlayers}</label>
|
||||||
<input type="checkbox" id="chkVlc" class="chkExternalPlayer" data-name="Vlc" data-scheme="vlc://{0}" />
|
|
||||||
<label for="chkVlc">Vlc</label>
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<div style="padding: 0 2px;">${LabelExternalPlayersHelp}</div>
|
<div style="padding: 0 2px;">${LabelExternalPlayersHelp}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -809,11 +809,9 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($.browser.chrome) {
|
|
||||||
requirejs(["thirdparty/cast_sender"], function () {
|
requirejs(["thirdparty/cast_sender"], function () {
|
||||||
|
|
||||||
initializeChromecast();
|
initializeChromecast();
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
})(window, window.chrome, console);
|
})(window, window.chrome, console);
|
|
@ -1012,8 +1012,7 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).on('pagebeforeshowready', "#dashboardPage", DashboardPage.onPageShow)
|
$(document).on('pageshowready', "#dashboardPage", DashboardPage.onPageShow).on('pagehide', "#dashboardPage", DashboardPage.onPageHide);
|
||||||
.on('pagehide', "#dashboardPage", DashboardPage.onPageHide);
|
|
||||||
|
|
||||||
(function ($, document, window) {
|
(function ($, document, window) {
|
||||||
|
|
||||||
|
@ -1308,7 +1307,7 @@ $(document).on('pagebeforeshowready', "#dashboardPage", DashboardPage.onPageShow
|
||||||
result.CustomPrefs[welcomeTourKey] = welcomeDismissValue;
|
result.CustomPrefs[welcomeTourKey] = welcomeDismissValue;
|
||||||
ApiClient.updateDisplayPreferences('dashboard', result, userId, 'dashboard');
|
ApiClient.updateDisplayPreferences('dashboard', result, userId, 'dashboard');
|
||||||
|
|
||||||
$(page).off('pagebeforeshow.checktour');
|
$(page).off('.checktour');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1373,7 +1372,7 @@ $(document).on('pagebeforeshowready', "#dashboardPage", DashboardPage.onPageShow
|
||||||
takeTour(page, Dashboard.getCurrentUserId());
|
takeTour(page, Dashboard.getCurrentUserId());
|
||||||
});
|
});
|
||||||
|
|
||||||
}).on('pagebeforeshowready.checktour', "#dashboardPage", function () {
|
}).on('pageshowready.checktour', "#dashboardPage", function () {
|
||||||
|
|
||||||
var page = this;
|
var page = this;
|
||||||
|
|
||||||
|
@ -1389,7 +1388,7 @@ $(document).on('pagebeforeshowready', "#dashboardPage", DashboardPage.onPageShow
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
|
|
||||||
$(document).on('pagebeforeshowready', ".type-interior", function () {
|
$(document).on('pageshowready', ".type-interior", function () {
|
||||||
|
|
||||||
var page = this;
|
var page = this;
|
||||||
|
|
||||||
|
|
|
@ -1,315 +1,301 @@
|
||||||
(function (window, store) {
|
(function (window, store) {
|
||||||
|
|
||||||
function getExternalPlayers() {
|
function getDeviceProfile(serverAddress, deviceId, item, startPositionTicks, maxBitrate, mediaSourceId, audioStreamIndex, subtitleStreamIndex) {
|
||||||
return JSON.parse(store.getItem('externalplayers') || '[]');
|
|
||||||
|
var bitrateSetting = AppSettings.maxStreamingBitrate();
|
||||||
|
|
||||||
|
var profile = {};
|
||||||
|
|
||||||
|
profile.MaxStreamingBitrate = bitrateSetting;
|
||||||
|
profile.MaxStaticBitrate = 40000000;
|
||||||
|
profile.MusicStreamingTranscodingBitrate = Math.min(bitrateSetting, 192000);
|
||||||
|
|
||||||
|
profile.DirectPlayProfiles = [];
|
||||||
|
|
||||||
|
profile.DirectPlayProfiles.push({
|
||||||
|
Container: 'mkv,mov,mp4,m4v,wmv',
|
||||||
|
Type: 'Video'
|
||||||
|
});
|
||||||
|
|
||||||
|
profile.DirectPlayProfiles.push({
|
||||||
|
Container: 'aac,mp3,flac,wma',
|
||||||
|
Type: 'Audio'
|
||||||
|
});
|
||||||
|
|
||||||
|
profile.TranscodingProfiles = [];
|
||||||
|
|
||||||
|
profile.TranscodingProfiles.push({
|
||||||
|
Container: 'ts',
|
||||||
|
Type: 'Video',
|
||||||
|
AudioCodec: 'aac',
|
||||||
|
VideoCodec: 'h264',
|
||||||
|
Context: 'Streaming',
|
||||||
|
Protocol: 'hls'
|
||||||
|
});
|
||||||
|
|
||||||
|
profile.TranscodingProfiles.push({
|
||||||
|
Container: 'aac',
|
||||||
|
Type: 'Audio',
|
||||||
|
AudioCodec: 'aac',
|
||||||
|
Context: 'Streaming',
|
||||||
|
Protocol: 'hls'
|
||||||
|
});
|
||||||
|
|
||||||
|
profile.ContainerProfiles = [];
|
||||||
|
|
||||||
|
var audioConditions = [];
|
||||||
|
|
||||||
|
var maxAudioChannels = '6';
|
||||||
|
|
||||||
|
audioConditions.push({
|
||||||
|
Condition: 'LessThanEqual',
|
||||||
|
Property: 'AudioChannels',
|
||||||
|
Value: maxAudioChannels
|
||||||
|
});
|
||||||
|
|
||||||
|
profile.CodecProfiles = [];
|
||||||
|
profile.CodecProfiles.push({
|
||||||
|
Type: 'Audio',
|
||||||
|
Conditions: audioConditions
|
||||||
|
});
|
||||||
|
|
||||||
|
profile.CodecProfiles.push({
|
||||||
|
Type: 'VideoAudio',
|
||||||
|
Codec: 'mp3',
|
||||||
|
Conditions: [{
|
||||||
|
Condition: 'LessThanEqual',
|
||||||
|
Property: 'AudioChannels',
|
||||||
|
Value: maxAudioChannels
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
profile.CodecProfiles.push({
|
||||||
|
Type: 'VideoAudio',
|
||||||
|
Codec: 'aac',
|
||||||
|
Conditions: [
|
||||||
|
{
|
||||||
|
Condition: 'LessThanEqual',
|
||||||
|
Property: 'AudioChannels',
|
||||||
|
Value: maxAudioChannels
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
profile.CodecProfiles.push({
|
||||||
|
Type: 'Video',
|
||||||
|
Codec: 'h264',
|
||||||
|
Conditions: [
|
||||||
|
{
|
||||||
|
Condition: 'EqualsAny',
|
||||||
|
Property: 'VideoProfile',
|
||||||
|
Value: 'high|main|baseline|constrained baseline'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Condition: 'LessThanEqual',
|
||||||
|
Property: 'VideoLevel',
|
||||||
|
Value: '41'
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Subtitle profiles
|
||||||
|
profile.SubtitleProfiles = [];
|
||||||
|
profile.ResponseProfiles = [];
|
||||||
|
|
||||||
|
return profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUrl(player, item) {
|
function validatePlaybackInfoResult(result) {
|
||||||
|
|
||||||
return 'vlc://http://www.google.com';
|
if (result.ErrorCode) {
|
||||||
|
|
||||||
}
|
MediaController.showPlaybackInfoErrorMessage(result.ErrorCode);
|
||||||
|
|
||||||
function getCodecLimits(maxBitrate) {
|
|
||||||
|
|
||||||
var maxWidth;
|
|
||||||
|
|
||||||
if (maxBitrate <= 1000000) {
|
|
||||||
maxWidth = 720;
|
|
||||||
}
|
|
||||||
else if (maxBitrate <= 5000000) {
|
|
||||||
maxWidth = 1280;
|
|
||||||
} else {
|
|
||||||
maxWidth = 1280;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
|
|
||||||
maxVideoAudioChannels: 6,
|
|
||||||
maxAudioChannels: 2,
|
|
||||||
maxVideoLevel: 50,
|
|
||||||
maxWidth: maxWidth,
|
|
||||||
maxSampleRate: 48000
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function canDirectStream(mediaType, mediaSource, maxBitrate) {
|
|
||||||
|
|
||||||
// If bitrate is unknown don't direct stream
|
|
||||||
if (!mediaSource.Bitrate || mediaSource.Bitrate > maxBitrate) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var codecLimits = getCodecLimits(maxBitrate);
|
|
||||||
|
|
||||||
if (mediaType == "Audio") {
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (mediaType == "Video") {
|
|
||||||
|
|
||||||
var videoStream = mediaSource.MediaStreams.filter(function (s) {
|
|
||||||
|
|
||||||
return s.Type == 'Video';
|
|
||||||
|
|
||||||
})[0];
|
|
||||||
|
|
||||||
if (!videoStream) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (videoStream.Width && videoStream.Width > codecLimits.maxWidth) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mediaSource.VideoType != 'VideoFile') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mediaSource.Protocol == 'File';
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Unrecognized MediaType');
|
|
||||||
}
|
|
||||||
|
|
||||||
function canPlayAudioStreamDirect(audioStream, isVideo, maxBitrate) {
|
|
||||||
|
|
||||||
var audioCodec = (audioStream.Codec || '').toLowerCase().replace('-', '');
|
|
||||||
|
|
||||||
if (audioCodec.indexOf('aac') == -1 &&
|
|
||||||
audioCodec.indexOf('mp3') == -1 &&
|
|
||||||
audioCodec.indexOf('mpeg') == -1) {
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var codecLimits = getCodecLimits(maxBitrate);
|
|
||||||
|
|
||||||
var maxChannels = isVideo ? codecLimits.maxVideoAudioChannels : codecLimits.maxAudioChannels;
|
|
||||||
|
|
||||||
if (!audioStream.Channels || audioStream.Channels > maxChannels) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!audioStream.SampleRate || audioStream.SampleRate > codecLimits.maxSampleRate) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var bitrate = audioStream.BitRate;
|
|
||||||
if (!bitrate) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isVideo) {
|
|
||||||
|
|
||||||
if (audioCodec.indexOf('aac') != -1 && bitrate > 768000) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (audioCodec.indexOf('mp3') != -1 || audioCodec.indexOf('mpeg') != -1) {
|
|
||||||
if (bitrate > 320000) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (bitrate > 320000) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSupportedCodec(mediaType, mediaSource) {
|
function getOptimalMediaSource(mediaType, versions) {
|
||||||
|
|
||||||
if (mediaType == "Audio") {
|
var optimalVersion = versions.filter(function (v) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (mediaType == "Video") {
|
|
||||||
|
|
||||||
return mediaSource.MediaStreams.filter(function (m) {
|
v.enableDirectPlay = MediaController.supportsDirectPlay(v);
|
||||||
|
|
||||||
return m.Type == "Video" && (m.Codec || '').toLowerCase() == 'h264';
|
return v.enableDirectPlay;
|
||||||
|
|
||||||
}).length > 0;
|
})[0];
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Unrecognized MediaType');
|
if (!optimalVersion) {
|
||||||
}
|
optimalVersion = versions.filter(function (v) {
|
||||||
|
|
||||||
function getStreamByIndex(streams, type, index) {
|
return v.SupportsDirectStream;
|
||||||
return streams.filter(function (s) {
|
|
||||||
|
|
||||||
return s.Type == type && s.Index == index;
|
|
||||||
|
|
||||||
})[0];
|
})[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMediaSourceInfo(item, maxBitrate, mediaSourceId, audioStreamIndex, subtitleStreamIndex) {
|
return optimalVersion || versions.filter(function (s) {
|
||||||
|
return s.SupportsTranscoding;
|
||||||
var sources = item.MediaSources.filter(function (m) {
|
|
||||||
|
|
||||||
m.audioStream = mediaSourceId == m.Id && audioStreamIndex != null ?
|
|
||||||
getStreamByIndex(m.MediaStreams, 'Audio', audioStreamIndex) :
|
|
||||||
getStreamByIndex(m.MediaStreams, 'Audio', m.DefaultAudioStreamIndex);
|
|
||||||
|
|
||||||
if (item.MediaType == "Audio" && !m.audioStream) {
|
|
||||||
m.audioStream = m.MediaStreams.filter(function (s) {
|
|
||||||
return s.Type == 'Audio';
|
|
||||||
})[0];
|
})[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
m.subtitleStream = mediaSourceId == m.Id && subtitleStreamIndex != null ?
|
var currentMediaSource;
|
||||||
getStreamByIndex(m.MediaStreams, 'Subtitle', subtitleStreamIndex) :
|
var currentItem;
|
||||||
getStreamByIndex(m.MediaStreams, 'Subtitle', m.DefaultSubtitleStreamIndex);
|
var basePlayerState;
|
||||||
|
var progressInterval;
|
||||||
|
|
||||||
return !mediaSourceId || m.Id == mediaSourceId;
|
function getVideoStreamInfo(item) {
|
||||||
|
|
||||||
|
var deferred = $.Deferred();
|
||||||
|
Dashboard.showModalLoadingMsg();
|
||||||
|
|
||||||
|
var deviceProfile = getDeviceProfile();
|
||||||
|
var startPosition = 0;
|
||||||
|
|
||||||
|
MediaController.getPlaybackInfo(item.Id, deviceProfile, startPosition).done(function (playbackInfoResult) {
|
||||||
|
|
||||||
|
if (validatePlaybackInfoResult(playbackInfoResult)) {
|
||||||
|
|
||||||
|
var mediaSource = getOptimalMediaSource(item.MediaType, playbackInfoResult.MediaSources);
|
||||||
|
|
||||||
|
if (mediaSource) {
|
||||||
|
|
||||||
|
if (mediaSource.RequiresOpening) {
|
||||||
|
|
||||||
|
MediaController.getLiveStream(item.Id, playbackInfoResult.PlaySessionId, deviceProfile, startPosition, mediaSource, null, null).done(function (openLiveStreamResult) {
|
||||||
|
|
||||||
|
openLiveStreamResult.MediaSource.enableDirectPlay = MediaController.supportsDirectPlay(openLiveStreamResult.MediaSource);
|
||||||
|
|
||||||
|
playInternalPostMediaSourceSelection(item, openLiveStreamResult.MediaSource, startPosition, deferred);
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
playInternalPostMediaSourceSelection(item, mediaSource, startPosition, deferred);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Dashboard.hideModalLoadingMsg();
|
||||||
|
MediaController.showPlaybackInfoErrorMessage('NoCompatibleStream');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Find first one that can be direct streamed
|
return deferred.promise();
|
||||||
var source = sources.filter(function (m) {
|
|
||||||
|
|
||||||
var audioStream = m.audioStream;
|
|
||||||
|
|
||||||
if (!audioStream || !canPlayAudioStreamDirect(audioStream, item.MediaType == 'Video', maxBitrate)) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m.subtitleStream && m.subtitleStream.IsExternal) {
|
function playInternalPostMediaSourceSelection(item, mediaSource, startPosition, deferred) {
|
||||||
return false;
|
|
||||||
|
Dashboard.hideModalLoadingMsg();
|
||||||
|
|
||||||
|
currentItem = item;
|
||||||
|
currentMediaSource = mediaSource;
|
||||||
|
|
||||||
|
basePlayerState = {
|
||||||
|
PlayState: {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return canDirectStream(item.MediaType, m, maxBitrate, audioStream);
|
|
||||||
|
|
||||||
})[0];
|
|
||||||
|
|
||||||
if (source) {
|
|
||||||
return {
|
|
||||||
mediaSource: source,
|
|
||||||
isStatic: true,
|
|
||||||
streamContainer: source.Container
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var streamInfo = MediaPlayer.createStreamInfo('Video', item, mediaSource, startPosition);
|
||||||
|
var currentSrc = streamInfo.url;
|
||||||
|
|
||||||
|
var audioStreamIndex = getParameterByName('AudioStreamIndex', currentSrc);
|
||||||
|
|
||||||
|
if (audioStreamIndex) {
|
||||||
|
basePlayerState.PlayState.AudioStreamIndex = parseInt(audioStreamIndex);
|
||||||
|
}
|
||||||
|
basePlayerState.PlayState.SubtitleStreamIndex = self.currentSubtitleStreamIndex;
|
||||||
|
|
||||||
|
basePlayerState.PlayState.PlayMethod = getParameterByName('static', currentSrc) == 'true' ?
|
||||||
|
'DirectStream' :
|
||||||
|
'Transcode';
|
||||||
|
|
||||||
|
basePlayerState.PlayState.LiveStreamId = getParameterByName('LiveStreamId', currentSrc);
|
||||||
|
basePlayerState.PlayState.PlaySessionId = getParameterByName('PlaySessionId', currentSrc);
|
||||||
|
|
||||||
|
basePlayerState.PlayState.MediaSourceId = mediaSource.Id;
|
||||||
|
basePlayerState.PlayState.CanSeek = false;
|
||||||
|
basePlayerState.NowPlayingItem = MediaPlayer.getNowPlayingItemForReporting(item, mediaSource);
|
||||||
|
|
||||||
|
deferred.resolveWith(null, [streamInfo]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find first one with supported codec
|
function getPlayerState(positionTicks) {
|
||||||
source = sources.filter(function (m) {
|
|
||||||
|
|
||||||
return isSupportedCodec(item.MediaType, m);
|
var state = basePlayerState;
|
||||||
|
|
||||||
})[0];
|
state.PlayState.PositionTicks = positionTicks;
|
||||||
|
|
||||||
source = source || sources[0];
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
var container = item.MediaType == 'Audio' ? 'mp3' : 'm3u8';
|
function onPlaybackStart() {
|
||||||
|
|
||||||
// Default to first one
|
closePlayMenu();
|
||||||
return {
|
|
||||||
mediaSource: source,
|
var state = getPlayerState();
|
||||||
isStatic: false,
|
|
||||||
streamContainer: container
|
var info = {
|
||||||
|
ItemId: state.NowPlayingItem.Id,
|
||||||
|
NowPlayingItem: state.NowPlayingItem
|
||||||
};
|
};
|
||||||
|
|
||||||
|
info = $.extend(info, state.PlayState);
|
||||||
|
|
||||||
|
ApiClient.reportPlaybackStart(info);
|
||||||
|
|
||||||
|
// This is really just a ping to let the server know we're still playing
|
||||||
|
progressInterval = setInterval(function () {
|
||||||
|
onPlaybackProgress(null);
|
||||||
|
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
|
||||||
|
showPostPlayMenu(currentItem);
|
||||||
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStreamInfo(serverAddress, deviceId, item, startPositionTicks, maxBitrate, mediaSourceId, audioStreamIndex, subtitleStreamIndex) {
|
function onPlaybackProgress(positionTicks) {
|
||||||
|
|
||||||
var mediaSourceInfo = getMediaSourceInfo(item, maxBitrate, mediaSourceId, audioStreamIndex, subtitleStreamIndex);
|
var state = getPlayerState(positionTicks);
|
||||||
|
|
||||||
var url = getStreamUrl(serverAddress, deviceId, item.MediaType, item.Id, mediaSourceInfo, startPositionTicks, maxBitrate);
|
var info = {
|
||||||
|
ItemId: state.NowPlayingItem.Id,
|
||||||
|
NowPlayingItem: state.NowPlayingItem
|
||||||
|
};
|
||||||
|
|
||||||
if (mediaSourceInfo.subtitleStream && mediaSourceInfo.subtitleStream.IsExternal) {
|
info = $.extend(info, state.PlayState);
|
||||||
url += "&SubtitleStreamIndex=" + mediaSourceInfo.Index;
|
|
||||||
|
ApiClient.reportPlaybackProgress(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaSourceInfo.url = url;
|
function onPlaybackStopped(positionTicks) {
|
||||||
|
|
||||||
return mediaSourceInfo;
|
var state = getPlayerState(positionTicks);
|
||||||
|
|
||||||
|
var stopInfo = {
|
||||||
|
itemId: state.NowPlayingItem.Id,
|
||||||
|
mediaSourceId: state.PlayState.MediaSourceId,
|
||||||
|
positionTicks: state.PlayState.PositionTicks
|
||||||
|
};
|
||||||
|
|
||||||
|
if (state.PlayState.LiveStreamId) {
|
||||||
|
stopInfo.LiveStreamId = state.PlayState.LiveStreamId;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStreamUrl(serverAddress, deviceId, mediaType, itemId, mediaSourceInfo, startPositionTicks, maxBitrate) {
|
if (state.PlayState.PlaySessionId) {
|
||||||
|
stopInfo.PlaySessionId = state.PlayState.PlaySessionId;
|
||||||
var url;
|
|
||||||
|
|
||||||
var codecLimits = getCodecLimits(maxBitrate);
|
|
||||||
|
|
||||||
if (mediaType == 'Audio') {
|
|
||||||
|
|
||||||
url = serverAddress + '/audio/' + itemId + '/stream.' + mediaSourceInfo.streamContainer;
|
|
||||||
|
|
||||||
url += '?mediasourceid=' + mediaSourceInfo.mediaSource.Id;
|
|
||||||
|
|
||||||
if (mediaSourceInfo.isStatic) {
|
|
||||||
url += '&static=true';
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
url += '&maxaudiochannels=' + codecLimits.maxAudioChannels;
|
|
||||||
|
|
||||||
if (startPositionTicks) {
|
|
||||||
url += '&startTimeTicks=' + startPositionTicks.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxBitrate) {
|
ApiClient.reportPlaybackStopped(stopInfo);
|
||||||
url += '&audiobitrate=' + Math.min(maxBitrate, 320000).toString();
|
|
||||||
|
if (progressInterval) {
|
||||||
|
clearInterval(progressInterval);
|
||||||
|
progressInterval = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
url += '&deviceId=' + deviceId;
|
function showPostPlayMenu(item) {
|
||||||
}
|
|
||||||
|
|
||||||
return url;
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (mediaType == 'Video') {
|
|
||||||
|
|
||||||
if (mediaSourceInfo.isStatic) {
|
|
||||||
url = serverAddress + '/videos/' + itemId + '/stream.' + mediaSourceInfo.streamContainer + '?static=true';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
url = serverAddress + '/videos/' + itemId + '/stream.' + mediaSourceInfo.streamContainer + '?static=false';
|
|
||||||
}
|
|
||||||
|
|
||||||
url += '&maxaudiochannels=' + codecLimits.maxVideoAudioChannels;
|
|
||||||
|
|
||||||
if (maxBitrate) {
|
|
||||||
|
|
||||||
var audioRate = 320000;
|
|
||||||
url += '&audiobitrate=' + audioRate.toString();
|
|
||||||
url += '&videobitrate=' + (maxBitrate - audioRate).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
url += '&profile=high';
|
|
||||||
url += '&level=41';
|
|
||||||
|
|
||||||
url += '&maxwidth=' + codecLimits.maxWidth;
|
|
||||||
|
|
||||||
url += '&videoCodec=h264';
|
|
||||||
url += '&audioCodec=aac';
|
|
||||||
|
|
||||||
url += '&mediasourceid=' + mediaSourceInfo.mediaSource.Id;
|
|
||||||
url += '&deviceId=' + deviceId;
|
|
||||||
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Unrecognized MediaType');
|
|
||||||
}
|
|
||||||
|
|
||||||
function getVideoUrl(item) {
|
|
||||||
|
|
||||||
var maxBitrate = AppSettings.maxStreamingBitrate();
|
|
||||||
|
|
||||||
var info = getStreamInfo(ApiClient.serverAddress(), ApiClient.deviceId(), item, null, maxBitrate);
|
|
||||||
|
|
||||||
return info.url;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPlayerUrl(item, player) {
|
|
||||||
|
|
||||||
return player.scheme.replace('{0}', getVideoUrl(item));
|
|
||||||
}
|
|
||||||
|
|
||||||
function showPostPlayMenu(item, userId) {
|
|
||||||
|
|
||||||
$('.externalPlayerPostPlayFlyout').popup("close").remove();
|
$('.externalPlayerPostPlayFlyout').popup("close").remove();
|
||||||
|
|
||||||
|
@ -379,23 +365,24 @@
|
||||||
|
|
||||||
$('.externalPlayerPostPlayFlyout').popup("close").remove();
|
$('.externalPlayerPostPlayFlyout').popup("close").remove();
|
||||||
|
|
||||||
ApiClient.stopActiveEncodings();
|
var position = 0;
|
||||||
|
|
||||||
if ($('#radioMarkInProgress', elem).checked()) {
|
if ($('#radioMarkInProgress', elem).checked()) {
|
||||||
|
|
||||||
var pct = $(".playstateSlider", elem).val();
|
var pct = $(".playstateSlider", elem).val();
|
||||||
var ticks = item.RunTimeTicks * (Number(pct) * .01);
|
var ticks = item.RunTimeTicks * (Number(pct) * .01);
|
||||||
|
|
||||||
ApiClient.markPlayed(userId, item.Id, new Date());
|
position = ticks;
|
||||||
}
|
}
|
||||||
else if (autoMarkWatched || $('#radioMarkWatched', elem).checked()) {
|
else if (autoMarkWatched || $('#radioMarkWatched', elem).checked()) {
|
||||||
|
|
||||||
ApiClient.markPlayed(userId, item.Id, new Date());
|
position = currentMediaSource.RunTimeTicks;
|
||||||
}
|
}
|
||||||
else if ($('#radioMarkUnwatched', elem).checked()) {
|
else if ($('#radioMarkUnwatched', elem).checked()) {
|
||||||
|
|
||||||
ApiClient.markUnplayed(userId, item.Id);
|
position = 0;
|
||||||
}
|
}
|
||||||
|
onPlaybackStopped(position);
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".playstateSlider", elem).on("change", function (e) {
|
$(".playstateSlider", elem).on("change", function (e) {
|
||||||
|
@ -417,11 +404,11 @@
|
||||||
$('.externalPlayerFlyout').popup("close").remove();
|
$('.externalPlayerFlyout').popup("close").remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
function showMenuForItem(item, userId) {
|
function showMenuForItem(item, players) {
|
||||||
|
|
||||||
closePlayMenu();
|
closePlayMenu();
|
||||||
|
|
||||||
var html = '<div data-role="popup" class="externalPlayerFlyout" data-theme="a">';
|
var html = '<div data-role="popup" class="externalPlayerFlyout" data-theme="a" data-dismissible="false">';
|
||||||
|
|
||||||
html += '<ul data-role="listview" style="min-width: 200px;">';
|
html += '<ul data-role="listview" style="min-width: 200px;">';
|
||||||
html += '<li data-role="list-divider" style="padding: 1em;text-align:center;">' + Globalize.translate('HeaderSelectExternalPlayer') + '</li>';
|
html += '<li data-role="list-divider" style="padding: 1em;text-align:center;">' + Globalize.translate('HeaderSelectExternalPlayer') + '</li>';
|
||||||
|
@ -429,9 +416,9 @@
|
||||||
|
|
||||||
html += '<div style="padding:1em;">';
|
html += '<div style="padding:1em;">';
|
||||||
|
|
||||||
html += getExternalPlayers().map(function (p) {
|
html += players.map(function (p) {
|
||||||
|
|
||||||
return '<a href="' + getPlayerUrl(item, p) + '" data-role="button" data-icon="play" class="btnExternalPlayer">' + p.name + '</a>';
|
return '<a href="' + p.url + '" data-role="button" data-icon="play" class="btnExternalPlayer" data-theme="b" data-mini="true">' + p.name + '</a>';
|
||||||
|
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
|
@ -449,12 +436,7 @@
|
||||||
|
|
||||||
$('.btnExternalPlayer', elem).on('click', function () {
|
$('.btnExternalPlayer', elem).on('click', function () {
|
||||||
|
|
||||||
closePlayMenu();
|
ExternalPlayer.onPlaybackStart();
|
||||||
|
|
||||||
setTimeout(function () {
|
|
||||||
|
|
||||||
showPostPlayMenu(item, userId);
|
|
||||||
}, 500);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,18 +446,41 @@
|
||||||
|
|
||||||
ApiClient.getItem(userId, itemId).done(function (item) {
|
ApiClient.getItem(userId, itemId).done(function (item) {
|
||||||
|
|
||||||
setTimeout(function () {
|
getVideoStreamInfo(item).done(function (streamInfo) {
|
||||||
|
|
||||||
showMenuForItem(item, userId);
|
setTimeout(function () {
|
||||||
|
ExternalPlayer.showPlayerSelectionMenu(item, streamInfo.url, streamInfo.mimeType);
|
||||||
}, 500);
|
}, 500);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getExternalPlayers(url, mimeType) {
|
||||||
|
|
||||||
|
var deferred = $.Deferred();
|
||||||
|
|
||||||
|
var players = [
|
||||||
|
{ name: 'Vlc', url: 'vlc://' + url, id: 'vlc' }
|
||||||
|
];
|
||||||
|
deferred.resolveWith(null, [players]);
|
||||||
|
|
||||||
|
return deferred.promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
function showPlayerSelectionMenu(item, url, mimeType) {
|
||||||
|
|
||||||
|
ExternalPlayer.getExternalPlayers(url, mimeType).done(function (players) {
|
||||||
|
showMenuForItem(item, players);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
window.ExternalPlayer = {
|
window.ExternalPlayer = {
|
||||||
|
|
||||||
getUrl: getUrl,
|
showMenu: showPlayMenu,
|
||||||
|
onPlaybackStart: onPlaybackStart,
|
||||||
|
onPlaybackStopped: onPlaybackStopped,
|
||||||
getExternalPlayers: getExternalPlayers,
|
getExternalPlayers: getExternalPlayers,
|
||||||
showMenu: showPlayMenu
|
showPlayerSelectionMenu: showPlayerSelectionMenu
|
||||||
};
|
};
|
||||||
|
|
||||||
})(window, window.appStorage);
|
})(window, window.appStorage);
|
|
@ -177,8 +177,7 @@
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
$(elem).html(html).lazyChildren();
|
$(elem).html(html).lazyChildren().createCardMenus();
|
||||||
$(elem).createCardMenus();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,8 +212,7 @@
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
$(elem).html(html).lazyChildren();
|
$(elem).html(html).lazyChildren().createCardMenus();
|
||||||
$(elem).createCardMenus();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,8 +314,7 @@
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
$(elem).html(html).lazyChildren();
|
$(elem).html(html).lazyChildren().createCardMenus();
|
||||||
$(elem).createCardMenus();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,8 +403,7 @@
|
||||||
});
|
});
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
var elem = $('#channel' + channel.Id + '', page).html(html).lazyChildren().trigger('create');
|
$('#channel' + channel.Id + '', page).html(html).lazyChildren().trigger('create').createCardMenus();
|
||||||
$(elem).createCardMenus();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -232,13 +232,20 @@
|
||||||
return html;
|
return html;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
playInExternalPlayer: function(id) {
|
||||||
|
|
||||||
|
Dashboard.loadExternalPlayer().done(function () {
|
||||||
|
ExternalPlayer.showMenu(id);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
showPlayMenu: function (positionTo, itemId, itemType, isFolder, mediaType, resumePositionTicks, showAddToPlaylist) {
|
showPlayMenu: function (positionTo, itemId, itemType, isFolder, mediaType, resumePositionTicks, showAddToPlaylist) {
|
||||||
|
|
||||||
var externalPlayers = ExternalPlayer.getExternalPlayers();
|
var externalPlayers = AppSettings.enableExternalPlayers();
|
||||||
|
|
||||||
if (!resumePositionTicks && mediaType != "Audio" && !isFolder) {
|
if (!resumePositionTicks && mediaType != "Audio" && !isFolder) {
|
||||||
|
|
||||||
if (!externalPlayers.length || mediaType != "Video") {
|
if (!externalPlayers || mediaType != "Video") {
|
||||||
MediaController.play(itemId);
|
MediaController.play(itemId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -253,8 +260,8 @@
|
||||||
|
|
||||||
html += '<li><a href="#" onclick="MediaController.play(\'' + itemId + '\');LibraryBrowser.closePlayMenu();">' + Globalize.translate('ButtonPlay') + '</a></li>';
|
html += '<li><a href="#" onclick="MediaController.play(\'' + itemId + '\');LibraryBrowser.closePlayMenu();">' + Globalize.translate('ButtonPlay') + '</a></li>';
|
||||||
|
|
||||||
if (!isFolder && externalPlayers.length) {
|
if (!isFolder && externalPlayers) {
|
||||||
html += '<li><a href="#" onclick="LibraryBrowser.closePlayMenu();ExternalPlayer.showMenu(\'' + itemId + '\');">' + Globalize.translate('ButtonPlayExternalPlayer') + '</a></li>';
|
html += '<li><a href="#" onclick="LibraryBrowser.closePlayMenu();LibraryBrowser.playInExternalPlayer(\'' + itemId + '\');">' + Globalize.translate('ButtonPlayExternalPlayer') + '</a></li>';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resumePositionTicks) {
|
if (resumePositionTicks) {
|
||||||
|
|
|
@ -284,7 +284,7 @@
|
||||||
|
|
||||||
var id = this.getAttribute('data-itemid');
|
var id = this.getAttribute('data-itemid');
|
||||||
|
|
||||||
ExternalPlayer.showMenu(id);
|
LibraryBrowser.playInExternalPlayer(id);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -420,7 +420,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mediaType == 'Video' && ExternalPlayer.getExternalPlayers().length) {
|
if (mediaType == 'Video' && AppSettings.enableExternalPlayers()) {
|
||||||
html += '<li data-icon="play"><a href="#" class="btnExternalPlayer" data-itemid="' + itemId + '">' + Globalize.translate('ButtonPlayExternalPlayer') + '</a></li>';
|
html += '<li data-icon="play"><a href="#" class="btnExternalPlayer" data-itemid="' + itemId + '">' + Globalize.translate('ButtonPlayExternalPlayer') + '</a></li>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,13 +30,14 @@
|
||||||
html += '<button id="btnCast" class="btnCast btnDefaultCast headerButton headerButtonRight" type="button" data-role="none" style="display:none;"><div class="headerSelectedPlayer"></div><div class="btnCastImage"></div></button>';
|
html += '<button id="btnCast" class="btnCast btnDefaultCast headerButton headerButtonRight" type="button" data-role="none" style="display:none;"><div class="headerSelectedPlayer"></div><div class="btnCastImage"></div></button>';
|
||||||
}
|
}
|
||||||
|
|
||||||
html += '<button onclick="Search.showSearchPanel($.mobile.activePage);" type="button" data-role="none" class="headerButton headerButtonRight headerSearchButton" style="display:none;"><div class="fa fa-search" style="font-size:21px;"></div></button>';
|
html += '<button onclick="Search.showSearchPanel();" type="button" data-role="none" class="headerButton headerButtonRight headerSearchButton" style="display:none;"><div class="fa fa-search" style="font-size:21px;"></div></button>';
|
||||||
|
html += '<div class="viewMenuSearch hide">';
|
||||||
html += '<div class="viewMenuSearch hide"><form class="viewMenuSearchForm">';
|
html += '<form class="viewMenuSearchForm">';
|
||||||
html += '<input type="text" data-role="none" data-type="search" class="headerSearchInput" autocomplete="off" spellcheck="off" />';
|
html += '<input type="text" data-role="none" data-type="search" class="headerSearchInput" autocomplete="off" spellcheck="off" />';
|
||||||
html += '<div class="searchInputIcon fa fa-search"></div>';
|
html += '<div class="searchInputIcon fa fa-search"></div>';
|
||||||
html += '<button data-role="none" type="button" data-iconpos="notext" class="imageButton btnCloseSearch"><i class="fa fa-close"></i></button>';
|
html += '<button data-role="none" type="button" data-iconpos="notext" class="imageButton btnCloseSearch"><i class="fa fa-close"></i></button>';
|
||||||
html += '</form></div>';
|
html += '</form>';
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
html += '<button class="headerButton headerButtonRight headerUserButton" type="button" data-role="none" onclick="Dashboard.showUserFlyout(this);">';
|
html += '<button class="headerButton headerButtonRight headerUserButton" type="button" data-role="none" onclick="Dashboard.showUserFlyout(this);">';
|
||||||
|
|
||||||
|
@ -50,8 +51,8 @@
|
||||||
|
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
$(document.body).prepend(html);
|
$(document.body).append(html);
|
||||||
$('.viewMenuBar').trigger('create').lazyChildren();
|
$('.viewMenuBar').lazyChildren();
|
||||||
|
|
||||||
$(document).trigger('headercreated');
|
$(document).trigger('headercreated');
|
||||||
bindMenuEvents();
|
bindMenuEvents();
|
||||||
|
@ -100,18 +101,21 @@
|
||||||
|
|
||||||
if (AppInfo.isTouchPreferred) {
|
if (AppInfo.isTouchPreferred) {
|
||||||
|
|
||||||
$('.libraryMenuButton').on('click', function () {
|
$('.libraryMenuButton').on('click', showLibraryMenu);
|
||||||
showLibraryMenu(false);
|
$('.dashboardMenuButton').on('click', showDashboardMenu);
|
||||||
});
|
|
||||||
$('.dashboardMenuButton').on('click', function () {
|
|
||||||
showDashboardMenu(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$('.libraryMenuButton').createHoverTouch().on('hovertouch', showLibraryMenu);
|
$('.libraryMenuButton').createHoverTouch().on('hovertouch', showLibraryMenu);
|
||||||
$('.dashboardMenuButton').createHoverTouch().on('hovertouch', showDashboardMenu);
|
$('.dashboardMenuButton').createHoverTouch().on('hovertouch', showDashboardMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Have to wait for document ready here because otherwise
|
||||||
|
// we may see the jQM redirect back and forth problem
|
||||||
|
$(initViewMenuBarHeadroom);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initViewMenuBarHeadroom() {
|
||||||
|
|
||||||
// grab an element
|
// grab an element
|
||||||
var viewMenuBar = document.getElementsByClassName("viewMenuBar")[0];
|
var viewMenuBar = document.getElementsByClassName("viewMenuBar")[0];
|
||||||
initHeadRoom(viewMenuBar);
|
initHeadRoom(viewMenuBar);
|
||||||
|
@ -478,19 +482,16 @@
|
||||||
|
|
||||||
function updateContextText(page) {
|
function updateContextText(page) {
|
||||||
|
|
||||||
var name = $(page)[0].getAttribute('data-contextname');
|
var jPage = $(page);
|
||||||
|
|
||||||
|
var name = jPage.attr('data-contextname');
|
||||||
|
|
||||||
if (name) {
|
if (name) {
|
||||||
|
|
||||||
$('.libraryMenuButtonText').html('<span>' + name + '</span>');
|
$('.libraryMenuButtonText').html('<span>' + name + '</span>');
|
||||||
|
|
||||||
}
|
}
|
||||||
//else if ($(page).hasClass('type-interior')) {
|
else if (jPage.hasClass('allLibraryPage') || jPage.hasClass('type-interior')) {
|
||||||
|
|
||||||
// $('.libraryMenuButtonText').html('<span>' + 'Dashboard' + '</span>');
|
|
||||||
|
|
||||||
//}
|
|
||||||
else if ($(page).hasClass('allLibraryPage') || $(page).hasClass('type-interior')) {
|
|
||||||
$('.libraryMenuButtonText').html('<span class="logoLibraryMenuButtonText">EMBY</span>');
|
$('.libraryMenuButtonText').html('<span class="logoLibraryMenuButtonText">EMBY</span>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -511,7 +512,7 @@
|
||||||
function buildViewMenuBar(page) {
|
function buildViewMenuBar(page) {
|
||||||
|
|
||||||
if ($(page).hasClass('standalonePage')) {
|
if ($(page).hasClass('standalonePage')) {
|
||||||
$('.viewMenuBar').remove();
|
$('.viewMenuBar').hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -519,7 +520,7 @@
|
||||||
$('.viewMenuBar').remove();
|
$('.viewMenuBar').remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
var viewMenuBar = $('.viewMenuBar');
|
var viewMenuBar = $('.viewMenuBar').show();
|
||||||
if (!$('.viewMenuBar').length) {
|
if (!$('.viewMenuBar').length) {
|
||||||
|
|
||||||
renderHeader();
|
renderHeader();
|
||||||
|
@ -539,24 +540,55 @@
|
||||||
updateViewMenuBarHeadroom(page, viewMenuBar);
|
updateViewMenuBarHeadroom(page, viewMenuBar);
|
||||||
requiresViewMenuRefresh = false;
|
requiresViewMenuRefresh = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The first time we create the view menu bar, wait until doc ready + login validated
|
||||||
|
// Otherwise we run into the jQM redirect back and forth problem
|
||||||
|
var updateViewMenuBarBeforePageShow = false;
|
||||||
|
|
||||||
$(document).on('pageinit', ".page", function () {
|
$(document).on('pageinit', ".page", function () {
|
||||||
|
|
||||||
var page = this;
|
var page = this;
|
||||||
|
|
||||||
$('.libraryViewNav', page).wrapInner('<div class="libraryViewNavInner"></div>');
|
$(function () {
|
||||||
|
onPageInitDocumentReady(page);
|
||||||
$('.libraryViewNav a', page).each(function () {
|
|
||||||
|
|
||||||
this.innerHTML = '<span class="libraryViewNavLinkContent">' + this.innerHTML + '</span>';
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}).on('pagebeforeshowready', ".page", function () {
|
}).on('pagebeforeshowready', ".page", function () {
|
||||||
|
|
||||||
var page = this;
|
var page = this;
|
||||||
|
|
||||||
|
if (updateViewMenuBarBeforePageShow) {
|
||||||
|
onPageBeforeShowDocumentReady(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
}).one('pageshowready', ".page", function () {
|
||||||
|
|
||||||
|
var page = this;
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
onPageBeforeShowDocumentReady(page);
|
||||||
|
updateViewMenuBarBeforePageShow = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
}).on('pageshowready', ".page", function () {
|
||||||
|
|
||||||
|
var page = this;
|
||||||
|
|
||||||
|
onPageShowDocumentReady(page);
|
||||||
|
});
|
||||||
|
|
||||||
|
function onPageInitDocumentReady(page) {
|
||||||
|
$('.libraryViewNav', page).wrapInner('<div class="libraryViewNavInner"></div>');
|
||||||
|
|
||||||
|
$('.libraryViewNav a', page).each(function () {
|
||||||
|
|
||||||
|
this.innerHTML = '<span class="libraryViewNavLinkContent">' + this.innerHTML + '</span>';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPageBeforeShowDocumentReady(page) {
|
||||||
|
|
||||||
buildViewMenuBar(page);
|
buildViewMenuBar(page);
|
||||||
|
|
||||||
var jpage = $(page);
|
var jpage = $(page);
|
||||||
|
@ -587,11 +619,9 @@
|
||||||
} else {
|
} else {
|
||||||
$(document.body).removeClass('dashboardDocument').removeClass('libraryDocument');
|
$(document.body).removeClass('dashboardDocument').removeClass('libraryDocument');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}).on('pageshow', ".libraryPage", function () {
|
function onPageShowDocumentReady(page) {
|
||||||
|
|
||||||
var page = this;
|
|
||||||
|
|
||||||
var elem = $('.libraryViewNavInner .ui-btn-active:visible', page);
|
var elem = $('.libraryViewNavInner .ui-btn-active:visible', page);
|
||||||
|
|
||||||
if (elem.length) {
|
if (elem.length) {
|
||||||
|
@ -600,7 +630,7 @@
|
||||||
// Scroll back up so in case vertical scroll was messed with
|
// Scroll back up so in case vertical scroll was messed with
|
||||||
$(document).scrollTop(0);
|
$(document).scrollTop(0);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
function initHeadRoom(elem) {
|
function initHeadRoom(elem) {
|
||||||
|
|
||||||
|
|
|
@ -229,7 +229,7 @@
|
||||||
|
|
||||||
self.play = function (options) {
|
self.play = function (options) {
|
||||||
|
|
||||||
doWithPlaybackValidation(function() {
|
doWithPlaybackValidation(function () {
|
||||||
if (typeof (options) === 'string') {
|
if (typeof (options) === 'string') {
|
||||||
options = { ids: [options] };
|
options = { ids: [options] };
|
||||||
}
|
}
|
||||||
|
@ -480,6 +480,82 @@
|
||||||
}, 300);
|
}, 300);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.getPlaybackInfo = function (itemId, deviceProfile, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex, liveStreamId) {
|
||||||
|
|
||||||
|
var postData = {
|
||||||
|
DeviceProfile: deviceProfile
|
||||||
|
};
|
||||||
|
|
||||||
|
var query = {
|
||||||
|
UserId: Dashboard.getCurrentUserId(),
|
||||||
|
StartTimeTicks: startPosition || 0
|
||||||
|
};
|
||||||
|
|
||||||
|
if (audioStreamIndex != null) {
|
||||||
|
query.AudioStreamIndex = audioStreamIndex;
|
||||||
|
}
|
||||||
|
if (subtitleStreamIndex != null) {
|
||||||
|
query.SubtitleStreamIndex = subtitleStreamIndex;
|
||||||
|
}
|
||||||
|
if (mediaSource) {
|
||||||
|
query.MediaSourceId = mediaSource.Id;
|
||||||
|
}
|
||||||
|
if (liveStreamId) {
|
||||||
|
query.LiveStreamId = liveStreamId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiClient.ajax({
|
||||||
|
url: ApiClient.getUrl('Items/' + itemId + '/PlaybackInfo', query),
|
||||||
|
type: 'POST',
|
||||||
|
data: JSON.stringify(postData),
|
||||||
|
contentType: "application/json",
|
||||||
|
dataType: "json"
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
self.getLiveStream = function (itemId, playSessionId, deviceProfile, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex) {
|
||||||
|
|
||||||
|
var postData = {
|
||||||
|
DeviceProfile: deviceProfile,
|
||||||
|
OpenToken: mediaSource.OpenToken
|
||||||
|
};
|
||||||
|
|
||||||
|
var query = {
|
||||||
|
UserId: Dashboard.getCurrentUserId(),
|
||||||
|
StartTimeTicks: startPosition || 0,
|
||||||
|
ItemId: itemId,
|
||||||
|
PlaySessionId: playSessionId
|
||||||
|
};
|
||||||
|
|
||||||
|
if (audioStreamIndex != null) {
|
||||||
|
query.AudioStreamIndex = audioStreamIndex;
|
||||||
|
}
|
||||||
|
if (subtitleStreamIndex != null) {
|
||||||
|
query.SubtitleStreamIndex = subtitleStreamIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiClient.ajax({
|
||||||
|
url: ApiClient.getUrl('LiveStreams/Open', query),
|
||||||
|
type: 'POST',
|
||||||
|
data: JSON.stringify(postData),
|
||||||
|
contentType: "application/json",
|
||||||
|
dataType: "json"
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
self.supportsDirectPlay = function (mediaSource) {
|
||||||
|
|
||||||
|
if (mediaSource.SupportsDirectPlay && mediaSource.Protocol == 'Http' && !mediaSource.RequiredHttpHeaders.length) {
|
||||||
|
|
||||||
|
// TODO: Need to verify the host is going to be reachable
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
window.MediaController = new mediaController();
|
window.MediaController = new mediaController();
|
||||||
|
|
|
@ -605,10 +605,11 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var idleHandlerTimeout;
|
||||||
function idleHandler() {
|
function idleHandler() {
|
||||||
|
|
||||||
if (timeout) {
|
if (idleHandlerTimeout) {
|
||||||
window.clearTimeout(timeout);
|
window.clearTimeout(idleHandlerTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (idleState == true) {
|
if (idleState == true) {
|
||||||
|
@ -618,7 +619,7 @@
|
||||||
|
|
||||||
idleState = false;
|
idleState = false;
|
||||||
|
|
||||||
timeout = window.setTimeout(function () {
|
idleHandlerTimeout = window.setTimeout(function () {
|
||||||
idleState = true;
|
idleState = true;
|
||||||
$('.hiddenOnIdle').addClass("inactive");
|
$('.hiddenOnIdle').addClass("inactive");
|
||||||
$('#videoPlayer').addClass('idlePlayer');
|
$('#videoPlayer').addClass('idlePlayer');
|
||||||
|
@ -978,11 +979,10 @@
|
||||||
|
|
||||||
function bindEventsForPlayback() {
|
function bindEventsForPlayback() {
|
||||||
|
|
||||||
var hideElementsOnIdle = !$.browser.mobile;
|
var hideElementsOnIdle = true;
|
||||||
|
|
||||||
if (hideElementsOnIdle) {
|
if (hideElementsOnIdle) {
|
||||||
$('.itemVideo').off('mousemove.videoplayer keydown.videoplayer scroll.videoplayer', idleHandler);
|
$('.itemVideo').off('mousemove.videoplayer keydown.videoplayer scroll.videoplayer mousedown.videoplayer', idleHandler).on('mousemove.videoplayer keydown.videoplayer scroll.videoplayer mousedown.videoplayer', idleHandler).trigger('mousemove');
|
||||||
$('.itemVideo').on('mousemove.videoplayer keydown.videoplayer scroll.videoplayer', idleHandler).trigger('mousemove');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).on('webkitfullscreenchange.videoplayer mozfullscreenchange.videoplayer msfullscreenchange.videoplayer fullscreenchange.videoplayer', function (e) {
|
$(document).on('webkitfullscreenchange.videoplayer mozfullscreenchange.videoplayer msfullscreenchange.videoplayer fullscreenchange.videoplayer', function (e) {
|
||||||
|
@ -1012,14 +1012,14 @@
|
||||||
|
|
||||||
function unbindEventsForPlayback() {
|
function unbindEventsForPlayback() {
|
||||||
|
|
||||||
$(document).off('webkitfullscreenchange.videoplayer mozfullscreenchange.videoplayer msfullscreenchange.videoplayer fullscreenchange.videoplayer');
|
$(document).off('.videoplayer');
|
||||||
|
|
||||||
// Stop playback on browser back button nav
|
// Stop playback on browser back button nav
|
||||||
$(window).off("popstate.videoplayer");
|
$(window).off("popstate.videoplayer");
|
||||||
|
|
||||||
$(document.body).off("mousemove.videoplayer");
|
$(document.body).off("mousemove.videoplayer");
|
||||||
|
|
||||||
$('.itemVideo').off('mousemove.videoplayer keydown.videoplayer scroll.videoplayer');
|
$('.itemVideo').off('mousemove.videoplayer keydown.videoplayer scroll.videoplayer mousedown.videoplayer');
|
||||||
}
|
}
|
||||||
|
|
||||||
self.canAutoPlayVideo = function () {
|
self.canAutoPlayVideo = function () {
|
||||||
|
@ -1084,7 +1084,7 @@
|
||||||
self.playVideoInternal = function (item, mediaSource, startPosition, streamInfo) {
|
self.playVideoInternal = function (item, mediaSource, startPosition, streamInfo) {
|
||||||
|
|
||||||
var videoUrl = streamInfo.url;
|
var videoUrl = streamInfo.url;
|
||||||
var contentType = streamInfo.contentType;
|
var contentType = streamInfo.mimeType;
|
||||||
var startPositionInSeekParam = streamInfo.startPositionInSeekParam;
|
var startPositionInSeekParam = streamInfo.startPositionInSeekParam;
|
||||||
self.startTimeTicksOffset = streamInfo.startTimeTicksOffset;
|
self.startTimeTicksOffset = streamInfo.startTimeTicksOffset;
|
||||||
|
|
||||||
|
@ -1296,7 +1296,6 @@
|
||||||
}).on("dblclick.mediaplayerevent", function () {
|
}).on("dblclick.mediaplayerevent", function () {
|
||||||
|
|
||||||
self.toggleFullscreen();
|
self.toggleFullscreen();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
bindEventsForPlayback();
|
bindEventsForPlayback();
|
||||||
|
|
|
@ -183,7 +183,7 @@
|
||||||
Protocol: 'hls'
|
Protocol: 'hls'
|
||||||
});
|
});
|
||||||
|
|
||||||
if (canPlayAac) {
|
if (canPlayAac && $.browser.safari) {
|
||||||
profile.TranscodingProfiles.push({
|
profile.TranscodingProfiles.push({
|
||||||
Container: 'aac',
|
Container: 'aac',
|
||||||
Type: 'Audio',
|
Type: 'Audio',
|
||||||
|
@ -471,7 +471,7 @@
|
||||||
subtitleStreamIndex = parseInt(subtitleStreamIndex);
|
subtitleStreamIndex = parseInt(subtitleStreamIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPlaybackInfo(self.currentItem.Id, deviceProfile, ticks, self.currentMediaSource, audioStreamIndex, subtitleStreamIndex, liveStreamId).done(function (result) {
|
MediaController.getPlaybackInfo(self.currentItem.Id, deviceProfile, ticks, self.currentMediaSource, audioStreamIndex, subtitleStreamIndex, liveStreamId).done(function (result) {
|
||||||
|
|
||||||
if (validatePlaybackInfoResult(result)) {
|
if (validatePlaybackInfoResult(result)) {
|
||||||
|
|
||||||
|
@ -685,22 +685,11 @@
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function supportsDirectPlay(mediaSource) {
|
|
||||||
|
|
||||||
if (mediaSource.SupportsDirectPlay && mediaSource.Protocol == 'Http' && !mediaSource.RequiredHttpHeaders.length) {
|
|
||||||
|
|
||||||
// TODO: Need to verify the host is going to be reachable
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getOptimalMediaSource(mediaType, versions) {
|
function getOptimalMediaSource(mediaType, versions) {
|
||||||
|
|
||||||
var optimalVersion = versions.filter(function (v) {
|
var optimalVersion = versions.filter(function (v) {
|
||||||
|
|
||||||
v.enableDirectPlay = supportsDirectPlay(v);
|
v.enableDirectPlay = MediaController.supportsDirectPlay(v);
|
||||||
|
|
||||||
return v.enableDirectPlay;
|
return v.enableDirectPlay;
|
||||||
|
|
||||||
|
@ -719,71 +708,6 @@
|
||||||
})[0];
|
})[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPlaybackInfo(itemId, deviceProfile, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex, liveStreamId) {
|
|
||||||
|
|
||||||
var postData = {
|
|
||||||
DeviceProfile: deviceProfile
|
|
||||||
};
|
|
||||||
|
|
||||||
var query = {
|
|
||||||
UserId: Dashboard.getCurrentUserId(),
|
|
||||||
StartTimeTicks: startPosition || 0
|
|
||||||
};
|
|
||||||
|
|
||||||
if (audioStreamIndex != null) {
|
|
||||||
query.AudioStreamIndex = audioStreamIndex;
|
|
||||||
}
|
|
||||||
if (subtitleStreamIndex != null) {
|
|
||||||
query.SubtitleStreamIndex = subtitleStreamIndex;
|
|
||||||
}
|
|
||||||
if (mediaSource) {
|
|
||||||
query.MediaSourceId = mediaSource.Id;
|
|
||||||
}
|
|
||||||
if (liveStreamId) {
|
|
||||||
query.LiveStreamId = liveStreamId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ApiClient.ajax({
|
|
||||||
url: ApiClient.getUrl('Items/' + itemId + '/PlaybackInfo', query),
|
|
||||||
type: 'POST',
|
|
||||||
data: JSON.stringify(postData),
|
|
||||||
contentType: "application/json",
|
|
||||||
dataType: "json"
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLiveStream(itemId, playSessionId, deviceProfile, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex) {
|
|
||||||
|
|
||||||
var postData = {
|
|
||||||
DeviceProfile: deviceProfile,
|
|
||||||
OpenToken: mediaSource.OpenToken
|
|
||||||
};
|
|
||||||
|
|
||||||
var query = {
|
|
||||||
UserId: Dashboard.getCurrentUserId(),
|
|
||||||
StartTimeTicks: startPosition || 0,
|
|
||||||
ItemId: itemId,
|
|
||||||
PlaySessionId: playSessionId
|
|
||||||
};
|
|
||||||
|
|
||||||
if (audioStreamIndex != null) {
|
|
||||||
query.AudioStreamIndex = audioStreamIndex;
|
|
||||||
}
|
|
||||||
if (subtitleStreamIndex != null) {
|
|
||||||
query.SubtitleStreamIndex = subtitleStreamIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ApiClient.ajax({
|
|
||||||
url: ApiClient.getUrl('LiveStreams/Open', query),
|
|
||||||
type: 'POST',
|
|
||||||
data: JSON.stringify(postData),
|
|
||||||
contentType: "application/json",
|
|
||||||
dataType: "json"
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.createStreamInfo = function (type, item, mediaSource, startPosition) {
|
self.createStreamInfo = function (type, item, mediaSource, startPosition) {
|
||||||
|
|
||||||
var mediaUrl;
|
var mediaUrl;
|
||||||
|
@ -867,7 +791,7 @@
|
||||||
|
|
||||||
return {
|
return {
|
||||||
url: mediaUrl,
|
url: mediaUrl,
|
||||||
contentType: contentType,
|
mimeType: contentType,
|
||||||
startTimeTicksOffset: startTimeTicksOffset,
|
startTimeTicksOffset: startTimeTicksOffset,
|
||||||
startPositionInSeekParam: startPositionInSeekParam,
|
startPositionInSeekParam: startPositionInSeekParam,
|
||||||
playMethod: playMethod
|
playMethod: playMethod
|
||||||
|
@ -900,7 +824,7 @@
|
||||||
Dashboard.showModalLoadingMsg();
|
Dashboard.showModalLoadingMsg();
|
||||||
}
|
}
|
||||||
|
|
||||||
getPlaybackInfo(item.Id, deviceProfile, startPosition).done(function (playbackInfoResult) {
|
MediaController.getPlaybackInfo(item.Id, deviceProfile, startPosition).done(function (playbackInfoResult) {
|
||||||
|
|
||||||
if (validatePlaybackInfoResult(playbackInfoResult)) {
|
if (validatePlaybackInfoResult(playbackInfoResult)) {
|
||||||
|
|
||||||
|
@ -910,9 +834,9 @@
|
||||||
|
|
||||||
if (mediaSource.RequiresOpening) {
|
if (mediaSource.RequiresOpening) {
|
||||||
|
|
||||||
getLiveStream(item.Id, playbackInfoResult.PlaySessionId, deviceProfile, startPosition, mediaSource, null, null).done(function (openLiveStreamResult) {
|
MediaController.getLiveStream(item.Id, playbackInfoResult.PlaySessionId, deviceProfile, startPosition, mediaSource, null, null).done(function (openLiveStreamResult) {
|
||||||
|
|
||||||
openLiveStreamResult.MediaSource.enableDirectPlay = supportsDirectPlay(openLiveStreamResult.MediaSource);
|
openLiveStreamResult.MediaSource.enableDirectPlay = MediaController.supportsDirectPlay(openLiveStreamResult.MediaSource);
|
||||||
|
|
||||||
playInternalPostMediaSourceSelection(item, openLiveStreamResult.MediaSource, startPosition, callback);
|
playInternalPostMediaSourceSelection(item, openLiveStreamResult.MediaSource, startPosition, callback);
|
||||||
});
|
});
|
||||||
|
@ -1474,8 +1398,17 @@
|
||||||
|
|
||||||
if (item) {
|
if (item) {
|
||||||
|
|
||||||
state.NowPlayingItem = state.NowPlayingItem || {};
|
state.NowPlayingItem = self.getNowPlayingItemForReporting(item, mediaSource);
|
||||||
var nowPlayingItem = state.NowPlayingItem;
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.getNowPlayingItemForReporting = function (item, mediaSource) {
|
||||||
|
|
||||||
|
var nowPlayingItem = {};
|
||||||
|
|
||||||
|
nowPlayingItem.RunTimeTicks = mediaSource.RunTimeTicks;
|
||||||
|
|
||||||
nowPlayingItem.Id = item.Id;
|
nowPlayingItem.Id = item.Id;
|
||||||
nowPlayingItem.MediaType = item.MediaType;
|
nowPlayingItem.MediaType = item.MediaType;
|
||||||
|
@ -1540,9 +1473,8 @@
|
||||||
nowPlayingItem.LogoItemId = item.ParentLogoItemId;
|
nowPlayingItem.LogoItemId = item.ParentLogoItemId;
|
||||||
nowPlayingItem.LogoImageTag = item.ParentLogoImageTag;
|
nowPlayingItem.LogoImageTag = item.ParentLogoImageTag;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
return nowPlayingItem;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.beginPlayerUpdates = function () {
|
self.beginPlayerUpdates = function () {
|
||||||
|
|
|
@ -2,21 +2,10 @@
|
||||||
|
|
||||||
function loadForm(page, userId, displayPreferences) {
|
function loadForm(page, userId, displayPreferences) {
|
||||||
|
|
||||||
var externalPlayers = JSON.parse(appStorage.getItem('externalplayers') || '[]');
|
|
||||||
|
|
||||||
$('#selectMaxBitrate', page).val(AppSettings.maxStreamingBitrate()).selectmenu("refresh");
|
$('#selectMaxBitrate', page).val(AppSettings.maxStreamingBitrate()).selectmenu("refresh");
|
||||||
$('#selectMaxChromecastBitrate', page).val(AppSettings.maxChromecastBitrate()).selectmenu("refresh");
|
$('#selectMaxChromecastBitrate', page).val(AppSettings.maxChromecastBitrate()).selectmenu("refresh");
|
||||||
|
|
||||||
$('.chkExternalPlayer', page).each(function () {
|
$('#chkExternalVideoPlayer', page).checked(AppSettings.enableExternalPlayers()).checkboxradio("refresh");
|
||||||
|
|
||||||
var chk = this;
|
|
||||||
chk.checked = externalPlayers.filter(function (p) {
|
|
||||||
|
|
||||||
return p.name == chk.getAttribute('data-name');
|
|
||||||
|
|
||||||
}).length > 0;
|
|
||||||
|
|
||||||
}).checkboxradio('refresh');
|
|
||||||
|
|
||||||
$('#selectThemeSong', page).val(appStorage.getItem('enableThemeSongs-' + userId) || '').selectmenu("refresh");
|
$('#selectThemeSong', page).val(appStorage.getItem('enableThemeSongs-' + userId) || '').selectmenu("refresh");
|
||||||
$('#selectBackdrop', page).val(appStorage.getItem('enableBackdrops-' + userId) || '').selectmenu("refresh");
|
$('#selectBackdrop', page).val(appStorage.getItem('enableBackdrops-' + userId) || '').selectmenu("refresh");
|
||||||
|
@ -55,16 +44,7 @@
|
||||||
|
|
||||||
Dashboard.showLoadingMsg();
|
Dashboard.showLoadingMsg();
|
||||||
|
|
||||||
var externalPlayers = $('.chkExternalPlayer:checked', page).get().map(function (i) {
|
AppSettings.enableExternalPlayers($('#chkExternalVideoPlayer', page).checked());
|
||||||
|
|
||||||
return {
|
|
||||||
name: i.getAttribute('data-name'),
|
|
||||||
scheme: i.getAttribute('data-scheme')
|
|
||||||
};
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
appStorage.setItem('externalplayers', JSON.stringify(externalPlayers));
|
|
||||||
|
|
||||||
AppSettings.maxStreamingBitrate($('#selectMaxBitrate', page).val());
|
AppSettings.maxStreamingBitrate($('#selectMaxBitrate', page).val());
|
||||||
AppSettings.maxChromecastBitrate($('#selectMaxChromecastBitrate', page).val());
|
AppSettings.maxChromecastBitrate($('#selectMaxChromecastBitrate', page).val());
|
||||||
|
@ -133,6 +113,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseInt(store.getItem('chromecastBitrate') || '') || 3000000;
|
return parseInt(store.getItem('chromecastBitrate') || '') || 3000000;
|
||||||
|
},
|
||||||
|
enableExternalPlayers: function (val) {
|
||||||
|
|
||||||
|
if (val != null) {
|
||||||
|
store.setItem('externalplayers', val.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return store.getItem('externalplayers') == 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -58,9 +58,11 @@
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
self.showSearchPanel = function (page) {
|
self.showSearchPanel = function () {
|
||||||
|
|
||||||
$('.viewMenuSearch').removeClass('hide');
|
var viewMenuSearch = $('.viewMenuSearch');
|
||||||
|
|
||||||
|
viewMenuSearch.removeClass('hide');
|
||||||
$('.headerSearchInput').focus();
|
$('.headerSearchInput').focus();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -191,7 +191,6 @@ var Dashboard = {
|
||||||
} else {
|
} else {
|
||||||
loginPage = 'login.html';
|
loginPage = 'login.html';
|
||||||
}
|
}
|
||||||
|
|
||||||
Dashboard.navigate(loginPage);
|
Dashboard.navigate(loginPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1491,6 +1490,25 @@ var Dashboard = {
|
||||||
Dashboard.ready(function () {
|
Dashboard.ready(function () {
|
||||||
$(page).trigger(name);
|
$(page).trigger(name);
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
loadExternalPlayer: function () {
|
||||||
|
|
||||||
|
var deferred = DeferredBuilder.Deferred();
|
||||||
|
|
||||||
|
require(['scripts/externalplayer.js'], function () {
|
||||||
|
|
||||||
|
if (Dashboard.isRunningInCordova()) {
|
||||||
|
require(['thirdparty/cordova/externalplayer.js'], function () {
|
||||||
|
|
||||||
|
deferred.resolve();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferred.promise();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1560,6 +1578,10 @@ var AppInfo = {};
|
||||||
else {
|
else {
|
||||||
AppInfo.enableFooterNotifications = true;
|
AppInfo.enableFooterNotifications = true;
|
||||||
AppInfo.enableSupporterMembership = true;
|
AppInfo.enableSupporterMembership = true;
|
||||||
|
|
||||||
|
if (!$.browser.android && !$.browser.ipad && !$.browser.iphone) {
|
||||||
|
AppInfo.enableAppLayouts = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AppInfo.enableUserImage = true;
|
AppInfo.enableUserImage = true;
|
||||||
|
@ -1576,6 +1598,7 @@ var AppInfo = {};
|
||||||
.on("websocketmessage.dashboard", Dashboard.onWebSocketMessageReceived)
|
.on("websocketmessage.dashboard", Dashboard.onWebSocketMessageReceived)
|
||||||
.on('requestfail.dashboard', Dashboard.onRequestFail);
|
.on('requestfail.dashboard', Dashboard.onRequestFail);
|
||||||
}
|
}
|
||||||
|
|
||||||
//localStorage.clear();
|
//localStorage.clear();
|
||||||
function createConnectionManager(appInfo) {
|
function createConnectionManager(appInfo) {
|
||||||
|
|
||||||
|
@ -1771,9 +1794,6 @@ var AppInfo = {};
|
||||||
|
|
||||||
$(document.body).append(footerHtml);
|
$(document.body).append(footerHtml);
|
||||||
|
|
||||||
var footerElem = $('.footer', document.body);
|
|
||||||
footerElem.trigger('create');
|
|
||||||
|
|
||||||
$(window).on("beforeunload", function () {
|
$(window).on("beforeunload", function () {
|
||||||
|
|
||||||
var apiClient = window.ApiClient;
|
var apiClient = window.ApiClient;
|
||||||
|
@ -1928,20 +1948,6 @@ $(document).on('pagecreate', ".page", function () {
|
||||||
|
|
||||||
$('.localnav a, .libraryViewNav a').attr('data-transition', 'none');
|
$('.localnav a, .libraryViewNav a').attr('data-transition', 'none');
|
||||||
|
|
||||||
}).on('pageshow', ".page", function () {
|
|
||||||
|
|
||||||
var page = this;
|
|
||||||
var require = this.getAttribute('data-require');
|
|
||||||
|
|
||||||
if (require) {
|
|
||||||
requirejs(require.split(','), function () {
|
|
||||||
|
|
||||||
Dashboard.firePageEvent(page, 'pageshowready');
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Dashboard.firePageEvent(page, 'pageshowready');
|
|
||||||
}
|
|
||||||
|
|
||||||
}).on('pagebeforeshow', ".page", function () {
|
}).on('pagebeforeshow', ".page", function () {
|
||||||
|
|
||||||
var page = this;
|
var page = this;
|
||||||
|
@ -1956,7 +1962,21 @@ $(document).on('pagecreate', ".page", function () {
|
||||||
Dashboard.firePageEvent(page, 'pagebeforeshowready');
|
Dashboard.firePageEvent(page, 'pagebeforeshowready');
|
||||||
}
|
}
|
||||||
|
|
||||||
}).on('pagebeforeshowready', ".page", function () {
|
}).on('pageshow', ".page", function () {
|
||||||
|
|
||||||
|
var page = this;
|
||||||
|
var require = this.getAttribute('data-require');
|
||||||
|
|
||||||
|
if (require) {
|
||||||
|
requirejs(require.split(','), function () {
|
||||||
|
|
||||||
|
Dashboard.firePageEvent(page, 'pageshowbeginready');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Dashboard.firePageEvent(page, 'pageshowbeginready');
|
||||||
|
}
|
||||||
|
|
||||||
|
}).on('pageshowbeginready', ".page", function () {
|
||||||
|
|
||||||
var page = $(this);
|
var page = $(this);
|
||||||
|
|
||||||
|
@ -1977,9 +1997,6 @@ $(document).on('pagecreate', ".page", function () {
|
||||||
Dashboard.ensureToolsMenu(page, user);
|
Dashboard.ensureToolsMenu(page, user);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Dashboard.ensureHeader(page);
|
|
||||||
Dashboard.ensurePageTitle(page);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
|
@ -1989,7 +2006,7 @@ $(document).on('pagecreate', ".page", function () {
|
||||||
if (isConnectMode) {
|
if (isConnectMode) {
|
||||||
|
|
||||||
if (!Dashboard.isServerlessPage()) {
|
if (!Dashboard.isServerlessPage()) {
|
||||||
Dashboard.logout(true);
|
Dashboard.logout();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1997,13 +2014,15 @@ $(document).on('pagecreate', ".page", function () {
|
||||||
if (!isConnectMode && this.id !== "loginPage" && !page.hasClass('forgotPasswordPage') && !page.hasClass('wizardPage')) {
|
if (!isConnectMode && this.id !== "loginPage" && !page.hasClass('forgotPasswordPage') && !page.hasClass('wizardPage')) {
|
||||||
|
|
||||||
console.log('Not logged into server. Redirecting to login.');
|
console.log('Not logged into server. Redirecting to login.');
|
||||||
Dashboard.logout(true);
|
Dashboard.logout();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dashboard.firePageEvent(page, 'pageshowready');
|
||||||
|
|
||||||
Dashboard.ensureHeader(page);
|
Dashboard.ensureHeader(page);
|
||||||
Dashboard.ensurePageTitle(page);
|
Dashboard.ensurePageTitle(page);
|
||||||
}
|
|
||||||
|
|
||||||
if (apiClient && !apiClient.isWebSocketOpen()) {
|
if (apiClient && !apiClient.isWebSocketOpen()) {
|
||||||
Dashboard.refreshSystemInfoFromServer();
|
Dashboard.refreshSystemInfoFromServer();
|
||||||
|
|
6
dashboard-ui/thirdparty/apiclient/ajax.js
vendored
|
@ -2,11 +2,13 @@
|
||||||
|
|
||||||
globalScope.AjaxApi = {
|
globalScope.AjaxApi = {
|
||||||
|
|
||||||
param: function(params) {
|
param: function (params) {
|
||||||
return jQuery.param(params);
|
return jQuery.param(params);
|
||||||
},
|
},
|
||||||
|
|
||||||
ajax: function(request) {
|
ajax: function (request) {
|
||||||
|
|
||||||
|
request.timeout = request.timeout || 30000;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return jQuery.ajax(request);
|
return jQuery.ajax(request);
|
||||||
|
|
50
dashboard-ui/thirdparty/cordova/externalplayer.js
vendored
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
(function () {
|
||||||
|
|
||||||
|
function showPlayerSelectionMenu(item, url, mimeType) {
|
||||||
|
|
||||||
|
window.plugins.launcher.launch({
|
||||||
|
uri: url,
|
||||||
|
dataType: mimeType
|
||||||
|
|
||||||
|
}, function () {
|
||||||
|
|
||||||
|
console.log('plugin launch success');
|
||||||
|
ExternalPlayer.onPlaybackStart();
|
||||||
|
|
||||||
|
}, function () {
|
||||||
|
|
||||||
|
console.log('plugin launch error');
|
||||||
|
ExternalPlayer.onPlaybackStart();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getExternalPlayers(url, mimeType) {
|
||||||
|
|
||||||
|
var deferred = $.Deferred();
|
||||||
|
|
||||||
|
window.plugins.launcher.canLaunch({
|
||||||
|
uri: url,
|
||||||
|
dataType: mimeType,
|
||||||
|
getAppList: true
|
||||||
|
}, function (data) {
|
||||||
|
|
||||||
|
console.log('plugin canLaunch succcess');
|
||||||
|
var players = data.appList.map(function (p) {
|
||||||
|
|
||||||
|
});
|
||||||
|
deferred.resolveWith(null, [players]);
|
||||||
|
|
||||||
|
}, function () {
|
||||||
|
console.log('plugin canLaunch error');
|
||||||
|
deferred.reject();
|
||||||
|
});
|
||||||
|
|
||||||
|
deferred.resolveWith(null, [players]);
|
||||||
|
|
||||||
|
return deferred.promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.ExternalPlayer.getExternalPlayers = getExternalPlayers;
|
||||||
|
window.ExternalPlayer.showPlayerSelectionMenu = showPlayerSelectionMenu;
|
||||||
|
|
||||||
|
})();
|