Bladeren bron

improve resiliency of recording process

Luke Pulverenti 8 jaren geleden
bovenliggende
commit
0c95297269

+ 1 - 0
MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs

@@ -205,6 +205,7 @@ namespace MediaBrowser.Api.UserLibrary
         private void SetItemCounts(BaseItemDto dto, ItemCounts counts)
         {
             dto.ChildCount = counts.ItemCount;
+            dto.ProgramCount = counts.ProgramCount;
             dto.SeriesCount = counts.SeriesCount;
             dto.EpisodeCount = counts.EpisodeCount;
             dto.MovieCount = counts.MovieCount;

+ 1 - 0
MediaBrowser.Model/Dto/BaseItemDto.cs

@@ -826,6 +826,7 @@ namespace MediaBrowser.Model.Dto
         /// </summary>
         /// <value>The series count.</value>
         public int? SeriesCount { get; set; }
+        public int? ProgramCount { get; set; }
         /// <summary>
         /// Gets or sets the episode count.
         /// </summary>

+ 1 - 0
MediaBrowser.Model/Dto/ItemCounts.cs

@@ -26,6 +26,7 @@
         /// <value>The game count.</value>
         public int GameCount { get; set; }
         public int ArtistCount { get; set; }
+        public int ProgramCount { get; set; }
         /// <summary>
         /// Gets or sets the game system count.
         /// </summary>

+ 7 - 23
MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs

@@ -20,6 +20,7 @@ using System.Text.RegularExpressions;
 using System.Threading;
 using System.Threading.Tasks;
 using CommonIO;
+using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Providers.TV;
 
 namespace MediaBrowser.Providers.Movies
@@ -59,30 +60,13 @@ namespace MediaBrowser.Providers.Movies
 
         public bool Supports(IHasImages item)
         {
-            //var channelItem = item as IChannelMediaItem;
-
-            //if (channelItem != null)
-            //{
-            //    if (channelItem.ContentType == ChannelMediaContentType.Movie)
-            //    {
-            //        return true;
-            //    }
-            //    if (channelItem.ContentType == ChannelMediaContentType.MovieExtra)
-            //    {
-            //        if (channelItem.ExtraType == ExtraType.Trailer)
-            //        {
-            //            return true;
-            //        }
-            //    }
-            //}
-
             // Supports images for tv movies
-            //var tvProgram = item as LiveTvProgram;
-            //if (tvProgram != null && tvProgram.IsMovie)
-            //{
-            //    return true;
-            //}
-            
+            var tvProgram = item as LiveTvProgram;
+            if (tvProgram != null && tvProgram.IsMovie)
+            {
+                return true;
+            }
+
             return item is Movie || item is BoxSet || item is MusicVideo;
         }
 

+ 1 - 0
MediaBrowser.Server.Implementations/Dto/DtoService.cs

@@ -437,6 +437,7 @@ namespace MediaBrowser.Server.Implementations.Dto
                 dto.TrailerCount = taggedItems.Count(i => i is Trailer);
                 dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo);
                 dto.SeriesCount = taggedItems.Count(i => i is Series);
+                dto.ProgramCount = taggedItems.Count(i => i is LiveTvProgram);
                 dto.SongCount = taggedItems.Count(i => i is Audio);
             }
 

+ 34 - 1
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs

@@ -61,11 +61,44 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                         cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
                     }
 
-                    await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false);
+                    await CopyUntilCancelled(response.Content, output, cancellationToken).ConfigureAwait(false);
                 }
             }
 
             _logger.Info("Recording completed to file {0}", targetFile);
         }
+
+        private const int BufferSize = 81920;
+        public static async Task CopyUntilCancelled(Stream source, Stream target, CancellationToken cancellationToken)
+        {
+            while (!cancellationToken.IsCancellationRequested)
+            {
+                var bytesRead = await CopyToAsyncInternal(source, target, BufferSize, cancellationToken).ConfigureAwait(false);
+
+                //var position = fs.Position;
+                //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
+
+                if (bytesRead == 0)
+                {
+                    await Task.Delay(100).ConfigureAwait(false);
+                }
+            }
+        }
+
+        private static async Task<int> CopyToAsyncInternal(Stream source, Stream destination, Int32 bufferSize, CancellationToken cancellationToken)
+        {
+            byte[] buffer = new byte[bufferSize];
+            int bytesRead;
+            int totalBytesRead = 0;
+
+            while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
+            {
+                await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
+
+                totalBytesRead += bytesRead;
+            }
+
+            return totalBytesRead;
+        }
     }
 }

+ 8 - 2
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -580,7 +580,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                 PrePaddingSeconds = Math.Max(config.PrePaddingSeconds, 0),
                 RecordAnyChannel = true,
                 RecordAnyTime = true,
-                RecordNewOnly = false,
+                RecordNewOnly = true,
 
                 Days = new List<DayOfWeek>
                 {
@@ -730,6 +730,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
                     return result.Item1;
                 }
+                catch (FileNotFoundException)
+                {
+                }
                 catch (Exception e)
                 {
                     _logger.ErrorException("Error getting channel stream", e);
@@ -751,6 +754,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
                     return new Tuple<MediaSourceInfo, ITunerHost, SemaphoreSlim>(result.Item1, hostInstance, result.Item2);
                 }
+                catch (FileNotFoundException)
+                {
+                }
                 catch (Exception e)
                 {
                     _logger.ErrorException("Error getting channel stream", e);
@@ -1213,7 +1219,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             }
 
             // Exclude programs that have already ended
-            allPrograms = allPrograms.Where(i => i.EndDate > DateTime.UtcNow && i.StartDate > DateTime.UtcNow);
+            allPrograms = allPrograms.Where(i => i.EndDate > DateTime.UtcNow);
 
             allPrograms = GetProgramsForSeries(seriesTimer, allPrograms);
 

+ 38 - 15
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs

@@ -78,21 +78,33 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
             var tempfile = Path.Combine(_appPaths.TranscodingTempPath, Guid.NewGuid().ToString("N") + ".ts");
 
-            try
-            {
-                await RecordWithTempFile(mediaSource, tempfile, targetFile, duration, onStarted, cancellationToken)
-                        .ConfigureAwait(false);
-            }
-            finally
+            await RecordWithTempFile(mediaSource, tempfile, targetFile, duration, onStarted, cancellationToken)
+                    .ConfigureAwait(false);
+        }
+
+        private async void DeleteTempFile(string path)
+        {
+            for (var i = 0; i < 10; i++)
             {
                 try
                 {
-                    File.Delete(tempfile);
+                    File.Delete(path);
+                    return;
+                }
+                catch (FileNotFoundException)
+                {
+                    return;
+                }
+                catch (DirectoryNotFoundException)
+                {
+                    return;
                 }
                 catch (Exception ex)
                 {
                     _logger.ErrorException("Error deleting recording temp file", ex);
                 }
+
+                await Task.Delay(1000).ConfigureAwait(false);
             }
         }
 
@@ -101,7 +113,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             var durationToken = new CancellationTokenSource(duration);
             cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
 
-            await RecordFromFile(mediaSource, mediaSource.Path, targetFile, duration, onStarted, cancellationToken).ConfigureAwait(false);
+            await RecordFromFile(mediaSource, mediaSource.Path, targetFile, false, duration, onStarted, cancellationToken).ConfigureAwait(false);
 
             _logger.Info("Recording completed to file {0}", targetFile);
         }
@@ -143,14 +155,21 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                         cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
                     }
 
-                    var tempFileTask = response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, cancellationToken);
+                    var tempFileTask = DirectRecorder.CopyUntilCancelled(response.Content, output, cancellationToken);
 
                     // Give the temp file a little time to build up
                     await Task.Delay(bufferMs, cancellationToken).ConfigureAwait(false);
 
-                    var recordTask = Task.Run(() => RecordFromFile(mediaSource, tempFile, targetFile, duration, onStarted, cancellationToken), cancellationToken);
+                    var recordTask = Task.Run(() => RecordFromFile(mediaSource, tempFile, targetFile, true, duration, onStarted, cancellationToken), CancellationToken.None);
 
-                    await tempFileTask.ConfigureAwait(false);
+                    try
+                    {
+                        await tempFileTask.ConfigureAwait(false);
+                    }
+                    catch (OperationCanceledException)
+                    {
+                        
+                    }
 
                     await recordTask.ConfigureAwait(false);
                 }
@@ -159,7 +178,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             _logger.Info("Recording completed to file {0}", targetFile);
         }
 
-        private Task RecordFromFile(MediaSourceInfo mediaSource, string inputFile, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
+        private Task RecordFromFile(MediaSourceInfo mediaSource, string inputFile, string targetFile, bool deleteInputFileAfterCompletion, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
         {
             _targetPath = targetFile;
             _fileSystem.CreateDirectory(Path.GetDirectoryName(targetFile));
@@ -200,7 +219,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             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);
+            process.Exited += (sender, args) => OnFfMpegProcessExited(process, inputFile, deleteInputFileAfterCompletion);
 
             process.Start();
 
@@ -309,8 +328,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
         /// <summary>
         /// Processes the exited.
         /// </summary>
-        /// <param name="process">The process.</param>
-        private void OnFfMpegProcessExited(Process process)
+        private void OnFfMpegProcessExited(Process process, string inputFile, bool deleteInputFileAfterCompletion)
         {
             _hasExited = true;
 
@@ -336,6 +354,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                 _logger.Error("FFMpeg recording exited with an error for {0}.", _targetPath);
                 _taskCompletionSource.TrySetException(new Exception(string.Format("Recording for {0} failed", _targetPath)));
             }
+
+            if (deleteInputFileAfterCompletion)
+            {
+                DeleteTempFile(inputFile);
+            }
         }
 
         private void DisposeLogStream()

+ 8 - 2
MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs

@@ -6,6 +6,7 @@ using MediaBrowser.Model.Logging;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
+using System.IO;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
@@ -195,7 +196,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
                     }
                     else if (streamId.StartsWith(host.Id, StringComparison.OrdinalIgnoreCase))
                     {
-                        hostsWithChannel = new List<TunerHostInfo> { host };
+                        hostsWithChannel = new List<TunerHostInfo> {host};
                         streamId = streamId.Substring(host.Id.Length);
                         break;
                     }
@@ -222,7 +223,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
                             }
                         }
 
-                        var stream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false);
+                        var stream =
+                            await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false);
 
                         if (EnableMediaProbing)
                         {
@@ -239,6 +241,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
                     }
                 }
             }
+            else
+            {
+                throw new FileNotFoundException();
+            }
 
             throw new LiveTvConflictException();
         }