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

pull/15/head
LukePulverenti Luke Pulverenti luke pulverenti 12 years ago
parent b8374e1b08
commit 9fbac9680d

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

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

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

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

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

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

@ -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);
});
}
}
}

@ -1,15 +1,13 @@
using MediaBrowser.Controller;
using MediaBrowser.Common.Net.Handlers;
using MediaBrowser.Controller;
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"]);
}
}
}

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

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

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

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

@ -1,15 +1,13 @@
using MediaBrowser.Controller;
using MediaBrowser.Common.Net.Handlers;
using MediaBrowser.Controller;
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;
}
}
}

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

@ -8,9 +8,9 @@ namespace MediaBrowser.Api
{
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 newHeight = originalImage.Height;

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

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

@ -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();
}
}
}

@ -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);
}
}

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

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

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

@ -1,11 +1,58 @@

using System.IO;
using System.Threading.Tasks;
using MediaBrowser.Common.Serialization;
namespace MediaBrowser.Common.Net.Handlers
{
public abstract class BaseJsonHandler : BaseHandler
{
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);
});
}
}
}

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

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

@ -10,7 +10,7 @@ namespace MediaBrowser.Controller.Xml
/// <summary>
/// Provides a base class for parsing metadata xml
/// </summary>
public abstract class BaseItemXmlParser<T>
public class BaseItemXmlParser<T>
where T : BaseItem, new()
{
/// <summary>
@ -215,6 +215,32 @@ namespace MediaBrowser.Controller.Xml
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":
FetchFromGenresNode(reader.ReadSubtree(), item);
break;

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

@ -33,6 +33,7 @@ namespace MediaBrowser.Model.Entities
public string Overview { get; set; }
public string Tagline { get; set; }
[IgnoreDataMember]
public IEnumerable<PersonInfo> People { get; set; }
public IEnumerable<string> Studios { get; set; }
@ -56,5 +57,49 @@ namespace MediaBrowser.Model.Entities
public IEnumerable<Video> LocalTrailers { 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);
}
}
}

@ -0,0 +1,11 @@

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

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

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

@ -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;
}
}
}
}

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

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

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

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

Loading…
Cancel
Save