Przeglądaj źródła

Moved the http server to it's own assembly. added comments and made other minor re-organizations.

LukePulverenti Luke Pulverenti luke pulverenti 13 lat temu
rodzic
commit
80b3ad7bd2
67 zmienionych plików z 804 dodań i 962 usunięć
  1. 20 71
      MediaBrowser.Api/ApiService.cs
  2. 0 6
      MediaBrowser.Api/HttpHandlers/GenreHandler.cs
  3. 2 8
      MediaBrowser.Api/HttpHandlers/GenresHandler.cs
  4. 4 20
      MediaBrowser.Api/HttpHandlers/ImageHandler.cs
  5. 0 6
      MediaBrowser.Api/HttpHandlers/InProgressItemsHandler.cs
  6. 2 31
      MediaBrowser.Api/HttpHandlers/ItemHandler.cs
  7. 2 8
      MediaBrowser.Api/HttpHandlers/ItemListHandler.cs
  8. 16 0
      MediaBrowser.Api/HttpHandlers/JsonHandler.cs
  9. 11 15
      MediaBrowser.Api/HttpHandlers/MediaHandler.cs
  10. 1 7
      MediaBrowser.Api/HttpHandlers/PersonHandler.cs
  11. 0 6
      MediaBrowser.Api/HttpHandlers/RecentlyAddedItemsHandler.cs
  12. 0 6
      MediaBrowser.Api/HttpHandlers/StudioHandler.cs
  13. 3 8
      MediaBrowser.Api/HttpHandlers/StudiosHandler.cs
  14. 1 7
      MediaBrowser.Api/HttpHandlers/UsersHandler.cs
  15. 6 1
      MediaBrowser.Api/MediaBrowser.Api.csproj
  16. 0 15
      MediaBrowser.Api/Model/BaseItemInfo.cs
  17. 60 13
      MediaBrowser.Api/Plugin.cs
  18. 46 31
      MediaBrowser.Common/ApiInteraction/ApiController.cs
  19. 0 412
      MediaBrowser.Common/ApiInteraction/DictionaryBaseItem.cs
  20. 6 6
      MediaBrowser.Common/Json/JsonSerializer.cs
  21. 0 4
      MediaBrowser.Common/Logging/Logger.cs
  22. 0 12
      MediaBrowser.Common/MediaBrowser.Common.csproj
  23. 0 36
      MediaBrowser.Common/Net/Handlers/JsonHandler.cs
  24. 0 77
      MediaBrowser.Common/Net/Response.cs
  25. 1 5
      MediaBrowser.Common/Plugins/BasePlugin.cs
  26. 1 6
      MediaBrowser.Common/Plugins/BasePluginConfiguration.cs
  27. 0 1
      MediaBrowser.Common/packages.config
  28. 9 2
      MediaBrowser.Controller/Events/ItemResolveEventArgs.cs
  29. 2 2
      MediaBrowser.Controller/IO/DirectoryWatchers.cs
  30. 3 0
      MediaBrowser.Controller/IO/Shortcut.cs
  31. 121 5
      MediaBrowser.Controller/Kernel.cs
  32. 6 0
      MediaBrowser.Controller/Library/ItemController.cs
  33. 4 0
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  34. 2 0
      MediaBrowser.Controller/Resolvers/AudioResolver.cs
  35. 19 0
      MediaBrowser.Controller/Resolvers/BaseItemResolver.cs
  36. 1 0
      MediaBrowser.Controller/Resolvers/FolderResolver.cs
  37. 11 3
      MediaBrowser.Controller/Resolvers/VideoResolver.cs
  38. 29 6
      MediaBrowser.Controller/UserController.cs
  39. 22 8
      MediaBrowser.Controller/Xml/BaseItemXmlParser.cs
  40. 4 0
      MediaBrowser.Controller/Xml/FolderXmlParser.cs
  41. 9 4
      MediaBrowser.Controller/Xml/XmlExtensions.cs
  42. 4 4
      MediaBrowser.HtmlBrowser/Handlers/EmbeddedResourceHandler.cs
  43. 4 0
      MediaBrowser.HtmlBrowser/MediaBrowser.HtmlBrowser.csproj
  44. 3 3
      MediaBrowser.HtmlBrowser/Plugin.cs
  45. 39 0
      MediaBrowser.Model/Entities/ApiBaseItem.cs
  46. 39 39
      MediaBrowser.Model/Entities/BaseItem.cs
  47. 20 24
      MediaBrowser.Model/Entities/Folder.cs
  48. 1 0
      MediaBrowser.Model/MediaBrowser.Model.csproj
  49. 0 1
      MediaBrowser.Model/Users/User.cs
  50. 1 1
      MediaBrowser.Movies/Entities/Movie.cs
  51. 1 1
      MediaBrowser.Movies/Metadata/MovieXmlParser.cs
  52. 1 1
      MediaBrowser.Net/CollectionExtensions.cs
  53. 8 20
      MediaBrowser.Net/Handlers/BaseEmbeddedResourceHandler.cs
  54. 111 0
      MediaBrowser.Net/Handlers/BaseHandler.cs
  55. 11 0
      MediaBrowser.Net/Handlers/BaseJsonHandler.cs
  56. 1 1
      MediaBrowser.Net/HttpServer.cs
  57. 66 0
      MediaBrowser.Net/MediaBrowser.Net.csproj
  58. 36 0
      MediaBrowser.Net/Properties/AssemblyInfo.cs
  59. 1 1
      MediaBrowser.Net/Request.cs
  60. 8 2
      MediaBrowser.Net/RequestContext.cs
  61. 1 1
      MediaBrowser.Net/StreamExtensions.cs
  62. 4 0
      MediaBrowser.Net/packages.config
  63. 1 5
      MediaBrowser.TV/Entities/Episode.cs
  64. 2 4
      MediaBrowser.TV/Entities/Series.cs
  65. 9 4
      MediaBrowser.TV/Metadata/EpisodeXmlParser.cs
  66. 2 2
      MediaBrowser.TV/Metadata/SeriesXmlParser.cs
  67. 6 0
      MediaBrowser.sln

+ 20 - 71
MediaBrowser.Api/ApiService.cs

@@ -1,10 +1,16 @@
 using System;
 using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Users;
 
 namespace MediaBrowser.Api
 {
+    /// <summary>
+    /// Contains some helpers for the api
+    /// </summary>
     public static class ApiService
     {
         public static BaseItem GetItemById(string id)
@@ -14,86 +20,29 @@ namespace MediaBrowser.Api
             return Kernel.Instance.GetItemById(guid);
         }
 
-        public static IEnumerable<CategoryInfo> GetAllStudios(Folder parent, Guid userId)
+        /// <summary>
+        /// Takes a BaseItem and returns the actual object that will be serialized by the api
+        /// </summary>
+        public static ApiBaseItemWrapper<BaseItem> GetSerializationObject(BaseItem item, bool includeChildren, Guid userId)
         {
-            Dictionary<string, int> data = new Dictionary<string, int>();
-            
-            IEnumerable<BaseItem> allItems = Kernel.Instance.GetParentalAllowedRecursiveChildren(parent, userId);
-
-            foreach (var item in allItems)
+            ApiBaseItemWrapper<BaseItem> wrapper = new ApiBaseItemWrapper<BaseItem>()
             {
-                if (item.Studios == null)
-                {
-                    continue;
-                }
-
-                foreach (string val in item.Studios)
-                {
-                    if (!data.ContainsKey(val))
-                    {
-                        data.Add(val, 1);
-                    }
-                    else
-                    {
-                        data[val]++;
-                    }
-                }
-            }
+                Item = item,
+                UserItemData = Kernel.Instance.GetUserItemData(userId, item.Id),
+                ItemType = item.GetType()
+            };
 
-            List<CategoryInfo> list = new List<CategoryInfo>();
-
-            foreach (string key in data.Keys)
+            if (includeChildren)
             {
-                list.Add(new CategoryInfo()
-                {
-                    Name = key,
-                    ItemCount = data[key]
-
-                });
-            }
-            
-            return list;
-        }
+                var folder = item as Folder;
 
-        public static IEnumerable<CategoryInfo> GetAllGenres(Folder parent, Guid userId)
-        {
-            Dictionary<string, int> data = new Dictionary<string, int>();
-
-            IEnumerable<BaseItem> allItems = Kernel.Instance.GetParentalAllowedRecursiveChildren(parent, userId);
-
-            foreach (var item in allItems)
-            {
-                if (item.Genres == null)
-                {
-                    continue;
-                }
-
-                foreach (string val in item.Genres)
+                if (folder != null)
                 {
-                    if (!data.ContainsKey(val))
-                    {
-                        data.Add(val, 1);
-                    }
-                    else
-                    {
-                        data[val]++;
-                    }
+                    wrapper.Children = Kernel.Instance.GetParentalAllowedChildren(folder, userId).Select(c => GetSerializationObject(c, false, userId));
                 }
             }
 
-            List<CategoryInfo> list = new List<CategoryInfo>();
-
-            foreach (string key in data.Keys)
-            {
-                list.Add(new CategoryInfo()
-                {
-                    Name = key,
-                    ItemCount = data[key]
-
-                });
-            }
-
-            return list;
+            return wrapper;
         }
     }
 }

+ 0 - 6
MediaBrowser.Api/HttpHandlers/GenreHandler.cs

@@ -1,5 +1,4 @@
 using System.Collections.Generic;
-using MediaBrowser.Common.Net;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.Entities;
 
@@ -10,11 +9,6 @@ namespace MediaBrowser.Api.HttpHandlers
     /// </summary>
     public class GenreHandler : ItemListHandler
     {
-        public GenreHandler(RequestContext ctx)
-            : base(ctx)
-        {
-        }
-
         protected override IEnumerable<BaseItem> ItemsToSerialize
         {
             get

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

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

+ 4 - 20
MediaBrowser.Api/HttpHandlers/ImageHandler.cs

@@ -1,27 +1,14 @@
 using System;
 using System.IO;
-using System.IO.Compression;
 using System.Linq;
-using MediaBrowser.Common.Net;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.Entities;
+using MediaBrowser.Net.Handlers;
 
 namespace MediaBrowser.Api.HttpHandlers
 {
-    public class ImageHandler : Response
+    public class ImageHandler : BaseHandler
     {
-        public ImageHandler(RequestContext ctx)
-            : base(ctx)
-        {
-            Headers["Content-Encoding"] = "gzip";
-
-            WriteStream = s =>
-            {
-                WriteReponse(s);
-                s.Close();
-            };
-        }
-
         private string _ImagePath = string.Empty;
         private string ImagePath
         {
@@ -149,12 +136,9 @@ namespace MediaBrowser.Api.HttpHandlers
             }
         }
 
-        private void WriteReponse(Stream stream)
+        protected override void WriteResponseToOutputStream(Stream stream)
         {
-            using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Compress, false))
-            {
-                ImageProcessor.ProcessImage(ImagePath, gzipStream, Width, Height, MaxWidth, MaxHeight, Quality);
-            }
+            ImageProcessor.ProcessImage(ImagePath, stream, Width, Height, MaxWidth, MaxHeight, Quality);
         }
 
         private string GetImagePath()

+ 0 - 6
MediaBrowser.Api/HttpHandlers/InProgressItemsHandler.cs

@@ -1,5 +1,4 @@
 using System.Collections.Generic;
-using MediaBrowser.Common.Net;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.Entities;
 
@@ -7,11 +6,6 @@ namespace MediaBrowser.Api.HttpHandlers
 {
     class InProgressItemsHandler : ItemListHandler
     {
-        public InProgressItemsHandler(RequestContext ctx)
-            : base(ctx)
-        {
-        }
-
         protected override IEnumerable<BaseItem> ItemsToSerialize
         {
             get

+ 2 - 31
MediaBrowser.Api/HttpHandlers/ItemHandler.cs

@@ -1,48 +1,19 @@
 using System;
-using MediaBrowser.Api.Model;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Common.Net.Handlers;
-using MediaBrowser.Controller;
+using MediaBrowser.Net.Handlers;
 using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.Api.HttpHandlers
 {
     public class ItemHandler : JsonHandler
     {
-        public ItemHandler(RequestContext ctx)
-            : base(ctx)
-        {
-        }
-
         protected sealed override object ObjectToSerialize
         {
             get
             {
                 Guid userId = Guid.Parse(QueryString["userid"]);
 
-                return GetSerializationObject(ItemToSerialize, true, userId);
-            }
-        }
-
-        public static object GetSerializationObject(BaseItem item, bool includeChildren, Guid userId)
-        {
-            BaseItemInfo wrapper = new BaseItemInfo()
-            {
-                Item = item,
-                UserItemData = Kernel.Instance.GetUserItemData(userId, item.Id)
-            };
-
-            if (includeChildren)
-            {
-                var folder = item as Folder;
-
-                if (folder != null)
-                {
-                    wrapper.Children = Kernel.Instance.GetParentalAllowedChildren(folder, userId);
-                }
+                return ApiService.GetSerializationObject(ItemToSerialize, true, userId);
             }
-
-            return wrapper;
         }
 
         protected virtual BaseItem ItemToSerialize

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

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

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

@@ -0,0 +1,16 @@
+using System.IO;
+using MediaBrowser.Common.Json;
+using MediaBrowser.Net.Handlers;
+
+namespace MediaBrowser.Api.HttpHandlers
+{
+    public abstract class JsonHandler : BaseJsonHandler
+    {
+        protected abstract object ObjectToSerialize { get; }
+
+        protected override void WriteResponseToOutputStream(Stream stream)
+        {
+            JsonSerializer.SerializeToStream(ObjectToSerialize, stream);
+        }
+    }
+}

+ 11 - 15
MediaBrowser.Api/HttpHandlers/MediaHandler.cs

@@ -1,23 +1,12 @@
 using System;
 using System.IO;
-using System.IO.Compression;
-using MediaBrowser.Common.Net;
 using MediaBrowser.Model.Entities;
+using MediaBrowser.Net.Handlers;
 
 namespace MediaBrowser.Api.HttpHandlers
 {
-    class MediaHandler : Response
+    class MediaHandler : BaseHandler
     {
-        public MediaHandler(RequestContext ctx)
-            : base(ctx)
-        {
-            WriteStream = s =>
-            {
-                WriteReponse(s);
-                s.Close();
-            };
-        }
-
         private string _MediaPath = string.Empty;
         private string MediaPath
         {
@@ -46,6 +35,14 @@ namespace MediaBrowser.Api.HttpHandlers
             return item.Path;
         }
 
+        public override bool GzipResponse
+        {
+            get
+            {
+                return false;
+            }
+        }
+
         public override string ContentType
         {
             get
@@ -87,7 +84,7 @@ namespace MediaBrowser.Api.HttpHandlers
             }
         }
 
-        private void WriteReponse(Stream stream)
+        protected override void WriteResponseToOutputStream(Stream stream)
         {
             try
             {
@@ -100,6 +97,5 @@ namespace MediaBrowser.Api.HttpHandlers
             {
             }
         }
-
     }
 }

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

@@ -1,16 +1,10 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
+using MediaBrowser.Controller;
 using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.Api.HttpHandlers
 {
     public class PersonHandler : ItemHandler
     {
-        public PersonHandler(RequestContext ctx)
-            : base(ctx)
-        {
-        }
-
         protected override BaseItem ItemToSerialize
         {
             get

+ 0 - 6
MediaBrowser.Api/HttpHandlers/RecentlyAddedItemsHandler.cs

@@ -1,5 +1,4 @@
 using System.Collections.Generic;
-using MediaBrowser.Common.Net;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.Entities;
 
@@ -7,11 +6,6 @@ namespace MediaBrowser.Api.HttpHandlers
 {
     class RecentlyAddedItemsHandler : ItemListHandler
     {
-        public RecentlyAddedItemsHandler(RequestContext ctx)
-            : base(ctx)
-        {
-        }
-
         protected override IEnumerable<BaseItem> ItemsToSerialize
         {
             get

+ 0 - 6
MediaBrowser.Api/HttpHandlers/StudioHandler.cs

@@ -1,5 +1,4 @@
 using System.Collections.Generic;
-using MediaBrowser.Common.Net;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.Entities;
 
@@ -10,11 +9,6 @@ namespace MediaBrowser.Api.HttpHandlers
     /// </summary>
     public class StudioHandler : ItemListHandler
     {
-        public StudioHandler(RequestContext ctx)
-            : base(ctx)
-        {
-        }
-
         protected override IEnumerable<BaseItem> ItemsToSerialize
         {
             get

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

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

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

@@ -1,16 +1,10 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Common.Net.Handlers;
+using MediaBrowser.Net.Handlers;
 using MediaBrowser.Controller;
 
 namespace MediaBrowser.Api.HttpHandlers
 {
     class UsersHandler : JsonHandler
     {
-        public UsersHandler(RequestContext ctx)
-            : base(ctx)
-        {
-        }
-
         protected override object ObjectToSerialize
         {
             get

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

@@ -37,6 +37,7 @@
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\packages\Rx-Main.1.0.11226\lib\Net4\System.Reactive.dll</HintPath>
     </Reference>
+    <Reference Include="System.Runtime.Serialization" />
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />
@@ -51,6 +52,7 @@
     <Compile Include="HttpHandlers\InProgressItemsHandler.cs" />
     <Compile Include="HttpHandlers\ItemHandler.cs" />
     <Compile Include="HttpHandlers\ItemListHandler.cs" />
+    <Compile Include="HttpHandlers\JsonHandler.cs" />
     <Compile Include="HttpHandlers\PersonHandler.cs" />
     <Compile Include="HttpHandlers\RecentlyAddedItemsHandler.cs" />
     <Compile Include="HttpHandlers\StudioHandler.cs" />
@@ -58,7 +60,6 @@
     <Compile Include="HttpHandlers\UsersHandler.cs" />
     <Compile Include="ImageProcessor.cs" />
     <Compile Include="HttpHandlers\MediaHandler.cs" />
-    <Compile Include="Model\BaseItemInfo.cs" />
     <Compile Include="Plugin.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
@@ -75,6 +76,10 @@
       <Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
       <Name>MediaBrowser.Model</Name>
     </ProjectReference>
+    <ProjectReference Include="..\MediaBrowser.Net\MediaBrowser.Net.csproj">
+      <Project>{5da08d1c-0d52-4b1b-aa66-e4a171d938f6}</Project>
+      <Name>MediaBrowser.Net</Name>
+    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
     <None Include="packages.config" />

+ 0 - 15
MediaBrowser.Api/Model/BaseItemInfo.cs

@@ -1,15 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Users;
-
-namespace MediaBrowser.Api.Model
-{
-    public class BaseItemInfo
-    {
-        public BaseItem Item { get; set; }
-
-        public UserItemData UserItemData { get; set; }
-
-        public IEnumerable<BaseItem> Children { get; set; }
-    }
-}

+ 60 - 13
MediaBrowser.Api/Plugin.cs

@@ -3,6 +3,8 @@ using System.Reactive.Linq;
 using MediaBrowser.Api.HttpHandlers;
 using MediaBrowser.Common.Plugins;
 using MediaBrowser.Controller;
+using MediaBrowser.Net;
+using MediaBrowser.Net.Handlers;
 
 namespace MediaBrowser.Api
 {
@@ -12,25 +14,70 @@ namespace MediaBrowser.Api
         {
             var httpServer = Kernel.Instance.HttpServer;
 
-            httpServer.Where(ctx => ctx.LocalPath.EndsWith("/api/users", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new UsersHandler(ctx)));
+            httpServer.Where(ctx => ctx.LocalPath.IndexOf("/api/", StringComparison.OrdinalIgnoreCase) != -1).Subscribe(ctx =>
+            {
+                BaseHandler handler = GetHandler(ctx);
 
-            httpServer.Where(ctx => ctx.LocalPath.EndsWith("/api/media", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new MediaHandler(ctx)));
-
-            httpServer.Where(ctx => ctx.LocalPath.EndsWith("/api/item", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new ItemHandler(ctx)));
-
-            httpServer.Where(ctx => ctx.LocalPath.EndsWith("/api/image", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new ImageHandler(ctx)));
-
-            httpServer.Where(ctx => ctx.LocalPath.EndsWith("/api/genre", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new GenreHandler(ctx)));
+                if (handler != null)
+                {
+                    ctx.Respond(handler);
+                }
+            });
+        }
 
-            httpServer.Where(ctx => ctx.LocalPath.EndsWith("/api/genres", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new GenresHandler(ctx)));
+        private BaseHandler GetHandler(RequestContext ctx)
+        {
+            BaseHandler handler = null;
 
-            httpServer.Where(ctx => ctx.LocalPath.EndsWith("/api/studio", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new StudioHandler(ctx)));
+            string localPath = ctx.LocalPath;
 
-            httpServer.Where(ctx => ctx.LocalPath.EndsWith("/api/studios", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new StudiosHandler(ctx)));
+            if (localPath.EndsWith("/api/item", StringComparison.OrdinalIgnoreCase))
+            {
+                handler = new ItemHandler();
+            }
+            else if (localPath.EndsWith("/api/image", StringComparison.OrdinalIgnoreCase))
+            {
+                handler = new ImageHandler();
+            } 
+            else if (localPath.EndsWith("/api/users", StringComparison.OrdinalIgnoreCase))
+            {
+                handler = new UsersHandler();
+            }
+            else if (localPath.EndsWith("/api/media", StringComparison.OrdinalIgnoreCase))
+            {
+                handler = new MediaHandler();
+            }
+            else if (localPath.EndsWith("/api/genre", StringComparison.OrdinalIgnoreCase))
+            {
+                handler = new GenreHandler();
+            }
+            else if (localPath.EndsWith("/api/genres", StringComparison.OrdinalIgnoreCase))
+            {
+                handler = new GenresHandler();
+            }
+            else if (localPath.EndsWith("/api/studio", StringComparison.OrdinalIgnoreCase))
+            {
+                handler = new StudioHandler();
+            }
+            else if (localPath.EndsWith("/api/studios", StringComparison.OrdinalIgnoreCase))
+            {
+                handler = new StudiosHandler();
+            }
+            else if (localPath.EndsWith("/api/recentlyaddeditems", StringComparison.OrdinalIgnoreCase))
+            {
+                handler = new RecentlyAddedItemsHandler();
+            }
+            else if (localPath.EndsWith("/api/inprogressitems", StringComparison.OrdinalIgnoreCase))
+            {
+                handler = new InProgressItemsHandler();
+            }
 
-            httpServer.Where(ctx => ctx.LocalPath.EndsWith("/api/recentlyaddeditems", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new RecentlyAddedItemsHandler(ctx)));
+            if (handler != null)
+            {
+                handler.RequestContext = ctx;
+            }
 
-            httpServer.Where(ctx => ctx.LocalPath.EndsWith("/api/inprogressitems", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new InProgressItemsHandler(ctx)));
+            return handler;
         }
     }
 }

+ 46 - 31
MediaBrowser.Common/ApiInteraction/ApiController.cs

@@ -2,11 +2,12 @@
 using System.Collections.Generic;
 using System.IO;
 using System.IO.Compression;
+using System.Linq;
 using System.Net;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Json;
-using MediaBrowser.Model.Users;
 using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Users;
 
 namespace MediaBrowser.Common.ApiInteraction
 {
@@ -21,19 +22,20 @@ namespace MediaBrowser.Common.ApiInteraction
             WebClient = new WebClient();
         }
 
-        public async Task<DictionaryBaseItem> GetRootItem(Guid userId)
+        public async Task<ApiBaseItemWrapper<ApiBaseItem>> GetRootItem(Guid userId)
         {
             string url = ApiUrl + "/item?userId=" + userId.ToString();
 
-            Stream stream = await WebClient.OpenReadTaskAsync(url);
-
-            using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, false))
+            using (Stream stream = await WebClient.OpenReadTaskAsync(url))
             {
-                return DictionaryBaseItem.FromApiOutput(gzipStream);
+                using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, false))
+                {
+                    return DeserializeBaseItemWrapper(gzipStream);
+                }
             }
         }
 
-        public async Task<DictionaryBaseItem> GetItem(Guid id, Guid userId)
+        public async Task<ApiBaseItemWrapper<ApiBaseItem>> GetItem(Guid id, Guid userId)
         {
             string url = ApiUrl + "/item?userId=" + userId.ToString();
 
@@ -42,11 +44,12 @@ namespace MediaBrowser.Common.ApiInteraction
                 url += "&id=" + id.ToString();
             }
 
-            Stream stream = await WebClient.OpenReadTaskAsync(url);
-
-            using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, false))
+            using (Stream stream = await WebClient.OpenReadTaskAsync(url))
             {
-                return DictionaryBaseItem.FromApiOutput(gzipStream);
+                using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, false))
+                {
+                    return DeserializeBaseItemWrapper(gzipStream);
+                }
             }
         }
 
@@ -54,11 +57,12 @@ namespace MediaBrowser.Common.ApiInteraction
         {
             string url = ApiUrl + "/users";
 
-            Stream stream = await WebClient.OpenReadTaskAsync(url);
-
-            using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, false))
+            using (Stream stream = await WebClient.OpenReadTaskAsync(url))
             {
-                return JsonSerializer.DeserializeFromStream<IEnumerable<User>>(gzipStream);
+                using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, false))
+                {
+                    return JsonSerializer.DeserializeFromStream<IEnumerable<User>>(gzipStream);
+                }
             }
         }
 
@@ -66,11 +70,12 @@ namespace MediaBrowser.Common.ApiInteraction
         {
             string url = ApiUrl + "/genres?userId=" + userId.ToString();
 
-            Stream stream = await WebClient.OpenReadTaskAsync(url);
-
-            using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, false))
+            using (Stream stream = await WebClient.OpenReadTaskAsync(url))
             {
-                return JsonSerializer.DeserializeFromStream<IEnumerable<CategoryInfo>>(gzipStream);
+                using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, false))
+                {
+                    return JsonSerializer.DeserializeFromStream<IEnumerable<CategoryInfo>>(gzipStream);
+                }
             }
         }
 
@@ -78,11 +83,12 @@ namespace MediaBrowser.Common.ApiInteraction
         {
             string url = ApiUrl + "/genre?userId=" + userId.ToString() + "&name=" + name;
 
-            Stream stream = await WebClient.OpenReadTaskAsync(url);
-
-            using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, false))
+            using (Stream stream = await WebClient.OpenReadTaskAsync(url))
             {
-                return JsonSerializer.DeserializeFromStream<CategoryInfo>(gzipStream);
+                using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, false))
+                {
+                    return JsonSerializer.DeserializeFromStream<CategoryInfo>(gzipStream);
+                }
             }
         }
 
@@ -90,11 +96,12 @@ namespace MediaBrowser.Common.ApiInteraction
         {
             string url = ApiUrl + "/studios?userId=" + userId.ToString();
 
-            Stream stream = await WebClient.OpenReadTaskAsync(url);
-
-            using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, false))
+            using (Stream stream = await WebClient.OpenReadTaskAsync(url))
             {
-                return JsonSerializer.DeserializeFromStream<IEnumerable<CategoryInfo>>(gzipStream);
+                using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, false))
+                {
+                    return JsonSerializer.DeserializeFromStream<IEnumerable<CategoryInfo>>(gzipStream);
+                }
             }
         }
 
@@ -102,12 +109,20 @@ namespace MediaBrowser.Common.ApiInteraction
         {
             string url = ApiUrl + "/studio?userId=" + userId.ToString() + "&name=" + name;
 
-            Stream stream = await WebClient.OpenReadTaskAsync(url);
-
-            using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, false))
+            using (Stream stream = await WebClient.OpenReadTaskAsync(url))
             {
-                return JsonSerializer.DeserializeFromStream<CategoryInfo>(gzipStream);
+                using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, false))
+                {
+                    return JsonSerializer.DeserializeFromStream<CategoryInfo>(gzipStream);
+                }
             }
         }
+
+        private static ApiBaseItemWrapper<ApiBaseItem> DeserializeBaseItemWrapper(Stream stream)
+        {
+            ApiBaseItemWrapper<ApiBaseItem> data = JsonSerializer.DeserializeFromStream<ApiBaseItemWrapper<ApiBaseItem>>(stream);
+
+            return data;
+        }
     }
 }

+ 0 - 412
MediaBrowser.Common/ApiInteraction/DictionaryBaseItem.cs

@@ -1,412 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using MediaBrowser.Common.Json;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Users;
-using System.Linq;
-
-namespace MediaBrowser.Common.ApiInteraction
-{
-    public class DictionaryBaseItem : BaseItem
-    {
-        private Dictionary<string, object> Dictionary { get; set; }
-
-        public UserItemData UserItemData { get; set; }
-        public IEnumerable<DictionaryBaseItem> Children { get; set; }
-
-        public DictionaryBaseItem(Dictionary<string, object> dictionary)
-        {
-            Dictionary = dictionary;
-        }
-
-        public override string Name
-        {
-            get
-            {
-                return GetString("Name");
-            }
-            set
-            {
-                SetValue("Name", value);
-            }
-        }
-
-        public override string ArtImagePath
-        {
-            get
-            {
-                return GetString("ArtImagePath");
-            }
-            set
-            {
-                SetValue("ArtImagePath", value);
-            }
-        }
-
-        public override string AspectRatio
-        {
-            get
-            {
-                return GetString("AspectRatio");
-            }
-            set
-            {
-                SetValue("AspectRatio", value);
-            }
-        }
-
-        public override string BannerImagePath
-        {
-            get
-            {
-                return GetString("BannerImagePath");
-            }
-            set
-            {
-                SetValue("BannerImagePath", value);
-            }
-        }
-
-        public override string CustomPin
-        {
-            get
-            {
-                return GetString("CustomPin");
-            }
-            set
-            {
-                SetValue("CustomPin", value);
-            }
-        }
-
-        public override string CustomRating
-        {
-            get
-            {
-                return GetString("CustomRating");
-            }
-            set
-            {
-                SetValue("CustomRating", value);
-            }
-        }
-
-        public override string DisplayMediaType
-        {
-            get
-            {
-                return GetString("DisplayMediaType");
-            }
-            set
-            {
-                SetValue("DisplayMediaType", value);
-            }
-        }
-
-        public override string LogoImagePath
-        {
-            get
-            {
-                return GetString("LogoImagePath");
-            }
-            set
-            {
-                SetValue("LogoImagePath", value);
-            }
-        }
-
-        public override string OfficialRating
-        {
-            get
-            {
-                return GetString("OfficialRating");
-            }
-            set
-            {
-                SetValue("OfficialRating", value);
-            }
-        }
-
-        public override string Overview
-        {
-            get
-            {
-                return GetString("Overview");
-            }
-            set
-            {
-                SetValue("Overview", value);
-            }
-        }
-
-        public override string Path
-        {
-            get
-            {
-                return GetString("Path");
-            }
-            set
-            {
-                SetValue("Path", value);
-            }
-        }
-
-        public override string PrimaryImagePath
-        {
-            get
-            {
-                return GetString("PrimaryImagePath");
-            }
-            set
-            {
-                SetValue("PrimaryImagePath", value);
-            }
-        }
-
-        public override string SortName
-        {
-            get
-            {
-                return GetString("SortName");
-            }
-            set
-            {
-                SetValue("SortName", value);
-            }
-        }
-
-        public override string Tagline
-        {
-            get
-            {
-                return GetString("Tagline");
-            }
-            set
-            {
-                SetValue("Tagline", value);
-            }
-        }
-
-        public override string TrailerUrl
-        {
-            get
-            {
-                return GetString("TrailerUrl");
-            }
-            set
-            {
-                SetValue("TrailerUrl", value);
-            }
-        }
-
-        public override DateTime DateCreated
-        {
-            get
-            {
-                return GetDateTime("DateCreated");
-            }
-            set
-            {
-                SetValue("DateCreated", value);
-            }
-        }
-
-        public override DateTime DateModified
-        {
-            get
-            {
-                return GetDateTime("DateModified");
-            }
-            set
-            {
-                SetValue("DateModified", value);
-            }
-        }
-
-        public override float? UserRating
-        {
-            get
-            {
-                return GetNullableFloat("UserRating");
-            }
-            set
-            {
-                SetValue("UserRating", value);
-            }
-        }
-
-        public override string ThumbnailImagePath
-        {
-            get
-            {
-                return GetString("ThumbnailImagePath");
-            }
-            set
-            {
-                SetValue("ThumbnailImagePath", value);
-            }
-        }
-
-        public override int? ProductionYear
-        {
-            get
-            {
-                return GetNullableInt("ProductionYear");
-            }
-            set
-            {
-                SetValue("ProductionYear", value);
-            }
-        }
-
-        public override TimeSpan? RunTime
-        {
-            get
-            {
-                return GetNullableTimeSpan("RunTime");
-            }
-            set
-            {
-                SetValue("RunTime", value);
-            }
-        }
-
-        public bool IsFolder
-        {
-            get
-            {
-                return GetBool("IsFolder");
-            }
-        }
-
-        public override Guid Id
-        {
-            get
-            {
-                return GetGuid("Id");
-            }
-            set
-            {
-                SetValue("Id", value);
-            }
-        }
-
-        public TimeSpan? GetNullableTimeSpan(string name)
-        {
-            string val = Dictionary[name] as string;
-
-            if (string.IsNullOrEmpty(val))
-            {
-                return null;
-            }
-
-            return TimeSpan.Parse(val);
-        }
-
-        public int? GetNullableInt(string name)
-        {
-            string val = Dictionary[name] as string;
-
-            if (string.IsNullOrEmpty(val))
-            {
-                return null;
-            }
-
-            return int.Parse(val);
-        }
-
-        public float? GetNullableFloat(string name)
-        {
-            string val = Dictionary[name] as string;
-
-            if (string.IsNullOrEmpty(val))
-            {
-                return null;
-            }
-
-            return float.Parse(val);
-        }
-
-        public DateTime? GetNullableDateTime(string name)
-        {
-            string val = Dictionary[name] as string;
-
-            if (string.IsNullOrEmpty(val))
-            {
-                return null;
-            }
-
-            return DateTime.Parse(val);
-        }
-
-        public DateTime GetDateTime(string name)
-        {
-            DateTime? val = GetNullableDateTime(name);
-
-            return val ?? DateTime.MinValue;
-        }
-
-        public bool? GetNullableBool(string name)
-        {
-            string val = Dictionary[name] as string;
-
-            if (string.IsNullOrEmpty(val))
-            {
-                return null;
-            }
-
-            return val != "false";
-        }
-
-        public Guid GetGuid(string name)
-        {
-            string val = GetString(name);
-
-            if (string.IsNullOrEmpty(val))
-            {
-                return Guid.Empty;
-            }
-
-            return Guid.Parse(val);
-        }
-
-        public bool GetBool(string name)
-        {
-            bool? val = GetNullableBool(name);
-
-            return val ?? false;
-        }
-
-        public string GetString(string name)
-        {
-            return Dictionary[name] as string;
-        }
-
-        private void SetValue<T>(string name, T value)
-        {
-            Dictionary[name] = value;
-        }
-
-        public static DictionaryBaseItem FromApiOutput(Stream stream)
-        {
-            Dictionary<string, object> data = JsonSerializer.DeserializeFromStream<Dictionary<string, object>>(stream);
-
-            string baseItem = data["Item"] as string;
-
-            DictionaryBaseItem item = new DictionaryBaseItem(JsonSerializer.DeserializeFromString<Dictionary<string, object>>(baseItem));
-
-            if (data.ContainsKey("UserItemData"))
-            {
-                item.UserItemData = JsonSerializer.DeserializeFromString<UserItemData>(data["UserItemData"].ToString());
-            }
-
-            if (data.ContainsKey("Children"))
-            {
-                item.Children = JsonSerializer.DeserializeFromString<IEnumerable<Dictionary<string, object>>>(data["Children"].ToString()).Select(c => new DictionaryBaseItem(c));
-            }
-
-            return item;
-        }
-    }
-}

+ 6 - 6
MediaBrowser.Common/Json/JsonSerializer.cs

@@ -1,23 +1,24 @@
 using System.IO;
+using System;
 
 namespace MediaBrowser.Common.Json
 {
     public class JsonSerializer
     {
-        public static void SerializeToStream<T>(T o, Stream stream)
+        public static void SerializeToStream<T>(T obj, Stream stream)
         {
             Configure();
             
-            ServiceStack.Text.JsonSerializer.SerializeToStream<T>(o, stream);
+            ServiceStack.Text.JsonSerializer.SerializeToStream<T>(obj, stream);
         }
 
-        public static void SerializeToFile<T>(T o, string file)
+        public static void SerializeToFile<T>(T obj, string file)
         {
             Configure();
 
             using (StreamWriter streamWriter = new StreamWriter(file))
             {
-                ServiceStack.Text.JsonSerializer.SerializeToWriter<T>(o, streamWriter);
+                ServiceStack.Text.JsonSerializer.SerializeToWriter<T>(obj, streamWriter);
             }
         }
 
@@ -44,12 +45,11 @@ namespace MediaBrowser.Common.Json
 
             return ServiceStack.Text.JsonSerializer.DeserializeFromString<T>(data);
         }
-        
+
         private static void Configure()
         {
             ServiceStack.Text.JsConfig.ExcludeTypeInfo = true;
             ServiceStack.Text.JsConfig.IncludeNullValues = false;
-            ServiceStack.Text.JsConfig.DateHandler = ServiceStack.Text.JsonDateHandler.ISO8601;
         }
     }
 }

+ 0 - 4
MediaBrowser.Common/Logging/Logger.cs

@@ -1,8 +1,4 @@
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
 namespace MediaBrowser.Common.Logging
 {

+ 0 - 12
MediaBrowser.Common/MediaBrowser.Common.csproj

@@ -35,9 +35,6 @@
     </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
-    <Reference Include="System.Reactive">
-      <HintPath>..\packages\Rx-Main.1.0.11226\lib\Net4\System.Reactive.dll</HintPath>
-    </Reference>
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />
@@ -48,15 +45,6 @@
     <Compile Include="ApiInteraction\ApiController.cs" />
     <Compile Include="Events\GenericItemEventArgs.cs" />
     <Compile Include="Json\JsonSerializer.cs" />
-    <Compile Include="ApiInteraction\DictionaryBaseItem.cs" />
-    <Compile Include="Net\CollectionExtensions.cs" />
-    <Compile Include="Net\Handlers\BaseEmbeddedResourceHandler.cs" />
-    <Compile Include="Net\Handlers\JsonHandler.cs" />
-    <Compile Include="Net\HttpServer.cs" />
-    <Compile Include="Net\Request.cs" />
-    <Compile Include="Net\RequestContext.cs" />
-    <Compile Include="Net\Response.cs" />
-    <Compile Include="Net\StreamExtensions.cs" />
     <Compile Include="Plugins\BasePluginConfiguration.cs" />
     <Compile Include="Logging\BaseLogger.cs" />
     <Compile Include="Logging\FileLogger.cs" />

+ 0 - 36
MediaBrowser.Common/Net/Handlers/JsonHandler.cs

@@ -1,36 +0,0 @@
-using System.IO;
-using System.IO.Compression;
-using MediaBrowser.Common.Json;
-
-namespace MediaBrowser.Common.Net.Handlers
-{
-    public abstract class JsonHandler : Response
-    {
-        public JsonHandler(RequestContext ctx)
-            : base(ctx)
-        {
-            Headers["Content-Encoding"] = "gzip";
-
-            WriteStream = s =>
-            {
-                WriteReponse(s);
-                s.Close();
-            };
-        }
-
-        public override string ContentType
-        {
-            get { return "application/json"; }
-        }
-
-        protected abstract object ObjectToSerialize { get; }
-
-        private void WriteReponse(Stream stream)
-        {
-            using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Compress, false))
-            {
-                JsonSerializer.SerializeToStream(ObjectToSerialize, gzipStream);
-            }
-        }
-    }
-}

+ 0 - 77
MediaBrowser.Common/Net/Response.cs

@@ -1,77 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.IO;
-
-namespace MediaBrowser.Common.Net
-{
-    public abstract class Response
-    {
-        protected RequestContext RequestContext { get; private set; }
-
-        protected NameValueCollection QueryString
-        {
-            get
-            {
-                return RequestContext.Request.QueryString;
-            }
-        }
-
-        public Response(RequestContext ctx)
-        {
-            RequestContext = ctx;
-
-            WriteStream = s => { };
-            Headers = new Dictionary<string, string>();
-        }
-
-        public abstract string ContentType { get; }
-
-        public virtual int StatusCode
-        {
-            get
-            {
-                return 200;
-            }
-        }
-
-        public virtual TimeSpan CacheDuration
-        {
-            get
-            {
-                return TimeSpan.FromTicks(0);
-            }
-        }
-
-        public virtual DateTime? LastDateModified
-        {
-            get
-            {
-                return null;
-            }
-        }
-
-        public IDictionary<string, string> Headers { get; set; }
-        public Action<Stream> WriteStream { get; set; }
-    }
-
-    /*public class ByteResponse : Response
-    {
-        public ByteResponse(byte[] bytes)
-        {
-            WriteStream = async s => 
-            {
-                await s.WriteAsync(bytes, 0, bytes.Length);
-                s.Close();
-            };
-        }
-    }
-
-    public class StringResponse : ByteResponse
-    {
-        public StringResponse(string message)
-            : base(Encoding.UTF8.GetBytes(message))
-        {
-        }
-    }*/
-}

+ 1 - 5
MediaBrowser.Common/Plugins/BasePlugin.cs

@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Reflection;
+using System.IO;
 using MediaBrowser.Common.Json;
 
 namespace MediaBrowser.Common.Plugins

+ 1 - 6
MediaBrowser.Common/Plugins/BasePluginConfiguration.cs

@@ -1,9 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
+
 namespace MediaBrowser.Common.Plugins
 {
     public class BasePluginConfiguration

+ 0 - 1
MediaBrowser.Common/packages.config

@@ -1,5 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="Rx-Main" version="1.0.11226" targetFramework="net45" />
   <package id="ServiceStack.Text" version="3.8.5" targetFramework="net45" />
 </packages>

+ 9 - 2
MediaBrowser.Controller/Events/ItemResolveEventArgs.cs

@@ -1,11 +1,13 @@
 using System;
 using System.Collections.Generic;
-using MediaBrowser.Model.Entities;
 using System.IO;
-using System.Linq;
+using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.Controller.Events
 {
+    /// <summary>
+    /// This is an EventArgs object used when resolving a Path into a BaseItem
+    /// </summary>
     public class ItemResolveEventArgs : PreBeginResolveEventArgs
     {
         public IEnumerable<KeyValuePair<string, FileAttributes>> FileSystemChildren { get; set; }
@@ -57,6 +59,11 @@ namespace MediaBrowser.Controller.Events
         }
     }
 
+    /// <summary>
+    /// This is an EventArgs object used before we begin resolving a Path into a BaseItem
+    /// File system children have not been collected yet, but consuming events will
+    /// have a chance to cancel resolution based on the Path, Parent and FileAttributes
+    /// </summary>
     public class PreBeginResolveEventArgs : EventArgs
     {
         public string Path { get; set; }

+ 2 - 2
MediaBrowser.Controller/IO/DirectoryWatchers.cs

@@ -24,9 +24,9 @@ namespace MediaBrowser.Controller.IO
 
             pathsToWatch.Add(rootFolder.Path);
 
-            foreach (Folder folder in rootFolder.FolderChildren)
+            foreach (Folder folder in rootFolder.Children.OfType<Folder>())
             {
-                foreach (Folder subFolder in folder.FolderChildren)
+                foreach (Folder subFolder in folder.Children.OfType<Folder>())
                 {
                     if (Path.IsPathRooted(subFolder.Path))
                     {

+ 3 - 0
MediaBrowser.Controller/IO/Shortcut.cs

@@ -5,6 +5,9 @@ using System.Text;
 
 namespace MediaBrowser.Controller.IO
 {
+    /// <summary>
+    /// Contains helpers to interact with shortcut files (.lnk)
+    /// </summary>
     public static class Shortcut
     {
         #region Signitures were imported from http://pinvoke.net

+ 121 - 5
MediaBrowser.Controller/Kernel.cs

@@ -7,7 +7,6 @@ using System.Text;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Json;
 using MediaBrowser.Common.Logging;
-using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Plugins;
 using MediaBrowser.Controller.Events;
 using MediaBrowser.Controller.IO;
@@ -16,6 +15,7 @@ using MediaBrowser.Controller.Resolvers;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Users;
+using MediaBrowser.Net;
 
 namespace MediaBrowser.Controller
 {
@@ -79,7 +79,7 @@ namespace MediaBrowser.Controller
 
             ReloadHttpServer();
 
-            ReloadPlugins();
+            LoadPlugins();
 
             // Get users from users folder
             // Load root media folder
@@ -94,7 +94,7 @@ namespace MediaBrowser.Controller
             Logger.LoggerInstance.LogSeverity = Configuration.LogSeverity;
         }
 
-        private void ReloadPlugins()
+        private void LoadPlugins()
         {
             // Find plugins
             Plugins = PluginController.GetAllPlugins();
@@ -261,7 +261,7 @@ namespace MediaBrowser.Controller
         /// </summary>
         public IEnumerable<BaseItem> GetParentalAllowedChildren(Folder folder, Guid userId)
         {
-            return folder.Children.ToList().Where(i => IsParentalAllowed(i, userId));
+            return folder.Children.Where(i => IsParentalAllowed(i, userId));
         }
 
         /// <summary>
@@ -307,7 +307,7 @@ namespace MediaBrowser.Controller
         {
             DateTime now = DateTime.Now;
 
-            return GetParentalAllowedRecursiveChildren(parent, userId).Where(i => (now - i.DateCreated).TotalDays < Configuration.RecentItemDays);
+            return GetParentalAllowedRecursiveChildren(parent, userId).Where(i => !(i is Folder) && (now - i.DateCreated).TotalDays < Configuration.RecentItemDays);
         }
 
         /// <summary>
@@ -330,6 +330,11 @@ namespace MediaBrowser.Controller
         {
             return GetParentalAllowedRecursiveChildren(parent, userId).Where(i =>
             {
+                if (i is Folder)
+                {
+                    return false;
+                }
+                
                 var userdata = GetUserItemData(userId, i.Id);
 
                 return userdata != null && userdata.PlaybackPosition.Ticks > 0;
@@ -359,5 +364,116 @@ namespace MediaBrowser.Controller
         {
             return GetParentalAllowedRecursiveChildren(parent, userId).Where(f => f.People != null && f.People.Any(s => s.Name.Equals(personName, StringComparison.OrdinalIgnoreCase)));
         }
+
+        /// <summary>
+        /// Gets all studios from all recursive children of a folder
+        /// The CategoryInfo class is used to keep track of the number of times each studio appears
+        /// </summary>
+        public IEnumerable<CategoryInfo> GetAllStudios(Folder parent, Guid userId)
+        {
+            Dictionary<string, int> data = new Dictionary<string, int>();
+
+            // Get all the allowed recursive children
+            IEnumerable<BaseItem> allItems = Kernel.Instance.GetParentalAllowedRecursiveChildren(parent, userId);
+
+            foreach (var item in allItems)
+            {
+                // Add each studio from the item to the data dictionary
+                // If the studio already exists, increment the count
+                if (item.Studios == null)
+                {
+                    continue;
+                }
+
+                foreach (string val in item.Studios)
+                {
+                    if (!data.ContainsKey(val))
+                    {
+                        data.Add(val, 1);
+                    }
+                    else
+                    {
+                        data[val]++;
+                    }
+                }
+            }
+
+            // Now go through the dictionary and create a Category for each studio
+            List<CategoryInfo> list = new List<CategoryInfo>();
+
+            foreach (string key in data.Keys)
+            {
+                // Get the original entity so that we can also supply the PrimaryImagePath
+                Studio entity = Kernel.Instance.ItemController.GetStudio(key);
+
+                if (entity != null)
+                {
+                    list.Add(new CategoryInfo()
+                    {
+                        Name = entity.Name,
+                        ItemCount = data[key],
+                        PrimaryImagePath = entity.PrimaryImagePath
+                    });
+                }
+            }
+
+            return list;
+        }
+
+        /// <summary>
+        /// Gets all genres from all recursive children of a folder
+        /// The CategoryInfo class is used to keep track of the number of times each genres appears
+        /// </summary>
+        public IEnumerable<CategoryInfo> GetAllGenres(Folder parent, Guid userId)
+        {
+            Dictionary<string, int> data = new Dictionary<string, int>();
+
+            // Get all the allowed recursive children
+            IEnumerable<BaseItem> allItems = Kernel.Instance.GetParentalAllowedRecursiveChildren(parent, userId);
+
+            foreach (var item in allItems)
+            {
+                // Add each genre from the item to the data dictionary
+                // If the genre already exists, increment the count
+                if (item.Genres == null)
+                {
+                    continue;
+                }
+
+                foreach (string val in item.Genres)
+                {
+                    if (!data.ContainsKey(val))
+                    {
+                        data.Add(val, 1);
+                    }
+                    else
+                    {
+                        data[val]++;
+                    }
+                }
+            }
+
+            // Now go through the dictionary and create a Category for each genre
+            List<CategoryInfo> list = new List<CategoryInfo>();
+
+            foreach (string key in data.Keys)
+            {
+                // Get the original entity so that we can also supply the PrimaryImagePath
+                Genre entity = Kernel.Instance.ItemController.GetGenre(key);
+
+                if (entity != null)
+                {
+                    list.Add(new CategoryInfo()
+                    {
+                        Name = entity.Name,
+                        ItemCount = data[key],
+                        PrimaryImagePath = entity.PrimaryImagePath
+                    });
+                }
+            }
+
+            return list;
+        }
+
     }
 }

+ 6 - 0
MediaBrowser.Controller/Library/ItemController.cs

@@ -323,6 +323,12 @@ namespace MediaBrowser.Controller.Library
             return null;
         }
 
+        public Genre GetGenre(string name)
+        {
+            // not yet implemented
+            return null;
+        }
+
         public Year GetYear(int value)
         {
             // not yet implemented

+ 4 - 0
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -66,6 +66,10 @@
       <Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
       <Name>MediaBrowser.Model</Name>
     </ProjectReference>
+    <ProjectReference Include="..\MediaBrowser.Net\MediaBrowser.Net.csproj">
+      <Project>{5da08d1c-0d52-4b1b-aa66-e4a171d938f6}</Project>
+      <Name>MediaBrowser.Net</Name>
+    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
     <None Include="packages.config" />

+ 2 - 0
MediaBrowser.Controller/Resolvers/AudioResolver.cs

@@ -8,6 +8,8 @@ namespace MediaBrowser.Controller.Resolvers
     {
         protected override Audio Resolve(ItemResolveEventArgs args)
         {
+            // Return audio if the path is a file and has a matching extension
+
             if (!args.IsFolder)
             {
                 if (IsAudioFile(args.Path))

+ 19 - 0
MediaBrowser.Controller/Resolvers/BaseItemResolver.cs

@@ -15,6 +15,9 @@ namespace MediaBrowser.Controller.Resolvers
             return null;
         }
 
+        /// <summary>
+        /// Sets initial values on the newly resolved item
+        /// </summary>
         protected virtual void SetItemValues(T item, ItemResolveEventArgs args)
         {
             // If the subclass didn't specify this
@@ -23,6 +26,7 @@ namespace MediaBrowser.Controller.Resolvers
                 item.Path = args.Path;
             }
 
+            // If the subclass didn't specify this
             if (args.Parent != null)
             {
                 item.Parent = args.Parent;
@@ -40,9 +44,14 @@ namespace MediaBrowser.Controller.Resolvers
             
             if (item != null)
             {
+                // Set initial values on the newly resolved item
+                
                 SetItemValues(item, args);
 
+                // Make sure the item has a name
                 EnsureName(item);
+
+                // Make sure DateCreated and DateModified have values
                 EnsureDates(item);
             }
 
@@ -59,6 +68,9 @@ namespace MediaBrowser.Controller.Resolvers
 
         }
 
+        /// <summary>
+        /// Ensures DateCreated and DateModified have values
+        /// </summary>
         private void EnsureDates(T item)
         {
             // If the subclass didn't supply dates, add them here
@@ -73,6 +85,9 @@ namespace MediaBrowser.Controller.Resolvers
             }
         }
 
+        /// <summary>
+        /// Fills in image paths based on files win the folder
+        /// </summary>
         protected virtual void PopulateImages(T item, ItemResolveEventArgs args)
         {
             List<string> backdropFiles = new List<string>();
@@ -88,6 +103,7 @@ namespace MediaBrowser.Controller.Resolvers
 
                 string ext = Path.GetExtension(filePath);
 
+                // Only support png and jpg files
                 if (!ext.EndsWith("png", StringComparison.OrdinalIgnoreCase) && !ext.EndsWith("jpg", StringComparison.OrdinalIgnoreCase))
                 {
                     continue;
@@ -137,6 +153,9 @@ namespace MediaBrowser.Controller.Resolvers
         }
     }
 
+    /// <summary>
+    /// Weed this to keep a list of resolvers, since Resolvers are built with generics
+    /// </summary>
     public interface IBaseItemResolver
     {
         BaseItem ResolvePath(ItemResolveEventArgs args);

+ 1 - 0
MediaBrowser.Controller/Resolvers/FolderResolver.cs

@@ -29,6 +29,7 @@ namespace MediaBrowser.Controller.Resolvers
 
             item.IsRoot = args.Parent == null;
 
+            // Read data from folder.xml, if it exists
             PopulateFolderMetadata(item, args);
         }
 

+ 11 - 3
MediaBrowser.Controller/Resolvers/VideoResolver.cs

@@ -1,20 +1,26 @@
-using System.IO;
+using System.Collections.Generic;
+using System.IO;
 using MediaBrowser.Controller.Events;
 using MediaBrowser.Model.Entities;
-using System.Linq;
-using System.Collections.Generic;
 
 namespace MediaBrowser.Controller.Resolvers
 {
+    /// <summary>
+    /// Resolves a Path into a Video
+    /// </summary>
     public class VideoResolver : BaseVideoResolver<Video>
     {
     }
 
+    /// <summary>
+    /// Resolves a Path into a Video or Video subclass
+    /// </summary>
     public abstract class BaseVideoResolver<T> : BaseItemResolver<T>
         where T : Video, new()
     {
         protected override T Resolve(ItemResolveEventArgs args)
         {
+            // If the path is a file check for a matching extensions
             if (!args.IsFolder)
             {
                 if (IsVideoFile(args.Path))
@@ -29,6 +35,7 @@ namespace MediaBrowser.Controller.Resolvers
 
             else
             {
+                // If the path is a folder, check if it's bluray or dvd
                 T item = ResolveFromFolderName(args.Path);
 
                 if (item != null)
@@ -36,6 +43,7 @@ namespace MediaBrowser.Controller.Resolvers
                     return item;
                 }
 
+                // Also check the subfolders for bluray or dvd
                 foreach (KeyValuePair<string, FileAttributes> folder in args.FileSystemChildren)
                 {
                     if (!folder.Value.HasFlag(FileAttributes.Directory))

+ 29 - 6
MediaBrowser.Controller/UserController.cs

@@ -6,8 +6,14 @@ using MediaBrowser.Model.Users;
 
 namespace MediaBrowser.Controller
 {
+    /// <summary>
+    /// Manages users within the system
+    /// </summary>
     public class UserController
     {
+        /// <summary>
+        /// Gets or sets the path to folder that contains data for all the users
+        /// </summary>
         public string UsersPath { get; set; }
 
         public UserController(string usersPath)
@@ -15,6 +21,9 @@ namespace MediaBrowser.Controller
             UsersPath = usersPath;
         }
 
+        /// <summary>
+        /// Gets all users within the system
+        /// </summary>
         public IEnumerable<User> GetAllUsers()
         {
             if (!Directory.Exists(UsersPath))
@@ -37,6 +46,9 @@ namespace MediaBrowser.Controller
             return list;
         }
 
+        /// <summary>
+        /// Gets a User from it's directory
+        /// </summary>
         private User GetFromDirectory(string path)
         {
             string file = Path.Combine(path, "user.js");
@@ -44,17 +56,28 @@ namespace MediaBrowser.Controller
             return JsonSerializer.DeserializeFromFile<User>(file);
         }
 
-        public void CreateUser(User user)
+        /// <summary>
+        /// Creates a User with a given name
+        /// </summary>
+        public User CreateUser(string name)
         {
-            user.Id = Guid.NewGuid();
+            var now = DateTime.Now;
 
-            user.DateCreated = user.DateModified = DateTime.Now;
+            User user = new User()
+            {
+                Name = name,
+                Id = Guid.NewGuid(),
+                DateCreated = now,
+                DateModified = now
+            };
+
+            user.Path = Path.Combine(UsersPath, user.Id.ToString());
 
-            string userFolder = Path.Combine(UsersPath, user.Id.ToString());
+            Directory.CreateDirectory(user.Path);
 
-            Directory.CreateDirectory(userFolder);
+            JsonSerializer.SerializeToFile(user, Path.Combine(user.Path, "user.js"));
 
-            JsonSerializer.SerializeToFile(user, Path.Combine(userFolder, "user.js"));
+            return user;
         }
     }
 }

+ 22 - 8
MediaBrowser.Controller/Xml/BaseItemXmlParser.cs

@@ -7,15 +7,23 @@ using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.Controller.Xml
 {
-    public class BaseItemXmlParser<T>
+    /// <summary>
+    /// Provides a base class for parsing metadata xml
+    /// </summary>
+    public abstract class BaseItemXmlParser<T>
         where T : BaseItem, new()
     {
+        /// <summary>
+        /// Fetches metadata for an item from one xml file
+        /// </summary>
         public virtual void Fetch(T item, string metadataFile)
         {
+            // Use XmlReader for best performance
             using (XmlReader reader = XmlReader.Create(metadataFile))
             {
                 reader.MoveToContent();
 
+                // Loop through each element
                 while (reader.Read())
                 {
                     if (reader.NodeType == XmlNodeType.Element)
@@ -25,7 +33,7 @@ namespace MediaBrowser.Controller.Xml
                 }
             }
 
-            // If dates weren't supplied in metadata, use values from the file
+            // If dates weren't supplied in metadata, use values from the xml file
             if (item.DateCreated == DateTime.MinValue)
             {
                 item.DateCreated = File.GetCreationTime(metadataFile);
@@ -37,10 +45,14 @@ namespace MediaBrowser.Controller.Xml
             }
         }
 
+        /// <summary>
+        /// Fetches metadata from one Xml Element
+        /// </summary>
         protected virtual void FetchDataFromXmlNode(XmlReader reader, T item)
         {
             switch (reader.Name)
             {
+                // DateCreated
                 case "Added":
                     DateTime added;
                     if (DateTime.TryParse(reader.ReadElementContentAsString() ?? string.Empty, out added))
@@ -49,6 +61,7 @@ namespace MediaBrowser.Controller.Xml
                     }
                     break;
 
+                // DisplayMediaType
                 case "Type":
                     {
                         item.DisplayMediaType = reader.ReadElementContentAsString() ?? string.Empty;
@@ -69,6 +82,7 @@ namespace MediaBrowser.Controller.Xml
                         break;
                     }
 
+                // TODO: Do we still need this?
                 case "banner":
                     item.BannerImagePath = reader.ReadElementContentAsString() ?? string.Empty;
                     break;
@@ -172,7 +186,7 @@ namespace MediaBrowser.Controller.Xml
 
                     string rating = reader.ReadElementContentAsString();
 
-                    if (!string.IsNullOrEmpty(rating))
+                    if (!string.IsNullOrWhiteSpace(rating))
                     {
                         float val;
 
@@ -428,7 +442,7 @@ namespace MediaBrowser.Controller.Xml
                             {
                                 string genre = reader.ReadElementContentAsString();
 
-                                if (!string.IsNullOrEmpty(genre))
+                                if (!string.IsNullOrWhiteSpace(genre))
                                 {
                                     list.Add(genre);
                                 }
@@ -461,7 +475,7 @@ namespace MediaBrowser.Controller.Xml
                             {
                                 string genre = reader.ReadElementContentAsString();
 
-                                if (!string.IsNullOrEmpty(genre))
+                                if (!string.IsNullOrWhiteSpace(genre))
                                 {
                                     list.Add(genre);
                                 }
@@ -522,7 +536,7 @@ namespace MediaBrowser.Controller.Xml
                             {
                                 string studio = reader.ReadElementContentAsString();
 
-                                if (!string.IsNullOrEmpty(studio))
+                                if (!string.IsNullOrWhiteSpace(studio))
                                 {
                                     list.Add(studio);
                                 }
@@ -555,7 +569,7 @@ namespace MediaBrowser.Controller.Xml
 
                                 int rating = 7;
 
-                                if (!string.IsNullOrEmpty(ratingString))
+                                if (!string.IsNullOrWhiteSpace(ratingString))
                                 {
                                     int.TryParse(ratingString, out rating);
                                 }
@@ -646,7 +660,7 @@ namespace MediaBrowser.Controller.Xml
         {
             value = (value ?? string.Empty).Trim(deliminator);
 
-            return string.IsNullOrEmpty(value) ? new string[] { } : value.Split(deliminator);
+            return string.IsNullOrWhiteSpace(value) ? new string[] { } : value.Split(deliminator);
         }
     }
 }

+ 4 - 0
MediaBrowser.Controller/Xml/FolderXmlParser.cs

@@ -2,6 +2,10 @@
 
 namespace MediaBrowser.Controller.Xml
 {
+    /// <summary>
+    /// Fetches metadata fro a folder.
+    /// Since folder.xml contains no folder-specific values, no overrides are needed
+    /// </summary>
     public class FolderXmlParser : BaseItemXmlParser<Folder>
     {
     }

+ 9 - 4
MediaBrowser.Controller/Xml/XmlExtensions.cs

@@ -1,5 +1,4 @@
-using System;
-using System.Globalization;
+using System.Globalization;
 using System.Xml;
 
 namespace MediaBrowser.Controller.Xml
@@ -8,13 +7,16 @@ namespace MediaBrowser.Controller.Xml
     {
         private static CultureInfo _usCulture = new CultureInfo("en-US");
 
+        /// <summary>
+        /// Reads a float from the current element of an XmlReader
+        /// </summary>
         public static float ReadFloatSafe(this XmlReader reader)
         {
             string valueString = reader.ReadElementContentAsString();
 
             float value = 0;
 
-            if (!string.IsNullOrEmpty(valueString))
+            if (!string.IsNullOrWhiteSpace(valueString))
             {
                 // float.TryParse is local aware, so it can be probamatic, force us culture
                 float.TryParse(valueString, NumberStyles.AllowDecimalPoint, _usCulture, out value);
@@ -23,13 +25,16 @@ namespace MediaBrowser.Controller.Xml
             return value;
         }
 
+        /// <summary>
+        /// Reads an int from the current element of an XmlReader
+        /// </summary>
         public static int ReadIntSafe(this XmlReader reader)
         {
             string valueString = reader.ReadElementContentAsString();
 
             int value = 0;
 
-            if (!string.IsNullOrEmpty(valueString))
+            if (!string.IsNullOrWhiteSpace(valueString))
             {
 
                 int.TryParse(valueString, out value);

+ 4 - 4
MediaBrowser.HtmlBrowser/Handlers/EmbeddedResourceHandler.cs

@@ -1,14 +1,14 @@
 using System.IO;
 using System.Reflection;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Common.Net.Handlers;
+using MediaBrowser.Net;
+using MediaBrowser.Net.Handlers;
 
 namespace MediaBrowser.HtmlBrowser.Handlers
 {
     class EmbeddedResourceHandler : BaseEmbeddedResourceHandler
     {
-        public EmbeddedResourceHandler(RequestContext ctx, string resourcePath)
-            : base(ctx, resourcePath)
+        public EmbeddedResourceHandler(string resourcePath)
+            : base(resourcePath)
         {
       
         }

+ 4 - 0
MediaBrowser.HtmlBrowser/MediaBrowser.HtmlBrowser.csproj

@@ -59,6 +59,10 @@
       <Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
       <Name>MediaBrowser.Model</Name>
     </ProjectReference>
+    <ProjectReference Include="..\MediaBrowser.Net\MediaBrowser.Net.csproj">
+      <Project>{5da08d1c-0d52-4b1b-aa66-e4a171d938f6}</Project>
+      <Name>MediaBrowser.Net</Name>
+    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
     <None Include="packages.config" />

+ 3 - 3
MediaBrowser.HtmlBrowser/Plugin.cs

@@ -1,6 +1,6 @@
 using System;
 using System.Reactive.Linq;
-using MediaBrowser.Common.Net.Handlers;
+using MediaBrowser.Net.Handlers;
 using MediaBrowser.Common.Plugins;
 using MediaBrowser.Controller;
 using MediaBrowser.HtmlBrowser.Handlers;
@@ -13,7 +13,7 @@ namespace MediaBrowser.HtmlBrowser
         {
             var httpServer = Kernel.Instance.HttpServer;
 
-            httpServer.Where(ctx => ctx.LocalPath.IndexOf("/browser/", StringComparison.OrdinalIgnoreCase) != -1).Subscribe(ctx =>
+            /*httpServer.Where(ctx => ctx.LocalPath.IndexOf("/browser/", StringComparison.OrdinalIgnoreCase) != -1).Subscribe(ctx =>
             {
                 string localPath = ctx.LocalPath;
                 string srch = "/browser/";
@@ -24,7 +24,7 @@ namespace MediaBrowser.HtmlBrowser
 
                 ctx.Respond(new EmbeddedResourceHandler(ctx, resource));
 
-            });
+            });*/
         }
     }
 }

+ 39 - 0
MediaBrowser.Model/Entities/ApiBaseItem.cs

@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using MediaBrowser.Model.Users;
+
+namespace MediaBrowser.Model.Entities
+{
+    /// <summary>
+    /// This is a concrete class that the UI can use to deserialize
+    /// It is flat in the sense that it will be used regardless of the type of BaseItem involved
+    /// </summary>
+    public class ApiBaseItem : BaseItem
+    {
+    }
+
+    /// <summary>
+    /// This is the full return object when requesting an Item
+    /// </summary>
+    public class ApiBaseItemWrapper<T>
+        where T : BaseItem
+    {
+        public T Item { get; set; }
+
+        public UserItemData UserItemData { get; set; }
+
+        public IEnumerable<ApiBaseItemWrapper<T>> Children { get; set; }
+
+        [IgnoreDataMember]
+        public Type ItemType { get; set; }
+
+        public string Type
+        {
+            get
+            {
+                return ItemType.Name;
+            }
+        }
+    }
+}

+ 39 - 39
MediaBrowser.Model/Entities/BaseItem.cs

@@ -6,70 +6,70 @@ namespace MediaBrowser.Model.Entities
 {
     public abstract class BaseItem
     {
-        public virtual string Name { get; set; }
-        public virtual string SortName { get; set; }
+        public string Name { get; set; }
+        public string SortName { get; set; }
 
-        public virtual Guid Id { get; set; }
+        public Guid Id { get; set; }
 
-        public virtual DateTime DateCreated { get; set; }
+        public DateTime DateCreated { get; set; }
 
-        public virtual DateTime DateModified { get; set; }
+        public DateTime DateModified { get; set; }
 
-        public virtual string Path { get; set; }
+        /// <summary>
+        /// When the item first debuted. For movies this could be premiere date, episodes would be first aired
+        /// </summary>
+        public DateTime? PremiereDate { get; set; }
+
+        public string Path { get; set; }
 
         [IgnoreDataMember]
         public Folder Parent { get; set; }
 
-        public virtual string PrimaryImagePath { get; set; }
-        public virtual string LogoImagePath { get; set; }
-        public virtual string ArtImagePath { get; set; }
-        public virtual string ThumbnailImagePath { get; set; }
-        public virtual string BannerImagePath { get; set; }
+        public string PrimaryImagePath { get; set; }
+        public string LogoImagePath { get; set; }
+        public string ArtImagePath { get; set; }
+        public string ThumbnailImagePath { get; set; }
+        public string BannerImagePath { get; set; }
 
-        public virtual IEnumerable<string> BackdropImagePaths { get; set; }
+        public IEnumerable<string> BackdropImagePaths { get; set; }
 
-        public virtual string OfficialRating { get; set; }
+        public string OfficialRating { get; set; }
 
-        public virtual string CustomRating { get; set; }
-        public virtual string CustomPin { get; set; }
+        public string CustomRating { get; set; }
+        public string CustomPin { get; set; }
 
-        public virtual string Overview { get; set; }
-        public virtual string Tagline { get; set; }
+        public string Overview { get; set; }
+        public string Tagline { get; set; }
 
         [IgnoreDataMember]
-        public virtual IEnumerable<PersonInfo> People { get; set; }
+        public IEnumerable<PersonInfo> People { get; set; }
 
-        public virtual IEnumerable<string> Studios { get; set; }
+        public IEnumerable<string> Studios { get; set; }
 
-        public virtual IEnumerable<string> Genres { get; set; }
+        public IEnumerable<string> Genres { get; set; }
 
-        public virtual string DisplayMediaType { get; set; }
+        public string DisplayMediaType { get; set; }
 
-        public virtual float? UserRating { get; set; }
-        public virtual TimeSpan? RunTime { get; set; }
+        public float? UserRating { get; set; }
+        public TimeSpan? RunTime { get; set; }
 
-        public virtual string AspectRatio { get; set; }
-        public virtual int? ProductionYear { get; set; }
+        public string AspectRatio { get; set; }
+        public int? ProductionYear { get; set; }
+
+        /// <summary>
+        /// If the item is part of a series, this is it's number in the series.
+        /// This could be episode number, album track number, etc.
+        /// </summary>
+        public int? IndexNumber { get; set; }
 
         [IgnoreDataMember]
-        public virtual IEnumerable<Video> LocalTrailers { get; set; }
+        public IEnumerable<Video> LocalTrailers { get; set; }
+
+        public string TrailerUrl { get; set; }
 
-        public virtual string TrailerUrl { get; set; }
-        
         public override string ToString()
         {
             return Name;
         }
-
-        /// <summary>
-        /// This is strictly to enhance json output, until I can find a way to customize service stack to add this without having to use a property
-        /// </summary>
-        public string Type
-        {
-            get
-            {
-                return GetType().Name;
-            }
-        }
     }
 }

+ 20 - 24
MediaBrowser.Model/Entities/Folder.cs

@@ -1,7 +1,4 @@
 using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
 using System.Runtime.Serialization;
 
 namespace MediaBrowser.Model.Entities
@@ -21,9 +18,6 @@ namespace MediaBrowser.Model.Entities
         [IgnoreDataMember]
         public BaseItem[] Children { get; set; }
 
-        [IgnoreDataMember]
-        public IEnumerable<Folder> FolderChildren { get { return Children.OfType<Folder>(); } }
-
         /// <summary>
         /// Finds an item by ID, recursively
         /// </summary>
@@ -36,17 +30,18 @@ namespace MediaBrowser.Model.Entities
 
             foreach (BaseItem item in Children)
             {
-                if (item.Id == id)
-                {
-                    return item;
-                }
-            }
+                var folder = item as Folder;
 
-            foreach (Folder folder in FolderChildren)
-            {
-                BaseItem item = folder.FindById(id);
+                if (folder != null)
+                {
+                    var foundItem = folder.FindById(id);
 
-                if (item != null)
+                    if (foundItem != null)
+                    {
+                        return foundItem;
+                    }
+                }
+                else if (item.Id == id)
                 {
                     return item;
                 }
@@ -67,17 +62,18 @@ namespace MediaBrowser.Model.Entities
 
             foreach (BaseItem item in Children)
             {
-                if (item.Path.Equals(path, StringComparison.OrdinalIgnoreCase))
-                {
-                    return item;
-                }
-            }
+                var folder = item as Folder;
 
-            foreach (Folder folder in FolderChildren)
-            {
-                BaseItem item = folder.FindByPath(path);
+                if (folder != null)
+                {
+                    var foundItem = folder.FindByPath(path);
 
-                if (item != null)
+                    if (foundItem != null)
+                    {
+                        return foundItem;
+                    }
+                }
+                else if (item.Path.Equals(path, StringComparison.OrdinalIgnoreCase))
                 {
                     return item;
                 }

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

@@ -36,6 +36,7 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="Configuration\Configuration.cs" />
+    <Compile Include="Entities\ApiBaseItem.cs" />
     <Compile Include="Entities\Audio.cs" />
     <Compile Include="Entities\BaseItem.cs" />
     <Compile Include="Entities\CategoryInfo.cs" />

+ 0 - 1
MediaBrowser.Model/Users/User.cs

@@ -6,7 +6,6 @@ namespace MediaBrowser.Model.Users
 {
     public class User : BaseItem
     {
-        public string Password { get; set; }
         public string MaxParentalRating { get; set; }
 
         private Dictionary<Guid, UserItemData> _ItemData = new Dictionary<Guid, UserItemData>();

+ 1 - 1
MediaBrowser.Movies/Entities/Movie.cs

@@ -1,6 +1,6 @@
 using System.Collections.Generic;
-using MediaBrowser.Model.Entities;
 using System.Runtime.Serialization;
+using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.Movies.Entities
 {

+ 1 - 1
MediaBrowser.Movies/Metadata/MovieXmlParser.cs

@@ -17,7 +17,7 @@ namespace MediaBrowser.Movies.Metadata
                 case "IMDB":
                 case "IMDbId":
                     string IMDbId = reader.ReadElementContentAsString() ?? string.Empty;
-                    if (!string.IsNullOrEmpty(IMDbId))
+                    if (!string.IsNullOrWhiteSpace(IMDbId))
                     {
                         item.ImdbId = IMDbId;
                     }

+ 1 - 1
MediaBrowser.Common/Net/CollectionExtensions.cs → MediaBrowser.Net/CollectionExtensions.cs

@@ -2,7 +2,7 @@ using System.Collections.Generic;
 using System.Collections.Specialized;
 using System.Linq;
 
-namespace MediaBrowser.Common.Net
+namespace MediaBrowser.Net
 {
     public static class CollectionExtensions
     {

+ 8 - 20
MediaBrowser.Common/Net/Handlers/BaseEmbeddedResourceHandler.cs → MediaBrowser.Net/Handlers/BaseEmbeddedResourceHandler.cs

@@ -1,23 +1,14 @@
-using System.IO;
-using System.IO.Compression;
-using System;
+using System;
+using System.IO;
 
-namespace MediaBrowser.Common.Net.Handlers
+namespace MediaBrowser.Net.Handlers
 {
-    public abstract class BaseEmbeddedResourceHandler : Response
+    public abstract class BaseEmbeddedResourceHandler : BaseHandler
     {
-        public BaseEmbeddedResourceHandler(RequestContext ctx, string resourcePath)
-            : base(ctx)
+        public BaseEmbeddedResourceHandler(string resourcePath)
+            : base()
         {
             ResourcePath = resourcePath;
-
-            Headers["Content-Encoding"] = "gzip";
-
-            WriteStream = s =>
-            {
-                WriteReponse(s);
-                s.Close();
-            };
         }
 
         protected string ResourcePath { get; set; }
@@ -57,12 +48,9 @@ namespace MediaBrowser.Common.Net.Handlers
             }
         }
 
-        private void WriteReponse(Stream stream)
+        protected override void WriteResponseToOutputStream(Stream stream)
         {
-            using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Compress, false))
-            {
-                GetEmbeddedResourceStream().CopyTo(gzipStream);
-            }
+            GetEmbeddedResourceStream().CopyTo(stream);
         }
 
         protected abstract Stream GetEmbeddedResourceStream();

+ 111 - 0
MediaBrowser.Net/Handlers/BaseHandler.cs

@@ -0,0 +1,111 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.IO;
+using System.IO.Compression;
+
+namespace MediaBrowser.Net.Handlers
+{
+    public abstract class BaseHandler
+    {
+        /// <summary>
+        /// Response headers
+        /// </summary>
+        public IDictionary<string, string> Headers = new Dictionary<string, string>();
+
+        /// <summary>
+        /// The action to write the response to the output stream
+        /// </summary>
+        public Action<Stream> WriteStream { get; set; }
+
+        /// <summary>
+        /// The original RequestContext
+        /// </summary>
+        public RequestContext RequestContext { get; set; }
+
+        /// <summary>
+        /// The original QueryString
+        /// </summary>
+        protected NameValueCollection QueryString
+        {
+            get
+            {
+                return RequestContext.Request.QueryString;
+            }
+        }
+
+        /// <summary>
+        /// Gets the MIME type to include in the response headers
+        /// </summary>
+        public abstract string ContentType { get; }
+
+        /// <summary>
+        /// Gets the status code to include in the response headers
+        /// </summary>
+        public virtual int StatusCode
+        {
+            get
+            {
+                return 200;
+            }
+        }
+
+        /// <summary>
+        /// Gets the cache duration to include in the response headers
+        /// </summary>
+        public virtual TimeSpan CacheDuration
+        {
+            get
+            {
+                return TimeSpan.FromTicks(0);
+            }
+        }
+
+        /// <summary>
+        /// Gets the last date modified of the content being returned, if this can be determined.
+        /// This will be used to invalidate the cache, so it's not needed if CacheDuration is 0.
+        /// </summary>
+        public virtual DateTime? LastDateModified
+        {
+            get
+            {
+                return null;
+            }
+        }
+
+        public virtual bool GzipResponse
+        {
+            get
+            {
+                return true;
+            }
+        }
+
+        public BaseHandler()
+        {
+            WriteStream = s =>
+            {
+                WriteReponse(s);
+                s.Close();
+            };
+        }
+
+        private void WriteReponse(Stream stream)
+        {
+            if (GzipResponse)
+            {
+                using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Compress, false))
+                {
+                    WriteResponseToOutputStream(gzipStream);
+                }
+            }
+            else
+            {
+                WriteResponseToOutputStream(stream);
+            }
+        }
+
+        protected abstract void WriteResponseToOutputStream(Stream stream);
+
+    }
+}

+ 11 - 0
MediaBrowser.Net/Handlers/BaseJsonHandler.cs

@@ -0,0 +1,11 @@
+
+namespace MediaBrowser.Net.Handlers
+{
+    public abstract class BaseJsonHandler : BaseHandler
+    {
+        public override string ContentType
+        {
+            get { return "application/json"; }
+        }
+    }
+}

+ 1 - 1
MediaBrowser.Common/Net/HttpServer.cs → MediaBrowser.Net/HttpServer.cs

@@ -2,7 +2,7 @@ using System;
 using System.Net;
 using System.Reactive.Linq;
 
-namespace MediaBrowser.Common.Net
+namespace MediaBrowser.Net
 {
     public class HttpServer : IObservable<RequestContext>, IDisposable
     {

+ 66 - 0
MediaBrowser.Net/MediaBrowser.Net.csproj

@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{5DA08D1C-0D52-4B1B-AA66-E4A171D938F6}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>MediaBrowser.Net</RootNamespace>
+    <AssemblyName>MediaBrowser.Net</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Reactive">
+      <HintPath>..\packages\Rx-Main.1.0.11226\lib\Net4\System.Reactive.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="CollectionExtensions.cs" />
+    <Compile Include="Handlers\BaseEmbeddedResourceHandler.cs" />
+    <Compile Include="Handlers\BaseJsonHandler.cs" />
+    <Compile Include="HttpServer.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Request.cs" />
+    <Compile Include="RequestContext.cs" />
+    <Compile Include="Handlers\BaseHandler.cs" />
+    <Compile Include="StreamExtensions.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 36 - 0
MediaBrowser.Net/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("MediaBrowser.Net")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("MediaBrowser.Net")]
+[assembly: AssemblyCopyright("Copyright ©  2012")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("eacc40b5-e24e-4467-8000-f40874048d45")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 1 - 1
MediaBrowser.Common/Net/Request.cs → MediaBrowser.Net/Request.cs

@@ -2,7 +2,7 @@
 using System.IO;
 using System.Linq;
 
-namespace MediaBrowser.Common.Net
+namespace MediaBrowser.Net
 {
     public class Request
     {

+ 8 - 2
MediaBrowser.Common/Net/RequestContext.cs → MediaBrowser.Net/RequestContext.cs

@@ -1,8 +1,9 @@
 using System;
 using System.Linq;
 using System.Net;
+using MediaBrowser.Net.Handlers;
 
-namespace MediaBrowser.Common.Net
+namespace MediaBrowser.Net
 {
     public class RequestContext
     {
@@ -23,7 +24,7 @@ namespace MediaBrowser.Common.Net
             Request = context.Request;
         }
 
-        public void Respond(Response handler)
+        public void Respond(BaseHandler handler)
         {
             Response.AddHeader("Access-Control-Allow-Origin", "*");
 
@@ -58,6 +59,11 @@ namespace MediaBrowser.Common.Net
 
             if (statusCode != 304)
             {
+                if (handler.GzipResponse)
+                {
+                    Response.AddHeader("Content-Encoding", "gzip");
+                }
+
                 if (cacheDuration.Ticks > 0)
                 {
                     CacheResponse(Response, cacheDuration, handler.LastDateModified);

+ 1 - 1
MediaBrowser.Common/Net/StreamExtensions.cs → MediaBrowser.Net/StreamExtensions.cs

@@ -2,7 +2,7 @@ using System;
 using System.IO;
 using System.Reactive.Linq;
 
-namespace MediaBrowser.Common.Net
+namespace MediaBrowser.Net
 {
     public static class StreamExtensions
     {

+ 4 - 0
MediaBrowser.Net/packages.config

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="Rx-Main" version="1.0.11226" targetFramework="net45" />
+</packages>

+ 1 - 5
MediaBrowser.TV/Entities/Episode.cs

@@ -1,13 +1,9 @@
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.TV.Entities
 {
     public class Episode : Video
     {
         public string SeasonNumber { get; set; }
-        public string EpisodeNumber { get; set; }
-        public DateTime? FirstAired { get; set; }
     }
 }

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

@@ -1,12 +1,10 @@
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.TV.Entities
 {
     public class Series : Folder
     {
-        public string TVDBSeriesId { get; set; }
+        public string TvdbId { get; set; }
         public string Status { get; set; }
     }
 }

+ 9 - 4
MediaBrowser.TV/Metadata/EpisodeXmlParser.cs

@@ -16,7 +16,7 @@ namespace MediaBrowser.TV.Metadata
                     {
                         string filename = reader.ReadElementContentAsString();
 
-                        if (!string.IsNullOrEmpty(filename))
+                        if (!string.IsNullOrWhiteSpace(filename))
                         {
                             string metadataFolder = Path.GetDirectoryName(item.Path);
                             item.PrimaryImagePath = Path.Combine(metadataFolder, filename);
@@ -24,7 +24,12 @@ namespace MediaBrowser.TV.Metadata
                         break;
                     }
                 case "EpisodeNumber":
-                    item.EpisodeNumber = reader.ReadElementContentAsString() ?? string.Empty;
+                    string number = reader.ReadElementContentAsString() ?? string.Empty;
+
+                    if (!string.IsNullOrWhiteSpace(number))
+                    {
+                        item.IndexNumber = int.Parse(number);
+                    }
                     break;
 
                 case "SeasonNumber":
@@ -39,13 +44,13 @@ namespace MediaBrowser.TV.Metadata
                     {
                         string firstAired = reader.ReadElementContentAsString() ?? string.Empty;
 
-                        if (!string.IsNullOrEmpty(firstAired))
+                        if (!string.IsNullOrWhiteSpace(firstAired))
                         {
                             DateTime airDate;
 
                             if (DateTime.TryParse(firstAired, out airDate) && airDate.Year > 1850)
                             {
-                                item.FirstAired = airDate;
+                                item.PremiereDate = airDate;
                                 item.ProductionYear = airDate.Year;
                             }
                         }

+ 2 - 2
MediaBrowser.TV/Metadata/SeriesXmlParser.cs

@@ -12,7 +12,7 @@ namespace MediaBrowser.TV.Metadata
             switch (reader.Name)
             {
                 case "id":
-                    item.TVDBSeriesId = reader.ReadElementContentAsString() ?? string.Empty;
+                    item.TvdbId = reader.ReadElementContentAsString() ?? string.Empty;
                     break;
 
                 case "SeriesName":
@@ -26,7 +26,7 @@ namespace MediaBrowser.TV.Metadata
                 case "Runtime":
                     {
                         string text = reader.ReadElementContentAsString() ?? string.Empty;
-                        if (!string.IsNullOrEmpty(text))
+                        if (!string.IsNullOrWhiteSpace(text))
                         {
 
                             int runtime;

+ 6 - 0
MediaBrowser.sln

@@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Common", "Medi
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Model", "MediaBrowser.Model\MediaBrowser.Model.csproj", "{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Net", "MediaBrowser.Net\MediaBrowser.Net.csproj", "{5DA08D1C-0D52-4B1B-AA66-E4A171D938F6}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -43,6 +45,10 @@ Global
 		{5758B2C7-949A-421D-B268-70A950CF8741}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{5758B2C7-949A-421D-B268-70A950CF8741}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{5758B2C7-949A-421D-B268-70A950CF8741}.Release|Any CPU.Build.0 = Release|Any CPU
+		{5DA08D1C-0D52-4B1B-AA66-E4A171D938F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{5DA08D1C-0D52-4B1B-AA66-E4A171D938F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{5DA08D1C-0D52-4B1B-AA66-E4A171D938F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{5DA08D1C-0D52-4B1B-AA66-E4A171D938F6}.Release|Any CPU.Build.0 = Release|Any CPU
 		{78AEA637-AF42-4F43-8E2B-0F2F0E2931F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{78AEA637-AF42-4F43-8E2B-0F2F0E2931F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{78AEA637-AF42-4F43-8E2B-0F2F0E2931F3}.Release|Any CPU.ActiveCfg = Release|Any CPU