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

This commit is contained in:
LukePulverenti Luke Pulverenti luke pulverenti 2012-08-15 09:20:29 -04:00
parent d5cf6d59a3
commit 3f1af19ce7
36 changed files with 486 additions and 385 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,14 +1,13 @@
using System; using System;
using System.Linq; using System.Linq;
using MediaBrowser.Common.Net.Handlers;
using MediaBrowser.Controller; using MediaBrowser.Controller;
namespace MediaBrowser.Api.HttpHandlers namespace MediaBrowser.Api.HttpHandlers
{ {
public class PluginConfigurationHandler : JsonHandler public class PluginConfigurationHandler : BaseJsonHandler
{ {
protected override object ObjectToSerialize protected override object GetObjectToSerialize()
{
get
{ {
string pluginName = QueryString["name"]; string pluginName = QueryString["name"];
@ -16,4 +15,3 @@ namespace MediaBrowser.Api.HttpHandlers
} }
} }
} }
}

View file

@ -1,4 +1,5 @@
using System.Linq; using System.Linq;
using MediaBrowser.Common.Net.Handlers;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Plugins;
@ -7,11 +8,9 @@ namespace MediaBrowser.Api.HttpHandlers
/// <summary> /// <summary>
/// Provides information about installed plugins /// Provides information about installed plugins
/// </summary> /// </summary>
public class PluginsHandler : JsonHandler public class PluginsHandler : BaseJsonHandler
{ {
protected override object ObjectToSerialize protected override object GetObjectToSerialize()
{
get
{ {
var plugins = Kernel.Instance.Plugins.Select(p => var plugins = Kernel.Instance.Plugins.Select(p =>
{ {
@ -34,4 +33,3 @@ namespace MediaBrowser.Api.HttpHandlers
} }
} }
} }
}

View file

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

View file

@ -1,13 +1,12 @@
using System; using System;
using MediaBrowser.Common.Net.Handlers;
using MediaBrowser.Controller; using MediaBrowser.Controller;
namespace MediaBrowser.Api.HttpHandlers namespace MediaBrowser.Api.HttpHandlers
{ {
public class UserConfigurationHandler : JsonHandler public class UserConfigurationHandler : BaseJsonHandler
{ {
protected override object ObjectToSerialize protected override object GetObjectToSerialize()
{
get
{ {
Guid userId = Guid.Parse(QueryString["userid"]); Guid userId = Guid.Parse(QueryString["userid"]);
@ -15,4 +14,3 @@ namespace MediaBrowser.Api.HttpHandlers
} }
} }
} }
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -185,7 +185,7 @@ namespace MediaBrowser.Common.Net.Handlers
} }
} }
public virtual void ProcessRequest(HttpListenerContext ctx) public virtual async Task ProcessRequest(HttpListenerContext ctx)
{ {
HttpListenerContext = ctx; HttpListenerContext = ctx;
@ -196,6 +196,8 @@ namespace MediaBrowser.Common.Net.Handlers
ctx.Response.KeepAlive = true; ctx.Response.KeepAlive = true;
try
{
if (SupportsByteRangeRequests && IsRangeRequest) if (SupportsByteRangeRequests && IsRangeRequest)
{ {
ctx.Response.Headers["Accept-Ranges"] = "bytes"; ctx.Response.Headers["Accept-Ranges"] = "bytes";
@ -223,19 +225,32 @@ namespace MediaBrowser.Common.Net.Handlers
} }
} }
if (StatusCode == 200 || StatusCode == 206) PrepareResponse();
if (IsResponseValid)
{ {
ProcessUncachedResponse(ctx, cacheDuration); await ProcessUncachedRequest(ctx, cacheDuration);
} }
else else
{ {
ctx.Response.StatusCode = StatusCode; ctx.Response.StatusCode = StatusCode;
ctx.Response.SendChunked = false; ctx.Response.SendChunked = false;
}
}
catch (Exception ex)
{
// It might be too late if some response data has already been transmitted, but try to set this
ctx.Response.StatusCode = 500;
Logger.LogException(ex);
}
finally
{
DisposeResponseStream(); DisposeResponseStream();
} }
} }
private async void ProcessUncachedResponse(HttpListenerContext ctx, TimeSpan cacheDuration) private async Task ProcessUncachedRequest(HttpListenerContext ctx, TimeSpan cacheDuration)
{ {
long? totalContentLength = TotalContentLength; long? totalContentLength = TotalContentLength;
@ -269,7 +284,7 @@ namespace MediaBrowser.Common.Net.Handlers
// Set the status code // Set the status code
ctx.Response.StatusCode = StatusCode; ctx.Response.StatusCode = StatusCode;
if (StatusCode == 200 || StatusCode == 206) if (IsResponseValid)
{ {
// Finally, write the response data // Finally, write the response data
Stream outputStream = ctx.Response.OutputStream; Stream outputStream = ctx.Response.OutputStream;
@ -288,23 +303,11 @@ namespace MediaBrowser.Common.Net.Handlers
outputStream = CompressedStream; outputStream = CompressedStream;
} }
try
{
await WriteResponseToOutputStream(outputStream); await WriteResponseToOutputStream(outputStream);
} }
catch (Exception ex)
{
Logger.LogException(ex);
}
finally
{
DisposeResponseStream();
}
}
else else
{ {
ctx.Response.SendChunked = false; ctx.Response.SendChunked = false;
DisposeResponseStream();
} }
} }
@ -317,9 +320,16 @@ namespace MediaBrowser.Common.Net.Handlers
response.Headers[HttpResponseHeader.LastModified] = lastModified.ToString("r"); response.Headers[HttpResponseHeader.LastModified] = lastModified.ToString("r");
} }
/// <summary>
/// Gives subclasses a chance to do and prep work, and also to validate data and set an error status code, if needed
/// </summary>
protected virtual void PrepareResponse()
{
}
protected abstract Task WriteResponseToOutputStream(Stream stream); protected abstract Task WriteResponseToOutputStream(Stream stream);
private void DisposeResponseStream() protected virtual void DisposeResponseStream()
{ {
if (CompressedStream != null) if (CompressedStream != null)
{ {
@ -366,5 +376,13 @@ namespace MediaBrowser.Common.Net.Handlers
{ {
return null; return null;
} }
private bool IsResponseValid
{
get
{
return StatusCode == 200 || StatusCode == 206;
}
}
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,11 @@

namespace MediaBrowser.Model.Entities
{
public enum MetadataProviders
{
Imdb,
Tmdb,
Tvdb,
Tvcom
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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