Browse Source

removed local trailers and special features from memory

Luke Pulverenti 12 năm trước cách đây
mục cha
commit
fbd052abfc
22 tập tin đã thay đổi với 325 bổ sung387 xóa
  1. 1 1
      MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
  2. 6 8
      MediaBrowser.Api/UserLibrary/ItemsService.cs
  3. 9 5
      MediaBrowser.Api/UserLibrary/UserLibraryService.cs
  4. 0 4
      MediaBrowser.Common.Implementations/BaseApplicationHost.cs
  5. 1 1
      MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
  6. 2 2
      MediaBrowser.Controller/Dto/DtoBuilder.cs
  7. 65 97
      MediaBrowser.Controller/Entities/BaseItem.cs
  8. 26 44
      MediaBrowser.Controller/Entities/Movies/Movie.cs
  9. 8 1
      MediaBrowser.Controller/Persistence/IItemRepository.cs
  10. 5 11
      MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs
  11. 0 1
      MediaBrowser.Controller/Providers/TV/RemoteEpisodeProvider.cs
  12. 0 1
      MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs
  13. 0 1
      MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs
  14. 6 49
      MediaBrowser.Server.Implementations/Library/LibraryManager.cs
  15. 13 15
      MediaBrowser.Server.Implementations/ScheduledTasks/ImageCleanupTask.cs
  16. 11 4
      MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs
  17. 22 20
      MediaBrowser.Server.Implementations/Sqlite/SQLiteDisplayPreferencesRepository.cs
  18. 62 41
      MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs
  19. 10 6
      MediaBrowser.Server.Implementations/Sqlite/SQLiteRepository.cs
  20. 37 33
      MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs
  21. 38 32
      MediaBrowser.Server.Implementations/Sqlite/SQLiteUserRepository.cs
  22. 3 10
      MediaBrowser.ServerApplication/LibraryExplorer.xaml.cs

+ 1 - 1
MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs

@@ -96,7 +96,7 @@ namespace MediaBrowser.Api.Playback.Progressive
 
 
                     var bytesRead = fsPosition - position;
                     var bytesRead = fsPosition - position;
 
 
-                    Logger.Debug("Streamed {0} bytes from file {1}", bytesRead, path);
+                    //Logger.Debug("Streamed {0} bytes from file {1}", bytesRead, path);
 
 
                     if (bytesRead == 0)
                     if (bytesRead == 0)
                     {
                     {

+ 6 - 8
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -547,22 +547,22 @@ namespace MediaBrowser.Api.UserLibrary
 
 
             if (request.HasTrailer.HasValue)
             if (request.HasTrailer.HasValue)
             {
             {
-                items = items.Where(i => request.HasTrailer.Value ? i.LocalTrailers.Count > 0 : i.LocalTrailers.Count == 0);
+                items = items.Where(i => request.HasTrailer.Value ? i.LocalTrailerIds.Count > 0 : i.LocalTrailerIds.Count == 0);
             }
             }
 
 
             if (request.HasThemeSong.HasValue)
             if (request.HasThemeSong.HasValue)
             {
             {
-                items = items.Where(i => request.HasThemeSong.Value ? i.ThemeSongs.Count > 0 : i.ThemeSongs.Count == 0);
+                items = items.Where(i => request.HasThemeSong.Value ? i.ThemeSongIds.Count > 0 : i.ThemeSongIds.Count == 0);
             }
             }
 
 
             if (request.HasThemeVideo.HasValue)
             if (request.HasThemeVideo.HasValue)
             {
             {
-                items = items.Where(i => request.HasThemeVideo.Value ? i.ThemeVideos.Count > 0 : i.ThemeVideos.Count == 0);
+                items = items.Where(i => request.HasThemeVideo.Value ? i.ThemeVideoIds.Count > 0 : i.ThemeVideoIds.Count == 0);
             }
             }
 
 
             if (request.HasSpecialFeature.HasValue)
             if (request.HasSpecialFeature.HasValue)
             {
             {
-                items = items.OfType<Movie>().Where(i => request.HasSpecialFeature.Value ? i.SpecialFeatures.Count > 0 : i.SpecialFeatures.Count == 0);
+                items = items.OfType<Movie>().Where(i => request.HasSpecialFeature.Value ? i.SpecialFeatureIds.Count > 0 : i.SpecialFeatureIds.Count == 0);
             }
             }
 
 
             if (request.HasSubtitles.HasValue)
             if (request.HasSubtitles.HasValue)
@@ -573,10 +573,8 @@ namespace MediaBrowser.Api.UserLibrary
                     {
                     {
                         return i.MediaStreams != null && i.MediaStreams.Any(m => m.Type == MediaStreamType.Subtitle);
                         return i.MediaStreams != null && i.MediaStreams.Any(m => m.Type == MediaStreamType.Subtitle);
                     }
                     }
-                    else
-                    {
-                        return i.MediaStreams == null || i.MediaStreams.All(m => m.Type != MediaStreamType.Subtitle);
-                    }
+
+                    return i.MediaStreams == null || i.MediaStreams.All(m => m.Type != MediaStreamType.Subtitle);
                 });
                 });
             }
             }
 
 

+ 9 - 5
MediaBrowser.Api/UserLibrary/UserLibraryService.cs

@@ -399,19 +399,23 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// </summary>
         private readonly ILibraryManager _libraryManager;
         private readonly ILibraryManager _libraryManager;
 
 
+        private readonly IItemRepository _itemRepo;
+
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="UserLibraryService" /> class.
         /// Initializes a new instance of the <see cref="UserLibraryService" /> class.
         /// </summary>
         /// </summary>
         /// <param name="userManager">The user manager.</param>
         /// <param name="userManager">The user manager.</param>
         /// <param name="libraryManager">The library manager.</param>
         /// <param name="libraryManager">The library manager.</param>
         /// <param name="userDataRepository">The user data repository.</param>
         /// <param name="userDataRepository">The user data repository.</param>
+        /// <param name="itemRepo">The item repo.</param>
         /// <exception cref="System.ArgumentNullException">jsonSerializer</exception>
         /// <exception cref="System.ArgumentNullException">jsonSerializer</exception>
-        public UserLibraryService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository)
+        public UserLibraryService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository, IItemRepository itemRepo)
             : base()
             : base()
         {
         {
             _userManager = userManager;
             _userManager = userManager;
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
             _userDataRepository = userDataRepository;
             _userDataRepository = userDataRepository;
+            _itemRepo = itemRepo;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -432,7 +436,7 @@ namespace MediaBrowser.Api.UserLibrary
 
 
             var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository);
             var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository);
 
 
-            var items = movie.SpecialFeatures.OrderBy(i => i.SortName).Select(i => dtoBuilder.GetBaseItemDto(i, user, fields)).Select(t => t.Result).ToList();
+            var items = _itemRepo.GetItems(movie.SpecialFeatureIds).OrderBy(i => i.SortName).Select(i => dtoBuilder.GetBaseItemDto(i, user, fields)).Select(t => t.Result).ToList();
 
 
             return ToOptimizedResult(items);
             return ToOptimizedResult(items);
         }
         }
@@ -453,7 +457,7 @@ namespace MediaBrowser.Api.UserLibrary
 
 
             var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository);
             var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository);
 
 
-            var items = item.LocalTrailers.OrderBy(i => i.SortName).Select(i => dtoBuilder.GetBaseItemDto(i, user, fields)).Select(t => t.Result).ToList();
+            var items = _itemRepo.GetItems(item.LocalTrailerIds).OrderBy(i => i.SortName).Select(i => dtoBuilder.GetBaseItemDto(i, user, fields)).Select(t => t.Result).ToList();
 
 
             return ToOptimizedResult(items);
             return ToOptimizedResult(items);
         }
         }
@@ -474,7 +478,7 @@ namespace MediaBrowser.Api.UserLibrary
 
 
             var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository);
             var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository);
 
 
-            var items = item.ThemeSongs.OrderBy(i => i.SortName).Select(i => dtoBuilder.GetBaseItemDto(i, user, fields)).Select(t => t.Result).ToArray();
+            var items = _itemRepo.GetItems(item.ThemeSongIds).OrderBy(i => i.SortName).Select(i => dtoBuilder.GetBaseItemDto(i, user, fields)).Select(t => t.Result).ToArray();
 
 
             var result = new ThemeSongsResult
             var result = new ThemeSongsResult
             {
             {
@@ -502,7 +506,7 @@ namespace MediaBrowser.Api.UserLibrary
 
 
             var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository);
             var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository);
 
 
-            var items = item.ThemeVideos.OrderBy(i => i.SortName).Select(i => dtoBuilder.GetBaseItemDto(i, user, fields)).Select(t => t.Result).ToArray();
+            var items = _itemRepo.GetItems(item.ThemeVideoIds).OrderBy(i => i.SortName).Select(i => dtoBuilder.GetBaseItemDto(i, user, fields)).Select(t => t.Result).ToArray();
 
 
             var result = new ThemeVideosResult
             var result = new ThemeVideosResult
             {
             {

+ 0 - 4
MediaBrowser.Common.Implementations/BaseApplicationHost.cs

@@ -340,8 +340,6 @@ namespace MediaBrowser.Common.Implementations
         protected void RegisterSingleInstance<T>(T obj, bool manageLifetime = true)
         protected void RegisterSingleInstance<T>(T obj, bool manageLifetime = true)
             where T : class
             where T : class
         {
         {
-            Logger.Info("Registering " + obj.GetType().Name);
-
             Container.RegisterSingle(obj);
             Container.RegisterSingle(obj);
 
 
             if (manageLifetime)
             if (manageLifetime)
@@ -421,8 +419,6 @@ namespace MediaBrowser.Common.Implementations
         {
         {
             var currentType = typeof(T);
             var currentType = typeof(T);
 
 
-            Logger.Info("Composing instances of " + currentType.Name);
-
             var parts = AllConcreteTypes.AsParallel().Where(currentType.IsAssignableFrom).Select(CreateInstance).Cast<T>().ToArray();
             var parts = AllConcreteTypes.AsParallel().Where(currentType.IsAssignableFrom).Select(CreateInstance).Cast<T>().ToArray();
 
 
             if (manageLiftime)
             if (manageLiftime)

+ 1 - 1
MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs

@@ -136,7 +136,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
                 {
                 {
                     var now = DateTime.UtcNow;
                     var now = DateTime.UtcNow;
 
 
-                    var isCacheValid = (!cachedInfo.MustRevalidate && !string.IsNullOrEmpty(cachedInfo.Etag) && (now - cachedInfo.RequestDate).TotalDays < 14)
+                    var isCacheValid = (!cachedInfo.MustRevalidate && !string.IsNullOrEmpty(cachedInfo.Etag) && (now - cachedInfo.RequestDate).TotalDays < 7)
                         || (cachedInfo.Expires.HasValue && cachedInfo.Expires.Value > now);
                         || (cachedInfo.Expires.HasValue && cachedInfo.Expires.Value > now);
 
 
                     if (isCacheValid)
                     if (isCacheValid)

+ 2 - 2
MediaBrowser.Controller/Dto/DtoBuilder.cs

@@ -331,7 +331,7 @@ namespace MediaBrowser.Controller.Dto
                 dto.CriticRatingSummary = item.CriticRatingSummary;
                 dto.CriticRatingSummary = item.CriticRatingSummary;
             }
             }
 
 
-            var localTrailerCount = item.LocalTrailers == null ? 0 : item.LocalTrailers.Count;
+            var localTrailerCount = item.LocalTrailerIds.Count;
 
 
             if (localTrailerCount > 0)
             if (localTrailerCount > 0)
             {
             {
@@ -492,7 +492,7 @@ namespace MediaBrowser.Controller.Dto
 
 
             if (movie != null)
             if (movie != null)
             {
             {
-                var specialFeatureCount = movie.SpecialFeatures == null ? 0 : movie.SpecialFeatures.Count;
+                var specialFeatureCount = movie.SpecialFeatureIds.Count;
 
 
                 if (specialFeatureCount > 0)
                 if (specialFeatureCount > 0)
                 {
                 {

+ 65 - 97
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -38,6 +38,9 @@ namespace MediaBrowser.Controller.Entities
             Images = new Dictionary<ImageType, string>();
             Images = new Dictionary<ImageType, string>();
             ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
             ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
             Tags = new List<string>();
             Tags = new List<string>();
+            ThemeSongIds = new List<Guid>();
+            ThemeVideoIds = new List<Guid>();
+            LocalTrailerIds = new List<Guid>();
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -572,7 +575,7 @@ namespace MediaBrowser.Controller.Entities
         /// </summary>
         /// </summary>
         /// <value>The tags.</value>
         /// <value>The tags.</value>
         public List<string> Tags { get; set; }
         public List<string> Tags { get; set; }
-        
+
         /// <summary>
         /// <summary>
         /// Override this if you need to combine/collapse person information
         /// Override this if you need to combine/collapse person information
         /// </summary>
         /// </summary>
@@ -612,7 +615,7 @@ namespace MediaBrowser.Controller.Entities
         /// </summary>
         /// </summary>
         /// <value>The revenue.</value>
         /// <value>The revenue.</value>
         public double? Revenue { get; set; }
         public double? Revenue { get; set; }
-        
+
         /// <summary>
         /// <summary>
         /// Gets or sets the production locations.
         /// Gets or sets the production locations.
         /// </summary>
         /// </summary>
@@ -630,7 +633,7 @@ namespace MediaBrowser.Controller.Entities
         /// </summary>
         /// </summary>
         /// <value>The critic rating summary.</value>
         /// <value>The critic rating summary.</value>
         public string CriticRatingSummary { get; set; }
         public string CriticRatingSummary { get; set; }
-        
+
         /// <summary>
         /// <summary>
         /// Gets or sets the community rating.
         /// Gets or sets the community rating.
         /// </summary>
         /// </summary>
@@ -672,84 +675,9 @@ namespace MediaBrowser.Controller.Entities
         /// <value>The critic reviews.</value>
         /// <value>The critic reviews.</value>
         public List<ItemReview> CriticReviews { get; set; }
         public List<ItemReview> CriticReviews { get; set; }
 
 
-        /// <summary>
-        /// The _local trailers
-        /// </summary>
-        private List<Trailer> _localTrailers;
-        /// <summary>
-        /// The _local trailers initialized
-        /// </summary>
-        private bool _localTrailersInitialized;
-        /// <summary>
-        /// The _local trailers sync lock
-        /// </summary>
-        private object _localTrailersSyncLock = new object();
-        /// <summary>
-        /// Gets the local trailers.
-        /// </summary>
-        /// <value>The local trailers.</value>
-        [IgnoreDataMember]
-        public List<Trailer> LocalTrailers
-        {
-            get
-            {
-                LazyInitializer.EnsureInitialized(ref _localTrailers, ref _localTrailersInitialized, ref _localTrailersSyncLock, LoadLocalTrailers);
-                return _localTrailers;
-            }
-            private set
-            {
-                _localTrailers = value;
-
-                if (value == null)
-                {
-                    _localTrailersInitialized = false;
-                }
-            }
-        }
-
-        private List<Audio.Audio> _themeSongs;
-        private bool _themeSongsInitialized;
-        private object _themeSongsSyncLock = new object();
-        [IgnoreDataMember]
-        public List<Audio.Audio> ThemeSongs
-        {
-            get
-            {
-                LazyInitializer.EnsureInitialized(ref _themeSongs, ref _themeSongsInitialized, ref _themeSongsSyncLock, LoadThemeSongs);
-                return _themeSongs;
-            }
-            private set
-            {
-                _themeSongs = value;
-
-                if (value == null)
-                {
-                    _themeSongsInitialized = false;
-                }
-            }
-        }
-
-        private List<Video> _themeVideos;
-        private bool _themeVideosInitialized;
-        private object _themeVideosSyncLock = new object();
-        [IgnoreDataMember]
-        public List<Video> ThemeVideos
-        {
-            get
-            {
-                LazyInitializer.EnsureInitialized(ref _themeVideos, ref _themeVideosInitialized, ref _themeVideosSyncLock, LoadThemeVideos);
-                return _themeVideos;
-            }
-            private set
-            {
-                _themeVideos = value;
-
-                if (value == null)
-                {
-                    _themeVideosInitialized = false;
-                }
-            }
-        }
+        public List<Guid> ThemeSongIds { get; set; }
+        public List<Guid> ThemeVideoIds { get; set; }
+        public List<Guid> LocalTrailerIds { get; set; }
 
 
         /// <summary>
         /// <summary>
         /// Loads local trailers from the file system
         /// Loads local trailers from the file system
@@ -956,36 +884,25 @@ namespace MediaBrowser.Controller.Entities
                 ResolveArgs = null;
                 ResolveArgs = null;
             }
             }
 
 
-            // Lazy load these again
-            LocalTrailers = null;
-            ThemeSongs = null;
-            ThemeVideos = null;
-
             // Refresh for the item
             // Refresh for the item
             var itemRefreshTask = ProviderManager.ExecuteMetadataProviders(this, cancellationToken, forceRefresh, allowSlowProviders);
             var itemRefreshTask = ProviderManager.ExecuteMetadataProviders(this, cancellationToken, forceRefresh, allowSlowProviders);
 
 
             cancellationToken.ThrowIfCancellationRequested();
             cancellationToken.ThrowIfCancellationRequested();
 
 
-            // Refresh metadata for local trailers
-            var trailerTasks = LocalTrailers.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders));
+            var themeSongsChanged = await RefreshThemeSongs(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false);
 
 
-            var themeSongTasks = ThemeSongs.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders));
+            var themeVideosChanged = await RefreshThemeVideos(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false);
 
 
-            var videoBackdropTasks = ThemeVideos.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders));
-            
-            cancellationToken.ThrowIfCancellationRequested();
+            var localTrailersChanged = await RefreshLocalTrailers(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false);
 
 
-            // Await the trailer tasks
-            await Task.WhenAll(trailerTasks).ConfigureAwait(false);
-            await Task.WhenAll(themeSongTasks).ConfigureAwait(false);
-            await Task.WhenAll(videoBackdropTasks).ConfigureAwait(false);
+            cancellationToken.ThrowIfCancellationRequested();
 
 
             cancellationToken.ThrowIfCancellationRequested();
             cancellationToken.ThrowIfCancellationRequested();
 
 
             // Get the result from the item task
             // Get the result from the item task
             var changed = await itemRefreshTask.ConfigureAwait(false);
             var changed = await itemRefreshTask.ConfigureAwait(false);
 
 
-            if (changed || forceSave)
+            if (changed || forceSave || themeSongsChanged || themeVideosChanged || localTrailersChanged)
             {
             {
                 cancellationToken.ThrowIfCancellationRequested();
                 cancellationToken.ThrowIfCancellationRequested();
 
 
@@ -995,6 +912,57 @@ namespace MediaBrowser.Controller.Entities
             return changed;
             return changed;
         }
         }
 
 
+        private async Task<bool> RefreshLocalTrailers(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
+        {
+            var newItems = LoadLocalTrailers().ToList();
+            var newItemIds = newItems.Select(i => i.Id).ToList();
+
+            var itemsChanged = !LocalTrailerIds.SequenceEqual(newItemIds);
+
+            var tasks = newItems.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders));
+
+            var results = await Task.WhenAll(tasks).ConfigureAwait(false);
+
+            LocalTrailerIds = newItemIds;
+
+            return itemsChanged || results.Contains(true);
+        }
+        
+        private async Task<bool> RefreshThemeVideos(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
+        {
+            var newThemeVideos = LoadThemeVideos().ToList();
+            var newThemeVideoIds = newThemeVideos.Select(i => i.Id).ToList();
+
+            var themeVideosChanged = !ThemeVideoIds.SequenceEqual(newThemeVideoIds);
+
+            var tasks = newThemeVideos.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders));
+
+            var results = await Task.WhenAll(tasks).ConfigureAwait(false);
+
+            ThemeVideoIds = newThemeVideoIds;
+
+            return themeVideosChanged || results.Contains(true);
+        }
+        
+        /// <summary>
+        /// Refreshes the theme songs.
+        /// </summary>
+        private async Task<bool> RefreshThemeSongs(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
+        {
+            var newThemeSongs = LoadThemeSongs().ToList();
+            var newThemeSongIds = newThemeSongs.Select(i => i.Id).ToList();
+
+            var themeSongsChanged = !ThemeSongIds.SequenceEqual(newThemeSongIds);
+
+            var tasks = newThemeSongs.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders));
+
+            var results = await Task.WhenAll(tasks).ConfigureAwait(false);
+
+            ThemeSongIds = newThemeSongIds;
+
+            return themeSongsChanged || results.Contains(true);
+        }
+
         /// <summary>
         /// <summary>
         /// Clear out all metadata properties. Extend for sub-classes.
         /// Clear out all metadata properties. Extend for sub-classes.
         /// </summary>
         /// </summary>

+ 26 - 44
MediaBrowser.Controller/Entities/Movies/Movie.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.IO;
+using System;
+using MediaBrowser.Controller.IO;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.IO;
 using System.IO;
@@ -14,6 +15,13 @@ namespace MediaBrowser.Controller.Entities.Movies
     /// </summary>
     /// </summary>
     public class Movie : Video
     public class Movie : Video
     {
     {
+        public List<Guid> SpecialFeatureIds { get; set; }
+
+        public Movie()
+        {
+            SpecialFeatureIds = new List<Guid>();
+        }
+        
         /// <summary>
         /// <summary>
         /// Should be overridden to return the proper folder where metadata lives
         /// Should be overridden to return the proper folder where metadata lives
         /// </summary>
         /// </summary>
@@ -36,41 +44,6 @@ namespace MediaBrowser.Controller.Entities.Movies
             return this.GetProviderId(MetadataProviders.Tmdb) ?? this.GetProviderId(MetadataProviders.Imdb) ?? base.GetUserDataKey();
             return this.GetProviderId(MetadataProviders.Tmdb) ?? this.GetProviderId(MetadataProviders.Imdb) ?? base.GetUserDataKey();
         }
         }
 
 
-        /// <summary>
-        /// The _special features
-        /// </summary>
-        private List<Video> _specialFeatures;
-        /// <summary>
-        /// The _special features initialized
-        /// </summary>
-        private bool _specialFeaturesInitialized;
-        /// <summary>
-        /// The _special features sync lock
-        /// </summary>
-        private object _specialFeaturesSyncLock = new object();
-        /// <summary>
-        /// Gets the special features.
-        /// </summary>
-        /// <value>The special features.</value>
-        [IgnoreDataMember]
-        public List<Video> SpecialFeatures
-        {
-            get
-            {
-                LazyInitializer.EnsureInitialized(ref _specialFeatures, ref _specialFeaturesInitialized, ref _specialFeaturesSyncLock, () => LoadSpecialFeatures().ToList());
-                return _specialFeatures;
-            }
-            private set
-            {
-                _specialFeatures = value;
-
-                if (value == null)
-                {
-                    _specialFeaturesInitialized = false;
-                }
-            }
-        }
-
         /// <summary>
         /// <summary>
         /// Needed because the resolver stops at the movie folder and we find the video inside.
         /// Needed because the resolver stops at the movie folder and we find the video inside.
         /// </summary>
         /// </summary>
@@ -94,21 +67,30 @@ namespace MediaBrowser.Controller.Entities.Movies
         /// <returns>Task{System.Boolean}.</returns>
         /// <returns>Task{System.Boolean}.</returns>
         public override async Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true, bool resetResolveArgs = true)
         public override async Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true, bool resetResolveArgs = true)
         {
         {
-            // Lazy load these again
-            SpecialFeatures = null;
-
             // Kick off a task to refresh the main item
             // Kick off a task to refresh the main item
             var result = await base.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders, resetResolveArgs).ConfigureAwait(false);
             var result = await base.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders, resetResolveArgs).ConfigureAwait(false);
 
 
-            var tasks = SpecialFeatures.Select(item => item.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders));
+            var specialFeaturesChanged = await RefreshSpecialFeatures(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false);
 
 
-            await Task.WhenAll(tasks).ConfigureAwait(false);
+            return specialFeaturesChanged || result;
+        }
+
+        private async Task<bool> RefreshSpecialFeatures(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
+        {
+            var newItems = LoadSpecialFeatures().ToList();
+            var newItemIds = newItems.Select(i => i.Id).ToList();
 
 
-            cancellationToken.ThrowIfCancellationRequested();
+            var itemsChanged = !SpecialFeatureIds.SequenceEqual(newItemIds);
 
 
-            return result;
-        }
+            var tasks = newItems.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders));
 
 
+            var results = await Task.WhenAll(tasks).ConfigureAwait(false);
+
+            SpecialFeatureIds = newItemIds;
+
+            return itemsChanged || results.Contains(true);
+        }
+        
         /// <summary>
         /// <summary>
         /// Loads the special features.
         /// Loads the special features.
         /// </summary>
         /// </summary>

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

@@ -24,7 +24,7 @@ namespace MediaBrowser.Controller.Persistence
         /// </summary>
         /// </summary>
         /// <param name="id">The id.</param>
         /// <param name="id">The id.</param>
         /// <returns>BaseItem.</returns>
         /// <returns>BaseItem.</returns>
-        BaseItem RetrieveItem(Guid id);
+        BaseItem GetItem(Guid id);
 
 
         /// <summary>
         /// <summary>
         /// Gets children of a given Folder
         /// Gets children of a given Folder
@@ -33,6 +33,13 @@ namespace MediaBrowser.Controller.Persistence
         /// <returns>IEnumerable{BaseItem}.</returns>
         /// <returns>IEnumerable{BaseItem}.</returns>
         IEnumerable<BaseItem> RetrieveChildren(Folder parent);
         IEnumerable<BaseItem> RetrieveChildren(Folder parent);
 
 
+        /// <summary>
+        /// Retrieves the items.
+        /// </summary>
+        /// <param name="ids">The ids.</param>
+        /// <returns>IEnumerable{BaseItem}.</returns>
+        IEnumerable<BaseItem> GetItems(IEnumerable<Guid> ids);
+
         /// <summary>
         /// <summary>
         /// Saves children of a given Folder
         /// Saves children of a given Folder
         /// </summary>
         /// </summary>

+ 5 - 11
MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs

@@ -57,15 +57,9 @@ namespace MediaBrowser.Controller.Providers.Music
 
 
             if (result != null)
             if (result != null)
             {
             {
-                if (!string.IsNullOrEmpty(result.Item1))
+                if (!string.IsNullOrEmpty(result))
                 {
                 {
-                    return result.Item1;
-                }
-
-                // If there were no artists returned at all, then don't bother with musicbrainz
-                if (!result.Item2)
-                {
-                    return null;
+                    return result;
                 }
                 }
             }
             }
 
 
@@ -94,7 +88,7 @@ namespace MediaBrowser.Controller.Providers.Music
             return artist != null ? artist.GetProviderId(MetadataProviders.Musicbrainz) : null;
             return artist != null ? artist.GetProviderId(MetadataProviders.Musicbrainz) : null;
         }
         }
 
 
-        private async Task<Tuple<string,bool>> FindIdFromLastFm(BaseItem item, CancellationToken cancellationToken)
+        private async Task<string> FindIdFromLastFm(BaseItem item, CancellationToken cancellationToken)
         {
         {
             //Execute the Artist search against our name and assume first one is the one we want
             //Execute the Artist search against our name and assume first one is the one we want
             var url = RootUrl + string.Format("method=artist.search&artist={0}&api_key={1}&format=json", UrlEncode(item.Name), ApiKey);
             var url = RootUrl + string.Format("method=artist.search&artist={0}&api_key={1}&format=json", UrlEncode(item.Name), ApiKey);
@@ -125,10 +119,10 @@ namespace MediaBrowser.Controller.Providers.Music
                 var artist = searchResult.results.artistmatches.artist.FirstOrDefault(i => i.name != null && string.Compare(i.name, item.Name, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace) == 0) ??
                 var artist = searchResult.results.artistmatches.artist.FirstOrDefault(i => i.name != null && string.Compare(i.name, item.Name, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace) == 0) ??
                     searchResult.results.artistmatches.artist.First();
                     searchResult.results.artistmatches.artist.First();
 
 
-                return new Tuple<string, bool>(artist.mbid, true);
+                return artist.mbid;
             }
             }
 
 
-            return new Tuple<string,bool>(null, false);
+            return null;
         }
         }
 
 
         private async Task<string> FindIdFromMusicBrainz(BaseItem item, CancellationToken cancellationToken)
         private async Task<string> FindIdFromMusicBrainz(BaseItem item, CancellationToken cancellationToken)

+ 0 - 1
MediaBrowser.Controller/Providers/TV/RemoteEpisodeProvider.cs

@@ -146,7 +146,6 @@ namespace MediaBrowser.Controller.Providers.TV
             string name = episode.Name;
             string name = episode.Name;
             string location = episode.Path;
             string location = episode.Path;
 
 
-            Logger.Debug("TvDbProvider: Fetching episode data for: " + name);
             string epNum = TVUtils.EpisodeNumberFromFile(location, episode.Season != null);
             string epNum = TVUtils.EpisodeNumberFromFile(location, episode.Season != null);
 
 
             if (epNum == null)
             if (epNum == null)

+ 0 - 1
MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs

@@ -130,7 +130,6 @@ namespace MediaBrowser.Controller.Providers.TV
         {
         {
             string name = season.Name;
             string name = season.Name;
 
 
-            Logger.Debug("TvDbProvider: Fetching season data: " + name);
             var seasonNumber = TVUtils.GetSeasonNumberFromPath(season.Path) ?? -1;
             var seasonNumber = TVUtils.GetSeasonNumberFromPath(season.Path) ?? -1;
 
 
             season.IndexNumber = seasonNumber;
             season.IndexNumber = seasonNumber;

+ 0 - 1
MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs

@@ -193,7 +193,6 @@ namespace MediaBrowser.Controller.Providers.TV
             var success = false;
             var success = false;
 
 
             var name = series.Name;
             var name = series.Name;
-            Logger.Debug("TvDbProvider: Fetching series data: " + name);
 
 
             if (!string.IsNullOrEmpty(seriesId))
             if (!string.IsNullOrEmpty(seriesId))
             {
             {

+ 6 - 49
MediaBrowser.Server.Implementations/Library/LibraryManager.cs

@@ -4,7 +4,6 @@ using MediaBrowser.Common.ScheduledTasks;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Persistence;
@@ -267,16 +266,6 @@ namespace MediaBrowser.Server.Implementations.Library
 
 
             items.Add(RootFolder);
             items.Add(RootFolder);
 
 
-            var specialFeatures = items.OfType<Movie>().SelectMany(i => i.SpecialFeatures).ToList();
-            var localTrailers = items.SelectMany(i => i.LocalTrailers).ToList();
-            var themeSongs = items.SelectMany(i => i.ThemeSongs).ToList();
-            var themeVideos = items.SelectMany(i => i.ThemeVideos).ToList();
-
-            items.AddRange(specialFeatures);
-            items.AddRange(localTrailers);
-            items.AddRange(themeSongs);
-            items.AddRange(themeVideos);
-
             // Need to use DistinctBy Id because there could be multiple instances with the same id
             // Need to use DistinctBy Id because there could be multiple instances with the same id
             // due to sharing the default library
             // due to sharing the default library
             var userRootFolders = _userManager.Users.Select(i => i.RootFolder)
             var userRootFolders = _userManager.Users.Select(i => i.RootFolder)
@@ -304,39 +293,6 @@ namespace MediaBrowser.Server.Implementations.Library
         private void UpdateItemInLibraryCache(BaseItem item)
         private void UpdateItemInLibraryCache(BaseItem item)
         {
         {
             LibraryItemsCache.AddOrUpdate(item.Id, item, delegate { return item; });
             LibraryItemsCache.AddOrUpdate(item.Id, item, delegate { return item; });
-
-            foreach (var subItem in item.LocalTrailers)
-            {
-                // Prevent access to foreach variable in closure
-                var copy = subItem;
-                LibraryItemsCache.AddOrUpdate(subItem.Id, subItem, delegate { return copy; });
-            }
-
-            foreach (var subItem in item.ThemeSongs)
-            {
-                // Prevent access to foreach variable in closure
-                var copy = subItem;
-                LibraryItemsCache.AddOrUpdate(subItem.Id, subItem, delegate { return copy; });
-            }
-
-            foreach (var subItem in item.ThemeVideos)
-            {
-                // Prevent access to foreach variable in closure
-                var copy = subItem;
-                LibraryItemsCache.AddOrUpdate(subItem.Id, subItem, delegate { return copy; });
-            }
-
-            var movie = item as Movie;
-
-            if (movie != null)
-            {
-                foreach (var subItem in movie.SpecialFeatures)
-                {
-                    // Prevent access to foreach variable in closure
-                    var special1 = subItem;
-                    LibraryItemsCache.AddOrUpdate(subItem.Id, subItem, delegate { return special1; });
-                }
-            }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -661,8 +617,6 @@ namespace MediaBrowser.Server.Implementations.Library
         {
         {
             cancellationToken.ThrowIfCancellationRequested();
             cancellationToken.ThrowIfCancellationRequested();
 
 
-            _logger.Debug("Getting {0}: {1}", typeof(T).Name, name);
-
             path = Path.Combine(path, FileSystem.GetValidFilename(name));
             path = Path.Combine(path, FileSystem.GetValidFilename(name));
 
 
             var fileInfo = new DirectoryInfo(path);
             var fileInfo = new DirectoryInfo(path);
@@ -968,9 +922,12 @@ namespace MediaBrowser.Server.Implementations.Library
 
 
             BaseItem item;
             BaseItem item;
 
 
-            LibraryItemsCache.TryGetValue(id, out item);
+            if (LibraryItemsCache.TryGetValue(id, out item))
+            {
+                return item;
+            }
 
 
-            return item;
+            return ItemRepository.GetItem(id);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -1130,7 +1087,7 @@ namespace MediaBrowser.Server.Implementations.Library
         /// <returns>Task{BaseItem}.</returns>
         /// <returns>Task{BaseItem}.</returns>
         public BaseItem RetrieveItem(Guid id)
         public BaseItem RetrieveItem(Guid id)
         {
         {
-            return ItemRepository.RetrieveItem(id);
+            return ItemRepository.GetItem(id);
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 13 - 15
MediaBrowser.Server.Implementations/ScheduledTasks/ImageCleanupTask.cs

@@ -3,6 +3,7 @@ using MediaBrowser.Controller;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Logging;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
@@ -28,6 +29,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private readonly ILibraryManager _libraryManager;
         private readonly ILibraryManager _libraryManager;
         private readonly IServerApplicationPaths _appPaths;
         private readonly IServerApplicationPaths _appPaths;
+        private readonly IItemRepository _itemRepo;
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="ImageCleanupTask" /> class.
         /// Initializes a new instance of the <see cref="ImageCleanupTask" /> class.
@@ -36,12 +38,13 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
         /// <param name="logger">The logger.</param>
         /// <param name="logger">The logger.</param>
         /// <param name="libraryManager">The library manager.</param>
         /// <param name="libraryManager">The library manager.</param>
         /// <param name="appPaths">The app paths.</param>
         /// <param name="appPaths">The app paths.</param>
-        public ImageCleanupTask(Kernel kernel, ILogger logger, ILibraryManager libraryManager, IServerApplicationPaths appPaths)
+        public ImageCleanupTask(Kernel kernel, ILogger logger, ILibraryManager libraryManager, IServerApplicationPaths appPaths, IItemRepository itemRepo)
         {
         {
             _kernel = kernel;
             _kernel = kernel;
             _logger = logger;
             _logger = logger;
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
             _appPaths = appPaths;
             _appPaths = appPaths;
+            _itemRepo = itemRepo;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -138,20 +141,14 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
                 images = images.Concat(item.ScreenshotImagePaths);
                 images = images.Concat(item.ScreenshotImagePaths);
             }
             }
 
 
-            if (item.LocalTrailers != null)
-            {
-                images = item.LocalTrailers.Aggregate(images, (current, subItem) => current.Concat(GetPathsInUse(subItem)));
-            }
+            var localTrailers = _itemRepo.GetItems(item.LocalTrailerIds).ToList();
+            images = localTrailers.Aggregate(images, (current, subItem) => current.Concat(GetPathsInUse(subItem)));
 
 
-            if (item.ThemeSongs != null)
-            {
-                images = item.ThemeSongs.Aggregate(images, (current, subItem) => current.Concat(GetPathsInUse(subItem)));
-            }
+            var themeSongs = _itemRepo.GetItems(item.ThemeSongIds).ToList();
+            images = themeSongs.Aggregate(images, (current, subItem) => current.Concat(GetPathsInUse(subItem)));
 
 
-            if (item.ThemeVideos != null)
-            {
-                images = item.ThemeVideos.Aggregate(images, (current, subItem) => current.Concat(GetPathsInUse(subItem)));
-            }
+            var themeVideos = _itemRepo.GetItems(item.ThemeVideoIds).ToList();
+            images = themeVideos.Aggregate(images, (current, subItem) => current.Concat(GetPathsInUse(subItem)));
 
 
             var video = item as Video;
             var video = item as Video;
 
 
@@ -162,9 +159,10 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
 
 
             var movie = item as Movie;
             var movie = item as Movie;
 
 
-            if (movie != null && movie.SpecialFeatures != null)
+            if (movie != null)
             {
             {
-                images = movie.SpecialFeatures.Aggregate(images, (current, subItem) => current.Concat(GetPathsInUse(subItem)));
+                var specialFeattures = _itemRepo.GetItems(movie.SpecialFeatureIds).ToList();
+                images = specialFeattures.Aggregate(images, (current, subItem) => current.Concat(GetPathsInUse(subItem)));
             }
             }
             
             
             return images;
             return images;

+ 11 - 4
MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs

@@ -5,6 +5,7 @@ using MediaBrowser.Controller;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Providers.MediaInfo;
 using MediaBrowser.Controller.Providers.MediaInfo;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using System;
 using System;
@@ -43,6 +44,8 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
         /// </summary>
         /// </summary>
         private readonly IIsoManager _isoManager;
         private readonly IIsoManager _isoManager;
 
 
+        private readonly IItemRepository _itemRepo;
+        
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         
         
         /// <summary>
         /// <summary>
@@ -67,11 +70,12 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
         /// <param name="logManager">The log manager.</param>
         /// <param name="logManager">The log manager.</param>
         /// <param name="mediaEncoder">The media encoder.</param>
         /// <param name="mediaEncoder">The media encoder.</param>
         /// <param name="isoManager">The iso manager.</param>
         /// <param name="isoManager">The iso manager.</param>
-        public VideoImagesTask(ILibraryManager libraryManager, ILogManager logManager, IMediaEncoder mediaEncoder, IIsoManager isoManager)
+        public VideoImagesTask(ILibraryManager libraryManager, ILogManager logManager, IMediaEncoder mediaEncoder, IIsoManager isoManager, IItemRepository itemRepo)
         {
         {
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
             _mediaEncoder = mediaEncoder;
             _mediaEncoder = mediaEncoder;
             _isoManager = isoManager;
             _isoManager = isoManager;
+            _itemRepo = itemRepo;
             _logger = logManager.GetLogger(GetType().Name);
             _logger = logManager.GetLogger(GetType().Name);
 
 
             ImageCache = new FileSystemRepository(Kernel.Instance.FFMpegManager.VideoImagesDataPath);
             ImageCache = new FileSystemRepository(Kernel.Instance.FFMpegManager.VideoImagesDataPath);
@@ -205,15 +209,18 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
         {
         {
             var allItems = sourceItems.ToList();
             var allItems = sourceItems.ToList();
 
 
-            var localTrailers = allItems.SelectMany(i => i.LocalTrailers);
-            var themeVideos = allItems.SelectMany(i => i.ThemeVideos);
+            var localTrailers = allItems.SelectMany(i => _itemRepo.GetItems(i.LocalTrailerIds).Cast<Video>());
+
+            var themeVideos = allItems.SelectMany(i => _itemRepo.GetItems(i.ThemeVideoIds).Cast<Video>());
 
 
             var videos = allItems.OfType<Video>().ToList();
             var videos = allItems.OfType<Video>().ToList();
 
 
             var items = videos;
             var items = videos;
             items.AddRange(localTrailers);
             items.AddRange(localTrailers);
+
             items.AddRange(themeVideos);
             items.AddRange(themeVideos);
-            items.AddRange(videos.OfType<Movie>().SelectMany(i => i.SpecialFeatures).ToList());
+
+            items.AddRange(videos.OfType<Movie>().SelectMany(i => _itemRepo.GetItems(i.SpecialFeatureIds).Cast<Video>()).ToList());
 
 
             return items.Where(i =>
             return items.Where(i =>
             {
             {

+ 22 - 20
MediaBrowser.Server.Implementations/Sqlite/SQLiteDisplayPreferencesRepository.cs

@@ -132,29 +132,31 @@ namespace MediaBrowser.Server.Implementations.Sqlite
 
 
             cancellationToken.ThrowIfCancellationRequested();
             cancellationToken.ThrowIfCancellationRequested();
 
 
-            var cmd = connection.CreateCommand();
-            cmd.CommandText = "replace into displaypreferences (id, data) values (@1, @2)";
-            cmd.AddParam("@1", displayPreferences.Id);
-            cmd.AddParam("@2", serialized);
-
-            using (var tran = connection.BeginTransaction())
+            using (var cmd = connection.CreateCommand())
             {
             {
-                try
+                cmd.CommandText = "replace into displaypreferences (id, data) values (@1, @2)";
+                cmd.AddParam("@1", displayPreferences.Id);
+                cmd.AddParam("@2", serialized);
+
+                using (var tran = connection.BeginTransaction())
                 {
                 {
-                    cmd.Transaction = tran;
+                    try
+                    {
+                        cmd.Transaction = tran;
 
 
-                    await cmd.ExecuteNonQueryAsync(cancellationToken);
+                        await cmd.ExecuteNonQueryAsync(cancellationToken);
 
 
-                    tran.Commit();
-                }
-                catch (OperationCanceledException)
-                {
-                    tran.Rollback();
-                }
-                catch (Exception e)
-                {
-                    Logger.ErrorException("Failed to commit transaction.", e);
-                    tran.Rollback();
+                        tran.Commit();
+                    }
+                    catch (OperationCanceledException)
+                    {
+                        tran.Rollback();
+                    }
+                    catch (Exception e)
+                    {
+                        Logger.ErrorException("Failed to commit transaction.", e);
+                        tran.Rollback();
+                    }
                 }
                 }
             }
             }
         }
         }
@@ -174,7 +176,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
 
 
             var cmd = connection.CreateCommand();
             var cmd = connection.CreateCommand();
             cmd.CommandText = "select data from displaypreferences where id = @id";
             cmd.CommandText = "select data from displaypreferences where id = @id";
-            
+
             var idParam = cmd.Parameters.Add("@id", DbType.Guid);
             var idParam = cmd.Parameters.Add("@id", DbType.Guid);
             idParam.Value = displayPreferencesId;
             idParam.Value = displayPreferencesId;
 
 

+ 62 - 41
MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs

@@ -1,3 +1,4 @@
+using System.Linq;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Persistence;
@@ -156,16 +157,32 @@ namespace MediaBrowser.Server.Implementations.Sqlite
         /// <param name="id">The id.</param>
         /// <param name="id">The id.</param>
         /// <returns>BaseItem.</returns>
         /// <returns>BaseItem.</returns>
         /// <exception cref="System.ArgumentException"></exception>
         /// <exception cref="System.ArgumentException"></exception>
-        public BaseItem RetrieveItem(Guid id)
+        public BaseItem GetItem(Guid id)
         {
         {
             if (id == Guid.Empty)
             if (id == Guid.Empty)
             {
             {
-                throw new ArgumentException();
+                throw new ArgumentNullException("id");
             }
             }
 
 
             return RetrieveItemInternal(id);
             return RetrieveItemInternal(id);
         }
         }
 
 
+        /// <summary>
+        /// Retrieves the items.
+        /// </summary>
+        /// <param name="ids">The ids.</param>
+        /// <returns>IEnumerable{BaseItem}.</returns>
+        /// <exception cref="System.ArgumentNullException">ids</exception>
+        public IEnumerable<BaseItem> GetItems(IEnumerable<Guid> ids)
+        {
+            if (ids == null)
+            {
+                throw new ArgumentNullException("ids");
+            }
+
+            return ids.Select(RetrieveItemInternal);
+        }
+
         /// <summary>
         /// <summary>
         /// Internal retrieve from items or users table
         /// Internal retrieve from items or users table
         /// </summary>
         /// </summary>
@@ -176,35 +193,37 @@ namespace MediaBrowser.Server.Implementations.Sqlite
         {
         {
             if (id == Guid.Empty)
             if (id == Guid.Empty)
             {
             {
-                throw new ArgumentException();
+                throw new ArgumentNullException("id");
             }
             }
 
 
-            var cmd = connection.CreateCommand();
-            cmd.CommandText = "select obj_type,data from items where guid = @guid";
-            var guidParam = cmd.Parameters.Add("@guid", DbType.Guid);
-            guidParam.Value = id;
-
-            using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
+            using (var cmd = connection.CreateCommand())
             {
             {
-                if (reader.Read())
+                cmd.CommandText = "select obj_type,data from items where guid = @guid";
+                var guidParam = cmd.Parameters.Add("@guid", DbType.Guid);
+                guidParam.Value = id;
+
+                using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
                 {
                 {
-                    var type = reader.GetString(0);
-                    using (var stream = GetStream(reader, 1))
+                    if (reader.Read())
                     {
                     {
-                        var itemType = _typeMapper.GetType(type);
-
-                        if (itemType == null)
+                        var type = reader.GetString(0);
+                        using (var stream = GetStream(reader, 1))
                         {
                         {
-                            Logger.Error("Cannot find type {0}.  Probably belongs to plug-in that is no longer loaded.", type);
-                            return null;
-                        }
+                            var itemType = _typeMapper.GetType(type);
 
 
-                        var item = _jsonSerializer.DeserializeFromStream(stream, itemType);
-                        return item as BaseItem;
+                            if (itemType == null)
+                            {
+                                Logger.Error("Cannot find type {0}.  Probably belongs to plug-in that is no longer loaded.", type);
+                                return null;
+                            }
+
+                            var item = _jsonSerializer.DeserializeFromStream(stream, itemType);
+                            return item as BaseItem;
+                        }
                     }
                     }
                 }
                 }
+                return null;
             }
             }
-            return null;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -220,30 +239,32 @@ namespace MediaBrowser.Server.Implementations.Sqlite
                 throw new ArgumentNullException();
                 throw new ArgumentNullException();
             }
             }
 
 
-            var cmd = connection.CreateCommand();
-            cmd.CommandText = "select obj_type,data from items where guid in (select child from children where guid = @guid)";
-            var guidParam = cmd.Parameters.Add("@guid", DbType.Guid);
-            guidParam.Value = parent.Id;
-
-            using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
+            using (var cmd = connection.CreateCommand())
             {
             {
-                while (reader.Read())
-                {
-                    var type = reader.GetString(0);
+                cmd.CommandText = "select obj_type,data from items where guid in (select child from children where guid = @guid)";
+                var guidParam = cmd.Parameters.Add("@guid", DbType.Guid);
+                guidParam.Value = parent.Id;
 
 
-                    using (var stream = GetStream(reader, 1))
+                using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
+                {
+                    while (reader.Read())
                     {
                     {
-                        var itemType = _typeMapper.GetType(type);
-                        if (itemType == null)
-                        {
-                            Logger.Error("Cannot find type {0}.  Probably belongs to plug-in that is no longer loaded.", type);
-                            continue;
-                        }
-                        var item = _jsonSerializer.DeserializeFromStream(stream, itemType) as BaseItem;
-                        if (item != null)
+                        var type = reader.GetString(0);
+
+                        using (var stream = GetStream(reader, 1))
                         {
                         {
-                            item.Parent = parent;
-                            yield return item;
+                            var itemType = _typeMapper.GetType(type);
+                            if (itemType == null)
+                            {
+                                Logger.Error("Cannot find type {0}.  Probably belongs to plug-in that is no longer loaded.", type);
+                                continue;
+                            }
+                            var item = _jsonSerializer.DeserializeFromStream(stream, itemType) as BaseItem;
+                            if (item != null)
+                            {
+                                item.Parent = parent;
+                                yield return item;
+                            }
                         }
                         }
                     }
                     }
                 }
                 }

+ 10 - 6
MediaBrowser.Server.Implementations/Sqlite/SQLiteRepository.cs

@@ -121,13 +121,14 @@ namespace MediaBrowser.Server.Implementations.Sqlite
             {
             {
                 try
                 try
                 {
                 {
-                    var cmd = connection.CreateCommand();
-
-                    foreach (var query in queries)
+                    using (var cmd = connection.CreateCommand())
                     {
                     {
-                        cmd.Transaction = tran;
-                        cmd.CommandText = query;
-                        cmd.ExecuteNonQuery();
+                        foreach (var query in queries)
+                        {
+                            cmd.Transaction = tran;
+                            cmd.CommandText = query;
+                            cmd.ExecuteNonQuery();
+                        }
                     }
                     }
 
 
                     tran.Commit();
                     tran.Commit();
@@ -268,6 +269,9 @@ namespace MediaBrowser.Server.Implementations.Sqlite
                         command.Transaction = tran;
                         command.Transaction = tran;
 
 
                         command.ExecuteNonQuery();
                         command.ExecuteNonQuery();
+
+                        command.Dispose();
+
                         numCommands++;
                         numCommands++;
                     }
                     }
 
 

+ 37 - 33
MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs

@@ -184,30 +184,32 @@ namespace MediaBrowser.Server.Implementations.Sqlite
 
 
             cancellationToken.ThrowIfCancellationRequested();
             cancellationToken.ThrowIfCancellationRequested();
 
 
-            var cmd = connection.CreateCommand();
-            cmd.CommandText = "replace into userdata (key, userId, data) values (@1, @2, @3)";
-            cmd.AddParam("@1", key);
-            cmd.AddParam("@2", userId);
-            cmd.AddParam("@3", serialized);
-
-            using (var tran = connection.BeginTransaction())
+            using (var cmd = connection.CreateCommand())
             {
             {
-                try
+                cmd.CommandText = "replace into userdata (key, userId, data) values (@1, @2, @3)";
+                cmd.AddParam("@1", key);
+                cmd.AddParam("@2", userId);
+                cmd.AddParam("@3", serialized);
+
+                using (var tran = connection.BeginTransaction())
                 {
                 {
-                    cmd.Transaction = tran;
+                    try
+                    {
+                        cmd.Transaction = tran;
 
 
-                    await cmd.ExecuteNonQueryAsync(cancellationToken);
+                        await cmd.ExecuteNonQueryAsync(cancellationToken);
 
 
-                    tran.Commit();
-                }
-                catch (OperationCanceledException)
-                {
-                    tran.Rollback();
-                }
-                catch (Exception e)
-                {
-                    Logger.ErrorException("Failed to commit transaction.", e);
-                    tran.Rollback();
+                        tran.Commit();
+                    }
+                    catch (OperationCanceledException)
+                    {
+                        tran.Rollback();
+                    }
+                    catch (Exception e)
+                    {
+                        Logger.ErrorException("Failed to commit transaction.", e);
+                        tran.Rollback();
+                    }
                 }
                 }
             }
             }
         }
         }
@@ -245,27 +247,29 @@ namespace MediaBrowser.Server.Implementations.Sqlite
         /// <returns>Task{UserItemData}.</returns>
         /// <returns>Task{UserItemData}.</returns>
         private async Task<UserItemData> RetrieveUserData(Guid userId, string key)
         private async Task<UserItemData> RetrieveUserData(Guid userId, string key)
         {
         {
-            var cmd = connection.CreateCommand();
-            cmd.CommandText = "select data from userdata where key = @key and userId=@userId";
+            using (var cmd = connection.CreateCommand())
+            {
+                cmd.CommandText = "select data from userdata where key = @key and userId=@userId";
 
 
-            var idParam = cmd.Parameters.Add("@key", DbType.String);
-            idParam.Value = key;
+                var idParam = cmd.Parameters.Add("@key", DbType.String);
+                idParam.Value = key;
 
 
-            var userIdParam = cmd.Parameters.Add("@userId", DbType.Guid);
-            userIdParam.Value = userId;
+                var userIdParam = cmd.Parameters.Add("@userId", DbType.Guid);
+                userIdParam.Value = userId;
 
 
-            using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow).ConfigureAwait(false))
-            {
-                if (reader.Read())
+                using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow).ConfigureAwait(false))
                 {
                 {
-                    using (var stream = GetStream(reader, 0))
+                    if (reader.Read())
                     {
                     {
-                        return _jsonSerializer.DeserializeFromStream<UserItemData>(stream);
+                        using (var stream = GetStream(reader, 0))
+                        {
+                            return _jsonSerializer.DeserializeFromStream<UserItemData>(stream);
+                        }
                     }
                     }
                 }
                 }
-            }
 
 
-            return new UserItemData();
+                return new UserItemData();
+            }
         }
         }
     }
     }
 }
 }

+ 38 - 32
MediaBrowser.Server.Implementations/Sqlite/SQLiteUserRepository.cs

@@ -127,29 +127,31 @@ namespace MediaBrowser.Server.Implementations.Sqlite
 
 
             cancellationToken.ThrowIfCancellationRequested();
             cancellationToken.ThrowIfCancellationRequested();
 
 
-            var cmd = connection.CreateCommand();
-            cmd.CommandText = "replace into users (guid, data) values (@1, @2)";
-            cmd.AddParam("@1", user.Id);
-            cmd.AddParam("@2", serialized);
-
-            using (var tran = connection.BeginTransaction())
+            using (var cmd = connection.CreateCommand())
             {
             {
-                try
+                cmd.CommandText = "replace into users (guid, data) values (@1, @2)";
+                cmd.AddParam("@1", user.Id);
+                cmd.AddParam("@2", serialized);
+
+                using (var tran = connection.BeginTransaction())
                 {
                 {
-                    cmd.Transaction = tran;
+                    try
+                    {
+                        cmd.Transaction = tran;
 
 
-                    await cmd.ExecuteNonQueryAsync(cancellationToken);
+                        await cmd.ExecuteNonQueryAsync(cancellationToken);
 
 
-                    tran.Commit();
-                }
-                catch (OperationCanceledException)
-                {
-                    tran.Rollback();
-                }
-                catch (Exception e)
-                {
-                    Logger.ErrorException("Failed to commit transaction.", e);
-                    tran.Rollback();
+                        tran.Commit();
+                    }
+                    catch (OperationCanceledException)
+                    {
+                        tran.Rollback();
+                    }
+                    catch (Exception e)
+                    {
+                        Logger.ErrorException("Failed to commit transaction.", e);
+                        tran.Rollback();
+                    }
                 }
                 }
             }
             }
         }
         }
@@ -160,17 +162,19 @@ namespace MediaBrowser.Server.Implementations.Sqlite
         /// <returns>IEnumerable{User}.</returns>
         /// <returns>IEnumerable{User}.</returns>
         public IEnumerable<User> RetrieveAllUsers()
         public IEnumerable<User> RetrieveAllUsers()
         {
         {
-            var cmd = connection.CreateCommand();
-            cmd.CommandText = "select data from users";
-
-            using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
+            using (var cmd = connection.CreateCommand())
             {
             {
-                while (reader.Read())
+                cmd.CommandText = "select data from users";
+
+                using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
                 {
                 {
-                    using (var stream = GetStream(reader, 0))
+                    while (reader.Read())
                     {
                     {
-                        var user = _jsonSerializer.DeserializeFromStream<User>(stream);
-                        yield return user;
+                        using (var stream = GetStream(reader, 0))
+                        {
+                            var user = _jsonSerializer.DeserializeFromStream<User>(stream);
+                            yield return user;
+                        }
                     }
                     }
                 }
                 }
             }
             }
@@ -197,12 +201,14 @@ namespace MediaBrowser.Server.Implementations.Sqlite
 
 
             cancellationToken.ThrowIfCancellationRequested();
             cancellationToken.ThrowIfCancellationRequested();
 
 
-            var cmd = connection.CreateCommand();
-            cmd.CommandText = "delete from users where guid=@guid";
-            var guidParam = cmd.Parameters.Add("@guid", DbType.Guid);
-            guidParam.Value = user.Id;
+            using (var cmd = connection.CreateCommand())
+            {
+                cmd.CommandText = "delete from users where guid=@guid";
+                var guidParam = cmd.Parameters.Add("@guid", DbType.Guid);
+                guidParam.Value = user.Id;
 
 
-            return ExecuteCommand(cmd);
+                return ExecuteCommand(cmd);
+            }
         }
         }
     }
     }
 }
 }

+ 3 - 10
MediaBrowser.ServerApplication/LibraryExplorer.xaml.cs

@@ -175,16 +175,9 @@ namespace MediaBrowser.ServerApplication
             {
             {
                 var item = (BaseItem)(tvwLibrary.SelectedItem as TreeViewItem).Tag;
                 var item = (BaseItem)(tvwLibrary.SelectedItem as TreeViewItem).Tag;
                 lblObjType.Content = "Type: " + item.GetType().Name;
                 lblObjType.Content = "Type: " + item.GetType().Name;
-                var trailers = item.LocalTrailers != null && item.LocalTrailers.Count > 0
-                                   ? "\nTrailers: " +
-                                     String.Join(Environment.NewLine, item.LocalTrailers.Select(t => t.Path))
-                                   : "";
+
                 var movie = item as Movie;
                 var movie = item as Movie;
-                var features = movie != null && movie.SpecialFeatures != null
-                                   ? "\nSpecial Features: " +
-                                     string.Join(Environment.NewLine,
-                                                 movie.SpecialFeatures.Select(f => f.Path))
-                                   : "";
+
                 var folder = item as Folder;
                 var folder = item as Folder;
                 if (folder != null)
                 if (folder != null)
                 {
                 {
@@ -222,7 +215,7 @@ namespace MediaBrowser.ServerApplication
                     lblIndexBy.Visibility = ddlIndexBy.Visibility = ddlSortBy.Visibility = lblSortBy.Visibility = Visibility.Hidden;
                     lblIndexBy.Visibility = ddlIndexBy.Visibility = ddlSortBy.Visibility = lblSortBy.Visibility = Visibility.Hidden;
 
 
                 }
                 }
-                txtData.Text = FormatJson(_jsonSerializer.SerializeToString(item)) + trailers + features;
+                txtData.Text = FormatJson(_jsonSerializer.SerializeToString(item));
 
 
                 var previews = new List<PreviewItem>();
                 var previews = new List<PreviewItem>();
                 await Task.Run(() =>
                 await Task.Run(() =>