|
@@ -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
|
|
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
|
|
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,26 +102,19 @@ namespace MediaBrowser.Common.Net.Handlers
|
|
|
|
|
|
protected override long? GetTotalContentLength()
|
|
protected override long? GetTotalContentLength()
|
|
{
|
|
{
|
|
- try
|
|
|
|
- {
|
|
|
|
- return FileStream.Length;
|
|
|
|
- }
|
|
|
|
- catch
|
|
|
|
- {
|
|
|
|
- return base.GetTotalContentLength();
|
|
|
|
- }
|
|
|
|
|
|
+ return SourceStream.Length;
|
|
}
|
|
}
|
|
|
|
|
|
protected override DateTime? GetLastDateModified()
|
|
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
|
|
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)
|
|
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)
|
|
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;
|