Pārlūkot izejas kodu

rework media versions to be based on original item id

Luke Pulverenti 11 gadi atpakaļ
vecāks
revīzija
327af0fe62
30 mainītis faili ar 325 papildinājumiem un 133 dzēšanām
  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
                 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)
             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")]
         [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
         public string Id { get; set; }
         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")]
         [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; }
         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")]
         [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
         public string Id { get; set; }
         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>
         /// <summary>
         /// Gets or sets a value indicating whether this <see cref="UpdateUserItemRating" /> is likes.
         /// Gets or sets a value indicating whether this <see cref="UpdateUserItemRating" /> is likes.
         /// </summary>
         /// </summary>
@@ -277,6 +280,9 @@ namespace MediaBrowser.Api.UserLibrary
         [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
         [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
         public string Id { get; set; }
         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>
         /// <summary>
         /// Gets or sets the position ticks.
         /// Gets or sets the position ticks.
         /// </summary>
         /// </summary>
@@ -312,6 +318,9 @@ namespace MediaBrowser.Api.UserLibrary
         [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
         [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
         public string Id { get; set; }
         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>
         /// <summary>
         /// Gets or sets the position ticks.
         /// Gets or sets the position ticks.
         /// </summary>
         /// </summary>
@@ -736,7 +745,8 @@ namespace MediaBrowser.Api.UserLibrary
                 CanSeek = request.CanSeek,
                 CanSeek = request.CanSeek,
                 Item = item,
                 Item = item,
                 SessionId = GetSession().Id,
                 SessionId = GetSession().Id,
-                QueueableMediaTypes = queueableMediaTypes.Split(',').ToList()
+                QueueableMediaTypes = queueableMediaTypes.Split(',').ToList(),
+                MediaVersionId = request.MediaVersionId
             };
             };
 
 
             _sessionManager.OnPlaybackStart(info);
             _sessionManager.OnPlaybackStart(info);
@@ -758,7 +768,8 @@ namespace MediaBrowser.Api.UserLibrary
                 PositionTicks = request.PositionTicks,
                 PositionTicks = request.PositionTicks,
                 IsMuted = request.IsMuted,
                 IsMuted = request.IsMuted,
                 IsPaused = request.IsPaused,
                 IsPaused = request.IsPaused,
-                SessionId = GetSession().Id
+                SessionId = GetSession().Id,
+                MediaVersionId = request.MediaVersionId
             };
             };
 
 
             var task = _sessionManager.OnPlaybackProgress(info);
             var task = _sessionManager.OnPlaybackProgress(info);
@@ -782,7 +793,8 @@ namespace MediaBrowser.Api.UserLibrary
             {
             {
                 Item = item,
                 Item = item,
                 PositionTicks = request.PositionTicks,
                 PositionTicks = request.PositionTicks,
-                SessionId = session.Id
+                SessionId = session.Id,
+                MediaVersionId = request.MediaVersionId
             };
             };
 
 
             var task = _sessionManager.OnPlaybackStopped(info);
             var task = _sessionManager.OnPlaybackStopped(info);

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

@@ -14,8 +14,7 @@ namespace MediaBrowser.Api.UserLibrary
     /// <summary>
     /// <summary>
     /// Class GetYears
     /// Class GetYears
     /// </summary>
     /// </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
     public class GetYears : GetItemsByName
     {
     {
     }
     }
@@ -23,8 +22,7 @@ namespace MediaBrowser.Api.UserLibrary
     /// <summary>
     /// <summary>
     /// Class GetYear
     /// Class GetYear
     /// </summary>
     /// </summary>
-    [Route("/Years/{Year}", "GET")]
-    [Api(Description = "Gets a year")]
+    [Route("/Years/{Year}", "GET", Summary = "Gets a year")]
     public class GetYear : IReturn<BaseItemDto>
     public class GetYear : IReturn<BaseItemDto>
     {
     {
         /// <summary>
         /// <summary>

+ 3 - 6
MediaBrowser.Api/VideosService.cs

@@ -13,8 +13,7 @@ using System.Threading.Tasks;
 
 
 namespace MediaBrowser.Api
 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>
     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")]
         [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; }
         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
     public class DeleteAlternateVersions : IReturnVoid
     {
     {
         [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
         [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
         public string Id { get; set; }
         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
     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)]
         [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>
         /// <returns>SessionInfoDto.</returns>
         SessionInfoDto GetSessionInfoDto(SessionInfo session);
         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>
         /// <summary>
         /// Gets the dto id.
         /// Gets the dto id.
         /// </summary>
         /// </summary>

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

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

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

@@ -34,5 +34,11 @@ namespace MediaBrowser.Controller.Session
         /// </summary>
         /// </summary>
         /// <value>The session id.</value>
         /// <value>The session id.</value>
         public Guid SessionId { get; set; }
         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>
         /// </summary>
         /// <value>The position ticks.</value>
         /// <value>The position ticks.</value>
         public long? PositionTicks { get; set; }
         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>
         /// </summary>
         /// <value>The position ticks.</value>
         /// <value>The position ticks.</value>
         public long? PositionTicks { get; set; }
         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>
         /// <value>The now playing item.</value>
         public BaseItem NowPlayingItem { get; set; }
         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>
         /// <summary>
         /// Gets or sets the now playing position ticks.
         /// Gets or sets the now playing position ticks.
         /// </summary>
         /// </summary>
         /// <value>The now playing position ticks.</value>
         /// <value>The now playing position ticks.</value>
         public long? NowPlayingPositionTicks { get; set; }
         public long? NowPlayingPositionTicks { get; set; }
-
         /// <summary>
         /// <summary>
         /// Gets or sets a value indicating whether this instance is paused.
         /// Gets or sets a value indicating whether this instance is paused.
         /// </summary>
         /// </summary>
@@ -161,6 +173,18 @@ namespace MediaBrowser.Controller.Session
         /// <value><c>true</c> if [supports fullscreen toggle]; otherwise, <c>false</c>.</value>
         /// <value><c>true</c> if [supports fullscreen toggle]; otherwise, <c>false</c>.</value>
         public bool SupportsFullscreenToggle { get; set; }
         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>
         /// <summary>
         /// Gets a value indicating whether this instance is active.
         /// Gets a value indicating whether this instance is active.
         /// </summary>
         /// </summary>

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

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

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

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

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

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

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

@@ -538,35 +538,26 @@ namespace MediaBrowser.Model.ApiClient
         /// <summary>
         /// <summary>
         /// Reports to the server that the user has begun playing an item
         /// Reports to the server that the user has begun playing an item
         /// </summary>
         /// </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>
         /// <returns>Task{UserItemDataDto}.</returns>
         /// <exception cref="ArgumentNullException">itemId</exception>
         /// <exception cref="ArgumentNullException">itemId</exception>
-        Task ReportPlaybackStartAsync(string itemId, string userId, bool isSeekable, List<string> queueableMediaTypes);
+        Task ReportPlaybackStartAsync(PlaybackStartInfo info);
 
 
         /// <summary>
         /// <summary>
         /// Reports playback progress to the server
         /// Reports playback progress to the server
         /// </summary>
         /// </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>
         /// <returns>Task{UserItemDataDto}.</returns>
         /// <exception cref="ArgumentNullException">itemId</exception>
         /// <exception cref="ArgumentNullException">itemId</exception>
-        Task ReportPlaybackProgressAsync(string itemId, string userId, long? positionTicks, bool isPaused, bool isMuted);
+        Task ReportPlaybackProgressAsync(PlaybackProgressInfo info);
 
 
         /// <summary>
         /// <summary>
         /// Reports to the server that the user has stopped playing an item
         /// Reports to the server that the user has stopped playing an item
         /// </summary>
         /// </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>
         /// <returns>Task{UserItemDataDto}.</returns>
         /// <exception cref="ArgumentNullException">itemId</exception>
         /// <exception cref="ArgumentNullException">itemId</exception>
-        Task ReportPlaybackStoppedAsync(string itemId, string userId, long? positionTicks);
+        Task ReportPlaybackStoppedAsync(PlaybackStopInfo info);
 
 
         /// <summary>
         /// <summary>
         /// Instructs antoher client to browse to a library item.
         /// 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.
         /// different directories and files.
         /// </summary>
         /// </summary>
         /// <value>The file watcher delay.</value>
         /// <value>The file watcher delay.</value>
-        public int RealtimeWatcherDelay { get; set; }
+        public int RealtimeMonitorDelay { get; set; }
 
 
         /// <summary>
         /// <summary>
         /// Gets or sets a value indicating whether [enable dashboard response caching].
         /// Gets or sets a value indicating whether [enable dashboard response caching].
@@ -239,7 +239,7 @@ namespace MediaBrowser.Model.Configuration
             MaxResumePct = 90;
             MaxResumePct = 90;
             MinResumeDurationSeconds = Convert.ToInt32(TimeSpan.FromMinutes(5).TotalSeconds);
             MinResumeDurationSeconds = Convert.ToInt32(TimeSpan.FromMinutes(5).TotalSeconds);
 
 
-            RealtimeWatcherDelay = 20;
+            RealtimeMonitorDelay = 30;
 
 
             RecentItemDays = 10;
             RecentItemDays = 10;
 
 

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

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

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

@@ -69,6 +69,12 @@ namespace MediaBrowser.Model.Entities
         /// </summary>
         /// </summary>
         /// <value>The thumb item identifier.</value>
         /// <value>The thumb item identifier.</value>
         public string BackdropItemId { get; set; }
         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>
         /// <summary>
         /// Gets a value indicating whether this instance has primary image.
         /// 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="Search\SearchQuery.cs" />
     <Compile Include="Session\BrowseRequest.cs" />
     <Compile Include="Session\BrowseRequest.cs" />
     <Compile Include="Session\MessageCommand.cs" />
     <Compile Include="Session\MessageCommand.cs" />
+    <Compile Include="Session\PlaybackReports.cs" />
     <Compile Include="Session\PlayRequest.cs" />
     <Compile Include="Session\PlayRequest.cs" />
     <Compile Include="Session\PlaystateCommand.cs" />
     <Compile Include="Session\PlaystateCommand.cs" />
     <Compile Include="Logging\ILogManager.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 SupportsFullscreenToggle { get; set; }
 
 
+        public bool SupportsOsdToggle { get; set; }
+
+        public bool SupportsNavigationControl { get; set; }
+        
         public SessionCapabilities()
         public SessionCapabilities()
         {
         {
             PlayableMediaTypes = new string[] {};
             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;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.ComponentModel;
+using System.Diagnostics;
 
 
 namespace MediaBrowser.Model.Session
 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>
         /// <value><c>true</c> if [supports remote control]; otherwise, <c>false</c>.</value>
         public bool SupportsRemoteControl { get; set; }
         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 event PropertyChangedEventHandler PropertyChanged;
 
 
         public SessionInfoDto()
         public SessionInfoDto()

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

@@ -267,12 +267,14 @@ namespace MediaBrowser.Server.Implementations.Dto
                 PlayableMediaTypes = session.PlayableMediaTypes,
                 PlayableMediaTypes = session.PlayableMediaTypes,
                 RemoteEndPoint = session.RemoteEndPoint,
                 RemoteEndPoint = session.RemoteEndPoint,
                 AdditionalUsers = session.AdditionalUsers,
                 AdditionalUsers = session.AdditionalUsers,
-                SupportsFullscreenToggle = session.SupportsFullscreenToggle
+                SupportsFullscreenToggle = session.SupportsFullscreenToggle,
+                SupportsNavigationControl = session.SupportsNavigationControl,
+                SupportsOsdToggle = session.SupportsOsdToggle
             };
             };
 
 
             if (session.NowPlayingItem != null)
             if (session.NowPlayingItem != null)
             {
             {
-                dto.NowPlayingItem = GetBaseItemInfo(session.NowPlayingItem);
+                dto.NowPlayingItem = GetNowPlayingInfo(session.NowPlayingItem, session.NowPlayingMediaVersionId, session.NowPlayingRunTimeTicks);
             }
             }
 
 
             if (session.UserId.HasValue)
             if (session.UserId.HasValue)
@@ -288,9 +290,11 @@ namespace MediaBrowser.Server.Implementations.Dto
         /// Converts a BaseItem to a BaseItemInfo
         /// Converts a BaseItem to a BaseItemInfo
         /// </summary>
         /// </summary>
         /// <param name="item">The item.</param>
         /// <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>
         /// <returns>BaseItemInfo.</returns>
         /// <exception cref="System.ArgumentNullException">item</exception>
         /// <exception cref="System.ArgumentNullException">item</exception>
-        public BaseItemInfo GetBaseItemInfo(BaseItem item)
+        private BaseItemInfo GetNowPlayingInfo(BaseItem item, string mediaVersionId, long? nowPlayingRuntimeTicks)
         {
         {
             if (item == null)
             if (item == null)
             {
             {
@@ -303,7 +307,8 @@ namespace MediaBrowser.Server.Implementations.Dto
                 Name = item.Name,
                 Name = item.Name,
                 MediaType = item.MediaType,
                 MediaType = item.MediaType,
                 Type = item.GetClientTypeName(),
                 Type = item.GetClientTypeName(),
-                RunTimeTicks = item.RunTimeTicks
+                RunTimeTicks = nowPlayingRuntimeTicks,
+                MediaVersionId = mediaVersionId
             };
             };
 
 
             info.PrimaryImageTag = GetImageCacheTag(item, ImageType.Primary);
             info.PrimaryImageTag = GetImageCacheTag(item, ImageType.Primary);
@@ -1103,9 +1108,7 @@ namespace MediaBrowser.Server.Implementations.Dto
 
 
                     if (dto.MediaVersions != null && dto.MediaVersions.Count > 0)
                     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
                     else
                     {
                     {
@@ -1292,24 +1295,25 @@ namespace MediaBrowser.Server.Implementations.Dto
 
 
         private List<MediaVersionInfo> GetMediaVersions(Audio item)
         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;
             return result;
         }
         }
 
 
         private MediaVersionInfo GetVersionInfo(Video i, bool isPrimary)
         private MediaVersionInfo GetVersionInfo(Video i, bool isPrimary)
         {
         {
+            var mediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery {ItemId = i.Id}).ToList();
+
             return new MediaVersionInfo
             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,
                 IsoType = i.IsoType,
                 LocationType = i.LocationType,
                 LocationType = i.LocationType,
-                MediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id }).ToList(),
-                Name = GetAlternateVersionName(i),
+                MediaStreams = mediaStreams,
+                Name = GetAlternateVersionName(i, mediaStreams),
                 Path = GetMappedPath(i),
                 Path = GetMappedPath(i),
                 RunTimeTicks = i.RunTimeTicks,
                 RunTimeTicks = i.RunTimeTicks,
                 Video3DFormat = i.Video3DFormat,
                 Video3DFormat = i.Video3DFormat,
@@ -1322,7 +1326,7 @@ namespace MediaBrowser.Server.Implementations.Dto
         {
         {
             return new MediaVersionInfo
             return new MediaVersionInfo
             {
             {
-                ItemId = i.Id.ToString("N"),
+                Id = i.Id.ToString("N"),
                 LocationType = i.LocationType,
                 LocationType = i.LocationType,
                 MediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id }).ToList(),
                 MediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id }).ToList(),
                 Name = i.Name,
                 Name = i.Name,
@@ -1351,32 +1355,29 @@ namespace MediaBrowser.Server.Implementations.Dto
             return path;
             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)
             if (video.Video3DFormat.HasValue)
             {
             {
-                name = "3D " + name;
-                name = name.Trim();
+                terms.Add("3D");
             }
             }
 
 
             if (video.VideoType == VideoType.BluRay)
             if (video.VideoType == VideoType.BluRay)
             {
             {
-                name = name + " " + "Bluray";
-                name = name.Trim();
+                terms.Add("Bluray");
             }
             }
             else if (video.VideoType == VideoType.Dvd)
             else if (video.VideoType == VideoType.Dvd)
             {
             {
-                name = name + " " + "DVD";
-                name = name.Trim();
+                terms.Add("DVD");
             }
             }
             else if (video.VideoType == VideoType.HdDvd)
             else if (video.VideoType == VideoType.HdDvd)
             {
             {
-                name = name + " " + "HD-DVD";
-                name = name.Trim();
+                terms.Add("HD-DVD");
             }
             }
             else if (video.VideoType == VideoType.Iso)
             else if (video.VideoType == VideoType.Iso)
             {
             {
@@ -1384,18 +1385,17 @@ namespace MediaBrowser.Server.Implementations.Dto
                 {
                 {
                     if (video.IsoType.Value == IsoType.BluRay)
                     if (video.IsoType.Value == IsoType.BluRay)
                     {
                     {
-                        name = name + " " + "Bluray";
+                        terms.Add("Bluray");
                     }
                     }
                     else if (video.IsoType.Value == IsoType.Dvd)
                     else if (video.IsoType.Value == IsoType.Dvd)
                     {
                     {
-                        name = name + " " + "DVD";
+                        terms.Add("DVD");
                     }
                     }
                 }
                 }
                 else
                 else
                 {
                 {
-                    name = name + " " + "ISO";
+                    terms.Add("ISO");
                 }
                 }
-                name = name.Trim();
             }
             }
 
 
             if (videoStream != null)
             if (videoStream != null)
@@ -1404,44 +1404,45 @@ namespace MediaBrowser.Server.Implementations.Dto
                 {
                 {
                     if (videoStream.Width.Value >= 3800)
                     if (videoStream.Width.Value >= 3800)
                     {
                     {
-                        name = name + " " + "4K";
-                        name = name.Trim();
+                        terms.Add("4K");
                     }
                     }
                     else if (videoStream.Width.Value >= 1900)
                     else if (videoStream.Width.Value >= 1900)
                     {
                     {
-                        name = name + " " + "1080P";
-                        name = name.Trim();
+                        terms.Add("1080P");
                     }
                     }
                     else if (videoStream.Width.Value >= 1270)
                     else if (videoStream.Width.Value >= 1270)
                     {
                     {
-                        name = name + " " + "720P";
-                        name = name.Trim();
+                        terms.Add("720P");
                     }
                     }
                     else if (videoStream.Width.Value >= 700)
                     else if (videoStream.Width.Value >= 700)
                     {
                     {
-                        name = name + " " + "480p";
-                        name = name.Trim();
+                        terms.Add("480P");
                     }
                     }
                     else
                     else
                     {
                     {
-                        name = name + " " + "SD";
-                        name = name.Trim();
+                        terms.Add("SD");
                     }
                     }
                 }
                 }
             }
             }
 
 
             if (videoStream != null && !string.IsNullOrWhiteSpace(videoStream.Codec))
             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)
         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)
                 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
                 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.Common.Net;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Session;
 using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Serialization;
 using System;
 using System;
+using System.Collections.Generic;
 
 
 namespace MediaBrowser.Server.Implementations.Roku
 namespace MediaBrowser.Server.Implementations.Roku
 {
 {
@@ -23,6 +25,9 @@ namespace MediaBrowser.Server.Implementations.Roku
         {
         {
             if (string.Equals(session.Client, "roku", StringComparison.OrdinalIgnoreCase))
             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);
                 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>
         /// </summary>
         /// <param name="session">The session.</param>
         /// <param name="session">The session.</param>
         /// <param name="item">The item.</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="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>
         /// <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.IsMuted = isMuted;
             session.IsPaused = isPaused;
             session.IsPaused = isPaused;
             session.NowPlayingPositionTicks = currentPositionTicks;
             session.NowPlayingPositionTicks = currentPositionTicks;
             session.NowPlayingItem = item;
             session.NowPlayingItem = item;
             session.LastActivityDate = DateTime.UtcNow;
             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>
         /// <summary>
@@ -246,6 +260,8 @@ namespace MediaBrowser.Server.Implementations.Session
                 session.NowPlayingItem = null;
                 session.NowPlayingItem = null;
                 session.NowPlayingPositionTicks = null;
                 session.NowPlayingPositionTicks = null;
                 session.IsPaused = false;
                 session.IsPaused = false;
+                session.NowPlayingRunTimeTicks = null;
+                session.NowPlayingMediaVersionId = null;
             }
             }
         }
         }
 
 
@@ -352,7 +368,9 @@ namespace MediaBrowser.Server.Implementations.Session
 
 
             var item = info.Item;
             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.CanSeek = info.CanSeek;
             session.QueueableMediaTypes = info.QueueableMediaTypes;
             session.QueueableMediaTypes = info.QueueableMediaTypes;
@@ -371,7 +389,8 @@ namespace MediaBrowser.Server.Implementations.Session
             EventHelper.QueueEventIfNotNull(PlaybackStart, this, new PlaybackProgressEventArgs
             EventHelper.QueueEventIfNotNull(PlaybackStart, this, new PlaybackProgressEventArgs
             {
             {
                 Item = item,
                 Item = item,
-                Users = users
+                Users = users,
+                MediaVersionId = info.MediaVersionId
 
 
             }, _logger);
             }, _logger);
         }
         }
@@ -405,7 +424,7 @@ namespace MediaBrowser.Server.Implementations.Session
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException"></exception>
         /// <exception cref="System.ArgumentNullException"></exception>
         /// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception>
         /// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception>
-        public async Task OnPlaybackProgress(PlaybackProgressInfo info)
+        public async Task OnPlaybackProgress(Controller.Session.PlaybackProgressInfo info)
         {
         {
             if (info == null)
             if (info == null)
             {
             {
@@ -419,7 +438,9 @@ namespace MediaBrowser.Server.Implementations.Session
 
 
             var session = Sessions.First(i => i.Id.Equals(info.SessionId));
             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();
             var key = info.Item.GetUserDataKey();
 
 
@@ -434,7 +455,8 @@ namespace MediaBrowser.Server.Implementations.Session
             {
             {
                 Item = info.Item,
                 Item = info.Item,
                 Users = users,
                 Users = users,
-                PlaybackPositionTicks = info.PositionTicks
+                PlaybackPositionTicks = info.PositionTicks,
+                MediaVersionId = mediaVersionId
 
 
             }, _logger);
             }, _logger);
         }
         }
@@ -458,7 +480,7 @@ namespace MediaBrowser.Server.Implementations.Session
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException">info</exception>
         /// <exception cref="System.ArgumentNullException">info</exception>
         /// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception>
         /// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception>
-        public async Task OnPlaybackStopped(PlaybackStopInfo info)
+        public async Task OnPlaybackStopped(Controller.Session.PlaybackStopInfo info)
         {
         {
             if (info == null)
             if (info == null)
             {
             {
@@ -494,16 +516,32 @@ namespace MediaBrowser.Server.Implementations.Session
                 playedToCompletion = await OnPlaybackStopped(user.Id, key, info.Item, info.PositionTicks).ConfigureAwait(false);
                 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
             EventHelper.QueueEventIfNotNull(PlaybackStopped, this, new PlaybackStopEventArgs
             {
             {
                 Item = info.Item,
                 Item = info.Item,
                 Users = users,
                 Users = users,
                 PlaybackPositionTicks = info.PositionTicks,
                 PlaybackPositionTicks = info.PositionTicks,
-                PlayedToCompletion = playedToCompletion
+                PlayedToCompletion = playedToCompletion,
+                MediaVersionId = mediaVersionId
 
 
             }, _logger);
             }, _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)
         private async Task<bool> OnPlaybackStopped(Guid userId, string userDataKey, BaseItem item, long? positionTicks)
         {
         {
             var data = _userDataRepository.GetUserData(userId, userDataKey);
             var data = _userDataRepository.GetUserData(userId, userDataKey);
@@ -899,6 +937,8 @@ namespace MediaBrowser.Server.Implementations.Session
 
 
             session.PlayableMediaTypes = capabilities.PlayableMediaTypes.ToList();
             session.PlayableMediaTypes = capabilities.PlayableMediaTypes.ToList();
             session.SupportsFullscreenToggle = capabilities.SupportsFullscreenToggle;
             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()
                     QueueableMediaTypes = queueableMediaTypes.Split(',').ToList()
                 };
                 };
 
 
+                if (vals.Length > 3)
+                {
+                    info.MediaVersionId = vals[3];
+                }
+
                 _sessionManager.OnPlaybackStart(info);
                 _sessionManager.OnPlaybackStart(info);
             }
             }
         }
         }
@@ -265,6 +270,11 @@ namespace MediaBrowser.Server.Implementations.Session
                     SessionId = session.Id
                     SessionId = session.Id
                 };
                 };
 
 
+                if (vals.Length > 4)
+                {
+                    info.MediaVersionId = vals[4];
+                }
+
                 _sessionManager.OnPlaybackProgress(info);
                 _sessionManager.OnPlaybackProgress(info);
             }
             }
         }
         }
@@ -304,6 +314,11 @@ namespace MediaBrowser.Server.Implementations.Session
                     SessionId = session.Id
                     SessionId = session.Id
                 };
                 };
 
 
+                if (vals.Length > 2)
+                {
+                    info.MediaVersionId = vals[2];
+                }
+
                 _sessionManager.OnPlaybackStopped(info);
                 _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
          * Constructs a url for a user image
          * @param {String} userId
          * @param {String} userId
@@ -3805,7 +3791,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
          * @param {String} userId
          * @param {String} userId
          * @param {String} itemId
          * @param {String} itemId
          */
          */
-        self.reportPlaybackStart = function (userId, itemId, canSeek, queueableMediaTypes) {
+        self.reportPlaybackStart = function (userId, itemId, mediaVersionId, canSeek, queueableMediaTypes) {
 
 
             if (!userId) {
             if (!userId) {
                 throw new Error("null userId");
                 throw new Error("null userId");
@@ -3823,6 +3809,10 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
                 var deferred = $.Deferred();
                 var deferred = $.Deferred();
 
 
                 var msg = [itemId, canSeek, queueableMediaTypes];
                 var msg = [itemId, canSeek, queueableMediaTypes];
+                
+                if (mediaVersionId) {
+                    msg.push(mediaVersionId);
+                }
 
 
                 self.sendWebSocketMessage("PlaybackStart", msg.join('|'));
                 self.sendWebSocketMessage("PlaybackStart", msg.join('|'));
 
 
@@ -3830,10 +3820,16 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
                 return deferred.promise();
                 return deferred.promise();
             }
             }
 
 
-            var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId, {
+            var params = {
                 CanSeek: canSeek,
                 CanSeek: canSeek,
                 QueueableMediaTypes: queueableMediaTypes
                 QueueableMediaTypes: queueableMediaTypes
-            });
+            };
+
+            if (mediaVersionId) {
+                params.mediaVersionId = mediaVersionId;
+            }
+
+            var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId, params);
 
 
             return self.ajax({
             return self.ajax({
                 type: "POST",
                 type: "POST",
@@ -3846,7 +3842,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
          * @param {String} userId
          * @param {String} userId
          * @param {String} itemId
          * @param {String} itemId
          */
          */
-        self.reportPlaybackProgress = function (userId, itemId, positionTicks, isPaused, isMuted) {
+        self.reportPlaybackProgress = function (userId, itemId, mediaVersionId, positionTicks, isPaused, isMuted) {
 
 
             if (!userId) {
             if (!userId) {
                 throw new Error("null userId");
                 throw new Error("null userId");
@@ -3860,7 +3856,12 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
 
 
                 var deferred = $.Deferred();
                 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);
                 self.sendWebSocketMessage("PlaybackProgress", msgData);
 
 
@@ -3877,6 +3878,10 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
                 params.positionTicks = positionTicks;
                 params.positionTicks = positionTicks;
             }
             }
 
 
+            if (mediaVersionId) {
+                params.mediaVersionId = mediaVersionId;
+            }
+
             var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId + "/Progress", params);
             var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId + "/Progress", params);
 
 
             return self.ajax({
             return self.ajax({
@@ -3890,7 +3895,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
          * @param {String} userId
          * @param {String} userId
          * @param {String} itemId
          * @param {String} itemId
          */
          */
-        self.reportPlaybackStopped = function (userId, itemId, positionTicks) {
+        self.reportPlaybackStopped = function (userId, itemId, mediaVersionId, positionTicks) {
 
 
             if (!userId) {
             if (!userId) {
                 throw new Error("null userId");
                 throw new Error("null userId");
@@ -3904,20 +3909,26 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
 
 
                 var deferred = $.Deferred();
                 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, []);
                 deferred.resolveWith(null, []);
                 return deferred.promise();
                 return deferred.promise();
             }
             }
 
 
-            var params = {
-
-            };
+            var params = {};
 
 
             if (positionTicks) {
             if (positionTicks) {
                 params.positionTicks = positionTicks;
                 params.positionTicks = positionTicks;
             }
             }
 
 
+            if (mediaVersionId) {
+                params.mediaVersionId = mediaVersionId;
+            }
+
             var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId, params);
             var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId, params);
 
 
             return self.ajax({
             return self.ajax({

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

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

+ 1 - 1
MediaBrowser.WebDashboard/packages.config

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