Browse Source

add temp file with recording conversion

Luke Pulverenti 9 years ago
parent
commit
a75f24e8e1

+ 1 - 1
MediaBrowser.Controller/Persistence/IItemRepository.cs

@@ -154,7 +154,7 @@ namespace MediaBrowser.Controller.Persistence
         /// </summary>
         /// <param name="query">The query.</param>
         /// <returns>List&lt;BaseItem&gt;.</returns>
-        IEnumerable<BaseItem> GetItemList(InternalItemsQuery query);
+        List<BaseItem> GetItemList(InternalItemsQuery query);
 
         /// <summary>
         /// Updates the inherited values.

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

@@ -1306,7 +1306,7 @@ namespace MediaBrowser.Server.Implementations.Dto
                     ItemIds = new[] { item.Id.ToString("N") }
                 });
 
-                dto.ArtistItems = artistItems.Items
+                dto.AlbumArtists = artistItems.Items
                     .Select(i =>
                     {
                         var artist = i.Item1;

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

@@ -1059,6 +1059,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
             if (recordingStatus == RecordingStatus.Completed)
             {
+                timer.Status = RecordingStatus.Completed;
+                _timerProvider.AddOrUpdate(timer);
+
                 OnSuccessfulRecording(info.IsSeries, recordPath);
                 _timerProvider.Delete(timer);
             }
@@ -1067,7 +1070,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                 const int retryIntervalSeconds = 60;
                 _logger.Info("Retrying recording in {0} seconds.", retryIntervalSeconds);
 
-                _timerProvider.StartTimer(timer, TimeSpan.FromSeconds(retryIntervalSeconds));
+                timer.Status = RecordingStatus.New;
+                timer.StartDate = DateTime.UtcNow.AddSeconds(retryIntervalSeconds);
+                _timerProvider.AddOrUpdate(timer);
             }
             else
             {
@@ -1119,7 +1124,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
                 if (regInfo.IsValid)
                 {
-                    return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, config);
+                    return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, config, _httpClient);
                 }
             }
 

+ 73 - 16
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs

@@ -8,7 +8,9 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using CommonIO;
-using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
@@ -22,8 +24,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
     {
         private readonly ILogger _logger;
         private readonly IFileSystem _fileSystem;
+        private readonly IHttpClient _httpClient;
         private readonly IMediaEncoder _mediaEncoder;
-        private readonly IApplicationPaths _appPaths;
+        private readonly IServerApplicationPaths _appPaths;
         private readonly LiveTvOptions _liveTvOptions;
         private bool _hasExited;
         private Stream _logFileStream;
@@ -32,7 +35,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
         private readonly IJsonSerializer _json;
         private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
 
-        public EncodedRecorder(ILogger logger, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IApplicationPaths appPaths, IJsonSerializer json, LiveTvOptions liveTvOptions)
+        public EncodedRecorder(ILogger logger, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IServerApplicationPaths appPaths, IJsonSerializer json, LiveTvOptions liveTvOptions, IHttpClient httpClient)
         {
             _logger = logger;
             _fileSystem = fileSystem;
@@ -40,6 +43,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             _appPaths = appPaths;
             _json = json;
             _liveTvOptions = liveTvOptions;
+            _httpClient = httpClient;
         }
 
         public string GetOutputPath(MediaSourceInfo mediaSource, string targetFile)
@@ -49,20 +53,73 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
         public async Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
         {
-            if (mediaSource.RunTimeTicks.HasValue)
+            var tempfile = Path.Combine(_appPaths.TranscodingTempPath, Guid.NewGuid().ToString("N") + ".ts");
+
+            try
             {
-                // The media source already has a fixed duration
-                // But add another stop 1 minute later just in case the recording gets stuck for any reason
-                var durationToken = new CancellationTokenSource(duration.Add(TimeSpan.FromMinutes(1)));
-                cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
+                await RecordInternal(mediaSource, tempfile, targetFile, duration, onStarted, cancellationToken)
+                        .ConfigureAwait(false);
             }
-            else
+            finally
+            {
+                File.Delete(tempfile);
+            }
+        }
+
+        public async Task RecordInternal(MediaSourceInfo mediaSource, string tempFile, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
+        {
+            var httpRequestOptions = new HttpRequestOptions()
             {
-                // The media source if infinite so we need to handle stopping ourselves
-                var durationToken = new CancellationTokenSource(duration);
-                cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
+                Url = mediaSource.Path
+            };
+
+            httpRequestOptions.BufferContent = false;
+
+            using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET").ConfigureAwait(false))
+            {
+                _logger.Info("Opened recording stream from tuner provider");
+
+                Directory.CreateDirectory(Path.GetDirectoryName(tempFile));
+
+                using (var output = _fileSystem.GetFileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read))
+                {
+                    //onStarted();
+
+                    _logger.Info("Copying recording stream to file {0}", tempFile);
+
+                    var bufferMs = 5000;
+
+                    if (mediaSource.RunTimeTicks.HasValue)
+                    {
+                        // The media source already has a fixed duration
+                        // But add another stop 1 minute later just in case the recording gets stuck for any reason
+                        var durationToken = new CancellationTokenSource(duration.Add(TimeSpan.FromMinutes(1)));
+                        cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
+                    }
+                    else
+                    {
+                        // The media source if infinite so we need to handle stopping ourselves
+                        var durationToken = new CancellationTokenSource(duration.Add(TimeSpan.FromMilliseconds(bufferMs)));
+                        cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
+                    }
+
+                    var tempFileTask = response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, cancellationToken);
+
+                    // Give the temp file a little time to build up
+                    await Task.Delay(bufferMs, cancellationToken).ConfigureAwait(false);
+
+                    await RecordFromFile(mediaSource, tempFile, targetFile, onStarted, cancellationToken)
+                            .ConfigureAwait(false);
+
+                    await tempFileTask.ConfigureAwait(false);
+                }
             }
 
+            _logger.Info("Recording completed to file {0}", targetFile);
+        }
+
+        private async Task RecordFromFile(MediaSourceInfo mediaSource, string inputFile, string targetFile, Action onStarted, CancellationToken cancellationToken)
+        {
             _targetPath = targetFile;
             _fileSystem.CreateDirectory(Path.GetDirectoryName(targetFile));
 
@@ -79,7 +136,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                     RedirectStandardInput = true,
 
                     FileName = _mediaEncoder.EncoderPath,
-                    Arguments = GetCommandLineArgs(mediaSource, targetFile, duration),
+                    Arguments = GetCommandLineArgs(mediaSource, inputFile, targetFile),
 
                     WindowStyle = ProcessWindowStyle.Hidden,
                     ErrorDialog = false
@@ -119,7 +176,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             await _taskCompletionSource.Task.ConfigureAwait(false);
         }
 
-        private string GetCommandLineArgs(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration)
+        private string GetCommandLineArgs(MediaSourceInfo mediaSource, string inputTempFile, string targetFile)
         {
             string videoArgs;
             if (EncodeVideo(mediaSource))
@@ -135,14 +192,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                 videoArgs = "-codec:v:0 copy";
             }
 
-            var commandLineArgs = "-fflags +genpts -async 1 -vsync -1 -re -i \"{0}\" -t {4} -sn {2} -map_metadata -1 -threads 0 {3} -y \"{1}\"";
+            var commandLineArgs = "-fflags +genpts -async 1 -vsync -1 -re -i \"{0}\" -sn {2} -map_metadata -1 -threads 0 {3} -y \"{1}\"";
 
             if (mediaSource.ReadAtNativeFramerate)
             {
                 commandLineArgs = "-re " + commandLineArgs;
             }
 
-            commandLineArgs = string.Format(commandLineArgs, mediaSource.Path, targetFile, videoArgs, GetAudioArgs(mediaSource), _mediaEncoder.GetTimeParameter(duration.Ticks));
+            commandLineArgs = string.Format(commandLineArgs, inputTempFile, targetFile, videoArgs, GetAudioArgs(mediaSource));
 
             return commandLineArgs;
         }

+ 9 - 3
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs

@@ -10,6 +10,7 @@ using System.Linq;
 using System.Threading;
 using CommonIO;
 using MediaBrowser.Controller.Power;
+using MediaBrowser.Model.LiveTv;
 
 namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 {
@@ -85,6 +86,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
         private void AddTimer(TimerInfo item)
         {
+            if (item.Status == RecordingStatus.Completed)
+            {
+                return;
+            }
+
             var startDate = RecordingHelper.GetStartTime(item);
             var now = DateTime.UtcNow;
 
@@ -117,15 +123,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             }
         }
 
-        public void StartTimer(TimerInfo item, TimeSpan length)
+        public void StartTimer(TimerInfo item, TimeSpan dueTime)
         {
             StopTimer(item);
 
-            var timer = new Timer(TimerCallback, item.Id, length, TimeSpan.Zero);
+            var timer = new Timer(TimerCallback, item.Id, dueTime, TimeSpan.Zero);
 
             if (_timers.TryAdd(item.Id, timer))
             {
-                _logger.Info("Creating recording timer for {0}, {1}. Timer will fire in {2} minutes", item.Id, item.Name, length.TotalMinutes.ToString(CultureInfo.InvariantCulture));
+                _logger.Info("Creating recording timer for {0}, {1}. Timer will fire in {2} minutes", item.Id, item.Name, dueTime.TotalMinutes.ToString(CultureInfo.InvariantCulture));
             }
             else
             {

+ 21 - 1
MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs

@@ -1742,7 +1742,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
             return " from TypedBaseItems A";
         }
 
-        public IEnumerable<BaseItem> GetItemList(InternalItemsQuery query)
+        public List<BaseItem> GetItemList(InternalItemsQuery query)
         {
             if (query == null)
             {
@@ -1842,6 +1842,16 @@ namespace MediaBrowser.Server.Implementations.Persistence
 
             CheckDisposed();
 
+            if (!query.EnableTotalRecordCount || (!query.Limit.HasValue && (query.StartIndex ?? 0) == 0))
+            {
+                var list = GetItemList(query);
+                return new QueryResult<BaseItem>
+                {
+                    Items = list.ToArray(),
+                    TotalRecordCount = list.Count
+                };
+            }
+
             var now = DateTime.UtcNow;
 
             using (var cmd = _connection.CreateCommand())
@@ -2196,6 +2206,16 @@ namespace MediaBrowser.Server.Implementations.Persistence
 
             CheckDisposed();
 
+            if (!query.EnableTotalRecordCount || (!query.Limit.HasValue && (query.StartIndex ?? 0) == 0))
+            {
+                var list = GetItemIdsList(query);
+                return new QueryResult<Guid>
+                {
+                    Items = list.ToArray(),
+                    TotalRecordCount = list.Count
+                };
+            }
+
             var now = DateTime.UtcNow;
 
             using (var cmd = _connection.CreateCommand())