Browse Source

rework media versions to be based on original item id

Luke Pulverenti 11 năm trước cách đây
mục cha
commit
327af0fe62
30 tập tin đã thay đổi với 325 bổ sung133 xóa
  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>