Переглянути джерело

rework media versions to be based on original item id

Luke Pulverenti 11 роки тому
батько
коміт
327af0fe62
30 змінених файлів з 325 додано та 133 видалено
  1. 3 1
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  2. 3 0
      MediaBrowser.Api/Playback/StreamRequest.cs
  3. 15 3
      MediaBrowser.Api/UserLibrary/UserLibraryService.cs
  4. 2 4
      MediaBrowser.Api/UserLibrary/YearsService.cs
  5. 3 6
      MediaBrowser.Api/VideosService.cs
  6. 0 7
      MediaBrowser.Controller/Dto/IDtoService.cs
  7. 1 0
      MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs
  8. 6 0
      MediaBrowser.Controller/Session/PlaybackInfo.cs
  9. 6 0
      MediaBrowser.Controller/Session/PlaybackProgressInfo.cs
  10. 6 0
      MediaBrowser.Controller/Session/PlaybackStopInfo.cs
  11. 25 1
      MediaBrowser.Controller/Session/SessionInfo.cs
  12. 5 5
      MediaBrowser.Dlna/PlayTo/DlnaController.cs
  13. 3 0
      MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
  14. 3 0
      MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
  15. 6 15
      MediaBrowser.Model/ApiClient/IApiClient.cs
  16. 2 2
      MediaBrowser.Model/Configuration/ServerConfiguration.cs
  17. 1 3
      MediaBrowser.Model/Dto/MediaVersionInfo.cs
  18. 6 0
      MediaBrowser.Model/Entities/BaseItemInfo.cs
  19. 1 0
      MediaBrowser.Model/MediaBrowser.Model.csproj
  20. 56 0
      MediaBrowser.Model/Session/PlaybackReports.cs
  21. 4 0
      MediaBrowser.Model/Session/SessionCapabilities.cs
  22. 14 2
      MediaBrowser.Model/Session/SessionInfoDto.cs
  23. 47 46
      MediaBrowser.Server.Implementations/Dto/DtoService.cs
  24. 2 2
      MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
  25. 5 0
      MediaBrowser.Server.Implementations/Roku/RokuControllerFactory.cs
  26. 48 8
      MediaBrowser.Server.Implementations/Session/SessionManager.cs
  27. 15 0
      MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs
  28. 35 24
      MediaBrowser.WebDashboard/ApiClient.js
  29. 1 3
      MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
  30. 1 1
      MediaBrowser.WebDashboard/packages.config

+ 3 - 1
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -1308,7 +1308,9 @@ namespace MediaBrowser.Api.Playback
                 RequestedUrl = url
             };
 
-            var item = DtoService.GetItemByDtoId(request.Id);
+            var item = string.IsNullOrEmpty(request.MediaVersionId) ?
+                DtoService.GetItemByDtoId(request.Id) :
+                DtoService.GetItemByDtoId(request.MediaVersionId);
 
             if (user != null && item.GetPlayAccess(user) != PlayAccess.Full)
             {

+ 3 - 0
MediaBrowser.Api/Playback/StreamRequest.cs

@@ -15,6 +15,9 @@ namespace MediaBrowser.Api.Playback
         [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
         public string Id { get; set; }
 
+        [ApiMember(Name = "MediaVersionId", Description = "The media version id, if playing an alternate version", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+        public string MediaVersionId { get; set; }
+        
         [ApiMember(Name = "DeviceId", Description = "The device id of the client requesting. Used to stop encoding processes when needed.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string DeviceId { get; set; }
 

+ 15 - 3
MediaBrowser.Api/UserLibrary/UserLibraryService.cs

@@ -241,6 +241,9 @@ namespace MediaBrowser.Api.UserLibrary
         [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
         public string Id { get; set; }
 
+        [ApiMember(Name = "MediaVersionId", Description = "The id of the MediaVersion", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
+        public string MediaVersionId { get; set; }
+
         /// <summary>
         /// Gets or sets a value indicating whether this <see cref="UpdateUserItemRating" /> is likes.
         /// </summary>
@@ -277,6 +280,9 @@ namespace MediaBrowser.Api.UserLibrary
         [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
         public string Id { get; set; }
 
+        [ApiMember(Name = "MediaVersionId", Description = "The id of the MediaVersion", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
+        public string MediaVersionId { get; set; }
+        
         /// <summary>
         /// Gets or sets the position ticks.
         /// </summary>
@@ -312,6 +318,9 @@ namespace MediaBrowser.Api.UserLibrary
         [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
         public string Id { get; set; }
 
+        [ApiMember(Name = "MediaVersionId", Description = "The id of the MediaVersion", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
+        public string MediaVersionId { get; set; }
+        
         /// <summary>
         /// Gets or sets the position ticks.
         /// </summary>
@@ -736,7 +745,8 @@ namespace MediaBrowser.Api.UserLibrary
                 CanSeek = request.CanSeek,
                 Item = item,
                 SessionId = GetSession().Id,
-                QueueableMediaTypes = queueableMediaTypes.Split(',').ToList()
+                QueueableMediaTypes = queueableMediaTypes.Split(',').ToList(),
+                MediaVersionId = request.MediaVersionId
             };
 
             _sessionManager.OnPlaybackStart(info);
@@ -758,7 +768,8 @@ namespace MediaBrowser.Api.UserLibrary
                 PositionTicks = request.PositionTicks,
                 IsMuted = request.IsMuted,
                 IsPaused = request.IsPaused,
-                SessionId = GetSession().Id
+                SessionId = GetSession().Id,
+                MediaVersionId = request.MediaVersionId
             };
 
             var task = _sessionManager.OnPlaybackProgress(info);
@@ -782,7 +793,8 @@ namespace MediaBrowser.Api.UserLibrary
             {
                 Item = item,
                 PositionTicks = request.PositionTicks,
-                SessionId = session.Id
+                SessionId = session.Id,
+                MediaVersionId = request.MediaVersionId
             };
 
             var task = _sessionManager.OnPlaybackStopped(info);

+ 2 - 4
MediaBrowser.Api/UserLibrary/YearsService.cs

@@ -14,8 +14,7 @@ namespace MediaBrowser.Api.UserLibrary
     /// <summary>
     /// Class GetYears
     /// </summary>
-    [Route("/Years", "GET")]
-    [Api(Description = "Gets all years from a given item, folder, or the entire library")]
+    [Route("/Years", "GET", Summary = "Gets all years from a given item, folder, or the entire library")]
     public class GetYears : GetItemsByName
     {
     }
@@ -23,8 +22,7 @@ namespace MediaBrowser.Api.UserLibrary
     /// <summary>
     /// Class GetYear
     /// </summary>
-    [Route("/Years/{Year}", "GET")]
-    [Api(Description = "Gets a year")]
+    [Route("/Years/{Year}", "GET", Summary = "Gets a year")]
     public class GetYear : IReturn<BaseItemDto>
     {
         /// <summary>

+ 3 - 6
MediaBrowser.Api/VideosService.cs

@@ -13,8 +13,7 @@ using System.Threading.Tasks;
 
 namespace MediaBrowser.Api
 {
-    [Route("/Videos/{Id}/AdditionalParts", "GET")]
-    [Api(Description = "Gets additional parts for a video.")]
+    [Route("/Videos/{Id}/AdditionalParts", "GET", Summary = "Gets additional parts for a video.")]
     public class GetAdditionalParts : IReturn<ItemsResult>
     {
         [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
@@ -28,16 +27,14 @@ namespace MediaBrowser.Api
         public string Id { get; set; }
     }
 
-    [Route("/Videos/{Id}/AlternateVersions", "DELETE")]
-    [Api(Description = "Assigns videos as alternates of antoher.")]
+    [Route("/Videos/{Id}/AlternateVersions", "DELETE", Summary = "Assigns videos as alternates of another.")]
     public class DeleteAlternateVersions : IReturnVoid
     {
         [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
         public string Id { get; set; }
     }
 
-    [Route("/Videos/MergeVersions", "POST")]
-    [Api(Description = "Merges videos into a single record")]
+    [Route("/Videos/MergeVersions", "POST", Summary = "Merges videos into a single record")]
     public class MergeVersions : IReturnVoid
     {
         [ApiMember(Name = "Ids", Description = "Item id list. This allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)]

+ 0 - 7
MediaBrowser.Controller/Dto/IDtoService.cs

@@ -28,13 +28,6 @@ namespace MediaBrowser.Controller.Dto
         /// <returns>SessionInfoDto.</returns>
         SessionInfoDto GetSessionInfoDto(SessionInfo session);
 
-        /// <summary>
-        /// Gets the base item info.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <returns>BaseItemInfo.</returns>
-        BaseItemInfo GetBaseItemInfo(BaseItem item);
-
         /// <summary>
         /// Gets the dto id.
         /// </summary>

+ 1 - 0
MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs

@@ -12,6 +12,7 @@ namespace MediaBrowser.Controller.Library
         public List<User> Users { get; set; }
         public long? PlaybackPositionTicks { get; set; }
         public BaseItem Item { get; set; }
+        public string MediaVersionId { get; set; }
 
         public PlaybackProgressEventArgs()
         {

+ 6 - 0
MediaBrowser.Controller/Session/PlaybackInfo.cs

@@ -34,5 +34,11 @@ namespace MediaBrowser.Controller.Session
         /// </summary>
         /// <value>The session id.</value>
         public Guid SessionId { get; set; }
+
+        /// <summary>
+        /// Gets or sets the media version identifier.
+        /// </summary>
+        /// <value>The media version identifier.</value>
+        public string MediaVersionId { get; set; }
     }
 }

+ 6 - 0
MediaBrowser.Controller/Session/PlaybackProgressInfo.cs

@@ -34,5 +34,11 @@ namespace MediaBrowser.Controller.Session
         /// </summary>
         /// <value>The position ticks.</value>
         public long? PositionTicks { get; set; }
+
+        /// <summary>
+        /// Gets or sets the media version identifier.
+        /// </summary>
+        /// <value>The media version identifier.</value>
+        public string MediaVersionId { get; set; }
     }
 }

+ 6 - 0
MediaBrowser.Controller/Session/PlaybackStopInfo.cs

@@ -22,5 +22,11 @@ namespace MediaBrowser.Controller.Session
         /// </summary>
         /// <value>The position ticks.</value>
         public long? PositionTicks { get; set; }
+
+        /// <summary>
+        /// Gets or sets the media version identifier.
+        /// </summary>
+        /// <value>The media version identifier.</value>
+        public string MediaVersionId { get; set; }
     }
 }

+ 25 - 1
MediaBrowser.Controller/Session/SessionInfo.cs

@@ -119,12 +119,24 @@ namespace MediaBrowser.Controller.Session
         /// <value>The now playing item.</value>
         public BaseItem NowPlayingItem { get; set; }
 
+        /// <summary>
+        /// Gets or sets the now playing media version identifier.
+        /// </summary>
+        /// <value>The now playing media version identifier.</value>
+        public string NowPlayingMediaVersionId { get; set; }
+        
+
+        /// <summary>
+        /// Gets or sets the now playing run time ticks.
+        /// </summary>
+        /// <value>The now playing run time ticks.</value>
+        public long? NowPlayingRunTimeTicks { get; set; }
+
         /// <summary>
         /// Gets or sets the now playing position ticks.
         /// </summary>
         /// <value>The now playing position ticks.</value>
         public long? NowPlayingPositionTicks { get; set; }
-
         /// <summary>
         /// Gets or sets a value indicating whether this instance is paused.
         /// </summary>
@@ -161,6 +173,18 @@ namespace MediaBrowser.Controller.Session
         /// <value><c>true</c> if [supports fullscreen toggle]; otherwise, <c>false</c>.</value>
         public bool SupportsFullscreenToggle { get; set; }
 
+        /// <summary>
+        /// Gets or sets a value indicating whether [supports osd toggle].
+        /// </summary>
+        /// <value><c>true</c> if [supports osd toggle]; otherwise, <c>false</c>.</value>
+        public bool SupportsOsdToggle { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether [supports navigation commands].
+        /// </summary>
+        /// <value><c>true</c> if [supports navigation commands]; otherwise, <c>false</c>.</value>
+        public bool SupportsNavigationControl { get; set; }
+        
         /// <summary>
         /// Gets a value indicating whether this instance is active.
         /// </summary>

+ 5 - 5
MediaBrowser.Dlna/PlayTo/DlnaController.cs

@@ -69,12 +69,12 @@ namespace MediaBrowser.Dlna.PlayTo
             _device.CurrentIdChanged += Device_CurrentIdChanged;
             _device.Start();
 
-            _updateTimer = new System.Threading.Timer(updateTimer_Elapsed, null, UpdateTimerIntervalMs, UpdateTimerIntervalMs);
+            _updateTimer = new Timer(updateTimer_Elapsed, null, UpdateTimerIntervalMs, UpdateTimerIntervalMs);
         }
 
         #region Device EventHandlers & Update Timer
 
-        System.Threading.Timer _updateTimer;
+        Timer _updateTimer;
 
         async void Device_PlaybackChanged(object sender, TransportStateEventArgs e)
         {
@@ -88,7 +88,7 @@ namespace MediaBrowser.Dlna.PlayTo
             {
                 _playbackStarted = false;
 
-                await _sessionManager.OnPlaybackStopped(new PlaybackStopInfo
+                await _sessionManager.OnPlaybackStopped(new Controller.Session.PlaybackStopInfo
                 {
                     Item = _currentItem,
                     SessionId = _session.Id,
@@ -164,7 +164,7 @@ namespace MediaBrowser.Dlna.PlayTo
                 var playlistItem = Playlist.FirstOrDefault(p => p.PlayState == 1);
                 if (playlistItem != null && playlistItem.Transcode)
                 {
-                    await _sessionManager.OnPlaybackProgress(new PlaybackProgressInfo
+                    await _sessionManager.OnPlaybackProgress(new Controller.Session.PlaybackProgressInfo
                     {
                         Item = _currentItem,
                         SessionId = _session.Id,
@@ -176,7 +176,7 @@ namespace MediaBrowser.Dlna.PlayTo
                 }
                 else if (_currentItem != null)
                 {
-                    await _sessionManager.OnPlaybackProgress(new PlaybackProgressInfo
+                    await _sessionManager.OnPlaybackProgress(new Controller.Session.PlaybackProgressInfo
                     {
                         Item = _currentItem,
                         SessionId = _session.Id,

+ 3 - 0
MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj

@@ -428,6 +428,9 @@
     <Compile Include="..\MediaBrowser.Model\Session\MessageCommand.cs">
       <Link>Session\MessageCommand.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Session\PlaybackReports.cs">
+      <Link>Session\PlaybackReports.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Session\PlayRequest.cs">
       <Link>Session\PlayRequest.cs</Link>
     </Compile>

+ 3 - 0
MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj

@@ -415,6 +415,9 @@
     <Compile Include="..\MediaBrowser.Model\Session\MessageCommand.cs">
       <Link>Session\MessageCommand.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Session\PlaybackReports.cs">
+      <Link>Session\PlaybackReports.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Session\PlayRequest.cs">
       <Link>Session\PlayRequest.cs</Link>
     </Compile>

+ 6 - 15
MediaBrowser.Model/ApiClient/IApiClient.cs

@@ -538,35 +538,26 @@ namespace MediaBrowser.Model.ApiClient
         /// <summary>
         /// Reports to the server that the user has begun playing an item
         /// </summary>
-        /// <param name="itemId">The item id.</param>
-        /// <param name="userId">The user id.</param>
-        /// <param name="isSeekable">if set to <c>true</c> [is seekable].</param>
-        /// <param name="queueableMediaTypes">The list of media types that the client is capable of queuing onto the playlist. See MediaType class.</param>
+        /// <param name="info">The information.</param>
         /// <returns>Task{UserItemDataDto}.</returns>
         /// <exception cref="ArgumentNullException">itemId</exception>
-        Task ReportPlaybackStartAsync(string itemId, string userId, bool isSeekable, List<string> queueableMediaTypes);
+        Task ReportPlaybackStartAsync(PlaybackStartInfo info);
 
         /// <summary>
         /// Reports playback progress to the server
         /// </summary>
-        /// <param name="itemId">The item id.</param>
-        /// <param name="userId">The user id.</param>
-        /// <param name="positionTicks">The position ticks.</param>
-        /// <param name="isPaused">if set to <c>true</c> [is paused].</param>
-        /// <param name="isMuted">if set to <c>true</c> [is muted].</param>
+        /// <param name="info">The information.</param>
         /// <returns>Task{UserItemDataDto}.</returns>
         /// <exception cref="ArgumentNullException">itemId</exception>
-        Task ReportPlaybackProgressAsync(string itemId, string userId, long? positionTicks, bool isPaused, bool isMuted);
+        Task ReportPlaybackProgressAsync(PlaybackProgressInfo info);
 
         /// <summary>
         /// Reports to the server that the user has stopped playing an item
         /// </summary>
-        /// <param name="itemId">The item id.</param>
-        /// <param name="userId">The user id.</param>
-        /// <param name="positionTicks">The position ticks.</param>
+        /// <param name="info">The information.</param>
         /// <returns>Task{UserItemDataDto}.</returns>
         /// <exception cref="ArgumentNullException">itemId</exception>
-        Task ReportPlaybackStoppedAsync(string itemId, string userId, long? positionTicks);
+        Task ReportPlaybackStoppedAsync(PlaybackStopInfo info);
 
         /// <summary>
         /// Instructs antoher client to browse to a library item.

+ 2 - 2
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -147,7 +147,7 @@ namespace MediaBrowser.Model.Configuration
         /// different directories and files.
         /// </summary>
         /// <value>The file watcher delay.</value>
-        public int RealtimeWatcherDelay { get; set; }
+        public int RealtimeMonitorDelay { get; set; }
 
         /// <summary>
         /// Gets or sets a value indicating whether [enable dashboard response caching].
@@ -239,7 +239,7 @@ namespace MediaBrowser.Model.Configuration
             MaxResumePct = 90;
             MinResumeDurationSeconds = Convert.ToInt32(TimeSpan.FromMinutes(5).TotalSeconds);
 
-            RealtimeWatcherDelay = 20;
+            RealtimeMonitorDelay = 30;
 
             RecentItemDays = 10;
 

+ 1 - 3
MediaBrowser.Model/Dto/MediaVersionInfo.cs

@@ -5,7 +5,7 @@ namespace MediaBrowser.Model.Dto
 {
     public class MediaVersionInfo
     {
-        public string ItemId { get; set; }
+        public string Id { get; set; }
 
         public string Path { get; set; }
 
@@ -23,8 +23,6 @@ namespace MediaBrowser.Model.Dto
         
         public List<MediaStream> MediaStreams { get; set; }
 
-        public List<ChapterInfoDto> Chapters { get; set; }
-
         public bool IsPrimaryVersion { get; set; }
     }
 }

+ 6 - 0
MediaBrowser.Model/Entities/BaseItemInfo.cs

@@ -69,6 +69,12 @@ namespace MediaBrowser.Model.Entities
         /// </summary>
         /// <value>The thumb item identifier.</value>
         public string BackdropItemId { get; set; }
+
+        /// <summary>
+        /// Gets or sets the media version identifier.
+        /// </summary>
+        /// <value>The media version identifier.</value>
+        public string MediaVersionId { get; set; }
         
         /// <summary>
         /// Gets a value indicating whether this instance has primary image.

+ 1 - 0
MediaBrowser.Model/MediaBrowser.Model.csproj

@@ -132,6 +132,7 @@
     <Compile Include="Search\SearchQuery.cs" />
     <Compile Include="Session\BrowseRequest.cs" />
     <Compile Include="Session\MessageCommand.cs" />
+    <Compile Include="Session\PlaybackReports.cs" />
     <Compile Include="Session\PlayRequest.cs" />
     <Compile Include="Session\PlaystateCommand.cs" />
     <Compile Include="Logging\ILogManager.cs" />

+ 56 - 0
MediaBrowser.Model/Session/PlaybackReports.cs

@@ -0,0 +1,56 @@
+
+namespace MediaBrowser.Model.Session
+{
+    /// <summary>
+    /// Class PlaybackStartInfo.
+    /// </summary>
+    public class PlaybackStartInfo
+    {
+        public string UserId { get; set; }
+
+        public string ItemId { get; set; }
+
+        public string MediaVersionId { get; set; }
+
+        public bool IsSeekable { get; set; }
+
+        public string[] QueueableMediaTypes { get; set; }
+
+        public PlaybackStartInfo()
+        {
+            QueueableMediaTypes = new string[] { };
+        }
+    }
+
+    /// <summary>
+    /// Class PlaybackProgressInfo.
+    /// </summary>
+    public class PlaybackProgressInfo
+    {
+        public string UserId { get; set; }
+
+        public string ItemId { get; set; }
+
+        public string MediaVersionId { get; set; }
+
+        public long? PositionTicks { get; set; }
+
+        public bool IsPaused { get; set; }
+
+        public bool IsMuted { get; set; }
+    }
+
+    /// <summary>
+    /// Class PlaybackStopInfo.
+    /// </summary>
+    public class PlaybackStopInfo
+    {
+        public string UserId { get; set; }
+
+        public string ItemId { get; set; }
+
+        public string MediaVersionId { get; set; }
+
+        public long? PositionTicks { get; set; }
+    }
+}

+ 4 - 0
MediaBrowser.Model/Session/SessionCapabilities.cs

@@ -7,6 +7,10 @@ namespace MediaBrowser.Model.Session
 
         public bool SupportsFullscreenToggle { get; set; }
 
+        public bool SupportsOsdToggle { get; set; }
+
+        public bool SupportsNavigationControl { get; set; }
+        
         public SessionCapabilities()
         {
             PlayableMediaTypes = new string[] {};

+ 14 - 2
MediaBrowser.Model/Session/SessionInfoDto.cs

@@ -1,8 +1,8 @@
-using System.Diagnostics;
-using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Entities;
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
+using System.Diagnostics;
 
 namespace MediaBrowser.Model.Session
 {
@@ -147,6 +147,18 @@ namespace MediaBrowser.Model.Session
         /// <value><c>true</c> if [supports remote control]; otherwise, <c>false</c>.</value>
         public bool SupportsRemoteControl { get; set; }
 
+        /// <summary>
+        /// Gets or sets a value indicating whether [supports osd toggle].
+        /// </summary>
+        /// <value><c>true</c> if [supports osd toggle]; otherwise, <c>false</c>.</value>
+        public bool SupportsOsdToggle { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether [supports navigation commands].
+        /// </summary>
+        /// <value><c>true</c> if [supports navigation commands]; otherwise, <c>false</c>.</value>
+        public bool SupportsNavigationControl { get; set; }
+        
         public event PropertyChangedEventHandler PropertyChanged;
 
         public SessionInfoDto()

+ 47 - 46
MediaBrowser.Server.Implementations/Dto/DtoService.cs

@@ -267,12 +267,14 @@ namespace MediaBrowser.Server.Implementations.Dto
                 PlayableMediaTypes = session.PlayableMediaTypes,
                 RemoteEndPoint = session.RemoteEndPoint,
                 AdditionalUsers = session.AdditionalUsers,
-                SupportsFullscreenToggle = session.SupportsFullscreenToggle
+                SupportsFullscreenToggle = session.SupportsFullscreenToggle,
+                SupportsNavigationControl = session.SupportsNavigationControl,
+                SupportsOsdToggle = session.SupportsOsdToggle
             };
 
             if (session.NowPlayingItem != null)
             {
-                dto.NowPlayingItem = GetBaseItemInfo(session.NowPlayingItem);
+                dto.NowPlayingItem = GetNowPlayingInfo(session.NowPlayingItem, session.NowPlayingMediaVersionId, session.NowPlayingRunTimeTicks);
             }
 
             if (session.UserId.HasValue)
@@ -288,9 +290,11 @@ namespace MediaBrowser.Server.Implementations.Dto
         /// Converts a BaseItem to a BaseItemInfo
         /// </summary>
         /// <param name="item">The item.</param>
+        /// <param name="mediaVersionId">The media version identifier.</param>
+        /// <param name="nowPlayingRuntimeTicks">The now playing runtime ticks.</param>
         /// <returns>BaseItemInfo.</returns>
         /// <exception cref="System.ArgumentNullException">item</exception>
-        public BaseItemInfo GetBaseItemInfo(BaseItem item)
+        private BaseItemInfo GetNowPlayingInfo(BaseItem item, string mediaVersionId, long? nowPlayingRuntimeTicks)
         {
             if (item == null)
             {
@@ -303,7 +307,8 @@ namespace MediaBrowser.Server.Implementations.Dto
                 Name = item.Name,
                 MediaType = item.MediaType,
                 Type = item.GetClientTypeName(),
-                RunTimeTicks = item.RunTimeTicks
+                RunTimeTicks = nowPlayingRuntimeTicks,
+                MediaVersionId = mediaVersionId
             };
 
             info.PrimaryImageTag = GetImageCacheTag(item, ImageType.Primary);
@@ -1103,9 +1108,7 @@ namespace MediaBrowser.Server.Implementations.Dto
 
                     if (dto.MediaVersions != null && dto.MediaVersions.Count > 0)
                     {
-                        chapters = dto.MediaVersions.Where(i => i.IsPrimaryVersion)
-                            .SelectMany(i => i.Chapters)
-                            .ToList();
+                        chapters = _itemRepo.GetChapters(item.Id).Select(c => GetChapterInfoDto(c, item)).ToList();
                     }
                     else
                     {
@@ -1292,24 +1295,25 @@ namespace MediaBrowser.Server.Implementations.Dto
 
         private List<MediaVersionInfo> GetMediaVersions(Audio item)
         {
-            var result = new List<MediaVersionInfo>();
-
-            result.Add(GetVersionInfo(item, true));
+            var result = new List<MediaVersionInfo>
+            {
+                GetVersionInfo(item, true)
+            };
 
             return result;
         }
 
         private MediaVersionInfo GetVersionInfo(Video i, bool isPrimary)
         {
+            var mediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery {ItemId = i.Id}).ToList();
+
             return new MediaVersionInfo
             {
-                Chapters = _itemRepo.GetChapters(i.Id).Select(c => GetChapterInfoDto(c, i)).ToList(),
-
-                ItemId = i.Id.ToString("N"),
+                Id = i.Id.ToString("N"),
                 IsoType = i.IsoType,
                 LocationType = i.LocationType,
-                MediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id }).ToList(),
-                Name = GetAlternateVersionName(i),
+                MediaStreams = mediaStreams,
+                Name = GetAlternateVersionName(i, mediaStreams),
                 Path = GetMappedPath(i),
                 RunTimeTicks = i.RunTimeTicks,
                 Video3DFormat = i.Video3DFormat,
@@ -1322,7 +1326,7 @@ namespace MediaBrowser.Server.Implementations.Dto
         {
             return new MediaVersionInfo
             {
-                ItemId = i.Id.ToString("N"),
+                Id = i.Id.ToString("N"),
                 LocationType = i.LocationType,
                 MediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id }).ToList(),
                 Name = i.Name,
@@ -1351,32 +1355,29 @@ namespace MediaBrowser.Server.Implementations.Dto
             return path;
         }
 
-        private string GetAlternateVersionName(Video video)
+        private string GetAlternateVersionName(Video video, List<MediaStream> mediaStreams)
         {
-            var name = "";
+            var terms = new List<string>();
 
-            var videoStream = video.GetDefaultVideoStream();
+            var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
+            var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
 
             if (video.Video3DFormat.HasValue)
             {
-                name = "3D " + name;
-                name = name.Trim();
+                terms.Add("3D");
             }
 
             if (video.VideoType == VideoType.BluRay)
             {
-                name = name + " " + "Bluray";
-                name = name.Trim();
+                terms.Add("Bluray");
             }
             else if (video.VideoType == VideoType.Dvd)
             {
-                name = name + " " + "DVD";
-                name = name.Trim();
+                terms.Add("DVD");
             }
             else if (video.VideoType == VideoType.HdDvd)
             {
-                name = name + " " + "HD-DVD";
-                name = name.Trim();
+                terms.Add("HD-DVD");
             }
             else if (video.VideoType == VideoType.Iso)
             {
@@ -1384,18 +1385,17 @@ namespace MediaBrowser.Server.Implementations.Dto
                 {
                     if (video.IsoType.Value == IsoType.BluRay)
                     {
-                        name = name + " " + "Bluray";
+                        terms.Add("Bluray");
                     }
                     else if (video.IsoType.Value == IsoType.Dvd)
                     {
-                        name = name + " " + "DVD";
+                        terms.Add("DVD");
                     }
                 }
                 else
                 {
-                    name = name + " " + "ISO";
+                    terms.Add("ISO");
                 }
-                name = name.Trim();
             }
 
             if (videoStream != null)
@@ -1404,44 +1404,45 @@ namespace MediaBrowser.Server.Implementations.Dto
                 {
                     if (videoStream.Width.Value >= 3800)
                     {
-                        name = name + " " + "4K";
-                        name = name.Trim();
+                        terms.Add("4K");
                     }
                     else if (videoStream.Width.Value >= 1900)
                     {
-                        name = name + " " + "1080P";
-                        name = name.Trim();
+                        terms.Add("1080P");
                     }
                     else if (videoStream.Width.Value >= 1270)
                     {
-                        name = name + " " + "720P";
-                        name = name.Trim();
+                        terms.Add("720P");
                     }
                     else if (videoStream.Width.Value >= 700)
                     {
-                        name = name + " " + "480p";
-                        name = name.Trim();
+                        terms.Add("480P");
                     }
                     else
                     {
-                        name = name + " " + "SD";
-                        name = name.Trim();
+                        terms.Add("SD");
                     }
                 }
             }
 
             if (videoStream != null && !string.IsNullOrWhiteSpace(videoStream.Codec))
             {
-                name = name + " " + videoStream.Codec.ToUpper();
-                name = name.Trim();
+                terms.Add(videoStream.Codec.ToUpper());
             }
 
-            if (string.IsNullOrWhiteSpace(name))
+            if (audioStream != null)
             {
-                return video.Name;
+                var audioCodec = string.Equals(audioStream.Codec, "dca", StringComparison.OrdinalIgnoreCase)
+                    ? audioStream.Profile
+                    : audioStream.Codec;
+
+                if (!string.IsNullOrEmpty(audioCodec))
+                {
+                    terms.Add(audioCodec.ToUpper());
+                }
             }
 
-            return name;
+            return string.Join("/", terms.ToArray());
         }
 
         private string GetMappedPath(string path)

+ 2 - 2
MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs

@@ -427,11 +427,11 @@ namespace MediaBrowser.Server.Implementations.IO
             {
                 if (_updateTimer == null)
                 {
-                    _updateTimer = new Timer(TimerStopped, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.RealtimeWatcherDelay), TimeSpan.FromMilliseconds(-1));
+                    _updateTimer = new Timer(TimerStopped, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.RealtimeMonitorDelay), TimeSpan.FromMilliseconds(-1));
                 }
                 else
                 {
-                    _updateTimer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.RealtimeWatcherDelay), TimeSpan.FromMilliseconds(-1));
+                    _updateTimer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.RealtimeMonitorDelay), TimeSpan.FromMilliseconds(-1));
                 }
             }
         }

+ 5 - 0
MediaBrowser.Server.Implementations/Roku/RokuControllerFactory.cs

@@ -1,8 +1,10 @@
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Serialization;
 using System;
+using System.Collections.Generic;
 
 namespace MediaBrowser.Server.Implementations.Roku
 {
@@ -23,6 +25,9 @@ namespace MediaBrowser.Server.Implementations.Roku
         {
             if (string.Equals(session.Client, "roku", StringComparison.OrdinalIgnoreCase))
             {
+                session.PlayableMediaTypes = new List<string> { MediaType.Video, MediaType.Audio };
+                session.SupportsFullscreenToggle = false;
+
                 return new RokuSessionController(_httpClient, _json, _appHost, session);
             }
 

+ 48 - 8
MediaBrowser.Server.Implementations/Session/SessionManager.cs

@@ -218,15 +218,29 @@ namespace MediaBrowser.Server.Implementations.Session
         /// </summary>
         /// <param name="session">The session.</param>
         /// <param name="item">The item.</param>
+        /// <param name="mediaVersionId">The media version identifier.</param>
         /// <param name="isPaused">if set to <c>true</c> [is paused].</param>
+        /// <param name="isMuted">if set to <c>true</c> [is muted].</param>
         /// <param name="currentPositionTicks">The current position ticks.</param>
-        private void UpdateNowPlayingItem(SessionInfo session, BaseItem item, bool isPaused, bool isMuted, long? currentPositionTicks = null)
+        private void UpdateNowPlayingItem(SessionInfo session, BaseItem item, string mediaVersionId, bool isPaused, bool isMuted, long? currentPositionTicks = null)
         {
             session.IsMuted = isMuted;
             session.IsPaused = isPaused;
             session.NowPlayingPositionTicks = currentPositionTicks;
             session.NowPlayingItem = item;
             session.LastActivityDate = DateTime.UtcNow;
+            session.NowPlayingMediaVersionId = mediaVersionId;
+
+            if (string.IsNullOrWhiteSpace(mediaVersionId))
+            {
+                session.NowPlayingRunTimeTicks = item.RunTimeTicks;
+            }
+            else
+            {
+                var version = _libraryManager.GetItemById(new Guid(mediaVersionId));
+
+                session.NowPlayingRunTimeTicks = version.RunTimeTicks;
+            }
         }
 
         /// <summary>
@@ -246,6 +260,8 @@ namespace MediaBrowser.Server.Implementations.Session
                 session.NowPlayingItem = null;
                 session.NowPlayingPositionTicks = null;
                 session.IsPaused = false;
+                session.NowPlayingRunTimeTicks = null;
+                session.NowPlayingMediaVersionId = null;
             }
         }
 
@@ -352,7 +368,9 @@ namespace MediaBrowser.Server.Implementations.Session
 
             var item = info.Item;
 
-            UpdateNowPlayingItem(session, item, false, false);
+            var mediaVersionId = GetMediaVersionId(item, info.MediaVersionId);
+
+            UpdateNowPlayingItem(session, item, mediaVersionId, false, false);
 
             session.CanSeek = info.CanSeek;
             session.QueueableMediaTypes = info.QueueableMediaTypes;
@@ -371,7 +389,8 @@ namespace MediaBrowser.Server.Implementations.Session
             EventHelper.QueueEventIfNotNull(PlaybackStart, this, new PlaybackProgressEventArgs
             {
                 Item = item,
-                Users = users
+                Users = users,
+                MediaVersionId = info.MediaVersionId
 
             }, _logger);
         }
@@ -405,7 +424,7 @@ namespace MediaBrowser.Server.Implementations.Session
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException"></exception>
         /// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception>
-        public async Task OnPlaybackProgress(PlaybackProgressInfo info)
+        public async Task OnPlaybackProgress(Controller.Session.PlaybackProgressInfo info)
         {
             if (info == null)
             {
@@ -419,7 +438,9 @@ namespace MediaBrowser.Server.Implementations.Session
 
             var session = Sessions.First(i => i.Id.Equals(info.SessionId));
 
-            UpdateNowPlayingItem(session, info.Item, info.IsPaused, info.IsMuted, info.PositionTicks);
+            var mediaVersionId = GetMediaVersionId(info.Item, info.MediaVersionId);
+
+            UpdateNowPlayingItem(session, info.Item, mediaVersionId, info.IsPaused, info.IsMuted, info.PositionTicks);
 
             var key = info.Item.GetUserDataKey();
 
@@ -434,7 +455,8 @@ namespace MediaBrowser.Server.Implementations.Session
             {
                 Item = info.Item,
                 Users = users,
-                PlaybackPositionTicks = info.PositionTicks
+                PlaybackPositionTicks = info.PositionTicks,
+                MediaVersionId = mediaVersionId
 
             }, _logger);
         }
@@ -458,7 +480,7 @@ namespace MediaBrowser.Server.Implementations.Session
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException">info</exception>
         /// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception>
-        public async Task OnPlaybackStopped(PlaybackStopInfo info)
+        public async Task OnPlaybackStopped(Controller.Session.PlaybackStopInfo info)
         {
             if (info == null)
             {
@@ -494,16 +516,32 @@ namespace MediaBrowser.Server.Implementations.Session
                 playedToCompletion = await OnPlaybackStopped(user.Id, key, info.Item, info.PositionTicks).ConfigureAwait(false);
             }
 
+            var mediaVersionId = GetMediaVersionId(info.Item, info.MediaVersionId);
+
             EventHelper.QueueEventIfNotNull(PlaybackStopped, this, new PlaybackStopEventArgs
             {
                 Item = info.Item,
                 Users = users,
                 PlaybackPositionTicks = info.PositionTicks,
-                PlayedToCompletion = playedToCompletion
+                PlayedToCompletion = playedToCompletion,
+                MediaVersionId = mediaVersionId
 
             }, _logger);
         }
 
+        private string GetMediaVersionId(BaseItem item, string reportedMediaVersionId)
+        {
+            if (string.IsNullOrWhiteSpace(reportedMediaVersionId))
+            {
+                if (item is Video || item is Audio)
+                {
+                    reportedMediaVersionId = item.Id.ToString("N");
+                }
+            }
+
+            return reportedMediaVersionId;
+        }
+
         private async Task<bool> OnPlaybackStopped(Guid userId, string userDataKey, BaseItem item, long? positionTicks)
         {
             var data = _userDataRepository.GetUserData(userId, userDataKey);
@@ -899,6 +937,8 @@ namespace MediaBrowser.Server.Implementations.Session
 
             session.PlayableMediaTypes = capabilities.PlayableMediaTypes.ToList();
             session.SupportsFullscreenToggle = capabilities.SupportsFullscreenToggle;
+            session.SupportsOsdToggle = capabilities.SupportsOsdToggle;
+            session.SupportsNavigationControl = capabilities.SupportsNavigationControl;
         }
     }
 }

+ 15 - 0
MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs

@@ -223,6 +223,11 @@ namespace MediaBrowser.Server.Implementations.Session
                     QueueableMediaTypes = queueableMediaTypes.Split(',').ToList()
                 };
 
+                if (vals.Length > 3)
+                {
+                    info.MediaVersionId = vals[3];
+                }
+
                 _sessionManager.OnPlaybackStart(info);
             }
         }
@@ -265,6 +270,11 @@ namespace MediaBrowser.Server.Implementations.Session
                     SessionId = session.Id
                 };
 
+                if (vals.Length > 4)
+                {
+                    info.MediaVersionId = vals[4];
+                }
+
                 _sessionManager.OnPlaybackProgress(info);
             }
         }
@@ -304,6 +314,11 @@ namespace MediaBrowser.Server.Implementations.Session
                     SessionId = session.Id
                 };
 
+                if (vals.Length > 2)
+                {
+                    info.MediaVersionId = vals[2];
+                }
+
                 _sessionManager.OnPlaybackStopped(info);
             }
         }

+ 35 - 24
MediaBrowser.WebDashboard/ApiClient.js

@@ -2191,20 +2191,6 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
             });
         };
 
-        /**
-         * Gets a list of all available conrete BaseItem types from the server
-         */
-        self.getItemTypes = function (options) {
-
-            var url = self.getUrl("Library/ItemTypes", options);
-
-            return self.ajax({
-                type: "GET",
-                url: url,
-                dataType: "json"
-            });
-        };
-
         /**
          * Constructs a url for a user image
          * @param {String} userId
@@ -3805,7 +3791,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
          * @param {String} userId
          * @param {String} itemId
          */
-        self.reportPlaybackStart = function (userId, itemId, canSeek, queueableMediaTypes) {
+        self.reportPlaybackStart = function (userId, itemId, mediaVersionId, canSeek, queueableMediaTypes) {
 
             if (!userId) {
                 throw new Error("null userId");
@@ -3823,6 +3809,10 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
                 var deferred = $.Deferred();
 
                 var msg = [itemId, canSeek, queueableMediaTypes];
+                
+                if (mediaVersionId) {
+                    msg.push(mediaVersionId);
+                }
 
                 self.sendWebSocketMessage("PlaybackStart", msg.join('|'));
 
@@ -3830,10 +3820,16 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
                 return deferred.promise();
             }
 
-            var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId, {
+            var params = {
                 CanSeek: canSeek,
                 QueueableMediaTypes: queueableMediaTypes
-            });
+            };
+
+            if (mediaVersionId) {
+                params.mediaVersionId = mediaVersionId;
+            }
+
+            var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId, params);
 
             return self.ajax({
                 type: "POST",
@@ -3846,7 +3842,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
          * @param {String} userId
          * @param {String} itemId
          */
-        self.reportPlaybackProgress = function (userId, itemId, positionTicks, isPaused, isMuted) {
+        self.reportPlaybackProgress = function (userId, itemId, mediaVersionId, positionTicks, isPaused, isMuted) {
 
             if (!userId) {
                 throw new Error("null userId");
@@ -3860,7 +3856,12 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
 
                 var deferred = $.Deferred();
 
-                var msgData = itemId + "|" + (positionTicks == null ? "" : positionTicks) + "|" + (isPaused == null ? "" : isPaused) + "|" + (isMuted == null ? "" : isMuted);
+                var msgData = itemId;
+
+                msgData += "|" + (positionTicks == null ? "" : positionTicks);
+                msgData += "|" + (isPaused == null ? "" : isPaused);
+                msgData += "|" + (isMuted == null ? "" : isMuted);
+                msgData += "|" + (mediaVersionId == null ? "" : mediaVersionId);
 
                 self.sendWebSocketMessage("PlaybackProgress", msgData);
 
@@ -3877,6 +3878,10 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
                 params.positionTicks = positionTicks;
             }
 
+            if (mediaVersionId) {
+                params.mediaVersionId = mediaVersionId;
+            }
+
             var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId + "/Progress", params);
 
             return self.ajax({
@@ -3890,7 +3895,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
          * @param {String} userId
          * @param {String} itemId
          */
-        self.reportPlaybackStopped = function (userId, itemId, positionTicks) {
+        self.reportPlaybackStopped = function (userId, itemId, mediaVersionId, positionTicks) {
 
             if (!userId) {
                 throw new Error("null userId");
@@ -3904,20 +3909,26 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
 
                 var deferred = $.Deferred();
 
-                self.sendWebSocketMessage("PlaybackStopped", itemId + "|" + (positionTicks == null ? "" : positionTicks));
+                var msg = itemId;
+                msg += "|" + (positionTicks == null ? "" : positionTicks);
+                msg += "|" + (mediaVersionId == null ? "" : mediaVersionId);
+
+                self.sendWebSocketMessage("PlaybackStopped", msg);
 
                 deferred.resolveWith(null, []);
                 return deferred.promise();
             }
 
-            var params = {
-
-            };
+            var params = {};
 
             if (positionTicks) {
                 params.positionTicks = positionTicks;
             }
 
+            if (mediaVersionId) {
+                params.mediaVersionId = mediaVersionId;
+            }
+
             var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId, params);
 
             return self.ajax({

+ 1 - 3
MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

@@ -84,9 +84,7 @@
     </ProjectReference>
   </ItemGroup>
   <ItemGroup>
-    <EmbeddedResource Include="ApiClient.js">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </EmbeddedResource>
+    <EmbeddedResource Include="ApiClient.js" />
     <Content Include="dashboard-ui\advancedserversettings.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>

+ 1 - 1
MediaBrowser.WebDashboard/packages.config

@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="MediaBrowser.ApiClient.Javascript" version="3.0.245" targetFramework="net45" />
+  <package id="MediaBrowser.ApiClient.Javascript" version="3.0.246" targetFramework="net45" />
 </packages>