From 2e03cb0916f69b324fe654f92f1642b21eb92005 Mon Sep 17 00:00:00 2001 From: LukePulverenti Luke Pulverenti luke pulverenti Date: Sat, 14 Jul 2012 16:45:11 -0400 Subject: [PATCH] Improved loading performance even more by switching from XmlDocument to XmlReader. Also added more api improvements. --- MediaBrowser.Api/HttpHandlers/ImageHandler.cs | 1 + MediaBrowser.Api/HttpHandlers/MediaHandler.cs | 105 +++ MediaBrowser.Api/MediaBrowser.Api.csproj | 1 + MediaBrowser.Api/Plugin.cs | 14 +- .../MediaBrowser.Common.csproj | 4 + .../Handlers/BaseEmbeddedResourceHandler.cs | 70 ++ MediaBrowser.Common/Net/RequestContext.cs | 11 +- MediaBrowser.Common/packages.config | 1 + .../Xml/BaseItemXmlParser.cs | 677 ++++++++++-------- MediaBrowser.Controller/Xml/XmlExtensions.cs | 65 +- .../Handlers/EmbeddedResourceHandler.cs | 23 + .../MediaBrowser.HtmlBrowser.csproj | 2 +- MediaBrowser.HtmlBrowser/Plugin.cs | 14 +- MediaBrowser.Model/MediaBrowser.Model.csproj | 6 +- .../Metadata/MovieXmlParser.cs | 15 +- MediaBrowser.TV/Metadata/EpisodeXmlParser.cs | 17 +- MediaBrowser.TV/Metadata/SeriesXmlParser.cs | 14 +- MediaBrowser.sln | 4 +- 18 files changed, 646 insertions(+), 398 deletions(-) create mode 100644 MediaBrowser.Api/HttpHandlers/MediaHandler.cs create mode 100644 MediaBrowser.Common/Net/Handlers/BaseEmbeddedResourceHandler.cs create mode 100644 MediaBrowser.HtmlBrowser/Handlers/EmbeddedResourceHandler.cs diff --git a/MediaBrowser.Api/HttpHandlers/ImageHandler.cs b/MediaBrowser.Api/HttpHandlers/ImageHandler.cs index 048339d573..12b3d6e572 100644 --- a/MediaBrowser.Api/HttpHandlers/ImageHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/ImageHandler.cs @@ -165,6 +165,7 @@ namespace MediaBrowser.Api.HttpHandlers return path; } + string id = QueryString["id"]; string personName = QueryString["personname"]; string imageType = QueryString["type"] ?? string.Empty; string imageIndex = QueryString["index"]; diff --git a/MediaBrowser.Api/HttpHandlers/MediaHandler.cs b/MediaBrowser.Api/HttpHandlers/MediaHandler.cs new file mode 100644 index 0000000000..d04ff21996 --- /dev/null +++ b/MediaBrowser.Api/HttpHandlers/MediaHandler.cs @@ -0,0 +1,105 @@ +using System; +using System.IO; +using System.IO.Compression; +using MediaBrowser.Common.Net; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Api.HttpHandlers +{ + class MediaHandler : Response + { + public MediaHandler(RequestContext ctx) + : base(ctx) + { + WriteStream = s => + { + WriteReponse(s); + s.Close(); + }; + } + + private string _MediaPath = string.Empty; + private string MediaPath + { + get + { + if (string.IsNullOrEmpty(_MediaPath)) + { + _MediaPath = GetMediaPath(); + } + + return _MediaPath; + } + } + + private string GetMediaPath() + { + string path = QueryString["path"] ?? string.Empty; + + if (!string.IsNullOrEmpty(path)) + { + return path; + } + + BaseItem item = ApiService.GetItemById(QueryString["id"]); + + return item.Path; + } + + public override string ContentType + { + get + { + // http://www.codingcereal.com/2011/10/an-array-of-45-video-mime-types/ + + string extension = Path.GetExtension(MediaPath); + + if (extension.EndsWith("mkv", StringComparison.OrdinalIgnoreCase)) + { + return "video/x-matroska"; + } + else if (extension.EndsWith("avi", StringComparison.OrdinalIgnoreCase)) + { + return "video/avi"; + } + else if (extension.EndsWith("wmv", StringComparison.OrdinalIgnoreCase)) + { + return "video/wmv"; + } + else if (extension.EndsWith("m4v", StringComparison.OrdinalIgnoreCase)) + { + return "video/m4v"; + } + else if (extension.EndsWith("flv", StringComparison.OrdinalIgnoreCase)) + { + return "video/flv"; + } + else if (extension.EndsWith("mov", StringComparison.OrdinalIgnoreCase)) + { + return "video/quicktime"; + } + else if (extension.EndsWith("mp4", StringComparison.OrdinalIgnoreCase)) + { + return "video/mp4"; + } + + return "video/x-matroska"; + } + } + + private void WriteReponse(Stream stream) + { + try + { + using (Stream input = File.OpenRead(MediaPath)) + { + input.CopyTo(stream); + } + } + catch + { + } + } + + } +} diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 4846774b1c..18bc85ff21 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -54,6 +54,7 @@ + diff --git a/MediaBrowser.Api/Plugin.cs b/MediaBrowser.Api/Plugin.cs index 471ddbddc4..28b45b996b 100644 --- a/MediaBrowser.Api/Plugin.cs +++ b/MediaBrowser.Api/Plugin.cs @@ -13,17 +13,19 @@ namespace MediaBrowser.Api { var httpServer = Kernel.Instance.HttpServer; - httpServer.Where(ctx => ctx.Request.Url.LocalPath.EndsWith("/api/item", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new ItemHandler(ctx))); + httpServer.Where(ctx => ctx.LocalPath.EndsWith("/api/media", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new MediaHandler(ctx))); - httpServer.Where(ctx => ctx.Request.Url.LocalPath.EndsWith("/api/image", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new ImageHandler(ctx))); + httpServer.Where(ctx => ctx.LocalPath.EndsWith("/api/item", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new ItemHandler(ctx))); - httpServer.Where(ctx => ctx.Request.Url.LocalPath.EndsWith("/api/genre", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new GenreHandler(ctx))); + httpServer.Where(ctx => ctx.LocalPath.EndsWith("/api/image", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new ImageHandler(ctx))); - httpServer.Where(ctx => ctx.Request.Url.LocalPath.EndsWith("/api/genres", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new GenresHandler(ctx))); + httpServer.Where(ctx => ctx.LocalPath.EndsWith("/api/genre", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new GenreHandler(ctx))); - httpServer.Where(ctx => ctx.Request.Url.LocalPath.EndsWith("/api/recentlyaddeditems", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new RecentlyAddedItemsHandler(ctx))); + httpServer.Where(ctx => ctx.LocalPath.EndsWith("/api/genres", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new GenresHandler(ctx))); - httpServer.Where(ctx => ctx.Request.Url.LocalPath.EndsWith("/api/inprogressitems", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new InProgressItemsHandler(ctx))); + httpServer.Where(ctx => ctx.LocalPath.EndsWith("/api/recentlyaddeditems", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new RecentlyAddedItemsHandler(ctx))); + + httpServer.Where(ctx => ctx.LocalPath.EndsWith("/api/inprogressitems", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new InProgressItemsHandler(ctx))); } } } diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 9326bea8bb..3c7f0ff045 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -33,6 +33,9 @@ ..\packages\Newtonsoft.Json.4.5.7\lib\net40\Newtonsoft.Json.dll + + ..\packages\ServiceStack.Text.3.8.5\lib\net35\ServiceStack.Text.dll + @@ -48,6 +51,7 @@ + diff --git a/MediaBrowser.Common/Net/Handlers/BaseEmbeddedResourceHandler.cs b/MediaBrowser.Common/Net/Handlers/BaseEmbeddedResourceHandler.cs new file mode 100644 index 0000000000..e64773f33f --- /dev/null +++ b/MediaBrowser.Common/Net/Handlers/BaseEmbeddedResourceHandler.cs @@ -0,0 +1,70 @@ +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/RequestContext.cs b/MediaBrowser.Common/Net/RequestContext.cs index 9a21b473de..d3635f34a5 100644 --- a/MediaBrowser.Common/Net/RequestContext.cs +++ b/MediaBrowser.Common/Net/RequestContext.cs @@ -9,6 +9,14 @@ namespace MediaBrowser.Common.Net 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; @@ -19,6 +27,8 @@ namespace MediaBrowser.Common.Net { Response.AddHeader("Access-Control-Allow-Origin", "*"); + Response.KeepAlive = true; + foreach (var header in handler.Headers) { Response.AddHeader(header.Key, header.Value); @@ -52,7 +62,6 @@ namespace MediaBrowser.Common.Net { CacheResponse(Response, cacheDuration, handler.LastDateModified); } - handler.WriteStream(Response.OutputStream); } else diff --git a/MediaBrowser.Common/packages.config b/MediaBrowser.Common/packages.config index 4f6bcdcffe..411c42bfb6 100644 --- a/MediaBrowser.Common/packages.config +++ b/MediaBrowser.Common/packages.config @@ -2,4 +2,5 @@ + \ No newline at end of file diff --git a/MediaBrowser.Controller/Xml/BaseItemXmlParser.cs b/MediaBrowser.Controller/Xml/BaseItemXmlParser.cs index 8948c979ef..d36f3533e4 100644 --- a/MediaBrowser.Controller/Xml/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Xml/BaseItemXmlParser.cs @@ -12,15 +12,17 @@ namespace MediaBrowser.Controller.Xml { public virtual void Fetch(T item, string metadataFile) { - XmlDocument doc = new XmlDocument(); - - doc.Load(metadataFile); - - XmlElement titleElement = doc.DocumentElement; - - foreach (XmlNode node in titleElement.ChildNodes) + using (XmlReader reader = XmlReader.Create(metadataFile)) { - FetchDataFromXmlNode(node, item); + reader.MoveToContent(); + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + FetchDataFromXmlNode(reader, item); + } + } } // If dates weren't supplied in metadata, use values from the file @@ -35,13 +37,13 @@ namespace MediaBrowser.Controller.Xml } } - protected virtual void FetchDataFromXmlNode(XmlNode node, T item) + protected virtual void FetchDataFromXmlNode(XmlReader reader, T item) { - switch (node.Name) + switch (reader.Name) { case "Added": DateTime added; - if (DateTime.TryParse(node.InnerText ?? string.Empty, out added)) + if (DateTime.TryParse(reader.ReadElementContentAsString() ?? string.Empty, out added)) { item.DateCreated = added; } @@ -49,7 +51,7 @@ namespace MediaBrowser.Controller.Xml case "Type": { - item.DisplayMediaType = node.InnerText ?? string.Empty; + item.DisplayMediaType = reader.ReadElementContentAsString() ?? string.Empty; switch (item.DisplayMediaType.ToLower()) { @@ -68,86 +70,65 @@ namespace MediaBrowser.Controller.Xml } case "banner": - item.BannerImagePath = node.InnerText ?? string.Empty; + item.BannerImagePath = reader.ReadElementContentAsString() ?? string.Empty; break; case "LocalTitle": - item.Name = node.InnerText ?? string.Empty; + item.Name = reader.ReadElementContentAsString() ?? string.Empty; break; case "SortTitle": - item.SortName = node.InnerText ?? string.Empty; + item.SortName = reader.ReadElementContentAsString() ?? string.Empty; break; case "Overview": case "Description": - item.Overview = node.InnerText ?? string.Empty; + item.Overview = reader.ReadElementContentAsString() ?? string.Empty; break; case "TagLine": - item.Tagline = node.InnerText ?? string.Empty; + item.Tagline = reader.ReadElementContentAsString() ?? string.Empty; break; case "ContentRating": case "MPAARating": - item.OfficialRating = node.InnerText ?? string.Empty; + item.OfficialRating = reader.ReadElementContentAsString() ?? string.Empty; break; case "CustomRating": - item.CustomRating = node.InnerText ?? string.Empty; + item.CustomRating = reader.ReadElementContentAsString() ?? string.Empty; break; case "CustomPin": - item.CustomPin = node.InnerText ?? string.Empty; - break; - - case "Covers": - FetchFromCoversNode(node, item); - break; - - case "Genres": - FetchFromGenresNode(node, item); + item.CustomPin = reader.ReadElementContentAsString() ?? string.Empty; break; case "Genre": { var genres = (item.Genres ?? new string[] { }).ToList(); - genres.AddRange(GetSplitValues(node.InnerText, '|')); + genres.AddRange(GetSplitValues(reader.ReadElementContentAsString(), '|')); item.Genres = genres; break; } case "AspectRatio": - item.AspectRatio = node.InnerText ?? string.Empty; - break; - - case "Rating": - case "IMDBrating": - float IMDBrating = node.SafeGetSingle((float)-1, (float)10); - - if (IMDBrating >= 0) - { - item.UserRating = IMDBrating; - } + item.AspectRatio = reader.ReadElementContentAsString() ?? string.Empty; break; case "Network": { var studios = (item.Studios ?? new string[] { }).ToList(); - studios.AddRange(GetSplitValues(node.InnerText, '|')); + studios.AddRange(GetSplitValues(reader.ReadElementContentAsString(), '|')); item.Studios = studios; break; } - case "Studios": - FetchFromStudiosNode(node, item); - break; case "Director": { - var list = (item.People ?? new PersonInfo[]{}).ToList(); - list.AddRange(GetSplitValues(node.InnerText, '|').Select(v => new PersonInfo() { Name = v, PersonType = PersonType.Director })); + var list = (item.People ?? new PersonInfo[] { }).ToList(); + list.AddRange(GetSplitValues(reader.ReadElementContentAsString(), '|').Select(v => new PersonInfo() { Name = v, PersonType = PersonType.Director })); item.People = list; break; @@ -155,7 +136,7 @@ namespace MediaBrowser.Controller.Xml case "Writer": { var list = (item.People ?? new PersonInfo[] { }).ToList(); - list.AddRange(GetSplitValues(node.InnerText, '|').Select(v => new PersonInfo() { Name = v, PersonType = PersonType.Writer })); + list.AddRange(GetSplitValues(reader.ReadElementContentAsString(), '|').Select(v => new PersonInfo() { Name = v, PersonType = PersonType.Writer })); item.People = list; break; @@ -165,28 +146,20 @@ namespace MediaBrowser.Controller.Xml case "GuestStars": { var list = (item.People ?? new PersonInfo[] { }).ToList(); - list.AddRange(GetSplitValues(node.InnerText, '|').Select(v => new PersonInfo() { Name = v, PersonType = PersonType.Actor })); + list.AddRange(GetSplitValues(reader.ReadElementContentAsString(), '|').Select(v => new PersonInfo() { Name = v, PersonType = PersonType.Actor })); item.People = list; break; } - case "Persons": - FetchDataFromPersonsNode(node, item); - break; - case "Trailer": - item.TrailerUrl = node.InnerText ?? string.Empty; - break; - - case "ParentalRating": - FetchFromParentalRatingNode(node, item); + item.TrailerUrl = reader.ReadElementContentAsString() ?? string.Empty; break; case "ProductionYear": { int ProductionYear; - if (int.TryParse(node.InnerText, out ProductionYear) && ProductionYear > 1850) + if (int.TryParse(reader.ReadElementContentAsString(), out ProductionYear) && ProductionYear > 1850) { item.ProductionYear = ProductionYear; } @@ -194,390 +167,478 @@ namespace MediaBrowser.Controller.Xml break; } + case "Rating": + case "IMDBrating": + + string rating = reader.ReadElementContentAsString(); + + if (!string.IsNullOrEmpty(rating)) + { + float val; + + if (float.TryParse(rating, out val)) + { + item.UserRating = val; + } + } + break; + + case "Genres": + FetchFromGenresNode(reader.ReadSubtree(), item); + break; + + case "Persons": + FetchDataFromPersonsNode(reader.ReadSubtree(), item); + break; + + case "ParentalRating": + FetchFromParentalRatingNode(reader.ReadSubtree(), item); + break; + + case "Studios": + FetchFromStudiosNode(reader.ReadSubtree(), item); + break; + case "MediaInfo": - FetchMediaInfo(node, item); + FetchMediaInfo(reader.ReadSubtree(), item); break; default: + reader.Skip(); break; } } - protected virtual void FetchFromCoversNode(XmlNode node, T item) + private void FetchMediaInfo(XmlReader reader, T item) { - string cover = node.SafeGetString("Front"); + var video = item as Video; - if (!string.IsNullOrEmpty(cover)) + if (video != null) { - item.PrimaryImagePath = cover; + FetchMediaInfo(reader, video); } } - protected virtual void FetchMediaInfo(XmlNode node, T item) + private void FetchMediaInfo(XmlReader reader, Video item) { - var iMediaInfo = item as Video; - - if (iMediaInfo != null) - { - FetchMediaInfo(node, iMediaInfo); - } - } + reader.MoveToContent(); - protected virtual void FetchMediaInfo(XmlNode node, Video item) - { - foreach (XmlNode childNode in node.ChildNodes) + while (reader.Read()) { - switch (childNode.Name) + if (reader.NodeType == XmlNodeType.Element) { - case "Audio": - { - AudioStream stream = FetchMediaInfoAudio(childNode); + switch (reader.Name) + { + case "Audio": + { + AudioStream stream = FetchMediaInfoAudio(reader.ReadSubtree()); - List streams = item.AudioStreams.ToList(); - streams.Add(stream); - item.AudioStreams = streams; + List streams = item.AudioStreams.ToList(); + streams.Add(stream); + item.AudioStreams = streams; - break; - } + break; + } - case "Video": - FetchMediaInfoVideo(childNode, item); - break; + case "Video": + FetchMediaInfoVideo(reader.ReadSubtree(), item); + break; - case "Subtitle": - FetchMediaInfoSubtitles(childNode, item); - break; + case "Subtitle": + FetchMediaInfoSubtitles(reader.ReadSubtree(), item); + break; - default: - break; + default: + reader.Skip(); + break; + } } } } - protected virtual AudioStream FetchMediaInfoAudio(XmlNode node) + private AudioStream FetchMediaInfoAudio(XmlReader reader) { AudioStream stream = new AudioStream(); - foreach (XmlNode childNode in node.ChildNodes) + reader.MoveToContent(); + + while (reader.Read()) { - switch (childNode.Name) + if (reader.NodeType == XmlNodeType.Element) { - case "BitRate": - stream.BitRate = childNode.SafeGetInt32(); - break; - - case "Channels": - stream.Channels = childNode.SafeGetInt32(); - break; + switch (reader.Name) + { + case "BitRate": + stream.BitRate = reader.ReadIntSafe(); + break; - case "Language": - stream.Language = childNode.InnerText ?? string.Empty; - break; + case "Channels": + stream.Channels = reader.ReadIntSafe(); + break; - case "Codec": - { - string codec = childNode.InnerText ?? string.Empty; + case "Language": + stream.Language = reader.ReadElementContentAsString() ?? string.Empty; + break; - switch (codec.ToLower()) + case "Codec": { - case "dts-es": - case "dts-es matrix": - case "dts-es discrete": - stream.AudioFormat = "DTS"; - stream.AudioProfile = "ES"; - break; - case "dts-hd hra": - case "dts-hd high resolution": - stream.AudioFormat = "DTS"; - stream.AudioProfile = "HRA"; - break; - case "dts ma": - case "dts-hd ma": - case "dts-hd master": - stream.AudioFormat = "DTS"; - stream.AudioProfile = "MA"; - break; - case "dolby digital": - case "dolby digital surround ex": - case "dolby surround": - stream.AudioFormat = "AC-3"; - break; - case "dolby digital plus": - stream.AudioFormat = "E-AC-3"; - break; - case "dolby truehd": - stream.AudioFormat = "AC-3"; - stream.AudioProfile = "TrueHD"; - break; - case "mp2": - stream.AudioFormat = "MPEG Audio"; - stream.AudioProfile = "Layer 2"; - break; - case "other": - break; - default: - stream.AudioFormat = codec; - break; + string codec = reader.ReadElementContentAsString() ?? string.Empty; + + switch (codec.ToLower()) + { + case "dts-es": + case "dts-es matrix": + case "dts-es discrete": + stream.AudioFormat = "DTS"; + stream.AudioProfile = "ES"; + break; + case "dts-hd hra": + case "dts-hd high resolution": + stream.AudioFormat = "DTS"; + stream.AudioProfile = "HRA"; + break; + case "dts ma": + case "dts-hd ma": + case "dts-hd master": + stream.AudioFormat = "DTS"; + stream.AudioProfile = "MA"; + break; + case "dolby digital": + case "dolby digital surround ex": + case "dolby surround": + stream.AudioFormat = "AC-3"; + break; + case "dolby digital plus": + stream.AudioFormat = "E-AC-3"; + break; + case "dolby truehd": + stream.AudioFormat = "AC-3"; + stream.AudioProfile = "TrueHD"; + break; + case "mp2": + stream.AudioFormat = "MPEG Audio"; + stream.AudioProfile = "Layer 2"; + break; + case "other": + break; + default: + stream.AudioFormat = codec; + break; + } + + break; } + default: + reader.Skip(); break; - } - - default: - break; + } } } return stream; } - protected virtual void FetchMediaInfoVideo(XmlNode node, Video item) + private void FetchMediaInfoVideo(XmlReader reader, Video item) { - foreach (XmlNode childNode in node.ChildNodes) + reader.MoveToContent(); + + while (reader.Read()) { - switch (childNode.Name) + if (reader.NodeType == XmlNodeType.Element) { - case "Width": - item.Width = childNode.SafeGetInt32(); - break; - - case "Height": - item.Height = childNode.SafeGetInt32(); - break; + switch (reader.Name) + { + case "Width": + item.Width = reader.ReadIntSafe(); + break; - case "BitRate": - item.VideoBitRate = childNode.SafeGetInt32(); - break; + case "Height": + item.Height = reader.ReadIntSafe(); + break; - case "FrameRate": - item.FrameRate = childNode.InnerText ?? string.Empty; - break; + case "BitRate": + item.VideoBitRate = reader.ReadIntSafe(); + break; - case "ScanType": - item.ScanType = childNode.InnerText ?? string.Empty; - break; + case "FrameRate": + item.FrameRate = reader.ReadElementContentAsString() ?? string.Empty; + break; - case "Duration": - item.RunTime = TimeSpan.FromMinutes(childNode.SafeGetInt32()); - break; + case "ScanType": + item.ScanType = reader.ReadElementContentAsString() ?? string.Empty; + break; - case "DurationSeconds": - int seconds = childNode.SafeGetInt32(); - if (seconds > 0) - { - item.RunTime = TimeSpan.FromSeconds(seconds); - } - break; + case "Duration": + item.RunTime = TimeSpan.FromMinutes(reader.ReadIntSafe()); + break; - case "Codec": - { - string videoCodec = childNode.InnerText ?? string.Empty; + case "DurationSeconds": + int seconds = reader.ReadIntSafe(); + if (seconds > 0) + { + item.RunTime = TimeSpan.FromSeconds(seconds); + } + break; - switch (videoCodec.ToLower()) + case "Codec": { - case "sorenson h.263": - item.VideoCodec = "Sorenson H263"; - break; - case "h.262": - item.VideoCodec = "MPEG-2 Video"; - break; - case "h.264": - item.VideoCodec = "AVC"; - break; - default: - item.VideoCodec = videoCodec; - break; + string videoCodec = reader.ReadElementContentAsString() ?? string.Empty; + + switch (videoCodec.ToLower()) + { + case "sorenson h.263": + item.VideoCodec = "Sorenson H263"; + break; + case "h.262": + item.VideoCodec = "MPEG-2 Video"; + break; + case "h.264": + item.VideoCodec = "AVC"; + break; + default: + item.VideoCodec = videoCodec; + break; + } + + break; } + default: + reader.Skip(); break; - } - - default: - break; + } } } } - protected virtual void FetchMediaInfoSubtitles(XmlNode node, Video item) + private void FetchMediaInfoSubtitles(XmlReader reader, Video item) { - List subtitles = item.Subtitles.ToList(); + List list = (item.Subtitles ?? new string[] { }).ToList(); + + reader.MoveToContent(); - foreach (XmlNode childNode in node.ChildNodes) + while (reader.Read()) { - switch (childNode.Name) + if (reader.NodeType == XmlNodeType.Element) { - case "Language": - string lang = childNode.InnerText; + switch (reader.Name) + { + case "Language": + { + string genre = reader.ReadElementContentAsString(); - if (!string.IsNullOrEmpty(lang)) - { - subtitles.Add(lang); - } - break; + if (!string.IsNullOrEmpty(genre)) + { + list.Add(genre); + } + break; + } - default: - break; + default: + reader.Skip(); + break; + } } } - item.Subtitles = subtitles; + item.Subtitles = list; } - protected virtual void FetchFromGenresNode(XmlNode node, T item) + private void FetchFromGenresNode(XmlReader reader, T item) { List list = (item.Genres ?? new string[] { }).ToList(); - foreach (XmlNode childNode in node.ChildNodes) + reader.MoveToContent(); + + while (reader.Read()) { - switch (childNode.Name) + if (reader.NodeType == XmlNodeType.Element) { - case "Genre": - string text = childNode.InnerText ?? string.Empty; + switch (reader.Name) + { + case "Genre": + { + string genre = reader.ReadElementContentAsString(); - if (!string.IsNullOrEmpty(text)) - { - list.Add(text); - } - break; + if (!string.IsNullOrEmpty(genre)) + { + list.Add(genre); + } + break; + } - default: - break; + default: + reader.Skip(); + break; + } } - } + item.Genres = list; } - protected virtual void FetchDataFromPersonsNode(XmlNode node, T item) + private void FetchDataFromPersonsNode(XmlReader reader, T item) { List list = (item.People ?? new PersonInfo[] { }).ToList(); - foreach (XmlNode childNode in node.ChildNodes) + reader.MoveToContent(); + + while (reader.Read()) { - switch (childNode.Name) + if (reader.NodeType == XmlNodeType.Element) { - case "Person": - { - list.Add(GetPersonFromXmlNode(childNode)); + switch (reader.Name) + { + case "Person": + { + list.Add(GetPersonFromXmlNode(reader)); + break; + } + default: + reader.Skip(); break; - } - - default: - break; + } } - } item.People = list; } - protected virtual void FetchFromStudiosNode(XmlNode node, T item) + private void FetchFromStudiosNode(XmlReader reader, T item) { List list = (item.Studios ?? new string[] { }).ToList(); - foreach (XmlNode childNode in node.ChildNodes) + reader.MoveToContent(); + + while (reader.Read()) { - switch (childNode.Name) + if (reader.NodeType == XmlNodeType.Element) { - case "Studio": - string text = childNode.InnerText ?? string.Empty; + switch (reader.Name) + { + case "Studio": + { + string studio = reader.ReadElementContentAsString(); - if (!string.IsNullOrEmpty(text)) - { - list.Add(text); - } - break; + if (!string.IsNullOrEmpty(studio)) + { + list.Add(studio); + } + break; + } - default: - break; + default: + reader.Skip(); + break; + } } } item.Studios = list; } - protected virtual void FetchFromParentalRatingNode(XmlNode node, T item) + private void FetchFromParentalRatingNode(XmlReader reader, T item) { - foreach (XmlNode childNode in node.ChildNodes) + reader.MoveToContent(); + + while (reader.Read()) { - switch (childNode.Name) + if (reader.NodeType == XmlNodeType.Element) { - case "Value": - { - int ParentalRating = childNode.SafeGetInt32((int)7); - - switch (ParentalRating) + switch (reader.Name) + { + case "Value": { - case -1: - item.OfficialRating = "NR"; - break; - case 0: - item.OfficialRating = "UR"; - break; - case 1: - item.OfficialRating = "G"; - break; - case 3: - item.OfficialRating = "PG"; - break; - case 4: - item.OfficialRating = "PG-13"; - break; - case 5: - item.OfficialRating = "NC-17"; - break; - case 6: - item.OfficialRating = "R"; - break; - default: - break; + string ratingString = reader.ReadElementContentAsString(); + + int rating = 7; + + if (!string.IsNullOrEmpty(ratingString)) + { + int.TryParse(ratingString, out rating); + } + + switch (rating) + { + case -1: + item.OfficialRating = "NR"; + break; + case 0: + item.OfficialRating = "UR"; + break; + case 1: + item.OfficialRating = "G"; + break; + case 3: + item.OfficialRating = "PG"; + break; + case 4: + item.OfficialRating = "PG-13"; + break; + case 5: + item.OfficialRating = "NC-17"; + break; + case 6: + item.OfficialRating = "R"; + break; + default: + break; + } + break; } - break; - } - default: - break; + default: + reader.Skip(); + break; + } } } } - private PersonInfo GetPersonFromXmlNode(XmlNode node) + private PersonInfo GetPersonFromXmlNode(XmlReader reader) { PersonInfo person = new PersonInfo(); - foreach (XmlNode childNode in node.ChildNodes) + reader.MoveToContent(); + + while (reader.Read()) { - switch (childNode.Name) + if (reader.NodeType == XmlNodeType.Element) { - case "Name": - person.Name = childNode.InnerText ?? string.Empty; - break; - - case "Type": - { - string type = childNode.InnerText ?? string.Empty; + switch (reader.Name) + { + case "Name": + person.Name = reader.ReadElementContentAsString() ?? string.Empty; + break; - if (type == "Director") + case "Type": { - person.PersonType = PersonType.Director; - } - else if (type == "Actor") - { - person.PersonType = PersonType.Actor; + string type = reader.ReadElementContentAsString() ?? string.Empty; + + if (type == "Director") + { + person.PersonType = PersonType.Director; + } + else if (type == "Actor") + { + person.PersonType = PersonType.Actor; + } + break; } - break; - } - case "Role": - person.Overview = childNode.InnerText ?? string.Empty; - break; + case "Role": + person.Overview = reader.ReadElementContentAsString() ?? string.Empty; + break; - default: - break; + default: + reader.Skip(); + break; + } } - } + return person; } diff --git a/MediaBrowser.Controller/Xml/XmlExtensions.cs b/MediaBrowser.Controller/Xml/XmlExtensions.cs index 4f753a3f8a..ae0cc2ce36 100644 --- a/MediaBrowser.Controller/Xml/XmlExtensions.cs +++ b/MediaBrowser.Controller/Xml/XmlExtensions.cs @@ -6,69 +6,36 @@ namespace MediaBrowser.Controller.Xml { public static class XmlExtensions { - public static int SafeGetInt32(this XmlNode node) - { - return SafeGetInt32(node, 0); - } + private static CultureInfo _usCulture = new CultureInfo("en-US"); - public static int SafeGetInt32(this XmlNode node, int defaultInt) + public static float ReadFloatSafe(this XmlReader reader) { - if (node != null && node.InnerText.Length > 0) - { - int rval; - if (Int32.TryParse(node.InnerText, out rval)) - { - return rval; - } - - } - return defaultInt; - } + string valueString = reader.ReadElementContentAsString(); - private static CultureInfo _usCulture = new CultureInfo("en-US"); + float value = 0; - public static float SafeGetSingle(this XmlNode rvalNode, float minValue, float maxValue) - { - if (rvalNode.InnerText.Length > 0) + if (!string.IsNullOrEmpty(valueString)) { - float rval; // float.TryParse is local aware, so it can be probamatic, force us culture - if (float.TryParse(rvalNode.InnerText, NumberStyles.AllowDecimalPoint, _usCulture, out rval)) - { - if (rval >= minValue && rval <= maxValue) - { - return rval; - } - } - + float.TryParse(valueString, NumberStyles.AllowDecimalPoint, _usCulture, out value); } - return minValue; - } - - public static float SafeGetSingle(this XmlNode doc, string path, float minValue, float maxValue) - { - XmlNode rvalNode = doc.SelectSingleNode(path); - if (rvalNode != null) - { - rvalNode.SafeGetSingle(minValue, maxValue); - } - return minValue; + return value; } - - public static string SafeGetString(this XmlNode node) + public static int ReadIntSafe(this XmlReader reader) { - return SafeGetString(node, null); - } + string valueString = reader.ReadElementContentAsString(); - public static string SafeGetString(this XmlNode node, string defaultValue) - { - if (node != null && node.InnerText.Length > 0) + int value = 0; + + if (!string.IsNullOrEmpty(valueString)) { - return node.InnerText; + + int.TryParse(valueString, out value); } - return defaultValue; + + return value; } } } diff --git a/MediaBrowser.HtmlBrowser/Handlers/EmbeddedResourceHandler.cs b/MediaBrowser.HtmlBrowser/Handlers/EmbeddedResourceHandler.cs new file mode 100644 index 0000000000..a153eb1195 --- /dev/null +++ b/MediaBrowser.HtmlBrowser/Handlers/EmbeddedResourceHandler.cs @@ -0,0 +1,23 @@ +using System.IO; +using System.Reflection; +using MediaBrowser.Common.Net; +using MediaBrowser.Common.Net.Handlers; + +namespace MediaBrowser.HtmlBrowser.Handlers +{ + class EmbeddedResourceHandler : BaseEmbeddedResourceHandler + { + public EmbeddedResourceHandler(RequestContext ctx, string resourcePath) + : base(ctx, resourcePath) + { + + } + + protected override Stream GetEmbeddedResourceStream() + { + string path = ResourcePath.Replace("/", "."); + + return Assembly.GetExecutingAssembly().GetManifestResourceStream("MediaBrowser.HtmlBrowser.Html." + path); + } + } +} diff --git a/MediaBrowser.HtmlBrowser/MediaBrowser.HtmlBrowser.csproj b/MediaBrowser.HtmlBrowser/MediaBrowser.HtmlBrowser.csproj index 5200d14591..d01ca1b1dc 100644 --- a/MediaBrowser.HtmlBrowser/MediaBrowser.HtmlBrowser.csproj +++ b/MediaBrowser.HtmlBrowser/MediaBrowser.HtmlBrowser.csproj @@ -42,6 +42,7 @@ + @@ -92,7 +93,6 @@ - diff --git a/MediaBrowser.HtmlBrowser/Plugin.cs b/MediaBrowser.HtmlBrowser/Plugin.cs index 47277797f2..1599881d0b 100644 --- a/MediaBrowser.HtmlBrowser/Plugin.cs +++ b/MediaBrowser.HtmlBrowser/Plugin.cs @@ -3,6 +3,7 @@ using System.Reactive.Linq; using MediaBrowser.Common.Net.Handlers; using MediaBrowser.Common.Plugins; using MediaBrowser.Controller; +using MediaBrowser.HtmlBrowser.Handlers; namespace MediaBrowser.HtmlBrowser { @@ -12,11 +13,18 @@ namespace MediaBrowser.HtmlBrowser { var httpServer = Kernel.Instance.HttpServer; - /*httpServer.Where(ctx => ctx.Request.Url.LocalPath.EndsWith("/browser/index.html", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new EmbeddedResourceHandler(ctx, "MediaBrowser.HtmlBrowser.Html.index.html"))); + httpServer.Where(ctx => ctx.LocalPath.IndexOf("/browser/", StringComparison.OrdinalIgnoreCase) != -1).Subscribe(ctx => + { + string localPath = ctx.LocalPath; + string srch = "/browser/"; - httpServer.Where(ctx => ctx.Request.Url.LocalPath.EndsWith("/browser/resource", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new EmbeddedResourceHandler(ctx))); + int index = localPath.IndexOf(srch, StringComparison.OrdinalIgnoreCase); - httpServer.Where(ctx => ctx.Request.Url.LocalPath.EndsWith("/browser/favicon.ico", StringComparison.OrdinalIgnoreCase)).Subscribe(ctx => ctx.Respond(new EmbeddedResourceHandler(ctx, "MediaBrowser.HtmlBrowser.Html.css.images.favicon.ico")));*/ + string resource = localPath.Substring(index + srch.Length); + + ctx.Respond(new EmbeddedResourceHandler(ctx, resource)); + + }); } } } diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 2f3548661e..783cf93500 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -53,15 +53,15 @@ + + + {9142eefa-7570-41e1-bfcc-468bb571af2f} MediaBrowser.Common - - -