From 80b3ad7bd20329e6a5bbf6eeb76af62c87434a7c Mon Sep 17 00:00:00 2001 From: LukePulverenti Luke Pulverenti luke pulverenti Date: Thu, 19 Jul 2012 22:22:44 -0400 Subject: Moved the http server to it's own assembly. added comments and made other minor re-organizations. --- MediaBrowser.Api/ApiService.cs | 91 +---- MediaBrowser.Api/HttpHandlers/GenreHandler.cs | 6 - MediaBrowser.Api/HttpHandlers/GenresHandler.cs | 10 +- MediaBrowser.Api/HttpHandlers/ImageHandler.cs | 24 +- .../HttpHandlers/InProgressItemsHandler.cs | 6 - MediaBrowser.Api/HttpHandlers/ItemHandler.cs | 33 +- MediaBrowser.Api/HttpHandlers/ItemListHandler.cs | 10 +- MediaBrowser.Api/HttpHandlers/JsonHandler.cs | 16 + MediaBrowser.Api/HttpHandlers/MediaHandler.cs | 26 +- MediaBrowser.Api/HttpHandlers/PersonHandler.cs | 8 +- .../HttpHandlers/RecentlyAddedItemsHandler.cs | 6 - MediaBrowser.Api/HttpHandlers/StudioHandler.cs | 6 - MediaBrowser.Api/HttpHandlers/StudiosHandler.cs | 11 +- MediaBrowser.Api/HttpHandlers/UsersHandler.cs | 8 +- MediaBrowser.Api/MediaBrowser.Api.csproj | 7 +- MediaBrowser.Api/Model/BaseItemInfo.cs | 15 - MediaBrowser.Api/Plugin.cs | 73 +++- .../ApiInteraction/ApiController.cs | 77 ++-- .../ApiInteraction/DictionaryBaseItem.cs | 412 --------------------- MediaBrowser.Common/Json/JsonSerializer.cs | 12 +- MediaBrowser.Common/Logging/Logger.cs | 4 - MediaBrowser.Common/MediaBrowser.Common.csproj | 12 - MediaBrowser.Common/Net/CollectionExtensions.cs | 14 - .../Net/Handlers/BaseEmbeddedResourceHandler.cs | 70 ---- MediaBrowser.Common/Net/Handlers/JsonHandler.cs | 36 -- MediaBrowser.Common/Net/HttpServer.cs | 42 --- MediaBrowser.Common/Net/Request.cs | 18 - MediaBrowser.Common/Net/RequestContext.cs | 112 ------ MediaBrowser.Common/Net/Response.cs | 77 ---- MediaBrowser.Common/Net/StreamExtensions.cs | 19 - MediaBrowser.Common/Plugins/BasePlugin.cs | 6 +- .../Plugins/BasePluginConfiguration.cs | 7 +- MediaBrowser.Common/packages.config | 1 - .../Events/ItemResolveEventArgs.cs | 11 +- MediaBrowser.Controller/IO/DirectoryWatchers.cs | 4 +- MediaBrowser.Controller/IO/Shortcut.cs | 3 + MediaBrowser.Controller/Kernel.cs | 126 ++++++- MediaBrowser.Controller/Library/ItemController.cs | 6 + .../MediaBrowser.Controller.csproj | 4 + MediaBrowser.Controller/Resolvers/AudioResolver.cs | 2 + .../Resolvers/BaseItemResolver.cs | 19 + .../Resolvers/FolderResolver.cs | 1 + MediaBrowser.Controller/Resolvers/VideoResolver.cs | 14 +- MediaBrowser.Controller/UserController.cs | 35 +- MediaBrowser.Controller/Xml/BaseItemXmlParser.cs | 30 +- MediaBrowser.Controller/Xml/FolderXmlParser.cs | 4 + MediaBrowser.Controller/Xml/XmlExtensions.cs | 13 +- .../Handlers/EmbeddedResourceHandler.cs | 8 +- .../MediaBrowser.HtmlBrowser.csproj | 4 + MediaBrowser.HtmlBrowser/Plugin.cs | 6 +- MediaBrowser.Model/Entities/ApiBaseItem.cs | 39 ++ MediaBrowser.Model/Entities/BaseItem.cs | 78 ++-- MediaBrowser.Model/Entities/Folder.cs | 44 +-- MediaBrowser.Model/MediaBrowser.Model.csproj | 1 + MediaBrowser.Model/Users/User.cs | 1 - MediaBrowser.Movies/Entities/Movie.cs | 2 +- MediaBrowser.Movies/Metadata/MovieXmlParser.cs | 2 +- MediaBrowser.Net/CollectionExtensions.cs | 14 + .../Handlers/BaseEmbeddedResourceHandler.cs | 58 +++ MediaBrowser.Net/Handlers/BaseHandler.cs | 111 ++++++ MediaBrowser.Net/Handlers/BaseJsonHandler.cs | 11 + MediaBrowser.Net/HttpServer.cs | 42 +++ MediaBrowser.Net/MediaBrowser.Net.csproj | 66 ++++ MediaBrowser.Net/Properties/AssemblyInfo.cs | 36 ++ MediaBrowser.Net/Request.cs | 18 + MediaBrowser.Net/RequestContext.cs | 118 ++++++ MediaBrowser.Net/StreamExtensions.cs | 19 + MediaBrowser.Net/packages.config | 4 + MediaBrowser.TV/Entities/Episode.cs | 6 +- MediaBrowser.TV/Entities/Series.cs | 6 +- MediaBrowser.TV/Metadata/EpisodeXmlParser.cs | 13 +- MediaBrowser.TV/Metadata/SeriesXmlParser.cs | 4 +- MediaBrowser.sln | 6 + 73 files changed, 1053 insertions(+), 1211 deletions(-) create mode 100644 MediaBrowser.Api/HttpHandlers/JsonHandler.cs delete mode 100644 MediaBrowser.Api/Model/BaseItemInfo.cs delete mode 100644 MediaBrowser.Common/ApiInteraction/DictionaryBaseItem.cs delete mode 100644 MediaBrowser.Common/Net/CollectionExtensions.cs delete mode 100644 MediaBrowser.Common/Net/Handlers/BaseEmbeddedResourceHandler.cs delete mode 100644 MediaBrowser.Common/Net/Handlers/JsonHandler.cs delete mode 100644 MediaBrowser.Common/Net/HttpServer.cs delete mode 100644 MediaBrowser.Common/Net/Request.cs delete mode 100644 MediaBrowser.Common/Net/RequestContext.cs delete mode 100644 MediaBrowser.Common/Net/Response.cs delete mode 100644 MediaBrowser.Common/Net/StreamExtensions.cs create mode 100644 MediaBrowser.Model/Entities/ApiBaseItem.cs create mode 100644 MediaBrowser.Net/CollectionExtensions.cs create mode 100644 MediaBrowser.Net/Handlers/BaseEmbeddedResourceHandler.cs create mode 100644 MediaBrowser.Net/Handlers/BaseHandler.cs create mode 100644 MediaBrowser.Net/Handlers/BaseJsonHandler.cs create mode 100644 MediaBrowser.Net/HttpServer.cs create mode 100644 MediaBrowser.Net/MediaBrowser.Net.csproj create mode 100644 MediaBrowser.Net/Properties/AssemblyInfo.cs create mode 100644 MediaBrowser.Net/Request.cs create mode 100644 MediaBrowser.Net/RequestContext.cs create mode 100644 MediaBrowser.Net/StreamExtensions.cs create mode 100644 MediaBrowser.Net/packages.config diff --git a/MediaBrowser.Api/ApiService.cs b/MediaBrowser.Api/ApiService.cs index 2687ce81a0..e495937790 100644 --- a/MediaBrowser.Api/ApiService.cs +++ b/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 { + /// + /// Contains some helpers for the api + /// 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 GetAllStudios(Folder parent, Guid userId) + /// + /// Takes a BaseItem and returns the actual object that will be serialized by the api + /// + public static ApiBaseItemWrapper GetSerializationObject(BaseItem item, bool includeChildren, Guid userId) { - Dictionary data = new Dictionary(); - - IEnumerable allItems = Kernel.Instance.GetParentalAllowedRecursiveChildren(parent, userId); - - foreach (var item in allItems) + ApiBaseItemWrapper wrapper = new ApiBaseItemWrapper() { - 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 list = new List(); - - 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 GetAllGenres(Folder parent, Guid userId) - { - Dictionary data = new Dictionary(); - - IEnumerable 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 list = new List(); - - foreach (string key in data.Keys) - { - list.Add(new CategoryInfo() - { - Name = key, - ItemCount = data[key] - - }); - } - - return list; + return wrapper; } } } diff --git a/MediaBrowser.Api/HttpHandlers/GenreHandler.cs b/MediaBrowser.Api/HttpHandlers/GenreHandler.cs index f43e3e1b02..477705e827 100644 --- a/MediaBrowser.Api/HttpHandlers/GenreHandler.cs +++ b/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 /// public class GenreHandler : ItemListHandler { - public GenreHandler(RequestContext ctx) - : base(ctx) - { - } - protected override IEnumerable ItemsToSerialize { get diff --git a/MediaBrowser.Api/HttpHandlers/GenresHandler.cs b/MediaBrowser.Api/HttpHandlers/GenresHandler.cs index 794b746cd4..e8e3fd80b7 100644 --- a/MediaBrowser.Api/HttpHandlers/GenresHandler.cs +++ b/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); } } } diff --git a/MediaBrowser.Api/HttpHandlers/ImageHandler.cs b/MediaBrowser.Api/HttpHandlers/ImageHandler.cs index ed6dcc3a9d..ae6228ae87 100644 --- a/MediaBrowser.Api/HttpHandlers/ImageHandler.cs +++ b/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() diff --git a/MediaBrowser.Api/HttpHandlers/InProgressItemsHandler.cs b/MediaBrowser.Api/HttpHandlers/InProgressItemsHandler.cs index e13cee866c..2bee275a45 100644 --- a/MediaBrowser.Api/HttpHandlers/InProgressItemsHandler.cs +++ b/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 ItemsToSerialize { get diff --git a/MediaBrowser.Api/HttpHandlers/ItemHandler.cs b/MediaBrowser.Api/HttpHandlers/ItemHandler.cs index dd508d1037..2ff2354939 100644 --- a/MediaBrowser.Api/HttpHandlers/ItemHandler.cs +++ b/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 diff --git a/MediaBrowser.Api/HttpHandlers/ItemListHandler.cs b/MediaBrowser.Api/HttpHandlers/ItemListHandler.cs index ed6e895f38..54af1b7e91 100644 --- a/MediaBrowser.Api/HttpHandlers/ItemListHandler.cs +++ b/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); }); } diff --git a/MediaBrowser.Api/HttpHandlers/JsonHandler.cs b/MediaBrowser.Api/HttpHandlers/JsonHandler.cs new file mode 100644 index 0000000000..e663085ec7 --- /dev/null +++ b/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); + } + } +} diff --git a/MediaBrowser.Api/HttpHandlers/MediaHandler.cs b/MediaBrowser.Api/HttpHandlers/MediaHandler.cs index d04ff21996..47aab3c410 100644 --- a/MediaBrowser.Api/HttpHandlers/MediaHandler.cs +++ b/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 { } } - } } diff --git a/MediaBrowser.Api/HttpHandlers/PersonHandler.cs b/MediaBrowser.Api/HttpHandlers/PersonHandler.cs index 6a9d376791..3c1a0ecf3a 100644 --- a/MediaBrowser.Api/HttpHandlers/PersonHandler.cs +++ b/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 diff --git a/MediaBrowser.Api/HttpHandlers/RecentlyAddedItemsHandler.cs b/MediaBrowser.Api/HttpHandlers/RecentlyAddedItemsHandler.cs index 8d85eedf4e..f36c9c9975 100644 --- a/MediaBrowser.Api/HttpHandlers/RecentlyAddedItemsHandler.cs +++ b/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 ItemsToSerialize { get diff --git a/MediaBrowser.Api/HttpHandlers/StudioHandler.cs b/MediaBrowser.Api/HttpHandlers/StudioHandler.cs index 9426d0b7c0..019ced0281 100644 --- a/MediaBrowser.Api/HttpHandlers/StudioHandler.cs +++ b/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 /// public class StudioHandler : ItemListHandler { - public StudioHandler(RequestContext ctx) - : base(ctx) - { - } - protected override IEnumerable ItemsToSerialize { get diff --git a/MediaBrowser.Api/HttpHandlers/StudiosHandler.cs b/MediaBrowser.Api/HttpHandlers/StudiosHandler.cs index 6b83b9e64f..66daca7774 100644 --- a/MediaBrowser.Api/HttpHandlers/StudiosHandler.cs +++ b/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); } } } diff --git a/MediaBrowser.Api/HttpHandlers/UsersHandler.cs b/MediaBrowser.Api/HttpHandlers/UsersHandler.cs index aa8dac11e4..816d4e83ac 100644 --- a/MediaBrowser.Api/HttpHandlers/UsersHandler.cs +++ b/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 diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index f493da552a..dd5c072324 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -37,6 +37,7 @@ False ..\packages\Rx-Main.1.0.11226\lib\Net4\System.Reactive.dll + @@ -51,6 +52,7 @@ + @@ -58,7 +60,6 @@ - @@ -75,6 +76,10 @@ {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b} MediaBrowser.Model + + {5da08d1c-0d52-4b1b-aa66-e4a171d938f6} + MediaBrowser.Net + diff --git a/MediaBrowser.Api/Model/BaseItemInfo.cs b/MediaBrowser.Api/Model/BaseItemInfo.cs deleted file mode 100644 index 646c273905..0000000000 --- a/MediaBrowser.Api/Model/BaseItemInfo.cs +++ /dev/null @@ -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 Children { get; set; } - } -} diff --git a/MediaBrowser.Api/Plugin.cs b/MediaBrowser.Api/Plugin.cs index 91bd906c8b..af885ef825 100644 --- a/MediaBrowser.Api/Plugin.cs +++ b/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; } } } diff --git a/MediaBrowser.Common/ApiInteraction/ApiController.cs b/MediaBrowser.Common/ApiInteraction/ApiController.cs index f237573550..b3a51c512e 100644 --- a/MediaBrowser.Common/ApiInteraction/ApiController.cs +++ b/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 GetRootItem(Guid userId) + public async Task> 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 GetItem(Guid id, Guid userId) + public async Task> 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>(gzipStream); + using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, false)) + { + return JsonSerializer.DeserializeFromStream>(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>(gzipStream); + using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, false)) + { + return JsonSerializer.DeserializeFromStream>(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(gzipStream); + using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, false)) + { + return JsonSerializer.DeserializeFromStream(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>(gzipStream); + using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, false)) + { + return JsonSerializer.DeserializeFromStream>(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(gzipStream); + using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, false)) + { + return JsonSerializer.DeserializeFromStream(gzipStream); + } } } + + private static ApiBaseItemWrapper DeserializeBaseItemWrapper(Stream stream) + { + ApiBaseItemWrapper data = JsonSerializer.DeserializeFromStream>(stream); + + return data; + } } } diff --git a/MediaBrowser.Common/ApiInteraction/DictionaryBaseItem.cs b/MediaBrowser.Common/ApiInteraction/DictionaryBaseItem.cs deleted file mode 100644 index 2bd6f1bcd5..0000000000 --- a/MediaBrowser.Common/ApiInteraction/DictionaryBaseItem.cs +++ /dev/null @@ -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 Dictionary { get; set; } - - public UserItemData UserItemData { get; set; } - public IEnumerable Children { get; set; } - - public DictionaryBaseItem(Dictionary 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(string name, T value) - { - Dictionary[name] = value; - } - - public static DictionaryBaseItem FromApiOutput(Stream stream) - { - Dictionary data = JsonSerializer.DeserializeFromStream>(stream); - - string baseItem = data["Item"] as string; - - DictionaryBaseItem item = new DictionaryBaseItem(JsonSerializer.DeserializeFromString>(baseItem)); - - if (data.ContainsKey("UserItemData")) - { - item.UserItemData = JsonSerializer.DeserializeFromString(data["UserItemData"].ToString()); - } - - if (data.ContainsKey("Children")) - { - item.Children = JsonSerializer.DeserializeFromString>>(data["Children"].ToString()).Select(c => new DictionaryBaseItem(c)); - } - - return item; - } - } -} diff --git a/MediaBrowser.Common/Json/JsonSerializer.cs b/MediaBrowser.Common/Json/JsonSerializer.cs index 55663357ab..a882334897 100644 --- a/MediaBrowser.Common/Json/JsonSerializer.cs +++ b/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 o, Stream stream) + public static void SerializeToStream(T obj, Stream stream) { Configure(); - ServiceStack.Text.JsonSerializer.SerializeToStream(o, stream); + ServiceStack.Text.JsonSerializer.SerializeToStream(obj, stream); } - public static void SerializeToFile(T o, string file) + public static void SerializeToFile(T obj, string file) { Configure(); using (StreamWriter streamWriter = new StreamWriter(file)) { - ServiceStack.Text.JsonSerializer.SerializeToWriter(o, streamWriter); + ServiceStack.Text.JsonSerializer.SerializeToWriter(obj, streamWriter); } } @@ -44,12 +45,11 @@ namespace MediaBrowser.Common.Json return ServiceStack.Text.JsonSerializer.DeserializeFromString(data); } - + private static void Configure() { ServiceStack.Text.JsConfig.ExcludeTypeInfo = true; ServiceStack.Text.JsConfig.IncludeNullValues = false; - ServiceStack.Text.JsConfig.DateHandler = ServiceStack.Text.JsonDateHandler.ISO8601; } } } diff --git a/MediaBrowser.Common/Logging/Logger.cs b/MediaBrowser.Common/Logging/Logger.cs index 5f4c2ff795..d1ae9b8f35 100644 --- a/MediaBrowser.Common/Logging/Logger.cs +++ b/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 { diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index f822b1a2bd..252c9c8cca 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -35,9 +35,6 @@ - - ..\packages\Rx-Main.1.0.11226\lib\Net4\System.Reactive.dll - @@ -48,15 +45,6 @@ - - - - - - - - - diff --git a/MediaBrowser.Common/Net/CollectionExtensions.cs b/MediaBrowser.Common/Net/CollectionExtensions.cs deleted file mode 100644 index 98d24dfc04..0000000000 --- a/MediaBrowser.Common/Net/CollectionExtensions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; - -namespace MediaBrowser.Common.Net -{ - public static class CollectionExtensions - { - public static IDictionary> ToDictionary(this NameValueCollection source) - { - return source.AllKeys.ToDictionary>(key => key, source.GetValues); - } - } -} \ No newline at end of file diff --git a/MediaBrowser.Common/Net/Handlers/BaseEmbeddedResourceHandler.cs b/MediaBrowser.Common/Net/Handlers/BaseEmbeddedResourceHandler.cs deleted file mode 100644 index e64773f33f..0000000000 --- a/MediaBrowser.Common/Net/Handlers/BaseEmbeddedResourceHandler.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.IO; -using System.IO.Compression; -using System; - -namespace MediaBrowser.Common.Net.Handlers -{ - public abstract class BaseEmbeddedResourceHandler : Response - { - public BaseEmbeddedResourceHandler(RequestContext ctx, string resourcePath) - : base(ctx) - { - ResourcePath = resourcePath; - - Headers["Content-Encoding"] = "gzip"; - - WriteStream = s => - { - WriteReponse(s); - s.Close(); - }; - } - - protected string ResourcePath { get; set; } - - public override string ContentType - { - get - { - string extension = Path.GetExtension(ResourcePath); - - if (extension.EndsWith("jpeg", StringComparison.OrdinalIgnoreCase) || extension.EndsWith("jpg", StringComparison.OrdinalIgnoreCase)) - { - return "image/jpeg"; - } - else if (extension.EndsWith("png", StringComparison.OrdinalIgnoreCase)) - { - return "image/png"; - } - else if (extension.EndsWith("ico", StringComparison.OrdinalIgnoreCase)) - { - return "image/ico"; - } - else if (extension.EndsWith("js", StringComparison.OrdinalIgnoreCase)) - { - return "application/x-javascript"; - } - else if (extension.EndsWith("css", StringComparison.OrdinalIgnoreCase)) - { - return "text/css"; - } - else if (extension.EndsWith("html", StringComparison.OrdinalIgnoreCase)) - { - return "text/html; charset=utf-8"; - } - - return "text/plain; charset=utf-8"; - } - } - - private void WriteReponse(Stream stream) - { - using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Compress, false)) - { - GetEmbeddedResourceStream().CopyTo(gzipStream); - } - } - - protected abstract Stream GetEmbeddedResourceStream(); - } -} diff --git a/MediaBrowser.Common/Net/Handlers/JsonHandler.cs b/MediaBrowser.Common/Net/Handlers/JsonHandler.cs deleted file mode 100644 index ecfdd311a1..0000000000 --- a/MediaBrowser.Common/Net/Handlers/JsonHandler.cs +++ /dev/null @@ -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); - } - } - } -} diff --git a/MediaBrowser.Common/Net/HttpServer.cs b/MediaBrowser.Common/Net/HttpServer.cs deleted file mode 100644 index fad8d13eb9..0000000000 --- a/MediaBrowser.Common/Net/HttpServer.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Net; -using System.Reactive.Linq; - -namespace MediaBrowser.Common.Net -{ - public class HttpServer : IObservable, IDisposable - { - private readonly HttpListener listener; - private readonly IObservable stream; - - public HttpServer(string url) - { - listener = new HttpListener(); - listener.Prefixes.Add(url); - listener.Start(); - stream = ObservableHttpContext(); - } - - private IObservable ObservableHttpContext() - { - return Observable.Create(obs => - Observable.FromAsyncPattern(listener.BeginGetContext, - listener.EndGetContext)() - .Select(c => new RequestContext(c)) - .Subscribe(obs)) - .Repeat() - .Retry() - .Publish() - .RefCount(); - } - public void Dispose() - { - listener.Stop(); - } - - public IDisposable Subscribe(IObserver observer) - { - return stream.Subscribe(observer); - } - } -} \ No newline at end of file diff --git a/MediaBrowser.Common/Net/Request.cs b/MediaBrowser.Common/Net/Request.cs deleted file mode 100644 index 795c9c36ba..0000000000 --- a/MediaBrowser.Common/Net/Request.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace MediaBrowser.Common.Net -{ - public class Request - { - public string HttpMethod { get; set; } - public IDictionary> Headers { get; set; } - public Stream InputStream { get; set; } - public string RawUrl { get; set; } - public int ContentLength - { - get { return int.Parse(Headers["Content-Length"].First()); } - } - } -} \ No newline at end of file diff --git a/MediaBrowser.Common/Net/RequestContext.cs b/MediaBrowser.Common/Net/RequestContext.cs deleted file mode 100644 index d3635f34a5..0000000000 --- a/MediaBrowser.Common/Net/RequestContext.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System; -using System.Linq; -using System.Net; - -namespace MediaBrowser.Common.Net -{ - public class RequestContext - { - public HttpListenerRequest Request { get; private set; } - public HttpListenerResponse Response { get; private set; } - - public string LocalPath - { - get - { - return Request.Url.LocalPath; - } - } - - public RequestContext(HttpListenerContext context) - { - Response = context.Response; - Request = context.Request; - } - - public void Respond(Response handler) - { - Response.AddHeader("Access-Control-Allow-Origin", "*"); - - Response.KeepAlive = true; - - foreach (var header in handler.Headers) - { - Response.AddHeader(header.Key, header.Value); - } - - int statusCode = handler.StatusCode; - Response.ContentType = handler.ContentType; - - TimeSpan cacheDuration = handler.CacheDuration; - - if (Request.Headers.AllKeys.Contains("If-Modified-Since")) - { - DateTime ifModifiedSince; - - if (DateTime.TryParse(Request.Headers["If-Modified-Since"].Replace(" GMT", string.Empty), out ifModifiedSince)) - { - // If the cache hasn't expired yet just return a 304 - if (IsCacheValid(ifModifiedSince, cacheDuration, handler.LastDateModified)) - { - statusCode = 304; - } - } - } - - Response.SendChunked = true; - Response.StatusCode = statusCode; - - if (statusCode != 304) - { - if (cacheDuration.Ticks > 0) - { - CacheResponse(Response, cacheDuration, handler.LastDateModified); - } - handler.WriteStream(Response.OutputStream); - } - else - { - Response.OutputStream.Flush(); - Response.OutputStream.Close(); - } - } - - private void CacheResponse(HttpListenerResponse response, TimeSpan duration, DateTime? dateModified) - { - DateTime lastModified = dateModified ?? DateTime.Now; - - response.Headers[HttpResponseHeader.CacheControl] = "Public"; - response.Headers[HttpResponseHeader.Expires] = DateTime.Now.Add(duration).ToString("r"); - response.Headers[HttpResponseHeader.LastModified] = lastModified.ToString("r"); - } - - private bool IsCacheValid(DateTime ifModifiedSince, TimeSpan cacheDuration, DateTime? dateModified) - { - if (dateModified.HasValue) - { - DateTime lastModified = NormalizeDateForComparison(dateModified.Value); - ifModifiedSince = NormalizeDateForComparison(ifModifiedSince); - - return lastModified <= ifModifiedSince; - } - - DateTime cacheExpirationDate = ifModifiedSince.Add(cacheDuration); - - if (DateTime.Now < cacheExpirationDate) - { - return true; - } - - return false; - } - - /// - /// When the browser sends the IfModifiedDate, it's precision is limited to seconds, so this will account for that - /// - private DateTime NormalizeDateForComparison(DateTime date) - { - return new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second); - } - - } -} \ No newline at end of file diff --git a/MediaBrowser.Common/Net/Response.cs b/MediaBrowser.Common/Net/Response.cs deleted file mode 100644 index 44b4e29622..0000000000 --- a/MediaBrowser.Common/Net/Response.cs +++ /dev/null @@ -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(); - } - - 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 Headers { get; set; } - public Action 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)) - { - } - }*/ -} \ No newline at end of file diff --git a/MediaBrowser.Common/Net/StreamExtensions.cs b/MediaBrowser.Common/Net/StreamExtensions.cs deleted file mode 100644 index c10e458ada..0000000000 --- a/MediaBrowser.Common/Net/StreamExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.IO; -using System.Reactive.Linq; - -namespace MediaBrowser.Common.Net -{ - public static class StreamExtensions - { - public static IObservable ReadBytes(this Stream stream, int count) - { - var buffer = new byte[count]; - return Observable.FromAsyncPattern((cb, state) => stream.BeginRead(buffer, 0, count, cb, state), ar => - { - stream.EndRead(ar); - return buffer; - })(); - } - } -} \ No newline at end of file diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs index 3438b09c20..be72dabca3 100644 --- a/MediaBrowser.Common/Plugins/BasePlugin.cs +++ b/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 diff --git a/MediaBrowser.Common/Plugins/BasePluginConfiguration.cs b/MediaBrowser.Common/Plugins/BasePluginConfiguration.cs index ad7972d949..d3e47c84a4 100644 --- a/MediaBrowser.Common/Plugins/BasePluginConfiguration.cs +++ b/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 diff --git a/MediaBrowser.Common/packages.config b/MediaBrowser.Common/packages.config index 15a978d36c..aad898882a 100644 --- a/MediaBrowser.Common/packages.config +++ b/MediaBrowser.Common/packages.config @@ -1,5 +1,4 @@  - \ No newline at end of file diff --git a/MediaBrowser.Controller/Events/ItemResolveEventArgs.cs b/MediaBrowser.Controller/Events/ItemResolveEventArgs.cs index c05c139fb0..7c4af71ebc 100644 --- a/MediaBrowser.Controller/Events/ItemResolveEventArgs.cs +++ b/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 { + /// + /// This is an EventArgs object used when resolving a Path into a BaseItem + /// public class ItemResolveEventArgs : PreBeginResolveEventArgs { public IEnumerable> FileSystemChildren { get; set; } @@ -57,6 +59,11 @@ namespace MediaBrowser.Controller.Events } } + /// + /// 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 + /// public class PreBeginResolveEventArgs : EventArgs { public string Path { get; set; } diff --git a/MediaBrowser.Controller/IO/DirectoryWatchers.cs b/MediaBrowser.Controller/IO/DirectoryWatchers.cs index dd27695834..8d102e80ce 100644 --- a/MediaBrowser.Controller/IO/DirectoryWatchers.cs +++ b/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()) { - foreach (Folder subFolder in folder.FolderChildren) + foreach (Folder subFolder in folder.Children.OfType()) { if (Path.IsPathRooted(subFolder.Path)) { diff --git a/MediaBrowser.Controller/IO/Shortcut.cs b/MediaBrowser.Controller/IO/Shortcut.cs index 376d16a795..83e2c0eda3 100644 --- a/MediaBrowser.Controller/IO/Shortcut.cs +++ b/MediaBrowser.Controller/IO/Shortcut.cs @@ -5,6 +5,9 @@ using System.Text; namespace MediaBrowser.Controller.IO { + /// + /// Contains helpers to interact with shortcut files (.lnk) + /// public static class Shortcut { #region Signitures were imported from http://pinvoke.net diff --git a/MediaBrowser.Controller/Kernel.cs b/MediaBrowser.Controller/Kernel.cs index 3f6e7bbb0e..9cbef68740 100644 --- a/MediaBrowser.Controller/Kernel.cs +++ b/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 /// public IEnumerable GetParentalAllowedChildren(Folder folder, Guid userId) { - return folder.Children.ToList().Where(i => IsParentalAllowed(i, userId)); + return folder.Children.Where(i => IsParentalAllowed(i, userId)); } /// @@ -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); } /// @@ -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))); } + + /// + /// 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 + /// + public IEnumerable GetAllStudios(Folder parent, Guid userId) + { + Dictionary data = new Dictionary(); + + // Get all the allowed recursive children + IEnumerable 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 list = new List(); + + 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; + } + + /// + /// 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 + /// + public IEnumerable GetAllGenres(Folder parent, Guid userId) + { + Dictionary data = new Dictionary(); + + // Get all the allowed recursive children + IEnumerable 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 list = new List(); + + 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; + } + } } diff --git a/MediaBrowser.Controller/Library/ItemController.cs b/MediaBrowser.Controller/Library/ItemController.cs index b872e8dbae..fee53e3bb6 100644 --- a/MediaBrowser.Controller/Library/ItemController.cs +++ b/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 diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 719d279eeb..1f57090a67 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -66,6 +66,10 @@ {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b} MediaBrowser.Model + + {5da08d1c-0d52-4b1b-aa66-e4a171d938f6} + MediaBrowser.Net + diff --git a/MediaBrowser.Controller/Resolvers/AudioResolver.cs b/MediaBrowser.Controller/Resolvers/AudioResolver.cs index f9ce5ecd70..2ca54e71d4 100644 --- a/MediaBrowser.Controller/Resolvers/AudioResolver.cs +++ b/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)) diff --git a/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs b/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs index e2c49e90c1..dccdf57e1b 100644 --- a/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs +++ b/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs @@ -15,6 +15,9 @@ namespace MediaBrowser.Controller.Resolvers return null; } + /// + /// Sets initial values on the newly resolved item + /// 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 } + /// + /// Ensures DateCreated and DateModified have values + /// private void EnsureDates(T item) { // If the subclass didn't supply dates, add them here @@ -73,6 +85,9 @@ namespace MediaBrowser.Controller.Resolvers } } + /// + /// Fills in image paths based on files win the folder + /// protected virtual void PopulateImages(T item, ItemResolveEventArgs args) { List backdropFiles = new List(); @@ -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 } } + /// + /// Weed this to keep a list of resolvers, since Resolvers are built with generics + /// public interface IBaseItemResolver { BaseItem ResolvePath(ItemResolveEventArgs args); diff --git a/MediaBrowser.Controller/Resolvers/FolderResolver.cs b/MediaBrowser.Controller/Resolvers/FolderResolver.cs index 5c57c6bb3c..6101a45fd8 100644 --- a/MediaBrowser.Controller/Resolvers/FolderResolver.cs +++ b/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); } diff --git a/MediaBrowser.Controller/Resolvers/VideoResolver.cs b/MediaBrowser.Controller/Resolvers/VideoResolver.cs index ba51dab875..df304c3290 100644 --- a/MediaBrowser.Controller/Resolvers/VideoResolver.cs +++ b/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 { + /// + /// Resolves a Path into a Video + /// public class VideoResolver : BaseVideoResolver diff --git a/MediaBrowser.HtmlBrowser/Plugin.cs b/MediaBrowser.HtmlBrowser/Plugin.cs index 1599881d0b..db3f72f6f5 100644 --- a/MediaBrowser.HtmlBrowser/Plugin.cs +++ b/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)); - }); + });*/ } } } diff --git a/MediaBrowser.Model/Entities/ApiBaseItem.cs b/MediaBrowser.Model/Entities/ApiBaseItem.cs new file mode 100644 index 0000000000..bdab9239a1 --- /dev/null +++ b/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 +{ + /// + /// 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 + /// + public class ApiBaseItem : BaseItem + { + } + + /// + /// This is the full return object when requesting an Item + /// + public class ApiBaseItemWrapper + where T : BaseItem + { + public T Item { get; set; } + + public UserItemData UserItemData { get; set; } + + public IEnumerable> Children { get; set; } + + [IgnoreDataMember] + public Type ItemType { get; set; } + + public string Type + { + get + { + return ItemType.Name; + } + } + } +} diff --git a/MediaBrowser.Model/Entities/BaseItem.cs b/MediaBrowser.Model/Entities/BaseItem.cs index 165328ac7b..46f859c9dc 100644 --- a/MediaBrowser.Model/Entities/BaseItem.cs +++ b/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; } + /// + /// When the item first debuted. For movies this could be premiere date, episodes would be first aired + /// + 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 BackdropImagePaths { get; set; } + public IEnumerable 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 People { get; set; } + public IEnumerable People { get; set; } - public virtual IEnumerable Studios { get; set; } + public IEnumerable Studios { get; set; } - public virtual IEnumerable Genres { get; set; } + public IEnumerable 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; } + + /// + /// 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. + /// + public int? IndexNumber { get; set; } [IgnoreDataMember] - public virtual IEnumerable + diff --git a/MediaBrowser.Model/Users/User.cs b/MediaBrowser.Model/Users/User.cs index 316e5b55c7..3761d981d4 100644 --- a/MediaBrowser.Model/Users/User.cs +++ b/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 _ItemData = new Dictionary(); diff --git a/MediaBrowser.Movies/Entities/Movie.cs b/MediaBrowser.Movies/Entities/Movie.cs index 2c44e7917e..585b5b7773 100644 --- a/MediaBrowser.Movies/Entities/Movie.cs +++ b/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 { diff --git a/MediaBrowser.Movies/Metadata/MovieXmlParser.cs b/MediaBrowser.Movies/Metadata/MovieXmlParser.cs index d8bbb49cbf..67650fa985 100644 --- a/MediaBrowser.Movies/Metadata/MovieXmlParser.cs +++ b/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; } diff --git a/MediaBrowser.Net/CollectionExtensions.cs b/MediaBrowser.Net/CollectionExtensions.cs new file mode 100644 index 0000000000..a6b59904f3 --- /dev/null +++ b/MediaBrowser.Net/CollectionExtensions.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; + +namespace MediaBrowser.Net +{ + public static class CollectionExtensions + { + public static IDictionary> ToDictionary(this NameValueCollection source) + { + return source.AllKeys.ToDictionary>(key => key, source.GetValues); + } + } +} \ No newline at end of file diff --git a/MediaBrowser.Net/Handlers/BaseEmbeddedResourceHandler.cs b/MediaBrowser.Net/Handlers/BaseEmbeddedResourceHandler.cs new file mode 100644 index 0000000000..d7f579d05a --- /dev/null +++ b/MediaBrowser.Net/Handlers/BaseEmbeddedResourceHandler.cs @@ -0,0 +1,58 @@ +using System; +using System.IO; + +namespace MediaBrowser.Net.Handlers +{ + public abstract class BaseEmbeddedResourceHandler : BaseHandler + { + public BaseEmbeddedResourceHandler(string resourcePath) + : base() + { + ResourcePath = resourcePath; + } + + protected string ResourcePath { get; set; } + + public override string ContentType + { + get + { + string extension = Path.GetExtension(ResourcePath); + + if (extension.EndsWith("jpeg", StringComparison.OrdinalIgnoreCase) || extension.EndsWith("jpg", StringComparison.OrdinalIgnoreCase)) + { + return "image/jpeg"; + } + else if (extension.EndsWith("png", StringComparison.OrdinalIgnoreCase)) + { + return "image/png"; + } + else if (extension.EndsWith("ico", StringComparison.OrdinalIgnoreCase)) + { + return "image/ico"; + } + else if (extension.EndsWith("js", StringComparison.OrdinalIgnoreCase)) + { + return "application/x-javascript"; + } + else if (extension.EndsWith("css", StringComparison.OrdinalIgnoreCase)) + { + return "text/css"; + } + else if (extension.EndsWith("html", StringComparison.OrdinalIgnoreCase)) + { + return "text/html; charset=utf-8"; + } + + return "text/plain; charset=utf-8"; + } + } + + protected override void WriteResponseToOutputStream(Stream stream) + { + GetEmbeddedResourceStream().CopyTo(stream); + } + + protected abstract Stream GetEmbeddedResourceStream(); + } +} diff --git a/MediaBrowser.Net/Handlers/BaseHandler.cs b/MediaBrowser.Net/Handlers/BaseHandler.cs new file mode 100644 index 0000000000..7a2bcffe97 --- /dev/null +++ b/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 + { + /// + /// Response headers + /// + public IDictionary Headers = new Dictionary(); + + /// + /// The action to write the response to the output stream + /// + public Action WriteStream { get; set; } + + /// + /// The original RequestContext + /// + public RequestContext RequestContext { get; set; } + + /// + /// The original QueryString + /// + protected NameValueCollection QueryString + { + get + { + return RequestContext.Request.QueryString; + } + } + + /// + /// Gets the MIME type to include in the response headers + /// + public abstract string ContentType { get; } + + /// + /// Gets the status code to include in the response headers + /// + public virtual int StatusCode + { + get + { + return 200; + } + } + + /// + /// Gets the cache duration to include in the response headers + /// + public virtual TimeSpan CacheDuration + { + get + { + return TimeSpan.FromTicks(0); + } + } + + /// + /// 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. + /// + 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); + + } +} \ No newline at end of file diff --git a/MediaBrowser.Net/Handlers/BaseJsonHandler.cs b/MediaBrowser.Net/Handlers/BaseJsonHandler.cs new file mode 100644 index 0000000000..725b89c32e --- /dev/null +++ b/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"; } + } + } +} diff --git a/MediaBrowser.Net/HttpServer.cs b/MediaBrowser.Net/HttpServer.cs new file mode 100644 index 0000000000..aa24538d05 --- /dev/null +++ b/MediaBrowser.Net/HttpServer.cs @@ -0,0 +1,42 @@ +using System; +using System.Net; +using System.Reactive.Linq; + +namespace MediaBrowser.Net +{ + public class HttpServer : IObservable, IDisposable + { + private readonly HttpListener listener; + private readonly IObservable stream; + + public HttpServer(string url) + { + listener = new HttpListener(); + listener.Prefixes.Add(url); + listener.Start(); + stream = ObservableHttpContext(); + } + + private IObservable ObservableHttpContext() + { + return Observable.Create(obs => + Observable.FromAsyncPattern(listener.BeginGetContext, + listener.EndGetContext)() + .Select(c => new RequestContext(c)) + .Subscribe(obs)) + .Repeat() + .Retry() + .Publish() + .RefCount(); + } + public void Dispose() + { + listener.Stop(); + } + + public IDisposable Subscribe(IObserver observer) + { + return stream.Subscribe(observer); + } + } +} \ No newline at end of file diff --git a/MediaBrowser.Net/MediaBrowser.Net.csproj b/MediaBrowser.Net/MediaBrowser.Net.csproj new file mode 100644 index 0000000000..6f1b7cccfe --- /dev/null +++ b/MediaBrowser.Net/MediaBrowser.Net.csproj @@ -0,0 +1,66 @@ + + + + + Debug + AnyCPU + {5DA08D1C-0D52-4B1B-AA66-E4A171D938F6} + Library + Properties + MediaBrowser.Net + MediaBrowser.Net + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + ..\packages\Rx-Main.1.0.11226\lib\Net4\System.Reactive.dll + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MediaBrowser.Net/Properties/AssemblyInfo.cs b/MediaBrowser.Net/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..00964b6a26 --- /dev/null +++ b/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")] diff --git a/MediaBrowser.Net/Request.cs b/MediaBrowser.Net/Request.cs new file mode 100644 index 0000000000..43f854233e --- /dev/null +++ b/MediaBrowser.Net/Request.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace MediaBrowser.Net +{ + public class Request + { + public string HttpMethod { get; set; } + public IDictionary> Headers { get; set; } + public Stream InputStream { get; set; } + public string RawUrl { get; set; } + public int ContentLength + { + get { return int.Parse(Headers["Content-Length"].First()); } + } + } +} \ No newline at end of file diff --git a/MediaBrowser.Net/RequestContext.cs b/MediaBrowser.Net/RequestContext.cs new file mode 100644 index 0000000000..4c1350701c --- /dev/null +++ b/MediaBrowser.Net/RequestContext.cs @@ -0,0 +1,118 @@ +using System; +using System.Linq; +using System.Net; +using MediaBrowser.Net.Handlers; + +namespace MediaBrowser.Net +{ + public class RequestContext + { + public HttpListenerRequest Request { get; private set; } + public HttpListenerResponse Response { get; private set; } + + public string LocalPath + { + get + { + return Request.Url.LocalPath; + } + } + + public RequestContext(HttpListenerContext context) + { + Response = context.Response; + Request = context.Request; + } + + public void Respond(BaseHandler handler) + { + Response.AddHeader("Access-Control-Allow-Origin", "*"); + + Response.KeepAlive = true; + + foreach (var header in handler.Headers) + { + Response.AddHeader(header.Key, header.Value); + } + + int statusCode = handler.StatusCode; + Response.ContentType = handler.ContentType; + + TimeSpan cacheDuration = handler.CacheDuration; + + if (Request.Headers.AllKeys.Contains("If-Modified-Since")) + { + DateTime ifModifiedSince; + + if (DateTime.TryParse(Request.Headers["If-Modified-Since"].Replace(" GMT", string.Empty), out ifModifiedSince)) + { + // If the cache hasn't expired yet just return a 304 + if (IsCacheValid(ifModifiedSince, cacheDuration, handler.LastDateModified)) + { + statusCode = 304; + } + } + } + + Response.SendChunked = true; + Response.StatusCode = statusCode; + + if (statusCode != 304) + { + if (handler.GzipResponse) + { + Response.AddHeader("Content-Encoding", "gzip"); + } + + if (cacheDuration.Ticks > 0) + { + CacheResponse(Response, cacheDuration, handler.LastDateModified); + } + handler.WriteStream(Response.OutputStream); + } + else + { + Response.OutputStream.Flush(); + Response.OutputStream.Close(); + } + } + + private void CacheResponse(HttpListenerResponse response, TimeSpan duration, DateTime? dateModified) + { + DateTime lastModified = dateModified ?? DateTime.Now; + + response.Headers[HttpResponseHeader.CacheControl] = "Public"; + response.Headers[HttpResponseHeader.Expires] = DateTime.Now.Add(duration).ToString("r"); + response.Headers[HttpResponseHeader.LastModified] = lastModified.ToString("r"); + } + + private bool IsCacheValid(DateTime ifModifiedSince, TimeSpan cacheDuration, DateTime? dateModified) + { + if (dateModified.HasValue) + { + DateTime lastModified = NormalizeDateForComparison(dateModified.Value); + ifModifiedSince = NormalizeDateForComparison(ifModifiedSince); + + return lastModified <= ifModifiedSince; + } + + DateTime cacheExpirationDate = ifModifiedSince.Add(cacheDuration); + + if (DateTime.Now < cacheExpirationDate) + { + return true; + } + + return false; + } + + /// + /// When the browser sends the IfModifiedDate, it's precision is limited to seconds, so this will account for that + /// + private DateTime NormalizeDateForComparison(DateTime date) + { + return new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second); + } + + } +} \ No newline at end of file diff --git a/MediaBrowser.Net/StreamExtensions.cs b/MediaBrowser.Net/StreamExtensions.cs new file mode 100644 index 0000000000..b4925cc7fd --- /dev/null +++ b/MediaBrowser.Net/StreamExtensions.cs @@ -0,0 +1,19 @@ +using System; +using System.IO; +using System.Reactive.Linq; + +namespace MediaBrowser.Net +{ + public static class StreamExtensions + { + public static IObservable ReadBytes(this Stream stream, int count) + { + var buffer = new byte[count]; + return Observable.FromAsyncPattern((cb, state) => stream.BeginRead(buffer, 0, count, cb, state), ar => + { + stream.EndRead(ar); + return buffer; + })(); + } + } +} \ No newline at end of file diff --git a/MediaBrowser.Net/packages.config b/MediaBrowser.Net/packages.config new file mode 100644 index 0000000000..47102a263f --- /dev/null +++ b/MediaBrowser.Net/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/MediaBrowser.TV/Entities/Episode.cs b/MediaBrowser.TV/Entities/Episode.cs index 9838e939b1..aa3227a77d 100644 --- a/MediaBrowser.TV/Entities/Episode.cs +++ b/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; } } } diff --git a/MediaBrowser.TV/Entities/Series.cs b/MediaBrowser.TV/Entities/Series.cs index 0034c9fe5b..f63580314c 100644 --- a/MediaBrowser.TV/Entities/Series.cs +++ b/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; } } } diff --git a/MediaBrowser.TV/Metadata/EpisodeXmlParser.cs b/MediaBrowser.TV/Metadata/EpisodeXmlParser.cs index 1c68580b15..7df64b405b 100644 --- a/MediaBrowser.TV/Metadata/EpisodeXmlParser.cs +++ b/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; } } diff --git a/MediaBrowser.TV/Metadata/SeriesXmlParser.cs b/MediaBrowser.TV/Metadata/SeriesXmlParser.cs index f4c0719008..9fdf1e7ca4 100644 --- a/MediaBrowser.TV/Metadata/SeriesXmlParser.cs +++ b/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; diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 6f94baf31d..23e78b42f9 100644 --- a/MediaBrowser.sln +++ b/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 -- cgit v1.2.3