From 40959a816f49d040e16e0178d0e11d51282d98cc Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 3 Dec 2013 23:18:50 -0500 Subject: [PATCH] more support for episodes directly in a series folder --- MediaBrowser.Api/LiveTv/LiveTvService.cs | 36 +++++++++++- MediaBrowser.Api/TvShowsService.cs | 25 ++++++++- MediaBrowser.Api/UserLibrary/ItemsService.cs | 55 +++++++++++-------- .../Entities/TV/Episode.cs | 36 ++++++++++++ .../LiveTv/ILiveTvManager.cs | 16 ++++++ .../LiveTv/RecordingInfo.cs | 20 ++++++- MediaBrowser.Model/Dto/BaseItemDto.cs | 6 ++ MediaBrowser.Model/LiveTv/RecordingInfoDto.cs | 30 ++++++++++ MediaBrowser.Model/LiveTv/TimerInfoDto.cs | 6 ++ .../TV/SeriesPostScanTask.cs | 1 - .../Dto/DtoService.cs | 6 ++ .../LiveTv/LiveTvManager.cs | 26 ++++++++- 12 files changed, 231 insertions(+), 32 deletions(-) diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 4cd75afca5..0e40db58cd 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -1,12 +1,12 @@ -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Querying; using ServiceStack.ServiceHost; using System; using System.Collections.Generic; using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace MediaBrowser.Api.LiveTv { @@ -58,6 +58,22 @@ namespace MediaBrowser.Api.LiveTv public string ChannelId { get; set; } } + [Route("/LiveTv/Recordings/{Id}", "GET")] + [Api(Description = "Gets a live tv recording")] + public class GetRecording : IReturn + { + [ApiMember(Name = "Id", Description = "Recording Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Id { get; set; } + } + + [Route("/LiveTv/Timers/{Id}", "GET")] + [Api(Description = "Gets a live tv timer")] + public class GetTimer : IReturn + { + [ApiMember(Name = "Id", Description = "Timer Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Id { get; set; } + } + [Route("/LiveTv/Timers", "GET")] [Api(Description = "Gets live tv timers")] public class GetTimers : IReturn> @@ -182,6 +198,20 @@ namespace MediaBrowser.Api.LiveTv return ToOptimizedResult(result); } + public object Get(GetRecording request) + { + var result = _liveTvManager.GetRecording(request.Id, CancellationToken.None).Result; + + return ToOptimizedResult(result); + } + + public object Get(GetTimer request) + { + var result = _liveTvManager.GetTimer(request.Id, CancellationToken.None).Result; + + return ToOptimizedResult(result); + } + public object Get(GetTimers request) { var result = _liveTvManager.GetTimers(new TimerQuery diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index 9191bfc0c7..68ebd60c54 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Dto; +using MediaBrowser.Api.UserLibrary; +using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; @@ -89,6 +90,9 @@ namespace MediaBrowser.Api [ApiMember(Name = "IsVirtualUnaired", Description = "Optional filter by items that are virtual unaired episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool? IsVirtualUnaired { get; set; } + + [ApiMember(Name = "AdjacentTo", Description = "Optional. Return items that are siblings of a supplied item.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string AdjacentTo { get; set; } } [Route("/Shows/{Id}/Seasons", "GET")] @@ -120,6 +124,9 @@ namespace MediaBrowser.Api [ApiMember(Name = "IsVirtualUnaired", Description = "Optional filter by items that are virtual unaired episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool? IsVirtualUnaired { get; set; } + + [ApiMember(Name = "AdjacentTo", Description = "Optional. Return items that are siblings of a supplied item.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string AdjacentTo { get; set; } } /// @@ -394,6 +401,13 @@ namespace MediaBrowser.Api seasons = _libraryManager.Sort(seasons, user, new[] { sortOrder }, SortOrder.Ascending) .Cast(); + // This must be the last filter + if (!string.IsNullOrEmpty(request.AdjacentTo)) + { + seasons = ItemsService.FilterForAdjacency(seasons, request.AdjacentTo) + .Cast(); + } + var returnItems = seasons.Select(i => _dtoService.GetBaseItemDto(i, fields, user)) .ToArray(); @@ -447,7 +461,7 @@ namespace MediaBrowser.Api if (!string.IsNullOrEmpty(request.SeasonId)) { - var season = _libraryManager.GetItemById(request.Id) as Season; + var season = _libraryManager.GetItemById(new Guid(request.SeasonId)) as Season; if (season.IndexNumber.HasValue) { @@ -496,6 +510,13 @@ namespace MediaBrowser.Api episodes = _libraryManager.Sort(episodes, user, new[] { sortOrder }, SortOrder.Ascending) .Cast(); + // This must be the last filter + if (!string.IsNullOrEmpty(request.AdjacentTo)) + { + episodes = ItemsService.FilterForAdjacency(episodes, request.AdjacentTo) + .Cast(); + } + var returnItems = episodes.Select(i => _dtoService.GetBaseItemDto(i, fields, user)) .ToArray(); diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 0e40ef3957..3a7ea5ddd6 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -310,6 +310,12 @@ namespace MediaBrowser.Api.UserLibrary items = ApplySortOrder(request, items, user, _libraryManager); + // This must be the last filter + if (!string.IsNullOrEmpty(request.AdjacentTo)) + { + items = FilterForAdjacency(items, request.AdjacentTo); + } + var itemsArray = items.ToList(); var pagedItems = ApplyPaging(request, itemsArray); @@ -666,30 +672,6 @@ namespace MediaBrowser.Api.UserLibrary }); } - if (!string.IsNullOrEmpty(request.AdjacentTo)) - { - var item = _dtoService.GetItemByDtoId(request.AdjacentTo); - - var allSiblings = item.Parent.GetChildren(user, true).OrderBy(i => i.SortName).ToList(); - - var index = allSiblings.IndexOf(item); - - var previousId = Guid.Empty; - var nextId = Guid.Empty; - - if (index > 0) - { - previousId = allSiblings[index - 1].Id; - } - - if (index < allSiblings.Count - 1) - { - nextId = allSiblings[index + 1].Id; - } - - items = items.Where(i => i.Id == previousId || i.Id == nextId); - } - // Min index number if (request.MinIndexNumber.HasValue) { @@ -1144,6 +1126,31 @@ namespace MediaBrowser.Api.UserLibrary return false; } + internal static IEnumerable FilterForAdjacency(IEnumerable items, string adjacentToId) + { + var list = items.ToList(); + + var adjacentToIdGuid = new Guid(adjacentToId); + var adjacentToItem = list.FirstOrDefault(i => i.Id == adjacentToIdGuid); + + var index = list.IndexOf(adjacentToItem); + + var previousId = Guid.Empty; + var nextId = Guid.Empty; + + if (index > 0) + { + previousId = list[index - 1].Id; + } + + if (index < list.Count - 1) + { + nextId = list[index + 1].Id; + } + + return list.Where(i => i.Id == previousId || i.Id == nextId); + } + /// /// Determines whether the specified item has image. /// diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index 40b999b02a..e9f250d2a4 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -235,6 +235,42 @@ namespace MediaBrowser.Controller.Entities.TV get { return LocationType == Model.Entities.LocationType.Virtual && IsUnaired; } } + [IgnoreDataMember] + public Guid? SeasonId + { + get + { + // First see if the parent is a Season + var season = Parent as Season; + + if (season != null) + { + return season.Id; + } + + var seasonNumber = ParentIndexNumber; + + // Parent is a Series + if (seasonNumber.HasValue) + { + var series = Parent as Series; + + if (series != null) + { + season = series.Children.OfType() + .FirstOrDefault(i => i.IndexNumber.HasValue && i.IndexNumber.Value == seasonNumber.Value); + + if (season != null) + { + return season.Id; + } + } + } + + return null; + } + } + public override IEnumerable GetDeletePaths() { return new[] { Path }; diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index d5b9cea270..4e73fc109f 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -51,6 +51,22 @@ namespace MediaBrowser.Controller.LiveTv /// IEnumerable{Channel}. QueryResult GetChannels(ChannelQuery query); + /// + /// Gets the recording. + /// + /// The identifier. + /// The cancellation token. + /// Task{RecordingInfoDto}. + Task GetRecording(string id, CancellationToken cancellationToken); + + /// + /// Gets the timer. + /// + /// The identifier. + /// The cancellation token. + /// Task{TimerInfoDto}. + Task GetTimer(string id, CancellationToken cancellationToken); + /// /// Gets the recordings. /// diff --git a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs index 65e977d75e..2c8e8cb464 100644 --- a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs +++ b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs @@ -20,7 +20,13 @@ namespace MediaBrowser.Controller.LiveTv /// ChannelName of the recording. /// public string ChannelName { get; set; } - + + /// + /// Gets or sets the type of the channel. + /// + /// The type of the channel. + public ChannelType ChannelType { get; set; } + /// /// Name of the recording. /// @@ -76,6 +82,18 @@ namespace MediaBrowser.Controller.LiveTv /// The episode title. public string EpisodeTitle { get; set; } + /// + /// Gets or sets the official rating. + /// + /// The official rating. + public string OfficialRating { get; set; } + + /// + /// Gets or sets the community rating. + /// + /// The community rating. + public float? CommunityRating { get; set; } + public RecordingInfo() { Genres = new List(); diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index bfa7517561..20f60586c1 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -312,6 +312,12 @@ namespace MediaBrowser.Model.Dto /// The series id. public string SeriesId { get; set; } + /// + /// Gets or sets the season identifier. + /// + /// The season identifier. + public string SeasonId { get; set; } + /// /// Gets or sets the special feature count. /// diff --git a/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs b/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs index 9ad6233a67..86e82b562d 100644 --- a/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs +++ b/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs @@ -81,6 +81,36 @@ namespace MediaBrowser.Model.LiveTv /// The episode title. public string EpisodeTitle { get; set; } + /// + /// Gets or sets the duration ms. + /// + /// The duration ms. + public int DurationMs { get; set; } + + /// + /// Gets or sets the type of the media. + /// + /// The type of the media. + public string MediaType { get; set; } + + /// + /// Gets or sets the type of the channel. + /// + /// The type of the channel. + public ChannelType ChannelType { get; set; } + + /// + /// Gets or sets the official rating. + /// + /// The official rating. + public string OfficialRating { get; set; } + + /// + /// Gets or sets the community rating. + /// + /// The community rating. + public float? CommunityRating { get; set; } + public RecordingInfoDto() { Genres = new List(); diff --git a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs index 59a320bfda..9ebc2314d4 100644 --- a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs +++ b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs @@ -74,5 +74,11 @@ namespace MediaBrowser.Model.LiveTv /// /// The post padding seconds. public int PostPaddingSeconds { get; set; } + + /// + /// Gets or sets the duration ms. + /// + /// The duration ms. + public int DurationMs { get; set; } } } diff --git a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs index 055a6c101c..56b06f4906 100644 --- a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs +++ b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs @@ -1,6 +1,5 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Entities; diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index f6291cf36b..f298c2d4d1 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -1029,6 +1029,12 @@ namespace MediaBrowser.Server.Implementations.Dto { dto.IndexNumberEnd = episode.IndexNumberEnd; dto.SpecialSeasonNumber = episode.AirsAfterSeasonNumber ?? episode.AirsBeforeSeasonNumber; + + var seasonId = episode.SeasonId; + if (seasonId.HasValue) + { + dto.SeasonId = seasonId.Value.ToString("N"); + } } // Add SeriesInfo diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 704d1ea59c..4fd1f0e43b 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -391,9 +391,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv Path = info.Path, Genres = info.Genres, IsRepeat = info.IsRepeat, - EpisodeTitle = info.EpisodeTitle + EpisodeTitle = info.EpisodeTitle, + ChannelType = info.ChannelType, + MediaType = info.ChannelType == ChannelType.Radio ? MediaType.Audio : MediaType.Video, + CommunityRating = info.CommunityRating, + OfficialRating = info.OfficialRating }; + var duration = info.EndDate - info.StartDate; + dto.DurationMs = Convert.ToInt32(duration.TotalMilliseconds); + if (!string.IsNullOrEmpty(info.ProgramId)) { dto.ProgramId = GetInternalProgramIdId(service.Name, info.ProgramId).ToString("N"); @@ -510,6 +517,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv PostPaddingSeconds = info.PostPaddingSeconds }; + var duration = info.EndDate - info.StartDate; + dto.DurationMs = Convert.ToInt32(duration.TotalMilliseconds); + if (!string.IsNullOrEmpty(info.ProgramId)) { dto.ProgramId = GetInternalProgramIdId(service.Name, info.ProgramId).ToString("N"); @@ -563,5 +573,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv await service.CancelTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false); } + + public async Task GetRecording(string id, CancellationToken cancellationToken) + { + var results = await GetRecordings(new RecordingQuery(), cancellationToken).ConfigureAwait(false); + + return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.CurrentCulture)); + } + + public async Task GetTimer(string id, CancellationToken cancellationToken) + { + var results = await GetTimers(new TimerQuery(), cancellationToken).ConfigureAwait(false); + + return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.CurrentCulture)); + } } }