diff --git a/ApiClient.js b/ApiClient.js
index f268c44ec8..c937c28ee6 100644
--- a/ApiClient.js
+++ b/ApiClient.js
@@ -78,6 +78,10 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
return name;
}());
+ self.deviceName = function () {
+ return deviceName;
+ };
+
self.deviceId = function () {
return deviceId;
};
diff --git a/dashboard-ui/css/librarybrowser.css b/dashboard-ui/css/librarybrowser.css
index 6c7148e8a6..747927a2ce 100644
--- a/dashboard-ui/css/librarybrowser.css
+++ b/dashboard-ui/css/librarybrowser.css
@@ -427,7 +427,7 @@ a.itemTag:hover {
background-size: cover;
background-position: center 15%;
background-repeat: no-repeat;
- height: 550px;
+ height: 570px;
position: relative;
}
@@ -482,13 +482,14 @@ a.itemTag:hover {
}
.itemDetailImage {
- max-height: 220px;
+ max-height: 280px;
max-width: 320px;
-moz-box-shadow: 0px 0 20px #000;
-webkit-box-shadow: 0px 0 20px #000;
box-shadow: 0px 0 20px #000;
border: solid 1px #222;
margin-top: -20px;
+ margin-bottom: 10px;
}
.noBackdrop .itemDetailImage {
@@ -645,7 +646,7 @@ a.itemTag:hover {
.detailImageProgressContainer {
position: absolute;
- bottom: 3px;
+ bottom: 10px;
right: 0;
left: 0;
text-align: center;
diff --git a/dashboard-ui/dlnaprofile.html b/dashboard-ui/dlnaprofile.html
index e7357ead27..8e0bcf4c9d 100644
--- a/dashboard-ui/dlnaprofile.html
+++ b/dashboard-ui/dlnaprofile.html
@@ -144,7 +144,7 @@
-
Response profiles provide a way to customize responses sent to the device when playing certain kinds of media.
+
Response profiles provide a way to customize information sent to the device when playing certain kinds of media.
diff --git a/dashboard-ui/itemdetails.html b/dashboard-ui/itemdetails.html
index 9bca6966f4..12a226314e 100644
--- a/dashboard-ui/itemdetails.html
+++ b/dashboard-ui/itemdetails.html
@@ -109,32 +109,32 @@
+
-
Previous
Next
diff --git a/dashboard-ui/scripts/chromecast.js b/dashboard-ui/scripts/chromecast.js
index 2b3ed93c99..d2b7f5d159 100644
--- a/dashboard-ui/scripts/chromecast.js
+++ b/dashboard-ui/scripts/chromecast.js
@@ -26,6 +26,8 @@
'ERROR': 'ERROR'
};
+ var PlayerName = 'Chromecast';
+
var CastPlayer = function () {
/* device variables */
@@ -37,8 +39,7 @@
this.currentMediaSession = null;
// @type {Number} volume
this.currentVolume = 0.5;
- // @type {Boolean} A flag for autoplay after load
- this.autoplay = true;
+
// @type {string} a chrome.cast.Session object
this.session = null;
// @type {PLAYER_STATE} A state for Cast media player
@@ -63,9 +64,6 @@
// @type {Number} A number in milliseconds for minimal progress update
this.timerStep = 1000;
- /* media contents from JSON */
- this.mediaContents = null;
-
this.hasReceivers = false;
this.initializeCastPlayer();
@@ -89,6 +87,8 @@
return;
}
+ // v1 Id AE4DA10A
+ // v2 Id 472F0435
var applicationID = 'AE4DA10A';
// request session
@@ -97,7 +97,7 @@
this.sessionListener.bind(this),
this.receiverListener.bind(this));
- console.log('chrome.cast.initialize');
+ console.log('chromecast.initialize');
chrome.cast.initialize(apiConfig, this.onInitSuccess.bind(this), this.onError.bind(this));
};
@@ -106,7 +106,8 @@
* Callback function for init success
*/
CastPlayer.prototype.onInitSuccess = function () {
- console.log("init success");
+ this.isInitialized = true;
+ console.log("chromecast init success");
};
/**
@@ -127,11 +128,11 @@
this.session = e;
if (this.session) {
this.deviceState = DEVICE_STATE.ACTIVE;
-
+ MediaController.setActivePlayer(PlayerName);
if (this.session.media[0]) {
this.onMediaDiscovered('activeSession', this.session.media[0]);
}
-
+
this.session.addUpdateListener(this.sessionUpdateListener.bind(this));
}
};
@@ -143,8 +144,6 @@
*/
CastPlayer.prototype.receiverListener = function (e) {
- console.log("cast.receiverListener", e);
-
if (e === 'available') {
console.log("chromecast receiver found");
this.hasReceivers = true;
@@ -166,7 +165,7 @@
this.currentMediaSession = null;
clearInterval(this.timer);
- MediaController.removeActivePlayer('Chromecast');
+ MediaController.removeActivePlayer(PlayerName);
}
};
@@ -176,7 +175,7 @@
* session request in opt_sessionRequest.
*/
CastPlayer.prototype.launchApp = function () {
- console.log("launching app...");
+ console.log("chromecast launching app...");
chrome.cast.requestSession(this.onRequestSessionSuccess.bind(this), this.onLaunchError.bind(this));
if (this.timer) {
clearInterval(this.timer);
@@ -188,7 +187,7 @@
* @param {Object} e A chrome.cast.Session object
*/
CastPlayer.prototype.onRequestSessionSuccess = function (e) {
- console.log("session success: " + e.sessionId);
+ console.log("chromecast session success: " + e.sessionId);
this.session = e;
this.deviceState = DEVICE_STATE.ACTIVE;
this.session.addUpdateListener(this.sessionUpdateListener.bind(this));
@@ -198,7 +197,7 @@
* Callback function for launch error
*/
CastPlayer.prototype.onLaunchError = function () {
- console.log("launch error");
+ console.log("chromecast launch error");
this.deviceState = DEVICE_STATE.ERROR;
Dashboard.alert({
@@ -207,7 +206,7 @@
});
- MediaController.removeActivePlayer('Chromecast');
+ MediaController.removeActivePlayer(PlayerName);
};
/**
@@ -228,56 +227,411 @@
this.castPlayerState = PLAYER_STATE.IDLE;
this.currentMediaSession = null;
clearInterval(this.timer);
-
- //// continue to play media locally
- //console.log("current time: " + this.currentMediaTime);
- //this.playMediaLocally(this.currentMediaTime);
};
+ function getMaxVideoAudioChannels() {
+ return 6;
+ }
+
+ function getMaxAudioChannels() {
+ return 2;
+ }
+
+ function getMaxVideoLevel() {
+ return 41;
+ }
+
+ function canDirectStream(mediaType, mediaSource, maxBitrate) {
+
+ // If bitrate is unknown don't direct stream
+ if (!mediaSource.Bitrate || mediaSource.Bitrate > maxBitrate) {
+ return false;
+ }
+
+ if (mediaType == "Audio") {
+
+ return ['mp3', 'aac'].indexOf(mediaSource.Container || '') != -1;
+ }
+ else if (mediaType == "Video") {
+
+ var videoStream = mediaSource.MediaStreams.filter(function (s) {
+
+ return s.Type == 'Video';
+
+ })[0];
+
+ if (!videoStream) {
+ return false;
+ }
+
+ if (['high', 'main', 'baseline'].indexOf((videoStream.Profile || '').toLowerCase()) == -1) {
+ return false;
+ }
+
+ if (!videoStream.Level || videoStream.Level > getMaxVideoLevel()) {
+ return false;
+ }
+
+ if (!videoStream.Width || videoStream.Width > 1920) {
+ return false;
+ }
+
+ if (!videoStream.Height || videoStream.Height > 1080) {
+ return false;
+ }
+
+ return ['mp4'].indexOf(mediaSource.Container || '') != -1;
+ }
+
+ throw new Error('Unrecognized MediaType');
+ }
+
+ function canPlayAudioStreamDirect(audioStream, isVideo) {
+
+ var audioCodec = (audioStream.Codec || '').toLowerCase().replace('-', '');
+
+ if (audioCodec.indexOf('aac') == -1 &&
+ audioCodec.indexOf('mp3') == -1 &&
+ audioCodec.indexOf('mpeg') == -1) {
+
+ return false;
+ }
+
+ var maxChannels = isVideo ? getMaxVideoAudioChannels() : getMaxAudioChannels();
+
+ if (!audioStream.Channels || audioStream.Channels > maxChannels) {
+ return false;
+ }
+
+ return true;
+ }
+
+ function isSupportedCodec(mediaType, mediaSource) {
+
+ if (mediaType == "Audio") {
+ return false;
+ }
+ else if (mediaType == "Video") {
+
+ return mediaSource.MediaStreams.filter(function (m) {
+
+ return m.Type == "Video" && (m.Codec || '').toLowerCase() == 'h264';
+
+ }).length > 0;
+ }
+
+ throw new Error('Unrecognized MediaType');
+ }
+
+ function getStreamByIndex(streams, type, index) {
+ return streams.filter(function (s) {
+
+ return s.Type == type && s.Index == index;
+
+ })[0];
+ }
+
+ function getDefaultAudioStream(mediaStreams, user) {
+
+ // Find all audio streams
+ var audioStreams = mediaStreams.filter(function (stream) {
+ return stream.Type == "Audio";
+
+ }).sort(function (a, b) {
+
+ var av = a.IsDefault ? 0 : 1;
+ var bv = b.IsDefault ? 0 : 1;
+
+ return av - bv;
+ });
+
+ if (user.Configuration.AudioLanguagePreference) {
+
+ for (var i = 0, length = audioStreams.length; i < length; i++) {
+ var mediaStream = audioStreams[i];
+
+ if (mediaStream.Language == user.Configuration.AudioLanguagePreference) {
+ return mediaStream.Index;
+ }
+
+ }
+ }
+
+ // Just use the first audio stream
+ return audioStreams[0];
+ }
+
+ function getMediaSourceInfo(user, item, maxBitrate, mediaSourceId, audioStreamIndex, subtitleStreamIndex) {
+
+ var sources = item.MediaSources;
+
+ // If a specific stream was requested, filter the list
+ if (mediaSourceId) {
+ sources = sources.filter(function (m) {
+
+ return m.Id == mediaSourceId;
+
+ });
+ }
+
+ // Find first one that can be direct streamed
+ var source = sources.filter(function (m) {
+
+ var audioStreams = m.MediaStreams.filter(function (s) {
+ return s.Type == 'Audio';
+ });
+
+ var audioStream = mediaSourceId == m.Id && audioStreamIndex != null ? getStreamByIndex(audioStreams, 'Audio', audioStreamIndex) : getDefaultAudioStream(audioStreams, user);
+
+ if (!audioStream || !canPlayAudioStreamDirect(audioStream, item.MediaType == 'Video')) {
+ return false;
+ }
+
+ var subtitleStream = mediaSourceId == m.Id && subtitleStreamIndex != null ? getStreamByIndex(m.MediaStreams, 'Subtitle', subtitleStreamIndex) : null;
+
+ if (subtitleStream) {
+ return false;
+ }
+
+ return canDirectStream(item.MediaType, m, maxBitrate, audioStream);
+
+ })[0];
+
+ if (source) {
+ return {
+ mediaSource: source,
+ isStatic: true,
+ streamContainer: source.Container
+ };
+ }
+
+ // Find first one with supported codec
+ source = sources.filter(function (m) {
+
+ return isSupportedCodec(item.MediaType, m);
+
+ })[0];
+
+ // Default to first one
+ return {
+ mediaSource: source || sources[0],
+ isStatic: false,
+ streamContainer: item.MediaType == 'Audio' ? 'mp3' : 'm3u8'
+ };
+ }
+
+ function getCustomData(item, mediaSourceId, startTimeTicks) {
+
+ return {
+
+ serverAddress: ApiClient.serverAddress(),
+ itemId: item.Id,
+ userId: Dashboard.getCurrentUserId(),
+ deviceName: ApiClient.deviceName(),
+ //deviceId: ApiClient.deviceId(),
+ startTimeTicks: startTimeTicks || 0
+ };
+
+ }
+
+ function getMetadata(item) {
+
+ var metadata = {};
+
+ if (item.Type == 'Episode') {
+ metadata = new chrome.cast.media.TvShowMediaMetadata();
+ metadata.type = chrome.cast.media.MetadataType.TV_SHOW;
+
+ metadata.episodeTitle = item.Name;
+
+ if (item.PremiereDate) {
+ metadata.originalAirdate = parseISO8601Date(item.PremiereDate).toISOString();
+ }
+
+ metadata.seriesTitle = item.SeriesName;
+
+ if (item.IndexNumber != null) {
+ metadata.episode = metadata.episodeNumber = item.IndexNumber;
+ }
+
+ if (item.ParentIndexNumber != null) {
+ metadata.season = metadata.seasonNumber = item.ParentIndexNumber;
+ }
+ }
+
+ else if (item.Type == 'Photo') {
+ metadata = new chrome.cast.media.PhotoMediaMetadata();
+ metadata.type = chrome.cast.media.MetadataType.PHOTO;
+
+ if (item.PremiereDate) {
+ metadata.creationDateTime = parseISO8601Date(item.PremiereDate).toISOString();
+ }
+ }
+
+ else if (item.MediaType == 'Audio') {
+ metadata = new chrome.cast.media.MusicTrackMediaMetadata();
+ metadata.type = chrome.cast.media.MetadataType.MUSIC_TRACK;
+
+ if (item.ProductionYear) {
+ metadata.releaseYear = item.ProductionYear;
+ }
+
+ if (item.PremiereDate) {
+ metadata.releaseDate = parseISO8601Date(item.PremiereDate).toISOString();
+ }
+
+ metadata.songName = item.Name;
+ metadata.artist = item.Artists & item.Artists.length ? item.Artists[0] : '';
+ metadata.albumArtist = item.AlbumArtist;
+
+ if (item.IndexNumber != null) {
+ metadata.trackNumber = item.IndexNumber;
+ }
+
+ if (item.ParentIndexNumber != null) {
+ metadata.discNumber = item.ParentIndexNumber;
+ }
+
+ var composer = (item.People || []).filter(function (p) {
+ return p.PersonType == 'Type';
+ })[0];
+
+ if (composer) {
+ metadata.composer = composer.Name;
+ }
+ }
+
+ else if (item.MediaType == 'Movie') {
+ metadata = new chrome.cast.media.MovieMediaMetadata();
+ metadata.type = chrome.cast.media.MetadataType.MUSIC_TRACK;
+
+ if (item.ProductionYear) {
+ metadata.releaseYear = item.ProductionYear;
+ }
+
+ if (item.PremiereDate) {
+ metadata.releaseDate = parseISO8601Date(item.PremiereDate).toISOString();
+ }
+ }
+
+ else {
+ metadata = new chrome.cast.media.GenericMediaMetadata();
+ metadata.type = chrome.cast.media.MetadataType.GENERIC;
+
+ if (item.ProductionYear) {
+ metadata.releaseYear = item.ProductionYear;
+ }
+
+ if (item.PremiereDate) {
+ metadata.releaseDate = parseISO8601Date(item.PremiereDate).toISOString();
+ }
+ }
+
+ metadata.title = item.Name;
+
+ if (item.Studios && item.Studios.length) {
+ metadata.Studio = item.Studios[0];
+ }
+
+ return metadata;
+ }
+
+ function getStreamUrl(item, mediaSourceInfo, startTimeTicks, maxBitrate) {
+
+ var url;
+
+ if (item.MediaType == 'Audio') {
+
+ url = ApiClient.serverAddress() + '/mediabrowser/audio/' + item.Id + '/stream.' + mediaSourceInfo.streamContainer + '?';
+ url += '&static=' + mediaSourceInfo.isStatic.toString();
+ url += '&maxaudiochannels=' + getMaxAudioChannels();
+
+ if (startTimeTicks) {
+ url += '&startTimeTicks=' + startTimeTicks.toString();
+ }
+
+ if (maxBitrate) {
+ url += '&audiobitrate=' + Math.min(maxBitrate, 320000).toString();
+ }
+
+ url += '&audiosamplerate=44100';
+ url += '&mediasourceid=' + mediaSourceInfo.mediaSource.Id;
+
+ return url;
+
+ }
+ else if (item.MediaType == 'Video') {
+
+ url = ApiClient.serverAddress() + '/mediabrowser/videos/' + item.Id + '/stream.' + mediaSourceInfo.streamContainer + '?';
+ url += 'static=' + mediaSourceInfo.isStatic.toString();
+ url += '&maxaudiochannels=' + getMaxVideoAudioChannels();
+
+ if (startTimeTicks) {
+ url += '&startTimeTicks=' + startTimeTicks.toString();
+ }
+
+ if (maxBitrate) {
+
+ var audioRate = 768000;
+ url += '&audiobitrate=' + audioRate.toString();
+ url += '&videobitrate=' + (maxBitrate - audioRate).toString();
+ }
+
+ url += '&profile=high';
+ url += '&level=' + getMaxVideoLevel();
+
+ url += '&maxwidth=1920';
+ url += '&maxheight=1080';
+
+ url += '&videoCodec=h264';
+ url += '&audioCodec=aac';
+
+ url += '&audiosamplerate=44100';
+ url += '&mediasourceid=' + mediaSourceInfo.mediaSource.Id;
+
+ return url;
+ }
+
+ throw new Error('Unrecognized MediaType');
+ }
+
/**
* Loads media into a running receiver application
* @param {Number} mediaIndex An index number to indicate current media content
*/
- CastPlayer.prototype.loadMedia = function (mediaIndex) {
-
+ CastPlayer.prototype.loadMedia = function (user, item, startTimeTicks, mediaSourceId, audioStreamIndex, subtitleStreamIndex) {
+
if (!this.session) {
console.log("no session");
return;
}
-
- //console.log("loading..." + this.mediaContents[mediaIndex]['title']);
-
- //var mediaInfo = new chrome.cast.media.MediaInfo(this.mediaContents[mediaIndex]['sources'][0]);
-
- //mediaInfo.contentType = 'video/mp4';
- //var request = new chrome.cast.media.LoadRequest(mediaInfo);
- //request.autoplay = this.autoplay;
- //if (this.localPlayerState == PLAYER_STATE.PLAYING) {
- // request.currentTime = this.localPlayer.currentTime;
- //}
- //else {
- // request.currentTime = 0;
- //}
- //var payload = {
- // "title:": this.mediaContents[0]['title'],
- // "thumb": this.mediaContents[0]['thumb']
- //};
- //var json = {
- // "payload": payload
- //};
+ var maxBitrate = 12000000;
+ var mediaInfo = getMediaSourceInfo(user, item, maxBitrate, mediaSourceId, audioStreamIndex, subtitleStreamIndex);
- //request.customData = json;
+ var streamUrl = getStreamUrl(item, mediaInfo, startTimeTicks, maxBitrate);
- //this.castPlayerState = PLAYER_STATE.LOADING;
- //this.session.loadMedia(request,
- // this.onMediaDiscovered.bind(this, 'loadMedia'),
- // this.onLoadMediaError.bind(this));
+ var castMediaInfo = new chrome.cast.media.MediaInfo(streamUrl);
- //document.getElementById("media_title").innerHTML = this.mediaContents[this.currentMediaIndex]['title'];
- //document.getElementById("media_subtitle").innerHTML = this.mediaContents[this.currentMediaIndex]['subtitle'];
- //document.getElementById("media_desc").innerHTML = this.mediaContents[this.currentMediaIndex]['description'];
+ castMediaInfo.customData = getCustomData(item, mediaInfo.mediaSource.Id, startTimeTicks);
+ castMediaInfo.metadata = getMetadata(item);
+ if (mediaInfo.streamContainer == 'm3u8') {
+ castMediaInfo.contentType = 'application/x-mpegURL';
+ } else {
+ castMediaInfo.contentType = item.MediaType.toLowerCase() + '/' + mediaInfo.streamContainer.toLowerCase();
+ }
+
+ castMediaInfo.streamType = mediaInfo.isStatic ? chrome.cast.media.StreamType.BUFFERED : chrome.cast.media.StreamType.LIVE;
+
+ var request = new chrome.cast.media.LoadRequest(castMediaInfo);
+ request.autoplay = true;
+ request.currentTime = 0;
+
+ this.castPlayerState = PLAYER_STATE.LOADING;
+ this.session.loadMedia(request,
+ this.onMediaDiscovered.bind(this, 'loadMedia'),
+ this.onLoadMediaError.bind(this));
};
/**
@@ -285,16 +639,11 @@
* @param {Object} mediaSession A new media object.
*/
CastPlayer.prototype.onMediaDiscovered = function (how, mediaSession) {
-
- console.log("new media session ID:" + mediaSession.mediaSessionId + ' (' + how + ')');
+
+ console.log("chromecast new media session ID:" + mediaSession.mediaSessionId + ' (' + how + ')');
this.currentMediaSession = mediaSession;
if (how == 'loadMedia') {
- if (this.autoplay) {
- this.castPlayerState = PLAYER_STATE.PLAYING;
- }
- else {
- this.castPlayerState = PLAYER_STATE.LOADED;
- }
+ this.castPlayerState = PLAYER_STATE.PLAYING;
}
if (how == 'activeSession') {
@@ -327,25 +676,13 @@
// }
//}
//document.getElementById("duration").innerHTML = duration;
-
- //if (this.localPlayerState == PLAYER_STATE.PLAYING) {
- // this.localPlayerState == PLAYER_STATE.STOPPED;
- // var vi = document.getElementById('video_image')
- // vi.style.display = 'block';
- // this.localPlayer.style.display = 'none';
- // // start progress timer
- // this.startProgressTimer(this.incrementMediaTime);
- //}
- //// update UIs
- //this.updateMediaControlUI();
- //this.updateDisplayMessage();
};
/**
* Callback function when media load returns error
*/
CastPlayer.prototype.onLoadMediaError = function (e) {
- console.log("media error");
+ console.log("chromecast media error");
this.castPlayerState = PLAYER_STATE.IDLE;
};
@@ -358,7 +695,7 @@
this.currentMediaTime = 0;
this.castPlayerState = PLAYER_STATE.IDLE;
}
- console.log("updating media");
+ console.log("chromecast updating media");
//this.updateProgressBar(e);
};
@@ -383,7 +720,7 @@
* Play media in Cast mode
*/
CastPlayer.prototype.playMedia = function () {
-
+
if (!this.currentMediaSession) {
return;
}
@@ -415,7 +752,7 @@
* Pause media playback in Cast mode
*/
CastPlayer.prototype.pauseMedia = function () {
-
+
if (!this.currentMediaSession) {
return;
}
@@ -433,7 +770,7 @@
* Stop CC playback
*/
CastPlayer.prototype.stopMedia = function () {
-
+
if (!this.currentMediaSession) {
return;
}
@@ -450,7 +787,7 @@
* @param {Boolean} mute A boolean
*/
CastPlayer.prototype.setReceiverVolume = function (mute) {
-
+
if (!this.currentMediaSession) {
return;
}
@@ -515,18 +852,6 @@
var pw = pos;
}
- if (this.localPlayerState == PLAYER_STATE.PLAYING || this.localPlayerState == PLAYER_STATE.PAUSED) {
- this.localPlayer.currentTime = curr;
- this.currentMediaTime = curr;
- this.localPlayer.play();
- }
-
- if (this.localPlayerState == PLAYER_STATE.PLAYING || this.localPlayerState == PLAYER_STATE.PAUSED
- || this.castPlayerState == PLAYER_STATE.PLAYING || this.castPlayerState == PLAYER_STATE.PAUSED) {
- p.style.width = pw + 'px';
- pi.style.marginLeft = pp + 'px';
- }
-
if (this.castPlayerState != PLAYER_STATE.PLAYING && this.castPlayerState != PLAYER_STATE.PAUSED) {
return;
}
@@ -616,24 +941,48 @@
var castPlayer = new CastPlayer();
- window.CastPlayer = castPlayer;
-
function chromecastPlayer() {
var self = this;
- self.name = 'Chromecast';
+ self.name = PlayerName;
self.play = function (options) {
+ if (options.items) {
+
+ Dashboard.getCurrentUser().done(function (user) {
+
+ castPlayer.loadMedia(user, options.items[0], options.startTimeTicks);
+ });
+
+ } else {
+
+ var userId = Dashboard.getCurrentUserId();
+
+ var query = {};
+ query.Limit = query.Limit || 100;
+ query.Fields = "MediaSources,Chapters";
+ query.ExcludeLocationTypes = "Virtual";
+ query.Ids = options.ids.join(',');
+
+ ApiClient.getItems(userId, query).done(function (result) {
+
+ options.items = result.Items;
+
+ self.play(options);
+
+ });
+ }
+
};
self.shuffle = function (id) {
-
+ self.play({ ids: [id] });
};
self.instantMix = function (id) {
-
+ self.play({ ids: [id] });
};
self.queue = function (options) {
@@ -645,7 +994,7 @@
};
self.stop = function () {
- CastPlayer.stop();
+ castPlayer.stop();
};
self.canQueueMediaType = function (mediaType) {
@@ -653,57 +1002,55 @@
return false;
};
- self.mute = function() {
- CastPlayer.mute();
+ self.mute = function () {
+ castPlayer.mute();
};
self.unMute = function () {
- CastPlayer.unMute();
+ castPlayer.unMute();
};
self.toggleMute = function () {
- CastPlayer.toggleMute();
+ castPlayer.toggleMute();
};
self.getTargets = function () {
var targets = [];
- var appName = null;
-
- //if (CastPlayer.session && CastPlayer.session.receiver && CastPlayer.session.friendlyName) {
- // appName = CastPlayer.session.friendlyName;
- //}
-
- //if (true) {
- // targets.push({
- // name: "Chromecast",
- // id: "Chromecast",
- // playerName: self.name,
- // playableMediaTypes: ["Audio", "Video"],
- // isLocalPlayer: false,
- // appName: appName
- // });
- //}
+ targets.push(self.getCurrentTargetInfo());
return targets;
};
+
+ self.getCurrentTargetInfo = function () {
+
+ var appName = null;
+
+ if (castPlayer.session && castPlayer.session.receiver && castPlayer.session.friendlyName) {
+ appName = castPlayer.session.friendlyName;
+ }
+
+ return {
+ name: PlayerName,
+ id: PlayerName,
+ playerName: self.name,
+ playableMediaTypes: ["Audio", "Video"],
+ isLocalPlayer: false,
+ appName: appName
+ };
+ };
}
MediaController.registerPlayer(new chromecastPlayer());
- $(document).on('headercreated', ".libraryPage", function () {
-
- var page = this;
-
- //castPlayer.updateMediaControlUI();
-
- });
-
$(MediaController).on('playerchange', function () {
- if (MediaController.getPlayerInfo().name == 'Chromecast') {
- window.CastPlayer.launchApp();
+ if (MediaController.getPlayerInfo().name == PlayerName) {
+
+ if (castPlayer.deviceState != DEVICE_STATE.ACTIVE && castPlayer.isInitialized) {
+ castPlayer.launchApp();
+ }
}
});
diff --git a/dashboard-ui/scripts/itemdetailpage.js b/dashboard-ui/scripts/itemdetailpage.js
index cefc647a85..5aa253812f 100644
--- a/dashboard-ui/scripts/itemdetailpage.js
+++ b/dashboard-ui/scripts/itemdetailpage.js
@@ -2,6 +2,22 @@
var currentItem;
+ function getExternalPlayUrl(item) {
+
+ var providerIds = item.ProviderIds || {};
+ if (item.GameSystem == "Nintendo" && item.MediaType == "Game" && providerIds.NesBox && providerIds.NesBoxRom) {
+
+ return "http://nesbox.com/game/" + providerIds.NesBox + '/rom/' + providerIds.NesBoxRom;
+ }
+
+ if (item.GameSystem == "Super Nintendo" && item.MediaType == "Game" && providerIds.NesBox && providerIds.NesBoxRom) {
+
+ return "http://snesbox.com/game/" + providerIds.NesBox + '/rom/' + providerIds.NesBoxRom;
+ }
+
+ return null;
+ }
+
function reload(page) {
var id = getParameterByName('id');
@@ -117,22 +133,6 @@
$('#btnEdit', page).attr('href', "edititemmetadata.html?id=" + id);
}
- function getExternalPlayUrl(item) {
-
-
- if (item.GameSystem == "Nintendo" && item.MediaType == "Game" && item.ProviderIds.NesBox && item.ProviderIds.NesBoxRom) {
-
- return "http://nesbox.com/game/" + item.ProviderIds.NesBox + '/rom/' + item.ProviderIds.NesBoxRom;
- }
-
- if (item.GameSystem == "Super Nintendo" && item.MediaType == "Game" && item.ProviderIds.NesBox && item.ProviderIds.NesBoxRom) {
-
- return "http://snesbox.com/game/" + item.ProviderIds.NesBox + '/rom/' + item.ProviderIds.NesBoxRom;
- }
-
- return null;
- };
-
function setPeopleHeader(page, item) {
if (item.Type == "Audio" || item.Type == "MusicAlbum" || item.MediaType == "Book" || item.MediaType == "Photo") {
diff --git a/dashboard-ui/scripts/librarybrowser.js b/dashboard-ui/scripts/librarybrowser.js
index ad24ffd1dc..8dc473f6bb 100644
--- a/dashboard-ui/scripts/librarybrowser.js
+++ b/dashboard-ui/scripts/librarybrowser.js
@@ -20,14 +20,14 @@
loadSavedQueryValues: function (key, query) {
- //var values = localStorage.getItem(key + '_' + Dashboard.getCurrentUserId());
+ var values = localStorage.getItem(key + '_' + Dashboard.getCurrentUserId());
- //if (values) {
+ if (values) {
- // values = JSON.parse(values);
+ values = JSON.parse(values);
- // return $.extend(query, values);
- //}
+ return $.extend(query, values);
+ }
return query;
},
@@ -1397,7 +1397,7 @@
var url;
- var imageHeight = 440;
+ var imageHeight = 600;
if (imageTags.Primary) {
diff --git a/dashboard-ui/scripts/mediacontroller.js b/dashboard-ui/scripts/mediacontroller.js
index e2425ea8bb..074bc880f1 100644
--- a/dashboard-ui/scripts/mediacontroller.js
+++ b/dashboard-ui/scripts/mediacontroller.js
@@ -43,14 +43,14 @@
$(self).trigger('playerchange');
};
- self.setLocalPlayerActive = function() {
- self.setActivePlayer(self.getLocalPlayer());
+ self.setDefaultPlayerActive = function() {
+ self.setActivePlayer(self.getDefaultPlayer());
};
self.removeActivePlayer = function (name) {
if (self.getPlayerInfo().name == name) {
- self.setLocalPlayerActive();
+ self.setDefaultPlayerActive();
}
};
@@ -163,6 +163,17 @@
return p.isLocalPlayer;
})[0];
};
+
+ self.getDefaultPlayer = function () {
+
+ return currentPlayer.isLocalPlayer ?
+
+ currentPlayer :
+
+ players.filter(function (p) {
+ return p.isDefaultPlayer;
+ })[0];
+ };
}
window.MediaController = new mediaController();
@@ -188,6 +199,12 @@
}
}
+ else if (msg.MessageType === "ServerShuttingDown") {
+ MediaController.setDefaultPlayerActive();
+ }
+ else if (msg.MessageType === "ServerRestarting") {
+ MediaController.setDefaultPlayerActive();
+ }
else if (msg.MessageType === "Playstate") {
if (msg.Data.Command === 'Stop') {
diff --git a/dashboard-ui/scripts/mediaplayer-video.js b/dashboard-ui/scripts/mediaplayer-video.js
index 277202c04c..50d5d41407 100644
--- a/dashboard-ui/scripts/mediaplayer-video.js
+++ b/dashboard-ui/scripts/mediaplayer-video.js
@@ -32,7 +32,7 @@
self.remoteFullscreen = function () {
var videoControls = $("#videoControls");
-
+
if (remoteFullscreen) {
exitFullScreenToWindow();
videoControls.removeClass("inactive");
@@ -403,7 +403,7 @@
};
function getAudioTracksHtml() {
-
+
var streams = currentMediaSource.MediaStreams.filter(function (currentStream) {
return currentStream.Type == "Audio";
});
@@ -625,9 +625,16 @@
function getInitialAudioStreamIndex(mediaStreams, user) {
- // Find all audio streams with at least one channel
+ // Find all audio streams
var audioStreams = mediaStreams.filter(function (stream) {
- return stream.Type == "Audio" && stream.Channels;
+ return stream.Type == "Audio";
+
+ }).sort(function (a, b) {
+
+ var av = a.IsDefault ? 0 : 1;
+ var bv = b.IsDefault ? 0 : 1;
+
+ return av - bv;
});
if (user.Configuration.AudioLanguagePreference) {
@@ -643,9 +650,9 @@
}
// Just use the first audio stream
- return audioStreams.length ? audioStreams[0].Index : null;
+ return audioStreams[0];
};
-
+
function getVideoQualityOptions(mediaStreams) {
var videoStream = mediaStreams.filter(function (stream) {
@@ -753,9 +760,6 @@
})[0];
m3U8Quality = $.extend(m3U8Quality, self.getFinalVideoParams(mediaSource, mp4Quality.maxWidth, mp4Quality.bitrate, baseParams.AudioStreamIndex, baseParams.SubtitleStreamIndex, '.mp4'));
- // Webm must be ahead of mp4 due to the issue of mp4 playing too fast in chrome
- var prioritizeWebmOverH264 = $.browser.chrome || $.browser.msie;
-
var isStatic = mp4Quality.isStatic;
self.startTimeTicksOffset = isStatic ? 0 : startPosition || 0;
@@ -815,18 +819,15 @@
html += '';
}
- if (prioritizeWebmOverH264 && !isStatic) {
+ // Have to put webm ahead of mp4 because it will play in fast forward in chrome
+ // And firefox doesn't like fragmented mp4
+ if (!isStatic) {
html += '';
}
html += '';
- if (!prioritizeWebmOverH264 && !isStatic) {
-
- html += '';
- }
-
html += '';
var mediaPlayer = $("#mediaPlayer").show();
@@ -1016,7 +1017,7 @@
if (e.keyCode == 27) {
self.stop();
- $(this).unbind("keyup.enhancePlayer");
+ $(this).off("keyup.enhancePlayer");
}
});
diff --git a/dashboard-ui/scripts/mediaplayer.js b/dashboard-ui/scripts/mediaplayer.js
index 9996fbc5cb..299a94e8ab 100644
--- a/dashboard-ui/scripts/mediaplayer.js
+++ b/dashboard-ui/scripts/mediaplayer.js
@@ -24,6 +24,7 @@
self.playlist = [];
self.isLocalPlayer = true;
+ self.isDefaultPlayer = true;
self.name = 'Html5 Player';
self.getTargets = function () {
@@ -115,7 +116,7 @@
}
// Chrome or IE with plugin installed
- if (canPlayWebm() && !$.browser.mozilla) {
+ if (canPlayWebm()) {
return '.webm';
}
@@ -232,7 +233,7 @@
return false;
}
- if (mediaSource.VideoType != "VideoFile" || mediaSource.LocationType != "FileSystem") {
+ if (mediaSource.VideoType != "VideoFile") {
console.log('Transcoding because the content is not a video file');
return false;
}
@@ -257,10 +258,7 @@
return false;
}
- var videoBitrate = videoStream.BitRate || 0;
- var audioBitrate = audioStream ? audioStream.BitRate || 0 : null;
-
- if ((videoBitrate + audioBitrate) > bitrate) {
+ if (!mediaSource.Bitrate || mediaSource.Bitrate > bitrate) {
console.log('Transcoding because bitrate is too high');
return false;
}
diff --git a/dashboard-ui/scripts/remotecontrol.js b/dashboard-ui/scripts/remotecontrol.js
index 932d988ea5..35add5b195 100644
--- a/dashboard-ui/scripts/remotecontrol.js
+++ b/dashboard-ui/scripts/remotecontrol.js
@@ -424,7 +424,7 @@
PlayCommand: playType
};
-
+
if (options.startPositionTicks) {
remoteOptions.startPositionTicks = options.startPositionTicks;
}
@@ -523,4 +523,18 @@
MediaController.registerPlayer(new remoteControlPlayer());
+ function onWebSocketMessageReceived(e, msg) {
+
+ if (msg.MessageType === "SessionEnded") {
+
+ console.log("Server reports another session ended");
+
+ if (MediaController.getPlayerInfo().id == msg.Data.Id) {
+ MediaController.setDefaultPlayerActive();
+ }
+ }
+ }
+
+ $(ApiClient).on("websocketmessage", onWebSocketMessageReceived);
+
})(window, document, jQuery);
\ No newline at end of file