瀏覽代碼

reduce scanning overhead a bit

Luke Pulverenti 12 年之前
父節點
當前提交
2b8b98b590

+ 0 - 16
MediaBrowser.Controller/Dto/DtoBuilder.cs

@@ -664,22 +664,6 @@ namespace MediaBrowser.Controller.Dto
             return null;
         }
 
-        /// <summary>
-        /// Gets the library update info.
-        /// </summary>
-        /// <param name="changeEvent">The <see cref="ChildrenChangedEventArgs" /> instance containing the event data.</param>
-        /// <returns>LibraryUpdateInfo.</returns>
-        public static LibraryUpdateInfo GetLibraryUpdateInfo(ChildrenChangedEventArgs changeEvent)
-        {
-            return new LibraryUpdateInfo
-            {
-                Folder = GetBaseItemInfo(changeEvent.Folder),
-                ItemsAdded = changeEvent.ItemsAdded.Select(GetBaseItemInfo),
-                ItemsRemoved = changeEvent.ItemsRemoved.Select(i => i.Id),
-                ItemsUpdated = changeEvent.ItemsUpdated.Select(i => i.Id)
-            };
-        }
-
         /// <summary>
         /// Converts a UserItemData to a DTOUserItemData
         /// </summary>

+ 1 - 1
MediaBrowser.Controller/Entities/Folder.cs

@@ -659,7 +659,7 @@ namespace MediaBrowser.Controller.Entities
 
             foreach (var tuple in list)
             {
-                if (tasks.Count > 50)
+                if (tasks.Count > 5)
                 {
                     await Task.WhenAll(tasks).ConfigureAwait(false);
                 }

+ 11 - 2
MediaBrowser.Controller/Providers/BaseItemXmlParser.cs

@@ -49,8 +49,17 @@ namespace MediaBrowser.Controller.Providers
                 throw new ArgumentNullException();
             }
 
+            var settings = new XmlReaderSettings
+            {
+                CheckCharacters = false,
+                IgnoreProcessingInstructions = true,
+                IgnoreComments = true,
+                IgnoreWhitespace = true,
+                ValidationType = ValidationType.None
+            };
+
             // Use XmlReader for best performance
-            using (var reader = XmlReader.Create(metadataFile))
+            using (var reader = XmlReader.Create(metadataFile, settings))
             {
                 reader.MoveToContent();
 
@@ -93,7 +102,7 @@ namespace MediaBrowser.Controller.Providers
                     {
                         var type = reader.ReadElementContentAsString();
 
-                        if (!string.IsNullOrWhiteSpace(type) && !type.Equals("none",StringComparison.OrdinalIgnoreCase))
+                        if (!string.IsNullOrWhiteSpace(type) && !type.Equals("none", StringComparison.OrdinalIgnoreCase))
                         {
                             item.DisplayMediaType = type;
                         }

+ 2 - 0
MediaBrowser.Controller/Providers/BaseMetadataProvider.cs

@@ -31,6 +31,8 @@ namespace MediaBrowser.Controller.Providers
         /// </summary>
         protected readonly Guid Id;
 
+        protected static readonly SemaphoreSlim XmlParsingResourcePool = new SemaphoreSlim(5, 5);
+        
         /// <summary>
         /// Supportses the specified item.
         /// </summary>

+ 14 - 3
MediaBrowser.Controller/Providers/FolderProviderFromXml.cs

@@ -58,7 +58,7 @@ namespace MediaBrowser.Controller.Providers
         /// <returns>Task{System.Boolean}.</returns>
         public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
-            return Task.Run(() => Fetch(item, cancellationToken));
+            return Fetch(item, cancellationToken);
         }
 
         /// <summary>
@@ -67,7 +67,7 @@ namespace MediaBrowser.Controller.Providers
         /// <param name="item">The item.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
-        private bool Fetch(BaseItem item, CancellationToken cancellationToken)
+        private async Task<bool> Fetch(BaseItem item, CancellationToken cancellationToken)
         {
             cancellationToken.ThrowIfCancellationRequested();
 
@@ -76,7 +76,18 @@ namespace MediaBrowser.Controller.Providers
             if (metadataFile.HasValue)
             {
                 var path = metadataFile.Value.Path;
-                new BaseItemXmlParser<Folder>(Logger).Fetch((Folder)item, path, cancellationToken);
+
+                await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+                try
+                {
+                    new BaseItemXmlParser<Folder>(Logger).Fetch((Folder)item, path, cancellationToken);
+                }
+                finally
+                {
+                    XmlParsingResourcePool.Release();
+                }
+
                 SetLastRefreshed(item, DateTime.UtcNow);
                 return true;
             }

+ 17 - 6
MediaBrowser.Controller/Providers/Movies/MovieProviderFromXml.cs

@@ -58,7 +58,7 @@ namespace MediaBrowser.Controller.Providers.Movies
         /// <returns>Task{System.Boolean}.</returns>
         public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
-            return Task.Run(() => Fetch(item, cancellationToken));
+            return Fetch(item, cancellationToken);
         }
 
         /// <summary>
@@ -67,7 +67,7 @@ namespace MediaBrowser.Controller.Providers.Movies
         /// <param name="item">The item.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
-        private bool Fetch(BaseItem item, CancellationToken cancellationToken)
+        private async Task<bool> Fetch(BaseItem item, CancellationToken cancellationToken)
         {
             cancellationToken.ThrowIfCancellationRequested();
             
@@ -77,14 +77,25 @@ namespace MediaBrowser.Controller.Providers.Movies
             {
                 var path = metadataFile.Value.Path;
                 var boxset = item as BoxSet;
-                if (boxset != null)
+
+                await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+                try
                 {
-                    new BaseItemXmlParser<BoxSet>(Logger).Fetch(boxset, path, cancellationToken);
+                    if (boxset != null)
+                    {
+                        new BaseItemXmlParser<BoxSet>(Logger).Fetch(boxset, path, cancellationToken);
+                    }
+                    else
+                    {
+                        new BaseItemXmlParser<Movie>(Logger).Fetch((Movie)item, path, cancellationToken);
+                    }
                 }
-                else
+                finally
                 {
-                    new BaseItemXmlParser<Movie>(Logger).Fetch((Movie)item, path, cancellationToken);
+                    XmlParsingResourcePool.Release();
                 }
+
                 SetLastRefreshed(item, DateTime.UtcNow);
                 return true;
             }

+ 15 - 25
MediaBrowser.Controller/Providers/TV/EpisodeProviderFromXml.cs

@@ -15,7 +15,8 @@ namespace MediaBrowser.Controller.Providers.TV
     /// </summary>
     public class EpisodeProviderFromXml : BaseMetadataProvider
     {
-        public EpisodeProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) : base(logManager, configurationManager)
+        public EpisodeProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager)
+            : base(logManager, configurationManager)
         {
         }
 
@@ -55,7 +56,7 @@ namespace MediaBrowser.Controller.Providers.TV
         /// <returns>Task{System.Boolean}.</returns>
         public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
-            return Task.Run(() => Fetch(item, cancellationToken));
+            return Fetch(item, cancellationToken);
         }
 
         /// <summary>
@@ -84,42 +85,31 @@ namespace MediaBrowser.Controller.Providers.TV
         /// <param name="item">The item.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
-        private bool Fetch(BaseItem item, CancellationToken cancellationToken)
+        private async Task<bool> Fetch(BaseItem item, CancellationToken cancellationToken)
         {
             cancellationToken.ThrowIfCancellationRequested();
-            
+
             var metadataFile = Path.Combine(item.MetaLocation, Path.ChangeExtension(Path.GetFileName(item.Path), ".xml"));
 
-            var episode = (Episode)item;
+            var file = item.ResolveArgs.Parent.ResolveArgs.GetMetaFileByPath(metadataFile);
 
-            if (!FetchMetadata(episode, item.ResolveArgs.Parent, metadataFile, cancellationToken))
+            if (!file.HasValue)
             {
-                // Don't set last refreshed if we didn't do anything
                 return false;
             }
 
-            SetLastRefreshed(item, DateTime.UtcNow);
-            return true;
-        }
-
-        /// <summary>
-        /// Fetches the metadata.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="parent">The parent.</param>
-        /// <param name="metadataFile">The metadata file.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
-        private bool FetchMetadata(Episode item, Folder parent, string metadataFile, CancellationToken cancellationToken)
-        {
-            var file = parent.ResolveArgs.GetMetaFileByPath(metadataFile);
+            await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
 
-            if (!file.HasValue)
+            try
             {
-                return false;
+                new EpisodeXmlParser(Logger).Fetch((Episode)item, metadataFile, cancellationToken);
+            }
+            finally
+            {
+                XmlParsingResourcePool.Release();
             }
 
-            new EpisodeXmlParser(Logger).Fetch(item, metadataFile, cancellationToken);
+            SetLastRefreshed(item, DateTime.UtcNow);
             return true;
         }
     }

+ 13 - 3
MediaBrowser.Controller/Providers/TV/SeriesProviderFromXml.cs

@@ -59,7 +59,7 @@ namespace MediaBrowser.Controller.Providers.TV
         /// <returns>Task{System.Boolean}.</returns>
         public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
-            return Task.Run(() => Fetch(item, cancellationToken));
+            return Fetch(item, cancellationToken);
         }
 
         /// <summary>
@@ -68,7 +68,7 @@ namespace MediaBrowser.Controller.Providers.TV
         /// <param name="item">The item.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
-        private bool Fetch(BaseItem item, CancellationToken cancellationToken)
+        private async Task<bool> Fetch(BaseItem item, CancellationToken cancellationToken)
         {
             cancellationToken.ThrowIfCancellationRequested();
             
@@ -78,7 +78,17 @@ namespace MediaBrowser.Controller.Providers.TV
             {
                 var path = metadataFile.Value.Path;
 
-                new SeriesXmlParser(Logger).Fetch((Series)item, path, cancellationToken);
+                await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+                try
+                {
+                    new SeriesXmlParser(Logger).Fetch((Series)item, path, cancellationToken);
+                }
+                finally
+                {
+                    XmlParsingResourcePool.Release();
+                }
+
                 SetLastRefreshed(item, DateTime.UtcNow);
 
                 return true;

+ 14 - 6
MediaBrowser.Model/Entities/LibraryUpdateInfo.cs

@@ -9,27 +9,35 @@ namespace MediaBrowser.Model.Entities
     public class LibraryUpdateInfo
     {
         /// <summary>
-        /// Gets or sets the folder.
+        /// Gets or sets the folders.
         /// </summary>
-        /// <value>The folder.</value>
-        public BaseItemInfo Folder { get; set; }
+        /// <value>The folders.</value>
+        public List<Guid> Folders { get; set; }
 
         /// <summary>
         /// Gets or sets the items added.
         /// </summary>
         /// <value>The items added.</value>
-        public IEnumerable<BaseItemInfo> ItemsAdded { get; set; }
+        public List<Guid> ItemsAdded { get; set; }
 
         /// <summary>
         /// Gets or sets the items removed.
         /// </summary>
         /// <value>The items removed.</value>
-        public IEnumerable<Guid> ItemsRemoved { get; set; }
+        public List<Guid> ItemsRemoved { get; set; }
 
         /// <summary>
         /// Gets or sets the items updated.
         /// </summary>
         /// <value>The items updated.</value>
-        public IEnumerable<Guid> ItemsUpdated { get; set; }
+        public List<Guid> ItemsUpdated { get; set; }
+
+        public LibraryUpdateInfo()
+        {
+            Folders = new List<Guid>();
+            ItemsAdded = new List<Guid>();
+            ItemsRemoved = new List<Guid>();
+            ItemsUpdated = new List<Guid>();
+        }
     }
 }

+ 8 - 0
MediaBrowser.Model/Weather/WeatherInfo.cs

@@ -21,5 +21,13 @@ namespace MediaBrowser.Model.Weather
         /// <value>The forecasts.</value>
         [ProtoMember(2)]
         public WeatherForecast[] Forecasts { get; set; }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="WeatherInfo"/> class.
+        /// </summary>
+        public WeatherInfo()
+        {
+            Forecasts = new WeatherForecast[] {};
+        }
     }
 }

+ 1 - 1
MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs

@@ -55,7 +55,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
         /// <summary>
         /// The audio image resource pool
         /// </summary>
-        private readonly SemaphoreSlim _audioImageResourcePool = new SemaphoreSlim(2, 2);
+        private readonly SemaphoreSlim _audioImageResourcePool = new SemaphoreSlim(1, 1);
 
         /// <summary>
         /// The _subtitle extraction resource pool

+ 2 - 0
MediaBrowser.Server.Implementations/ScheduledTasks/AudioImagesTask.cs

@@ -147,6 +147,8 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
                 {
                     // Image is already in the cache
                     item.PrimaryImagePath = path;
+
+                    await _libraryManager.SaveItem(item, cancellationToken).ConfigureAwait(false);
                 }
             }
 

+ 15 - 27
MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs

@@ -2,6 +2,7 @@
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
 using System;
 using System.Collections.Generic;
@@ -60,40 +61,27 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="progress">The progress.</param>
         /// <returns>Task.</returns>
-        public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+        public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
         {
-            var videos = _libraryManager.RootFolder.RecursiveChildren.OfType<Video>().Where(v => v.Chapters != null).ToList();
+            var videos = _libraryManager.RootFolder.RecursiveChildren
+                .OfType<Video>()
+                .Where(v => v.Chapters != null && v.Chapters.Count != 0)
+                .ToList();
 
             var numComplete = 0;
 
-            var tasks = videos.Select(v => Task.Run(async () =>
+            foreach (var video in videos)
             {
-                try
-                {
-                    await _kernel.FFMpegManager.PopulateChapterImages(v, cancellationToken, true, true);
-                }
-                catch (OperationCanceledException)
-                {
-                    throw;
-                }
-                catch (Exception ex)
-                {
-                    _logger.ErrorException("Error creating chapter images for {0}", ex, v.Name);
-                }
-                finally
-                {
-                    lock (progress)
-                    {
-                        numComplete++;
-                        double percent = numComplete;
-                        percent /= videos.Count;
+                cancellationToken.ThrowIfCancellationRequested();
+
+                await _kernel.FFMpegManager.PopulateChapterImages(video, cancellationToken, true, true);
 
-                        progress.Report(100 * percent);
-                    }
-                }
-            }));
+                numComplete++;
+                double percent = numComplete;
+                percent /= videos.Count;
 
-            return Task.WhenAll(tasks);
+                progress.Report(100 * percent);
+            }
         }
 
         /// <summary>

+ 2 - 0
MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs

@@ -176,6 +176,8 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
                 {
                     // Image is already in the cache
                     item.PrimaryImagePath = path;
+
+                    await _libraryManager.SaveItem(item, cancellationToken).ConfigureAwait(false);
                 }
             }
 

+ 77 - 3
MediaBrowser.ServerApplication/EntryPoints/WebSocketEvents.cs

@@ -1,4 +1,6 @@
-using MediaBrowser.Common.Events;
+using System.Linq;
+using System.Threading;
+using MediaBrowser.Common.Events;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Plugins;
 using MediaBrowser.Common.ScheduledTasks;
@@ -8,6 +10,7 @@ using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Plugins;
 using MediaBrowser.Controller.Updates;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Tasks;
 using MediaBrowser.Model.Updates;
@@ -49,8 +52,33 @@ namespace MediaBrowser.ServerApplication.EntryPoints
         /// </summary>
         private readonly IServerApplicationHost _appHost;
 
+        /// <summary>
+        /// The _task manager
+        /// </summary>
         private readonly ITaskManager _taskManager;
-        
+
+        /// <summary>
+        /// The _library changed sync lock
+        /// </summary>
+        private readonly object _libraryChangedSyncLock = new object();
+
+        /// <summary>
+        /// Gets or sets the library update info.
+        /// </summary>
+        /// <value>The library update info.</value>
+        private LibraryUpdateInfo LibraryUpdateInfo { get; set; }
+
+        /// <summary>
+        /// Gets or sets the library update timer.
+        /// </summary>
+        /// <value>The library update timer.</value>
+        private Timer LibraryUpdateTimer { get; set; }
+
+        /// <summary>
+        /// The library update duration
+        /// </summary>
+        private const int LibraryUpdateDuration = 60000;
+
         /// <summary>
         /// Initializes a new instance of the <see cref="WebSocketEvents" /> class.
         /// </summary>
@@ -145,7 +173,47 @@ namespace MediaBrowser.ServerApplication.EntryPoints
         /// <param name="e">The <see cref="ChildrenChangedEventArgs" /> instance containing the event data.</param>
         void libraryManager_LibraryChanged(object sender, ChildrenChangedEventArgs e)
         {
-            _serverManager.SendWebSocketMessage("LibraryChanged", () => DtoBuilder.GetLibraryUpdateInfo(e));
+            lock (_libraryChangedSyncLock)
+            {
+                if (LibraryUpdateInfo == null)
+                {
+                    LibraryUpdateInfo = new LibraryUpdateInfo();
+                }
+
+                if (LibraryUpdateTimer == null)
+                {
+                    LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
+                                                   Timeout.Infinite);
+                }
+                else
+                {
+                    LibraryUpdateTimer.Change(LibraryUpdateDuration, Timeout.Infinite);
+                }
+
+                LibraryUpdateInfo.Folders.Add(e.Folder.Id);
+
+                LibraryUpdateInfo.ItemsAdded.AddRange(e.ItemsAdded.Select(i => i.Id));
+                LibraryUpdateInfo.ItemsUpdated.AddRange(e.ItemsUpdated.Select(i => i.Id));
+                LibraryUpdateInfo.ItemsRemoved.AddRange(e.ItemsRemoved.Select(i => i.Id));
+            }
+        }
+
+        /// <summary>
+        /// Libraries the update timer callback.
+        /// </summary>
+        /// <param name="state">The state.</param>
+        private void LibraryUpdateTimerCallback(object state)
+        {
+            lock (_libraryChangedSyncLock)
+            {
+                _serverManager.SendWebSocketMessage("LibraryChanged", LibraryUpdateInfo);
+
+                if (LibraryUpdateTimer != null)
+                {
+                    LibraryUpdateTimer.Dispose();
+                    LibraryUpdateTimer = null;
+                }
+            }
         }
 
         /// <summary>
@@ -206,6 +274,12 @@ namespace MediaBrowser.ServerApplication.EntryPoints
         {
             if (dispose)
             {
+                if (LibraryUpdateTimer != null)
+                {
+                    LibraryUpdateTimer.Dispose();
+                    LibraryUpdateTimer = null;
+                }
+                
                 _userManager.UserDeleted -= userManager_UserDeleted;
                 _userManager.UserUpdated -= userManager_UserUpdated;
 

+ 3 - 0
MediaBrowser.sln

@@ -205,4 +205,7 @@ Global
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 	EndGlobalSection
+	GlobalSection(Performance) = preSolution
+		HasPerformanceSessions = true
+	EndGlobalSection
 EndGlobal