2
0
Luke Pulverenti 8 жил өмнө
parent
commit
ab9acdc771

+ 126 - 16
Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -1311,7 +1311,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                     timer.Status = RecordingStatus.InProgress;
                     timer.Status = RecordingStatus.InProgress;
                     _timerProvider.AddOrUpdate(timer, false);
                     _timerProvider.AddOrUpdate(timer, false);
 
 
-                    SaveNfo(timer, recordPath, seriesPath);
+                    SaveRecordingMetadata(timer, recordPath, seriesPath);
                     EnforceKeepUpTo(timer);
                     EnforceKeepUpTo(timer);
                 };
                 };
 
 
@@ -1597,32 +1597,126 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             ((IProcess)sender).Dispose();
             ((IProcess)sender).Dispose();
         }
         }
 
 
-        private void SaveNfo(TimerInfo timer, string recordingPath, string seriesPath)
+        private async Task SaveRecordingImage(string recordingPath, LiveTvProgram program, ItemImageInfo image)
         {
         {
-            try
+            if (!image.IsLocalFile)
             {
             {
-                var program = string.IsNullOrWhiteSpace(timer.ProgramId) ? null : _libraryManager.GetItemList(new InternalItemsQuery
-                {
-                    IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
-                    Limit = 1,
-                    ExternalId = timer.ProgramId
+                image = await _libraryManager.ConvertImageToLocal(program, image, 0).ConfigureAwait(false);
+            }
 
 
-                }).FirstOrDefault();
+            string imageSaveFilenameWithoutExtension = null;
 
 
-                if (timer.IsSports)
+            switch (image.Type)
+            {
+                case ImageType.Primary:
+
+                    if (program.IsSeries)
+                    {
+                        imageSaveFilenameWithoutExtension = Path.GetFileNameWithoutExtension(recordingPath) + "-thumb";
+                    }
+                    else
+                    {
+                        imageSaveFilenameWithoutExtension = "poster";
+                    }
+
+                    break;
+                case ImageType.Logo:
+                    imageSaveFilenameWithoutExtension = "logo";
+                    break;
+                case ImageType.Thumb:
+                    imageSaveFilenameWithoutExtension = "landscape";
+                    break;
+                case ImageType.Backdrop:
+                    imageSaveFilenameWithoutExtension = "fanart";
+                    break;
+                default:
+                    break;
+            }
+
+            if (string.IsNullOrWhiteSpace(imageSaveFilenameWithoutExtension))
+            {
+                return;
+            }
+
+            var imageSavePath = Path.Combine(Path.GetDirectoryName(recordingPath), imageSaveFilenameWithoutExtension);
+
+            // preserve original image extension
+            imageSavePath = Path.ChangeExtension(imageSavePath, Path.GetExtension(image.Path));
+
+            _fileSystem.CopyFile(image.Path, imageSavePath, true);
+        }
+
+        private async Task SaveRecordingImages(string recordingPath, LiveTvProgram program)
+        {
+            var image = program.GetImageInfo(ImageType.Primary, 0);
+
+            if (image != null)
+            {
+                try
                 {
                 {
-                    AddGenre(timer.Genres, "Sports");
+                    await SaveRecordingImage(recordingPath, program, image).ConfigureAwait(false);
                 }
                 }
-                if (timer.IsKids)
+                catch (Exception ex)
                 {
                 {
-                    AddGenre(timer.Genres, "Kids");
-                    AddGenre(timer.Genres, "Children");
+                    _logger.ErrorException("Error saving recording image", ex);
                 }
                 }
-                if (timer.IsNews)
+            }
+
+            if (!program.IsSeries)
+            {
+                image = program.GetImageInfo(ImageType.Backdrop, 0);
+                if (image != null)
                 {
                 {
-                    AddGenre(timer.Genres, "News");
+                    try
+                    {
+                        await SaveRecordingImage(recordingPath, program, image).ConfigureAwait(false);
+                    }
+                    catch (Exception ex)
+                    {
+                        _logger.ErrorException("Error saving recording image", ex);
+                    }
                 }
                 }
 
 
+                image = program.GetImageInfo(ImageType.Thumb, 0);
+                if (image != null)
+                {
+                    try
+                    {
+                        await SaveRecordingImage(recordingPath, program, image).ConfigureAwait(false);
+                    }
+                    catch (Exception ex)
+                    {
+                        _logger.ErrorException("Error saving recording image", ex);
+                    }
+                }
+
+                image = program.GetImageInfo(ImageType.Logo, 0);
+                if (image != null)
+                {
+                    try
+                    {
+                        await SaveRecordingImage(recordingPath, program, image).ConfigureAwait(false);
+                    }
+                    catch (Exception ex)
+                    {
+                        _logger.ErrorException("Error saving recording image", ex);
+                    }
+                }
+            }
+        }
+
+        private async void SaveRecordingMetadata(TimerInfo timer, string recordingPath, string seriesPath)
+        {
+            try
+            {
+                var program = string.IsNullOrWhiteSpace(timer.ProgramId) ? null : _libraryManager.GetItemList(new InternalItemsQuery
+                {
+                    IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
+                    Limit = 1,
+                    ExternalId = timer.ProgramId
+
+                }).FirstOrDefault() as LiveTvProgram;
+
                 // dummy this up
                 // dummy this up
                 if (program == null)
                 if (program == null)
                 {
                 {
@@ -1642,6 +1736,20 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                     };
                     };
                 }
                 }
 
 
+                if (timer.IsSports)
+                {
+                    AddGenre(program.Genres, "Sports");
+                }
+                if (timer.IsKids)
+                {
+                    AddGenre(program.Genres, "Kids");
+                    AddGenre(program.Genres, "Children");
+                }
+                if (timer.IsNews)
+                {
+                    AddGenre(program.Genres, "News");
+                }
+
                 if (timer.IsProgramSeries)
                 if (timer.IsProgramSeries)
                 {
                 {
                     SaveSeriesNfo(timer, seriesPath);
                     SaveSeriesNfo(timer, seriesPath);
@@ -1655,6 +1763,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                 {
                 {
                     SaveVideoNfo(timer, recordingPath, program, false);
                     SaveVideoNfo(timer, recordingPath, program, false);
                 }
                 }
+
+                await SaveRecordingImages(recordingPath, program).ConfigureAwait(false);
             }
             }
             catch (Exception ex)
             catch (Exception ex)
             {
             {

+ 65 - 18
Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs

@@ -31,6 +31,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
         private readonly IServerApplicationPaths _appPaths;
         private readonly IServerApplicationPaths _appPaths;
         private readonly LiveTvOptions _liveTvOptions;
         private readonly LiveTvOptions _liveTvOptions;
         private bool _hasExited;
         private bool _hasExited;
+        private Stream _logFileStream;
         private string _targetPath;
         private string _targetPath;
         private IProcess _process;
         private IProcess _process;
         private readonly IProcessFactory _processFactory;
         private readonly IProcessFactory _processFactory;
@@ -84,26 +85,22 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             _targetPath = targetFile;
             _targetPath = targetFile;
             _fileSystem.CreateDirectory(Path.GetDirectoryName(targetFile));
             _fileSystem.CreateDirectory(Path.GetDirectoryName(targetFile));
 
 
-            var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "record-transcode-" + Guid.NewGuid().ToString("N") + ".txt");
-            _fileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath));
-
             var process = _processFactory.Create(new ProcessOptions
             var process = _processFactory.Create(new ProcessOptions
             {
             {
                 CreateNoWindow = true,
                 CreateNoWindow = true,
-                UseShellExecute = true,
+                UseShellExecute = false,
 
 
                 // Must consume both stdout and stderr or deadlocks may occur
                 // Must consume both stdout and stderr or deadlocks may occur
                 //RedirectStandardOutput = true,
                 //RedirectStandardOutput = true,
-                RedirectStandardError = false,
-                RedirectStandardInput = false,
+                RedirectStandardError = true,
+                RedirectStandardInput = true,
 
 
                 FileName = _mediaEncoder.EncoderPath,
                 FileName = _mediaEncoder.EncoderPath,
                 Arguments = GetCommandLineArgs(mediaSource, inputFile, targetFile, duration),
                 Arguments = GetCommandLineArgs(mediaSource, inputFile, targetFile, duration),
 
 
                 IsHidden = true,
                 IsHidden = true,
                 ErrorDialog = false,
                 ErrorDialog = false,
-                EnableRaisingEvents = true,
-                WorkingDirectory = Path.GetDirectoryName(logFilePath)
+                EnableRaisingEvents = true
             });
             });
 
 
             _process = process;
             _process = process;
@@ -111,9 +108,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
             var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
             _logger.Info(commandLineLogMessage);
             _logger.Info(commandLineLogMessage);
 
 
-            _mediaEncoder.SetLogFilename(Path.GetFileName(logFilePath));
+            var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "record-transcode-" + Guid.NewGuid() + ".txt");
+            _fileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath));
 
 
-            //var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(_json.SerializeToString(mediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
+            // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
+            _logFileStream = _fileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true);
+
+            var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(_json.SerializeToString(mediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
+            _logFileStream.Write(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length);
 
 
             process.Exited += (sender, args) => OnFfMpegProcessExited(process, inputFile);
             process.Exited += (sender, args) => OnFfMpegProcessExited(process, inputFile);
 
 
@@ -126,8 +128,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
 
             onStarted();
             onStarted();
 
 
+            // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
+            StartStreamingLog(process.StandardError.BaseStream, _logFileStream);
+
             _logger.Info("ffmpeg recording process started for {0}", _targetPath);
             _logger.Info("ffmpeg recording process started for {0}", _targetPath);
-            _mediaEncoder.ClearLogFilename();
 
 
             return _taskCompletionSource.Task;
             return _taskCompletionSource.Task;
         }
         }
@@ -150,7 +154,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
 
             var durationParam = " -t " + _mediaEncoder.GetTimeParameter(duration.Ticks);
             var durationParam = " -t " + _mediaEncoder.GetTimeParameter(duration.Ticks);
             var inputModifiers = "-fflags +genpts -async 1 -vsync -1";
             var inputModifiers = "-fflags +genpts -async 1 -vsync -1";
-            var commandLineArgs = "-i \"{0}\"{4} -sn {2} -map_metadata -1 -threads 0 {3} -loglevel info -y \"{1}\"";
+            var commandLineArgs = "-i \"{0}\"{4} -sn {2} -map_metadata -1 -threads 0 {3} -y \"{1}\"";
 
 
             long startTimeTicks = 0;
             long startTimeTicks = 0;
             //if (mediaSource.DateLiveStreamOpened.HasValue)
             //if (mediaSource.DateLiveStreamOpened.HasValue)
@@ -230,19 +234,16 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             return output;
             return output;
         }
         }
 
 
-        private bool _isCancelled;
         private void Stop()
         private void Stop()
         {
         {
             if (!_hasExited)
             if (!_hasExited)
             {
             {
                 try
                 try
                 {
                 {
-                    _isCancelled = true;
-
                     _logger.Info("Killing ffmpeg recording process for {0}", _targetPath);
                     _logger.Info("Killing ffmpeg recording process for {0}", _targetPath);
 
 
-                    _process.Kill();
-                    //_process.StandardInput.WriteLine("q");
+                    //process.Kill();
+                    _process.StandardInput.WriteLine("q");
                 }
                 }
                 catch (Exception ex)
                 catch (Exception ex)
                 {
                 {
@@ -258,9 +259,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
         {
         {
             _hasExited = true;
             _hasExited = true;
 
 
+            DisposeLogStream();
+
             try
             try
             {
             {
-                var exitCode = _isCancelled ? 0 : process.ExitCode;
+                var exitCode = process.ExitCode;
 
 
                 _logger.Info("FFMpeg recording exited with code {0} for {1}", exitCode, _targetPath);
                 _logger.Info("FFMpeg recording exited with code {0} for {1}", exitCode, _targetPath);
 
 
@@ -279,5 +282,49 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                 _taskCompletionSource.TrySetException(new Exception(string.Format("Recording for {0} failed", _targetPath)));
                 _taskCompletionSource.TrySetException(new Exception(string.Format("Recording for {0} failed", _targetPath)));
             }
             }
         }
         }
+
+        private void DisposeLogStream()
+        {
+            if (_logFileStream != null)
+            {
+                try
+                {
+                    _logFileStream.Dispose();
+                }
+                catch (Exception ex)
+                {
+                    _logger.ErrorException("Error disposing recording log stream", ex);
+                }
+
+                _logFileStream = null;
+            }
+        }
+
+        private async void StartStreamingLog(Stream source, Stream target)
+        {
+            try
+            {
+                using (var reader = new StreamReader(source))
+                {
+                    while (!reader.EndOfStream)
+                    {
+                        var line = await reader.ReadLineAsync().ConfigureAwait(false);
+
+                        var bytes = Encoding.UTF8.GetBytes(Environment.NewLine + line);
+
+                        await target.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
+                        await target.FlushAsync().ConfigureAwait(false);
+                    }
+                }
+            }
+            catch (ObjectDisposedException)
+            {
+                // Don't spam the log. This doesn't seem to throw in windows, but sometimes under linux
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error reading ffmpeg recording log", ex);
+            }
+        }
     }
     }
 }
 }

+ 0 - 1
MediaBrowser.Providers/Manager/ImageSaver.cs

@@ -18,7 +18,6 @@ using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
-using MediaBrowser.Model.IO;
 
 
 namespace MediaBrowser.Providers.Manager
 namespace MediaBrowser.Providers.Manager
 {
 {