|
@@ -103,8 +103,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
|
|
|
public event EventHandler<RecordingStatusChangedEventArgs> RecordingStatusChanged;
|
|
|
|
|
|
- private readonly ConcurrentDictionary<string, CancellationTokenSource> _activeRecordings =
|
|
|
- new ConcurrentDictionary<string, CancellationTokenSource>(StringComparer.OrdinalIgnoreCase);
|
|
|
+ private readonly ConcurrentDictionary<string, ActiveRecordingInfo> _activeRecordings =
|
|
|
+ new ConcurrentDictionary<string, ActiveRecordingInfo>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
|
|
public string Name
|
|
|
{
|
|
@@ -268,11 +268,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
{
|
|
|
_timerProvider.Delete(remove);
|
|
|
}
|
|
|
- CancellationTokenSource cancellationTokenSource;
|
|
|
+ ActiveRecordingInfo activeRecordingInfo;
|
|
|
|
|
|
- if (_activeRecordings.TryGetValue(timerId, out cancellationTokenSource))
|
|
|
+ if (_activeRecordings.TryGetValue(timerId, out activeRecordingInfo))
|
|
|
{
|
|
|
- cancellationTokenSource.Cancel();
|
|
|
+ activeRecordingInfo.CancellationTokenSource.Cancel();
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -653,11 +653,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- var cancellationTokenSource = new CancellationTokenSource();
|
|
|
+ var activeRecordingInfo = new ActiveRecordingInfo
|
|
|
+ {
|
|
|
+ CancellationTokenSource = new CancellationTokenSource(),
|
|
|
+ TimerId = timer.Id
|
|
|
+ };
|
|
|
|
|
|
- if (_activeRecordings.TryAdd(timer.Id, cancellationTokenSource))
|
|
|
+ if (_activeRecordings.TryAdd(timer.Id, activeRecordingInfo))
|
|
|
{
|
|
|
- await RecordStream(timer, recordingEndDate, cancellationTokenSource.Token).ConfigureAwait(false);
|
|
|
+ await RecordStream(timer, recordingEndDate, activeRecordingInfo, activeRecordingInfo.CancellationTokenSource.Token).ConfigureAwait(false);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
@@ -674,7 +678,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate, CancellationToken cancellationToken)
|
|
|
+ private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate, ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken)
|
|
|
{
|
|
|
if (timer == null)
|
|
|
{
|
|
@@ -729,8 +733,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
var recordingFileName = _fileSystem.GetValidFilename(RecordingHelper.GetRecordingName(timer, info)).Trim() + ".ts";
|
|
|
|
|
|
recordPath = Path.Combine(recordPath, recordingFileName);
|
|
|
- recordPath = EnsureFileUnique(recordPath);
|
|
|
- _fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath));
|
|
|
|
|
|
var recordingId = info.Id.GetMD5().ToString("N");
|
|
|
var recording = _recordingProvider.GetAll().FirstOrDefault(x => string.Equals(x.Id, recordingId, StringComparison.OrdinalIgnoreCase));
|
|
@@ -788,6 +790,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
{
|
|
|
recordPath = Path.ChangeExtension(recordPath, ".mp4");
|
|
|
}
|
|
|
+ recordPath = EnsureFileUnique(recordPath, timer.Id);
|
|
|
+ _fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath));
|
|
|
+ activeRecordingInfo.Path = recordPath;
|
|
|
|
|
|
_libraryMonitor.ReportFileSystemChangeBeginning(recordPath);
|
|
|
|
|
@@ -798,9 +803,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
|
|
|
_logger.Info("Beginning recording. Will record for {0} minutes.", duration.TotalMinutes.ToString(CultureInfo.InvariantCulture));
|
|
|
|
|
|
- var durationToken = new CancellationTokenSource(duration);
|
|
|
- var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
|
|
|
-
|
|
|
_logger.Info("Writing file to path: " + recordPath);
|
|
|
_logger.Info("Opening recording stream from tuner provider");
|
|
|
|
|
@@ -810,10 +812,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
isResourceOpen = false;
|
|
|
};
|
|
|
|
|
|
- await recorder.Record(mediaStreamInfo, recordPath, onStarted, linkedToken).ConfigureAwait(false);
|
|
|
+ await recorder.Record(mediaStreamInfo, recordPath, duration, onStarted, cancellationToken).ConfigureAwait(false);
|
|
|
|
|
|
recording.Status = RecordingStatus.Completed;
|
|
|
- _logger.Info("Recording completed");
|
|
|
+ _logger.Info("Recording completed: {0}", recordPath);
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
@@ -827,17 +829,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
}
|
|
|
catch (OperationCanceledException)
|
|
|
{
|
|
|
- _logger.Info("Recording stopped");
|
|
|
+ _logger.Info("Recording stopped: {0}", recordPath);
|
|
|
recording.Status = RecordingStatus.Completed;
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
- _logger.ErrorException("Error recording", ex);
|
|
|
+ _logger.ErrorException("Error recording to {0}", ex, recordPath);
|
|
|
recording.Status = RecordingStatus.Error;
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
|
- CancellationTokenSource removed;
|
|
|
+ ActiveRecordingInfo removed;
|
|
|
_activeRecordings.TryRemove(timer.Id, out removed);
|
|
|
}
|
|
|
|
|
@@ -863,12 +865,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private string EnsureFileUnique(string path)
|
|
|
+ private string EnsureFileUnique(string path, string timerId)
|
|
|
{
|
|
|
var originalPath = path;
|
|
|
var index = 1;
|
|
|
|
|
|
- while (_fileSystem.FileExists(path))
|
|
|
+ while (FileExists(path, timerId))
|
|
|
{
|
|
|
var parent = Path.GetDirectoryName(originalPath);
|
|
|
var name = Path.GetFileNameWithoutExtension(originalPath);
|
|
@@ -881,6 +883,22 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
return path;
|
|
|
}
|
|
|
|
|
|
+ private bool FileExists(string path, string timerId)
|
|
|
+ {
|
|
|
+ if (_fileSystem.FileExists(path))
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ var hasRecordingAtPath = _activeRecordings.Values.ToList().Any(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) && !string.Equals(i.TimerId, timerId, StringComparison.OrdinalIgnoreCase));
|
|
|
+
|
|
|
+ if (hasRecordingAtPath)
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
private async Task<IRecorder> GetRecorder()
|
|
|
{
|
|
|
if (GetConfiguration().EnableRecordingEncoding)
|
|
@@ -1064,7 +1082,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
{
|
|
|
foreach (var pair in _activeRecordings.ToList())
|
|
|
{
|
|
|
- pair.Value.Cancel();
|
|
|
+ pair.Value.CancellationTokenSource.Cancel();
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1081,5 +1099,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
|
|
IsRegistered = true
|
|
|
});
|
|
|
}
|
|
|
+
|
|
|
+ class ActiveRecordingInfo
|
|
|
+ {
|
|
|
+ public string Path { get; set; }
|
|
|
+ public string TimerId { get; set; }
|
|
|
+ public CancellationTokenSource CancellationTokenSource { get; set; }
|
|
|
+ }
|
|
|
}
|
|
|
}
|