Parcourir la source

Re-worked provider id's, api client, moved people to the api item wrapper and added server error handling

LukePulverenti Luke Pulverenti luke pulverenti il y a 13 ans
Parent
commit
3f1af19ce7
36 fichiers modifiés avec 474 ajouts et 373 suppressions
  1. 12 7
      MediaBrowser.Api/ApiService.cs
  2. 6 5
      MediaBrowser.Api/HttpHandlers/BaseMediaHandler.cs
  3. 6 8
      MediaBrowser.Api/HttpHandlers/GenresHandler.cs
  4. 60 15
      MediaBrowser.Api/HttpHandlers/ImageHandler.cs
  5. 11 6
      MediaBrowser.Api/HttpHandlers/ItemHandler.cs
  6. 6 8
      MediaBrowser.Api/HttpHandlers/ItemListHandler.cs
  7. 0 20
      MediaBrowser.Api/HttpHandlers/JsonHandler.cs
  8. 5 7
      MediaBrowser.Api/HttpHandlers/PersonHandler.cs
  9. 5 7
      MediaBrowser.Api/HttpHandlers/PluginConfigurationHandler.cs
  10. 17 19
      MediaBrowser.Api/HttpHandlers/PluginsHandler.cs
  11. 6 8
      MediaBrowser.Api/HttpHandlers/StudiosHandler.cs
  12. 5 7
      MediaBrowser.Api/HttpHandlers/UserConfigurationHandler.cs
  13. 5 7
      MediaBrowser.Api/HttpHandlers/UsersHandler.cs
  14. 6 8
      MediaBrowser.Api/HttpHandlers/YearsHandler.cs
  15. 2 2
      MediaBrowser.Api/ImageProcessor.cs
  16. 0 1
      MediaBrowser.Api/MediaBrowser.Api.csproj
  17. 25 9
      MediaBrowser.ApiInteraction/ApiClient.cs
  18. 0 52
      MediaBrowser.ApiInteraction/BaseClient.cs
  19. 11 0
      MediaBrowser.ApiInteraction/IHttpClient.cs
  20. 1 1
      MediaBrowser.ApiInteraction/MediaBrowser.ApiInteraction.csproj
  21. 1 8
      MediaBrowser.Common/Configuration/ApplicationPaths.cs
  22. 57 39
      MediaBrowser.Common/Net/Handlers/BaseHandler.cs
  23. 49 2
      MediaBrowser.Common/Net/Handlers/BaseJsonHandler.cs
  24. 79 79
      MediaBrowser.Common/Net/Handlers/StaticFileHandler.cs
  25. 2 0
      MediaBrowser.Common/Net/MimeTypes.cs
  26. 27 1
      MediaBrowser.Controller/Xml/BaseItemXmlParser.cs
  27. 2 5
      MediaBrowser.Model/Entities/ApiBaseItem.cs
  28. 45 0
      MediaBrowser.Model/Entities/BaseItem.cs
  29. 11 0
      MediaBrowser.Model/Entities/MetadataProviders.cs
  30. 1 0
      MediaBrowser.Model/MediaBrowser.Model.csproj
  31. 0 1
      MediaBrowser.Movies/MediaBrowser.Movies.csproj
  32. 0 32
      MediaBrowser.Movies/Metadata/MovieXmlParser.cs
  33. 2 2
      MediaBrowser.Movies/Resolvers/MovieResolver.cs
  34. 2 4
      MediaBrowser.TV/Entities/Series.cs
  35. 1 2
      MediaBrowser.TV/Metadata/EpisodeXmlParser.cs
  36. 6 1
      MediaBrowser.TV/Metadata/SeriesXmlParser.cs

+ 12 - 7
MediaBrowser.Api/ApiService.cs

@@ -58,6 +58,8 @@ namespace MediaBrowser.Api
                 {
                 {
                     wrapper.Children = Kernel.Instance.GetParentalAllowedChildren(folder, userId).Select(c => GetSerializationObject(c, false, userId));
                     wrapper.Children = Kernel.Instance.GetParentalAllowedChildren(folder, userId).Select(c => GetSerializationObject(c, false, userId));
                 }
                 }
+
+                wrapper.People = item.People;
             }
             }
 
 
             return wrapper;
             return wrapper;
@@ -136,15 +138,18 @@ namespace MediaBrowser.Api
 
 
                     _FFMpegPath = Path.Combine(FFMpegDirectory, filename);
                     _FFMpegPath = Path.Combine(FFMpegDirectory, filename);
 
 
-                    if (!File.Exists(_FFMpegPath))
+                    // Always re-extract the first time to handle new versions
+                    if (File.Exists(_FFMpegPath))
+                    {
+                        File.Delete(_FFMpegPath);
+                    }
+
+                    // Extract ffprobe
+                    using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MediaBrowser.Api.FFMpeg." + filename))
                     {
                     {
-                        // Extract ffprobe
-                        using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MediaBrowser.Api.FFMpeg." + filename))
+                        using (FileStream fileStream = new FileStream(_FFMpegPath, FileMode.Create))
                         {
                         {
-                            using (FileStream fileStream = new FileStream(_FFMpegPath, FileMode.Create))
-                            {
-                                stream.CopyTo(fileStream);
-                            }
+                            stream.CopyTo(fileStream);
                         }
                         }
                     }
                     }
                 }
                 }

+ 6 - 5
MediaBrowser.Api/HttpHandlers/BaseMediaHandler.cs

@@ -107,17 +107,18 @@ namespace MediaBrowser.Api.HttpHandlers
             }
             }
         }
         }
 
 
-        public override void ProcessRequest(HttpListenerContext ctx)
+        public override async Task ProcessRequest(HttpListenerContext ctx)
         {
         {
             HttpListenerContext = ctx;
             HttpListenerContext = ctx;
 
 
             if (!RequiresConversion())
             if (!RequiresConversion())
             {
             {
-                new StaticFileHandler() { Path = LibraryItem.Path }.ProcessRequest(ctx);
-                return;
+                await new StaticFileHandler() { Path = LibraryItem.Path }.ProcessRequest(ctx);
+            }
+            else
+            {
+                await base.ProcessRequest(ctx);
             }
             }
-
-            base.ProcessRequest(ctx);
         }
         }
 
 
         protected abstract string GetCommandLineArguments();
         protected abstract string GetCommandLineArguments();

+ 6 - 8
MediaBrowser.Api/HttpHandlers/GenresHandler.cs

@@ -1,20 +1,18 @@
 using System;
 using System;
+using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 
 
 namespace MediaBrowser.Api.HttpHandlers
 namespace MediaBrowser.Api.HttpHandlers
 {
 {
-    public class GenresHandler : JsonHandler
+    public class GenresHandler : BaseJsonHandler
     {
     {
-        protected sealed override object ObjectToSerialize
+        protected override object GetObjectToSerialize()
         {
         {
-            get
-            {
-                Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder;
-                Guid userId = Guid.Parse(QueryString["userid"]);
+            Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder;
+            Guid userId = Guid.Parse(QueryString["userid"]);
 
 
-                return Kernel.Instance.GetAllGenres(parent, userId);
-            }
+            return Kernel.Instance.GetAllGenres(parent, userId);
         }
         }
     }
     }
 }
 }

+ 60 - 15
MediaBrowser.Api/HttpHandlers/ImageHandler.cs

@@ -2,6 +2,8 @@
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using MediaBrowser.Common.Logging;
+using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
@@ -10,12 +12,12 @@ namespace MediaBrowser.Api.HttpHandlers
 {
 {
     public class ImageHandler : BaseHandler
     public class ImageHandler : BaseHandler
     {
     {
-        private string _ImagePath = string.Empty;
+        private string _ImagePath = null;
         private string ImagePath
         private string ImagePath
         {
         {
             get
             get
             {
             {
-                if (string.IsNullOrEmpty(_ImagePath))
+                if (_ImagePath == null)
                 {
                 {
                     _ImagePath = GetImagePath();
                     _ImagePath = GetImagePath();
                 }
                 }
@@ -24,18 +26,61 @@ namespace MediaBrowser.Api.HttpHandlers
             }
             }
         }
         }
 
 
-        public override string ContentType
+        private Stream _SourceStream = null;
+        private Stream SourceStream
         {
         {
             get
             get
             {
             {
-                string extension = Path.GetExtension(ImagePath);
+                EnsureSourceStream();
+
+                return _SourceStream;
+            }
+        }
 
 
-                if (extension.EndsWith("png", StringComparison.OrdinalIgnoreCase))
+
+        private bool _SourceStreamEnsured = false;
+        private void EnsureSourceStream()
+        {
+            if (!_SourceStreamEnsured)
+            {
+                try
+                {
+                    _SourceStream = File.OpenRead(ImagePath);
+                }
+                catch (FileNotFoundException ex)
+                {
+                    StatusCode = 404;
+                    Logger.LogException(ex);
+                }
+                catch (DirectoryNotFoundException ex)
+                {
+                    StatusCode = 404;
+                    Logger.LogException(ex);
+                }
+                catch (UnauthorizedAccessException ex)
+                {
+                    StatusCode = 403;
+                    Logger.LogException(ex);
+                }
+                finally
                 {
                 {
-                    return "image/png";
+                    _SourceStreamEnsured = true;
                 }
                 }
+            }
+        }
+        
+        public override string ContentType
+        {
+            get
+            {
+                EnsureSourceStream();
 
 
-                return "image/jpeg";
+                if (SourceStream == null)
+                {
+                    return null;
+                }
+                
+                return MimeTypes.GetMimeType(ImagePath);
             }
             }
         }
         }
 
 
@@ -49,14 +94,14 @@ namespace MediaBrowser.Api.HttpHandlers
 
 
         protected override DateTime? GetLastDateModified()
         protected override DateTime? GetLastDateModified()
         {
         {
-            try
-            {
-                return File.GetLastWriteTime(ImagePath);
-            }
-            catch
+            EnsureSourceStream();
+
+            if (SourceStream == null)
             {
             {
-                return base.GetLastDateModified();
+                return null;
             }
             }
+
+            return File.GetLastWriteTime(ImagePath);
         }
         }
 
 
         private int? Height
         private int? Height
@@ -142,7 +187,7 @@ namespace MediaBrowser.Api.HttpHandlers
 
 
                 if (string.IsNullOrEmpty(imageType))
                 if (string.IsNullOrEmpty(imageType))
                 {
                 {
-                    return Model.Entities.ImageType.Primary;
+                    return ImageType.Primary;
                 }
                 }
 
 
                 return (ImageType)Enum.Parse(typeof(ImageType), imageType, true);
                 return (ImageType)Enum.Parse(typeof(ImageType), imageType, true);
@@ -153,7 +198,7 @@ namespace MediaBrowser.Api.HttpHandlers
         {
         {
             return Task.Run(() =>
             return Task.Run(() =>
             {
             {
-                ImageProcessor.ProcessImage(ImagePath, stream, Width, Height, MaxWidth, MaxHeight, Quality);
+                ImageProcessor.ProcessImage(SourceStream, stream, Width, Height, MaxWidth, MaxHeight, Quality);
             });
             });
         }
         }
 
 

+ 11 - 6
MediaBrowser.Api/HttpHandlers/ItemHandler.cs

@@ -1,18 +1,23 @@
 using System;
 using System;
+using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 
 
 namespace MediaBrowser.Api.HttpHandlers
 namespace MediaBrowser.Api.HttpHandlers
 {
 {
-    public class ItemHandler : JsonHandler
+    public class ItemHandler : BaseJsonHandler
     {
     {
-        protected sealed override object ObjectToSerialize
+        protected sealed override object GetObjectToSerialize()
         {
         {
-            get
-            {
-                Guid userId = Guid.Parse(QueryString["userid"]);
+            Guid userId = Guid.Parse(QueryString["userid"]);
+
+            BaseItem item = ItemToSerialize;
 
 
-                return ApiService.GetSerializationObject(ItemToSerialize, true, userId);
+            if (item == null)
+            {
+                return null;
             }
             }
+
+            return ApiService.GetSerializationObject(item, true, userId);
         }
         }
 
 
         protected virtual BaseItem ItemToSerialize
         protected virtual BaseItem ItemToSerialize

+ 6 - 8
MediaBrowser.Api/HttpHandlers/ItemListHandler.cs

@@ -1,22 +1,20 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 
 
 namespace MediaBrowser.Api.HttpHandlers
 namespace MediaBrowser.Api.HttpHandlers
 {
 {
-    public abstract class ItemListHandler : JsonHandler
+    public abstract class ItemListHandler : BaseJsonHandler
     {
     {
-        protected sealed override object ObjectToSerialize
+        protected override object GetObjectToSerialize()
         {
         {
-            get
+            return ItemsToSerialize.Select(i =>
             {
             {
-                return ItemsToSerialize.Select(i =>
-                {
-                    return ApiService.GetSerializationObject(i, false, UserId);
+                return ApiService.GetSerializationObject(i, false, UserId);
 
 
-                });
-            }
+            });
         }
         }
 
 
         protected abstract IEnumerable<BaseItem> ItemsToSerialize
         protected abstract IEnumerable<BaseItem> ItemsToSerialize

+ 0 - 20
MediaBrowser.Api/HttpHandlers/JsonHandler.cs

@@ -1,20 +0,0 @@
-using System.IO;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Net.Handlers;
-using MediaBrowser.Common.Serialization;
-
-namespace MediaBrowser.Api.HttpHandlers
-{
-    public abstract class JsonHandler : BaseJsonHandler
-    {
-        protected abstract object ObjectToSerialize { get; }
-
-        protected override Task WriteResponseToOutputStream(Stream stream)
-        {
-            return Task.Run(() =>
-            {
-                JsonSerializer.SerializeToStream(ObjectToSerialize, stream);
-            });
-        }
-    }
-}

+ 5 - 7
MediaBrowser.Api/HttpHandlers/PersonHandler.cs

@@ -1,15 +1,13 @@
-using MediaBrowser.Controller;
+using MediaBrowser.Common.Net.Handlers;
+using MediaBrowser.Controller;
 
 
 namespace MediaBrowser.Api.HttpHandlers
 namespace MediaBrowser.Api.HttpHandlers
 {
 {
-    public class PersonHandler : JsonHandler
+    public class PersonHandler : BaseJsonHandler
     {
     {
-        protected sealed override object ObjectToSerialize
+        protected override object GetObjectToSerialize()
         {
         {
-            get
-            {
-                return Kernel.Instance.ItemController.GetPerson(QueryString["name"]);
-            }
+            return Kernel.Instance.ItemController.GetPerson(QueryString["name"]);
         }
         }
     }
     }
 }
 }

+ 5 - 7
MediaBrowser.Api/HttpHandlers/PluginConfigurationHandler.cs

@@ -1,19 +1,17 @@
 using System;
 using System;
 using System.Linq;
 using System.Linq;
+using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 
 
 namespace MediaBrowser.Api.HttpHandlers
 namespace MediaBrowser.Api.HttpHandlers
 {
 {
-    public class PluginConfigurationHandler : JsonHandler
+    public class PluginConfigurationHandler : BaseJsonHandler
     {
     {
-        protected override object ObjectToSerialize
+        protected override object GetObjectToSerialize()
         {
         {
-            get
-            {
-                string pluginName = QueryString["name"];
+            string pluginName = QueryString["name"];
 
 
-                return Kernel.Instance.Plugins.First(p => p.Name.Equals(pluginName, StringComparison.OrdinalIgnoreCase)).Configuration;
-            }
+            return Kernel.Instance.Plugins.First(p => p.Name.Equals(pluginName, StringComparison.OrdinalIgnoreCase)).Configuration;
         }
         }
     }
     }
 }
 }

+ 17 - 19
MediaBrowser.Api/HttpHandlers/PluginsHandler.cs

@@ -1,4 +1,5 @@
 using System.Linq;
 using System.Linq;
+using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.Plugins;
 using MediaBrowser.Model.Plugins;
 
 
@@ -7,31 +8,28 @@ namespace MediaBrowser.Api.HttpHandlers
     /// <summary>
     /// <summary>
     /// Provides information about installed plugins
     /// Provides information about installed plugins
     /// </summary>
     /// </summary>
-    public class PluginsHandler : JsonHandler
+    public class PluginsHandler : BaseJsonHandler
     {
     {
-        protected override object ObjectToSerialize
+        protected override object GetObjectToSerialize()
         {
         {
-            get
+            var plugins = Kernel.Instance.Plugins.Select(p =>
             {
             {
-                var plugins = Kernel.Instance.Plugins.Select(p =>
+                return new PluginInfo()
                 {
                 {
-                    return new PluginInfo()
-                    {
-                        Path = p.Path,
-                        Name = p.Name,
-                        Enabled = p.Enabled,
-                        DownloadToUI = p.DownloadToUI,
-                        Version = p.Version
-                    };
-                });
+                    Path = p.Path,
+                    Name = p.Name,
+                    Enabled = p.Enabled,
+                    DownloadToUI = p.DownloadToUI,
+                    Version = p.Version
+                };
+            });
 
 
-                if (QueryString["uionly"] == "1")
-                {
-                    plugins = plugins.Where(p => p.DownloadToUI);
-                }
-
-                return plugins;
+            if (QueryString["uionly"] == "1")
+            {
+                plugins = plugins.Where(p => p.DownloadToUI);
             }
             }
+
+            return plugins;
         }
         }
     }
     }
 }
 }

+ 6 - 8
MediaBrowser.Api/HttpHandlers/StudiosHandler.cs

@@ -1,20 +1,18 @@
 using System;
 using System;
+using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 
 
 namespace MediaBrowser.Api.HttpHandlers
 namespace MediaBrowser.Api.HttpHandlers
 {
 {
-    public class StudiosHandler : JsonHandler
+    public class StudiosHandler : BaseJsonHandler
     {
     {
-        protected override object ObjectToSerialize
+        protected override object GetObjectToSerialize()
         {
         {
-            get
-            {
-                Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder;
-                Guid userId = Guid.Parse(QueryString["userid"]);
+            Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder;
+            Guid userId = Guid.Parse(QueryString["userid"]);
 
 
-                return Kernel.Instance.GetAllStudios(parent, userId);
-            }
+            return Kernel.Instance.GetAllStudios(parent, userId);
         }
         }
     }
     }
 }
 }

+ 5 - 7
MediaBrowser.Api/HttpHandlers/UserConfigurationHandler.cs

@@ -1,18 +1,16 @@
 using System;
 using System;
+using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 
 
 namespace MediaBrowser.Api.HttpHandlers
 namespace MediaBrowser.Api.HttpHandlers
 {
 {
-    public class UserConfigurationHandler : JsonHandler
+    public class UserConfigurationHandler : BaseJsonHandler
     {
     {
-        protected override object ObjectToSerialize
+        protected override object GetObjectToSerialize()
         {
         {
-            get
-            {
-                Guid userId = Guid.Parse(QueryString["userid"]);
+            Guid userId = Guid.Parse(QueryString["userid"]);
 
 
-                return Kernel.Instance.GetUserConfiguration(userId);
-            }
+            return Kernel.Instance.GetUserConfiguration(userId);
         }
         }
     }
     }
 }
 }

+ 5 - 7
MediaBrowser.Api/HttpHandlers/UsersHandler.cs

@@ -1,15 +1,13 @@
-using MediaBrowser.Controller;
+using MediaBrowser.Common.Net.Handlers;
+using MediaBrowser.Controller;
 
 
 namespace MediaBrowser.Api.HttpHandlers
 namespace MediaBrowser.Api.HttpHandlers
 {
 {
-    class UsersHandler : JsonHandler
+    class UsersHandler : BaseJsonHandler
     {
     {
-        protected override object ObjectToSerialize
+        protected override object GetObjectToSerialize()
         {
         {
-            get
-            {
-                return Kernel.Instance.Users;
-            }
+            return Kernel.Instance.Users;
         }
         }
     }
     }
 }
 }

+ 6 - 8
MediaBrowser.Api/HttpHandlers/YearsHandler.cs

@@ -1,20 +1,18 @@
 using System;
 using System;
+using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 
 
 namespace MediaBrowser.Api.HttpHandlers
 namespace MediaBrowser.Api.HttpHandlers
 {
 {
-    public class YearsHandler : JsonHandler
+    public class YearsHandler : BaseJsonHandler
     {
     {
-        protected override object ObjectToSerialize
+        protected override object GetObjectToSerialize()
         {
         {
-            get
-            {
-                Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder;
-                Guid userId = Guid.Parse(QueryString["userid"]);
+            Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder;
+            Guid userId = Guid.Parse(QueryString["userid"]);
 
 
-                return Kernel.Instance.GetAllYears(parent, userId);
-            }
+            return Kernel.Instance.GetAllYears(parent, userId);
         }
         }
     }
     }
 }
 }

+ 2 - 2
MediaBrowser.Api/ImageProcessor.cs

@@ -8,9 +8,9 @@ namespace MediaBrowser.Api
 {
 {
     public static class ImageProcessor
     public static class ImageProcessor
     {
     {
-        public static void ProcessImage(string path, Stream toStream, int? width, int? height, int? maxWidth, int? maxHeight, int? quality)
+        public static void ProcessImage(Stream sourceImageStream, Stream toStream, int? width, int? height, int? maxWidth, int? maxHeight, int? quality)
         {
         {
-            Image originalImage = Image.FromFile(path);
+            Image originalImage = Image.FromStream(sourceImageStream);
 
 
             var newWidth = originalImage.Width;
             var newWidth = originalImage.Width;
             var newHeight = originalImage.Height;
             var newHeight = originalImage.Height;

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

@@ -56,7 +56,6 @@
     <Compile Include="HttpHandlers\ItemHandler.cs" />
     <Compile Include="HttpHandlers\ItemHandler.cs" />
     <Compile Include="HttpHandlers\ItemListHandler.cs" />
     <Compile Include="HttpHandlers\ItemListHandler.cs" />
     <Compile Include="HttpHandlers\ItemsWithPersonHandler.cs" />
     <Compile Include="HttpHandlers\ItemsWithPersonHandler.cs" />
-    <Compile Include="HttpHandlers\JsonHandler.cs" />
     <Compile Include="HttpHandlers\PersonHandler.cs" />
     <Compile Include="HttpHandlers\PersonHandler.cs" />
     <Compile Include="HttpHandlers\PluginConfigurationHandler.cs" />
     <Compile Include="HttpHandlers\PluginConfigurationHandler.cs" />
     <Compile Include="HttpHandlers\PluginsHandler.cs" />
     <Compile Include="HttpHandlers\PluginsHandler.cs" />

+ 25 - 9
MediaBrowser.ApiInteraction/ApiClient.cs

@@ -2,7 +2,6 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
-using System.Net.Http;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
@@ -10,20 +9,32 @@ using MediaBrowser.Model.Users;
 
 
 namespace MediaBrowser.ApiInteraction
 namespace MediaBrowser.ApiInteraction
 {
 {
-    public class ApiClient : BaseClient
+    public class ApiClient : IDisposable
     {
     {
-        public IJsonSerializer JsonSerializer { get; set; }
+        /// <summary>
+        /// Gets or sets the server host name (myserver or 192.168.x.x)
+        /// </summary>
+        public string ServerHostName { get; set; }
 
 
-        public ApiClient()
-            : base()
-        {
-        }
+        /// <summary>
+        /// Gets or sets the port number used by the API
+        /// </summary>
+        public int ServerApiPort { get; set; }
 
 
-        public ApiClient(HttpClientHandler handler)
-            : base(handler)
+        /// <summary>
+        /// Gets the current api url based on hostname and port.
+        /// </summary>
+        protected string ApiUrl
         {
         {
+            get
+            {
+                return string.Format("http://{0}:{1}/mediabrowser/api", ServerHostName, ServerApiPort);
+            }
         }
         }
 
 
+        public IHttpClient HttpClient { get; set; }
+        public IJsonSerializer JsonSerializer { get; set; }
+
         /// <summary>
         /// <summary>
         /// Gets an image url that can be used to download an image from the api
         /// Gets an image url that can be used to download an image from the api
         /// </summary>
         /// </summary>
@@ -278,5 +289,10 @@ namespace MediaBrowser.ApiInteraction
                 return JsonSerializer.DeserializeFromStream<IEnumerable<ApiBaseItemWrapper<ApiBaseItem>>>(stream);
                 return JsonSerializer.DeserializeFromStream<IEnumerable<ApiBaseItemWrapper<ApiBaseItem>>>(stream);
             }
             }
         }
         }
+
+        public void Dispose()
+        {
+            HttpClient.Dispose();
+        }
     }
     }
 }
 }

+ 0 - 52
MediaBrowser.ApiInteraction/BaseClient.cs

@@ -1,52 +0,0 @@
-using System;
-using System.Net;
-using System.Net.Http;
-
-namespace MediaBrowser.ApiInteraction
-{
-    /// <summary>
-    /// Provides a base class used by the api and image services
-    /// </summary>
-    public abstract class BaseClient : IDisposable
-    {
-        /// <summary>
-        /// Gets or sets the server host name (myserver or 192.168.x.x)
-        /// </summary>
-        public string ServerHostName { get; set; }
-
-        /// <summary>
-        /// Gets or sets the port number used by the API
-        /// </summary>
-        public int ServerApiPort { get; set; }
-
-        /// <summary>
-        /// Gets the current api url based on hostname and port.
-        /// </summary>
-        protected string ApiUrl
-        {
-            get
-            {
-                return string.Format("http://{0}:{1}/mediabrowser/api", ServerHostName, ServerApiPort);
-            }
-        }
-
-        protected HttpClient HttpClient { get; private set; }
-
-        public BaseClient()
-            : this(new HttpClientHandler())
-        {
-        }
-
-        public BaseClient(HttpClientHandler clientHandler)
-        {
-            clientHandler.AutomaticDecompression = DecompressionMethods.Deflate;
-
-            HttpClient = new HttpClient(clientHandler);
-        }
-
-        public void Dispose()
-        {
-            HttpClient.Dispose();
-        }
-    }
-}

+ 11 - 0
MediaBrowser.ApiInteraction/IHttpClient.cs

@@ -0,0 +1,11 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.ApiInteraction
+{
+    public interface IHttpClient : IDisposable  
+    {
+        Task<Stream> GetStreamAsync(string url);
+    }
+}

+ 1 - 1
MediaBrowser.ApiInteraction/MediaBrowser.ApiInteraction.csproj

@@ -40,7 +40,7 @@
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <Compile Include="ApiClient.cs" />
     <Compile Include="ApiClient.cs" />
-    <Compile Include="BaseClient.cs" />
+    <Compile Include="IHttpClient.cs" />
     <Compile Include="IJsonSerializer.cs" />
     <Compile Include="IJsonSerializer.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
   </ItemGroup>

+ 1 - 8
MediaBrowser.Common/Configuration/ApplicationPaths.cs

@@ -1,12 +1,5 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Configuration;
 using System.IO;
 using System.IO;
-using System.ComponentModel.Composition;
-using System.ComponentModel.Composition.Hosting;
-using System.Configuration;
 using System.Reflection;
 using System.Reflection;
 
 
 namespace MediaBrowser.Common.Configuration
 namespace MediaBrowser.Common.Configuration

+ 57 - 39
MediaBrowser.Common/Net/Handlers/BaseHandler.cs

@@ -185,7 +185,7 @@ namespace MediaBrowser.Common.Net.Handlers
             }
             }
         }
         }
 
 
-        public virtual void ProcessRequest(HttpListenerContext ctx)
+        public virtual async Task ProcessRequest(HttpListenerContext ctx)
         {
         {
             HttpListenerContext = ctx;
             HttpListenerContext = ctx;
 
 
@@ -196,46 +196,61 @@ namespace MediaBrowser.Common.Net.Handlers
 
 
             ctx.Response.KeepAlive = true;
             ctx.Response.KeepAlive = true;
 
 
-            if (SupportsByteRangeRequests && IsRangeRequest)
+            try
             {
             {
-                ctx.Response.Headers["Accept-Ranges"] = "bytes";
-            }
-            
-            // Set the initial status code
-            // When serving a range request, we need to return status code 206 to indicate a partial response body
-            StatusCode = SupportsByteRangeRequests && IsRangeRequest ? 206 : 200;
+                if (SupportsByteRangeRequests && IsRangeRequest)
+                {
+                    ctx.Response.Headers["Accept-Ranges"] = "bytes";
+                }
 
 
-            ctx.Response.ContentType = ContentType;
+                // Set the initial status code
+                // When serving a range request, we need to return status code 206 to indicate a partial response body
+                StatusCode = SupportsByteRangeRequests && IsRangeRequest ? 206 : 200;
 
 
-            TimeSpan cacheDuration = CacheDuration;
+                ctx.Response.ContentType = ContentType;
 
 
-            if (ctx.Request.Headers.AllKeys.Contains("If-Modified-Since"))
-            {
-                DateTime ifModifiedSince;
+                TimeSpan cacheDuration = CacheDuration;
 
 
-                if (DateTime.TryParse(ctx.Request.Headers["If-Modified-Since"].Replace(" GMT", string.Empty), out ifModifiedSince))
+                if (ctx.Request.Headers.AllKeys.Contains("If-Modified-Since"))
                 {
                 {
-                    // If the cache hasn't expired yet just return a 304
-                    if (IsCacheValid(ifModifiedSince, cacheDuration, LastDateModified))
+                    DateTime ifModifiedSince;
+
+                    if (DateTime.TryParse(ctx.Request.Headers["If-Modified-Since"].Replace(" GMT", string.Empty), out ifModifiedSince))
                     {
                     {
-                        StatusCode = 304;
+                        // If the cache hasn't expired yet just return a 304
+                        if (IsCacheValid(ifModifiedSince, cacheDuration, LastDateModified))
+                        {
+                            StatusCode = 304;
+                        }
                     }
                     }
                 }
                 }
-            }
 
 
-            if (StatusCode == 200 || StatusCode == 206)
+                PrepareResponse();
+
+                if (IsResponseValid)
+                {
+                    await ProcessUncachedRequest(ctx, cacheDuration);
+                }
+                else
+                {
+                    ctx.Response.StatusCode = StatusCode;
+                    ctx.Response.SendChunked = false;
+                }
+            }
+            catch (Exception ex)
             {
             {
-                ProcessUncachedResponse(ctx, cacheDuration);
+                // It might be too late if some response data has already been transmitted, but try to set this
+                ctx.Response.StatusCode = 500;
+                
+                Logger.LogException(ex);
             }
             }
-            else
+            finally
             {
             {
-                ctx.Response.StatusCode = StatusCode;
-                ctx.Response.SendChunked = false;
                 DisposeResponseStream();
                 DisposeResponseStream();
             }
             }
         }
         }
 
 
-        private async void ProcessUncachedResponse(HttpListenerContext ctx, TimeSpan cacheDuration)
+        private async Task ProcessUncachedRequest(HttpListenerContext ctx, TimeSpan cacheDuration)
         {
         {
             long? totalContentLength = TotalContentLength;
             long? totalContentLength = TotalContentLength;
 
 
@@ -269,7 +284,7 @@ namespace MediaBrowser.Common.Net.Handlers
             // Set the status code
             // Set the status code
             ctx.Response.StatusCode = StatusCode;
             ctx.Response.StatusCode = StatusCode;
 
 
-            if (StatusCode == 200 || StatusCode == 206)
+            if (IsResponseValid)
             {
             {
                 // Finally, write the response data
                 // Finally, write the response data
                 Stream outputStream = ctx.Response.OutputStream;
                 Stream outputStream = ctx.Response.OutputStream;
@@ -288,23 +303,11 @@ namespace MediaBrowser.Common.Net.Handlers
                     outputStream = CompressedStream;
                     outputStream = CompressedStream;
                 }
                 }
 
 
-                try
-                {
-                    await WriteResponseToOutputStream(outputStream);
-                }
-                catch (Exception ex)
-                {
-                    Logger.LogException(ex);
-                }
-                finally
-                {
-                    DisposeResponseStream();
-                }
+                await WriteResponseToOutputStream(outputStream);
             }
             }
             else
             else
             {
             {
                 ctx.Response.SendChunked = false;
                 ctx.Response.SendChunked = false;
-                DisposeResponseStream();
             }
             }
         }
         }
 
 
@@ -317,9 +320,16 @@ namespace MediaBrowser.Common.Net.Handlers
             response.Headers[HttpResponseHeader.LastModified] = lastModified.ToString("r");
             response.Headers[HttpResponseHeader.LastModified] = lastModified.ToString("r");
         }
         }
 
 
+        /// <summary>
+        /// Gives subclasses a chance to do and prep work, and also to validate data and set an error status code, if needed
+        /// </summary>
+        protected virtual void PrepareResponse()
+        {
+        }
+
         protected abstract Task WriteResponseToOutputStream(Stream stream);
         protected abstract Task WriteResponseToOutputStream(Stream stream);
 
 
-        private void DisposeResponseStream()
+        protected virtual void DisposeResponseStream()
         {
         {
             if (CompressedStream != null)
             if (CompressedStream != null)
             {
             {
@@ -366,5 +376,13 @@ namespace MediaBrowser.Common.Net.Handlers
         {
         {
             return null;
             return null;
         }
         }
+
+        private bool IsResponseValid
+        {
+            get
+            {
+                return StatusCode == 200 || StatusCode == 206;
+            }
+        }
     }
     }
 }
 }

+ 49 - 2
MediaBrowser.Common/Net/Handlers/BaseJsonHandler.cs

@@ -1,11 +1,58 @@
-
+using System.IO;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Serialization;
+
 namespace MediaBrowser.Common.Net.Handlers
 namespace MediaBrowser.Common.Net.Handlers
 {
 {
     public abstract class BaseJsonHandler : BaseHandler
     public abstract class BaseJsonHandler : BaseHandler
     {
     {
         public override string ContentType
         public override string ContentType
         {
         {
-            get { return "application/json"; }
+            get { return MimeTypes.JsonMimeType; }
+        }
+
+        private bool _ObjectToSerializeEnsured = false;
+        private object _ObjectToSerialize;
+     
+        private void EnsureObjectToSerialize()
+        {
+            if (!_ObjectToSerializeEnsured)
+            {
+                _ObjectToSerialize = GetObjectToSerialize();
+
+                if (_ObjectToSerialize == null)
+                {
+                    StatusCode = 404;
+                }
+
+                _ObjectToSerializeEnsured = true;
+            }
+        }
+
+        private object ObjectToSerialize
+        {
+            get
+            {
+                EnsureObjectToSerialize();
+                return _ObjectToSerialize;
+            }
+        }
+
+        protected abstract object GetObjectToSerialize();
+
+        protected override void PrepareResponse()
+        {
+            base.PrepareResponse();
+
+            EnsureObjectToSerialize();
+        }
+
+        protected override Task WriteResponseToOutputStream(Stream stream)
+        {
+            return Task.Run(() =>
+            {
+                JsonSerializer.SerializeToStream(ObjectToSerialize, stream);
+            });
         }
         }
     }
     }
 }
 }

+ 79 - 79
MediaBrowser.Common/Net/Handlers/StaticFileHandler.cs

@@ -28,37 +28,44 @@ namespace MediaBrowser.Common.Net.Handlers
             }
             }
         }
         }
 
 
-        private bool FileStreamDiscovered = false;
-        private FileStream _FileStream = null;
-        private FileStream FileStream
+        private bool _SourceStreamEnsured = false;
+        private Stream _SourceStream = null;
+        private Stream SourceStream
         {
         {
             get
             get
             {
             {
-                if (!FileStreamDiscovered)
+                EnsureSourceStream();
+                return _SourceStream;
+            }
+        }
+
+        private void EnsureSourceStream()
+        {
+            if (!_SourceStreamEnsured)
+            {
+                try
                 {
                 {
-                    try
-                    {
-                        _FileStream = File.OpenRead(Path);
-                    }
-                    catch (FileNotFoundException)
-                    {
-                        StatusCode = 404;
-                    }
-                    catch (DirectoryNotFoundException)
-                    {
-                        StatusCode = 404;
-                    }
-                    catch (UnauthorizedAccessException)
-                    {
-                        StatusCode = 403;
-                    }
-                    finally
-                    {
-                        FileStreamDiscovered = true;
-                    }
+                    _SourceStream = File.OpenRead(Path);
+                }
+                catch (FileNotFoundException ex)
+                {
+                    StatusCode = 404;
+                    Logger.LogException(ex);
+                }
+                catch (DirectoryNotFoundException ex)
+                {
+                    StatusCode = 404;
+                    Logger.LogException(ex);
+                }
+                catch (UnauthorizedAccessException ex)
+                {
+                    StatusCode = 403;
+                    Logger.LogException(ex);
+                }
+                finally
+                {
+                    _SourceStreamEnsured = true;
                 }
                 }
-
-                return _FileStream;
             }
             }
         }
         }
 
 
@@ -74,14 +81,14 @@ namespace MediaBrowser.Common.Net.Handlers
         {
         {
             get
             get
             {
             {
-                string contentType = ContentType;
-
                 // Can't compress these
                 // Can't compress these
                 if (IsRangeRequest)
                 if (IsRangeRequest)
                 {
                 {
                     return false;
                     return false;
                 }
                 }
 
 
+                string contentType = ContentType;
+
                 // Don't compress media
                 // Don't compress media
                 if (contentType.StartsWith("audio/", StringComparison.OrdinalIgnoreCase) || contentType.StartsWith("video/", StringComparison.OrdinalIgnoreCase))
                 if (contentType.StartsWith("audio/", StringComparison.OrdinalIgnoreCase) || contentType.StartsWith("video/", StringComparison.OrdinalIgnoreCase))
                 {
                 {
@@ -95,26 +102,19 @@ namespace MediaBrowser.Common.Net.Handlers
 
 
         protected override long? GetTotalContentLength()
         protected override long? GetTotalContentLength()
         {
         {
-            try
-            {
-                return FileStream.Length;
-            }
-            catch
-            {
-                return base.GetTotalContentLength();
-            }
+            return SourceStream.Length;
         }
         }
 
 
         protected override DateTime? GetLastDateModified()
         protected override DateTime? GetLastDateModified()
         {
         {
-            try
-            {
-                return File.GetLastWriteTime(Path);
-            }
-            catch
+            EnsureSourceStream();
+
+            if (SourceStream == null)
             {
             {
-                return base.GetLastDateModified();
+                return null;
             }
             }
+
+            return File.GetLastWriteTime(Path);
         }
         }
 
 
         public override string ContentType
         public override string ContentType
@@ -125,48 +125,48 @@ namespace MediaBrowser.Common.Net.Handlers
             }
             }
         }
         }
 
 
+        protected override void PrepareResponse()
+        {
+            base.PrepareResponse();
+
+            EnsureSourceStream();
+        }
+
         protected async override Task WriteResponseToOutputStream(Stream stream)
         protected async override Task WriteResponseToOutputStream(Stream stream)
         {
         {
-            try
+            if (IsRangeRequest)
             {
             {
-                if (FileStream != null)
+                KeyValuePair<long, long?> requestedRange = RequestedRanges.First();
+
+                // If the requested range is "0-" and we know the total length, we can optimize by avoiding having to buffer the content into memory
+                if (requestedRange.Value == null && TotalContentLength != null)
                 {
                 {
-                    if (IsRangeRequest)
-                    {
-                        KeyValuePair<long, long?> requestedRange = RequestedRanges.First();
-
-                        // If the requested range is "0-" and we know the total length, we can optimize by avoiding having to buffer the content into memory
-                        if (requestedRange.Value == null && TotalContentLength != null)
-                        {
-                            await ServeCompleteRangeRequest(requestedRange, stream);
-                        }
-                        else if (TotalContentLength.HasValue)
-                        {
-                            // This will have to buffer a portion of the content into memory
-                            await ServePartialRangeRequestWithKnownTotalContentLength(requestedRange, stream);
-                        }
-                        else
-                        {
-                            // This will have to buffer the entire content into memory
-                            await ServePartialRangeRequestWithUnknownTotalContentLength(requestedRange, stream);
-                        }
-                    }
-                    else
-                    {
-                        await FileStream.CopyToAsync(stream);
-                    }
+                    await ServeCompleteRangeRequest(requestedRange, stream);
+                }
+                else if (TotalContentLength.HasValue)
+                {
+                    // This will have to buffer a portion of the content into memory
+                    await ServePartialRangeRequestWithKnownTotalContentLength(requestedRange, stream);
+                }
+                else
+                {
+                    // This will have to buffer the entire content into memory
+                    await ServePartialRangeRequestWithUnknownTotalContentLength(requestedRange, stream);
                 }
                 }
             }
             }
-            catch (Exception ex)
+            else
             {
             {
-                Logger.LogException("WriteResponseToOutputStream", ex);
+                await SourceStream.CopyToAsync(stream);
             }
             }
-            finally
+        }
+
+        protected override void DisposeResponseStream()
+        {
+            base.DisposeResponseStream();
+
+            if (SourceStream != null)
             {
             {
-                if (FileStream != null)
-                {
-                    FileStream.Dispose();
-                }
+                SourceStream.Dispose();
             }
             }
         }
         }
 
 
@@ -188,10 +188,10 @@ namespace MediaBrowser.Common.Net.Handlers
 
 
             if (rangeStart > 0)
             if (rangeStart > 0)
             {
             {
-                FileStream.Position = rangeStart;
+                SourceStream.Position = rangeStart;
             }
             }
 
 
-            await FileStream.CopyToAsync(responseStream);
+            await SourceStream.CopyToAsync(responseStream);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -200,7 +200,7 @@ namespace MediaBrowser.Common.Net.Handlers
         private async Task ServePartialRangeRequestWithUnknownTotalContentLength(KeyValuePair<long, long?> requestedRange, Stream responseStream)
         private async Task ServePartialRangeRequestWithUnknownTotalContentLength(KeyValuePair<long, long?> requestedRange, Stream responseStream)
         {
         {
             // Read the entire stream so that we can determine the length
             // Read the entire stream so that we can determine the length
-            byte[] bytes = await ReadBytes(FileStream, 0, null);
+            byte[] bytes = await ReadBytes(SourceStream, 0, null);
 
 
             long totalContentLength = bytes.LongLength;
             long totalContentLength = bytes.LongLength;
 
 
@@ -226,7 +226,7 @@ namespace MediaBrowser.Common.Net.Handlers
             long rangeLength = 1 + rangeEnd - rangeStart;
             long rangeLength = 1 + rangeEnd - rangeStart;
 
 
             // Only read the bytes we need
             // Only read the bytes we need
-            byte[] bytes = await ReadBytes(FileStream, Convert.ToInt32(rangeStart), Convert.ToInt32(rangeLength));
+            byte[] bytes = await ReadBytes(SourceStream, Convert.ToInt32(rangeStart), Convert.ToInt32(rangeLength));
 
 
             // Content-Length is the length of what we're serving, not the original content
             // Content-Length is the length of what we're serving, not the original content
             HttpListenerContext.Response.ContentLength64 = rangeLength;
             HttpListenerContext.Response.ContentLength64 = rangeLength;

+ 2 - 0
MediaBrowser.Common/Net/MimeTypes.cs

@@ -5,6 +5,8 @@ namespace MediaBrowser.Common.Net
 {
 {
     public static class MimeTypes
     public static class MimeTypes
     {
     {
+        public static string JsonMimeType = "application/json";
+
         public static string GetMimeType(string path)
         public static string GetMimeType(string path)
         {
         {
             string ext = Path.GetExtension(path);
             string ext = Path.GetExtension(path);

+ 27 - 1
MediaBrowser.Controller/Xml/BaseItemXmlParser.cs

@@ -10,7 +10,7 @@ namespace MediaBrowser.Controller.Xml
     /// <summary>
     /// <summary>
     /// Provides a base class for parsing metadata xml
     /// Provides a base class for parsing metadata xml
     /// </summary>
     /// </summary>
-    public abstract class BaseItemXmlParser<T>
+    public class BaseItemXmlParser<T>
         where T : BaseItem, new()
         where T : BaseItem, new()
     {
     {
         /// <summary>
         /// <summary>
@@ -215,6 +215,32 @@ namespace MediaBrowser.Controller.Xml
                         break;
                         break;
                     }
                     }
 
 
+                case "TMDbId":
+                    string tmdb = reader.ReadString();
+                    if (!string.IsNullOrWhiteSpace(tmdb))
+                    {
+                        item.SetProviderId(MetadataProviders.Tmdb, tmdb);
+                    }
+                    break;
+
+                case "TVcomId":
+                    string TVcomId = reader.ReadString();
+                    if (!string.IsNullOrWhiteSpace(TVcomId))
+                    {
+                        item.SetProviderId(MetadataProviders.Tvcom, TVcomId);
+                    }
+                    break;
+
+                case "IMDB_ID":
+                case "IMDB":
+                case "IMDbId":
+                    string IMDbId = reader.ReadString();
+                    if (!string.IsNullOrWhiteSpace(IMDbId))
+                    {
+                        item.SetProviderId(MetadataProviders.Imdb, IMDbId);
+                    }
+                    break;
+
                 case "Genres":
                 case "Genres":
                     FetchFromGenresNode(reader.ReadSubtree(), item);
                     FetchFromGenresNode(reader.ReadSubtree(), item);
                     break;
                     break;

+ 2 - 5
MediaBrowser.Model/Entities/ApiBaseItem.cs

@@ -11,14 +11,9 @@ namespace MediaBrowser.Model.Entities
     public class ApiBaseItem : BaseItem
     public class ApiBaseItem : BaseItem
     {
     {
         // TV Series
         // TV Series
-        public string TvdbId { get; set; }
         public string Status { get; set; }
         public string Status { get; set; }
         public IEnumerable<DayOfWeek> AirDays { get; set; }
         public IEnumerable<DayOfWeek> AirDays { get; set; }
         public string AirTime { get; set; }
         public string AirTime { get; set; }
-
-        // Movie
-        public string TmdbId { get; set; }
-        public string ImdbId { get; set; }
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -49,6 +44,8 @@ namespace MediaBrowser.Model.Entities
             return Type.Equals(type, StringComparison.OrdinalIgnoreCase);
             return Type.Equals(type, StringComparison.OrdinalIgnoreCase);
         }
         }
 
 
+        public IEnumerable<PersonInfo> People { get; set; }
+
         /// <summary>
         /// <summary>
         /// If the item does not have a logo, this will hold the Id of the Parent that has one.
         /// If the item does not have a logo, this will hold the Id of the Parent that has one.
         /// </summary>
         /// </summary>

+ 45 - 0
MediaBrowser.Model/Entities/BaseItem.cs

@@ -33,6 +33,7 @@ namespace MediaBrowser.Model.Entities
         public string Overview { get; set; }
         public string Overview { get; set; }
         public string Tagline { get; set; }
         public string Tagline { get; set; }
 
 
+        [IgnoreDataMember]
         public IEnumerable<PersonInfo> People { get; set; }
         public IEnumerable<PersonInfo> People { get; set; }
 
 
         public IEnumerable<string> Studios { get; set; }
         public IEnumerable<string> Studios { get; set; }
@@ -56,5 +57,49 @@ namespace MediaBrowser.Model.Entities
         public IEnumerable<Video> LocalTrailers { get; set; }
         public IEnumerable<Video> LocalTrailers { get; set; }
 
 
         public string TrailerUrl { get; set; }
         public string TrailerUrl { get; set; }
+
+        public Dictionary<string, string> ProviderIds { get; set; }
+
+        /// <summary>
+        /// Gets a provider id
+        /// </summary>
+        public string GetProviderId(MetadataProviders provider)
+        {
+            return GetProviderId(provider.ToString());
+        }
+
+        /// <summary>
+        /// Gets a provider id
+        /// </summary>
+        public string GetProviderId(string name)
+        {
+            if (ProviderIds == null)
+            {
+                return null;
+            }
+
+            return ProviderIds[name];
+        }
+
+        /// <summary>
+        /// Sets a provider id
+        /// </summary>
+        public void SetProviderId(string name, string value)
+        {
+            if (ProviderIds == null)
+            {
+                ProviderIds = new Dictionary<string, string>();
+            }
+
+            ProviderIds[name] = value;
+        }
+
+        /// <summary>
+        /// Sets a provider id
+        /// </summary>
+        public void SetProviderId(MetadataProviders provider, string value)
+        {
+            SetProviderId(provider.ToString(), value);
+        }
     }
     }
 }
 }

+ 11 - 0
MediaBrowser.Model/Entities/MetadataProviders.cs

@@ -0,0 +1,11 @@
+
+namespace MediaBrowser.Model.Entities
+{
+    public enum MetadataProviders
+    {
+        Imdb,
+        Tmdb,
+        Tvdb,
+        Tvcom
+    }
+}

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

@@ -41,6 +41,7 @@
     <Compile Include="Entities\Folder.cs" />
     <Compile Include="Entities\Folder.cs" />
     <Compile Include="Entities\Genre.cs" />
     <Compile Include="Entities\Genre.cs" />
     <Compile Include="Entities\ImageType.cs" />
     <Compile Include="Entities\ImageType.cs" />
+    <Compile Include="Entities\MetadataProviders.cs" />
     <Compile Include="Entities\Person.cs" />
     <Compile Include="Entities\Person.cs" />
     <Compile Include="Entities\Studio.cs" />
     <Compile Include="Entities\Studio.cs" />
     <Compile Include="Entities\Video.cs" />
     <Compile Include="Entities\Video.cs" />

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

@@ -45,7 +45,6 @@
     <Compile Include="Resolvers\BoxSetResolver.cs" />
     <Compile Include="Resolvers\BoxSetResolver.cs" />
     <Compile Include="Entities\Movie.cs" />
     <Compile Include="Entities\Movie.cs" />
     <Compile Include="Resolvers\MovieResolver.cs" />
     <Compile Include="Resolvers\MovieResolver.cs" />
-    <Compile Include="Metadata\MovieXmlParser.cs" />
     <Compile Include="Plugin.cs" />
     <Compile Include="Plugin.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
   </ItemGroup>

+ 0 - 32
MediaBrowser.Movies/Metadata/MovieXmlParser.cs

@@ -1,32 +0,0 @@
-using System.Xml;
-using MediaBrowser.Controller.Xml;
-using MediaBrowser.Movies.Entities;
-
-namespace MediaBrowser.Movies.Metadata
-{
-    public class MovieXmlParser : BaseItemXmlParser<Movie>
-    {
-        protected override void FetchDataFromXmlNode(XmlReader reader, Movie item)
-        {
-            switch (reader.Name)
-            {
-                case "TMDbId":
-                    item.TmdbId = reader.ReadString();
-                    break;
-
-                case "IMDB":
-                case "IMDbId":
-                    string IMDbId = reader.ReadString();
-                    if (!string.IsNullOrWhiteSpace(IMDbId))
-                    {
-                        item.ImdbId = IMDbId;
-                    }
-                    break;
-
-                default:
-                    base.FetchDataFromXmlNode(reader, item);
-                    break;
-            }
-        }
-    }
-}

+ 2 - 2
MediaBrowser.Movies/Resolvers/MovieResolver.cs

@@ -6,9 +6,9 @@ using System.Linq;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Events;
 using MediaBrowser.Controller.Events;
 using MediaBrowser.Controller.Resolvers;
 using MediaBrowser.Controller.Resolvers;
+using MediaBrowser.Controller.Xml;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Movies.Entities;
 using MediaBrowser.Movies.Entities;
-using MediaBrowser.Movies.Metadata;
 
 
 namespace MediaBrowser.Movies.Resolvers
 namespace MediaBrowser.Movies.Resolvers
 {
 {
@@ -97,7 +97,7 @@ namespace MediaBrowser.Movies.Resolvers
 
 
             if (metadataFile.HasValue)
             if (metadataFile.HasValue)
             {
             {
-                new MovieXmlParser().Fetch(item, metadataFile.Value.Key);
+                new BaseItemXmlParser<Movie>().Fetch(item, metadataFile.Value.Key);
             }
             }
 
 
             PopulateBonusFeatures(item, args);
             PopulateBonusFeatures(item, args);

+ 2 - 4
MediaBrowser.TV/Entities/Series.cs

@@ -1,13 +1,11 @@
-using MediaBrowser.Model.Entities;
-using System;
-using System.Linq;
+using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using MediaBrowser.Model.Entities;
 
 
 namespace MediaBrowser.TV.Entities
 namespace MediaBrowser.TV.Entities
 {
 {
     public class Series : Folder
     public class Series : Folder
     {
     {
-        public string TvdbId { get; set; }
         public string Status { get; set; }
         public string Status { get; set; }
         public IEnumerable<DayOfWeek> AirDays { get; set; }
         public IEnumerable<DayOfWeek> AirDays { get; set; }
         public string AirTime { get; set; }
         public string AirTime { get; set; }

+ 1 - 2
MediaBrowser.TV/Metadata/EpisodeXmlParser.cs

@@ -1,5 +1,4 @@
-using System;
-using System.IO;
+using System.IO;
 using System.Xml;
 using System.Xml;
 using MediaBrowser.Controller.Xml;
 using MediaBrowser.Controller.Xml;
 using MediaBrowser.TV.Entities;
 using MediaBrowser.TV.Entities;

+ 6 - 1
MediaBrowser.TV/Metadata/SeriesXmlParser.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Xml;
 using System.Xml;
 using MediaBrowser.Controller.Xml;
 using MediaBrowser.Controller.Xml;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.TV.Entities;
 using MediaBrowser.TV.Entities;
 
 
 namespace MediaBrowser.TV.Metadata
 namespace MediaBrowser.TV.Metadata
@@ -12,7 +13,11 @@ namespace MediaBrowser.TV.Metadata
             switch (reader.Name)
             switch (reader.Name)
             {
             {
                 case "id":
                 case "id":
-                    item.TvdbId = reader.ReadString();
+                    string id = reader.ReadString();
+                    if (!string.IsNullOrWhiteSpace(id))
+                    {
+                        item.SetProviderId(MetadataProviders.Tvdb, id);
+                    }
                     break;
                     break;
 
 
                 case "Airs_DayOfWeek":
                 case "Airs_DayOfWeek":