From ee5007c55d764cb858ecf11a3f52733464991e36 Mon Sep 17 00:00:00 2001 From: Techywarrior Date: Wed, 3 Apr 2013 16:22:15 -0700 Subject: [PATCH] chapter menu update based on time, split videoJS extensions into own file --- dashboard-ui/scripts/extensions.js | 402 +--------------- dashboard-ui/scripts/mediaplayer.js | 528 ++++++++++------------ dashboard-ui/scripts/videojsextensions.js | 485 +++++++++++++++++++- 3 files changed, 723 insertions(+), 692 deletions(-) diff --git a/dashboard-ui/scripts/extensions.js b/dashboard-ui/scripts/extensions.js index 7455c1970d..87ed8972f2 100644 --- a/dashboard-ui/scripts/extensions.js +++ b/dashboard-ui/scripts/extensions.js @@ -331,404 +331,22 @@ function parseISO8601Date(s, toLocal) { })(jQuery, window); -/* - JS for the quality selector in video.js player - */ - -/* - Define the base class for the quality selector button. - Most of this code is copied from the _V_.TextTrackButton - class. - - https://github.com/zencoder/video-js/blob/master/src/tracks.js#L560) - */ -_V_.ResolutionSelector = _V_.Button.extend({ - - kind: "quality", - className: "vjs-quality-button", - - init: function (player, options) { - - this._super(player, options); - - // Save the starting resolution as a property of the player object - player.options.currentResolution = this.buttonText; - - this.menu = this.createMenu(); - - if (this.items.length === 0) { - this.hide(); - } - }, - - createMenu: function () { - - var menu = new _V_.Menu(this.player); - - // Add a title list item to the top - menu.el.appendChild(_V_.createElement("li", { - className: "vjs-menu-title", - innerHTML: _V_.uc(this.kind) - })); - - this.items = this.createItems(); - - // Add menu items to the menu - this.each(this.items, function (item) { - menu.addItem(item); - }); - - // Add list to element - this.addComponent(menu); - - return menu; - }, - - // Override the default _V_.Button createElement so the button text isn't hidden - createElement: function (type, attrs) { - - // Add standard Aria and Tabindex info - attrs = _V_.merge({ - className: this.buildCSSClass(), - innerHTML: '
' + this.buttonText + '
', - role: "button", - tabIndex: 0 - }, attrs); - - return this._super(type, attrs); - }, - - // Create a menu item for each text track - createItems: function () { - - var items = []; - - this.each(this.availableRes, function (res) { - - items.push(new _V_.ResolutionMenuItem(this.player, { - - label: res[0].res, - src: res - })); - }); - - return items; - }, - - buildCSSClass: function () { - - return this.className + " vjs-menu-button " + this._super(); - }, - - // Focus - Add keyboard functionality to element - onFocus: function () { - - // Show the menu, and keep showing when the menu items are in focus - this.menu.lockShowing(); - this.menu.el.style.display = "block"; - - // When tabbing through, the menu should hide when focus goes from the last menu item to the next tabbed element. - _V_.one(this.menu.el.childNodes[this.menu.el.childNodes.length - 1], "blur", this.proxy(function () { - - this.menu.unlockShowing(); - })); - }, - - // Can't turn off list display that we turned on with focus, because list would go away. - onBlur: function () { }, - - onClick: function () { - - /* - When you click the button it adds focus, which will show the menu indefinitely. - So we'll remove focus when the mouse leaves the button. - Focus is needed for tab navigation. - */ - this.one('mouseout', this.proxy(function () { - - this.menu.unlockShowing(); - this.el.blur(); - })); - } -}); - -/* - Define the base class for the quality menu items - */ -_V_.ResolutionMenuItem = _V_.MenuItem.extend({ - - init: function (player, options) { - - // Modify options for parent MenuItem class's init. - options.selected = (options.label === player.options.currentResolution); - this._super(player, options); - - this.player.addEvent('changeRes', _V_.proxy(this, this.update)); - }, - - onClick: function () { - - // Check that we are changing to a new quality (not the one we are already on) - if (this.options.label === this.player.options.currentResolution) - return; - - var resolutions = new Array(); - resolutions['high'] = new Array(1500000, 128000, 1920, 1080); - resolutions['medium'] = new Array(750000, 128000, 1280, 720); - resolutions['low'] = new Array(200000, 128000, 720, 480); - - var current_time = this.player.currentTime(); - - // Set the button text to the newly chosen quality - jQuery(this.player.controlBar.el).find('.vjs-quality-text').html(this.options.label); - - // Change the source and make sure we don't start the video over - var currentSrc = this.player.tag.src; - var src = parse_src_url(currentSrc); - var newSrc = "/mediabrowser/" + src.Type + "/" + src.item_id + "/stream." + src.stream + "?audioChannels=" + src.audioChannels + "&audioBitrate=" + resolutions[this.options.src[0].res][1] + - "&videoBitrate=" + resolutions[this.options.src[0].res][0] + "&maxWidth=" + resolutions[this.options.src[0].res][2] + "&maxHeight=" + resolutions[this.options.src[0].res][3] + - "&videoCodec=" + src.videoCodec + "&audioCodec=" + src.audioCodec; - - if (this.player.duration() == "Infinity") { - if (currentSrc.indexOf("StartTimeTicks") >= 0) { - var startTimeTicks = currentSrc.match(new RegExp("StartTimeTicks=[0-9]+", "g")); - var start_time = startTimeTicks[0].replace("StartTimeTicks=", ""); - - newSrc += "&StartTimeTicks=" + Math.floor(parseInt(start_time) + (10000000 * current_time)); - } else { - newSrc += "&StartTimeTicks=" + Math.floor(10000000 * current_time); - } - - this.player.src(newSrc).one('loadedmetadata', function () { - this.play(); - }); - } else { - this.player.src(newSrc).one('loadedmetadata', function () { - this.currentTime(current_time); - this.play(); - }); - } - - // Save the newly selected resolution in our player options property - this.player.options.currentResolution = this.options.label; - - // Update the classes to reflect the currently selected resolution - this.player.triggerEvent('changeRes'); - }, - - update: function () { - - if (this.options.label === this.player.options.currentResolution) { - this.selected(true); - } else { - this.selected(false); - } - } -}); - - -/* - JS for the chapter selector in video.js player - */ - -/* - Define the base class for the chapter selector button. - */ -_V_.ChapterSelector = _V_.Button.extend({ - - kind: "chapter", - className: "vjs-chapter-button", - - init: function (player, options) { - - this._super(player, options); - - this.menu = this.createMenu(); - - if (this.items.length === 0) { - this.hide(); - } - }, - - createMenu: function () { - - var menu = new _V_.Menu(this.player); - - // Add a title list item to the top - menu.el.appendChild(_V_.createElement("li", { - className: "vjs-menu-title", - innerHTML: _V_.uc(this.kind) - })); - - this.items = this.createItems(); - - // Add menu items to the menu - this.each(this.items, function (item) { - menu.addItem(item); - }); - - // Add list to element - this.addComponent(menu); - - return menu; - }, - - // Override the default _V_.Button createElement so the button text isn't hidden - createElement: function (type, attrs) { - - // Add standard Aria and Tabindex info - attrs = _V_.merge({ - className: this.buildCSSClass(), - innerHTML: '
' + this.buttonText + '
', - role: "button", - tabIndex: 0 - }, attrs); - - return this._super(type, attrs); - }, - - // Create a menu item for each chapter - createItems: function () { - - var items = []; - - this.each(this.Chapters, function (chapter) { - - items.push(new _V_.ChapterMenuItem(this.player, { - label: chapter[0].Name, - src: chapter - })); - }); - - return items; - }, - - buildCSSClass: function () { - - return this.className + " vjs-menu-button " + this._super(); - }, - - // Focus - Add keyboard functionality to element - onFocus: function () { - - // Show the menu, and keep showing when the menu items are in focus - this.menu.lockShowing(); - this.menu.el.style.display = "block"; - - // When tabbing through, the menu should hide when focus goes from the last menu item to the next tabbed element. - _V_.one(this.menu.el.childNodes[this.menu.el.childNodes.length - 1], "blur", this.proxy(function () { - - this.menu.unlockShowing(); - })); - }, - - // Can't turn off list display that we turned on with focus, because list would go away. - onBlur: function () { }, - - onClick: function () { - - /* - When you click the button it adds focus, which will show the menu indefinitely. - So we'll remove focus when the mouse leaves the button. - Focus is needed for tab navigation. - */ - this.one('mouseout', this.proxy(function () { - - this.menu.unlockShowing(); - this.el.blur(); - })); - } -}); - -/* - Define the base class for the chapter menu items - */ -_V_.ChapterMenuItem = _V_.MenuItem.extend({ - - init: function (player, options) { - - // Modify options for parent MenuItem class's init. - //options.selected = ( options.label === player.options.currentResolution ); - this._super(player, options); - - this.player.addEvent('changeChapter', _V_.proxy(this, this.update)); - }, - - onClick: function () { - - // Set the button text to the newly chosen chapter - //jQuery( this.player.controlBar.el ).find( '.vjs-chapter-text' ).html( this.options.label ); - - if (this.player.duration() == "Infinity") { - var currentSrc = this.player.tag.src; - - if (currentSrc.indexOf("StartTimeTicks") >= 0) { - var newSrc = currentSrc.replace(new RegExp("StartTimeTicks=[0-9]+", "g"), "StartTimeTicks=" + this.options.src[0].StartPositionTicks); - } else { - var newSrc = currentSrc += "&StartTimeTicks=" + this.options.src[0].StartPositionTicks; - } - - this.player.src(newSrc).one('loadedmetadata', function () { - this.play(); - }); - } else { - //figure out the time from ticks - var current_time = parseFloat(this.options.src[0].StartPositionTicks) / 10000000; - - this.player.currentTime(current_time); - } - }, - - update: function () { - } -}); - -/* - JS for the stop button in video.js player - */ - -/* - Define the base class for the stop button. - */ - -_V_.StopButton = _V_.Button.extend({ - - kind: "stop", - className: "vjs-stop-button", - - init: function (player, options) { - - this._super(player, options); - - }, - - buildCSSClass: function () { - - return this.className + " vjs-menu-button " + this._super(); - }, - - onClick: function () { - MediaPlayer.stop(); - } -}); - - //convert Ticks to human hr:min:sec format function ticks_to_human(str) { - var in_seconds = (str / 10000000); - var hours = Math.floor(in_seconds / 3600); - var minutes = Math.floor((in_seconds - (hours * 3600)) / 60); - var seconds = '0' + Math.round(in_seconds - (hours * 3600) - (minutes * 60)); + var in_seconds = (str / 10000000); + var hours = Math.floor(in_seconds / 3600); + var minutes = Math.floor((in_seconds - (hours * 3600)) / 60); + var seconds = '0' + Math.round(in_seconds - (hours * 3600) - (minutes * 60)); - var time = ''; + var time = ''; - if (hours > 0) time += hours + ":"; - if (minutes < 10 && hours == 0) time += minutes; - else time += ('0' + minutes).substr(-2); - time += ":" + seconds.substr(-2); + if (hours > 0) time += hours + ":"; + if (minutes < 10 && hours == 0) time += minutes; + else time += ('0' + minutes).substr(-2); + time += ":" + seconds.substr(-2); - return time; + return time; }; //parse video player src URL diff --git a/dashboard-ui/scripts/mediaplayer.js b/dashboard-ui/scripts/mediaplayer.js index 5d2c487417..f05ba46d1a 100644 --- a/dashboard-ui/scripts/mediaplayer.js +++ b/dashboard-ui/scripts/mediaplayer.js @@ -1,390 +1,320 @@ var MediaPlayer = { - testableAudioElement: document.createElement('audio'), - testableVideoElement: document.createElement('video'), + testableAudioElement: document.createElement('audio'), + testableVideoElement: document.createElement('video'), - canPlay: function (item) { + canPlay: function (item) { - if (item.MediaType === "Video") { + if (item.MediaType === "Video") { - var media = MediaPlayer.testableVideoElement; + var media = MediaPlayer.testableVideoElement; - if (media.canPlayType) { + if (media.canPlayType) { - return media.canPlayType('video/mp4').replace(/no/, '') || media.canPlayType('video/mp2t').replace(/no/, '') || media.canPlayType('video/webm').replace(/no/, '') || media.canPlayType('application/x-mpegURL').replace(/no/, '') || media.canPlayType('video/ogv').replace(/no/, ''); - } + return media.canPlayType('video/mp4').replace(/no/, '') || media.canPlayType('video/mp2t').replace(/no/, '') || media.canPlayType('video/webm').replace(/no/, '') || media.canPlayType('application/x-mpegURL').replace(/no/, '') || media.canPlayType('video/ogv').replace(/no/, ''); + } - return false; - } + return false; + } - if (item.MediaType === "Audio") { + if (item.MediaType === "Audio") { - var media = MediaPlayer.testableAudioElement; + var media = MediaPlayer.testableAudioElement; - if (media.canPlayType) { + if (media.canPlayType) { - return media.canPlayType('audio/mpeg').replace(/no/, '') || media.canPlayType('audio/webm').replace(/no/, '') || media.canPlayType('audio/aac').replace(/no/, '') || media.canPlayType('audio/ogg').replace(/no/, ''); - } + return media.canPlayType('audio/mpeg').replace(/no/, '') || media.canPlayType('audio/webm').replace(/no/, '') || media.canPlayType('audio/aac').replace(/no/, '') || media.canPlayType('audio/ogg').replace(/no/, ''); + } - return false; - } + return false; + } - return false; - }, + return false; + }, - play: function (items, startPosition) { + play: function (items, startPosition) { - if (MediaPlayer.isPlaying()) { - MediaPlayer.stop(); - } + if (MediaPlayer.isPlaying()) { + MediaPlayer.stop(); + } - var item = items[0]; + var item = items[0]; - var mediaElement; + var mediaElement; - if (item.MediaType === "Video") { + if (item.MediaType === "Video") { - mediaElement = MediaPlayer.playVideo(items, startPosition); - } + mediaElement = MediaPlayer.playVideo(items, startPosition); + } - else if (item.MediaType === "Audio") { + else if (item.MediaType === "Audio") { - mediaElement = MediaPlayer.playAudio(items); - } + mediaElement = MediaPlayer.playAudio(items); + } - if (!mediaElement) { - return; - } + if (!mediaElement) { + return; + } - MediaPlayer.mediaElement = mediaElement; + MediaPlayer.mediaElement = mediaElement; - var nowPlayingBar = $('#nowPlayingBar').show(); + var nowPlayingBar = $('#nowPlayingBar').show(); - if (items.length > 1) { - $('#previousTrackButton', nowPlayingBar)[0].disabled = false; - $('#nextTrackButton', nowPlayingBar)[0].disabled = false; - } else { - $('#previousTrackButton', nowPlayingBar)[0].disabled = true; - $('#nextTrackButton', nowPlayingBar)[0].disabled = true; - } + if (items.length > 1) { + $('#previousTrackButton', nowPlayingBar)[0].disabled = false; + $('#nextTrackButton', nowPlayingBar)[0].disabled = false; + } else { + $('#previousTrackButton', nowPlayingBar)[0].disabled = true; + $('#nextTrackButton', nowPlayingBar)[0].disabled = true; + } - //display image and title - var imageTags = item.ImageTags || {}; - var html = ''; + //display image and title + var imageTags = item.ImageTags || {}; + var html = ''; - var url = ""; + var url = ""; - if (item.BackdropImageTags && item.BackdropImageTags.length) { + if (item.BackdropImageTags && item.BackdropImageTags.length) { - url = ApiClient.getImageUrl(item.Id, { - type: "Backdrop", - height: 36, - tag: item.BackdropImageTags[0] - }); - } - else if (imageTags.Thumb) { + url = ApiClient.getImageUrl(item.Id, { + type: "Backdrop", + height: 36, + tag: item.BackdropImageTags[0] + }); + } + else if (imageTags.Thumb) { - url = ApiClient.getImageUrl(item.Id, { - type: "Thumb", - height: 36, - tag: item.ImageTags.Thumb - }); - } - else if (imageTags.Primary) { + url = ApiClient.getImageUrl(item.Id, { + type: "Thumb", + height: 36, + tag: item.ImageTags.Thumb + }); + } + else if (imageTags.Primary) { - url = ApiClient.getImageUrl(item.Id, { - type: "Primary", - height: 36, - tag: item.ImageTags.Primary - }); - } else { - url = "css/images/items/detail/video.png"; - } + url = ApiClient.getImageUrl(item.Id, { + type: "Primary", + height: 36, + tag: item.ImageTags.Primary + }); + } else { + url = "css/images/items/detail/video.png"; + } - var name = item.Name; - var series_name = ''; + var name = item.Name; + var series_name = ''; - if (item.IndexNumber != null) { - name = item.IndexNumber + " - " + name; - } - if (item.ParentIndexNumber != null) { - name = item.ParentIndexNumber + "." + name; - } - if (item.SeriesName || item.Album || item.ProductionYear) { - series_name = item.SeriesName || item.Album || item.ProductionYear; - } + if (item.IndexNumber != null) { + name = item.IndexNumber + " - " + name; + } + if (item.ParentIndexNumber != null) { + name = item.ParentIndexNumber + "." + name; + } + if (item.SeriesName || item.Album || item.ProductionYear) { + series_name = item.SeriesName || item.Album || item.ProductionYear; + } - html += "
"; - if (item.Type == "Movie") - html += '
' + name + '
' + series_name + '
'; - else - html += '
' + series_name + '
' + name + '
'; + html += "
"; + if (item.Type == "Movie") + html += '
' + name + '
' + series_name + '
'; + else + html += '
' + series_name + '
' + name + '
'; - $('#mediaInfo', nowPlayingBar).html(html); - }, + $('#mediaInfo', nowPlayingBar).html(html); + }, - playAudio: function (items, params) { - var item = items[0]; + playAudio: function (items, params) { + var item = items[0]; - var baseParams = { - audioChannels: 2, - audioBitrate: 128000 - }; + var baseParams = { + audioChannels: 2, + audioBitrate: 128000 + }; - $.extend(baseParams, params); + $.extend(baseParams, params); - var mp3Url = ApiClient.getUrl('Audio/' + item.Id + '/stream.mp3', $.extend({}, baseParams, { - audioCodec: 'mp3' - })); + var mp3Url = ApiClient.getUrl('Audio/' + item.Id + '/stream.mp3', $.extend({}, baseParams, { + audioCodec: 'mp3' + })); - var aacUrl = ApiClient.getUrl('Audio/' + item.Id + '/stream.aac', $.extend({}, baseParams, { - audioCodec: 'aac' - })); + var aacUrl = ApiClient.getUrl('Audio/' + item.Id + '/stream.aac', $.extend({}, baseParams, { + audioCodec: 'aac' + })); - var webmUrl = ApiClient.getUrl('Audio/' + item.Id + '/stream.webm', $.extend({}, baseParams, { - audioCodec: 'Vorbis' - })); + var webmUrl = ApiClient.getUrl('Audio/' + item.Id + '/stream.webm', $.extend({}, baseParams, { + audioCodec: 'Vorbis' + })); - /* ffmpeg always says the ogg stream is corrupt after conversion - var oggUrl = ApiClient.getUrl('Audio/' + item.Id + '/stream.oga', $.extend({}, baseParams, { - audioCodec: 'Vorbis' - })); - */ + /* ffmpeg always says the ogg stream is corrupt after conversion + var oggUrl = ApiClient.getUrl('Audio/' + item.Id + '/stream.oga', $.extend({}, baseParams, { + audioCodec: 'Vorbis' + })); + */ - var html = ''; - html += '