Bläddra i källkod

update people saving

Luke Pulverenti 10 år sedan
förälder
incheckning
b1be09242c
90 ändrade filer med 799 tillägg och 727 borttagningar
  1. 9 0
      MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
  2. 9 0
      MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
  3. 6 2
      MediaBrowser.Controller/Entities/BaseItem.cs
  4. 11 0
      MediaBrowser.Controller/Entities/BasePluginFolder.cs
  5. 9 0
      MediaBrowser.Controller/Entities/CollectionFolder.cs
  6. 9 0
      MediaBrowser.Controller/Entities/GameGenre.cs
  7. 9 0
      MediaBrowser.Controller/Entities/GameSystem.cs
  8. 9 0
      MediaBrowser.Controller/Entities/Genre.cs
  9. 6 0
      MediaBrowser.Controller/Entities/IHasMetadata.cs
  10. 9 0
      MediaBrowser.Controller/Entities/Person.cs
  11. 9 0
      MediaBrowser.Controller/Entities/Studio.cs
  12. 9 0
      MediaBrowser.Controller/Entities/User.cs
  13. 11 1
      MediaBrowser.Controller/Entities/UserView.cs
  14. 9 0
      MediaBrowser.Controller/Entities/Year.cs
  15. 15 0
      MediaBrowser.Controller/Persistence/IItemRepository.cs
  16. 13 11
      MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
  17. 2 8
      MediaBrowser.Controller/Providers/LocalMetadataResult.cs
  18. 10 0
      MediaBrowser.Controller/Providers/MetadataResult.cs
  19. 3 3
      MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs
  20. 12 18
      MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs
  21. 6 4
      MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs
  22. 6 4
      MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs
  23. 25 27
      MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs
  24. 6 4
      MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs
  25. 4 2
      MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs
  26. 5 3
      MediaBrowser.LocalMetadata/Parsers/SeasonXmlParser.cs
  27. 6 4
      MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs
  28. 1 1
      MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs
  29. 1 2
      MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs
  30. 1 1
      MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs
  31. 1 1
      MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs
  32. 1 1
      MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs
  33. 1 7
      MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs
  34. 1 1
      MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs
  35. 1 1
      MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs
  36. 1 1
      MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
  37. 1 1
      MediaBrowser.LocalMetadata/Providers/SeasonXmlProvider.cs
  38. 1 1
      MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs
  39. 4 10
      MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs
  40. 4 12
      MediaBrowser.Providers/Books/BookMetadataService.cs
  41. 19 24
      MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
  42. 2 10
      MediaBrowser.Providers/Channels/AudioChannelItemMetadataService.cs
  43. 2 10
      MediaBrowser.Providers/Channels/ChannelMetadataService.cs
  44. 2 10
      MediaBrowser.Providers/Channels/VideoChannelItemMetadataService.cs
  45. 6 14
      MediaBrowser.Providers/Folders/FolderMetadataService.cs
  46. 2 11
      MediaBrowser.Providers/Folders/UserViewMetadataService.cs
  47. 2 10
      MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs
  48. 9 14
      MediaBrowser.Providers/Games/GameMetadataService.cs
  49. 7 12
      MediaBrowser.Providers/Games/GameSystemMetadataService.cs
  50. 2 10
      MediaBrowser.Providers/Genres/GenreMetadataService.cs
  51. 2 9
      MediaBrowser.Providers/LiveTv/AudioRecordingService.cs
  52. 2 5
      MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs
  53. 2 14
      MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs
  54. 2 9
      MediaBrowser.Providers/LiveTv/VideoRecordingService.cs
  55. 35 17
      MediaBrowser.Providers/Manager/MetadataService.cs
  56. 9 4
      MediaBrowser.Providers/Manager/ProviderUtils.cs
  57. 6 4
      MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
  58. 16 14
      MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs
  59. 14 19
      MediaBrowser.Providers/Movies/MovieMetadataService.cs
  60. 14 19
      MediaBrowser.Providers/Music/AlbumMetadataService.cs
  61. 7 18
      MediaBrowser.Providers/Music/ArtistMetadataService.cs
  62. 9 14
      MediaBrowser.Providers/Music/AudioMetadataService.cs
  63. 9 14
      MediaBrowser.Providers/Music/MusicVideoMetadataService.cs
  64. 2 10
      MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs
  65. 7 12
      MediaBrowser.Providers/People/PersonMetadataService.cs
  66. 2 11
      MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs
  67. 2 10
      MediaBrowser.Providers/Photos/PhotoMetadataService.cs
  68. 9 14
      MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
  69. 2 9
      MediaBrowser.Providers/Studios/StudioMetadataService.cs
  70. 19 24
      MediaBrowser.Providers/TV/EpisodeMetadataService.cs
  71. 6 14
      MediaBrowser.Providers/TV/SeasonMetadataService.cs
  72. 34 42
      MediaBrowser.Providers/TV/SeriesMetadataService.cs
  73. 37 29
      MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs
  74. 12 10
      MediaBrowser.Providers/TV/TvdbSeriesProvider.cs
  75. 2 10
      MediaBrowser.Providers/Users/UserMetadataService.cs
  76. 6 14
      MediaBrowser.Providers/Videos/VideoMetadataService.cs
  77. 2 10
      MediaBrowser.Providers/Years/YearMetadataService.cs
  78. 9 4
      MediaBrowser.Server.Implementations/Library/LibraryManager.cs
  79. 153 0
      MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
  80. 21 20
      MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
  81. 7 22
      MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs
  82. 6 23
      MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
  83. 6 6
      MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs
  84. 6 4
      MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
  85. 1 1
      MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs
  86. 1 1
      MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs
  87. 8 6
      MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs
  88. 1 3
      MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs
  89. 1 1
      MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs
  90. 1 1
      MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs

+ 9 - 0
MediaBrowser.Controller/Entities/Audio/MusicArtist.cs

@@ -216,5 +216,14 @@ namespace MediaBrowser.Controller.Entities.Audio
                 return hasArtist != null && hasArtist.HasAnyArtist(Name);
             };
         }
+
+        [IgnoreDataMember]
+        public override bool SupportsPeople
+        {
+            get
+            {
+                return false;
+            }
+        }
     }
 }

+ 9 - 0
MediaBrowser.Controller/Entities/Audio/MusicGenre.cs

@@ -71,5 +71,14 @@ namespace MediaBrowser.Controller.Entities.Audio
         {
             return i => (i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase);
         }
+
+        [IgnoreDataMember]
+        public override bool SupportsPeople
+        {
+            get
+            {
+                return false;
+            }
+        }
     }
 }

+ 6 - 2
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -35,7 +35,6 @@ namespace MediaBrowser.Controller.Entities
         {
             Genres = new List<string>();
             Studios = new List<string>();
-            People = new List<PersonInfo>();
             ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
             LockedFields = new List<MetadataFields>();
             ImageInfos = new List<ItemImageInfo>();
@@ -776,6 +775,12 @@ namespace MediaBrowser.Controller.Entities
             get { return IsFolder || Parent != null; }
         }
 
+        [IgnoreDataMember]
+        public virtual bool SupportsPeople
+        {
+            get { return true; }
+        }
+
         /// <summary>
         /// Refreshes owned items such as trailers, theme videos, special features, etc.
         /// Returns true or false indicating if changes were found.
@@ -1239,7 +1244,6 @@ namespace MediaBrowser.Controller.Entities
         /// <exception cref="System.ArgumentNullException"></exception>
         public void AddPerson(PersonInfo person)
         {
-            PeopleHelper.AddPerson(People, person);
         }
 
         /// <summary>

+ 11 - 0
MediaBrowser.Controller/Entities/BasePluginFolder.cs

@@ -1,4 +1,6 @@
 
+using System.Runtime.Serialization;
+
 namespace MediaBrowser.Controller.Entities
 {
     /// <summary>
@@ -21,5 +23,14 @@ namespace MediaBrowser.Controller.Entities
         {
             return true;
         }
+
+        [IgnoreDataMember]
+        public override bool SupportsPeople
+        {
+            get
+            {
+                return false;
+            }
+        }
     }
 }

+ 9 - 0
MediaBrowser.Controller/Entities/CollectionFolder.cs

@@ -194,5 +194,14 @@ namespace MediaBrowser.Controller.Entities
                 .Where(i => i.Path != null && PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase))
                 .SelectMany(c => c.Children);
         }
+
+        [IgnoreDataMember]
+        public override bool SupportsPeople
+        {
+            get
+            {
+                return false;
+            }
+        }
     }
 }

+ 9 - 0
MediaBrowser.Controller/Entities/GameGenre.cs

@@ -62,5 +62,14 @@ namespace MediaBrowser.Controller.Entities
         {
             return i => (i is Game) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase);
         }
+
+        [IgnoreDataMember]
+        public override bool SupportsPeople
+        {
+            get
+            {
+                return false;
+            }
+        }
     }
 }

+ 9 - 0
MediaBrowser.Controller/Entities/GameSystem.cs

@@ -58,5 +58,14 @@ namespace MediaBrowser.Controller.Entities
 
             return id;
         }
+
+        [IgnoreDataMember]
+        public override bool SupportsPeople
+        {
+            get
+            {
+                return false;
+            }
+        }
     }
 }

+ 9 - 0
MediaBrowser.Controller/Entities/Genre.cs

@@ -66,5 +66,14 @@ namespace MediaBrowser.Controller.Entities
         {
             return i => !(i is Game) && !(i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase);
         }
+
+        [IgnoreDataMember]
+        public override bool SupportsPeople
+        {
+            get
+            {
+                return false;
+            }
+        }
     }
 }

+ 6 - 0
MediaBrowser.Controller/Entities/IHasMetadata.cs

@@ -59,5 +59,11 @@ namespace MediaBrowser.Controller.Entities
         /// Afters the metadata refresh.
         /// </summary>
         void AfterMetadataRefresh();
+
+        /// <summary>
+        /// Gets a value indicating whether [supports people].
+        /// </summary>
+        /// <value><c>true</c> if [supports people]; otherwise, <c>false</c>.</value>
+        bool SupportsPeople { get; }
     }
 }

+ 9 - 0
MediaBrowser.Controller/Entities/Person.cs

@@ -78,6 +78,15 @@ namespace MediaBrowser.Controller.Entities
         {
             return i => LibraryManager.GetPeople(i).Any(p => string.Equals(p.Name, Name, StringComparison.OrdinalIgnoreCase));
         }
+
+        [IgnoreDataMember]
+        public override bool SupportsPeople
+        {
+            get
+            {
+                return false;
+            }
+        }
     }
 
     /// <summary>

+ 9 - 0
MediaBrowser.Controller/Entities/Studio.cs

@@ -72,5 +72,14 @@ namespace MediaBrowser.Controller.Entities
         {
             return i => i.Studios.Contains(Name, StringComparer.OrdinalIgnoreCase);
         }
+
+        [IgnoreDataMember]
+        public override bool SupportsPeople
+        {
+            get
+            {
+                return false;
+            }
+        }
     }
 }

+ 9 - 0
MediaBrowser.Controller/Entities/User.cs

@@ -295,5 +295,14 @@ namespace MediaBrowser.Controller.Entities
 
             return config.GroupedFolders.Select(i => new Guid(i)).Contains(id);
         }
+
+        [IgnoreDataMember]
+        public override bool SupportsPeople
+        {
+            get
+            {
+                return false;
+            }
+        }
     }
 }

+ 11 - 1
MediaBrowser.Controller/Entities/UserView.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Playlists;
+using System.Runtime.Serialization;
+using MediaBrowser.Controller.Playlists;
 using MediaBrowser.Controller.TV;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Querying;
@@ -93,5 +94,14 @@ namespace MediaBrowser.Controller.Entities
 
             return standaloneTypes.Contains(collectionFolder.CollectionType ?? string.Empty);
         }
+
+        [IgnoreDataMember]
+        public override bool SupportsPeople
+        {
+            get
+            {
+                return false;
+            }
+        }
     }
 }

+ 9 - 0
MediaBrowser.Controller/Entities/Year.cs

@@ -88,5 +88,14 @@ namespace MediaBrowser.Controller.Entities
             var val = GetYearValue();
             return i => i.ProductionYear.HasValue && val.HasValue && i.ProductionYear.Value == val.Value;
         }
+
+        [IgnoreDataMember]
+        public override bool SupportsPeople
+        {
+            get
+            {
+                return false;
+            }
+        }
     }
 }

+ 15 - 0
MediaBrowser.Controller/Persistence/IItemRepository.cs

@@ -147,6 +147,21 @@ namespace MediaBrowser.Controller.Persistence
         /// <param name="query">The query.</param>
         /// <returns>List&lt;Guid&gt;.</returns>
         List<Guid> GetItemIdsList(InternalItemsQuery query);
+
+        /// <summary>
+        /// Gets the people.
+        /// </summary>
+        /// <param name="itemId">The item identifier.</param>
+        /// <returns>List&lt;PersonInfo&gt;.</returns>
+        List<PersonInfo> GetPeople(Guid itemId);
+
+        /// <summary>
+        /// Updates the people.
+        /// </summary>
+        /// <param name="itemId">The item identifier.</param>
+        /// <param name="people">The people.</param>
+        /// <returns>Task.</returns>
+        Task UpdatePeople(Guid itemId, List<PersonInfo> people);
     }
 }
 

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

@@ -40,7 +40,7 @@ namespace MediaBrowser.Controller.Providers
         /// <param name="metadataFile">The metadata file.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <exception cref="System.ArgumentNullException"></exception>
-        public void Fetch(T item, string metadataFile, CancellationToken cancellationToken)
+        public void Fetch(MetadataResult<T> item, string metadataFile, CancellationToken cancellationToken)
         {
             if (item == null)
             {
@@ -72,7 +72,7 @@ namespace MediaBrowser.Controller.Providers
         /// <param name="settings">The settings.</param>
         /// <param name="encoding">The encoding.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
-        private void Fetch(T item, string metadataFile, XmlReaderSettings settings, Encoding encoding, CancellationToken cancellationToken)
+        private void Fetch(MetadataResult<T> item, string metadataFile, XmlReaderSettings settings, Encoding encoding, CancellationToken cancellationToken)
         {
             using (var streamReader = new StreamReader(metadataFile, encoding))
             {
@@ -101,9 +101,11 @@ namespace MediaBrowser.Controller.Providers
         /// Fetches metadata from one Xml Element
         /// </summary>
         /// <param name="reader">The reader.</param>
-        /// <param name="item">The item.</param>
-        protected virtual void FetchDataFromXmlNode(XmlReader reader, T item)
+        /// <param name="itemResult">The item result.</param>
+        protected virtual void FetchDataFromXmlNode(XmlReader reader, MetadataResult<T> itemResult)
         {
+            var item = itemResult.Item;
+
             switch (reader.Name)
             {
                 // DateCreated
@@ -490,7 +492,7 @@ namespace MediaBrowser.Controller.Providers
                             {
                                 continue;
                             }
-                            PeopleHelper.AddPerson(item.People, p);
+                            PeopleHelper.AddPerson(itemResult.People, p);
                         }
                         break;
                     }
@@ -502,7 +504,7 @@ namespace MediaBrowser.Controller.Providers
                             {
                                 continue;
                             }
-                            PeopleHelper.AddPerson(item.People, p);
+                            PeopleHelper.AddPerson(itemResult.People, p);
                         }
                         break;
                     }
@@ -516,7 +518,7 @@ namespace MediaBrowser.Controller.Providers
                         {
                             // This is one of the mis-named "Actors" full nodes created by MB2
                             // Create a reader and pass it to the persons node processor
-                            FetchDataFromPersonsNode(new XmlTextReader(new StringReader("<Persons>" + actors + "</Persons>")), item);
+                            FetchDataFromPersonsNode(new XmlTextReader(new StringReader("<Persons>" + actors + "</Persons>")), itemResult);
                         }
                         else
                         {
@@ -527,7 +529,7 @@ namespace MediaBrowser.Controller.Providers
                                 {
                                     continue;
                                 }
-                                PeopleHelper.AddPerson(item.People, p);
+                                PeopleHelper.AddPerson(itemResult.People, p);
                             }
                         }
                         break;
@@ -541,7 +543,7 @@ namespace MediaBrowser.Controller.Providers
                             {
                                 continue;
                             }
-                            PeopleHelper.AddPerson(item.People, p);
+                            PeopleHelper.AddPerson(itemResult.People, p);
                         }
                         break;
                     }
@@ -833,7 +835,7 @@ namespace MediaBrowser.Controller.Providers
                     {
                         using (var subtree = reader.ReadSubtree())
                         {
-                            FetchDataFromPersonsNode(subtree, item);
+                            FetchDataFromPersonsNode(subtree, itemResult);
                         }
                         break;
                     }
@@ -1133,7 +1135,7 @@ namespace MediaBrowser.Controller.Providers
         /// </summary>
         /// <param name="reader">The reader.</param>
         /// <param name="item">The item.</param>
-        private void FetchDataFromPersonsNode(XmlReader reader, T item)
+        private void FetchDataFromPersonsNode(XmlReader reader, MetadataResult<T> item)
         {
             reader.MoveToContent();
 

+ 2 - 8
MediaBrowser.Controller/Providers/LocalMetadataResult.cs

@@ -1,23 +1,17 @@
-using System.Collections.Generic;
 using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Entities;
+using System.Collections.Generic;
 
 namespace MediaBrowser.Controller.Providers
 {
-    public class LocalMetadataResult<T>
+    public class LocalMetadataResult<T> : MetadataResult<T>
         where T : IHasMetadata
     {
-        public bool HasMetadata { get; set; }
-        public T Item { get; set; }
-        
         public List<LocalImageInfo> Images { get; set; }
-        public List<ChapterInfo> Chapters { get; set; }
         public List<UserItemData> UserDataLIst { get; set; }
 
         public LocalMetadataResult()
         {
             Images = new List<LocalImageInfo>();
-            Chapters = new List<ChapterInfo>();
             UserDataLIst = new List<UserItemData>();
         }
     }

+ 10 - 0
MediaBrowser.Controller/Providers/MetadataResult.cs

@@ -1,8 +1,18 @@
+using MediaBrowser.Controller.Entities;
+using System.Collections.Generic;
+
 namespace MediaBrowser.Controller.Providers
 {
     public class MetadataResult<T>
     {
+        public List<PersonInfo> People { get; set; }
+
         public bool HasMetadata { get; set; }
         public T Item { get; set; }
+
+        public MetadataResult()
+        {
+            People = new List<PersonInfo>();
+        }
     }
 }

+ 3 - 3
MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs

@@ -14,7 +14,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
         {
         }
 
-        protected override void FetchDataFromXmlNode(XmlReader reader, BoxSet item)
+        protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult<BoxSet> item)
         {
             switch (reader.Name)
             {
@@ -32,7 +32,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
             }
         }
 
-        private void FetchFromCollectionItemsNode(XmlReader reader, BoxSet item)
+        private void FetchFromCollectionItemsNode(XmlReader reader, MetadataResult<BoxSet> item)
         {
             reader.MoveToContent();
 
@@ -66,7 +66,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
                 }
             }
 
-            item.LinkedChildren = list;
+            item.Item.LinkedChildren = list;
         }
     }
 }

+ 12 - 18
MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs

@@ -1,13 +1,13 @@
-using System;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
+using System;
 using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Threading;
 using System.Xml;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
 
 namespace MediaBrowser.LocalMetadata.Parsers
 {
@@ -17,7 +17,6 @@ namespace MediaBrowser.LocalMetadata.Parsers
     public class EpisodeXmlParser : BaseItemXmlParser<Episode>
     {
         private List<LocalImageInfo> _imagesFound;
-        private List<ChapterInfo> _chaptersFound;
 
         public EpisodeXmlParser(ILogger logger)
             : base(logger)
@@ -26,14 +25,12 @@ namespace MediaBrowser.LocalMetadata.Parsers
 
         private string _xmlPath;
 
-        public void Fetch(Episode item, 
+        public void Fetch(MetadataResult<Episode> item, 
             List<LocalImageInfo> images,
-            List<ChapterInfo> chapters, 
             string metadataFile, 
             CancellationToken cancellationToken)
         {
             _imagesFound = images;
-            _chaptersFound = chapters;
             _xmlPath = metadataFile;
 
             Fetch(item, metadataFile, cancellationToken);
@@ -45,16 +42,13 @@ namespace MediaBrowser.LocalMetadata.Parsers
         /// Fetches the data from XML node.
         /// </summary>
         /// <param name="reader">The reader.</param>
-        /// <param name="item">The item.</param>
-        protected override void FetchDataFromXmlNode(XmlReader reader, Episode item)
+        /// <param name="result">The result.</param>
+        protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult<Episode> result)
         {
+            var item = result.Item;
+
             switch (reader.Name)
             {
-                case "Chapters":
-
-                    _chaptersFound.AddRange(FetchChaptersFromXmlNode(item, reader.ReadSubtree()));
-                    break;
-
                 case "Episode":
 
                     //MB generated metadata is within an "Episode" node
@@ -67,7 +61,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
                         {
                             if (subTree.NodeType == XmlNodeType.Element)
                             {
-                                FetchDataFromXmlNode(subTree, item);
+                                FetchDataFromXmlNode(subTree, result);
                             }
                         }
 
@@ -263,7 +257,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
 
 
                 default:
-                    base.FetchDataFromXmlNode(reader, item);
+                    base.FetchDataFromXmlNode(reader, result);
                     break;
             }
         }

+ 6 - 4
MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs

@@ -16,7 +16,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
         }
 
         private readonly Task _cachedTask = Task.FromResult(true);
-        public Task FetchAsync(GameSystem item, string metadataFile, CancellationToken cancellationToken)
+        public Task FetchAsync(MetadataResult<GameSystem> item, string metadataFile, CancellationToken cancellationToken)
         {
             Fetch(item, metadataFile, cancellationToken);
 
@@ -29,9 +29,11 @@ namespace MediaBrowser.LocalMetadata.Parsers
         /// Fetches the data from XML node.
         /// </summary>
         /// <param name="reader">The reader.</param>
-        /// <param name="item">The item.</param>
-        protected override void FetchDataFromXmlNode(XmlReader reader, GameSystem item)
+        /// <param name="result">The result.</param>
+        protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult<GameSystem> result)
         {
+            var item = result.Item;
+
             switch (reader.Name)
             {
                 case "GameSystem":
@@ -56,7 +58,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
 
 
                 default:
-                    base.FetchDataFromXmlNode(reader, item);
+                    base.FetchDataFromXmlNode(reader, result);
                     break;
             }
         }

+ 6 - 4
MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs

@@ -22,7 +22,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
         }
 
         private readonly Task _cachedTask = Task.FromResult(true);
-        public Task FetchAsync(Game item, string metadataFile, CancellationToken cancellationToken)
+        public Task FetchAsync(MetadataResult<Game> item, string metadataFile, CancellationToken cancellationToken)
         {
             Fetch(item, metadataFile, cancellationToken);
 
@@ -35,9 +35,11 @@ namespace MediaBrowser.LocalMetadata.Parsers
         /// Fetches the data from XML node.
         /// </summary>
         /// <param name="reader">The reader.</param>
-        /// <param name="item">The item.</param>
-        protected override void FetchDataFromXmlNode(XmlReader reader, Game item)
+        /// <param name="result">The result.</param>
+        protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult<Game> result)
         {
+            var item = result.Item;
+
             switch (reader.Name)
             {
                 case "GameSystem":
@@ -97,7 +99,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
 
 
                 default:
-                    base.FetchDataFromXmlNode(reader, item);
+                    base.FetchDataFromXmlNode(reader, result);
                     break;
             }
         }

+ 25 - 27
MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs

@@ -1,43 +1,31 @@
-using System.Collections.Generic;
-using System.Threading;
-using System.Xml;
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
+using System.Xml;
 
 namespace MediaBrowser.LocalMetadata.Parsers
 {
     /// <summary>
     /// Class EpisodeXmlParser
     /// </summary>
-    public class MovieXmlParser : BaseItemXmlParser<Video>
+    public class BaseVideoXmlParser<T> : BaseItemXmlParser<T>
+        where T : Video
     {
-        private List<ChapterInfo> _chaptersFound;
-
-        public MovieXmlParser(ILogger logger)
+        public BaseVideoXmlParser(ILogger logger)
             : base(logger)
         {
         }
 
-        public void Fetch(Video item, 
-            List<ChapterInfo> chapters, 
-            string metadataFile, 
-            CancellationToken cancellationToken)
-        {
-            _chaptersFound = chapters;
-
-            Fetch(item, metadataFile, cancellationToken);
-        }
-
         /// <summary>
         /// Fetches the data from XML node.
         /// </summary>
         /// <param name="reader">The reader.</param>
-        /// <param name="item">The item.</param>
-        protected override void FetchDataFromXmlNode(XmlReader reader, Video item)
+        /// <param name="result">The result.</param>
+        protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult<T> result)
         {
+            var item = result.Item;
+
             switch (reader.Name)
             {
                 case "TmdbCollectionName":
@@ -53,15 +41,25 @@ namespace MediaBrowser.LocalMetadata.Parsers
                         break;
                     }
 
-                case "Chapters":
-
-                    _chaptersFound.AddRange(FetchChaptersFromXmlNode(item, reader.ReadSubtree()));
-                    break;
-
                 default:
-                    base.FetchDataFromXmlNode(reader, item);
+                    base.FetchDataFromXmlNode(reader, result);
                     break;
             }
         }
     }
+
+    public class MovieXmlParser : BaseVideoXmlParser<Movie>
+    {
+        public MovieXmlParser(ILogger logger) : base(logger)
+        {
+        }
+    }
+
+    public class VideoXmlParser : BaseVideoXmlParser<Video>
+    {
+        public VideoXmlParser(ILogger logger)
+            : base(logger)
+        {
+        }
+    }
 }

+ 6 - 4
MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs

@@ -6,7 +6,7 @@ using System.Xml;
 
 namespace MediaBrowser.LocalMetadata.Parsers
 {
-    public class MusicVideoXmlParser : BaseItemXmlParser<MusicVideo>
+    public class MusicVideoXmlParser : BaseVideoXmlParser<MusicVideo>
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="BaseItemXmlParser{T}" /> class.
@@ -21,9 +21,11 @@ namespace MediaBrowser.LocalMetadata.Parsers
         /// Fetches the data from XML node.
         /// </summary>
         /// <param name="reader">The reader.</param>
-        /// <param name="item">The item.</param>
-        protected override void FetchDataFromXmlNode(XmlReader reader, MusicVideo item)
+        /// <param name="result">The result.</param>
+        protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult<MusicVideo> result)
         {
+            var item = result.Item;
+
             switch (reader.Name)
             {
                 case "Artist":
@@ -44,7 +46,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
                     break;
 
                 default:
-                    base.FetchDataFromXmlNode(reader, item);
+                    base.FetchDataFromXmlNode(reader, result);
                     break;
             }
         }

+ 4 - 2
MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs

@@ -16,8 +16,10 @@ namespace MediaBrowser.LocalMetadata.Parsers
         {
         }
 
-        protected override void FetchDataFromXmlNode(XmlReader reader, Playlist item)
+        protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult<Playlist> result)
         {
+            var item = result.Item;
+
             switch (reader.Name)
             {
                 case "OwnerUserId":
@@ -59,7 +61,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
                     break;
 
                 default:
-                    base.FetchDataFromXmlNode(reader, item);
+                    base.FetchDataFromXmlNode(reader, result);
                     break;
             }
         }

+ 5 - 3
MediaBrowser.LocalMetadata/Parsers/SeasonXmlParser.cs

@@ -16,9 +16,11 @@ namespace MediaBrowser.LocalMetadata.Parsers
         /// Fetches the data from XML node.
         /// </summary>
         /// <param name="reader">The reader.</param>
-        /// <param name="item">The item.</param>
-        protected override void FetchDataFromXmlNode(XmlReader reader, Season item)
+        /// <param name="result">The result.</param>
+        protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult<Season> result)
         {
+            var item = result.Item;
+
             switch (reader.Name)
             {
                 case "SeasonNumber":
@@ -38,7 +40,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
                     }
 
                 default:
-                    base.FetchDataFromXmlNode(reader, item);
+                    base.FetchDataFromXmlNode(reader, result);
                     break;
             }
         }

+ 6 - 4
MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs

@@ -26,9 +26,11 @@ namespace MediaBrowser.LocalMetadata.Parsers
         /// Fetches the data from XML node.
         /// </summary>
         /// <param name="reader">The reader.</param>
-        /// <param name="item">The item.</param>
-        protected override void FetchDataFromXmlNode(XmlReader reader, Series item)
+        /// <param name="result">The result.</param>
+        protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult<Series> result)
         {
+            var item = result.Item;
+
             switch (reader.Name)
             {
                 case "Series":
@@ -42,7 +44,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
                         {
                             if (subTree.NodeType == XmlNodeType.Element)
                             {
-                                FetchDataFromXmlNode(subTree, item);
+                                FetchDataFromXmlNode(subTree, result);
                             }
                         }
 
@@ -110,7 +112,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
                     }
 
                 default:
-                    base.FetchDataFromXmlNode(reader, item);
+                    base.FetchDataFromXmlNode(reader, result);
                     break;
             }
         }

+ 1 - 1
MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs

@@ -23,7 +23,7 @@ namespace MediaBrowser.LocalMetadata.Providers
 
         protected override void Fetch(LocalMetadataResult<BoxSet> result, string path, CancellationToken cancellationToken)
         {
-            new BoxSetXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
+            new BoxSetXmlParser(_logger).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 1 - 2
MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs

@@ -25,10 +25,9 @@ namespace MediaBrowser.LocalMetadata.Providers
             var images = new List<LocalImageInfo>();
             var chapters = new List<ChapterInfo>();
 
-            new EpisodeXmlParser(_logger).Fetch(result.Item, images, chapters, path, cancellationToken);
+            new EpisodeXmlParser(_logger).Fetch(result, images, path, cancellationToken);
 
             result.Images = images;
-            result.Chapters = chapters;
         }
 
         protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 1 - 1
MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs

@@ -22,7 +22,7 @@ namespace MediaBrowser.LocalMetadata.Providers
 
         protected override void Fetch(LocalMetadataResult<Folder> result, string path, CancellationToken cancellationToken)
         {
-            new BaseItemXmlParser<Folder>(_logger).Fetch(result.Item, path, cancellationToken);
+            new BaseItemXmlParser<Folder>(_logger).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 1 - 1
MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs

@@ -20,7 +20,7 @@ namespace MediaBrowser.LocalMetadata.Providers
 
         protected override void Fetch(LocalMetadataResult<GameSystem> result, string path, CancellationToken cancellationToken)
         {
-            new GameSystemXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
+            new GameSystemXmlParser(_logger).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 1 - 1
MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs

@@ -20,7 +20,7 @@ namespace MediaBrowser.LocalMetadata.Providers
 
         protected override void Fetch(LocalMetadataResult<Game> result, string path, CancellationToken cancellationToken)
         {
-            new GameXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
+            new GameXmlParser(_logger).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 1 - 7
MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs

@@ -2,9 +2,7 @@
 using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.LocalMetadata.Parsers;
-using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
-using System.Collections.Generic;
 using System.IO;
 using System.Threading;
 
@@ -22,11 +20,7 @@ namespace MediaBrowser.LocalMetadata.Providers
 
         protected override void Fetch(LocalMetadataResult<Movie> result, string path, CancellationToken cancellationToken)
         {
-            var chapters = new List<ChapterInfo>();
-
-            new MovieXmlParser(_logger).Fetch(result.Item, chapters, path, cancellationToken);
-
-            result.Chapters = chapters;
+            new MovieXmlParser(_logger).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 1 - 1
MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs

@@ -20,7 +20,7 @@ namespace MediaBrowser.LocalMetadata.Providers
 
         protected override void Fetch(LocalMetadataResult<MusicVideo> result, string path, CancellationToken cancellationToken)
         {
-            new MusicVideoXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
+            new MusicVideoXmlParser(_logger).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 1 - 1
MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs

@@ -19,7 +19,7 @@ namespace MediaBrowser.LocalMetadata.Providers
 
         protected override void Fetch(LocalMetadataResult<Person> result, string path, CancellationToken cancellationToken)
         {
-            new BaseItemXmlParser<Person>(_logger).Fetch(result.Item, path, cancellationToken);
+            new BaseItemXmlParser<Person>(_logger).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 1 - 1
MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs

@@ -20,7 +20,7 @@ namespace MediaBrowser.LocalMetadata.Providers
 
         protected override void Fetch(LocalMetadataResult<Playlist> result, string path, CancellationToken cancellationToken)
         {
-            new PlaylistXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
+            new PlaylistXmlParser(_logger).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 1 - 1
MediaBrowser.LocalMetadata/Providers/SeasonXmlProvider.cs

@@ -23,7 +23,7 @@ namespace MediaBrowser.LocalMetadata.Providers
 
         protected override void Fetch(LocalMetadataResult<Season> result, string path, CancellationToken cancellationToken)
         {
-            new SeasonXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
+            new SeasonXmlParser(_logger).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 1 - 1
MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs

@@ -23,7 +23,7 @@ namespace MediaBrowser.LocalMetadata.Providers
 
         protected override void Fetch(LocalMetadataResult<Series> result, string path, CancellationToken cancellationToken)
         {
-            new SeriesXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
+            new SeriesXmlParser(_logger).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 4 - 10
MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs

@@ -1,12 +1,10 @@
-using System.Collections.Generic;
-using System.IO;
-using System.Threading;
-using MediaBrowser.Common.IO;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.LocalMetadata.Parsers;
-using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
+using System.IO;
+using System.Threading;
 
 namespace MediaBrowser.LocalMetadata.Providers
 {
@@ -22,11 +20,7 @@ namespace MediaBrowser.LocalMetadata.Providers
 
         protected override void Fetch(LocalMetadataResult<Video> result, string path, CancellationToken cancellationToken)
         {
-            var chapters = new List<ChapterInfo>();
-
-            new MovieXmlParser(_logger).Fetch(result.Item, chapters, path, cancellationToken);
-
-            result.Chapters = chapters;
+            new VideoXmlParser(_logger).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 4 - 12
MediaBrowser.Providers/Books/BookMetadataService.cs

@@ -12,25 +12,17 @@ namespace MediaBrowser.Providers.Books
 {
     public class BookMetadataService : MetadataService<Book, BookInfo>
     {
-        public BookMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public BookMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(Book source, Book target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<Book> source, MetadataResult<Book> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
 
-            if (replaceData || string.IsNullOrEmpty(target.SeriesName))
+            if (replaceData || string.IsNullOrEmpty(target.Item.SeriesName))
             {
-                target.SeriesName = source.SeriesName;
+                target.Item.SeriesName = source.Item.SeriesName;
             }
         }
     }

+ 19 - 24
MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs

@@ -15,33 +15,10 @@ namespace MediaBrowser.Providers.BoxSets
 {
     public class BoxSetMetadataService : MetadataService<BoxSet, BoxSetInfo>
     {
-        public BoxSetMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public BoxSetMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(BoxSet source, BoxSet target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
-        {
-            ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
-
-            if (mergeMetadataSettings)
-            {
-                var list = source.LinkedChildren.Where(i => i.Type != LinkedChildType.Manual).ToList();
-
-                list.AddRange(target.LinkedChildren.Where(i => i.Type == LinkedChildType.Manual));
-
-                target.LinkedChildren = list;
-                target.Shares = source.Shares;
-            }
-        }
-
         protected override async Task<ItemUpdateType> BeforeSave(BoxSet item, bool isFullRefresh, ItemUpdateType currentUpdateType)
         {
             var updateType = await base.BeforeSave(item, isFullRefresh, currentUpdateType).ConfigureAwait(false);
@@ -59,5 +36,23 @@ namespace MediaBrowser.Providers.BoxSets
 
             return updateType;
         }
+
+        protected override void MergeData(MetadataResult<BoxSet> source, MetadataResult<BoxSet> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        {
+            ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+
+            var sourceItem = source.Item;
+            var targetItem = target.Item;
+
+            if (mergeMetadataSettings)
+            {
+                var list = sourceItem.LinkedChildren.Where(i => i.Type != LinkedChildType.Manual).ToList();
+
+                list.AddRange(targetItem.LinkedChildren.Where(i => i.Type == LinkedChildType.Manual));
+
+                targetItem.LinkedChildren = list;
+                targetItem.Shares = sourceItem.Shares;
+            }
+        }
     }
 }

+ 2 - 10
MediaBrowser.Providers/Channels/AudioChannelItemMetadataService.cs

@@ -12,19 +12,11 @@ namespace MediaBrowser.Providers.Channels
 {
     public class AudioChannelItemMetadataService : MetadataService<ChannelAudioItem, ItemLookupInfo>
     {
-        public AudioChannelItemMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public AudioChannelItemMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(ChannelAudioItem source, ChannelAudioItem target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<ChannelAudioItem> source, MetadataResult<ChannelAudioItem> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
         }

+ 2 - 10
MediaBrowser.Providers/Channels/ChannelMetadataService.cs

@@ -12,19 +12,11 @@ namespace MediaBrowser.Providers.Channels
 {
     public class ChannelMetadataService : MetadataService<Channel, ItemLookupInfo>
     {
-        public ChannelMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public ChannelMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(Channel source, Channel target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<Channel> source, MetadataResult<Channel> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
         }

+ 2 - 10
MediaBrowser.Providers/Channels/VideoChannelItemMetadataService.cs

@@ -12,19 +12,11 @@ namespace MediaBrowser.Providers.Channels
 {
     public class VideoChannelItemMetadataService : MetadataService<ChannelVideoItem, ChannelItemLookupInfo>
     {
-        public VideoChannelItemMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public VideoChannelItemMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(ChannelVideoItem source, ChannelVideoItem target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<ChannelVideoItem> source, MetadataResult<ChannelVideoItem> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
         }

+ 6 - 14
MediaBrowser.Providers/Folders/FolderMetadataService.cs

@@ -12,23 +12,10 @@ namespace MediaBrowser.Providers.Folders
 {
     public class FolderMetadataService : MetadataService<Folder, ItemLookupInfo>
     {
-        public FolderMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public FolderMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(Folder source, Folder target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
-        {
-            ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
-        }
-
         public override int Order
         {
             get
@@ -37,5 +24,10 @@ namespace MediaBrowser.Providers.Folders
                 return 10;
             }
         }
+
+        protected override void MergeData(MetadataResult<Folder> source, MetadataResult<Folder> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        {
+            ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+        }
     }
 }

+ 2 - 11
MediaBrowser.Providers/Folders/UserViewMetadataService.cs

@@ -12,20 +12,11 @@ namespace MediaBrowser.Providers.Folders
 {
     public class UserViewMetadataService : MetadataService<UserView, ItemLookupInfo>
     {
-        public UserViewMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager)
-            : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public UserViewMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(UserView source, UserView target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<UserView> source, MetadataResult<UserView> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
         }

+ 2 - 10
MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs

@@ -12,19 +12,11 @@ namespace MediaBrowser.Providers.GameGenres
 {
     public class GameGenreMetadataService : MetadataService<GameGenre, ItemLookupInfo>
     {
-        public GameGenreMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public GameGenreMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(GameGenre source, GameGenre target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<GameGenre> source, MetadataResult<GameGenre> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
         }

+ 9 - 14
MediaBrowser.Providers/Games/GameMetadataService.cs

@@ -12,30 +12,25 @@ namespace MediaBrowser.Providers.Games
 {
     public class GameMetadataService : MetadataService<Game, GameInfo>
     {
-        public GameMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public GameMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(Game source, Game target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<Game> source, MetadataResult<Game> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
 
-            if (replaceData || string.IsNullOrEmpty(target.GameSystem))
+            var sourceItem = source.Item;
+            var targetItem = target.Item;
+
+            if (replaceData || string.IsNullOrEmpty(targetItem.GameSystem))
             {
-                target.GameSystem = source.GameSystem;
+                targetItem.GameSystem = sourceItem.GameSystem;
             }
 
-            if (replaceData || !target.PlayersSupported.HasValue)
+            if (replaceData || !targetItem.PlayersSupported.HasValue)
             {
-                target.PlayersSupported = source.PlayersSupported;
+                targetItem.PlayersSupported = sourceItem.PlayersSupported;
             }
         }
     }

+ 7 - 12
MediaBrowser.Providers/Games/GameSystemMetadataService.cs

@@ -14,25 +14,20 @@ namespace MediaBrowser.Providers.Games
 {
     public class GameSystemMetadataService : MetadataService<GameSystem, GameSystemInfo>
     {
-        public GameSystemMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public GameSystemMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(GameSystem source, GameSystem target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<GameSystem> source, MetadataResult<GameSystem> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
 
-            if (replaceData || string.IsNullOrEmpty(target.GameSystemName))
+            var sourceItem = source.Item;
+            var targetItem = target.Item;
+
+            if (replaceData || string.IsNullOrEmpty(targetItem.GameSystemName))
             {
-                target.GameSystemName = source.GameSystemName;
+                targetItem.GameSystemName = sourceItem.GameSystemName;
             }
         }
     }

+ 2 - 10
MediaBrowser.Providers/Genres/GenreMetadataService.cs

@@ -12,19 +12,11 @@ namespace MediaBrowser.Providers.Genres
 {
     public class GenreMetadataService : MetadataService<Genre, ItemLookupInfo>
     {
-        public GenreMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public GenreMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(Genre source, Genre target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<Genre> source, MetadataResult<Genre> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
         }

+ 2 - 9
MediaBrowser.Providers/LiveTv/AudioRecordingService.cs

@@ -14,18 +14,11 @@ namespace MediaBrowser.Providers.LiveTv
 {
     public class AudioRecordingService : MetadataService<LiveTvAudioRecording, ItemLookupInfo>
     {
-        public AudioRecordingService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public AudioRecordingService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        protected override void MergeData(LiveTvAudioRecording source, LiveTvAudioRecording target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<LiveTvAudioRecording> source, MetadataResult<LiveTvAudioRecording> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
         }

+ 2 - 5
MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs

@@ -14,14 +14,11 @@ namespace MediaBrowser.Providers.LiveTv
 {
     public class ChannelMetadataService : MetadataService<LiveTvChannel, ItemLookupInfo>
     {
-        public ChannelMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public ChannelMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        protected override void MergeData(LiveTvChannel source, LiveTvChannel target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<LiveTvChannel> source, MetadataResult<LiveTvChannel> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
         }

+ 2 - 14
MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs

@@ -12,23 +12,11 @@ namespace MediaBrowser.Providers.LiveTv
 {
     public class ProgramMetadataService : MetadataService<LiveTvProgram, LiveTvProgramLookupInfo>
     {
-        public ProgramMetadataService(
-            IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager,
-            IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager)
-            : base(
-                serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public ProgramMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings"></param>
-        protected override void MergeData(LiveTvProgram source, LiveTvProgram target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<LiveTvProgram> source, MetadataResult<LiveTvProgram> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
         }

+ 2 - 9
MediaBrowser.Providers/LiveTv/VideoRecordingService.cs

@@ -14,18 +14,11 @@ namespace MediaBrowser.Providers.LiveTv
 {
     public class VideoRecordingService : MetadataService<LiveTvVideoRecording, ItemLookupInfo>
     {
-        public VideoRecordingService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public VideoRecordingService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        protected override void MergeData(LiveTvVideoRecording source, LiveTvVideoRecording target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<LiveTvVideoRecording> source, MetadataResult<LiveTvVideoRecording> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
         }

+ 35 - 17
MediaBrowser.Providers/Manager/MetadataService.cs

@@ -24,8 +24,9 @@ namespace MediaBrowser.Providers.Manager
         protected readonly IProviderRepository ProviderRepo;
         protected readonly IFileSystem FileSystem;
         protected readonly IUserDataManager UserDataManager;
+        protected readonly ILibraryManager LibraryManager;
 
-        protected MetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager)
+        protected MetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager)
         {
             ServerConfigurationManager = serverConfigurationManager;
             Logger = logger;
@@ -33,6 +34,7 @@ namespace MediaBrowser.Providers.Manager
             ProviderRepo = providerRepo;
             FileSystem = fileSystem;
             UserDataManager = userDataManager;
+            LibraryManager = libraryManager;
         }
 
         /// <summary>
@@ -118,6 +120,11 @@ namespace MediaBrowser.Providers.Manager
                 refreshResult.AddStatus(ProviderRefreshStatus.Failure, ex.Message);
             }
 
+            var metadataResult = new MetadataResult<TItemType>
+            {
+                Item = itemOfType
+            };
+
             // Next run metadata providers
             if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
             {
@@ -136,7 +143,7 @@ namespace MediaBrowser.Providers.Manager
                 {
                     var id = await CreateInitialLookupInfo(itemOfType, cancellationToken).ConfigureAwait(false);
 
-                    var result = await RefreshWithProviders(itemOfType, id, refreshOptions, providers, itemImageProvider, cancellationToken).ConfigureAwait(false);
+                    var result = await RefreshWithProviders(metadataResult, id, refreshOptions, providers, itemImageProvider, cancellationToken).ConfigureAwait(false);
 
                     updateType = updateType | result.UpdateType;
                     refreshResult.AddStatus(result.Status, result.ErrorMessage);
@@ -174,7 +181,7 @@ namespace MediaBrowser.Providers.Manager
                 }
 
                 // Save to database
-                await SaveItem(itemOfType, updateType, cancellationToken);
+                await SaveItem(metadataResult, updateType, cancellationToken).ConfigureAwait(false);
             }
 
             if (updateType > ItemUpdateType.None || refreshResult.IsDirty)
@@ -321,9 +328,14 @@ namespace MediaBrowser.Providers.Manager
             return providers;
         }
 
-        protected Task SaveItem(TItemType item, ItemUpdateType reason, CancellationToken cancellationToken)
+        protected async Task SaveItem(MetadataResult<TItemType> result, ItemUpdateType reason, CancellationToken cancellationToken)
         {
-            return item.UpdateToRepository(reason, cancellationToken);
+            await result.Item.UpdateToRepository(reason, cancellationToken).ConfigureAwait(false);
+
+            if (result.Item.SupportsPeople)
+            {
+                await LibraryManager.UpdatePeople(result.Item as BaseItem, result.People);
+            }
         }
 
         public bool CanRefresh(IHasMetadata item)
@@ -331,7 +343,7 @@ namespace MediaBrowser.Providers.Manager
             return item is TItemType;
         }
 
-        protected virtual async Task<RefreshResult> RefreshWithProviders(TItemType item,
+        protected virtual async Task<RefreshResult> RefreshWithProviders(MetadataResult<TItemType> metadata,
             TIdType id,
             MetadataRefreshOptions options,
             List<IMetadataProvider> providers,
@@ -344,6 +356,8 @@ namespace MediaBrowser.Providers.Manager
                 Providers = providers.Select(i => i.GetType().FullName.GetMD5()).ToList()
             };
 
+            var item = metadata.Item;
+
             var customProviders = providers.OfType<ICustomMetadataProvider<TItemType>>().ToList();
             var logName = item.LocationType == LocationType.Remote ? item.Name ?? item.Path : item.Path ?? item.Name;
 
@@ -352,11 +366,16 @@ namespace MediaBrowser.Providers.Manager
                 await RunCustomProvider(provider, item, logName, options, refreshResult, cancellationToken).ConfigureAwait(false);
             }
 
-            var temp = CreateNew();
-            temp.Path = item.Path;
+            var temp = new MetadataResult<TItemType>
+            {
+                Item = CreateNew()
+            };
+            temp.Item.Path = item.Path;
             var successfulProviderCount = 0;
             var failedProviderCount = 0;
 
+            var userDataList = new List<UserItemData>();
+
             // If replacing all metadata, run internet providers first
             if (options.ReplaceAllMetadata)
             {
@@ -371,7 +390,6 @@ namespace MediaBrowser.Providers.Manager
             }
 
             var hasLocalMetadata = false;
-            var userDataList = new List<UserItemData>();
 
             foreach (var provider in providers.OfType<ILocalMetadataProvider<TItemType>>().ToList())
             {
@@ -393,7 +411,7 @@ namespace MediaBrowser.Providers.Manager
 
                         userDataList = localItem.UserDataLIst;
 
-                        MergeData(localItem.Item, temp, new List<MetadataFields>(), !options.ReplaceAllMetadata, true);
+                        MergeData(localItem, temp, new List<MetadataFields>(), !options.ReplaceAllMetadata, true);
                         refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport;
 
                         // Only one local provider allowed per item
@@ -452,10 +470,10 @@ namespace MediaBrowser.Providers.Manager
                     if (!hasLocalMetadata)
                     {
                         // TODO: If the new metadata from above has some blank data, this can cause old data to get filled into those empty fields
-                        MergeData(item, temp, new List<MetadataFields>(), false, true);
+                        MergeData(metadata, temp, new List<MetadataFields>(), false, true);
                     }
 
-                    MergeData(temp, item, item.LockedFields, true, true);
+                    MergeData(temp, metadata, item.LockedFields, true, true);
                 }
             }
 
@@ -526,7 +544,7 @@ namespace MediaBrowser.Providers.Manager
             return new TItemType();
         }
 
-        private async Task<RefreshResult> ExecuteRemoteProviders(TItemType temp, string logName, TIdType id, IEnumerable<IRemoteMetadataProvider<TItemType, TIdType>> providers, CancellationToken cancellationToken)
+        private async Task<RefreshResult> ExecuteRemoteProviders(MetadataResult<TItemType> temp, string logName, TIdType id, IEnumerable<IRemoteMetadataProvider<TItemType, TIdType>> providers, CancellationToken cancellationToken)
         {
             var refreshResult = new RefreshResult();
 
@@ -537,7 +555,7 @@ namespace MediaBrowser.Providers.Manager
 
                 if (id != null)
                 {
-                    MergeNewData(temp, id);
+                    MergeNewData(temp.Item, id);
                 }
 
                 try
@@ -548,7 +566,7 @@ namespace MediaBrowser.Providers.Manager
                     {
                         NormalizeRemoteResult(result.Item);
 
-                        MergeData(result.Item, temp, new List<MetadataFields>(), false, false);
+                        MergeData(result, temp, new List<MetadataFields>(), false, false);
 
                         refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataDownload;
 
@@ -624,8 +642,8 @@ namespace MediaBrowser.Providers.Manager
             }
         }
 
-        protected abstract void MergeData(TItemType source,
-            TItemType target,
+        protected abstract void MergeData(MetadataResult<TItemType> source,
+            MetadataResult<TItemType> target,
             List<MetadataFields> lockedFields,
             bool replaceData,
             bool mergeMetadataSettings);

+ 9 - 4
MediaBrowser.Providers/Manager/ProviderUtils.cs

@@ -1,5 +1,6 @@
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using System;
 using System.Collections.Generic;
@@ -8,12 +9,16 @@ namespace MediaBrowser.Providers.Manager
 {
     public static class ProviderUtils
     {
-        public static void MergeBaseItemData(BaseItem source, 
-            BaseItem target, 
+        public static void MergeBaseItemData<T>(MetadataResult<T> sourceResult,
+            MetadataResult<T> targetResult, 
             List<MetadataFields> lockedFields, 
             bool replaceData, 
             bool mergeMetadataSettings)
+            where T : BaseItem
         {
+            var source = sourceResult.Item;
+            var target = targetResult.Item;
+
             if (source == null)
             {
                 throw new ArgumentNullException("source");
@@ -100,9 +105,9 @@ namespace MediaBrowser.Providers.Manager
 
             if (!lockedFields.Contains(MetadataFields.Cast))
             {
-                if (replaceData || target.People.Count == 0)
+                if (replaceData || targetResult.People.Count == 0)
                 {
-                    target.People = source.People;
+                    targetResult.People = sourceResult.People;
                 }
             }
 

+ 6 - 4
MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs

@@ -51,6 +51,7 @@ namespace MediaBrowser.Providers.MediaInfo
         private readonly IServerConfigurationManager _config;
         private readonly ISubtitleManager _subtitleManager;
         private readonly IChapterManager _chapterManager;
+        private readonly ILibraryManager _libraryManager;
 
         public string Name
         {
@@ -92,7 +93,7 @@ namespace MediaBrowser.Providers.MediaInfo
             return FetchAudioInfo(item, cancellationToken);
         }
 
-        public FFProbeProvider(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager, IChapterManager chapterManager)
+        public FFProbeProvider(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager, IChapterManager chapterManager, ILibraryManager libraryManager)
         {
             _logger = logger;
             _isoManager = isoManager;
@@ -107,6 +108,7 @@ namespace MediaBrowser.Providers.MediaInfo
             _config = config;
             _subtitleManager = subtitleManager;
             _chapterManager = chapterManager;
+            _libraryManager = libraryManager;
         }
 
         private readonly Task<ItemUpdateType> _cachedTask = Task.FromResult(ItemUpdateType.None);
@@ -138,8 +140,8 @@ namespace MediaBrowser.Providers.MediaInfo
                 FetchShortcutInfo(item);
                 return Task.FromResult(ItemUpdateType.MetadataEdit);
             }
-            
-            var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem, _config, _subtitleManager, _chapterManager);
+
+            var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem, _config, _subtitleManager, _chapterManager, _libraryManager);
 
             return prober.ProbeVideo(item, options, cancellationToken);
         }
@@ -157,7 +159,7 @@ namespace MediaBrowser.Providers.MediaInfo
                 return _cachedTask;
             }
 
-            var prober = new FFProbeAudioInfo(_mediaEncoder, _itemRepo, _appPaths, _json);
+            var prober = new FFProbeAudioInfo(_mediaEncoder, _itemRepo, _appPaths, _json, _libraryManager);
 
             return prober.Probe(item, cancellationToken);
         }

+ 16 - 14
MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs

@@ -34,8 +34,6 @@ namespace MediaBrowser.Providers.Movies
 
         public async Task<MetadataResult<T>> GetMetadata(ItemLookupInfo itemId, CancellationToken cancellationToken)
         {
-            var result = new MetadataResult<T>();
-
             var tmdbId = itemId.GetProviderId(MetadataProviders.Tmdb);
             var imdbId = itemId.GetProviderId(MetadataProviders.Imdb);
 
@@ -56,12 +54,10 @@ namespace MediaBrowser.Providers.Movies
             {
                 cancellationToken.ThrowIfCancellationRequested();
 
-                result.Item = await FetchMovieData(tmdbId, imdbId, itemId.MetadataLanguage, itemId.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
-
-                result.HasMetadata = result.Item != null;
+                return await FetchMovieData(tmdbId, imdbId, itemId.MetadataLanguage, itemId.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
             }
 
-            return result;
+            return new MetadataResult<T>();
         }
 
         /// <summary>
@@ -73,8 +69,13 @@ namespace MediaBrowser.Providers.Movies
         /// <param name="preferredCountryCode">The preferred country code.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{`0}.</returns>
-        private async Task<T> FetchMovieData(string tmdbId, string imdbId, string language, string preferredCountryCode, CancellationToken cancellationToken)
+        private async Task<MetadataResult<T>> FetchMovieData(string tmdbId, string imdbId, string language, string preferredCountryCode, CancellationToken cancellationToken)
         {
+            var item = new MetadataResult<T>
+            {
+                Item = new T()
+            };
+
             string dataFilePath = null;
             MovieDbProvider.CompleteMovieData movieInfo = null;
 
@@ -82,7 +83,7 @@ namespace MediaBrowser.Providers.Movies
             if (string.IsNullOrEmpty(tmdbId))
             {
                 movieInfo = await MovieDbProvider.Current.FetchMainResult(imdbId, false, language, cancellationToken).ConfigureAwait(false);
-                if (movieInfo == null) return null;
+                if (movieInfo == null) return item;
 
                 tmdbId = movieInfo.id.ToString(_usCulture);
 
@@ -96,9 +97,8 @@ namespace MediaBrowser.Providers.Movies
             dataFilePath = dataFilePath ?? MovieDbProvider.Current.GetDataFilePath(tmdbId, language);
             movieInfo = movieInfo ?? _jsonSerializer.DeserializeFromFile<MovieDbProvider.CompleteMovieData>(dataFilePath);
 
-            var item = new T();
-
             ProcessMainInfo(item, preferredCountryCode, movieInfo);
+            item.HasMetadata = true;
 
             return item;
         }
@@ -106,11 +106,13 @@ namespace MediaBrowser.Providers.Movies
         /// <summary>
         /// Processes the main info.
         /// </summary>
-        /// <param name="movie">The movie.</param>
+        /// <param name="resultItem">The result item.</param>
         /// <param name="preferredCountryCode">The preferred country code.</param>
         /// <param name="movieData">The movie data.</param>
-        private void ProcessMainInfo(T movie, string preferredCountryCode, MovieDbProvider.CompleteMovieData movieData)
+        private void ProcessMainInfo(MetadataResult<T> resultItem, string preferredCountryCode, MovieDbProvider.CompleteMovieData movieData)
         {
+            var movie = resultItem.Item;
+
             movie.Name = movieData.GetTitle() ?? movie.Name;
 
             var hasOriginalTitle = movie as IHasOriginalTitle;
@@ -235,7 +237,7 @@ namespace MediaBrowser.Providers.Movies
             {
                 foreach (var actor in movieData.casts.cast.OrderBy(a => a.order))
                 {
-                    PeopleHelper.AddPerson(movie.People, new PersonInfo { Name = actor.name.Trim(), Role = actor.character, Type = PersonType.Actor, SortOrder = actor.order });
+                    PeopleHelper.AddPerson(resultItem.People, new PersonInfo { Name = actor.name.Trim(), Role = actor.character, Type = PersonType.Actor, SortOrder = actor.order });
                 }
             }
 
@@ -244,7 +246,7 @@ namespace MediaBrowser.Providers.Movies
             {
                 foreach (var person in movieData.casts.crew)
                 {
-                    PeopleHelper.AddPerson(movie.People, new PersonInfo { Name = person.name.Trim(), Role = person.job, Type = person.department });
+                    PeopleHelper.AddPerson(resultItem.People, new PersonInfo { Name = person.name.Trim(), Role = person.job, Type = person.department });
                 }
             }
 

+ 14 - 19
MediaBrowser.Providers/Movies/MovieMetadataService.cs

@@ -12,28 +12,10 @@ namespace MediaBrowser.Providers.Movies
 {
     public class MovieMetadataService : MetadataService<Movie, MovieInfo>
     {
-        public MovieMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public MovieMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(Movie source, Movie target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
-        {
-            ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
-
-            if (replaceData || string.IsNullOrEmpty(target.TmdbCollectionName))
-            {
-                target.TmdbCollectionName = source.TmdbCollectionName;
-            }
-        }
-
         protected override bool IsFullLocalMetadata(Movie item)
         {
             if (string.IsNullOrWhiteSpace(item.Overview))
@@ -46,5 +28,18 @@ namespace MediaBrowser.Providers.Movies
             }
             return base.IsFullLocalMetadata(item);
         }
+
+        protected override void MergeData(MetadataResult<Movie> source, MetadataResult<Movie> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        {
+            ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+
+            var sourceItem = source.Item;
+            var targetItem = target.Item;
+
+            if (replaceData || string.IsNullOrEmpty(targetItem.TmdbCollectionName))
+            {
+                targetItem.TmdbCollectionName = sourceItem.TmdbCollectionName;
+            }
+        }
     }
 }

+ 14 - 19
MediaBrowser.Providers/Music/AlbumMetadataService.cs

@@ -15,28 +15,10 @@ namespace MediaBrowser.Providers.Music
 {
     public class AlbumMetadataService : MetadataService<MusicAlbum, AlbumInfo>
     {
-        public AlbumMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public AlbumMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(MusicAlbum source, MusicAlbum target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
-        {
-            ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
-
-            if (replaceData || target.Artists.Count == 0)
-            {
-                target.Artists = source.Artists;
-            }
-        }
-
         protected override async Task<ItemUpdateType> BeforeSave(MusicAlbum item, bool isFullRefresh, ItemUpdateType currentUpdateType)
         {
             var updateType = await base.BeforeSave(item, isFullRefresh, currentUpdateType).ConfigureAwait(false);
@@ -171,5 +153,18 @@ namespace MediaBrowser.Providers.Music
 
             return updateType;
         }
+
+        protected override void MergeData(MetadataResult<MusicAlbum> source, MetadataResult<MusicAlbum> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        {
+            ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+
+            var sourceItem = source.Item;
+            var targetItem = target.Item;
+
+            if (replaceData || targetItem.Artists.Count == 0)
+            {
+                targetItem.Artists = sourceItem.Artists;
+            }
+        }
     }
 }

+ 7 - 18
MediaBrowser.Providers/Music/ArtistMetadataService.cs

@@ -15,24 +15,8 @@ namespace MediaBrowser.Providers.Music
 {
     public class ArtistMetadataService : MetadataService<MusicArtist, ArtistInfo>
     {
-        private readonly ILibraryManager _libraryManager;
-
-        public ArtistMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public ArtistMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
-            _libraryManager = libraryManager;
-        }
-
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(MusicArtist source, MusicArtist target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
-        {
-            ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
         }
 
         protected override async Task<ItemUpdateType> BeforeSave(MusicArtist item, bool isFullRefresh, ItemUpdateType currentUpdateType)
@@ -46,7 +30,7 @@ namespace MediaBrowser.Providers.Music
                     var itemFilter = item.GetItemFilter();
 
                     var taggedItems = item.IsAccessedByName ?
-                        _libraryManager.RootFolder.GetRecursiveChildren(i => !i.IsFolder && itemFilter(i)).ToList() :
+                        LibraryManager.RootFolder.GetRecursiveChildren(i => !i.IsFolder && itemFilter(i)).ToList() :
                         item.GetRecursiveChildren(i => i is IHasArtist && !i.IsFolder).ToList();
 
                     if (!item.LockedFields.Contains(MetadataFields.Genres))
@@ -67,5 +51,10 @@ namespace MediaBrowser.Providers.Music
             
             return updateType;
         }
+
+        protected override void MergeData(MetadataResult<MusicArtist> source, MetadataResult<MusicArtist> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        {
+            ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+        }
     }
 }

+ 9 - 14
MediaBrowser.Providers/Music/AudioMetadataService.cs

@@ -12,30 +12,25 @@ namespace MediaBrowser.Providers.Music
 {
     public class AudioMetadataService : MetadataService<Audio, SongInfo>
     {
-        public AudioMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public AudioMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(Audio source, Audio target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<Audio> source, MetadataResult<Audio> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
 
-            if (replaceData || target.Artists.Count == 0)
+            var sourceItem = source.Item;
+            var targetItem = target.Item;
+
+            if (replaceData || targetItem.Artists.Count == 0)
             {
-                target.Artists = source.Artists;
+                targetItem.Artists = sourceItem.Artists;
             }
 
-            if (replaceData || string.IsNullOrEmpty(target.Album))
+            if (replaceData || string.IsNullOrEmpty(targetItem.Album))
             {
-                target.Album = source.Album;
+                targetItem.Album = sourceItem.Album;
             }
         }
     }

+ 9 - 14
MediaBrowser.Providers/Music/MusicVideoMetadataService.cs

@@ -13,30 +13,25 @@ namespace MediaBrowser.Providers.Music
 {
     class MusicVideoMetadataService : MetadataService<MusicVideo, MusicVideoInfo>
     {
-        public MusicVideoMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public MusicVideoMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(MusicVideo source, MusicVideo target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<MusicVideo> source, MetadataResult<MusicVideo> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
 
-            if (replaceData || string.IsNullOrEmpty(target.Album))
+            var sourceItem = source.Item;
+            var targetItem = target.Item;
+
+            if (replaceData || string.IsNullOrEmpty(targetItem.Album))
             {
-                target.Album = source.Album;
+                targetItem.Album = sourceItem.Album;
             }
 
-            if (replaceData || target.Artists.Count == 0)
+            if (replaceData || targetItem.Artists.Count == 0)
             {
-                target.Artists = source.Artists.ToList();
+                targetItem.Artists = sourceItem.Artists.ToList();
             }
         }
     }

+ 2 - 10
MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs

@@ -12,19 +12,11 @@ namespace MediaBrowser.Providers.MusicGenres
 {
     public class MusicGenreMetadataService : MetadataService<MusicGenre, ItemLookupInfo>
     {
-        public MusicGenreMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public MusicGenreMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(MusicGenre source, MusicGenre target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<MusicGenre> source, MetadataResult<MusicGenre> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
         }

+ 7 - 12
MediaBrowser.Providers/People/PersonMetadataService.cs

@@ -14,25 +14,20 @@ namespace MediaBrowser.Providers.People
 {
     public class PersonMetadataService : MetadataService<Person, PersonLookupInfo>
     {
-        public PersonMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public PersonMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(Person source, Person target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<Person> source, MetadataResult<Person> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
 
-            if (replaceData || string.IsNullOrEmpty(target.PlaceOfBirth))
+            var sourceItem = source.Item;
+            var targetItem = target.Item;
+
+            if (replaceData || string.IsNullOrEmpty(targetItem.PlaceOfBirth))
             {
-                target.PlaceOfBirth = source.PlaceOfBirth;
+                targetItem.PlaceOfBirth = sourceItem.PlaceOfBirth;
             }
         }
     }

+ 2 - 11
MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs

@@ -12,20 +12,11 @@ namespace MediaBrowser.Providers.Photos
 {
     class PhotoAlbumMetadataService : MetadataService<PhotoAlbum, ItemLookupInfo>
     {
-        public PhotoAlbumMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager)
-            : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public PhotoAlbumMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(PhotoAlbum source, PhotoAlbum target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<PhotoAlbum> source, MetadataResult<PhotoAlbum> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
         }

+ 2 - 10
MediaBrowser.Providers/Photos/PhotoMetadataService.cs

@@ -12,19 +12,11 @@ namespace MediaBrowser.Providers.Photos
 {
     class PhotoMetadataService : MetadataService<Photo, ItemLookupInfo>
     {
-        public PhotoMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public PhotoMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(Photo source, Photo target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<Photo> source, MetadataResult<Photo> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
         }

+ 9 - 14
MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs

@@ -12,31 +12,26 @@ namespace MediaBrowser.Providers.Playlists
 {
     class PlaylistMetadataService : MetadataService<Playlist, ItemLookupInfo>
     {
-        public PlaylistMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public PlaylistMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(Playlist source, Playlist target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<Playlist> source, MetadataResult<Playlist> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
 
-            if (replaceData || string.IsNullOrEmpty(target.PlaylistMediaType))
+            var sourceItem = source.Item;
+            var targetItem = target.Item;
+
+            if (replaceData || string.IsNullOrEmpty(targetItem.PlaylistMediaType))
             {
-                target.PlaylistMediaType = source.PlaylistMediaType;
+                targetItem.PlaylistMediaType = sourceItem.PlaylistMediaType;
             }
 
             if (mergeMetadataSettings)
             {
-                target.LinkedChildren = source.LinkedChildren;
-                target.Shares = source.Shares;
+                targetItem.LinkedChildren = sourceItem.LinkedChildren;
+                targetItem.Shares = sourceItem.Shares;
             }
         }
     }

+ 2 - 9
MediaBrowser.Providers/Studios/StudioMetadataService.cs

@@ -14,18 +14,11 @@ namespace MediaBrowser.Providers.Studios
 {
     public class StudioMetadataService : MetadataService<Studio, ItemLookupInfo>
     {
-        public StudioMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public StudioMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        protected override void MergeData(Studio source, Studio target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<Studio> source, MetadataResult<Studio> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
         }

+ 19 - 24
MediaBrowser.Providers/TV/EpisodeMetadataService.cs

@@ -12,55 +12,50 @@ namespace MediaBrowser.Providers.TV
 {
     public class EpisodeMetadataService : MetadataService<Episode, EpisodeInfo>
     {
-        public EpisodeMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public EpisodeMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(Episode source, Episode target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<Episode> source, MetadataResult<Episode> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
 
-            if (replaceData || !target.AirsBeforeSeasonNumber.HasValue)
+            var sourceItem = source.Item;
+            var targetItem = target.Item;
+
+            if (replaceData || !targetItem.AirsBeforeSeasonNumber.HasValue)
             {
-                target.AirsBeforeSeasonNumber = source.AirsBeforeSeasonNumber;
+                targetItem.AirsBeforeSeasonNumber = sourceItem.AirsBeforeSeasonNumber;
             }
 
-            if (replaceData || !target.AirsAfterSeasonNumber.HasValue)
+            if (replaceData || !targetItem.AirsAfterSeasonNumber.HasValue)
             {
-                target.AirsAfterSeasonNumber = source.AirsAfterSeasonNumber;
+                targetItem.AirsAfterSeasonNumber = sourceItem.AirsAfterSeasonNumber;
             }
 
-            if (replaceData || !target.AirsBeforeEpisodeNumber.HasValue)
+            if (replaceData || !targetItem.AirsBeforeEpisodeNumber.HasValue)
             {
-                target.AirsBeforeEpisodeNumber = source.AirsBeforeEpisodeNumber;
+                targetItem.AirsBeforeEpisodeNumber = sourceItem.AirsBeforeEpisodeNumber;
             }
 
-            if (replaceData || !target.DvdSeasonNumber.HasValue)
+            if (replaceData || !targetItem.DvdSeasonNumber.HasValue)
             {
-                target.DvdSeasonNumber = source.DvdSeasonNumber;
+                targetItem.DvdSeasonNumber = sourceItem.DvdSeasonNumber;
             }
 
-            if (replaceData || !target.DvdEpisodeNumber.HasValue)
+            if (replaceData || !targetItem.DvdEpisodeNumber.HasValue)
             {
-                target.DvdEpisodeNumber = source.DvdEpisodeNumber;
+                targetItem.DvdEpisodeNumber = sourceItem.DvdEpisodeNumber;
             }
 
-            if (replaceData || !target.AbsoluteEpisodeNumber.HasValue)
+            if (replaceData || !targetItem.AbsoluteEpisodeNumber.HasValue)
             {
-                target.AbsoluteEpisodeNumber = source.AbsoluteEpisodeNumber;
+                targetItem.AbsoluteEpisodeNumber = sourceItem.AbsoluteEpisodeNumber;
             }
 
-            if (replaceData || !target.IndexNumberEnd.HasValue)
+            if (replaceData || !targetItem.IndexNumberEnd.HasValue)
             {
-                target.IndexNumberEnd = source.IndexNumberEnd;
+                targetItem.IndexNumberEnd = sourceItem.IndexNumberEnd;
             }
         }
     }

+ 6 - 14
MediaBrowser.Providers/TV/SeasonMetadataService.cs

@@ -14,23 +14,10 @@ namespace MediaBrowser.Providers.TV
 {
     public class SeasonMetadataService : MetadataService<Season, SeasonInfo>
     {
-        public SeasonMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public SeasonMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(Season source, Season target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
-        {
-            ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
-        }
-
         protected override async Task<ItemUpdateType> BeforeSave(Season item, bool isFullRefresh, ItemUpdateType currentUpdateType)
         {
             var updateType = await base.BeforeSave(item, isFullRefresh, currentUpdateType).ConfigureAwait(false);
@@ -46,5 +33,10 @@ namespace MediaBrowser.Providers.TV
 
             return updateType;
         }
+
+        protected override void MergeData(MetadataResult<Season> source, MetadataResult<Season> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        {
+            ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+        }
     }
 }

+ 34 - 42
MediaBrowser.Providers/TV/SeriesMetadataService.cs

@@ -16,76 +16,68 @@ namespace MediaBrowser.Providers.TV
     public class SeriesMetadataService : MetadataService<Series, SeriesInfo>
     {
         private readonly ILocalizationManager _localization;
-        private readonly ILibraryManager _libraryManager;
 
-        public SeriesMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILocalizationManager localization, ILibraryManager libraryManager)
-            : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public SeriesMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager, ILocalizationManager localization) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
             _localization = localization;
-            _libraryManager = libraryManager;
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(Series source, Series target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override async Task AfterMetadataRefresh(Series item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
         {
-            ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+            await base.AfterMetadataRefresh(item, refreshOptions, cancellationToken).ConfigureAwait(false);
 
-            if (replaceData || target.SeasonCount == 0)
+            if (refreshOptions.IsPostRecursiveRefresh)
             {
-                target.SeasonCount = source.SeasonCount;
+                var provider = new DummySeasonProvider(ServerConfigurationManager, Logger, _localization, LibraryManager);
+
+                await provider.Run(item, CancellationToken.None).ConfigureAwait(false);
             }
+        }
 
-            if (replaceData || string.IsNullOrEmpty(target.AirTime))
+        protected override bool IsFullLocalMetadata(Series item)
+        {
+            if (string.IsNullOrWhiteSpace(item.Overview))
             {
-                target.AirTime = source.AirTime;
+                return false;
             }
-
-            if (replaceData || !target.Status.HasValue)
+            if (!item.ProductionYear.HasValue)
             {
-                target.Status = source.Status;
+                return false;
             }
+            return base.IsFullLocalMetadata(item);
+        }
+
+        protected override void MergeData(MetadataResult<Series> source, MetadataResult<Series> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        {
+            ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+
+            var sourceItem = source.Item;
+            var targetItem = target.Item;
 
-            if (replaceData || target.AirDays == null || target.AirDays.Count == 0)
+            if (replaceData || targetItem.SeasonCount == 0)
             {
-                target.AirDays = source.AirDays;
+                targetItem.SeasonCount = sourceItem.SeasonCount;
             }
 
-            if (mergeMetadataSettings)
+            if (replaceData || string.IsNullOrEmpty(targetItem.AirTime))
             {
-                target.DisplaySpecialsWithSeasons = source.DisplaySpecialsWithSeasons;
+                targetItem.AirTime = sourceItem.AirTime;
             }
-        }
-
-        protected override async Task AfterMetadataRefresh(Series item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
-        {
-            await base.AfterMetadataRefresh(item, refreshOptions, cancellationToken).ConfigureAwait(false);
 
-            if (refreshOptions.IsPostRecursiveRefresh)
+            if (replaceData || !targetItem.Status.HasValue)
             {
-                var provider = new DummySeasonProvider(ServerConfigurationManager, Logger, _localization, _libraryManager);
-
-                await provider.Run(item, CancellationToken.None).ConfigureAwait(false);
+                targetItem.Status = sourceItem.Status;
             }
-        }
 
-        protected override bool IsFullLocalMetadata(Series item)
-        {
-            if (string.IsNullOrWhiteSpace(item.Overview))
+            if (replaceData || targetItem.AirDays == null || targetItem.AirDays.Count == 0)
             {
-                return false;
+                targetItem.AirDays = sourceItem.AirDays;
             }
-            if (!item.ProductionYear.HasValue)
+
+            if (mergeMetadataSettings)
             {
-                return false;
+                targetItem.DisplaySpecialsWithSeasons = sourceItem.DisplaySpecialsWithSeasons;
             }
-            return base.IsFullLocalMetadata(item);
         }
     }
 }

+ 37 - 29
MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs

@@ -56,10 +56,12 @@ namespace MediaBrowser.Providers.TV
 
                 try
                 {
-                    var item = FetchEpisodeData(searchInfo, identity, seriesDataPath, searchInfo.SeriesProviderIds, cancellationToken);
+                    var metadataResult = FetchEpisodeData(searchInfo, identity, seriesDataPath, searchInfo.SeriesProviderIds, cancellationToken);
 
-                    if (item != null)
+                    if (metadataResult.HasMetadata)
                     {
+                        var item = metadataResult.Item;
+
                         list.Add(new RemoteSearchResult
                         {
                             IndexNumber = item.IndexNumber,
@@ -103,9 +105,7 @@ namespace MediaBrowser.Providers.TV
 
                 try
                 {
-                    result.Item = FetchEpisodeData(searchInfo, identity, seriesDataPath, searchInfo.SeriesProviderIds,
-                        cancellationToken);
-                    result.HasMetadata = result.Item != null;
+                    result = FetchEpisodeData(searchInfo, identity, seriesDataPath, searchInfo.SeriesProviderIds, cancellationToken);
                 }
                 catch (FileNotFoundException)
                 {
@@ -231,21 +231,23 @@ namespace MediaBrowser.Providers.TV
         /// <param name="seriesProviderIds">The series provider ids.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        private Episode FetchEpisodeData(EpisodeInfo id, EpisodeIdentity identity, string seriesDataPath, Dictionary<string, string> seriesProviderIds, CancellationToken cancellationToken)
+        private MetadataResult<Episode> FetchEpisodeData(EpisodeInfo id, EpisodeIdentity identity, string seriesDataPath, Dictionary<string, string> seriesProviderIds, CancellationToken cancellationToken)
         {
             var episodeNumber = identity.IndexNumber;
             var seasonOffset = TvdbSeriesProvider.GetSeriesOffset(seriesProviderIds) ?? 0;
             var seasonNumber = identity.SeasonIndex + seasonOffset;
             
             string file;
-            var success = false;
             var usingAbsoluteData = false;
 
-            var episode = new Episode
+            var result = new MetadataResult<Episode>()
             {
-                IndexNumber = id.IndexNumber,
-                ParentIndexNumber = id.ParentIndexNumber,
-                IndexNumberEnd = id.IndexNumberEnd
+                Item = new Episode
+                {
+                    IndexNumber = id.IndexNumber,
+                    ParentIndexNumber = id.ParentIndexNumber,
+                    IndexNumberEnd = id.IndexNumberEnd
+                }
             };
 
             try
@@ -253,9 +255,9 @@ namespace MediaBrowser.Providers.TV
                 if (seasonNumber != null)
                 {
                     file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber));
-                    FetchMainEpisodeInfo(episode, file, cancellationToken);
+                    FetchMainEpisodeInfo(result, file, cancellationToken);
 
-                    success = true;
+                    result.HasMetadata = true;
                 }
             }
             catch (FileNotFoundException)
@@ -267,11 +269,12 @@ namespace MediaBrowser.Providers.TV
                 }
             }
 
-            if (!success)
+            if (!result.HasMetadata)
             {
                 file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", episodeNumber));
 
-                FetchMainEpisodeInfo(episode, file, cancellationToken);
+                FetchMainEpisodeInfo(result, file, cancellationToken);
+                result.HasMetadata = true;
                 usingAbsoluteData = true;
             }
 
@@ -291,7 +294,7 @@ namespace MediaBrowser.Providers.TV
 
                 try
                 {
-                    FetchAdditionalPartInfo(episode, file, cancellationToken);
+                    FetchAdditionalPartInfo(result, file, cancellationToken);
                 }
                 catch (FileNotFoundException)
                 {
@@ -305,13 +308,15 @@ namespace MediaBrowser.Providers.TV
                 episodeNumber++;
             }
 
-            return episode;
+            return result;
         }
 
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 
-        private void FetchMainEpisodeInfo(Episode item, string xmlFile, CancellationToken cancellationToken)
+        private void FetchMainEpisodeInfo(MetadataResult<Episode> result, string xmlFile, CancellationToken cancellationToken)
         {
+            var item = result.Item;
+
             using (var streamReader = new StreamReader(xmlFile, Encoding.UTF8))
             {
                 // Use XmlReader for best performance
@@ -546,7 +551,7 @@ namespace MediaBrowser.Providers.TV
                                         {
                                             if (!item.LockedFields.Contains(MetadataFields.Cast))
                                             {
-                                                AddPeople(item, val, PersonType.Director);
+                                                AddPeople(result, val, PersonType.Director);
                                             }
                                         }
 
@@ -560,7 +565,7 @@ namespace MediaBrowser.Providers.TV
                                         {
                                             if (!item.LockedFields.Contains(MetadataFields.Cast))
                                             {
-                                                AddGuestStars(item, val);
+                                                AddGuestStars(result, val);
                                             }
                                         }
 
@@ -574,7 +579,7 @@ namespace MediaBrowser.Providers.TV
                                         {
                                             if (!item.LockedFields.Contains(MetadataFields.Cast))
                                             {
-                                                AddPeople(item, val, PersonType.Writer);
+                                                AddPeople(result, val, PersonType.Writer);
                                             }
                                         }
 
@@ -591,18 +596,19 @@ namespace MediaBrowser.Providers.TV
             }
         }
 
-        private void AddPeople(BaseItem item, string val, string personType)
+        private void AddPeople<T>(MetadataResult<T> result, string val, string personType)
         {
             // Sometimes tvdb actors have leading spaces
             foreach (var person in val.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries)
                                             .Where(i => !string.IsNullOrWhiteSpace(i))
                                             .Select(str => new PersonInfo { Type = personType, Name = str.Trim() }))
             {
-                PeopleHelper.AddPerson(item.People, person);
+                PeopleHelper.AddPerson(result.People, person);
             }
         }
 
-        private void AddGuestStars(BaseItem item, string val)
+        private void AddGuestStars<T>(MetadataResult<T> result, string val)
+            where T : BaseItem
         {
             // Sometimes tvdb actors have leading spaces
             //Regex Info:
@@ -626,13 +632,15 @@ namespace MediaBrowser.Providers.TV
             {
                 if (!string.IsNullOrWhiteSpace(person.Name))
                 {
-                    PeopleHelper.AddPerson(item.People, person);
+                    PeopleHelper.AddPerson(result.People, person);
                 }
             }
         }
 
-        private void FetchAdditionalPartInfo(Episode item, string xmlFile, CancellationToken cancellationToken)
+        private void FetchAdditionalPartInfo(MetadataResult<Episode> result, string xmlFile, CancellationToken cancellationToken)
         {
+            var item = result.Item;
+
             using (var streamReader = new StreamReader(xmlFile, Encoding.UTF8))
             {
                 // Use XmlReader for best performance
@@ -688,7 +696,7 @@ namespace MediaBrowser.Providers.TV
                                         {
                                             if (!item.LockedFields.Contains(MetadataFields.Cast))
                                             {
-                                                AddPeople(item, val, PersonType.Director);
+                                                AddPeople(result, val, PersonType.Director);
                                             }
                                         }
 
@@ -702,7 +710,7 @@ namespace MediaBrowser.Providers.TV
                                         {
                                             if (!item.LockedFields.Contains(MetadataFields.Cast))
                                             {
-                                                AddGuestStars(item, val);
+                                                AddGuestStars(result, val);
                                             }
                                         }
 
@@ -716,7 +724,7 @@ namespace MediaBrowser.Providers.TV
                                         {
                                             if (!item.LockedFields.Contains(MetadataFields.Cast))
                                             {
-                                                AddPeople(item, val, PersonType.Writer);
+                                                AddPeople(result, val, PersonType.Writer);
                                             }
                                         }
 

+ 12 - 10
MediaBrowser.Providers/TV/TvdbSeriesProvider.cs

@@ -121,7 +121,7 @@ namespace MediaBrowser.Providers.TV
                 result.Item = new Series();
                 result.HasMetadata = true;
 
-                FetchSeriesData(result.Item, seriesId, cancellationToken);
+                FetchSeriesData(result, seriesId, cancellationToken);
                 await FindAnimeSeriesIndex(result.Item, itemId).ConfigureAwait(false);
             }
 
@@ -159,12 +159,14 @@ namespace MediaBrowser.Providers.TV
         /// <summary>
         /// Fetches the series data.
         /// </summary>
-        /// <param name="series">The series.</param>
+        /// <param name="result">The result.</param>
         /// <param name="seriesId">The series id.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        private void FetchSeriesData(Series series, string seriesId, CancellationToken cancellationToken)
+        private void FetchSeriesData(MetadataResult<Series> result, string seriesId, CancellationToken cancellationToken)
         {
+            var series = result.Item;
+
             series.SetProviderId(MetadataProviders.Tvdb, seriesId);
 
             var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesId);
@@ -178,7 +180,7 @@ namespace MediaBrowser.Providers.TV
 
             cancellationToken.ThrowIfCancellationRequested();
 
-            FetchActors(series, actorsXmlPath);
+            FetchActors(result, actorsXmlPath);
         }
 
         /// <summary>
@@ -618,9 +620,9 @@ namespace MediaBrowser.Providers.TV
         /// <summary>
         /// Fetches the actors.
         /// </summary>
-        /// <param name="series">The series.</param>
+        /// <param name="result">The result.</param>
         /// <param name="actorsXmlPath">The actors XML path.</param>
-        private void FetchActors(Series series, string actorsXmlPath)
+        private void FetchActors(MetadataResult<Series> result, string actorsXmlPath)
         {
             var settings = new XmlReaderSettings
             {
@@ -648,7 +650,7 @@ namespace MediaBrowser.Providers.TV
                                     {
                                         using (var subtree = reader.ReadSubtree())
                                         {
-                                            FetchDataFromActorNode(series, subtree);
+                                            FetchDataFromActorNode(result, subtree);
                                         }
                                         break;
                                     }
@@ -665,9 +667,9 @@ namespace MediaBrowser.Providers.TV
         /// <summary>
         /// Fetches the data from actor node.
         /// </summary>
-        /// <param name="series">The series.</param>
+        /// <param name="result">The result.</param>
         /// <param name="reader">The reader.</param>
-        private void FetchDataFromActorNode(Series series, XmlReader reader)
+        private void FetchDataFromActorNode(MetadataResult<Series> result, XmlReader reader)
         {
             reader.MoveToContent();
 
@@ -719,7 +721,7 @@ namespace MediaBrowser.Providers.TV
 
             if (!string.IsNullOrWhiteSpace(personInfo.Name))
             {
-                PeopleHelper.AddPerson(series.People, personInfo);
+                PeopleHelper.AddPerson(result.People, personInfo);
             }
         }
 

+ 2 - 10
MediaBrowser.Providers/Users/UserMetadataService.cs

@@ -14,19 +14,11 @@ namespace MediaBrowser.Providers.Users
 {
     public class UserMetadataService : MetadataService<User, ItemLookupInfo>
     {
-        public UserMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public UserMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(User source, User target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<User> source, MetadataResult<User> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
         }

+ 6 - 14
MediaBrowser.Providers/Videos/VideoMetadataService.cs

@@ -12,23 +12,10 @@ namespace MediaBrowser.Providers.Videos
 {
     public class VideoMetadataService : MetadataService<Video, ItemLookupInfo>
     {
-        public VideoMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public VideoMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(Video source, Video target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
-        {
-            ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
-        }
-
         public override int Order
         {
             get
@@ -37,5 +24,10 @@ namespace MediaBrowser.Providers.Videos
                 return 10;
             }
         }
+
+        protected override void MergeData(MetadataResult<Video> source, MetadataResult<Video> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        {
+            ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+        }
     }
 }

+ 2 - 10
MediaBrowser.Providers/Years/YearMetadataService.cs

@@ -14,19 +14,11 @@ namespace MediaBrowser.Providers.Years
 {
     public class YearMetadataService : MetadataService<Year, ItemLookupInfo>
     {
-        public YearMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
+        public YearMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager, libraryManager)
         {
         }
 
-        /// <summary>
-        /// Merges the specified source.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        /// <param name="lockedFields">The locked fields.</param>
-        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
-        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
-        protected override void MergeData(Year source, Year target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        protected override void MergeData(MetadataResult<Year> source, MetadataResult<Year> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
         }

+ 9 - 4
MediaBrowser.Server.Implementations/Library/LibraryManager.cs

@@ -2060,7 +2060,7 @@ namespace MediaBrowser.Server.Implementations.Library
 
         public List<PersonInfo> GetPeople(BaseItem item)
         {
-            return item.People ?? new List<PersonInfo>();
+            return item.People ?? ItemRepository.GetPeople(item.Id);
         }
 
         public List<PersonInfo> GetAllPeople()
@@ -2072,10 +2072,15 @@ namespace MediaBrowser.Server.Implementations.Library
                 .ToList();
         }
 
-        public Task UpdatePeople(BaseItem item, List<PersonInfo> people)
+        public async Task UpdatePeople(BaseItem item, List<PersonInfo> people)
         {
-            item.People = people;
-            return item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+            await ItemRepository.UpdatePeople(item.Id, people).ConfigureAwait(false);
+
+            if (item.People != null)
+            {
+                item.People = null;
+                await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
+            }
         }
     }
 }

+ 153 - 0
MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs

@@ -65,6 +65,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
         private IDbCommand _saveChildrenCommand;
         private IDbCommand _deleteItemCommand;
 
+        private IDbCommand _deletePeopleCommand;
+        private IDbCommand _savePersonCommand;
         /// <summary>
         /// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
         /// </summary>
@@ -121,6 +123,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
                                 "create table if not exists ChildrenIds (ParentId GUID, ItemId GUID, PRIMARY KEY (ParentId, ItemId))",
                                 "create index if not exists idx_ChildrenIds on ChildrenIds(ParentId,ItemId)",
 
+                                "create table if not exists People (ItemId GUID, Name TEXT NOT NULL, Role TEXT, PersonType TEXT, SortOrder int, ListOrder int)",
+
                                 //pragmas
                                 "pragma temp_store = memory",
 
@@ -208,6 +212,19 @@ namespace MediaBrowser.Server.Implementations.Persistence
             _saveChildrenCommand.CommandText = "replace into ChildrenIds (ParentId, ItemId) values (@ParentId, @ItemId)";
             _saveChildrenCommand.Parameters.Add(_saveChildrenCommand, "@ParentId");
             _saveChildrenCommand.Parameters.Add(_saveChildrenCommand, "@ItemId");
+
+            _deletePeopleCommand = _connection.CreateCommand();
+            _deletePeopleCommand.CommandText = "delete from People where ItemId=@Id";
+            _deletePeopleCommand.Parameters.Add(_deletePeopleCommand, "@Id");
+
+            _savePersonCommand = _connection.CreateCommand();
+            _savePersonCommand.CommandText = "insert into People (ItemId, Name, Role, PersonType, SortOrder, ListOrder) values (@ItemId, @Name, @Role, @PersonType, @SortOrder, @ListOrder)";
+            _savePersonCommand.Parameters.Add(_savePersonCommand, "@ItemId");
+            _savePersonCommand.Parameters.Add(_savePersonCommand, "@Name");
+            _savePersonCommand.Parameters.Add(_savePersonCommand, "@Role");
+            _savePersonCommand.Parameters.Add(_savePersonCommand, "@PersonType");
+            _savePersonCommand.Parameters.Add(_savePersonCommand, "@SortOrder");
+            _savePersonCommand.Parameters.Add(_savePersonCommand, "@ListOrder");
         }
 
         /// <summary>
@@ -1091,5 +1108,141 @@ namespace MediaBrowser.Server.Implementations.Persistence
             CheckDisposed();
             return _mediaStreamsRepository.SaveMediaStreams(id, streams, cancellationToken);
         }
+
+
+        public List<PersonInfo> GetPeople(Guid itemId)
+        {
+            if (itemId == Guid.Empty)
+            {
+                throw new ArgumentNullException("itemId");
+            }
+
+            CheckDisposed();
+
+            using (var cmd = _connection.CreateCommand())
+            {
+                cmd.CommandText = "select ItemId, Name, Role, PersonType, SortOrder from People where ItemId=@ItemId order by ListOrder";
+
+                cmd.Parameters.Add(cmd, "@ItemId", DbType.Guid).Value = itemId;
+
+                var list = new List<PersonInfo>();
+
+                using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
+                {
+                    while (reader.Read())
+                    {
+                        list.Add(GetPerson(reader));
+                    }
+                }
+
+                return list;
+            }
+        }
+
+        public async Task UpdatePeople(Guid itemId, List<PersonInfo> people)
+        {
+            if (itemId == Guid.Empty)
+            {
+                throw new ArgumentNullException("itemId");
+            }
+
+            if (people == null)
+            {
+                throw new ArgumentNullException("people");
+            }
+
+            CheckDisposed();
+
+            var cancellationToken = CancellationToken.None;
+
+            await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+            IDbTransaction transaction = null;
+
+            try
+            {
+                transaction = _connection.BeginTransaction();
+
+                // First delete 
+                _deletePeopleCommand.GetParameter(0).Value = itemId;
+                _deletePeopleCommand.Transaction = transaction;
+
+                _deletePeopleCommand.ExecuteNonQuery();
+
+                var listIndex = 0;
+
+                foreach (var person in people)
+                {
+                    cancellationToken.ThrowIfCancellationRequested();
+
+                    _savePersonCommand.GetParameter(0).Value = itemId;
+                    _savePersonCommand.GetParameter(1).Value = person.Name;
+                    _savePersonCommand.GetParameter(2).Value = person.Role;
+                    _savePersonCommand.GetParameter(3).Value = person.Type;
+                    _savePersonCommand.GetParameter(4).Value = person.SortOrder;
+                    _savePersonCommand.GetParameter(5).Value = listIndex;
+
+                    _savePersonCommand.Transaction = transaction;
+
+                    _savePersonCommand.ExecuteNonQuery();
+                    listIndex++;
+                }
+
+                transaction.Commit();
+            }
+            catch (OperationCanceledException)
+            {
+                if (transaction != null)
+                {
+                    transaction.Rollback();
+                }
+
+                throw;
+            }
+            catch (Exception e)
+            {
+                _logger.ErrorException("Failed to save people:", e);
+
+                if (transaction != null)
+                {
+                    transaction.Rollback();
+                }
+
+                throw;
+            }
+            finally
+            {
+                if (transaction != null)
+                {
+                    transaction.Dispose();
+                }
+
+                _writeLock.Release();
+            }
+        }
+
+        private PersonInfo GetPerson(IDataReader reader)
+        {
+            var item = new PersonInfo();
+
+            item.Name = reader.GetString(1);
+
+            if (!reader.IsDBNull(2))
+            {
+                item.Role = reader.GetString(2);
+            }
+
+            if (!reader.IsDBNull(3))
+            {
+                item.Type = reader.GetString(3);
+            }
+
+            if (!reader.IsDBNull(4))
+            {
+                item.SortOrder = reader.GetInt32(4);
+            }
+
+            return item;
+        }
     }
 }

+ 21 - 20
MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs

@@ -1,4 +1,5 @@
 using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Model.Entities;
@@ -42,12 +43,11 @@ namespace MediaBrowser.XbmcMetadata.Parsers
         /// Fetches metadata for an item from one xml file
         /// </summary>
         /// <param name="item">The item.</param>
-        /// <param name="userDataList">The user data list.</param>
         /// <param name="metadataFile">The metadata file.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <exception cref="System.ArgumentNullException">
         /// </exception>
-        public void Fetch(T item, List<UserItemData> userDataList, string metadataFile, CancellationToken cancellationToken)
+        public void Fetch(LocalMetadataResult<T> item, string metadataFile, CancellationToken cancellationToken)
         {
             if (item == null)
             {
@@ -67,7 +67,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                 ValidationType = ValidationType.None
             };
 
-            Fetch(item, userDataList, metadataFile, settings, cancellationToken);
+            Fetch(item, metadataFile, settings, cancellationToken);
         }
 
         protected virtual bool SupportsUrlAfterClosingXmlTag
@@ -79,11 +79,10 @@ namespace MediaBrowser.XbmcMetadata.Parsers
         /// Fetches the specified item.
         /// </summary>
         /// <param name="item">The item.</param>
-        /// <param name="userDataList">The user data list.</param>
         /// <param name="metadataFile">The metadata file.</param>
         /// <param name="settings">The settings.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
-        private void Fetch(T item, List<UserItemData> userDataList, string metadataFile, XmlReaderSettings settings, CancellationToken cancellationToken)
+        private void Fetch(LocalMetadataResult<T> item, string metadataFile, XmlReaderSettings settings, CancellationToken cancellationToken)
         {
             if (!SupportsUrlAfterClosingXmlTag)
             {
@@ -101,7 +100,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
 
                             if (reader.NodeType == XmlNodeType.Element)
                             {
-                                FetchDataFromXmlNode(reader, item, userDataList);
+                                FetchDataFromXmlNode(reader, item);
                             }
                         }
                     }
@@ -122,7 +121,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                 {
                     var endingXml = xml.Substring(index);
 
-                    ParseProviderLinks(item, endingXml);
+                    ParseProviderLinks(item.Item, endingXml);
 
                     // If the file is just an imdb url, don't go any further
                     if (index == 0)
@@ -136,7 +135,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                 {
                     // If the file is just an Imdb url, handle that
 
-                    ParseProviderLinks(item, xml);
+                    ParseProviderLinks(item.Item, xml);
 
                     return;
                 }
@@ -160,7 +159,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
 
                             if (reader.NodeType == XmlNodeType.Element)
                             {
-                                FetchDataFromXmlNode(reader, item, userDataList);
+                                FetchDataFromXmlNode(reader, item);
                             }
                         }
                     }
@@ -183,8 +182,10 @@ namespace MediaBrowser.XbmcMetadata.Parsers
             // http://www.themoviedb.org/movie/36557
         }
 
-        protected virtual void FetchDataFromXmlNode(XmlReader reader, T item, List<UserItemData> userDataList)
+        protected virtual void FetchDataFromXmlNode(XmlReader reader, LocalMetadataResult<T> itemResult)
         {
+            var item = itemResult.Item;
+
             var userDataUserId = _config.GetNfoConfiguration().UserId;
 
             switch (reader.Name)
@@ -573,7 +574,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                             {
                                 continue;
                             }
-                            PeopleHelper.AddPerson(item.People, p);
+                            PeopleHelper.AddPerson(itemResult.People, p);
                         }
                         break;
                     }
@@ -592,7 +593,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                                 {
                                     continue;
                                 }
-                                PeopleHelper.AddPerson(item.People, p);
+                                PeopleHelper.AddPerson(itemResult.People, p);
                             }
                         }
                         break;
@@ -606,7 +607,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                             {
                                 continue;
                             }
-                            PeopleHelper.AddPerson(item.People, p);
+                            PeopleHelper.AddPerson(itemResult.People, p);
                         }
                         break;
                     }
@@ -617,7 +618,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                         {
                             var person = GetPersonFromXmlNode(subtree);
 
-                            PeopleHelper.AddPerson(item.People, person);
+                            PeopleHelper.AddPerson(itemResult.People, person);
                         }
                         break;
                     }
@@ -931,7 +932,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                             {
                                 if (!string.IsNullOrWhiteSpace(userDataUserId))
                                 {
-                                    var userData = GetOrAdd(userDataList, userDataUserId);
+                                    var userData = GetOrAdd(itemResult.UserDataLIst, userDataUserId);
 
                                     userData.Played = parsedValue;
                                 }
@@ -951,7 +952,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                             {
                                 if (!string.IsNullOrWhiteSpace(userDataUserId))
                                 {
-                                    var userData = GetOrAdd(userDataList, userDataUserId);
+                                    var userData = GetOrAdd(itemResult.UserDataLIst, userDataUserId);
 
                                     userData.PlayCount = parsedValue;
 
@@ -976,7 +977,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                             {
                                 if (!string.IsNullOrWhiteSpace(userDataUserId))
                                 {
-                                    var userData = GetOrAdd(userDataList, userDataUserId);
+                                    var userData = GetOrAdd(itemResult.UserDataLIst, userDataUserId);
 
                                     userData.LastPlayedDate = parsedValue;
                                 }
@@ -991,7 +992,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                         {
                             if (!string.IsNullOrWhiteSpace(userDataUserId))
                             {
-                                var userData = GetOrAdd(userDataList, userDataUserId);
+                                var userData = GetOrAdd(itemResult.UserDataLIst, userDataUserId);
 
                                 FetchFromResumeNode(subtree, item, userData);
                             }
@@ -1010,7 +1011,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                             {
                                 if (!string.IsNullOrWhiteSpace(userDataUserId))
                                 {
-                                    var userData = GetOrAdd(userDataList, userDataUserId);
+                                    var userData = GetOrAdd(itemResult.UserDataLIst, userDataUserId);
 
                                     userData.IsFavorite = parsedValue;
                                 }
@@ -1030,7 +1031,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                             {
                                 if (!string.IsNullOrWhiteSpace(userDataUserId))
                                 {
-                                    var userData = GetOrAdd(userDataList, userDataUserId);
+                                    var userData = GetOrAdd(itemResult.UserDataLIst, userDataUserId);
 
                                     userData.Rating = parsedValue;
                                 }

+ 7 - 22
MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs

@@ -2,7 +2,6 @@
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
 using System;
 using System.Collections.Generic;
@@ -14,26 +13,16 @@ namespace MediaBrowser.XbmcMetadata.Parsers
 {
     public class EpisodeNfoParser : BaseNfoParser<Episode>
     {
-        private List<LocalImageInfo> _imagesFound;
-        private List<ChapterInfo> _chaptersFound;
-        private string _xmlPath;
-
         public EpisodeNfoParser(ILogger logger, IConfigurationManager config) : base(logger, config)
         {
         }
 
-        public void Fetch(Episode item,
-            List<UserItemData> userDataList,
+        public void Fetch(LocalMetadataResult<Episode> item,
             List<LocalImageInfo> images,
-            List<ChapterInfo> chapters, 
             string metadataFile, 
             CancellationToken cancellationToken)
         {
-            _imagesFound = images;
-            _chaptersFound = chapters;
-            _xmlPath = metadataFile;
-
-            Fetch(item, userDataList, metadataFile, cancellationToken);
+            Fetch(item, metadataFile, cancellationToken);
         }
 
         private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
@@ -42,17 +31,13 @@ namespace MediaBrowser.XbmcMetadata.Parsers
         /// Fetches the data from XML node.
         /// </summary>
         /// <param name="reader">The reader.</param>
-        /// <param name="item">The item.</param>
-        /// <param name="userDataList">The user data list.</param>
-        protected override void FetchDataFromXmlNode(XmlReader reader, Episode item, List<UserItemData> userDataList)
+        /// <param name="itemResult">The item result.</param>
+        protected override void FetchDataFromXmlNode(XmlReader reader, LocalMetadataResult<Episode> itemResult)
         {
+            var item = itemResult.Item;
+
             switch (reader.Name)
             {
-                //case "Chapters":
-
-                //    _chaptersFound.AddRange(FetchChaptersFromXmlNode(item, reader.ReadSubtree()));
-                //    break;
-
                 case "season":
                     {
                         var number = reader.ReadElementContentAsString();
@@ -206,7 +191,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
 
 
                 default:
-                    base.FetchDataFromXmlNode(reader, item, userDataList);
+                    base.FetchDataFromXmlNode(reader, itemResult);
                     break;
             }
         }

+ 6 - 23
MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs

@@ -1,33 +1,20 @@
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
 using System.Collections.Generic;
-using System.Threading;
 using System.Xml;
 
 namespace MediaBrowser.XbmcMetadata.Parsers
 {
     class MovieNfoParser : BaseNfoParser<Video>
     {
-        private List<ChapterInfo> _chaptersFound;
-
         public MovieNfoParser(ILogger logger, IConfigurationManager config) : base(logger, config)
         {
         }
 
-        public void Fetch(Video item,
-            List<UserItemData> userDataList,
-            List<ChapterInfo> chapters, 
-            string metadataFile, 
-            CancellationToken cancellationToken)
-        {
-            _chaptersFound = chapters;
-
-            Fetch(item, userDataList, metadataFile, cancellationToken);
-        }
-
         protected override bool SupportsUrlAfterClosingXmlTag
         {
             get
@@ -40,10 +27,11 @@ namespace MediaBrowser.XbmcMetadata.Parsers
         /// Fetches the data from XML node.
         /// </summary>
         /// <param name="reader">The reader.</param>
-        /// <param name="item">The item.</param>
-        /// <param name="userDataList">The user data list.</param>
-        protected override void FetchDataFromXmlNode(XmlReader reader, Video item, List<UserItemData> userDataList)
+        /// <param name="itemResult">The item result.</param>
+        protected override void FetchDataFromXmlNode(XmlReader reader, LocalMetadataResult<Video> itemResult)
         {
+            var item = itemResult.Item;
+
             switch (reader.Name)
             {
                 case "id":
@@ -93,13 +81,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                         break;
                     }
 
-                //case "chapter":
-
-                //    _chaptersFound.AddRange(FetchChaptersFromXmlNode(item, reader.ReadSubtree()));
-                //    break;
-
                 default:
-                    base.FetchDataFromXmlNode(reader, item, userDataList);
+                    base.FetchDataFromXmlNode(reader, itemResult);
                     break;
             }
         }

+ 6 - 6
MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs

@@ -1,8 +1,7 @@
 using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Logging;
-using System.Collections.Generic;
 using System.Globalization;
 using System.Xml;
 
@@ -18,10 +17,11 @@ namespace MediaBrowser.XbmcMetadata.Parsers
         /// Fetches the data from XML node.
         /// </summary>
         /// <param name="reader">The reader.</param>
-        /// <param name="item">The item.</param>
-        /// <param name="userDataList">The user data list.</param>
-        protected override void FetchDataFromXmlNode(XmlReader reader, Season item, List<UserItemData> userDataList)
+        /// <param name="itemResult">The item result.</param>
+        protected override void FetchDataFromXmlNode(XmlReader reader, LocalMetadataResult<Season> itemResult)
         {
+            var item = itemResult.Item;
+
             switch (reader.Name)
             {
                 case "seasonnumber":
@@ -41,7 +41,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                     }
 
                 default:
-                    base.FetchDataFromXmlNode(reader, item, userDataList);
+                    base.FetchDataFromXmlNode(reader, itemResult);
                     break;
             }
         }

+ 6 - 4
MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs

@@ -2,6 +2,7 @@
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
 using System;
@@ -20,10 +21,11 @@ namespace MediaBrowser.XbmcMetadata.Parsers
         /// Fetches the data from XML node.
         /// </summary>
         /// <param name="reader">The reader.</param>
-        /// <param name="item">The item.</param>
-        /// <param name="userDataList">The user data list.</param>
-        protected override void FetchDataFromXmlNode(XmlReader reader, Series item, List<UserItemData> userDataList)
+        /// <param name="itemResult">The item result.</param>
+        protected override void FetchDataFromXmlNode(XmlReader reader, LocalMetadataResult<Series> itemResult)
         {
+            var item = itemResult.Item;
+
             switch (reader.Name)
             {
                 case "id":
@@ -88,7 +90,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                     }
 
                 default:
-                    base.FetchDataFromXmlNode(reader, item, userDataList);
+                    base.FetchDataFromXmlNode(reader, itemResult);
                     break;
             }
         }

+ 1 - 1
MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs

@@ -23,7 +23,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
 
         protected override void Fetch(LocalMetadataResult<MusicAlbum> result, string path, CancellationToken cancellationToken)
         {
-            new BaseNfoParser<MusicAlbum>(_logger, _config).Fetch(result.Item, result.UserDataLIst, path, cancellationToken);
+            new BaseNfoParser<MusicAlbum>(_logger, _config).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 1 - 1
MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs

@@ -23,7 +23,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
 
         protected override void Fetch(LocalMetadataResult<MusicArtist> result, string path, CancellationToken cancellationToken)
         {
-            new BaseNfoParser<MusicArtist>(_logger, _config).Fetch(result.Item, result.UserDataLIst, path, cancellationToken);
+            new BaseNfoParser<MusicArtist>(_logger, _config).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 8 - 6
MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs

@@ -2,11 +2,9 @@
 using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.XbmcMetadata.Parsers;
 using MediaBrowser.XbmcMetadata.Savers;
-using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Threading;
@@ -28,11 +26,15 @@ namespace MediaBrowser.XbmcMetadata.Providers
 
         protected override void Fetch(LocalMetadataResult<T> result, string path, CancellationToken cancellationToken)
         {
-            var chapters = new List<ChapterInfo>();
+            var tmpItem = new LocalMetadataResult<Video>
+            {
+                Item = result.Item
+            };
+            new MovieNfoParser(_logger, _config).Fetch(tmpItem, path, cancellationToken);
 
-            new MovieNfoParser(_logger, _config).Fetch(result.Item, result.UserDataLIst, chapters, path, cancellationToken);
-
-            result.Chapters = chapters;
+            result.Item = (T)tmpItem.Item;
+            result.People = tmpItem.People;
+            result.UserDataLIst = tmpItem.UserDataLIst;
         }
 
         protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 1 - 3
MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs

@@ -26,12 +26,10 @@ namespace MediaBrowser.XbmcMetadata.Providers
         protected override void Fetch(LocalMetadataResult<Episode> result, string path, CancellationToken cancellationToken)
         {
             var images = new List<LocalImageInfo>();
-            var chapters = new List<ChapterInfo>();
 
-            new EpisodeNfoParser(_logger, _config).Fetch(result.Item, result.UserDataLIst, images, chapters, path, cancellationToken);
+            new EpisodeNfoParser(_logger, _config).Fetch(result, images, path, cancellationToken);
 
             result.Images = images;
-            result.Chapters = chapters;
         }
 
         protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 1 - 1
MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs

@@ -23,7 +23,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
 
         protected override void Fetch(LocalMetadataResult<Season> result, string path, CancellationToken cancellationToken)
         {
-            new SeasonNfoParser(_logger, _config).Fetch(result.Item, result.UserDataLIst, path, cancellationToken);
+            new SeasonNfoParser(_logger, _config).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 1 - 1
MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs

@@ -23,7 +23,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
 
         protected override void Fetch(LocalMetadataResult<Series> result, string path, CancellationToken cancellationToken)
         {
-            new SeriesNfoParser(_logger, _config).Fetch(result.Item, result.UserDataLIst, path, cancellationToken);
+            new SeriesNfoParser(_logger, _config).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)