diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index e1f4799f18..5f86c71ece 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/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) { diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs index 6b0375e2d2..89e38711fe 100644 --- a/MediaBrowser.Api/Playback/StreamRequest.cs +++ b/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; } diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index e026aec038..5173008f9f 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/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; } + /// /// Gets or sets a value indicating whether this is likes. /// @@ -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; } + /// /// Gets or sets the position ticks. /// @@ -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; } + /// /// Gets or sets the position ticks. /// @@ -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); diff --git a/MediaBrowser.Api/UserLibrary/YearsService.cs b/MediaBrowser.Api/UserLibrary/YearsService.cs index b8b0aa9e94..8a3bc12b2b 100644 --- a/MediaBrowser.Api/UserLibrary/YearsService.cs +++ b/MediaBrowser.Api/UserLibrary/YearsService.cs @@ -14,8 +14,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class GetYears /// - [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 /// /// Class GetYear /// - [Route("/Years/{Year}", "GET")] - [Api(Description = "Gets a year")] + [Route("/Years/{Year}", "GET", Summary = "Gets a year")] public class GetYear : IReturn { /// diff --git a/MediaBrowser.Api/VideosService.cs b/MediaBrowser.Api/VideosService.cs index fa4b22cea7..380f89bc73 100644 --- a/MediaBrowser.Api/VideosService.cs +++ b/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 { [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)] diff --git a/MediaBrowser.Controller/Dto/IDtoService.cs b/MediaBrowser.Controller/Dto/IDtoService.cs index 03039dc831..a02851a9ff 100644 --- a/MediaBrowser.Controller/Dto/IDtoService.cs +++ b/MediaBrowser.Controller/Dto/IDtoService.cs @@ -28,13 +28,6 @@ namespace MediaBrowser.Controller.Dto /// SessionInfoDto. SessionInfoDto GetSessionInfoDto(SessionInfo session); - /// - /// Gets the base item info. - /// - /// The item. - /// BaseItemInfo. - BaseItemInfo GetBaseItemInfo(BaseItem item); - /// /// Gets the dto id. /// diff --git a/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs b/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs index 2ec3d308e3..ca815c1141 100644 --- a/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs +++ b/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs @@ -12,6 +12,7 @@ namespace MediaBrowser.Controller.Library public List Users { get; set; } public long? PlaybackPositionTicks { get; set; } public BaseItem Item { get; set; } + public string MediaVersionId { get; set; } public PlaybackProgressEventArgs() { diff --git a/MediaBrowser.Controller/Session/PlaybackInfo.cs b/MediaBrowser.Controller/Session/PlaybackInfo.cs index ab3111e766..5cb488ff1c 100644 --- a/MediaBrowser.Controller/Session/PlaybackInfo.cs +++ b/MediaBrowser.Controller/Session/PlaybackInfo.cs @@ -34,5 +34,11 @@ namespace MediaBrowser.Controller.Session /// /// The session id. public Guid SessionId { get; set; } + + /// + /// Gets or sets the media version identifier. + /// + /// The media version identifier. + public string MediaVersionId { get; set; } } } diff --git a/MediaBrowser.Controller/Session/PlaybackProgressInfo.cs b/MediaBrowser.Controller/Session/PlaybackProgressInfo.cs index a075432605..0c825932c8 100644 --- a/MediaBrowser.Controller/Session/PlaybackProgressInfo.cs +++ b/MediaBrowser.Controller/Session/PlaybackProgressInfo.cs @@ -34,5 +34,11 @@ namespace MediaBrowser.Controller.Session /// /// The position ticks. public long? PositionTicks { get; set; } + + /// + /// Gets or sets the media version identifier. + /// + /// The media version identifier. + public string MediaVersionId { get; set; } } } diff --git a/MediaBrowser.Controller/Session/PlaybackStopInfo.cs b/MediaBrowser.Controller/Session/PlaybackStopInfo.cs index 5d1ce01315..3391c96cf7 100644 --- a/MediaBrowser.Controller/Session/PlaybackStopInfo.cs +++ b/MediaBrowser.Controller/Session/PlaybackStopInfo.cs @@ -22,5 +22,11 @@ namespace MediaBrowser.Controller.Session /// /// The position ticks. public long? PositionTicks { get; set; } + + /// + /// Gets or sets the media version identifier. + /// + /// The media version identifier. + public string MediaVersionId { get; set; } } } diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs index 73e33d78c1..953d186e84 100644 --- a/MediaBrowser.Controller/Session/SessionInfo.cs +++ b/MediaBrowser.Controller/Session/SessionInfo.cs @@ -119,12 +119,24 @@ namespace MediaBrowser.Controller.Session /// The now playing item. public BaseItem NowPlayingItem { get; set; } + /// + /// Gets or sets the now playing media version identifier. + /// + /// The now playing media version identifier. + public string NowPlayingMediaVersionId { get; set; } + + + /// + /// Gets or sets the now playing run time ticks. + /// + /// The now playing run time ticks. + public long? NowPlayingRunTimeTicks { get; set; } + /// /// Gets or sets the now playing position ticks. /// /// The now playing position ticks. public long? NowPlayingPositionTicks { get; set; } - /// /// Gets or sets a value indicating whether this instance is paused. /// @@ -161,6 +173,18 @@ namespace MediaBrowser.Controller.Session /// true if [supports fullscreen toggle]; otherwise, false. public bool SupportsFullscreenToggle { get; set; } + /// + /// Gets or sets a value indicating whether [supports osd toggle]. + /// + /// true if [supports osd toggle]; otherwise, false. + public bool SupportsOsdToggle { get; set; } + + /// + /// Gets or sets a value indicating whether [supports navigation commands]. + /// + /// true if [supports navigation commands]; otherwise, false. + public bool SupportsNavigationControl { get; set; } + /// /// Gets a value indicating whether this instance is active. /// diff --git a/MediaBrowser.Dlna/PlayTo/DlnaController.cs b/MediaBrowser.Dlna/PlayTo/DlnaController.cs index cb4bda1277..bda737ad2d 100644 --- a/MediaBrowser.Dlna/PlayTo/DlnaController.cs +++ b/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, diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index c46e355df7..960b0f6358 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -428,6 +428,9 @@ Session\MessageCommand.cs + + Session\PlaybackReports.cs + Session\PlayRequest.cs diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index 7ef8912091..b010ad9c99 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -415,6 +415,9 @@ Session\MessageCommand.cs + + Session\PlaybackReports.cs + Session\PlayRequest.cs diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs index 46d3dcc3f5..91ac67a1f7 100644 --- a/MediaBrowser.Model/ApiClient/IApiClient.cs +++ b/MediaBrowser.Model/ApiClient/IApiClient.cs @@ -538,35 +538,26 @@ namespace MediaBrowser.Model.ApiClient /// /// Reports to the server that the user has begun playing an item /// - /// The item id. - /// The user id. - /// if set to true [is seekable]. - /// The list of media types that the client is capable of queuing onto the playlist. See MediaType class. + /// The information. /// Task{UserItemDataDto}. /// itemId - Task ReportPlaybackStartAsync(string itemId, string userId, bool isSeekable, List queueableMediaTypes); + Task ReportPlaybackStartAsync(PlaybackStartInfo info); /// /// Reports playback progress to the server /// - /// The item id. - /// The user id. - /// The position ticks. - /// if set to true [is paused]. - /// if set to true [is muted]. + /// The information. /// Task{UserItemDataDto}. /// itemId - Task ReportPlaybackProgressAsync(string itemId, string userId, long? positionTicks, bool isPaused, bool isMuted); + Task ReportPlaybackProgressAsync(PlaybackProgressInfo info); /// /// Reports to the server that the user has stopped playing an item /// - /// The item id. - /// The user id. - /// The position ticks. + /// The information. /// Task{UserItemDataDto}. /// itemId - Task ReportPlaybackStoppedAsync(string itemId, string userId, long? positionTicks); + Task ReportPlaybackStoppedAsync(PlaybackStopInfo info); /// /// Instructs antoher client to browse to a library item. diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index c2765754e9..1f304112f5 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -147,7 +147,7 @@ namespace MediaBrowser.Model.Configuration /// different directories and files. /// /// The file watcher delay. - public int RealtimeWatcherDelay { get; set; } + public int RealtimeMonitorDelay { get; set; } /// /// 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; diff --git a/MediaBrowser.Model/Dto/MediaVersionInfo.cs b/MediaBrowser.Model/Dto/MediaVersionInfo.cs index 3da5e2b8da..c2e0d83b2b 100644 --- a/MediaBrowser.Model/Dto/MediaVersionInfo.cs +++ b/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 MediaStreams { get; set; } - public List Chapters { get; set; } - public bool IsPrimaryVersion { get; set; } } } diff --git a/MediaBrowser.Model/Entities/BaseItemInfo.cs b/MediaBrowser.Model/Entities/BaseItemInfo.cs index b704bdb57c..0fac979490 100644 --- a/MediaBrowser.Model/Entities/BaseItemInfo.cs +++ b/MediaBrowser.Model/Entities/BaseItemInfo.cs @@ -69,6 +69,12 @@ namespace MediaBrowser.Model.Entities /// /// The thumb item identifier. public string BackdropItemId { get; set; } + + /// + /// Gets or sets the media version identifier. + /// + /// The media version identifier. + public string MediaVersionId { get; set; } /// /// Gets a value indicating whether this instance has primary image. diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 6a7e723a79..5e9b97939a 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -132,6 +132,7 @@ + diff --git a/MediaBrowser.Model/Session/PlaybackReports.cs b/MediaBrowser.Model/Session/PlaybackReports.cs new file mode 100644 index 0000000000..662dc57805 --- /dev/null +++ b/MediaBrowser.Model/Session/PlaybackReports.cs @@ -0,0 +1,56 @@ + +namespace MediaBrowser.Model.Session +{ + /// + /// Class PlaybackStartInfo. + /// + 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[] { }; + } + } + + /// + /// Class PlaybackProgressInfo. + /// + 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; } + } + + /// + /// Class PlaybackStopInfo. + /// + public class PlaybackStopInfo + { + public string UserId { get; set; } + + public string ItemId { get; set; } + + public string MediaVersionId { get; set; } + + public long? PositionTicks { get; set; } + } +} diff --git a/MediaBrowser.Model/Session/SessionCapabilities.cs b/MediaBrowser.Model/Session/SessionCapabilities.cs index 731ebacccf..7b3b04ce92 100644 --- a/MediaBrowser.Model/Session/SessionCapabilities.cs +++ b/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[] {}; diff --git a/MediaBrowser.Model/Session/SessionInfoDto.cs b/MediaBrowser.Model/Session/SessionInfoDto.cs index d44bbeaeb5..09a8375d81 100644 --- a/MediaBrowser.Model/Session/SessionInfoDto.cs +++ b/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 /// true if [supports remote control]; otherwise, false. public bool SupportsRemoteControl { get; set; } + /// + /// Gets or sets a value indicating whether [supports osd toggle]. + /// + /// true if [supports osd toggle]; otherwise, false. + public bool SupportsOsdToggle { get; set; } + + /// + /// Gets or sets a value indicating whether [supports navigation commands]. + /// + /// true if [supports navigation commands]; otherwise, false. + public bool SupportsNavigationControl { get; set; } + public event PropertyChangedEventHandler PropertyChanged; public SessionInfoDto() diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index c94cdda846..520b01af22 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/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 /// /// The item. + /// The media version identifier. + /// The now playing runtime ticks. /// BaseItemInfo. /// item - 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 GetMediaVersions(Audio item) { - var result = new List(); - - result.Add(GetVersionInfo(item, true)); + var result = new List + { + 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 mediaStreams) { - var name = ""; + var terms = new List(); - 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) diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs index 65bbb35bac..0a0b3f4bcd 100644 --- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs +++ b/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)); } } } diff --git a/MediaBrowser.Server.Implementations/Roku/RokuControllerFactory.cs b/MediaBrowser.Server.Implementations/Roku/RokuControllerFactory.cs index 71f70421a9..53f28e7ae7 100644 --- a/MediaBrowser.Server.Implementations/Roku/RokuControllerFactory.cs +++ b/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 { MediaType.Video, MediaType.Audio }; + session.SupportsFullscreenToggle = false; + return new RokuSessionController(_httpClient, _json, _appHost, session); } diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index 1fb5af127c..c895062617 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -218,15 +218,29 @@ namespace MediaBrowser.Server.Implementations.Session /// /// The session. /// The item. + /// The media version identifier. /// if set to true [is paused]. + /// if set to true [is muted]. /// The current position ticks. - 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; + } } /// @@ -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 /// Task. /// /// positionTicks - 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 /// Task. /// info /// positionTicks - 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 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; } } } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs index e5fbc7d9fa..5c4c00e7d2 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/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); } } diff --git a/MediaBrowser.WebDashboard/ApiClient.js b/MediaBrowser.WebDashboard/ApiClient.js index 97c26ec78a..47719df8d2 100644 --- a/MediaBrowser.WebDashboard/ApiClient.js +++ b/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({ diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 16eec26408..a0dbae3d4c 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -84,9 +84,7 @@ - - PreserveNewest - + PreserveNewest diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config index 0bdea523e3..bbc2c0f068 100644 --- a/MediaBrowser.WebDashboard/packages.config +++ b/MediaBrowser.WebDashboard/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file