mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-11-17 19:08:53 -07:00
commit
35dc17edaa
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using MediaBrowser.Model.LiveTv;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.LiveTv
|
namespace MediaBrowser.Controller.LiveTv
|
||||||
{
|
{
|
||||||
@ -54,6 +55,7 @@ namespace MediaBrowser.Controller.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; }
|
||||||
|
|
||||||
public bool SkipEpisodesInLibrary { get; set; }
|
public bool SkipEpisodesInLibrary { get; set; }
|
||||||
|
|
||||||
@ -109,6 +111,7 @@ namespace MediaBrowser.Controller.LiveTv
|
|||||||
{
|
{
|
||||||
Days = new List<DayOfWeek>();
|
Days = new List<DayOfWeek>();
|
||||||
SkipEpisodesInLibrary = true;
|
SkipEpisodesInLibrary = true;
|
||||||
|
KeepUntil = KeepUntil.UntilDeleted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ 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].
|
||||||
@ -68,4 +69,12 @@ namespace MediaBrowser.Model.LiveTv
|
|||||||
Days = new List<DayOfWeek>();
|
Days = new List<DayOfWeek>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum KeepUntil
|
||||||
|
{
|
||||||
|
UntilDeleted,
|
||||||
|
UntilSpaceNeeded,
|
||||||
|
UntilWatched,
|
||||||
|
UntilDate
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return new[] { ".srt", ".ssa", ".ass", ".sub" };
|
return new[] { ".srt", ".ssa", ".ass", ".sub", ".smi", ".sami" };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,11 +329,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
foreach (var timer in timers.ToList())
|
foreach (var timer in timers.ToList())
|
||||||
{
|
{
|
||||||
if (DateTime.UtcNow > timer.EndDate && !_activeRecordings.ContainsKey(timer.Id))
|
if (DateTime.UtcNow > timer.EndDate && !_activeRecordings.ContainsKey(timer.Id))
|
||||||
|
{
|
||||||
|
OnTimerOutOfDate(timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTimerOutOfDate(TimerInfo timer)
|
||||||
{
|
{
|
||||||
_timerProvider.Delete(timer);
|
_timerProvider.Delete(timer);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ChannelInfo> _channelCache = null;
|
private List<ChannelInfo> _channelCache = null;
|
||||||
private async Task<IEnumerable<ChannelInfo>> GetChannelsAsync(bool enableCache, CancellationToken cancellationToken)
|
private async Task<IEnumerable<ChannelInfo>> GetChannelsAsync(bool enableCache, CancellationToken cancellationToken)
|
||||||
@ -424,7 +429,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
|
|
||||||
foreach (var timer in timers)
|
foreach (var timer in timers)
|
||||||
{
|
{
|
||||||
CancelTimerInternal(timer.Id);
|
CancelTimerInternal(timer.Id, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
var remove = _seriesTimerProvider.GetAll().FirstOrDefault(r => string.Equals(r.Id, timerId, StringComparison.OrdinalIgnoreCase));
|
var remove = _seriesTimerProvider.GetAll().FirstOrDefault(r => string.Equals(r.Id, timerId, StringComparison.OrdinalIgnoreCase));
|
||||||
@ -435,12 +440,20 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
return Task.FromResult(true);
|
return Task.FromResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CancelTimerInternal(string timerId)
|
private void CancelTimerInternal(string timerId, bool isSeriesCancelled)
|
||||||
{
|
{
|
||||||
var remove = _timerProvider.GetAll().FirstOrDefault(r => string.Equals(r.Id, timerId, StringComparison.OrdinalIgnoreCase));
|
var timer = _timerProvider.GetAll().FirstOrDefault(r => string.Equals(r.Id, timerId, StringComparison.OrdinalIgnoreCase));
|
||||||
if (remove != null)
|
if (timer != null)
|
||||||
{
|
{
|
||||||
_timerProvider.Delete(remove);
|
if (string.IsNullOrWhiteSpace(timer.SeriesTimerId) || isSeriesCancelled)
|
||||||
|
{
|
||||||
|
_timerProvider.Delete(timer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
timer.Status = RecordingStatus.Cancelled;
|
||||||
|
_timerProvider.AddOrUpdate(timer, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ActiveRecordingInfo activeRecordingInfo;
|
ActiveRecordingInfo activeRecordingInfo;
|
||||||
|
|
||||||
@ -452,7 +465,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
|
|
||||||
public Task CancelTimerAsync(string timerId, CancellationToken cancellationToken)
|
public Task CancelTimerAsync(string timerId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
CancelTimerInternal(timerId);
|
CancelTimerInternal(timerId, false);
|
||||||
return Task.FromResult(true);
|
return Task.FromResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -463,6 +476,22 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
|
|
||||||
public Task CreateTimerAsync(TimerInfo info, CancellationToken cancellationToken)
|
public Task CreateTimerAsync(TimerInfo info, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
var existingTimer = _timerProvider.GetAll()
|
||||||
|
.FirstOrDefault(i => string.Equals(info.ProgramId, i.ProgramId, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
if (existingTimer != null)
|
||||||
|
{
|
||||||
|
if (existingTimer.Status == RecordingStatus.Cancelled)
|
||||||
|
{
|
||||||
|
existingTimer.Status = RecordingStatus.New;
|
||||||
|
_timerProvider.Update(existingTimer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException("A scheduled recording already exists for this program.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return CreateTimer(info, cancellationToken);
|
return CreateTimer(info, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -549,6 +578,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
instance.RecordNewOnly = info.RecordNewOnly;
|
instance.RecordNewOnly = info.RecordNewOnly;
|
||||||
instance.SkipEpisodesInLibrary = info.SkipEpisodesInLibrary;
|
instance.SkipEpisodesInLibrary = info.SkipEpisodesInLibrary;
|
||||||
instance.KeepUpTo = info.KeepUpTo;
|
instance.KeepUpTo = info.KeepUpTo;
|
||||||
|
instance.KeepUntil = info.KeepUntil;
|
||||||
instance.StartDate = info.StartDate;
|
instance.StartDate = info.StartDate;
|
||||||
|
|
||||||
_seriesTimerProvider.Update(instance);
|
_seriesTimerProvider.Update(instance);
|
||||||
@ -569,12 +599,54 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task UpdateTimerAsync(TimerInfo info, CancellationToken cancellationToken)
|
public Task UpdateTimerAsync(TimerInfo updatedTimer, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_timerProvider.Update(info);
|
var existingTimer = _timerProvider
|
||||||
|
.GetAll()
|
||||||
|
.FirstOrDefault(i => string.Equals(i.Id, updatedTimer.Id, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
if (existingTimer == null)
|
||||||
|
{
|
||||||
|
throw new ResourceNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only update if not currently active
|
||||||
|
ActiveRecordingInfo activeRecordingInfo;
|
||||||
|
if (!_activeRecordings.TryGetValue(updatedTimer.Id, out activeRecordingInfo))
|
||||||
|
{
|
||||||
|
UpdateExistingTimerWithNewData(existingTimer, updatedTimer);
|
||||||
|
|
||||||
|
_timerProvider.Update(existingTimer);
|
||||||
|
}
|
||||||
|
|
||||||
return Task.FromResult(true);
|
return Task.FromResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateExistingTimerWithNewData(TimerInfo existingTimer, TimerInfo updatedTimer)
|
||||||
|
{
|
||||||
|
// Update the program info but retain the status
|
||||||
|
existingTimer.ChannelId = updatedTimer.ChannelId;
|
||||||
|
existingTimer.CommunityRating = updatedTimer.CommunityRating;
|
||||||
|
existingTimer.EndDate = updatedTimer.EndDate;
|
||||||
|
existingTimer.EpisodeNumber = updatedTimer.EpisodeNumber;
|
||||||
|
existingTimer.EpisodeTitle = updatedTimer.EpisodeTitle;
|
||||||
|
existingTimer.Genres = updatedTimer.Genres;
|
||||||
|
existingTimer.HomePageUrl = updatedTimer.HomePageUrl;
|
||||||
|
existingTimer.IsKids = updatedTimer.IsKids;
|
||||||
|
existingTimer.IsMovie = updatedTimer.IsMovie;
|
||||||
|
existingTimer.IsProgramSeries = updatedTimer.IsProgramSeries;
|
||||||
|
existingTimer.IsSports = updatedTimer.IsSports;
|
||||||
|
existingTimer.Name = updatedTimer.Name;
|
||||||
|
existingTimer.OfficialRating = updatedTimer.OfficialRating;
|
||||||
|
existingTimer.OriginalAirDate = updatedTimer.OriginalAirDate;
|
||||||
|
existingTimer.Overview = updatedTimer.Overview;
|
||||||
|
existingTimer.ProductionYear = updatedTimer.ProductionYear;
|
||||||
|
existingTimer.ProgramId = updatedTimer.ProgramId;
|
||||||
|
existingTimer.SeasonNumber = updatedTimer.SeasonNumber;
|
||||||
|
existingTimer.ShortOverview = updatedTimer.ShortOverview;
|
||||||
|
existingTimer.StartDate = updatedTimer.StartDate;
|
||||||
|
}
|
||||||
|
|
||||||
public Task<ImageStream> GetChannelImageAsync(string channelId, CancellationToken cancellationToken)
|
public Task<ImageStream> GetChannelImageAsync(string channelId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
@ -597,7 +669,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
|
|
||||||
public Task<IEnumerable<TimerInfo>> GetTimersAsync(CancellationToken cancellationToken)
|
public Task<IEnumerable<TimerInfo>> GetTimersAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return Task.FromResult((IEnumerable<TimerInfo>)_timerProvider.GetAll());
|
var excludeStatues = new List<RecordingStatus>
|
||||||
|
{
|
||||||
|
RecordingStatus.Completed,
|
||||||
|
RecordingStatus.Cancelled
|
||||||
|
};
|
||||||
|
|
||||||
|
var timers = _timerProvider.GetAll()
|
||||||
|
.Where(i => !excludeStatues.Contains(i.Status));
|
||||||
|
|
||||||
|
return Task.FromResult(timers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<SeriesTimerInfo> GetNewTimerDefaultsAsync(CancellationToken cancellationToken, ProgramInfo program = null)
|
public Task<SeriesTimerInfo> GetNewTimerDefaultsAsync(CancellationToken cancellationToken, ProgramInfo program = null)
|
||||||
@ -630,6 +711,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
defaults.ProgramId = program.Id;
|
defaults.ProgramId = program.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defaults.SkipEpisodesInLibrary = true;
|
||||||
|
defaults.KeepUntil = KeepUntil.UntilDeleted;
|
||||||
|
|
||||||
return Task.FromResult(defaults);
|
return Task.FromResult(defaults);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -860,8 +944,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
|
|
||||||
if (recordingEndDate <= DateTime.UtcNow)
|
if (recordingEndDate <= DateTime.UtcNow)
|
||||||
{
|
{
|
||||||
_logger.Warn("Recording timer fired for timer {0}, Id: {1}, but the program has already ended.", timer.Name, timer.Id);
|
_logger.Warn("Recording timer fired for updatedTimer {0}, Id: {1}, but the program has already ended.", timer.Name, timer.Id);
|
||||||
_timerProvider.Delete(timer);
|
OnTimerOutOfDate(timer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -982,7 +1066,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
return Path.Combine(recordPath, recordingFileName);
|
return Path.Combine(recordPath, recordingFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate, ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken)
|
private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate,
|
||||||
|
ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (timer == null)
|
if (timer == null)
|
||||||
{
|
{
|
||||||
@ -1014,9 +1099,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var allMediaSources = await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false);
|
var allMediaSources =
|
||||||
|
await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|
||||||
var liveStreamInfo = await GetChannelStreamInternal(timer.ChannelId, allMediaSources[0].Id, CancellationToken.None).ConfigureAwait(false);
|
var liveStreamInfo =
|
||||||
|
await
|
||||||
|
GetChannelStreamInternal(timer.ChannelId, allMediaSources[0].Id, CancellationToken.None)
|
||||||
|
.ConfigureAwait(false);
|
||||||
liveStream = liveStreamInfo.Item1;
|
liveStream = liveStreamInfo.Item1;
|
||||||
var mediaStreamInfo = liveStreamInfo.Item1.PublicMediaSource;
|
var mediaStreamInfo = liveStreamInfo.Item1.PublicMediaSource;
|
||||||
var tunerHost = liveStreamInfo.Item2;
|
var tunerHost = liveStreamInfo.Item2;
|
||||||
@ -1036,7 +1125,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
|
|
||||||
var duration = recordingEndDate - DateTime.UtcNow;
|
var duration = recordingEndDate - DateTime.UtcNow;
|
||||||
|
|
||||||
_logger.Info("Beginning recording. Will record for {0} minutes.", duration.TotalMinutes.ToString(CultureInfo.InvariantCulture));
|
_logger.Info("Beginning recording. Will record for {0} minutes.",
|
||||||
|
duration.TotalMinutes.ToString(CultureInfo.InvariantCulture));
|
||||||
|
|
||||||
_logger.Info("Writing file to path: " + recordPath);
|
_logger.Info("Writing file to path: " + recordPath);
|
||||||
_logger.Info("Opening recording stream from tuner provider");
|
_logger.Info("Opening recording stream from tuner provider");
|
||||||
@ -1058,7 +1148,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
mediaStreamInfo.RunTimeTicks = duration.Ticks;
|
mediaStreamInfo.RunTimeTicks = duration.Ticks;
|
||||||
}
|
}
|
||||||
|
|
||||||
await recorder.Record(mediaStreamInfo, recordPath, duration, onStarted, cancellationToken).ConfigureAwait(false);
|
await
|
||||||
|
recorder.Record(mediaStreamInfo, recordPath, duration, onStarted, cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
recordingStatus = RecordingStatus.Completed;
|
recordingStatus = RecordingStatus.Completed;
|
||||||
_logger.Info("Recording completed: {0}", recordPath);
|
_logger.Info("Recording completed: {0}", recordPath);
|
||||||
@ -1092,14 +1184,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
ActiveRecordingInfo removed;
|
ActiveRecordingInfo removed;
|
||||||
_activeRecordings.TryRemove(timer.Id, out removed);
|
_activeRecordings.TryRemove(timer.Id, out removed);
|
||||||
|
|
||||||
if (recordingStatus == RecordingStatus.Completed)
|
if (recordingStatus != RecordingStatus.Completed && DateTime.UtcNow < timer.EndDate)
|
||||||
{
|
|
||||||
timer.Status = RecordingStatus.Completed;
|
|
||||||
_timerProvider.Delete(timer);
|
|
||||||
|
|
||||||
OnSuccessfulRecording(timer, recordPath);
|
|
||||||
}
|
|
||||||
else if (DateTime.UtcNow < timer.EndDate)
|
|
||||||
{
|
{
|
||||||
const int retryIntervalSeconds = 60;
|
const int retryIntervalSeconds = 60;
|
||||||
_logger.Info("Retrying recording in {0} seconds.", retryIntervalSeconds);
|
_logger.Info("Retrying recording in {0} seconds.", retryIntervalSeconds);
|
||||||
@ -1108,6 +1193,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
timer.StartDate = DateTime.UtcNow.AddSeconds(retryIntervalSeconds);
|
timer.StartDate = DateTime.UtcNow.AddSeconds(retryIntervalSeconds);
|
||||||
_timerProvider.AddOrUpdate(timer);
|
_timerProvider.AddOrUpdate(timer);
|
||||||
}
|
}
|
||||||
|
else if (File.Exists(recordPath))
|
||||||
|
{
|
||||||
|
timer.Status = RecordingStatus.Completed;
|
||||||
|
_timerProvider.AddOrUpdate(timer, false);
|
||||||
|
OnSuccessfulRecording(timer, recordPath);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_timerProvider.Delete(timer);
|
_timerProvider.Delete(timer);
|
||||||
@ -1353,41 +1444,78 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
return _config.GetConfiguration<LiveTvOptions>("livetv");
|
return _config.GetConfiguration<LiveTvOptions>("livetv");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool ShouldCancelTimerForSeriesTimer(SeriesTimerInfo seriesTimer, TimerInfo timer)
|
||||||
|
{
|
||||||
|
return seriesTimer.SkipEpisodesInLibrary && IsProgramAlreadyInLibrary(timer);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task UpdateTimersForSeriesTimer(List<ProgramInfo> epgData, SeriesTimerInfo seriesTimer, bool deleteInvalidTimers)
|
private async Task UpdateTimersForSeriesTimer(List<ProgramInfo> epgData, SeriesTimerInfo seriesTimer, bool deleteInvalidTimers)
|
||||||
{
|
{
|
||||||
var newTimers = GetTimersForSeries(seriesTimer, epgData, true).ToList();
|
var allTimers = GetTimersForSeries(seriesTimer, epgData)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
var registration = await _liveTvManager.GetRegistrationInfo("seriesrecordings").ConfigureAwait(false);
|
var registration = await _liveTvManager.GetRegistrationInfo("seriesrecordings").ConfigureAwait(false);
|
||||||
|
|
||||||
if (registration.IsValid)
|
if (registration.IsValid)
|
||||||
{
|
{
|
||||||
foreach (var timer in newTimers)
|
foreach (var timer in allTimers)
|
||||||
{
|
{
|
||||||
_timerProvider.AddOrUpdate(timer);
|
var existingTimer = _timerProvider
|
||||||
|
.GetAll()
|
||||||
|
.FirstOrDefault(i => string.Equals(i.Id, timer.Id, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
if (existingTimer == null)
|
||||||
|
{
|
||||||
|
if (ShouldCancelTimerForSeriesTimer(seriesTimer, timer))
|
||||||
|
{
|
||||||
|
timer.Status = RecordingStatus.Cancelled;
|
||||||
|
}
|
||||||
|
_timerProvider.Add(timer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Only update if not currently active
|
||||||
|
ActiveRecordingInfo activeRecordingInfo;
|
||||||
|
if (!_activeRecordings.TryGetValue(timer.Id, out activeRecordingInfo))
|
||||||
|
{
|
||||||
|
UpdateExistingTimerWithNewData(existingTimer, timer);
|
||||||
|
|
||||||
|
if (ShouldCancelTimerForSeriesTimer(seriesTimer, timer))
|
||||||
|
{
|
||||||
|
existingTimer.Status = RecordingStatus.Cancelled;
|
||||||
|
}
|
||||||
|
_timerProvider.Update(existingTimer);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deleteInvalidTimers)
|
if (deleteInvalidTimers)
|
||||||
{
|
{
|
||||||
var allTimers = GetTimersForSeries(seriesTimer, epgData, false)
|
var allTimerIds = allTimers
|
||||||
.Select(i => i.Id)
|
.Select(i => i.Id)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
var deleteStatuses = new List<RecordingStatus>
|
||||||
|
{
|
||||||
|
RecordingStatus.New
|
||||||
|
};
|
||||||
|
|
||||||
var deletes = _timerProvider.GetAll()
|
var deletes = _timerProvider.GetAll()
|
||||||
.Where(i => string.Equals(i.SeriesTimerId, seriesTimer.Id, StringComparison.OrdinalIgnoreCase))
|
.Where(i => string.Equals(i.SeriesTimerId, seriesTimer.Id, StringComparison.OrdinalIgnoreCase))
|
||||||
.Where(i => !allTimers.Contains(i.Id, StringComparer.OrdinalIgnoreCase) && i.StartDate > DateTime.UtcNow)
|
.Where(i => !allTimerIds.Contains(i.Id, StringComparer.OrdinalIgnoreCase) && i.StartDate > DateTime.UtcNow)
|
||||||
|
.Where(i => deleteStatuses.Contains(i.Status))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
foreach (var timer in deletes)
|
foreach (var timer in deletes)
|
||||||
{
|
{
|
||||||
await CancelTimerAsync(timer.Id, CancellationToken.None).ConfigureAwait(false);
|
CancelTimerInternal(timer.Id, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<TimerInfo> GetTimersForSeries(SeriesTimerInfo seriesTimer,
|
private IEnumerable<TimerInfo> GetTimersForSeries(SeriesTimerInfo seriesTimer,
|
||||||
IEnumerable<ProgramInfo> allPrograms,
|
IEnumerable<ProgramInfo> allPrograms)
|
||||||
bool filterByCurrentRecordings)
|
|
||||||
{
|
{
|
||||||
if (seriesTimer == null)
|
if (seriesTimer == null)
|
||||||
{
|
{
|
||||||
@ -1403,15 +1531,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
|
|
||||||
allPrograms = GetProgramsForSeries(seriesTimer, allPrograms);
|
allPrograms = GetProgramsForSeries(seriesTimer, allPrograms);
|
||||||
|
|
||||||
if (filterByCurrentRecordings && seriesTimer.SkipEpisodesInLibrary)
|
|
||||||
{
|
|
||||||
allPrograms = allPrograms.Where(i => !IsProgramAlreadyInLibrary(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
return allPrograms.Select(i => RecordingHelper.CreateTimer(i, seriesTimer));
|
return allPrograms.Select(i => RecordingHelper.CreateTimer(i, seriesTimer));
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsProgramAlreadyInLibrary(ProgramInfo program)
|
private bool IsProgramAlreadyInLibrary(TimerInfo program)
|
||||||
{
|
{
|
||||||
if ((program.EpisodeNumber.HasValue && program.SeasonNumber.HasValue) || !string.IsNullOrWhiteSpace(program.EpisodeTitle))
|
if ((program.EpisodeNumber.HasValue && program.SeasonNumber.HasValue) || !string.IsNullOrWhiteSpace(program.EpisodeTitle))
|
||||||
{
|
{
|
||||||
|
@ -32,7 +32,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
|
|
||||||
foreach (var item in GetAll().ToList())
|
foreach (var item in GetAll().ToList())
|
||||||
{
|
{
|
||||||
AddTimer(item);
|
AddOrUpdateSystemTimer(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,17 +55,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
public override void Update(TimerInfo item)
|
public override void Update(TimerInfo item)
|
||||||
{
|
{
|
||||||
base.Update(item);
|
base.Update(item);
|
||||||
|
AddOrUpdateSystemTimer(item);
|
||||||
Timer timer;
|
|
||||||
if (_timers.TryGetValue(item.Id, out timer))
|
|
||||||
{
|
|
||||||
var timespan = RecordingHelper.GetStartTime(item) - DateTime.UtcNow;
|
|
||||||
timer.Change(timespan, TimeSpan.Zero);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AddTimer(item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddOrUpdate(TimerInfo item, bool resetTimer)
|
public void AddOrUpdate(TimerInfo item, bool resetTimer)
|
||||||
@ -96,12 +86,25 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
}
|
}
|
||||||
|
|
||||||
base.Add(item);
|
base.Add(item);
|
||||||
AddTimer(item);
|
AddOrUpdateSystemTimer(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddTimer(TimerInfo item)
|
private bool ShouldStartTimer(TimerInfo item)
|
||||||
{
|
{
|
||||||
if (item.Status == RecordingStatus.Completed)
|
if (item.Status == RecordingStatus.Completed ||
|
||||||
|
item.Status == RecordingStatus.Cancelled)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddOrUpdateSystemTimer(TimerInfo item)
|
||||||
|
{
|
||||||
|
StopTimer(item);
|
||||||
|
|
||||||
|
if (!ShouldStartTimer(item))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -115,14 +118,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var timerLength = startDate - now;
|
var dueTime = startDate - now;
|
||||||
StartTimer(item, timerLength);
|
StartTimer(item, dueTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StartTimer(TimerInfo item, TimeSpan dueTime)
|
private void StartTimer(TimerInfo item, TimeSpan dueTime)
|
||||||
{
|
{
|
||||||
StopTimer(item);
|
|
||||||
|
|
||||||
var timer = new Timer(TimerCallback, item.Id, dueTime, TimeSpan.Zero);
|
var timer = new Timer(TimerCallback, item.Id, dueTime, TimeSpan.Zero);
|
||||||
|
|
||||||
if (_timers.TryAdd(item.Id, timer))
|
if (_timers.TryAdd(item.Id, timer))
|
||||||
|
@ -102,6 +102,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||||||
RecordAnyTime = info.RecordAnyTime,
|
RecordAnyTime = info.RecordAnyTime,
|
||||||
SkipEpisodesInLibrary = info.SkipEpisodesInLibrary,
|
SkipEpisodesInLibrary = info.SkipEpisodesInLibrary,
|
||||||
KeepUpTo = info.KeepUpTo,
|
KeepUpTo = info.KeepUpTo,
|
||||||
|
KeepUntil = info.KeepUntil,
|
||||||
RecordNewOnly = info.RecordNewOnly,
|
RecordNewOnly = info.RecordNewOnly,
|
||||||
ExternalChannelId = info.ChannelId,
|
ExternalChannelId = info.ChannelId,
|
||||||
ExternalProgramId = info.ProgramId,
|
ExternalProgramId = info.ProgramId,
|
||||||
@ -312,6 +313,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||||||
RecordAnyTime = dto.RecordAnyTime,
|
RecordAnyTime = dto.RecordAnyTime,
|
||||||
SkipEpisodesInLibrary = dto.SkipEpisodesInLibrary,
|
SkipEpisodesInLibrary = dto.SkipEpisodesInLibrary,
|
||||||
KeepUpTo = dto.KeepUpTo,
|
KeepUpTo = dto.KeepUpTo,
|
||||||
|
KeepUntil = dto.KeepUntil,
|
||||||
RecordNewOnly = dto.RecordNewOnly,
|
RecordNewOnly = dto.RecordNewOnly,
|
||||||
ProgramId = dto.ExternalProgramId,
|
ProgramId = dto.ExternalProgramId,
|
||||||
ChannelId = dto.ExternalChannelId,
|
ChannelId = dto.ExternalChannelId,
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
</nlog>
|
</nlog>
|
||||||
<appSettings>
|
<appSettings>
|
||||||
<add key="DebugProgramDataPath" value="ProgramData-Server"/>
|
<add key="DebugProgramDataPath" value="ProgramData-Server"/>
|
||||||
<add key="ReleaseProgramDataPath" value="%ApplicationData%/emby"/>
|
<add key="ReleaseProgramDataPath" value="ProgramData-Server"/>
|
||||||
</appSettings>
|
</appSettings>
|
||||||
<runtime>
|
<runtime>
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
@ -1219,7 +1219,7 @@ namespace MediaBrowser.Server.Startup.Common
|
|||||||
var apiUrl = GetLocalApiUrl(address);
|
var apiUrl = GetLocalApiUrl(address);
|
||||||
apiUrl += "/system/ping";
|
apiUrl += "/system/ping";
|
||||||
|
|
||||||
if ((DateTime.UtcNow - _lastAddressCacheClear).TotalMinutes >= 5)
|
if ((DateTime.UtcNow - _lastAddressCacheClear).TotalMinutes >= 10)
|
||||||
{
|
{
|
||||||
_lastAddressCacheClear = DateTime.UtcNow;
|
_lastAddressCacheClear = DateTime.UtcNow;
|
||||||
_validAddressResults.Clear();
|
_validAddressResults.Clear();
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>MediaBrowser.Common.Internal</id>
|
<id>MediaBrowser.Common.Internal</id>
|
||||||
<version>3.0.658</version>
|
<version>3.0.659</version>
|
||||||
<title>MediaBrowser.Common.Internal</title>
|
<title>MediaBrowser.Common.Internal</title>
|
||||||
<authors>Luke</authors>
|
<authors>Luke</authors>
|
||||||
<owners>ebr,Luke,scottisafool</owners>
|
<owners>ebr,Luke,scottisafool</owners>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description>
|
<description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description>
|
||||||
<copyright>Copyright © Emby 2013</copyright>
|
<copyright>Copyright © Emby 2013</copyright>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency id="MediaBrowser.Common" version="3.0.658" />
|
<dependency id="MediaBrowser.Common" version="3.0.659" />
|
||||||
<dependency id="NLog" version="4.3.8" />
|
<dependency id="NLog" version="4.3.8" />
|
||||||
<dependency id="SimpleInjector" version="3.2.2" />
|
<dependency id="SimpleInjector" version="3.2.2" />
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>MediaBrowser.Common</id>
|
<id>MediaBrowser.Common</id>
|
||||||
<version>3.0.658</version>
|
<version>3.0.659</version>
|
||||||
<title>MediaBrowser.Common</title>
|
<title>MediaBrowser.Common</title>
|
||||||
<authors>Emby Team</authors>
|
<authors>Emby Team</authors>
|
||||||
<owners>ebr,Luke,scottisafool</owners>
|
<owners>ebr,Luke,scottisafool</owners>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>MediaBrowser.Server.Core</id>
|
<id>MediaBrowser.Server.Core</id>
|
||||||
<version>3.0.658</version>
|
<version>3.0.659</version>
|
||||||
<title>Media Browser.Server.Core</title>
|
<title>Media Browser.Server.Core</title>
|
||||||
<authors>Emby Team</authors>
|
<authors>Emby Team</authors>
|
||||||
<owners>ebr,Luke,scottisafool</owners>
|
<owners>ebr,Luke,scottisafool</owners>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<description>Contains core components required to build plugins for Emby Server.</description>
|
<description>Contains core components required to build plugins for Emby Server.</description>
|
||||||
<copyright>Copyright © Emby 2013</copyright>
|
<copyright>Copyright © Emby 2013</copyright>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency id="MediaBrowser.Common" version="3.0.658" />
|
<dependency id="MediaBrowser.Common" version="3.0.659" />
|
||||||
<dependency id="Interfaces.IO" version="1.0.0.5" />
|
<dependency id="Interfaces.IO" version="1.0.0.5" />
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
Loading…
Reference in New Issue
Block a user