From 1e9ffb83cf060bf2f755cd5a62782209eaaa6a4b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 27 Nov 2013 14:04:19 -0500 Subject: [PATCH] added live tv timers page --- MediaBrowser.Api/Images/ImageService.cs | 55 +++++++++++++ MediaBrowser.Api/LiveTv/LiveTvService.cs | 23 ++++++ .../LiveTv/ILiveTvManager.cs | 8 ++ .../MediaBrowser.Model.Portable.csproj | 3 + .../MediaBrowser.Model.net35.csproj | 3 + MediaBrowser.Model/LiveTv/RecordingQuery.cs | 15 ++++ MediaBrowser.Model/LiveTv/TimerInfoDto.cs | 72 +++++++++++++++++ MediaBrowser.Model/MediaBrowser.Model.csproj | 1 + .../LiveTv/LiveTvManager.cs | 62 ++++++++++++++- .../Sorting/AiredEpisodeOrderComparer.cs | 19 ++--- .../MediaBrowser.ServerApplication.csproj | 6 +- .../packages.config | 2 +- .../Api/DashboardService.cs | 3 +- MediaBrowser.WebDashboard/ApiClient.js | 79 +++++++++++++++++-- .../MediaBrowser.WebDashboard.csproj | 72 ++++++++--------- MediaBrowser.WebDashboard/packages.config | 2 +- 16 files changed, 368 insertions(+), 57 deletions(-) create mode 100644 MediaBrowser.Model/LiveTv/TimerInfoDto.cs diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 9112518b86..27881d12ba 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -269,6 +269,19 @@ namespace MediaBrowser.Api.Images public Guid Id { get; set; } } + [Route("/LiveTv/Channels/{Id}/Images/{Type}", "DELETE")] + [Route("/LiveTv/Channels/{Id}/Images/{Type}/{Index}", "DELETE")] + [Api(Description = "Deletes an item image")] + public class DeleteChannelImage : DeleteImageRequest, IReturnVoid + { + /// + /// Gets or sets the id. + /// + /// The id. + [ApiMember(Name = "Id", Description = "Channel Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] + public string Id { get; set; } + } + /// /// Class PostUserImage /// @@ -344,6 +357,25 @@ namespace MediaBrowser.Api.Images public Stream RequestStream { get; set; } } + [Route("/LiveTv/Channels/{Id}/Images/{Type}", "POST")] + [Route("/LiveTv/Channels/{Id}/Images/{Type}/{Index}", "POST")] + [Api(Description = "Posts an item image")] + public class PostChannelImage : DeleteImageRequest, IRequiresRequestStream, IReturnVoid + { + /// + /// Gets or sets the id. + /// + /// The id. + [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + public string Id { get; set; } + + /// + /// The raw Http Request Input Stream + /// + /// The request stream. + public Stream RequestStream { get; set; } + } + /// /// Class ImageService /// @@ -622,6 +654,20 @@ namespace MediaBrowser.Api.Images Task.WaitAll(task); } + public void Post(PostChannelImage request) + { + var pathInfo = PathInfo.Parse(RequestContext.PathInfo); + var id = pathInfo.GetArgumentValue(2); + + request.Type = (ImageType)Enum.Parse(typeof(ImageType), pathInfo.GetArgumentValue(4), true); + + var item = _liveTv.GetChannel(id); + + var task = PostImage(item, request.RequestStream, request.Type, RequestContext.ContentType); + + Task.WaitAll(task); + } + /// /// Deletes the specified request. /// @@ -648,6 +694,15 @@ namespace MediaBrowser.Api.Images Task.WaitAll(task); } + public void Delete(DeleteChannelImage request) + { + var item = _liveTv.GetChannel(request.Id); + + var task = item.DeleteImage(request.Type, request.Index); + + Task.WaitAll(task); + } + /// /// Deletes the specified request. /// diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index db50f463d4..2961c920f8 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -57,6 +57,17 @@ namespace MediaBrowser.Api.LiveTv public string ChannelId { get; set; } } + [Route("/LiveTv/Timers", "GET")] + [Api(Description = "Gets live tv timers")] + public class GetTimers : IReturn> + { + [ApiMember(Name = "ServiceName", Description = "Optional filter by service.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ServiceName { get; set; } + + [ApiMember(Name = "ChannelId", Description = "Optional filter by channel id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ChannelId { get; set; } + } + [Route("/LiveTv/Programs", "GET")] [Api(Description = "Gets available live tv epgs..")] public class GetPrograms : IReturn> @@ -153,5 +164,17 @@ namespace MediaBrowser.Api.LiveTv return ToOptimizedResult(result); } + + public object Get(GetTimers request) + { + var result = _liveTvManager.GetTimers(new TimerQuery + { + ChannelId = request.ChannelId, + ServiceName = request.ServiceName + + }, CancellationToken.None).Result; + + return ToOptimizedResult(result); + } } } \ No newline at end of file diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index 91634b4bfb..7938c38ec9 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -45,6 +45,14 @@ namespace MediaBrowser.Controller.LiveTv /// QueryResult{RecordingInfoDto}. Task> GetRecordings(RecordingQuery query, CancellationToken cancellationToken); + /// + /// Gets the timers. + /// + /// The query. + /// The cancellation token. + /// Task{QueryResult{TimerInfoDto}}. + Task> GetTimers(TimerQuery query, CancellationToken cancellationToken); + /// /// Gets the channel. /// diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index 6962cb470b..67888aa46d 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -251,6 +251,9 @@ LiveTv\RecordingStatus.cs + + LiveTv\TimerInfoDto.cs + Logging\ILogger.cs diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index 0bf2684bfd..cfe4a5462f 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -238,6 +238,9 @@ LiveTv\RecordingStatus.cs + + LiveTv\TimerInfoDto.cs + Logging\ILogger.cs diff --git a/MediaBrowser.Model/LiveTv/RecordingQuery.cs b/MediaBrowser.Model/LiveTv/RecordingQuery.cs index 8c83b0fcbd..0820c7785e 100644 --- a/MediaBrowser.Model/LiveTv/RecordingQuery.cs +++ b/MediaBrowser.Model/LiveTv/RecordingQuery.cs @@ -17,4 +17,19 @@ /// The name of the service. public string ServiceName { get; set; } } + + public class TimerQuery + { + /// + /// Gets or sets the channel identifier. + /// + /// The channel identifier. + public string ChannelId { get; set; } + + /// + /// Gets or sets the name of the service. + /// + /// The name of the service. + public string ServiceName { get; set; } + } } diff --git a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs new file mode 100644 index 0000000000..6a8339031a --- /dev/null +++ b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Model.LiveTv +{ + public class TimerInfoDto + { + /// + /// Id of the recording. + /// + public string Id { get; set; } + + /// + /// Gets or sets the external identifier. + /// + /// The external identifier. + public string ExternalId { get; set; } + + /// + /// ChannelId of the recording. + /// + public string ChannelId { get; set; } + + /// + /// ChannelName of the recording. + /// + public string ChannelName { get; set; } + + /// + /// Name of the recording. + /// + public string Name { get; set; } + + /// + /// Description of the recording. + /// + public string Description { get; set; } + + /// + /// The start date of the recording, in UTC. + /// + public DateTime StartDate { get; set; } + + /// + /// The end date of the recording, in UTC. + /// + public DateTime EndDate { get; set; } + + /// + /// Gets or sets the status. + /// + /// The status. + public RecordingStatus Status { get; set; } + + /// + /// Gets or sets a value indicating whether this instance is recurring. + /// + /// true if this instance is recurring; otherwise, false. + public bool IsRecurring { get; set; } + + /// + /// Gets or sets the recurring days. + /// + /// The recurring days. + public List RecurringDays { get; set; } + + public TimerInfoDto() + { + RecurringDays = new List(); + } + } +} diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 1cbdc60efa..103e583aed 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -66,6 +66,7 @@ + diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 12241876a1..00ac83f15c 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -417,7 +417,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv .ToList(); } - var returnArray = list.ToArray(); + var returnArray = list.OrderByDescending(i => i.StartDate) + .ToArray(); return new QueryResult { @@ -451,5 +452,64 @@ namespace MediaBrowser.Server.Implementations.LiveTv { throw new NotImplementedException(); } + + public async Task> GetTimers(TimerQuery query, CancellationToken cancellationToken) + { + var list = new List(); + + foreach (var service in GetServices(query.ServiceName, query.ChannelId)) + { + var timers = await GetTimers(service, cancellationToken).ConfigureAwait(false); + + list.AddRange(timers); + } + + if (!string.IsNullOrEmpty(query.ChannelId)) + { + list = list.Where(i => string.Equals(i.ChannelId, query.ChannelId)) + .ToList(); + } + + var returnArray = list.OrderByDescending(i => i.StartDate) + .ToArray(); + + return new QueryResult + { + Items = returnArray, + TotalRecordCount = returnArray.Length + }; + } + + private async Task> GetTimers(ILiveTvService service, CancellationToken cancellationToken) + { + var timers = await service.GetTimersAsync(cancellationToken).ConfigureAwait(false); + + return timers.Select(i => GetTimerInfoDto(i, service)); + } + + private TimerInfoDto GetTimerInfoDto(TimerInfo info, ILiveTvService service) + { + var id = service.Name + info.ChannelId + info.Id; + id = id.GetMD5().ToString("N"); + + var dto = new TimerInfoDto + { + ChannelName = info.ChannelName, + Description = info.Description, + EndDate = info.EndDate, + Name = info.Name, + StartDate = info.StartDate, + Id = id, + ExternalId = info.Id, + ChannelId = GetInternalChannelId(service.Name, info.ChannelId).ToString("N"), + Status = info.Status, + IsRecurring = info.IsRecurring, + RecurringDays = info.RecurringDays + }; + + return dto; + } + + } } diff --git a/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs b/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs index bdc343dea6..76971342a0 100644 --- a/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs +++ b/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.Server.Implementations.Sorting if (val != 0) { - return val; + //return val; } } @@ -49,8 +49,8 @@ namespace MediaBrowser.Server.Implementations.Sorting private int Compare(Episode x, Episode y) { - var isXSpecial = (x.ParentIndexNumber ?? -1) == 0; - var isYSpecial = (y.ParentIndexNumber ?? -1) == 0; + var isXSpecial = (x.PhysicalSeasonNumber ?? -1) == 0; + var isYSpecial = (y.PhysicalSeasonNumber ?? -1) == 0; if (isXSpecial && isYSpecial) { @@ -67,12 +67,12 @@ namespace MediaBrowser.Server.Implementations.Sorting return CompareEpisodeToSpecial(x, y); } - return CompareEpisodeToSpecial(x, y) * -1; + return CompareEpisodeToSpecial(y, x) * -1; } private int CompareEpisodeToSpecial(Episode x, Episode y) { - var xSeason = x.ParentIndexNumber ?? -1; + var xSeason = x.PhysicalSeasonNumber ?? -1; var ySeason = y.AirsAfterSeasonNumber ?? y.AirsBeforeSeasonNumber ?? -1; if (xSeason != ySeason) @@ -85,8 +85,9 @@ namespace MediaBrowser.Server.Implementations.Sorting // Compare episode number // Add 1 to to non-specials to account for AirsBeforeEpisodeNumber - var xEpisode = (x.IndexNumber ?? 0) * 1000 + 1; - var yEpisode = (y.AirsBeforeEpisodeNumber ?? 0) * 1000; + var xEpisode = x.IndexNumber ?? -1; + xEpisode++; + var yEpisode = y.AirsBeforeEpisodeNumber ?? 10000; return xEpisode.CompareTo(yEpisode); } @@ -119,8 +120,8 @@ namespace MediaBrowser.Server.Implementations.Sorting private int CompareEpisodes(Episode x, Episode y) { - var xValue = ((x.ParentIndexNumber ?? -1) * 1000) + (x.IndexNumber ?? -1); - var yValue = ((y.ParentIndexNumber ?? -1) * 1000) + (y.IndexNumber ?? -1); + var xValue = ((x.PhysicalSeasonNumber ?? -1) * 1000) + (x.IndexNumber ?? -1); + var yValue = ((y.PhysicalSeasonNumber ?? -1) * 1000) + (y.IndexNumber ?? -1); return xValue.CompareTo(yValue); } diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index f24283e70d..cfacffe087 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -119,9 +119,9 @@ ..\packages\Hardcodet.Wpf.TaskbarNotification.1.0.4.0\lib\net40\Hardcodet.Wpf.TaskbarNotification.dll - + False - ..\packages\MediaBrowser.IsoMounting.3.0.61\lib\net45\MediaBrowser.IsoMounter.dll + ..\packages\MediaBrowser.IsoMounting.3.0.65\lib\net45\MediaBrowser.IsoMounter.dll False @@ -129,7 +129,7 @@ False - ..\packages\MediaBrowser.IsoMounting.3.0.61\lib\net45\pfmclrapi.dll + ..\packages\MediaBrowser.IsoMounting.3.0.65\lib\net45\pfmclrapi.dll False diff --git a/MediaBrowser.ServerApplication/packages.config b/MediaBrowser.ServerApplication/packages.config index 0893a1b38a..e01ca1f672 100644 --- a/MediaBrowser.ServerApplication/packages.config +++ b/MediaBrowser.ServerApplication/packages.config @@ -1,7 +1,7 @@  - + diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 2b74eebb0c..69f05631f3 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -430,7 +430,7 @@ namespace MediaBrowser.WebDashboard.Api "http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js", "http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js", "scripts/all.js" + versionString, - "thirdparty/jstree1.0fix2/jquery.jstree.js" + "thirdparty/jstree1.0fix3/jquery.jstree.js" }; var tags = files.Select(s => string.Format("", s)).ToArray(); @@ -483,6 +483,7 @@ namespace MediaBrowser.WebDashboard.Api "livetvchannels.js", "livetvguide.js", "livetvrecordings.js", + "livetvtimers.js", "loginpage.js", "logpage.js", "medialibrarypage.js", diff --git a/MediaBrowser.WebDashboard/ApiClient.js b/MediaBrowser.WebDashboard/ApiClient.js index e63eb4d2b1..69f3f020cf 100644 --- a/MediaBrowser.WebDashboard/ApiClient.js +++ b/MediaBrowser.WebDashboard/ApiClient.js @@ -380,7 +380,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi self.getLiveTvServices = function (options) { - var url = self.getUrl("/LiveTv/Services", options || {}); + var url = self.getUrl("LiveTv/Services", options || {}); return self.ajax({ type: "GET", @@ -395,7 +395,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi throw new Error("null id"); } - var url = self.getUrl("/LiveTv/Channels/" + id); + var url = self.getUrl("LiveTv/Channels/" + id); return self.ajax({ type: "GET", @@ -406,7 +406,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi self.getLiveTvChannels = function (options) { - var url = self.getUrl("/LiveTv/Channels", options || {}); + var url = self.getUrl("LiveTv/Channels", options || {}); return self.ajax({ type: "GET", @@ -417,7 +417,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi self.getLiveTvPrograms = function (options) { - var url = self.getUrl("/LiveTv/Programs", options || {}); + var url = self.getUrl("LiveTv/Programs", options || {}); return self.ajax({ type: "GET", @@ -428,7 +428,76 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi self.getLiveTvRecordings = function (options) { - var url = self.getUrl("/LiveTv/Recordings", options || {}); + var url = self.getUrl("LiveTv/Recordings", options || {}); + + return self.ajax({ + type: "GET", + url: url, + dataType: "json" + }); + }; + + self.getLiveTvRecording = function (id) { + + if (!id) { + throw new Error("null id"); + } + + var url = self.getUrl("LiveTv/Recordings/" + id); + + return self.ajax({ + type: "GET", + url: url, + dataType: "json" + }); + }; + + self.deleteLiveTvRecording = function (id) { + + if (!id) { + throw new Error("null id"); + } + + var url = self.getUrl("LiveTv/Recordings/" + id); + + return self.ajax({ + type: "DELETE", + url: url + }); + }; + + self.cancelLiveTvTimer = function (id) { + + if (!id) { + throw new Error("null id"); + } + + var url = self.getUrl("LiveTv/Timers/" + id); + + return self.ajax({ + type: "DELETE", + url: url + }); + }; + + self.getLiveTvTimers = function (options) { + + var url = self.getUrl("LiveTv/Timers", options || {}); + + return self.ajax({ + type: "GET", + url: url, + dataType: "json" + }); + }; + + self.getLiveTvTimer = function (id) { + + if (!id) { + throw new Error("null id"); + } + + var url = self.getUrl("LiveTv/Timers/" + id); return self.ajax({ type: "GET", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 54e70cb9ab..5585e0db5e 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -80,9 +80,18 @@ + + PreserveNewest + + + PreserveNewest + PreserveNewest + + PreserveNewest + PreserveNewest @@ -281,18 +290,6 @@ PreserveNewest - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - PreserveNewest @@ -350,6 +347,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -595,76 +595,76 @@ PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config index ab57a48b15..35511052f4 100644 --- a/MediaBrowser.WebDashboard/packages.config +++ b/MediaBrowser.WebDashboard/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file