浏览代码

Merge pull request #2137 from MediaBrowser/dev

Dev
Luke 8 年之前
父节点
当前提交
c9ba91d7a5
共有 54 个文件被更改,包括 615 次插入582 次删除
  1. 3 2
      MediaBrowser.Api/ItemUpdateService.cs
  2. 2 0
      MediaBrowser.Controller/IServerApplicationHost.cs
  3. 34 123
      MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
  4. 0 2
      MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
  5. 2 2
      MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs
  6. 2 2
      MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs
  7. 2 2
      MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs
  8. 2 2
      MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs
  9. 5 5
      MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs
  10. 2 2
      MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs
  11. 2 2
      MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs
  12. 0 48
      MediaBrowser.LocalMetadata/Parsers/SeasonXmlParser.cs
  13. 2 2
      MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs
  14. 4 2
      MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs
  15. 4 2
      MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs
  16. 4 2
      MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs
  17. 4 2
      MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs
  18. 4 2
      MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs
  19. 4 2
      MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs
  20. 4 2
      MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs
  21. 4 2
      MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs
  22. 4 2
      MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
  23. 0 44
      MediaBrowser.LocalMetadata/Providers/SeasonXmlProvider.cs
  24. 4 2
      MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs
  25. 4 2
      MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs
  26. 9 112
      MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs
  27. 4 1
      MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
  28. 1 1
      MediaBrowser.Model/ApiClient/ServerCredentials.cs
  29. 5 10
      MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
  30. 29 36
      MediaBrowser.Server.Implementations/Udp/UdpServer.cs
  31. 5 0
      MediaBrowser.Server.Mono/Native/BaseMonoApp.cs
  32. 5 0
      MediaBrowser.Server.Startup.Common/ApplicationHost.cs
  33. 2 0
      MediaBrowser.Server.Startup.Common/INativeApp.cs
  34. 0 1
      MediaBrowser.ServerApplication/MainStartup.cs
  35. 1 0
      MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
  36. 358 0
      MediaBrowser.ServerApplication/Native/LoopbackUtil.cs
  37. 5 0
      MediaBrowser.ServerApplication/Native/WindowsApp.cs
  38. 22 130
      MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
  39. 1 1
      MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs
  40. 2 2
      MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
  41. 1 1
      MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs
  42. 1 1
      MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
  43. 4 2
      MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs
  44. 4 2
      MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs
  45. 4 2
      MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs
  46. 4 2
      MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs
  47. 4 3
      MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs
  48. 4 2
      MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs
  49. 4 2
      MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs
  50. 28 5
      MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
  51. 1 1
      MediaBrowser.sln
  52. 2 2
      Nuget/MediaBrowser.Common.Internal.nuspec
  53. 1 1
      Nuget/MediaBrowser.Common.nuspec
  54. 2 2
      Nuget/MediaBrowser.Server.Core.nuspec

+ 3 - 2
MediaBrowser.Api/ItemUpdateService.cs

@@ -70,12 +70,13 @@ namespace MediaBrowser.Api
                 Cultures = _localizationManager.GetCultures().ToList()
             };
 
-            if (!item.IsVirtualItem && !(item is ICollectionFolder) && !(item is UserView) && !(item is AggregateFolder) && !(item is LiveTvChannel) && !(item is IItemByName))
+            if (!item.IsVirtualItem && !(item is ICollectionFolder) && !(item is UserView) && !(item is AggregateFolder) && !(item is LiveTvChannel) && !(item is IItemByName) &&
+                item.SourceType == SourceType.Library)
             {
                 var inheritedContentType = _libraryManager.GetInheritedContentType(item);
                 var configuredContentType = _libraryManager.GetConfiguredContentType(item);
 
-                if (string.IsNullOrWhiteSpace(inheritedContentType) || string.Equals(inheritedContentType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase) || !string.IsNullOrWhiteSpace(configuredContentType))
+                if (string.IsNullOrWhiteSpace(inheritedContentType) || !string.IsNullOrWhiteSpace(configuredContentType))
                 {
                     info.ContentTypeOptions = GetContentTypeOptions(true);
                     info.ContentType = configuredContentType;

+ 2 - 0
MediaBrowser.Controller/IServerApplicationHost.cs

@@ -89,5 +89,7 @@ namespace MediaBrowser.Controller
         string GetLocalApiUrl(IPAddress ipAddress);
 
         void LaunchUrl(string url);
+
+        void EnableLoopback(string appName);
     }
 }

+ 34 - 123
MediaBrowser.Controller/Providers/BaseItemXmlParser.cs

@@ -23,14 +23,18 @@ namespace MediaBrowser.Controller.Providers
         /// The logger
         /// </summary>
         protected ILogger Logger { get; private set; }
+        protected IProviderManager ProviderManager { get; private set; }
+
+        private Dictionary<string, string> _validProviderIds;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="BaseItemXmlParser{T}" /> class.
         /// </summary>
         /// <param name="logger">The logger.</param>
-        public BaseItemXmlParser(ILogger logger)
+        public BaseItemXmlParser(ILogger logger, IProviderManager providerManager)
         {
             Logger = logger;
+            ProviderManager = providerManager;
         }
 
         /// <summary>
@@ -60,6 +64,22 @@ namespace MediaBrowser.Controller.Providers
                 ValidationType = ValidationType.None
             };
 
+            _validProviderIds = _validProviderIds = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
+
+            var idInfos = ProviderManager.GetExternalIdInfos(item.Item);
+
+            foreach (var info in idInfos)
+            {
+                var id = info.Key + "Id";
+                if (!_validProviderIds.ContainsKey(id))
+                {
+                    _validProviderIds.Add(id, info.Key);
+                }
+            }
+
+            //Additional Mappings
+            _validProviderIds.Add("IMDB", "Imdb");
+
             //Fetch(item, metadataFile, settings, Encoding.GetEncoding("ISO-8859-1"), cancellationToken);
             Fetch(item, metadataFile, settings, Encoding.UTF8, cancellationToken);
         }
@@ -657,14 +677,6 @@ namespace MediaBrowser.Controller.Providers
                         break;
                     }
 
-                case "TvDbId":
-                    var tvdbId = reader.ReadElementContentAsString();
-                    if (!string.IsNullOrWhiteSpace(tvdbId))
-                    {
-                        item.SetProviderId(MetadataProviders.Tvdb, tvdbId);
-                    }
-                    break;
-
                 case "VoteCount":
                     {
                         var val = reader.ReadElementContentAsString();
@@ -679,95 +691,6 @@ namespace MediaBrowser.Controller.Providers
                         }
                         break;
                     }
-                case "MusicBrainzAlbumId":
-                    {
-                        var mbz = reader.ReadElementContentAsString();
-                        if (!string.IsNullOrWhiteSpace(mbz))
-                        {
-                            item.SetProviderId(MetadataProviders.MusicBrainzAlbum, mbz);
-                        }
-                        break;
-                    }
-                case "MusicBrainzAlbumArtistId":
-                    {
-                        var mbz = reader.ReadElementContentAsString();
-                        if (!string.IsNullOrWhiteSpace(mbz))
-                        {
-                            item.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, mbz);
-                        }
-                        break;
-                    }
-                case "MusicBrainzArtistId":
-                    {
-                        var mbz = reader.ReadElementContentAsString();
-                        if (!string.IsNullOrWhiteSpace(mbz))
-                        {
-                            item.SetProviderId(MetadataProviders.MusicBrainzArtist, mbz);
-                        }
-                        break;
-                    }
-                case "MusicBrainzReleaseGroupId":
-                    {
-                        var mbz = reader.ReadElementContentAsString();
-                        if (!string.IsNullOrWhiteSpace(mbz))
-                        {
-                            item.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, mbz);
-                        }
-                        break;
-                    }
-                case "TVRageId":
-                    {
-                        var id = reader.ReadElementContentAsString();
-                        if (!string.IsNullOrWhiteSpace(id))
-                        {
-                            item.SetProviderId(MetadataProviders.TvRage, id);
-                        }
-                        break;
-                    }
-                case "TvMazeId":
-                    {
-                        var id = reader.ReadElementContentAsString();
-                        if (!string.IsNullOrWhiteSpace(id))
-                        {
-                            item.SetProviderId(MetadataProviders.TvMaze, id);
-                        }
-                        break;
-                    }
-                case "AudioDbArtistId":
-                    {
-                        var id = reader.ReadElementContentAsString();
-                        if (!string.IsNullOrWhiteSpace(id))
-                        {
-                            item.SetProviderId(MetadataProviders.AudioDbArtist, id);
-                        }
-                        break;
-                    }
-                case "AudioDbAlbumId":
-                    {
-                        var id = reader.ReadElementContentAsString();
-                        if (!string.IsNullOrWhiteSpace(id))
-                        {
-                            item.SetProviderId(MetadataProviders.AudioDbAlbum, id);
-                        }
-                        break;
-                    }
-                case "RottenTomatoesId":
-                    var rtId = reader.ReadElementContentAsString();
-                    if (!string.IsNullOrWhiteSpace(rtId))
-                    {
-                        item.SetProviderId(MetadataProviders.RottenTomatoes, rtId);
-                    }
-                    break;
-
-                case "TMDbId":
-                    var tmdb = reader.ReadElementContentAsString();
-                    if (!string.IsNullOrWhiteSpace(tmdb))
-                    {
-                        item.SetProviderId(MetadataProviders.Tmdb, tmdb);
-                    }
-                    break;
-
-                case "TMDbCollectionId":
                 case "CollectionNumber":
                     var tmdbCollection = reader.ReadElementContentAsString();
                     if (!string.IsNullOrWhiteSpace(tmdbCollection))
@@ -776,30 +699,6 @@ namespace MediaBrowser.Controller.Providers
                     }
                     break;
 
-                case "TVcomId":
-                    var TVcomId = reader.ReadElementContentAsString();
-                    if (!string.IsNullOrWhiteSpace(TVcomId))
-                    {
-                        item.SetProviderId(MetadataProviders.Tvcom, TVcomId);
-                    }
-                    break;
-
-                case "Zap2ItId":
-                    var zap2ItId = reader.ReadElementContentAsString();
-                    if (!string.IsNullOrWhiteSpace(zap2ItId))
-                    {
-                        item.SetProviderId(MetadataProviders.Zap2It, zap2ItId);
-                    }
-                    break;
-
-                case "IMDB":
-                    var imDbId = reader.ReadElementContentAsString();
-                    if (!string.IsNullOrWhiteSpace(imDbId))
-                    {
-                        item.SetProviderId(MetadataProviders.Imdb, imDbId);
-                    }
-                    break;
-
                 case "Genres":
                     {
                         using (var subtree = reader.ReadSubtree())
@@ -891,7 +790,19 @@ namespace MediaBrowser.Controller.Providers
                     }
 
                 default:
-                    reader.Skip();
+                    if (_validProviderIds.ContainsKey(reader.Name))
+                    {
+                        var id = reader.ReadElementContentAsString();
+                        if (!string.IsNullOrWhiteSpace(id))
+                        {
+                            item.SetProviderId(_validProviderIds[reader.Name], id);
+                        }
+                    }
+                    else
+                    {
+                        reader.Skip();
+                    }
+
                     break;
             }
         }

+ 0 - 2
MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj

@@ -63,7 +63,6 @@
     <Compile Include="Parsers\MovieXmlParser.cs" />
     <Compile Include="Parsers\MusicVideoXmlParser.cs" />
     <Compile Include="Parsers\PlaylistXmlParser.cs" />
-    <Compile Include="Parsers\SeasonXmlParser.cs" />
     <Compile Include="Parsers\SeriesXmlParser.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Providers\BoxSetXmlProvider.cs" />
@@ -75,7 +74,6 @@
     <Compile Include="Providers\MusicVideoXmlProvider.cs" />
     <Compile Include="Providers\PersonXmlProvider.cs" />
     <Compile Include="Providers\PlaylistXmlProvider.cs" />
-    <Compile Include="Providers\SeasonXmlProvider.cs" />
     <Compile Include="Providers\SeriesXmlProvider.cs" />
     <Compile Include="Providers\VideoXmlProvider.cs" />
     <Compile Include="Savers\BoxSetXmlSaver.cs" />

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

@@ -9,8 +9,8 @@ namespace MediaBrowser.LocalMetadata.Parsers
 {
     public class BoxSetXmlParser : BaseItemXmlParser<BoxSet>
     {
-        public BoxSetXmlParser(ILogger logger)
-            : base(logger)
+        public BoxSetXmlParser(ILogger logger, IProviderManager providerManager)
+            : base(logger, providerManager)
         {
         }
 

+ 2 - 2
MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs

@@ -20,8 +20,8 @@ namespace MediaBrowser.LocalMetadata.Parsers
         private List<LocalImageInfo> _imagesFound;
         private readonly IFileSystem _fileSystem;
 
-        public EpisodeXmlParser(ILogger logger, IFileSystem fileSystem)
-            : base(logger)
+        public EpisodeXmlParser(ILogger logger, IFileSystem fileSystem, IProviderManager providerManager)
+            : base(logger, providerManager)
         {
             _fileSystem = fileSystem;
         }

+ 2 - 2
MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs

@@ -10,8 +10,8 @@ namespace MediaBrowser.LocalMetadata.Parsers
 {
     public class GameSystemXmlParser : BaseItemXmlParser<GameSystem>
     {
-        public GameSystemXmlParser(ILogger logger)
-            : base(logger)
+        public GameSystemXmlParser(ILogger logger, IProviderManager providerManager)
+            : base(logger, providerManager)
         {
         }
 

+ 2 - 2
MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs

@@ -16,8 +16,8 @@ namespace MediaBrowser.LocalMetadata.Parsers
     {
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 
-        public GameXmlParser(ILogger logger)
-            : base(logger)
+        public GameXmlParser(ILogger logger, IProviderManager providerManager)
+            : base(logger, providerManager)
         {
         }
 

+ 5 - 5
MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs

@@ -12,8 +12,8 @@ namespace MediaBrowser.LocalMetadata.Parsers
     public class BaseVideoXmlParser<T> : BaseItemXmlParser<T>
         where T : Video
     {
-        public BaseVideoXmlParser(ILogger logger)
-            : base(logger)
+        public BaseVideoXmlParser(ILogger logger, IProviderManager providerManager)
+            : base(logger, providerManager)
         {
         }
 
@@ -50,15 +50,15 @@ namespace MediaBrowser.LocalMetadata.Parsers
 
     public class MovieXmlParser : BaseVideoXmlParser<Movie>
     {
-        public MovieXmlParser(ILogger logger) : base(logger)
+        public MovieXmlParser(ILogger logger, IProviderManager providerManager) : base(logger, providerManager)
         {
         }
     }
 
     public class VideoXmlParser : BaseVideoXmlParser<Video>
     {
-        public VideoXmlParser(ILogger logger)
-            : base(logger)
+        public VideoXmlParser(ILogger logger, IProviderManager providerManager)
+            : base(logger, providerManager)
         {
         }
     }

+ 2 - 2
MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs

@@ -12,8 +12,8 @@ namespace MediaBrowser.LocalMetadata.Parsers
         /// Initializes a new instance of the <see cref="BaseItemXmlParser{T}" /> class.
         /// </summary>
         /// <param name="logger">The logger.</param>
-        public MusicVideoXmlParser(ILogger logger)
-            : base(logger)
+        public MusicVideoXmlParser(ILogger logger, IProviderManager providerManager)
+            : base(logger, providerManager)
         {
         }
 

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

@@ -11,8 +11,8 @@ namespace MediaBrowser.LocalMetadata.Parsers
 {
     public class PlaylistXmlParser : BaseItemXmlParser<Playlist>
     {
-        public PlaylistXmlParser(ILogger logger)
-            : base(logger)
+        public PlaylistXmlParser(ILogger logger, IProviderManager providerManager)
+            : base(logger, providerManager)
         {
         }
 

+ 0 - 48
MediaBrowser.LocalMetadata/Parsers/SeasonXmlParser.cs

@@ -1,48 +0,0 @@
-using System.Xml;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.LocalMetadata.Parsers
-{
-    public class SeasonXmlParser : BaseItemXmlParser<Season>
-    {
-        public SeasonXmlParser(ILogger logger)
-            : base(logger)
-        {
-        }
-
-        /// <summary>
-        /// Fetches the data from XML node.
-        /// </summary>
-        /// <param name="reader">The reader.</param>
-        /// <param name="result">The result.</param>
-        protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult<Season> result)
-        {
-            var item = result.Item;
-
-            switch (reader.Name)
-            {
-                case "SeasonNumber":
-                    {
-                        var number = reader.ReadElementContentAsString();
-
-                        if (!string.IsNullOrWhiteSpace(number))
-                        {
-                            int num;
-
-                            if (int.TryParse(number, out num))
-                            {
-                                item.IndexNumber = num;
-                            }
-                        }
-                        break;
-                    }
-
-                default:
-                    base.FetchDataFromXmlNode(reader, result);
-                    break;
-            }
-        }
-    }
-}

+ 2 - 2
MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs

@@ -17,8 +17,8 @@ namespace MediaBrowser.LocalMetadata.Parsers
         /// Initializes a new instance of the <see cref="BaseItemXmlParser{T}" /> class.
         /// </summary>
         /// <param name="logger">The logger.</param>
-        public SeriesXmlParser(ILogger logger)
-            : base(logger)
+        public SeriesXmlParser(ILogger logger, IProviderManager providerManager)
+            : base(logger, providerManager)
         {
         }
 

+ 4 - 2
MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs

@@ -14,16 +14,18 @@ namespace MediaBrowser.LocalMetadata.Providers
     public class BoxSetXmlProvider : BaseXmlProvider<BoxSet>
     {
         private readonly ILogger _logger;
+        private readonly IProviderManager _providerManager;
 
-        public BoxSetXmlProvider(IFileSystem fileSystem, ILogger logger)
+        public BoxSetXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
             : base(fileSystem)
         {
             _logger = logger;
+            _providerManager = providerManager;
         }
 
         protected override void Fetch(MetadataResult<BoxSet> result, string path, CancellationToken cancellationToken)
         {
-            new BoxSetXmlParser(_logger).Fetch(result, path, cancellationToken);
+            new BoxSetXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)

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

@@ -13,11 +13,13 @@ namespace MediaBrowser.LocalMetadata.Providers
     public class EpisodeXmlProvider : BaseXmlProvider<Episode>
     {
         private readonly ILogger _logger;
+        private readonly IProviderManager _providerManager;
 
-        public EpisodeXmlProvider(IFileSystem fileSystem, ILogger logger)
+        public EpisodeXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
             : base(fileSystem)
         {
             _logger = logger;
+            _providerManager = providerManager;
         }
 
         protected override void Fetch(MetadataResult<Episode> result, string path, CancellationToken cancellationToken)
@@ -25,7 +27,7 @@ namespace MediaBrowser.LocalMetadata.Providers
             var images = new List<LocalImageInfo>();
             var chapters = new List<ChapterInfo>();
 
-            new EpisodeXmlParser(_logger, FileSystem).Fetch(result, images, path, cancellationToken);
+            new EpisodeXmlParser(_logger, FileSystem, _providerManager).Fetch(result, images, path, cancellationToken);
 
             result.Images = images;
         }

+ 4 - 2
MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs

@@ -13,16 +13,18 @@ namespace MediaBrowser.LocalMetadata.Providers
     public class FolderXmlProvider : BaseXmlProvider<Folder>
     {
         private readonly ILogger _logger;
+        private readonly IProviderManager _providerManager;
 
-        public FolderXmlProvider(IFileSystem fileSystem, ILogger logger)
+        public FolderXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
             : base(fileSystem)
         {
             _logger = logger;
+            _providerManager = providerManager;
         }
 
         protected override void Fetch(MetadataResult<Folder> result, string path, CancellationToken cancellationToken)
         {
-            new BaseItemXmlParser<Folder>(_logger).Fetch(result, path, cancellationToken);
+            new BaseItemXmlParser<Folder>(_logger, _providerManager).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 4 - 2
MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs

@@ -11,16 +11,18 @@ namespace MediaBrowser.LocalMetadata.Providers
     public class GameSystemXmlProvider : BaseXmlProvider<GameSystem>
     {
         private readonly ILogger _logger;
+        private readonly IProviderManager _providerManager;
 
-        public GameSystemXmlProvider(IFileSystem fileSystem, ILogger logger)
+        public GameSystemXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
             : base(fileSystem)
         {
             _logger = logger;
+            _providerManager = providerManager;
         }
 
         protected override void Fetch(MetadataResult<GameSystem> result, string path, CancellationToken cancellationToken)
         {
-            new GameSystemXmlParser(_logger).Fetch(result, path, cancellationToken);
+            new GameSystemXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 4 - 2
MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs

@@ -11,16 +11,18 @@ namespace MediaBrowser.LocalMetadata.Providers
     public class GameXmlProvider : BaseXmlProvider<Game>
     {
         private readonly ILogger _logger;
+        private readonly IProviderManager _providerManager;
 
-        public GameXmlProvider(IFileSystem fileSystem, ILogger logger)
+        public GameXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
             : base(fileSystem)
         {
             _logger = logger;
+            _providerManager = providerManager;
         }
 
         protected override void Fetch(MetadataResult<Game> result, string path, CancellationToken cancellationToken)
         {
-            new GameXmlParser(_logger).Fetch(result, path, cancellationToken);
+            new GameXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 4 - 2
MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs

@@ -11,16 +11,18 @@ namespace MediaBrowser.LocalMetadata.Providers
     public class MovieXmlProvider : BaseXmlProvider<Movie>
     {
         private readonly ILogger _logger;
+        private readonly IProviderManager _providerManager;
 
-        public MovieXmlProvider(IFileSystem fileSystem, ILogger logger)
+        public MovieXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
             : base(fileSystem)
         {
             _logger = logger;
+            _providerManager = providerManager;
         }
 
         protected override void Fetch(MetadataResult<Movie> result, string path, CancellationToken cancellationToken)
         {
-            new MovieXmlParser(_logger).Fetch(result, path, cancellationToken);
+            new MovieXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 4 - 2
MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs

@@ -10,16 +10,18 @@ namespace MediaBrowser.LocalMetadata.Providers
     class MusicVideoXmlProvider : BaseXmlProvider<MusicVideo>
     {
         private readonly ILogger _logger;
+        private readonly IProviderManager _providerManager;
 
-        public MusicVideoXmlProvider(IFileSystem fileSystem, ILogger logger)
+        public MusicVideoXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
             : base(fileSystem)
         {
             _logger = logger;
+            _providerManager = providerManager;
         }
 
         protected override void Fetch(MetadataResult<MusicVideo> result, string path, CancellationToken cancellationToken)
         {
-            new MusicVideoXmlParser(_logger).Fetch(result, path, cancellationToken);
+            new MusicVideoXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 4 - 2
MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs

@@ -10,16 +10,18 @@ namespace MediaBrowser.LocalMetadata.Providers
     public class PersonXmlProvider : BaseXmlProvider<Person>
     {
         private readonly ILogger _logger;
+        private readonly IProviderManager _providerManager;
 
-        public PersonXmlProvider(IFileSystem fileSystem, ILogger logger)
+        public PersonXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
             : base(fileSystem)
         {
             _logger = logger;
+            _providerManager = providerManager;
         }
 
         protected override void Fetch(MetadataResult<Person> result, string path, CancellationToken cancellationToken)
         {
-            new BaseItemXmlParser<Person>(_logger).Fetch(result, path, cancellationToken);
+            new BaseItemXmlParser<Person>(_logger, _providerManager).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 4 - 2
MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs

@@ -11,16 +11,18 @@ namespace MediaBrowser.LocalMetadata.Providers
     class PlaylistXmlProvider : BaseXmlProvider<Playlist>
     {
         private readonly ILogger _logger;
+        private readonly IProviderManager _providerManager;
 
-        public PlaylistXmlProvider(IFileSystem fileSystem, ILogger logger)
+        public PlaylistXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
             : base(fileSystem)
         {
             _logger = logger;
+            _providerManager = providerManager;
         }
 
         protected override void Fetch(MetadataResult<Playlist> result, string path, CancellationToken cancellationToken)
         {
-            new PlaylistXmlParser(_logger).Fetch(result, path, cancellationToken);
+            new PlaylistXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 0 - 44
MediaBrowser.LocalMetadata/Providers/SeasonXmlProvider.cs

@@ -1,44 +0,0 @@
-using System.IO;
-using System.Threading;
-using CommonIO;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.LocalMetadata.Parsers;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.LocalMetadata.Providers
-{
-    /// <summary>
-    /// Class SeriesProviderFromXml
-    /// </summary>
-    public class SeasonXmlProvider : BaseXmlProvider<Season>, IHasOrder
-    {
-        private readonly ILogger _logger;
-
-        public SeasonXmlProvider(IFileSystem fileSystem, ILogger logger)
-            : base(fileSystem)
-        {
-            _logger = logger;
-        }
-
-        protected override void Fetch(MetadataResult<Season> result, string path, CancellationToken cancellationToken)
-        {
-            new SeasonXmlParser(_logger).Fetch(result, path, cancellationToken);
-        }
-
-        protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
-        {
-            return directoryService.GetFile(Path.Combine(info.Path, "season.xml"));
-        }
-
-        public int Order
-        {
-            get
-            {
-                // After Xbmc
-                return 1;
-            }
-        }
-    }
-}
-

+ 4 - 2
MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs

@@ -14,16 +14,18 @@ namespace MediaBrowser.LocalMetadata.Providers
     public class SeriesXmlProvider : BaseXmlProvider<Series>, IHasOrder
     {
         private readonly ILogger _logger;
+        private readonly IProviderManager _providerManager;
 
-        public SeriesXmlProvider(IFileSystem fileSystem, ILogger logger)
+        public SeriesXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
             : base(fileSystem)
         {
             _logger = logger;
+            _providerManager = providerManager;
         }
 
         protected override void Fetch(MetadataResult<Series> result, string path, CancellationToken cancellationToken)
         {
-            new SeriesXmlParser(_logger).Fetch(result, path, cancellationToken);
+            new SeriesXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)

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

@@ -10,16 +10,18 @@ namespace MediaBrowser.LocalMetadata.Providers
     class VideoXmlProvider : BaseXmlProvider<Video>
     {
         private readonly ILogger _logger;
+        private readonly IProviderManager _providerManager;
 
-        public VideoXmlProvider(IFileSystem fileSystem, ILogger logger)
+        public VideoXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
             : base(fileSystem)
         {
             _logger = logger;
+            _providerManager = providerManager;
         }
 
         protected override void Fetch(MetadataResult<Video> result, string path, CancellationToken cancellationToken)
         {
-            new VideoXmlParser(_logger).Fetch(result, path, cancellationToken);
+            new VideoXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 9 - 112
MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs

@@ -133,7 +133,7 @@ namespace MediaBrowser.LocalMetadata.Savers
         /// <param name="xmlTagsUsed">The XML tags used.</param>
         public static void Save(StringBuilder xml, string path, List<string> xmlTagsUsed, IServerConfigurationManager config, IFileSystem fileSystem)
         {
-			if (fileSystem.FileExists(path))
+            if (fileSystem.FileExists(path))
             {
                 var position = xml.ToString().LastIndexOf("</", StringComparison.OrdinalIgnoreCase);
                 xml.Insert(position, GetCustomTags(path, xmlTagsUsed));
@@ -145,7 +145,7 @@ namespace MediaBrowser.LocalMetadata.Savers
             //Add the new node to the document.
             xmlDocument.InsertBefore(xmlDocument.CreateXmlDeclaration("1.0", "UTF-8", "yes"), xmlDocument.DocumentElement);
 
-			fileSystem.CreateDirectory(Path.GetDirectoryName(path));
+            fileSystem.CreateDirectory(Path.GetDirectoryName(path));
 
             var wasHidden = false;
 
@@ -445,121 +445,18 @@ namespace MediaBrowser.LocalMetadata.Savers
                 builder.Append("<RunningTime>" + Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture) + "</RunningTime>");
             }
 
-            var imdb = item.GetProviderId(MetadataProviders.Imdb);
-
-            if (!string.IsNullOrEmpty(imdb))
-            {
-                builder.Append("<IMDB>" + SecurityElement.Escape(imdb) + "</IMDB>");
-            }
-
-            var tmdb = item.GetProviderId(MetadataProviders.Tmdb);
-
-            if (!string.IsNullOrEmpty(tmdb))
-            {
-                builder.Append("<TMDbId>" + SecurityElement.Escape(tmdb) + "</TMDbId>");
-            }
-
-            if (!(item is Series))
+            if (item.ProviderIds != null)
             {
-                var tvdb = item.GetProviderId(MetadataProviders.Tvdb);
-
-                if (!string.IsNullOrEmpty(tvdb))
+                foreach (var providerKey in item.ProviderIds.Keys)
                 {
-                    builder.Append("<TvDbId>" + SecurityElement.Escape(tvdb) + "</TvDbId>");
+                    var providerId = item.ProviderIds[providerKey];
+                    if (!string.IsNullOrEmpty(providerId))
+                    {
+                        builder.Append(string.Format("<{0}>{1}</{0}>", providerKey + "Id", SecurityElement.Escape(providerId)));
+                    }
                 }
             }
 
-            var externalId = item.GetProviderId(MetadataProviders.Tvcom);
-
-            if (!string.IsNullOrEmpty(externalId))
-            {
-                builder.Append("<TVcomId>" + SecurityElement.Escape(externalId) + "</TVcomId>");
-            }
-
-            externalId = item.GetProviderId(MetadataProviders.RottenTomatoes);
-
-            if (!string.IsNullOrEmpty(externalId))
-            {
-                builder.Append("<RottenTomatoesId>" + SecurityElement.Escape(externalId) + "</RottenTomatoesId>");
-            }
-
-            externalId = item.GetProviderId(MetadataProviders.Zap2It);
-
-            if (!string.IsNullOrEmpty(externalId))
-            {
-                builder.Append("<Zap2ItId>" + SecurityElement.Escape(externalId) + "</Zap2ItId>");
-            }
-
-            externalId = item.GetProviderId(MetadataProviders.MusicBrainzAlbum);
-
-            if (!string.IsNullOrEmpty(externalId))
-            {
-                builder.Append("<MusicBrainzAlbumId>" + SecurityElement.Escape(externalId) + "</MusicBrainzAlbumId>");
-            }
-
-            externalId = item.GetProviderId(MetadataProviders.MusicBrainzAlbumArtist);
-
-            if (!string.IsNullOrEmpty(externalId))
-            {
-                builder.Append("<MusicBrainzAlbumArtistId>" + SecurityElement.Escape(externalId) + "</MusicBrainzAlbumArtistId>");
-            }
-
-            externalId = item.GetProviderId(MetadataProviders.MusicBrainzArtist);
-
-            if (!string.IsNullOrEmpty(externalId))
-            {
-                builder.Append("<MusicBrainzArtistId>" + SecurityElement.Escape(externalId) + "</MusicBrainzArtistId>");
-            }
-
-            externalId = item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
-
-            if (!string.IsNullOrEmpty(externalId))
-            {
-                builder.Append("<MusicBrainzReleaseGroupId>" + SecurityElement.Escape(externalId) + "</MusicBrainzReleaseGroupId>");
-            }
-
-            externalId = item.GetProviderId(MetadataProviders.Gamesdb);
-
-            if (!string.IsNullOrEmpty(externalId))
-            {
-                builder.Append("<GamesDbId>" + SecurityElement.Escape(externalId) + "</GamesDbId>");
-            }
-
-            externalId = item.GetProviderId(MetadataProviders.TmdbCollection);
-
-            if (!string.IsNullOrEmpty(externalId))
-            {
-                builder.Append("<TMDbCollectionId>" + SecurityElement.Escape(externalId) + "</TMDbCollectionId>");
-            }
-
-            externalId = item.GetProviderId(MetadataProviders.AudioDbArtist);
-
-            if (!string.IsNullOrEmpty(externalId))
-            {
-                builder.Append("<AudioDbArtistId>" + SecurityElement.Escape(externalId) + "</AudioDbArtistId>");
-            }
-
-            externalId = item.GetProviderId(MetadataProviders.AudioDbAlbum);
-
-            if (!string.IsNullOrEmpty(externalId))
-            {
-                builder.Append("<AudioDbAlbumId>" + SecurityElement.Escape(externalId) + "</AudioDbAlbumId>");
-            }
-
-            externalId = item.GetProviderId(MetadataProviders.TvRage);
-
-            if (!string.IsNullOrEmpty(externalId))
-            {
-                builder.Append("<TVRageId>" + SecurityElement.Escape(externalId) + "</TVRageId>");
-            }
-
-            externalId = item.GetProviderId(MetadataProviders.TvMaze);
-
-            if (!string.IsNullOrEmpty(externalId))
-            {
-                builder.Append("<TvMazeId>" + SecurityElement.Escape(externalId) + "</TvMazeId>");
-            }
-
             var hasTagline = item as IHasTaglines;
             if (hasTagline != null)
             {

+ 4 - 1
MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs

@@ -818,14 +818,17 @@ namespace MediaBrowser.MediaEncoding.Subtitles
 
         public string GetSubtitleFileCharacterSetFromLanguage(string language)
         {
+            // https://developer.xamarin.com/api/type/System.Text.Encoding/
+
             switch (language.ToLower())
             {
+                case "hun":
+                    return "windows-1252";
                 case "pol":
                 case "cze":
                 case "ces":
                 case "slo":
                 case "slk":
-                case "hun":
                 case "slv":
                 case "srp":
                 case "hrv":

+ 1 - 1
MediaBrowser.Model/ApiClient/ServerCredentials.cs

@@ -67,7 +67,7 @@ namespace MediaBrowser.Model.ApiClient
                 }
                 if (!string.IsNullOrEmpty(server.ManualAddress))
                 {
-                    existing.LocalAddress = server.ManualAddress;
+                    existing.ManualAddress = server.ManualAddress;
                 }
                 if (!string.IsNullOrEmpty(server.Name))
                 {

+ 5 - 10
MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs

@@ -54,14 +54,14 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
         {
             if (args.IsDirectory)
             {
+                if (args.HasParent<Series>())
+                {
+                    return null;
+                }
+
                 var collectionType = args.GetCollectionType();
                 if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
                 {
-                    if (args.HasParent<Series>())
-                    {
-                        return null;
-                    }
-
                     var configuredContentType = _libraryManager.GetConfiguredContentType(args.Path);
                     if (!string.Equals(configuredContentType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
                     {
@@ -76,11 +76,6 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
                 {
                     if (string.IsNullOrWhiteSpace(collectionType))
                     {
-                        if (args.HasParent<Series>())
-                        {
-                            return null;
-                        }
-
                         if (args.Parent.IsRoot)
                         {
                             return null;

+ 29 - 36
MediaBrowser.Server.Implementations/Udp/UdpServer.cs

@@ -30,7 +30,7 @@ namespace MediaBrowser.Server.Implementations.Udp
 
         private bool _isDisposed;
 
-        private readonly List<Tuple<string, byte[], Action<string, Encoding>>> _responders = new List<Tuple<string, byte[], Action<string, Encoding>>>();
+        private readonly List<Tuple<string, bool, Func<string, string, Encoding, Task>>> _responders = new List<Tuple<string, bool, Func<string, string, Encoding, Task>>>();
 
         private readonly IServerApplicationHost _appHost;
         private readonly IJsonSerializer _json;
@@ -49,43 +49,35 @@ namespace MediaBrowser.Server.Implementations.Udp
             _appHost = appHost;
             _json = json;
 
-            AddMessageResponder("who is EmbyServer?", RespondToV2Message);
-            AddMessageResponder("who is MediaBrowserServer_v2?", RespondToV2Message);
-            AddMessageResponder("who is MediaBrowserServer?", RespondToV1Message);
+            AddMessageResponder("who is EmbyServer?", true, RespondToV2Message);
+            AddMessageResponder("who is MediaBrowserServer_v2?", false, RespondToV2Message);
         }
 
-        private void AddMessageResponder(string message, Action<string, Encoding> responder)
+        private void AddMessageResponder(string message, bool isSubstring, Func<string, string, Encoding, Task> responder)
         {
-            var expectedMessageBytes = Encoding.UTF8.GetBytes(message);
-
-            _responders.Add(new Tuple<string, byte[], Action<string, Encoding>>(message, expectedMessageBytes, responder));
+            _responders.Add(new Tuple<string, bool, Func<string, string, Encoding, Task>>(message, isSubstring, responder));
         }
 
         /// <summary>
         /// Raises the <see cref="E:MessageReceived" /> event.
         /// </summary>
         /// <param name="e">The <see cref="UdpMessageReceivedEventArgs"/> instance containing the event data.</param>
-        private void OnMessageReceived(UdpMessageReceivedEventArgs e)
+        private async void OnMessageReceived(UdpMessageReceivedEventArgs e)
         {
-            var responder = _responders.FirstOrDefault(i => i.Item2.SequenceEqual(e.Bytes));
             var encoding = Encoding.UTF8;
+            var responder = GetResponder(e.Bytes, encoding);
 
             if (responder == null)
             {
-                var text = Encoding.Unicode.GetString(e.Bytes);
-                responder = _responders.FirstOrDefault(i => string.Equals(i.Item1, text, StringComparison.OrdinalIgnoreCase));
-
-                if (responder != null)
-                {
-                    encoding = Encoding.Unicode;
-                }
+                encoding = Encoding.Unicode;
+                responder = GetResponder(e.Bytes, encoding);
             }
 
             if (responder != null)
             {
                 try
                 {
-                    responder.Item3(e.RemoteEndPoint, encoding);
+                    await responder.Item2.Item3(responder.Item1, e.RemoteEndPoint, encoding).ConfigureAwait(false);
                 }
                 catch (Exception ex)
                 {
@@ -94,33 +86,34 @@ namespace MediaBrowser.Server.Implementations.Udp
             }
         }
 
-        private async void RespondToV1Message(string endpoint, Encoding encoding)
+        private Tuple<string, Tuple<string, bool, Func<string, string, Encoding, Task>>> GetResponder(byte[] bytes, Encoding encoding)
         {
-            var localUrl = await _appHost.GetLocalApiUrl().ConfigureAwait(false);
-
-            if (!string.IsNullOrEmpty(localUrl))
+            var text = encoding.GetString(bytes);
+            var responder = _responders.FirstOrDefault(i =>
             {
-                // This is how we did the old v1 search, so need to strip off the protocol
-                var index = localUrl.IndexOf("://", StringComparison.OrdinalIgnoreCase);
-
-                if (index != -1)
+                if (i.Item2)
                 {
-                    localUrl = localUrl.Substring(index + 3);
+                    return text.IndexOf(i.Item1, StringComparison.OrdinalIgnoreCase) != -1;
                 }
+                return string.Equals(i.Item1, text, StringComparison.OrdinalIgnoreCase);
+            });
 
-                // Send a response back with our ip address and port
-                var response = String.Format("MediaBrowserServer|{0}", localUrl);
-
-                await SendAsync(Encoding.UTF8.GetBytes(response), endpoint);
-            }
-            else
+            if (responder == null)
             {
-                _logger.Warn("Unable to respond to udp request because the local ip address could not be determined.");
+                return null;
             }
+            return new Tuple<string, Tuple<string, bool, Func<string, string, Encoding, Task>>>(text, responder);
         }
 
-        private async void RespondToV2Message(string endpoint, Encoding encoding)
+        private async Task RespondToV2Message(string messageText, string endpoint, Encoding encoding)
         {
+            var parts = messageText.Split('|');
+
+            if (parts.Length > 1)
+            {
+                _appHost.EnableLoopback(parts[1]);
+            }
+
             var localUrl = await _appHost.GetLocalApiUrl().ConfigureAwait(false);
 
             if (!string.IsNullOrEmpty(localUrl))
@@ -132,7 +125,7 @@ namespace MediaBrowser.Server.Implementations.Udp
                     Name = _appHost.FriendlyName
                 };
 
-                await SendAsync(encoding.GetBytes(_json.SerializeToString(response)), endpoint);
+                await SendAsync(encoding.GetBytes(_json.SerializeToString(response)), endpoint).ConfigureAwait(false);
             }
             else
             {

+ 5 - 0
MediaBrowser.Server.Mono/Native/BaseMonoApp.cs

@@ -278,6 +278,11 @@ namespace MediaBrowser.Server.Mono.Native
 
             return info;
         }
+
+        public void EnableLoopback(string appName)
+        {
+
+        }
     }
 
     public class NullPowerManagement : IPowerManagement

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

@@ -1392,5 +1392,10 @@ namespace MediaBrowser.Server.Startup.Common
         {
             NativeApp.LaunchUrl(url);
         }
+
+        public void EnableLoopback(string appName)
+        {
+            NativeApp.EnableLoopback(appName);
+        }
     }
 }

+ 2 - 0
MediaBrowser.Server.Startup.Common/INativeApp.cs

@@ -107,5 +107,7 @@ namespace MediaBrowser.Server.Startup.Common
         void LaunchUrl(string url);
 
         IDbConnector GetDbConnector();
+
+        void EnableLoopback(string appName);
     }
 }

+ 0 - 1
MediaBrowser.ServerApplication/MainStartup.cs

@@ -292,7 +292,6 @@ namespace MediaBrowser.ServerApplication
                              ErrorModes.SEM_NOGPFAULTERRORBOX | ErrorModes.SEM_NOOPENFILEERRORBOX);
             }
 
-
             var task = _appHost.Init(initProgress);
             Task.WaitAll(task);
 

+ 1 - 0
MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj

@@ -119,6 +119,7 @@
     <Compile Include="MainStartup.cs" />
     <Compile Include="Native\LnkShortcutHandler.cs" />
     <Compile Include="Native\DbConnector.cs" />
+    <Compile Include="Native\LoopbackUtil.cs" />
     <Compile Include="Native\Standby.cs" />
     <Compile Include="Native\ServerAuthorization.cs" />
     <Compile Include="Native\WindowsApp.cs" />

+ 358 - 0
MediaBrowser.ServerApplication/Native/LoopbackUtil.cs

@@ -0,0 +1,358 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.ServerApplication.Native
+{
+    /// <summary>
+    /// http://blogs.msdn.com/b/fiddler/archive/2011/12/10/fiddler-windows-8-apps-enable-LoopUtil-network-isolation-exemption.aspx
+    /// </summary>
+    public class LoopUtil
+    {
+        //http://msdn.microsoft.com/en-us/library/windows/desktop/aa379595(v=vs.85).aspx
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct SID_AND_ATTRIBUTES
+        {
+            public IntPtr Sid;
+            public uint Attributes;
+        }
+
+        [StructLayoutAttribute(LayoutKind.Sequential)]
+        internal struct INET_FIREWALL_AC_CAPABILITIES
+        {
+            public uint count;
+            public IntPtr capabilities; //SID_AND_ATTRIBUTES
+        }
+
+        [StructLayoutAttribute(LayoutKind.Sequential)]
+        internal struct INET_FIREWALL_AC_BINARIES
+        {
+            public uint count;
+            public IntPtr binaries;
+        }
+
+        [StructLayoutAttribute(LayoutKind.Sequential)]
+        internal struct INET_FIREWALL_APP_CONTAINER
+        {
+            internal IntPtr appContainerSid;
+            internal IntPtr userSid;
+            [MarshalAs(UnmanagedType.LPWStr)]
+            public string appContainerName;
+            [MarshalAs(UnmanagedType.LPWStr)]
+            public string displayName;
+            [MarshalAs(UnmanagedType.LPWStr)]
+            public string description;
+            internal INET_FIREWALL_AC_CAPABILITIES capabilities;
+            internal INET_FIREWALL_AC_BINARIES binaries;
+            [MarshalAs(UnmanagedType.LPWStr)]
+            public string workingDirectory;
+            [MarshalAs(UnmanagedType.LPWStr)]
+            public string packageFullName;
+        }
+
+
+        // Call this API to free the memory returned by the Enumeration API 
+        [DllImport("FirewallAPI.dll")]
+        internal static extern void NetworkIsolationFreeAppContainers(IntPtr pACs);
+
+        // Call this API to load the current list of LoopUtil-enabled AppContainers
+        [DllImport("FirewallAPI.dll")]
+        internal static extern uint NetworkIsolationGetAppContainerConfig(out uint pdwCntACs, out IntPtr appContainerSids);
+
+        // Call this API to set the LoopUtil-exemption list 
+        [DllImport("FirewallAPI.dll")]
+        private static extern uint NetworkIsolationSetAppContainerConfig(uint pdwCntACs, SID_AND_ATTRIBUTES[] appContainerSids);
+
+
+        // Use this API to convert a string SID into an actual SID 
+        [DllImport("advapi32.dll", SetLastError = true)]
+        internal static extern bool ConvertStringSidToSid(string strSid, out IntPtr pSid);
+
+        [DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)]
+        static extern bool ConvertSidToStringSid(
+            [MarshalAs(UnmanagedType.LPArray)] byte[] pSID,
+            out IntPtr ptrSid);
+
+        [DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)]
+        static extern bool ConvertSidToStringSid(IntPtr pSid, out string strSid);
+
+        // Use this API to convert a string reference (e.g. "@{blah.pri?ms-resource://whatever}") into a plain string 
+        [DllImport("shlwapi.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
+        internal static extern int SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf);
+
+        // Call this API to enumerate all of the AppContainers on the system 
+        [DllImport("FirewallAPI.dll")]
+        internal static extern uint NetworkIsolationEnumAppContainers(uint Flags, out uint pdwCntPublicACs, out IntPtr ppACs);
+        //        DWORD NetworkIsolationEnumAppContainers(
+        //  _In_   DWORD Flags,
+        //  _Out_  DWORD *pdwNumPublicAppCs,
+        //  _Out_  PINET_FIREWALL_APP_CONTAINER *ppPublicAppCs
+        //);
+
+        //http://msdn.microsoft.com/en-gb/library/windows/desktop/hh968116.aspx
+        enum NETISO_FLAG
+        {
+            NETISO_FLAG_FORCE_COMPUTE_BINARIES = 0x1,
+            NETISO_FLAG_MAX = 0x2
+        }
+
+
+        public class AppContainer
+        {
+            public String appContainerName { get; set; }
+            public String displayName { get; set; }
+            public String workingDirectory { get; set; }
+            public String StringSid { get; set; }
+            public List<uint> capabilities { get; set; }
+            public bool LoopUtil { get; set; }
+
+            public AppContainer(String _appContainerName, String _displayName, String _workingDirectory, IntPtr _sid)
+            {
+                this.appContainerName = _appContainerName;
+                this.displayName = _displayName;
+                this.workingDirectory = _workingDirectory;
+                String tempSid;
+                ConvertSidToStringSid(_sid, out tempSid);
+                this.StringSid = tempSid;
+            }
+        }
+
+        internal List<LoopUtil.INET_FIREWALL_APP_CONTAINER> _AppList;
+        internal List<LoopUtil.SID_AND_ATTRIBUTES> _AppListConfig;
+        public List<AppContainer> Apps = new List<AppContainer>();
+        internal IntPtr _pACs;
+
+        public LoopUtil()
+        {
+            LoadApps();
+        }
+
+        public void LoadApps()
+        {
+            Apps.Clear();
+            _pACs = IntPtr.Zero;
+            //Full List of Apps
+            _AppList = PI_NetworkIsolationEnumAppContainers();
+            //List of Apps that have LoopUtil enabled.
+            _AppListConfig = PI_NetworkIsolationGetAppContainerConfig();
+            foreach (var PI_app in _AppList)
+            {
+                AppContainer app = new AppContainer(PI_app.appContainerName, PI_app.displayName, PI_app.workingDirectory, PI_app.appContainerSid);
+
+                var app_capabilities = LoopUtil.getCapabilites(PI_app.capabilities);
+                if (app_capabilities.Count > 0)
+                {
+                    //var sid = new SecurityIdentifier(app_capabilities[0], 0);
+
+                    IntPtr arrayValue = IntPtr.Zero;
+                    //var b = LoopUtil.ConvertStringSidToSid(app_capabilities[0].Sid, out arrayValue);
+                    //string mysid;
+                    //var b = LoopUtil.ConvertSidToStringSid(app_capabilities[0].Sid, out mysid);
+                }
+                app.LoopUtil = CheckLoopback(PI_app.appContainerSid);
+                Apps.Add(app);
+            }
+        }
+        private bool CheckLoopback(IntPtr intPtr)
+        {
+            foreach (SID_AND_ATTRIBUTES item in _AppListConfig)
+            {
+                string left, right;
+                ConvertSidToStringSid(item.Sid, out left);
+                ConvertSidToStringSid(intPtr, out right);
+
+                if (left == right)
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private bool CreateExcemptions(string appName)
+        {
+            var hasChanges = false;
+
+            foreach (var app in Apps)
+            {
+                if ((app.appContainerName ?? string.Empty).IndexOf(appName, StringComparison.OrdinalIgnoreCase) != -1 || 
+                    (app.displayName ?? string.Empty).IndexOf(appName, StringComparison.OrdinalIgnoreCase) != -1)
+                {
+                    if (!app.LoopUtil)
+                    {
+                        app.LoopUtil = true;
+                        hasChanges = true;
+                    }
+                }
+            }
+
+            return hasChanges;
+        }
+
+        public static void Run(string appName)
+        {
+            var util = new LoopUtil();
+            util.LoadApps();
+
+            var hasChanges = util.CreateExcemptions(appName);
+
+            if (hasChanges)
+            {
+                util.SaveLoopbackState();
+            }
+            util.SaveLoopbackState();
+        }
+
+        private static List<SID_AND_ATTRIBUTES> getCapabilites(INET_FIREWALL_AC_CAPABILITIES cap)
+        {
+            List<SID_AND_ATTRIBUTES> mycap = new List<SID_AND_ATTRIBUTES>();
+
+            IntPtr arrayValue = cap.capabilities;
+
+            var structSize = Marshal.SizeOf(typeof(SID_AND_ATTRIBUTES));
+            for (var i = 0; i < cap.count; i++)
+            {
+                var cur = (SID_AND_ATTRIBUTES)Marshal.PtrToStructure(arrayValue, typeof(SID_AND_ATTRIBUTES));
+                mycap.Add(cur);
+                arrayValue = new IntPtr((long)(arrayValue) + (long)(structSize));
+            }
+
+            return mycap;
+
+        }
+
+        private static List<SID_AND_ATTRIBUTES> getContainerSID(INET_FIREWALL_AC_CAPABILITIES cap)
+        {
+            List<SID_AND_ATTRIBUTES> mycap = new List<SID_AND_ATTRIBUTES>();
+
+            IntPtr arrayValue = cap.capabilities;
+
+            var structSize = Marshal.SizeOf(typeof(SID_AND_ATTRIBUTES));
+            for (var i = 0; i < cap.count; i++)
+            {
+                var cur = (SID_AND_ATTRIBUTES)Marshal.PtrToStructure(arrayValue, typeof(SID_AND_ATTRIBUTES));
+                mycap.Add(cur);
+                arrayValue = new IntPtr((long)(arrayValue) + (long)(structSize));
+            }
+
+            return mycap;
+
+        }
+
+        private static List<SID_AND_ATTRIBUTES> PI_NetworkIsolationGetAppContainerConfig()
+        {
+
+            IntPtr arrayValue = IntPtr.Zero;
+            uint size = 0;
+            var list = new List<SID_AND_ATTRIBUTES>();
+
+            // Pin down variables
+            GCHandle handle_pdwCntPublicACs = GCHandle.Alloc(size, GCHandleType.Pinned);
+            GCHandle handle_ppACs = GCHandle.Alloc(arrayValue, GCHandleType.Pinned);
+
+            uint retval = NetworkIsolationGetAppContainerConfig(out size, out arrayValue);
+
+            var structSize = Marshal.SizeOf(typeof(SID_AND_ATTRIBUTES));
+            for (var i = 0; i < size; i++)
+            {
+                var cur = (SID_AND_ATTRIBUTES)Marshal.PtrToStructure(arrayValue, typeof(SID_AND_ATTRIBUTES));
+                list.Add(cur);
+                arrayValue = new IntPtr((long)(arrayValue) + (long)(structSize));
+            }
+
+            //release pinned variables.
+            handle_pdwCntPublicACs.Free();
+            handle_ppACs.Free();
+
+            return list;
+
+
+        }
+
+        private List<INET_FIREWALL_APP_CONTAINER> PI_NetworkIsolationEnumAppContainers()
+        {
+
+            IntPtr arrayValue = IntPtr.Zero;
+            uint size = 0;
+            var list = new List<INET_FIREWALL_APP_CONTAINER>();
+
+            // Pin down variables
+            GCHandle handle_pdwCntPublicACs = GCHandle.Alloc(size, GCHandleType.Pinned);
+            GCHandle handle_ppACs = GCHandle.Alloc(arrayValue, GCHandleType.Pinned);
+
+            //uint retval2 = NetworkIsolationGetAppContainerConfig( out size, out arrayValue);
+
+            uint retval = NetworkIsolationEnumAppContainers((Int32)NETISO_FLAG.NETISO_FLAG_MAX, out size, out arrayValue);
+            _pACs = arrayValue; //store the pointer so it can be freed when we close the form
+
+            var structSize = Marshal.SizeOf(typeof(INET_FIREWALL_APP_CONTAINER));
+            for (var i = 0; i < size; i++)
+            {
+                var cur = (INET_FIREWALL_APP_CONTAINER)Marshal.PtrToStructure(arrayValue, typeof(INET_FIREWALL_APP_CONTAINER));
+                list.Add(cur);
+                arrayValue = new IntPtr((long)(arrayValue) + (long)(structSize));
+            }
+
+            //release pinned variables.
+            handle_pdwCntPublicACs.Free();
+            handle_ppACs.Free();
+
+            return list;
+
+
+        }
+
+        public bool SaveLoopbackState()
+        {
+            var countEnabled = CountEnabledLoopUtil();
+            SID_AND_ATTRIBUTES[] arr = new SID_AND_ATTRIBUTES[countEnabled];
+            int count = 0;
+
+            for (int i = 0; i < Apps.Count; i++)
+            {
+                if (Apps[i].LoopUtil)
+                {
+                    arr[count].Attributes = 0;
+                    //TO DO:
+                    IntPtr ptr;
+                    ConvertStringSidToSid(Apps[i].StringSid, out ptr);
+                    arr[count].Sid = ptr;
+                    count++;
+                }
+
+            }
+
+
+            if (NetworkIsolationSetAppContainerConfig((uint)countEnabled, arr) == 0)
+            {
+                return true;
+            }
+            else
+            { return false; }
+
+        }
+
+        private int CountEnabledLoopUtil()
+        {
+            var count = 0;
+            for (int i = 0; i < Apps.Count; i++)
+            {
+                if (Apps[i].LoopUtil)
+                {
+                    count++;
+                }
+
+            }
+            return count;
+        }
+
+        public void FreeResources()
+        {
+            NetworkIsolationFreeAppContainers(_pACs);
+        }
+
+    }
+}

+ 5 - 0
MediaBrowser.ServerApplication/Native/WindowsApp.cs

@@ -203,5 +203,10 @@ namespace MediaBrowser.ServerApplication.Native
         {
             ((Process)sender).Dispose();
         }
+
+        public void EnableLoopback(string appName)
+        {
+            LoopUtil.Run(appName);
+        }
     }
 }

+ 22 - 130
MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs

@@ -25,19 +25,22 @@ namespace MediaBrowser.XbmcMetadata.Parsers
         /// The logger
         /// </summary>
         protected ILogger Logger { get; private set; }
+        protected IProviderManager ProviderManager { get; private set; }
 
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
         private readonly IConfigurationManager _config;
+        private Dictionary<string, string> _validProviderIds;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="BaseNfoParser{T}" /> class.
         /// </summary>
         /// <param name="logger">The logger.</param>
         /// <param name="config">The configuration.</param>
-        public BaseNfoParser(ILogger logger, IConfigurationManager config)
+        public BaseNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
         {
             Logger = logger;
             _config = config;
+            ProviderManager = providerManager;
         }
 
         /// <summary>
@@ -68,6 +71,24 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                 ValidationType = ValidationType.None
             };
 
+            _validProviderIds = _validProviderIds = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
+
+            var idInfos = ProviderManager.GetExternalIdInfos(item.Item);
+
+            foreach (var info in idInfos)
+            {
+                var id = info.Key + "Id";
+                if (!_validProviderIds.ContainsKey(id))
+                {
+                    _validProviderIds.Add(id, info.Key);
+                }
+            }
+
+            //Additional Mappings
+            _validProviderIds.Add("collectionnumber", "TmdbCollection");
+            _validProviderIds.Add("tmdbcolid", "TmdbCollection");
+            _validProviderIds.Add("imdb_id", "Imdb");
+
             Fetch(item, metadataFile, settings, cancellationToken);
         }
 
@@ -760,14 +781,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                         break;
                     }
 
-                case "tvdbid":
-                    var tvdbId = reader.ReadElementContentAsString();
-                    if (!string.IsNullOrWhiteSpace(tvdbId))
-                    {
-                        item.SetProviderId(MetadataProviders.Tvdb, tvdbId);
-                    }
-                    break;
-
                 case "votes":
                     {
                         var val = reader.ReadElementContentAsString();
@@ -782,127 +795,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                         }
                         break;
                     }
-                case "musicbrainzalbumid":
-                    {
-                        var mbz = reader.ReadElementContentAsString();
-                        if (!string.IsNullOrWhiteSpace(mbz))
-                        {
-                            item.SetProviderId(MetadataProviders.MusicBrainzAlbum, mbz);
-                        }
-                        break;
-                    }
-                case "musicbrainzalbumartistid":
-                    {
-                        var mbz = reader.ReadElementContentAsString();
-                        if (!string.IsNullOrWhiteSpace(mbz))
-                        {
-                            item.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, mbz);
-                        }
-                        break;
-                    }
-                case "musicbrainzartistid":
-                    {
-                        var mbz = reader.ReadElementContentAsString();
-                        if (!string.IsNullOrWhiteSpace(mbz))
-                        {
-                            item.SetProviderId(MetadataProviders.MusicBrainzArtist, mbz);
-                        }
-                        break;
-                    }
-                case "musicbrainzreleasegroupid":
-                    {
-                        var mbz = reader.ReadElementContentAsString();
-                        if (!string.IsNullOrWhiteSpace(mbz))
-                        {
-                            item.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, mbz);
-                        }
-                        break;
-                    }
-                case "tvrageid":
-                    {
-                        var id = reader.ReadElementContentAsString();
-                        if (!string.IsNullOrWhiteSpace(id))
-                        {
-                            item.SetProviderId(MetadataProviders.TvRage, id);
-                        }
-                        break;
-                    }
-                case "tvmazeid":
-                    {
-                        var id = reader.ReadElementContentAsString();
-                        if (!string.IsNullOrWhiteSpace(id))
-                        {
-                            item.SetProviderId(MetadataProviders.TvMaze, id);
-                        }
-                        break;
-                    }
-                case "audiodbartistid":
-                    {
-                        var id = reader.ReadElementContentAsString();
-                        if (!string.IsNullOrWhiteSpace(id))
-                        {
-                            item.SetProviderId(MetadataProviders.AudioDbArtist, id);
-                        }
-                        break;
-                    }
-                case "audiodbalbumid":
-                    {
-                        var id = reader.ReadElementContentAsString();
-                        if (!string.IsNullOrWhiteSpace(id))
-                        {
-                            item.SetProviderId(MetadataProviders.AudioDbAlbum, id);
-                        }
-                        break;
-                    }
-                case "rottentomatoesid":
-                    var rtId = reader.ReadElementContentAsString();
-                    if (!string.IsNullOrWhiteSpace(rtId))
-                    {
-                        item.SetProviderId(MetadataProviders.RottenTomatoes, rtId);
-                    }
-                    break;
-
-                case "tmdbid":
-                    var tmdb = reader.ReadElementContentAsString();
-                    if (!string.IsNullOrWhiteSpace(tmdb))
-                    {
-                        item.SetProviderId(MetadataProviders.Tmdb, tmdb);
-                    }
-                    break;
-
-                case "collectionnumber":
-                case "tmdbcolid":
-                    var tmdbCollection = reader.ReadElementContentAsString();
-                    if (!string.IsNullOrWhiteSpace(tmdbCollection))
-                    {
-                        item.SetProviderId(MetadataProviders.TmdbCollection, tmdbCollection);
-                    }
-                    break;
-
-                case "tvcomid":
-                    var TVcomId = reader.ReadElementContentAsString();
-                    if (!string.IsNullOrWhiteSpace(TVcomId))
-                    {
-                        item.SetProviderId(MetadataProviders.Tvcom, TVcomId);
-                    }
-                    break;
-
-                case "zap2itid":
-                    var zap2ItId = reader.ReadElementContentAsString();
-                    if (!string.IsNullOrWhiteSpace(zap2ItId))
-                    {
-                        item.SetProviderId(MetadataProviders.Zap2It, zap2ItId);
-                    }
-                    break;
-
-                case "imdb_id":
-                case "imdbid":
-                    var imDbId = reader.ReadElementContentAsString();
-                    if (!string.IsNullOrWhiteSpace(imDbId))
-                    {
-                        item.SetProviderId(MetadataProviders.Imdb, imDbId);
-                    }
-                    break;
 
                 case "genre":
                     {

+ 1 - 1
MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs

@@ -12,7 +12,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
 {
     public class EpisodeNfoParser : BaseNfoParser<Episode>
     {
-        public EpisodeNfoParser(ILogger logger, IConfigurationManager config) : base(logger, config)
+        public EpisodeNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(logger, config, providerManager)
         {
         }
 

+ 2 - 2
MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs

@@ -10,8 +10,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers
 {
     class MovieNfoParser : BaseNfoParser<Video>
     {
-        public MovieNfoParser(ILogger logger, IConfigurationManager config)
-            : base(logger, config)
+        public MovieNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
+            : base(logger, config, providerManager)
         {
         }
 

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

@@ -9,7 +9,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
 {
     public class SeasonNfoParser : BaseNfoParser<Season>
     {
-        public SeasonNfoParser(ILogger logger, IConfigurationManager config) : base(logger, config)
+        public SeasonNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(logger, config, providerManager)
         {
         }
 

+ 1 - 1
MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs

@@ -11,7 +11,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
 {
     public class SeriesNfoParser : BaseNfoParser<Series>
     {
-        public SeriesNfoParser(ILogger logger, IConfigurationManager config) : base(logger, config)
+        public SeriesNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(logger, config, providerManager)
         {
         }
 

+ 4 - 2
MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs

@@ -13,17 +13,19 @@ namespace MediaBrowser.XbmcMetadata.Providers
     {
         private readonly ILogger _logger;
         private readonly IConfigurationManager _config;
+        private readonly IProviderManager _providerManager;
 
-        public AlbumNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config)
+        public AlbumNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
             : base(fileSystem)
         {
             _logger = logger;
             _config = config;
+            _providerManager = providerManager;
         }
 
         protected override void Fetch(MetadataResult<MusicAlbum> result, string path, CancellationToken cancellationToken)
         {
-            new BaseNfoParser<MusicAlbum>(_logger, _config).Fetch(result, path, cancellationToken);
+            new BaseNfoParser<MusicAlbum>(_logger, _config, _providerManager).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 4 - 2
MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs

@@ -13,17 +13,19 @@ namespace MediaBrowser.XbmcMetadata.Providers
     {
         private readonly ILogger _logger;
         private readonly IConfigurationManager _config;
+        private readonly IProviderManager _providerManager;
 
-        public ArtistNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config)
+        public ArtistNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
             : base(fileSystem)
         {
             _logger = logger;
             _config = config;
+            _providerManager = providerManager;
         }
 
         protected override void Fetch(MetadataResult<MusicArtist> result, string path, CancellationToken cancellationToken)
         {
-            new BaseNfoParser<MusicArtist>(_logger, _config).Fetch(result, path, cancellationToken);
+            new BaseNfoParser<MusicArtist>(_logger, _config, _providerManager).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 4 - 2
MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs

@@ -15,12 +15,14 @@ namespace MediaBrowser.XbmcMetadata.Providers
     {
         private readonly ILogger _logger;
         private readonly IConfigurationManager _config;
+        private readonly IProviderManager _providerManager;
 
-        public BaseVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config)
+        public BaseVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
             : base(fileSystem)
         {
             _logger = logger;
             _config = config;
+            _providerManager = providerManager;
         }
 
         protected override void Fetch(MetadataResult<T> result, string path, CancellationToken cancellationToken)
@@ -29,7 +31,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
             {
                 Item = result.Item
             };
-            new MovieNfoParser(_logger, _config).Fetch(tmpItem, path, cancellationToken);
+            new MovieNfoParser(_logger, _config, _providerManager).Fetch(tmpItem, path, cancellationToken);
 
             result.Item = (T)tmpItem.Item;
             result.People = tmpItem.People;

+ 4 - 2
MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs

@@ -14,19 +14,21 @@ namespace MediaBrowser.XbmcMetadata.Providers
     {
         private readonly ILogger _logger;
         private readonly IConfigurationManager _config;
+        private readonly IProviderManager _providerManager;
 
-        public EpisodeNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config)
+        public EpisodeNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
             : base(fileSystem)
         {
             _logger = logger;
             _config = config;
+            _providerManager = providerManager;
         }
 
         protected override void Fetch(MetadataResult<Episode> result, string path, CancellationToken cancellationToken)
         {
             var images = new List<LocalImageInfo>();
 
-            new EpisodeNfoParser(_logger, _config).Fetch(result, images, path, cancellationToken);
+            new EpisodeNfoParser(_logger, _config, _providerManager).Fetch(result, images, path, cancellationToken);
 
             result.Images = images;
         }

+ 4 - 3
MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs

@@ -2,27 +2,28 @@
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Logging;
 
 namespace MediaBrowser.XbmcMetadata.Providers
 {
     public class MovieNfoProvider : BaseVideoNfoProvider<Movie>
     {
-        public MovieNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) : base(fileSystem, logger, config)
+        public MovieNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(fileSystem, logger, config, providerManager)
         {
         }
     }
 
     public class MusicVideoNfoProvider : BaseVideoNfoProvider<MusicVideo>
     {
-        public MusicVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) : base(fileSystem, logger, config)
+        public MusicVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(fileSystem, logger, config, providerManager)
         {
         }
     }
 
     public class VideoNfoProvider : BaseVideoNfoProvider<Video>
     {
-        public VideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config) : base(fileSystem, logger, config)
+        public VideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(fileSystem, logger, config, providerManager)
         {
         }
     }

+ 4 - 2
MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs

@@ -13,17 +13,19 @@ namespace MediaBrowser.XbmcMetadata.Providers
     {
         private readonly ILogger _logger;
         private readonly IConfigurationManager _config;
+        private readonly IProviderManager _providerManager;
 
-        public SeasonNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config)
+        public SeasonNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
             : base(fileSystem)
         {
             _logger = logger;
             _config = config;
+            _providerManager = providerManager;
         }
 
         protected override void Fetch(MetadataResult<Season> result, string path, CancellationToken cancellationToken)
         {
-            new SeasonNfoParser(_logger, _config).Fetch(result, path, cancellationToken);
+            new SeasonNfoParser(_logger, _config, _providerManager).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 4 - 2
MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs

@@ -13,17 +13,19 @@ namespace MediaBrowser.XbmcMetadata.Providers
     {
         private readonly ILogger _logger;
         private readonly IConfigurationManager _config;
+        private readonly IProviderManager _providerManager;
 
-        public SeriesNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config)
+        public SeriesNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
             : base(fileSystem)
         {
             _logger = logger;
             _config = config;
+            _providerManager = providerManager;
         }
 
         protected override void Fetch(MetadataResult<Series> result, string path, CancellationToken cancellationToken)
         {
-            new SeriesNfoParser(_logger, _config).Fetch(result, path, cancellationToken);
+            new SeriesNfoParser(_logger, _config, _providerManager).Fetch(result, path, cancellationToken);
         }
 
         protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)

+ 28 - 5
MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs

@@ -27,8 +27,8 @@ namespace MediaBrowser.XbmcMetadata.Savers
     {
         private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
 
-        private static readonly Dictionary<string, string> CommonTags = new[] {     
-               
+        private static readonly Dictionary<string, string> CommonTags = new[] {
+
                     "plot",
                     "customrating",
                     "lockdata",
@@ -428,6 +428,8 @@ namespace MediaBrowser.XbmcMetadata.Savers
         /// <returns>Task.</returns>
         public static void AddCommonNodes(BaseItem item, XmlWriter writer, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataRepo, IFileSystem fileSystem, IServerConfigurationManager config)
         {
+            var writtenProviderIds = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
+
             var overview = (item.Overview ?? string.Empty)
                 .StripHtml()
                 .Replace("&quot;", "'");
@@ -572,6 +574,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
             if (!string.IsNullOrEmpty(rt))
             {
                 writer.WriteElementString("rottentomatoesid", rt);
+                writtenProviderIds.Add(MetadataProviders.RottenTomatoes.ToString());
             }
 
             var tmdbCollection = item.GetProviderId(MetadataProviders.TmdbCollection);
@@ -579,6 +582,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
             if (!string.IsNullOrEmpty(tmdbCollection))
             {
                 writer.WriteElementString("collectionnumber", tmdbCollection);
+                writtenProviderIds.Add(MetadataProviders.TmdbCollection.ToString());
             }
 
             var imdb = item.GetProviderId(MetadataProviders.Imdb);
@@ -592,6 +596,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
                 {
                     writer.WriteElementString("imdbid", imdb);
                 }
+                writtenProviderIds.Add(MetadataProviders.Imdb.ToString());
             }
 
             // Series xml saver already saves this
@@ -601,6 +606,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
                 if (!string.IsNullOrEmpty(tvdb))
                 {
                     writer.WriteElementString("tvdbid", tvdb);
+                    writtenProviderIds.Add(MetadataProviders.Tvdb.ToString());
                 }
             }
 
@@ -608,12 +614,14 @@ namespace MediaBrowser.XbmcMetadata.Savers
             if (!string.IsNullOrEmpty(tmdb))
             {
                 writer.WriteElementString("tmdbid", tmdb);
+                writtenProviderIds.Add(MetadataProviders.Tmdb.ToString());
             }
 
             var tvcom = item.GetProviderId(MetadataProviders.Tvcom);
             if (!string.IsNullOrEmpty(tvcom))
             {
                 writer.WriteElementString("tvcomid", tvcom);
+                writtenProviderIds.Add(MetadataProviders.Tvcom.ToString());
             }
 
             if (!string.IsNullOrEmpty(item.PreferredMetadataLanguage))
@@ -766,6 +774,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
             if (!string.IsNullOrEmpty(externalId))
             {
                 writer.WriteElementString("audiodbartistid", externalId);
+                writtenProviderIds.Add(MetadataProviders.AudioDbArtist.ToString());
             }
 
             externalId = item.GetProviderId(MetadataProviders.AudioDbAlbum);
@@ -773,6 +782,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
             if (!string.IsNullOrEmpty(externalId))
             {
                 writer.WriteElementString("audiodbalbumid", externalId);
+                writtenProviderIds.Add(MetadataProviders.AudioDbAlbum.ToString());
             }
 
             externalId = item.GetProviderId(MetadataProviders.Zap2It);
@@ -780,6 +790,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
             if (!string.IsNullOrEmpty(externalId))
             {
                 writer.WriteElementString("zap2itid", externalId);
+                writtenProviderIds.Add(MetadataProviders.Zap2It.ToString());
             }
 
             externalId = item.GetProviderId(MetadataProviders.MusicBrainzAlbum);
@@ -787,6 +798,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
             if (!string.IsNullOrEmpty(externalId))
             {
                 writer.WriteElementString("musicbrainzalbumid", externalId);
+                writtenProviderIds.Add(MetadataProviders.MusicBrainzAlbum.ToString());
             }
 
             externalId = item.GetProviderId(MetadataProviders.MusicBrainzAlbumArtist);
@@ -794,6 +806,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
             if (!string.IsNullOrEmpty(externalId))
             {
                 writer.WriteElementString("musicbrainzalbumartistid", externalId);
+                writtenProviderIds.Add(MetadataProviders.MusicBrainzAlbumArtist.ToString());
             }
 
             externalId = item.GetProviderId(MetadataProviders.MusicBrainzArtist);
@@ -801,6 +814,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
             if (!string.IsNullOrEmpty(externalId))
             {
                 writer.WriteElementString("musicbrainzartistid", externalId);
+                writtenProviderIds.Add(MetadataProviders.MusicBrainzArtist.ToString());
             }
 
             externalId = item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
@@ -808,24 +822,33 @@ namespace MediaBrowser.XbmcMetadata.Savers
             if (!string.IsNullOrEmpty(externalId))
             {
                 writer.WriteElementString("musicbrainzreleasegroupid", externalId);
+                writtenProviderIds.Add(MetadataProviders.MusicBrainzReleaseGroup.ToString());
             }
 
             externalId = item.GetProviderId(MetadataProviders.Gamesdb);
             if (!string.IsNullOrEmpty(externalId))
             {
                 writer.WriteElementString("gamesdbid", externalId);
+                writtenProviderIds.Add(MetadataProviders.Gamesdb.ToString());
             }
 
             externalId = item.GetProviderId(MetadataProviders.TvRage);
             if (!string.IsNullOrEmpty(externalId))
             {
                 writer.WriteElementString("tvrageid", externalId);
+                writtenProviderIds.Add(MetadataProviders.TvRage.ToString());
             }
 
-            externalId = item.GetProviderId(MetadataProviders.TvMaze);
-            if (!string.IsNullOrEmpty(externalId))
+            if (item.ProviderIds != null)
             {
-                writer.WriteElementString("tvmazeid", externalId);
+                foreach (var providerKey in item.ProviderIds.Keys)
+                {
+                    var providerId = item.ProviderIds[providerKey];
+                    if (!string.IsNullOrEmpty(providerId) && !writtenProviderIds.Contains(providerKey))
+                    {
+                        writer.WriteElementString(providerKey.ToLower() + "id", providerId);
+                    }
+                }
             }
 
             if (options.SaveImagePathsInNfo)

+ 1 - 1
MediaBrowser.sln

@@ -1,7 +1,7 @@
 
 Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio 14
-VisualStudioVersion = 14.0.24720.0
+VisualStudioVersion = 14.0.25420.1
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{F0E0E64C-2A6F-4E35-9533-D53AC07C2CD1}"
 EndProject

+ 2 - 2
Nuget/MediaBrowser.Common.Internal.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common.Internal</id>
-        <version>3.0.656</version>
+        <version>3.0.657</version>
         <title>MediaBrowser.Common.Internal</title>
         <authors>Luke</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description>
         <copyright>Copyright © Emby 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.656" />
+            <dependency id="MediaBrowser.Common" version="3.0.657" />
             <dependency id="NLog" version="4.3.6" />
             <dependency id="SimpleInjector" version="3.2.0" />
         </dependencies>

+ 1 - 1
Nuget/MediaBrowser.Common.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common</id>
-        <version>3.0.656</version>
+        <version>3.0.657</version>
         <title>MediaBrowser.Common</title>
         <authors>Emby Team</authors>
         <owners>ebr,Luke,scottisafool</owners>

+ 2 - 2
Nuget/MediaBrowser.Server.Core.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Server.Core</id>
-        <version>3.0.656</version>
+        <version>3.0.657</version>
         <title>Media Browser.Server.Core</title>
         <authors>Emby Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains core components required to build plugins for Emby Server.</description>
         <copyright>Copyright © Emby 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.656" />
+            <dependency id="MediaBrowser.Common" version="3.0.657" />
 			<dependency id="Interfaces.IO" version="1.0.0.5" />
         </dependencies>
     </metadata>