mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
implement keep up to
This commit is contained in:
parent
adb39f4090
commit
64d63c1b4b
@ -9,6 +9,7 @@ namespace MediaBrowser.Controller.LiveTv
|
|||||||
public TimerInfo()
|
public TimerInfo()
|
||||||
{
|
{
|
||||||
Genres = new List<string>();
|
Genres = new List<string>();
|
||||||
|
KeepUntil = KeepUntil.UntilDeleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -109,5 +110,7 @@ namespace MediaBrowser.Controller.LiveTv
|
|||||||
public string ShortOverview { get; set; }
|
public string ShortOverview { get; set; }
|
||||||
public string OfficialRating { get; set; }
|
public string OfficialRating { get; set; }
|
||||||
public List<string> Genres { get; set; }
|
public List<string> Genres { get; set; }
|
||||||
|
public string RecordingPath { get; set; }
|
||||||
|
public KeepUntil KeepUntil { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,5 +105,6 @@ namespace MediaBrowser.Model.LiveTv
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value><c>true</c> if this instance is post padding required; otherwise, <c>false</c>.</value>
|
/// <value><c>true</c> if this instance is post padding required; otherwise, <c>false</c>.</value>
|
||||||
public bool IsPostPaddingRequired { get; set; }
|
public bool IsPostPaddingRequired { get; set; }
|
||||||
|
public KeepUntil KeepUntil { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -27,7 +27,6 @@ namespace MediaBrowser.Model.LiveTv
|
|||||||
public bool RecordAnyChannel { get; set; }
|
public bool RecordAnyChannel { get; set; }
|
||||||
|
|
||||||
public int KeepUpTo { get; set; }
|
public int KeepUpTo { get; set; }
|
||||||
public KeepUntil KeepUntil { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether [record new only].
|
/// Gets or sets a value indicating whether [record new only].
|
||||||
|
@ -442,7 +442,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
|
|
||||||
private void CancelTimerInternal(string timerId, bool isSeriesCancelled)
|
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 (timer != null)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(timer.SeriesTimerId) || isSeriesCancelled)
|
if (string.IsNullOrWhiteSpace(timer.SeriesTimerId) || isSeriesCancelled)
|
||||||
@ -474,10 +474,20 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
return Task.FromResult(true);
|
return Task.FromResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task CreateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
public Task CreateTimerAsync(TimerInfo info, CancellationToken cancellationToken)
|
public Task CreateTimerAsync(TimerInfo info, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<string> CreateTimer(TimerInfo timer, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var existingTimer = _timerProvider.GetAll()
|
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)
|
if (existingTimer != null)
|
||||||
{
|
{
|
||||||
@ -485,6 +495,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
{
|
{
|
||||||
existingTimer.Status = RecordingStatus.New;
|
existingTimer.Status = RecordingStatus.New;
|
||||||
_timerProvider.Update(existingTimer);
|
_timerProvider.Update(existingTimer);
|
||||||
|
return Task.FromResult(existingTimer.Id);
|
||||||
}
|
}
|
||||||
else
|
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");
|
timer.Id = Guid.NewGuid().ToString("N");
|
||||||
|
|
||||||
ProgramInfo programInfo = null;
|
ProgramInfo programInfo = null;
|
||||||
@ -601,9 +602,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
|
|
||||||
public Task UpdateTimerAsync(TimerInfo updatedTimer, CancellationToken cancellationToken)
|
public Task UpdateTimerAsync(TimerInfo updatedTimer, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var existingTimer = _timerProvider
|
var existingTimer = _timerProvider.GetTimer(updatedTimer.Id);
|
||||||
.GetAll()
|
|
||||||
.FirstOrDefault(i => string.Equals(i.Id, updatedTimer.Id, StringComparison.OrdinalIgnoreCase));
|
|
||||||
|
|
||||||
if (existingTimer == null)
|
if (existingTimer == null)
|
||||||
{
|
{
|
||||||
@ -1137,6 +1136,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
_timerProvider.AddOrUpdate(timer, false);
|
_timerProvider.AddOrUpdate(timer, false);
|
||||||
|
|
||||||
SaveNfo(timer, recordPath, seriesPath);
|
SaveNfo(timer, recordPath, seriesPath);
|
||||||
|
EnforceKeepUpTo(timer);
|
||||||
};
|
};
|
||||||
|
|
||||||
var pathWithDuration = tunerHost.ApplyDuration(mediaStreamInfo.Path, duration);
|
var pathWithDuration = tunerHost.ApplyDuration(mediaStreamInfo.Path, duration);
|
||||||
@ -1148,8 +1148,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
mediaStreamInfo.RunTimeTicks = duration.Ticks;
|
mediaStreamInfo.RunTimeTicks = duration.Ticks;
|
||||||
}
|
}
|
||||||
|
|
||||||
await
|
await recorder.Record(mediaStreamInfo, recordPath, duration, onStarted, cancellationToken)
|
||||||
recorder.Record(mediaStreamInfo, recordPath, duration, onStarted, cancellationToken)
|
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
recordingStatus = RecordingStatus.Completed;
|
recordingStatus = RecordingStatus.Completed;
|
||||||
@ -1195,6 +1194,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
}
|
}
|
||||||
else if (File.Exists(recordPath))
|
else if (File.Exists(recordPath))
|
||||||
{
|
{
|
||||||
|
timer.RecordingPath = recordPath;
|
||||||
timer.Status = RecordingStatus.Completed;
|
timer.Status = RecordingStatus.Completed;
|
||||||
_timerProvider.AddOrUpdate(timer, false);
|
_timerProvider.AddOrUpdate(timer, false);
|
||||||
OnSuccessfulRecording(timer, recordPath);
|
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)
|
private string EnsureFileUnique(string path, string timerId)
|
||||||
{
|
{
|
||||||
var originalPath = path;
|
var originalPath = path;
|
||||||
@ -1460,9 +1556,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
{
|
{
|
||||||
foreach (var timer in allTimers)
|
foreach (var timer in allTimers)
|
||||||
{
|
{
|
||||||
var existingTimer = _timerProvider
|
var existingTimer = _timerProvider.GetTimer(timer.Id);
|
||||||
.GetAll()
|
|
||||||
.FirstOrDefault(i => string.Equals(i.Id, timer.Id, StringComparison.OrdinalIgnoreCase));
|
|
||||||
|
|
||||||
if (existingTimer == null)
|
if (existingTimer == null)
|
||||||
{
|
{
|
||||||
@ -1484,6 +1578,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
{
|
{
|
||||||
existingTimer.Status = RecordingStatus.Cancelled;
|
existingTimer.Status = RecordingStatus.Cancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
existingTimer.SeriesTimerId = seriesTimer.Id;
|
||||||
_timerProvider.Update(existingTimer);
|
_timerProvider.Update(existingTimer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1649,8 +1745,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
return channelIds.SelectMany(GetEpgDataForChannel).ToList();
|
return channelIds.SelectMany(GetEpgDataForChannel).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool _disposed;
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
_disposed = true;
|
||||||
foreach (var pair in _activeRecordings.ToList())
|
foreach (var pair in _activeRecordings.ToList())
|
||||||
{
|
{
|
||||||
pair.Value.CancellationTokenSource.Cancel();
|
pair.Value.CancellationTokenSource.Cancel();
|
||||||
|
@ -25,6 +25,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
timer.PostPaddingSeconds = series.PostPaddingSeconds;
|
timer.PostPaddingSeconds = series.PostPaddingSeconds;
|
||||||
timer.IsPostPaddingRequired = series.IsPostPaddingRequired;
|
timer.IsPostPaddingRequired = series.IsPostPaddingRequired;
|
||||||
timer.IsPrePaddingRequired = series.IsPrePaddingRequired;
|
timer.IsPrePaddingRequired = series.IsPrePaddingRequired;
|
||||||
|
timer.KeepUntil = series.KeepUntil;
|
||||||
timer.Priority = series.Priority;
|
timer.Priority = series.Priority;
|
||||||
timer.Name = parent.Name;
|
timer.Name = parent.Name;
|
||||||
timer.Overview = parent.Overview;
|
timer.Overview = parent.Overview;
|
||||||
|
@ -156,5 +156,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
EventHelper.FireEventIfNotNull(TimerFired, this, new GenericEventArgs<TimerInfo> { Argument = timer }, Logger);
|
EventHelper.FireEventIfNotNull(TimerFired, this, new GenericEventArgs<TimerInfo> { Argument = timer }, Logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TimerInfo GetTimer(string id)
|
||||||
|
{
|
||||||
|
return GetAll().FirstOrDefault(r => string.Equals(r.Id, id, StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||||||
PostPaddingSeconds = info.PostPaddingSeconds,
|
PostPaddingSeconds = info.PostPaddingSeconds,
|
||||||
IsPostPaddingRequired = info.IsPostPaddingRequired,
|
IsPostPaddingRequired = info.IsPostPaddingRequired,
|
||||||
IsPrePaddingRequired = info.IsPrePaddingRequired,
|
IsPrePaddingRequired = info.IsPrePaddingRequired,
|
||||||
|
KeepUntil = info.KeepUntil,
|
||||||
ExternalChannelId = info.ChannelId,
|
ExternalChannelId = info.ChannelId,
|
||||||
ExternalSeriesTimerId = info.SeriesTimerId,
|
ExternalSeriesTimerId = info.SeriesTimerId,
|
||||||
ServiceName = service.Name,
|
ServiceName = service.Name,
|
||||||
@ -247,6 +248,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||||||
PostPaddingSeconds = dto.PostPaddingSeconds,
|
PostPaddingSeconds = dto.PostPaddingSeconds,
|
||||||
IsPostPaddingRequired = dto.IsPostPaddingRequired,
|
IsPostPaddingRequired = dto.IsPostPaddingRequired,
|
||||||
IsPrePaddingRequired = dto.IsPrePaddingRequired,
|
IsPrePaddingRequired = dto.IsPrePaddingRequired,
|
||||||
|
KeepUntil = dto.KeepUntil,
|
||||||
Priority = dto.Priority,
|
Priority = dto.Priority,
|
||||||
SeriesTimerId = dto.ExternalSeriesTimerId,
|
SeriesTimerId = dto.ExternalSeriesTimerId,
|
||||||
ProgramId = dto.ExternalProgramId,
|
ProgramId = dto.ExternalProgramId,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user