|
|
|
@ -442,7 +442,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
|
|
|
|
|
|
|
private void CancelTimerInternal(string timerId, bool isSeriesCancelled)
|
|
|
|
|
{
|
|
|
|
|
var timer = _timerProvider.GetAll().FirstOrDefault(r => string.Equals(r.Id, timerId, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
var timer = _timerProvider.GetTimer(timerId);
|
|
|
|
|
if (timer != null)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(timer.SeriesTimerId) || isSeriesCancelled)
|
|
|
|
@ -474,10 +474,20 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
|
|
return Task.FromResult(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Task CreateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Task CreateTimerAsync(TimerInfo info, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Task<string> CreateTimer(TimerInfo timer, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
var existingTimer = _timerProvider.GetAll()
|
|
|
|
|
.FirstOrDefault(i => string.Equals(info.ProgramId, i.ProgramId, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
.FirstOrDefault(i => string.Equals(timer.ProgramId, i.ProgramId, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
|
|
|
|
if (existingTimer != null)
|
|
|
|
|
{
|
|
|
|
@ -485,6 +495,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
|
|
{
|
|
|
|
|
existingTimer.Status = RecordingStatus.New;
|
|
|
|
|
_timerProvider.Update(existingTimer);
|
|
|
|
|
return Task.FromResult(existingTimer.Id);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
@ -492,16 +503,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return CreateTimer(info, cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Task CreateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
return CreateSeriesTimer(info, cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Task<string> CreateTimer(TimerInfo timer, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
timer.Id = Guid.NewGuid().ToString("N");
|
|
|
|
|
|
|
|
|
|
ProgramInfo programInfo = null;
|
|
|
|
@ -601,9 +602,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
|
|
|
|
|
|
|
public Task UpdateTimerAsync(TimerInfo updatedTimer, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
var existingTimer = _timerProvider
|
|
|
|
|
.GetAll()
|
|
|
|
|
.FirstOrDefault(i => string.Equals(i.Id, updatedTimer.Id, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
var existingTimer = _timerProvider.GetTimer(updatedTimer.Id);
|
|
|
|
|
|
|
|
|
|
if (existingTimer == null)
|
|
|
|
|
{
|
|
|
|
@ -1137,6 +1136,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
|
|
_timerProvider.AddOrUpdate(timer, false);
|
|
|
|
|
|
|
|
|
|
SaveNfo(timer, recordPath, seriesPath);
|
|
|
|
|
EnforceKeepUpTo(timer);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var pathWithDuration = tunerHost.ApplyDuration(mediaStreamInfo.Path, duration);
|
|
|
|
@ -1148,8 +1148,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
|
|
mediaStreamInfo.RunTimeTicks = duration.Ticks;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await
|
|
|
|
|
recorder.Record(mediaStreamInfo, recordPath, duration, onStarted, cancellationToken)
|
|
|
|
|
await recorder.Record(mediaStreamInfo, recordPath, duration, onStarted, cancellationToken)
|
|
|
|
|
.ConfigureAwait(false);
|
|
|
|
|
|
|
|
|
|
recordingStatus = RecordingStatus.Completed;
|
|
|
|
@ -1195,6 +1194,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
|
|
}
|
|
|
|
|
else if (File.Exists(recordPath))
|
|
|
|
|
{
|
|
|
|
|
timer.RecordingPath = recordPath;
|
|
|
|
|
timer.Status = RecordingStatus.Completed;
|
|
|
|
|
_timerProvider.AddOrUpdate(timer, false);
|
|
|
|
|
OnSuccessfulRecording(timer, recordPath);
|
|
|
|
@ -1205,6 +1205,102 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async void EnforceKeepUpTo(TimerInfo timer)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(timer.SeriesTimerId))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var seriesTimerId = timer.SeriesTimerId;
|
|
|
|
|
var seriesTimer = _seriesTimerProvider.GetAll().FirstOrDefault(i => string.Equals(i.Id, seriesTimerId, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
|
|
|
|
if (seriesTimer == null || seriesTimer.KeepUpTo <= 1)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_disposed)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await _recordingDeleteSemaphore.WaitAsync().ConfigureAwait(false);
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_disposed)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var timersToDelete = _timerProvider.GetAll()
|
|
|
|
|
.Where(i => i.Status == RecordingStatus.Completed && !string.IsNullOrWhiteSpace(i.RecordingPath))
|
|
|
|
|
.Where(i => string.Equals(i.SeriesTimerId, seriesTimerId, StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
.OrderByDescending(i => i.EndDate)
|
|
|
|
|
.Where(i => File.Exists(i.RecordingPath))
|
|
|
|
|
.Skip(seriesTimer.KeepUpTo - 1)
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
await DeleteLibraryItemsForTimers(timersToDelete).ConfigureAwait(false);
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
_recordingDeleteSemaphore.Release();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private readonly SemaphoreSlim _recordingDeleteSemaphore = new SemaphoreSlim(1,1);
|
|
|
|
|
private async Task DeleteLibraryItemsForTimers(List<TimerInfo> timers)
|
|
|
|
|
{
|
|
|
|
|
foreach (var timer in timers)
|
|
|
|
|
{
|
|
|
|
|
if (_disposed)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await DeleteLibraryItemForTimer(timer).ConfigureAwait(false);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.ErrorException("Error deleting recording", ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task DeleteLibraryItemForTimer(TimerInfo timer)
|
|
|
|
|
{
|
|
|
|
|
var libraryItem = _libraryManager.FindByPath(timer.RecordingPath, false);
|
|
|
|
|
|
|
|
|
|
if (libraryItem != null)
|
|
|
|
|
{
|
|
|
|
|
await _libraryManager.DeleteItem(libraryItem, new DeleteOptions
|
|
|
|
|
{
|
|
|
|
|
DeleteFileLocation = true
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
File.Delete(timer.RecordingPath);
|
|
|
|
|
}
|
|
|
|
|
catch (DirectoryNotFoundException)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
catch (FileNotFoundException)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_timerProvider.Delete(timer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string EnsureFileUnique(string path, string timerId)
|
|
|
|
|
{
|
|
|
|
|
var originalPath = path;
|
|
|
|
@ -1460,9 +1556,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
|
|
{
|
|
|
|
|
foreach (var timer in allTimers)
|
|
|
|
|
{
|
|
|
|
|
var existingTimer = _timerProvider
|
|
|
|
|
.GetAll()
|
|
|
|
|
.FirstOrDefault(i => string.Equals(i.Id, timer.Id, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
var existingTimer = _timerProvider.GetTimer(timer.Id);
|
|
|
|
|
|
|
|
|
|
if (existingTimer == null)
|
|
|
|
|
{
|
|
|
|
@ -1484,6 +1578,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
|
|
{
|
|
|
|
|
existingTimer.Status = RecordingStatus.Cancelled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
existingTimer.SeriesTimerId = seriesTimer.Id;
|
|
|
|
|
_timerProvider.Update(existingTimer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1649,8 +1745,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
|
|
return channelIds.SelectMany(GetEpgDataForChannel).ToList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool _disposed;
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
_disposed = true;
|
|
|
|
|
foreach (var pair in _activeRecordings.ToList())
|
|
|
|
|
{
|
|
|
|
|
pair.Value.CancellationTokenSource.Cancel();
|
|
|
|
|