Browse Source

add migrations for new release

Luke Pulverenti 9 năm trước cách đây
mục cha
commit
a0b1ddf0a7

+ 3 - 2
MediaBrowser.Controller/LiveTv/ITunerHost.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Model.Dto;
+using System;
+using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.LiveTv;
 using System.Collections.Generic;
 using System.Threading;
@@ -37,7 +38,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="streamId">The stream identifier.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
-        Task<MediaSourceInfo> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken);
+        Task<Tuple<MediaSourceInfo,SemaphoreSlim>> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken);
         /// <summary>
         /// Gets the channel stream media sources.
         /// </summary>

+ 4 - 0
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -226,11 +226,15 @@ namespace MediaBrowser.Model.Configuration
 
         public bool EnableDateLastRefresh { get; set; }
 
+        public string[] Migrations { get; set; }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
         /// </summary>
         public ServerConfiguration()
         {
+            Migrations = new string[] {};
+
             ImageSavingConvention = ImageSavingConvention.Compatible;
             PublicPort = 8096;
             PublicHttpsPort = 8920;

+ 64 - 25
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -487,6 +487,29 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
         {
             _logger.Info("Streaming Channel " + channelId);
 
+            foreach (var hostInstance in _liveTvManager.TunerHosts)
+            {
+                try
+                {
+                    var result = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false);
+
+                    result.Item2.Release();
+
+                    return result.Item1;
+                }
+                catch (Exception e)
+                {
+                    _logger.ErrorException("Error getting channel stream", e);
+                }
+            }
+
+            throw new ApplicationException("Tuner not found.");
+        }
+
+        private async Task<Tuple<MediaSourceInfo, SemaphoreSlim>> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken)
+        {
+            _logger.Info("Streaming Channel " + channelId);
+
             foreach (var hostInstance in _liveTvManager.TunerHosts)
             {
                 try
@@ -653,40 +676,56 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
             try
             {
-                var mediaStreamInfo = await GetChannelStream(timer.ChannelId, null, CancellationToken.None);
-
-                // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg
-                await Task.Delay(3000, cancellationToken).ConfigureAwait(false);
+                var result = await GetChannelStreamInternal(timer.ChannelId, null, CancellationToken.None);
+                var mediaStreamInfo = result.Item1;
+                var isResourceOpen = true;
 
-                var duration = recordingEndDate - DateTime.UtcNow;
-
-                HttpRequestOptions httpRequestOptions = new HttpRequestOptions()
+                // Unfortunately due to the semaphore we have to have a nested try/finally
+                try
                 {
-                    Url = mediaStreamInfo.Path
-                };
+                    // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg
+                    await Task.Delay(3000, cancellationToken).ConfigureAwait(false);
+
+                    var duration = recordingEndDate - DateTime.UtcNow;
 
-                recording.Path = recordPath;
-                recording.Status = RecordingStatus.InProgress;
-                recording.DateLastUpdated = DateTime.UtcNow;
-                _recordingProvider.Update(recording);
+                    HttpRequestOptions httpRequestOptions = new HttpRequestOptions()
+                    {
+                        Url = mediaStreamInfo.Path
+                    };
+
+                    recording.Path = recordPath;
+                    recording.Status = RecordingStatus.InProgress;
+                    recording.DateLastUpdated = DateTime.UtcNow;
+                    _recordingProvider.Update(recording);
+
+                    _logger.Info("Beginning recording.");
+
+                    httpRequestOptions.BufferContent = false;
+                    var durationToken = new CancellationTokenSource(duration);
+                    var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
+                    httpRequestOptions.CancellationToken = linkedToken;
+                    _logger.Info("Writing file to path: " + recordPath);
+                    using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET"))
+                    {
+                        using (var output = _fileSystem.GetFileStream(recordPath, FileMode.Create, FileAccess.Write, FileShare.Read))
+                        {
+                            result.Item2.Release();
+                            isResourceOpen = false;
 
-                _logger.Info("Beginning recording.");
+                            await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, linkedToken);
+                        }
+                    }
 
-                httpRequestOptions.BufferContent = false;
-                var durationToken = new CancellationTokenSource(duration);
-                var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
-                httpRequestOptions.CancellationToken = linkedToken;
-                _logger.Info("Writing file to path: " + recordPath);
-                using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET"))
+                    recording.Status = RecordingStatus.Completed;
+                    _logger.Info("Recording completed");
+                }
+                finally
                 {
-                    using (var output = _fileSystem.GetFileStream(recordPath, FileMode.Create, FileAccess.Write, FileShare.Read))
+                    if (isResourceOpen)
                     {
-                        await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, linkedToken);
+                        result.Item2.Release();
                     }
                 }
-
-                recording.Status = RecordingStatus.Completed;
-                _logger.Info("Recording completed");
             }
             catch (OperationCanceledException)
             {

+ 1 - 1
MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs

@@ -9,7 +9,7 @@ using System.Threading.Tasks;
 
 namespace MediaBrowser.Server.Implementations.LiveTv
 {
-    class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask, IHasKey
+    public class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask, IHasKey
     {
         private readonly ILiveTvManager _liveTvManager;
         private readonly IConfigurationManager _config;

+ 39 - 5
MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs

@@ -141,7 +141,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
 
         protected abstract Task<MediaSourceInfo> GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken);
 
-        public async Task<MediaSourceInfo> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken)
+        public async Task<Tuple<MediaSourceInfo, SemaphoreSlim>> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken)
         {
             if (IsValidChannelId(channelId))
             {
@@ -173,9 +173,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
                     try
                     {
                         var stream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false);
-                        
-                        await AddMediaInfo(stream, false, cancellationToken).ConfigureAwait(false);
-                        return stream;
+                        var resourcePool = GetLock(host.Url);
+
+                        await AddMediaInfo(stream, false, resourcePool, cancellationToken).ConfigureAwait(false);
+                        return new Tuple<MediaSourceInfo, SemaphoreSlim>(stream, resourcePool);
                     }
                     catch (Exception ex)
                     {
@@ -187,7 +188,40 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
             throw new LiveTvConflictException();
         }
 
-        private async Task AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken)
+        /// <summary>
+        /// The _semaphoreLocks
+        /// </summary>
+        private readonly ConcurrentDictionary<string, SemaphoreSlim> _semaphoreLocks = new ConcurrentDictionary<string, SemaphoreSlim>(StringComparer.OrdinalIgnoreCase);
+        /// <summary>
+        /// Gets the lock.
+        /// </summary>
+        /// <param name="url">The filename.</param>
+        /// <returns>System.Object.</returns>
+        private SemaphoreSlim GetLock(string url)
+        {
+            return _semaphoreLocks.GetOrAdd(url, key => new SemaphoreSlim(1, 1));
+        }
+
+        private async Task AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio, SemaphoreSlim resourcePool, CancellationToken cancellationToken)
+        {
+            await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+            try
+            {
+                await AddMediaInfoInternal(mediaSource, isAudio, cancellationToken).ConfigureAwait(false);
+
+                // Leave the resource locked. it will be released upstream
+            }
+            catch (Exception)
+            {
+                // Release the resource if there's some kind of failure.
+                resourcePool.Release();
+
+                throw;
+            }
+        }
+
+        private async Task AddMediaInfoInternal(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken)
         {
             var originalRuntime = mediaSource.RunTimeTicks;
 

+ 2 - 3
MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs

@@ -1,5 +1,4 @@
-using MediaBrowser.Common.IO;
-using MediaBrowser.Common.Progress;
+using MediaBrowser.Common.Progress;
 using MediaBrowser.Common.ScheduledTasks;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
@@ -17,7 +16,7 @@ using MediaBrowser.Controller.Entities.Audio;
 
 namespace MediaBrowser.Server.Implementations.Persistence
 {
-    class CleanDatabaseScheduledTask : IScheduledTask
+    public class CleanDatabaseScheduledTask : IScheduledTask
     {
         private readonly ILibraryManager _libraryManager;
         private readonly IItemRepository _itemRepo;

+ 8 - 5
MediaBrowser.Server.Startup.Common/ApplicationHost.cs

@@ -333,18 +333,18 @@ namespace MediaBrowser.Server.Startup.Common
             });
 
             LogManager.RemoveConsoleOutput();
+
+            PerformPostInitMigrations();
         }
 
-        public override async Task Init(IProgress<double> progress)
+        public override Task Init(IProgress<double> progress)
         {
             HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
             HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
 
             PerformPreInitMigrations();
 
-            await base.Init(progress).ConfigureAwait(false);
-
-            PerformPostInitMigrations();
+            return base.Init(progress);
         }
 
         private void PerformPreInitMigrations()
@@ -362,7 +362,10 @@ namespace MediaBrowser.Server.Startup.Common
 
         private void PerformPostInitMigrations()
         {
-            var migrations = new List<IVersionMigration>();
+            var migrations = new List<IVersionMigration>
+            {
+                new Release5767(ServerConfigurationManager, TaskManager)
+            };
 
             foreach (var task in migrations)
             {

+ 1 - 0
MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj

@@ -72,6 +72,7 @@
     <Compile Include="INativeApp.cs" />
     <Compile Include="MbLinkShortcutHandler.cs" />
     <Compile Include="Migrations\IVersionMigration.cs" />
+    <Compile Include="Migrations\Release5767.cs" />
     <Compile Include="Migrations\RenameXmlOptions.cs" />
     <Compile Include="NativeEnvironment.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />

+ 47 - 0
MediaBrowser.Server.Startup.Common/Migrations/Release5767.cs

@@ -0,0 +1,47 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using MediaBrowser.Common.ScheduledTasks;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Server.Implementations.LiveTv;
+using MediaBrowser.Server.Implementations.Persistence;
+using MediaBrowser.Server.Implementations.ScheduledTasks;
+
+namespace MediaBrowser.Server.Startup.Common.Migrations
+{
+    public class Release5767 : IVersionMigration
+    {
+        private readonly IServerConfigurationManager _config;
+        private readonly ITaskManager _taskManager;
+
+        public Release5767(IServerConfigurationManager config, ITaskManager taskManager)
+        {
+            _config = config;
+            _taskManager = taskManager;
+        }
+
+        public void Run()
+        {
+            var name = "5767";
+
+            if (_config.Configuration.Migrations.Contains(name, StringComparer.OrdinalIgnoreCase))
+            {
+                return;
+            }
+
+            Task.Run(async () =>
+            {
+                await Task.Delay(3000).ConfigureAwait(false);
+
+                _taskManager.QueueScheduledTask<RefreshChannelsScheduledTask>();
+                _taskManager.QueueScheduledTask<CleanDatabaseScheduledTask>();
+                _taskManager.QueueScheduledTask<RefreshMediaLibraryTask>();
+            });
+
+            var list = _config.Configuration.Migrations.ToList();
+            list.Add(name);
+            _config.Configuration.Migrations = list.ToArray();
+            _config.SaveConfiguration();
+        }
+    }
+}