support deleting and canceling live tv recordings and timers

This commit is contained in:
Luke Pulverenti 2013-11-29 11:58:24 -05:00
parent 4892fb4e95
commit 235b838fbe
26 changed files with 280 additions and 81 deletions

View File

@ -1,4 +1,5 @@
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Querying;
@ -82,6 +83,22 @@ namespace MediaBrowser.Api.LiveTv
public string UserId { get; set; }
}
[Route("/LiveTv/Recordings/{Id}", "DELETE")]
[Api(Description = "Deletes a live tv recording")]
public class DeleteRecording : IReturnVoid
{
[ApiMember(Name = "Id", Description = "Recording Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
[Route("/LiveTv/Timers/{Id}", "DELETE")]
[Api(Description = "Cancels a live tv timer")]
public class CancelTimer : IReturnVoid
{
[ApiMember(Name = "Id", Description = "Timer Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
public class LiveTvService : BaseApiService
{
private readonly ILiveTvManager _liveTvManager;
@ -176,5 +193,19 @@ namespace MediaBrowser.Api.LiveTv
return ToOptimizedResult(result);
}
public void Delete(DeleteRecording request)
{
var task = _liveTvManager.DeleteRecording(request.Id);
Task.WaitAll(task);
}
public void Delete(CancelTimer request)
{
var task = _liveTvManager.CancelTimer(request.Id);
Task.WaitAll(task);
}
}
}

View File

@ -31,7 +31,8 @@ namespace MediaBrowser.Api.ScheduledTasks
[Api(Description = "Gets scheduled tasks")]
public class GetScheduledTasks : IReturn<List<TaskInfo>>
{
[ApiMember(Name = "IsHidden", Description = "Optional filter tasks that are hidden, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsHidden { get; set; }
}
/// <summary>
@ -112,10 +113,33 @@ namespace MediaBrowser.Api.ScheduledTasks
/// <returns>IEnumerable{TaskInfo}.</returns>
public object Get(GetScheduledTasks request)
{
var result = TaskManager.ScheduledTasks.OrderBy(i => i.Name)
.Select(ScheduledTaskHelpers.GetTaskInfo).ToList();
IEnumerable<IScheduledTaskWorker> result = TaskManager.ScheduledTasks
.OrderBy(i => i.Name);
return ToOptimizedResult(result);
if (request.IsHidden.HasValue)
{
var val = request.IsHidden.Value;
result = result.Where(i =>
{
var isHidden = false;
var configurableTask = i.ScheduledTask as IConfigurableScheduledTask;
if (configurableTask != null)
{
isHidden = configurableTask.IsHidden;
}
return isHidden == val;
});
}
var infos = result
.Select(ScheduledTaskHelpers.GetTaskInfo)
.ToList();
return ToOptimizedResult(infos);
}
/// <summary>

View File

@ -46,8 +46,10 @@ namespace MediaBrowser.Api.ScheduledTasks
/// <returns>Task{IEnumerable{TaskInfo}}.</returns>
protected override Task<IEnumerable<TaskInfo>> GetDataToSend(object state)
{
return Task.FromResult(TaskManager.ScheduledTasks.OrderBy(i => i.Name)
.Select(ScheduledTaskHelpers.GetTaskInfo));
return Task.FromResult(TaskManager.ScheduledTasks
.OrderBy(i => i.Name)
.Select(ScheduledTaskHelpers.GetTaskInfo)
.Where(i => !i.IsHidden));
}
}
}

View File

@ -81,8 +81,11 @@ namespace MediaBrowser.Api
[ApiMember(Name = "Season", Description = "Optional filter by season number.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public int? Season { get; set; }
[ApiMember(Name = "ExcludeLocationTypes", Description = "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string ExcludeLocationTypes { get; set; }
[ApiMember(Name = "IsMissing", Description = "Optional filter by items that are missing episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsMissing { get; set; }
[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; }
}
[Route("/Shows/{Id}/Seasons", "GET")]
@ -106,11 +109,14 @@ namespace MediaBrowser.Api
[ApiMember(Name = "Id", Description = "The series id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public Guid Id { get; set; }
[ApiMember(Name = "ExcludeLocationTypes", Description = "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string ExcludeLocationTypes { get; set; }
[ApiMember(Name = "IsSpecialSeason", Description = "Optional. Filter by special season.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsSpecialSeason { get; set; }
[ApiMember(Name = "IsMissing", Description = "Optional filter by items that are missing episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsMissing { get; set; }
[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; }
}
/// <summary>
@ -380,12 +386,7 @@ namespace MediaBrowser.Api
}
}
// ExcludeLocationTypes
if (!string.IsNullOrEmpty(request.ExcludeLocationTypes))
{
var vals = request.ExcludeLocationTypes.Split(',');
seasons = seasons.Where(f => !vals.Contains(f.LocationType.ToString(), StringComparer.OrdinalIgnoreCase));
}
seasons = FilterVirtualSeasons(request, seasons);
seasons = _libraryManager.Sort(seasons, user, new[] { sortOrder }, SortOrder.Ascending)
.Cast<Season>();
@ -400,6 +401,34 @@ namespace MediaBrowser.Api
};
}
private IEnumerable<Season> FilterVirtualSeasons(GetSeasons request, IEnumerable<Season> items)
{
if (request.IsMissing.HasValue && request.IsVirtualUnaired.HasValue)
{
var isMissing = request.IsMissing.Value;
var isVirtualUnaired = request.IsVirtualUnaired.Value;
if (!isMissing && !isVirtualUnaired)
{
return items.Where(i => !i.IsMissingOrVirtualUnaired);
}
}
if (request.IsMissing.HasValue)
{
var val = request.IsMissing.Value;
items = items.Where(i => i.IsMissingSeason == val);
}
if (request.IsVirtualUnaired.HasValue)
{
var val = request.IsVirtualUnaired.Value;
items = items.Where(i => i.IsVirtualUnaired == val);
}
return items;
}
public object Get(GetEpisodes request)
{
var user = _userManager.GetUserById(request.UserId);
@ -431,11 +460,16 @@ namespace MediaBrowser.Api
episodes = episodes.Where(i => !i.IsVirtualUnaired);
}
// ExcludeLocationTypes
if (!string.IsNullOrEmpty(request.ExcludeLocationTypes))
if (request.IsMissing.HasValue)
{
var vals = request.ExcludeLocationTypes.Split(',');
episodes = episodes.Where(f => !vals.Contains(f.LocationType.ToString(), StringComparer.OrdinalIgnoreCase));
var val = request.IsMissing.Value;
episodes = episodes.Where(i => i.IsMissingEpisode == val);
}
if (request.IsVirtualUnaired.HasValue)
{
var val = request.IsVirtualUnaired.Value;
episodes = episodes.Where(i => i.IsVirtualUnaired == val);
}
episodes = _libraryManager.Sort(episodes, user, new[] { sortOrder }, SortOrder.Ascending)

View File

@ -218,7 +218,7 @@ namespace MediaBrowser.Common.Implementations
try
{
// Increase the max http request limit
ServicePointManager.DefaultConnectionLimit = Math.Max(48, ServicePointManager.DefaultConnectionLimit);
ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit);
}
catch (Exception ex)
{

View File

@ -1,4 +1,5 @@
using MediaBrowser.Common.Configuration;
using System.Reflection;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.Logging;

View File

@ -29,7 +29,6 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
/// <summary>
/// Initializes a new instance of the <see cref="DeleteCacheFileTask" /> class.
/// </summary>
/// <param name="appPaths">The app paths.</param>
public DeleteCacheFileTask(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem)
{
ApplicationPaths = appPaths;

View File

@ -42,4 +42,9 @@ namespace MediaBrowser.Common.ScheduledTasks
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
IEnumerable<ITaskTrigger> GetDefaultTriggers();
}
public interface IConfigurableScheduledTask
{
bool IsHidden { get; }
}
}

View File

@ -16,6 +16,15 @@ namespace MediaBrowser.Common.ScheduledTasks
/// <returns>TaskInfo.</returns>
public static TaskInfo GetTaskInfo(IScheduledTaskWorker task)
{
var isHidden = false;
var configurableTask = task.ScheduledTask as IConfigurableScheduledTask;
if (configurableTask != null)
{
isHidden = configurableTask.IsHidden;
}
return new TaskInfo
{
Name = task.Name,
@ -25,7 +34,8 @@ namespace MediaBrowser.Common.ScheduledTasks
LastExecutionResult = task.LastExecutionResult,
Triggers = task.Triggers.Select(GetTriggerInfo).ToList(),
Description = task.Description,
Category = task.Category
Category = task.Category,
IsHidden = isHidden
};
}

View File

@ -24,6 +24,20 @@ namespace MediaBrowser.Controller.LiveTv
/// <returns>Task.</returns>
Task ScheduleRecording(string programId);
/// <summary>
/// Deletes the recording.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>Task.</returns>
Task DeleteRecording(string id);
/// <summary>
/// Cancels the timer.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>Task.</returns>
Task CancelTimer(string id);
/// <summary>
/// Adds the parts.
/// </summary>

View File

@ -47,14 +47,6 @@ namespace MediaBrowser.Controller.LiveTv
/// <returns>Task.</returns>
Task CreateTimerAsync(TimerInfo info, CancellationToken cancellationToken);
/// <summary>
/// Updates the timer asynchronous.
/// </summary>
/// <param name="info">The information.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task UpdateTimerAsync(TimerInfo info, CancellationToken cancellationToken);
/// <summary>
/// Gets the channel image asynchronous.
/// </summary>

View File

@ -40,6 +40,12 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
public DateTime EndDate { get; set; }
/// <summary>
/// Gets or sets the program identifier.
/// </summary>
/// <value>The program identifier.</value>
public string ProgramId { get; set; }
/// <summary>
/// Gets or sets the status.
/// </summary>

View File

@ -1,6 +1,5 @@
using MediaBrowser.Model.LiveTv;
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.LiveTv
{
@ -21,6 +20,12 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
public string ChannelName { get; set; }
/// <summary>
/// Gets or sets the program identifier.
/// </summary>
/// <value>The program identifier.</value>
public string ProgramId { get; set; }
/// <summary>
/// Name of the recording.
/// </summary>
@ -52,16 +57,5 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
/// <value><c>true</c> if this instance is recurring; otherwise, <c>false</c>.</value>
public bool IsRecurring { get; set; }
/// <summary>
/// Gets or sets the recurring days.
/// </summary>
/// <value>The recurring days.</value>
public List<DayOfWeek> RecurringDays { get; set; }
public TimerInfo()
{
RecurringDays = new List<DayOfWeek>();
}
}
}

View File

@ -10,4 +10,11 @@ namespace MediaBrowser.Model.LiveTv
Conflicted,
Deleted
}
public enum RecurrenceType
{
Manual,
NewProgramEvents,
AllProgramEvents
}
}

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
namespace MediaBrowser.Model.LiveTv
{
@ -26,6 +25,12 @@ namespace MediaBrowser.Model.LiveTv
/// </summary>
public string ChannelName { get; set; }
/// <summary>
/// Gets or sets the program identifier.
/// </summary>
/// <value>The program identifier.</value>
public string ProgramId { get; set; }
/// <summary>
/// Name of the recording.
/// </summary>
@ -57,16 +62,5 @@ namespace MediaBrowser.Model.LiveTv
/// </summary>
/// <value><c>true</c> if this instance is recurring; otherwise, <c>false</c>.</value>
public bool IsRecurring { get; set; }
/// <summary>
/// Gets or sets the recurring days.
/// </summary>
/// <value>The recurring days.</value>
public List<DayOfWeek> RecurringDays { get; set; }
public TimerInfoDto()
{
RecurringDays = new List<DayOfWeek>();
}
}
}

View File

@ -1,5 +1,4 @@
using MediaBrowser.Model.Entities;

namespace MediaBrowser.Model.Querying
{
public class EpisodeQuery
@ -8,7 +7,9 @@ namespace MediaBrowser.Model.Querying
public string SeriesId { get; set; }
public LocationType[] ExcludeLocationTypes { get; set; }
public bool? IsMissing { get; set; }
public bool? IsVirtualUnaired { get; set; }
public int? SeasonNumber { get; set; }
@ -17,7 +18,6 @@ namespace MediaBrowser.Model.Querying
public EpisodeQuery()
{
Fields = new ItemFields[] { };
ExcludeLocationTypes = new LocationType[] { };
}
}
@ -27,7 +27,9 @@ namespace MediaBrowser.Model.Querying
public string SeriesId { get; set; }
public LocationType[] ExcludeLocationTypes { get; set; }
public bool? IsMissing { get; set; }
public bool? IsVirtualUnaired { get; set; }
public ItemFields[] Fields { get; set; }
@ -36,7 +38,6 @@ namespace MediaBrowser.Model.Querying
public SeasonQuery()
{
Fields = new ItemFields[] { };
ExcludeLocationTypes = new LocationType[] { };
}
}
}

View File

@ -56,6 +56,12 @@ namespace MediaBrowser.Model.Tasks
/// <value>The category.</value>
public string Category { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is hidden.
/// </summary>
/// <value><c>true</c> if this instance is hidden; otherwise, <c>false</c>.</value>
public bool IsHidden { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="TaskInfo"/> class.
/// </summary>

View File

@ -397,6 +397,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
Status = info.Status
};
if (!string.IsNullOrEmpty(info.ProgramId))
{
dto.ProgramId = GetInternalProgramIdId(service.Name, info.ProgramId).ToString("N");
}
return dto;
}
@ -503,13 +508,61 @@ namespace MediaBrowser.Server.Implementations.LiveTv
ExternalId = info.Id,
ChannelId = GetInternalChannelId(service.Name, info.ChannelId).ToString("N"),
Status = info.Status,
IsRecurring = info.IsRecurring,
RecurringDays = info.RecurringDays
IsRecurring = info.IsRecurring
};
if (!string.IsNullOrEmpty(info.ProgramId))
{
dto.ProgramId = GetInternalProgramIdId(service.Name, info.ProgramId).ToString("N");
}
return dto;
}
public async Task DeleteRecording(string recordingId)
{
var recordings = await GetRecordings(new RecordingQuery
{
}, CancellationToken.None).ConfigureAwait(false);
var recording = recordings.Items
.FirstOrDefault(i => string.Equals(recordingId, i.Id, StringComparison.OrdinalIgnoreCase));
if (recording == null)
{
throw new ResourceNotFoundException(string.Format("Recording with Id {0} not found", recordingId));
}
var channel = GetChannel(recording.ChannelId);
var service = GetServices(channel.ServiceName, null)
.First();
await service.DeleteRecordingAsync(recording.ExternalId, CancellationToken.None).ConfigureAwait(false);
}
public async Task CancelTimer(string id)
{
var timers = await GetTimers(new TimerQuery
{
}, CancellationToken.None).ConfigureAwait(false);
var timer = timers.Items
.FirstOrDefault(i => string.Equals(id, i.Id, StringComparison.OrdinalIgnoreCase));
if (timer == null)
{
throw new ResourceNotFoundException(string.Format("Timer with Id {0} not found", id));
}
var channel = GetChannel(timer.ChannelId);
var service = GetServices(channel.ServiceName, null)
.First();
await service.CancelTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
}
}
}

View File

@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.LiveTv
{
class RefreshChannelsScheduledTask : IScheduledTask
class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask
{
private readonly ILiveTvManager _liveTvManager;
@ -33,7 +33,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public Task Execute(System.Threading.CancellationToken cancellationToken, IProgress<double> progress)
{
var manager = (LiveTvManager) _liveTvManager;
var manager = (LiveTvManager)_liveTvManager;
return manager.RefreshChannels(progress, cancellationToken);
}
@ -50,5 +50,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
new IntervalTrigger{ Interval = TimeSpan.FromHours(2)}
};
}
public bool IsHidden
{
get { return _liveTvManager.Services.Count == 0; }
}
}
}

View File

@ -17,14 +17,19 @@ namespace MediaBrowser.ServerApplication.Native
/// <returns>HttpClient.</returns>
public static HttpClient GetHttpClient(bool enableHttpCompression)
{
return new HttpClient(new WebRequestHandler
var client = new HttpClient(new WebRequestHandler
{
CachePolicy = new RequestCachePolicy(RequestCacheLevel.Revalidate),
AutomaticDecompression = enableHttpCompression ? DecompressionMethods.Deflate : DecompressionMethods.None
})
{
Timeout = TimeSpan.FromSeconds(20)
};
client.DefaultRequestHeaders.Add("Connection", "Keep-Alive");
return client;
}
}
}

View File

@ -506,6 +506,20 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
});
};
self.createLiveTvTimer = function (options) {
if (!options) {
throw new Error("null options");
}
var url = self.getUrl("LiveTv/Timers", options);
return self.ajax({
type: "POST",
url: url
});
};
/**
* Gets the current server status
*/
@ -1019,9 +1033,11 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
/**
* Gets the server's scheduled tasks
*/
self.getScheduledTasks = function () {
self.getScheduledTasks = function (options) {
var url = self.getUrl("ScheduledTasks");
options = options || {};
var url = self.getUrl("ScheduledTasks", options);
return self.ajax({
type: "GET",

View File

@ -284,10 +284,10 @@
<Content Include="dashboard-ui\css\images\userdata\thumbs_up_on.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\css\images\userdata\playedoff.png">
<Content Include="dashboard-ui\css\images\userdata\checkedoff.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\css\images\userdata\playedon.png">
<Content Include="dashboard-ui\css\images\userdata\checkedon.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\css\librarybrowser.css">

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MediaBrowser.ApiClient.Javascript" version="3.0.201" targetFramework="net45" />
<package id="MediaBrowser.ApiClient.Javascript" version="3.0.203" targetFramework="net45" />
<package id="ServiceStack.Common" version="3.9.62" targetFramework="net45" />
<package id="ServiceStack.Text" version="3.9.62" targetFramework="net45" />
</packages>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common.Internal</id>
<version>3.0.252</version>
<version>3.0.253</version>
<title>MediaBrowser.Common.Internal</title>
<authors>Luke</authors>
<owners>ebr,Luke,scottisafool</owners>
@ -12,7 +12,7 @@
<description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
<dependency id="MediaBrowser.Common" version="3.0.252" />
<dependency id="MediaBrowser.Common" version="3.0.253" />
<dependency id="NLog" version="2.1.0" />
<dependency id="ServiceStack.Text" version="3.9.58" />
<dependency id="SimpleInjector" version="2.3.6" />

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common</id>
<version>3.0.252</version>
<version>3.0.253</version>
<title>MediaBrowser.Common</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Server.Core</id>
<version>3.0.252</version>
<version>3.0.253</version>
<title>Media Browser.Server.Core</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>
@ -12,7 +12,7 @@
<description>Contains core components required to build plugins for Media Browser Server.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
<dependency id="MediaBrowser.Common" version="3.0.252" />
<dependency id="MediaBrowser.Common" version="3.0.253" />
</dependencies>
</metadata>
<files>