From f62af07381b633d8e7ddf5787d9048fbbf4e3c85 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Sat, 20 Apr 2019 12:18:44 +0200 Subject: [PATCH 1/6] Handle exception when loading unsupported assembly Fixes #1256 --- Emby.Server.Implementations/ApplicationHost.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 82042f5ca7..0295f10981 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1167,7 +1167,7 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - Logger.LogError(ex, "Error loading plugin {pluginName}", plugin.GetType().FullName); + Logger.LogError(ex, "Error loading plugin {PluginName}", plugin.GetType().FullName); return null; } @@ -1348,8 +1348,19 @@ namespace Emby.Server.Implementations { foreach (var file in Directory.EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.AllDirectories)) { - Logger.LogInformation("Loading assembly {Path}", file); - yield return Assembly.LoadFrom(file); + Assembly plugAss; + try + { + plugAss = Assembly.LoadFrom(file); + } + catch (TypeLoadException ex) + { + Logger.LogError(ex, "Failed to load assembly {Path}", file); + continue; + } + + Logger.LogInformation("Loaded assembly {Assembly} from {Path}", plugAss.FullName, file); + yield return plugAss; } } From 6973182ade7af9173abaf835608327be30b6b162 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Sat, 20 Apr 2019 14:02:00 +0200 Subject: [PATCH 2/6] Fix more possible exceptions --- .../ApplicationHost.cs | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 0295f10981..5d5a63a635 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1181,10 +1181,32 @@ namespace Emby.Server.Implementations { Logger.LogInformation("Loading assemblies"); - AllConcreteTypes = GetComposablePartAssemblies() - .SelectMany(x => x.ExportedTypes) - .Where(type => type.IsClass && !type.IsAbstract && !type.IsInterface && !type.IsGenericType) - .ToArray(); + AllConcreteTypes = GetTypes(GetComposablePartAssemblies()).ToArray(); + } + + private IEnumerable GetTypes(IEnumerable assemblies) + { + foreach (var ass in assemblies) + { + Type[] exportedTypes; + try + { + exportedTypes = ass.GetExportedTypes(); + } + catch (TypeLoadException ex) + { + Logger.LogError(ex, "Error getting exported types from {Assembly}", ass.FullName); + continue; + } + + foreach (Type type in exportedTypes) + { + if (type.IsClass && !type.IsAbstract && !type.IsInterface && !type.IsGenericType) + { + yield return type; + } + } + } } private CertificateInfo CertificateInfo { get; set; } @@ -1353,7 +1375,7 @@ namespace Emby.Server.Implementations { plugAss = Assembly.LoadFrom(file); } - catch (TypeLoadException ex) + catch (FileLoadException ex) { Logger.LogError(ex, "Failed to load assembly {Path}", file); continue; From 764c6d5461ff359f50ab76a60589d871545eec2a Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Sat, 20 Apr 2019 17:42:11 +0200 Subject: [PATCH 3/6] Fix comparison for empty password migration --- Emby.Server.Implementations/Data/SqliteUserRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs index 182df0edc9..5957b29031 100644 --- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserRepository.cs @@ -81,7 +81,7 @@ namespace Emby.Server.Implementations.Data { // If the user password is the sha1 hash of the empty string, remove it if (!string.Equals(user.Password, "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal) - || !string.Equals(user.Password, "$SHA1$DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal)) + && !string.Equals(user.Password, "$SHA1$DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal)) { continue; } From 5fb4922c6f0d01050bca109a2e066163da5863fa Mon Sep 17 00:00:00 2001 From: Joshua Boniface Date: Sat, 20 Apr 2019 14:24:40 -0400 Subject: [PATCH 4/6] Bump version to 10.3.1 --- Dockerfile | 2 +- Dockerfile.arm | 2 +- Dockerfile.arm64 | 2 +- SharedVersion.cs | 4 ++-- build.yaml | 2 +- deployment/debian-package-x64/pkg-src/changelog | 6 ++++++ deployment/fedora-package-x64/pkg-src/jellyfin.spec | 4 +++- 7 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 050f8fc49f..3242003e16 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,7 +21,7 @@ RUN apt-get update \ COPY --from=ffmpeg / / COPY --from=builder /jellyfin /jellyfin -ARG JELLYFIN_WEB_VERSION=10.3.0 +ARG JELLYFIN_WEB_VERSION=10.3.1 RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && rm -rf /jellyfin/jellyfin-web \ && mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web diff --git a/Dockerfile.arm b/Dockerfile.arm index 81089b5196..3cec9133f1 100644 --- a/Dockerfile.arm +++ b/Dockerfile.arm @@ -30,7 +30,7 @@ RUN apt-get update \ && chmod 777 /cache /config /media COPY --from=builder /jellyfin /jellyfin -ARG JELLYFIN_WEB_VERSION=10.3.0 +ARG JELLYFIN_WEB_VERSION=10.3.1 RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && rm -rf /jellyfin/jellyfin-web \ && mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index 1ad4dc5a28..476ef2929d 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -31,7 +31,7 @@ RUN apt-get update \ && chmod 777 /cache /config /media COPY --from=builder /jellyfin /jellyfin -ARG JELLYFIN_WEB_VERSION=10.3.0 +ARG JELLYFIN_WEB_VERSION=10.3.1 RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && rm -rf /jellyfin/jellyfin-web \ && mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web diff --git a/SharedVersion.cs b/SharedVersion.cs index 3a0263bd67..9120f988cf 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,4 +1,4 @@ using System.Reflection; -[assembly: AssemblyVersion("10.3.0")] -[assembly: AssemblyFileVersion("10.3.0")] +[assembly: AssemblyVersion("10.3.1")] +[assembly: AssemblyFileVersion("10.3.1")] diff --git a/build.yaml b/build.yaml index 47ca589f8b..0f843bf74a 100644 --- a/build.yaml +++ b/build.yaml @@ -1,7 +1,7 @@ --- # We just wrap `build` so this is really it name: "jellyfin" -version: "10.3.0" +version: "10.3.1" packages: - debian-package-x64 - debian-package-armhf diff --git a/deployment/debian-package-x64/pkg-src/changelog b/deployment/debian-package-x64/pkg-src/changelog index 3908c277b3..ce30ca6f6d 100644 --- a/deployment/debian-package-x64/pkg-src/changelog +++ b/deployment/debian-package-x64/pkg-src/changelog @@ -1,3 +1,9 @@ +jellyfin (10.3.1-1) unstable; urgency=medium + + * New upstream version 10.3.1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.1 + + -- Jellyfin Packaging Team Sat, 20 Apr 2019 14:24:07 -0400 + jellyfin (10.3.0-1) unstable; urgency=medium * New upstream version 10.3.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.0 diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.spec b/deployment/fedora-package-x64/pkg-src/jellyfin.spec index 36fe78c628..0d938aef96 100644 --- a/deployment/fedora-package-x64/pkg-src/jellyfin.spec +++ b/deployment/fedora-package-x64/pkg-src/jellyfin.spec @@ -7,7 +7,7 @@ %endif Name: jellyfin -Version: 10.3.0 +Version: 10.3.1 Release: 1%{?dist} Summary: The Free Software Media Browser License: GPLv2 @@ -140,6 +140,8 @@ fi %systemd_postun_with_restart jellyfin.service %changelog +* Sat Apr 20 2019 Jellyfin Packaging Team +- New upstream version 10.3.1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.1 * Fri Apr 19 2019 Jellyfin Packaging Team - New upstream version 10.3.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.0 * Thu Feb 28 2019 Jellyfin Packaging Team From 696a36b4a5ada69f00875afa5e1ff1c596c7a371 Mon Sep 17 00:00:00 2001 From: Joshua Boniface Date: Sat, 20 Apr 2019 15:59:50 -0400 Subject: [PATCH 5/6] Update submodule for 10.3.1 --- MediaBrowser.WebDashboard/jellyfin-web | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.WebDashboard/jellyfin-web b/MediaBrowser.WebDashboard/jellyfin-web index 874b51234e..97f6808e12 160000 --- a/MediaBrowser.WebDashboard/jellyfin-web +++ b/MediaBrowser.WebDashboard/jellyfin-web @@ -1 +1 @@ -Subproject commit 874b51234ee4e1f01e2e7410980a1003f316d6a2 +Subproject commit 97f6808e12243bbb9b58344185511a9369913c0b From 28c2ac528d46ba97b920d37300fa814bd6f4a51a Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Wed, 24 Apr 2019 14:06:54 +0200 Subject: [PATCH 6/6] Re-add content length, semi revert of changes in #1010 (#1287) * Re-add content length, semi revert of changes in #1010 --- .../HttpServer/FileWriter.cs | 6 +- .../HttpServer/HttpListenerHost.cs | 1 + .../HttpServer/HttpResultFactory.cs | 13 ++- .../HttpServer/RangeRequestWriter.cs | 1 + .../HttpServer/ResponseFilter.cs | 3 +- .../HttpServer/StreamWriter.cs | 11 ++- .../Services/HttpResult.cs | 5 ++ .../Services/ResponseHelper.cs | 79 ++++++++----------- .../BaseProgressiveStreamingService.cs | 53 ++++++++++++- 9 files changed, 113 insertions(+), 59 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs index c4d2a70e23..c6b7d31a83 100644 --- a/Emby.Server.Implementations/HttpServer/FileWriter.cs +++ b/Emby.Server.Implementations/HttpServer/FileWriter.cs @@ -67,6 +67,7 @@ namespace Emby.Server.Implementations.HttpServer if (string.IsNullOrWhiteSpace(rangeHeader)) { + Headers[HeaderNames.ContentLength] = TotalContentLength.ToString(CultureInfo.InvariantCulture); StatusCode = HttpStatusCode.OK; } else @@ -99,10 +100,13 @@ namespace Emby.Server.Implementations.HttpServer RangeStart = requestedRange.Key; RangeLength = 1 + RangeEnd - RangeStart; + // Content-Length is the length of what we're serving, not the original content + var lengthString = RangeLength.ToString(CultureInfo.InvariantCulture); + Headers[HeaderNames.ContentLength] = lengthString; var rangeString = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}"; Headers[HeaderNames.ContentRange] = rangeString; - Logger.LogInformation("Setting range response values for {0}. RangeRequest: {1} Content-Range: {2}", Path, RangeHeader, rangeString); + Logger.LogInformation("Setting range response values for {0}. RangeRequest: {1} Content-Length: {2}, Content-Range: {3}", Path, RangeHeader, lengthString, rangeString); } /// diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 831391cee6..1fd27a7e36 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -638,6 +638,7 @@ namespace Emby.Server.Implementations.HttpServer private static Task Write(IResponse response, string text) { var bOutput = Encoding.UTF8.GetBytes(text); + response.OriginalResponse.ContentLength = bOutput.Length; return response.OutputStream.WriteAsync(bOutput, 0, bOutput.Length); } diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index 134f3c8418..5c29988c76 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -132,7 +132,7 @@ namespace Emby.Server.Implementations.HttpServer content = Array.Empty(); } - result = new StreamWriter(content, contentType); + result = new StreamWriter(content, contentType, contentLength); } else { @@ -176,7 +176,7 @@ namespace Emby.Server.Implementations.HttpServer bytes = Array.Empty(); } - result = new StreamWriter(bytes, contentType); + result = new StreamWriter(bytes, contentType, contentLength); } else { @@ -335,13 +335,13 @@ namespace Emby.Server.Implementations.HttpServer if (isHeadRequest) { - var result = new StreamWriter(Array.Empty(), contentType); + var result = new StreamWriter(Array.Empty(), contentType, contentLength); AddResponseHeaders(result, responseHeaders); return result; } else { - var result = new StreamWriter(content, contentType); + var result = new StreamWriter(content, contentType, contentLength); AddResponseHeaders(result, responseHeaders); return result; } @@ -581,6 +581,11 @@ namespace Emby.Server.Implementations.HttpServer } else { + if (totalContentLength.HasValue) + { + responseHeaders["Content-Length"] = totalContentLength.Value.ToString(CultureInfo.InvariantCulture); + } + if (isHeadRequest) { using (stream) diff --git a/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs index 449159834a..e27f794ba6 100644 --- a/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs +++ b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs @@ -96,6 +96,7 @@ namespace Emby.Server.Implementations.HttpServer RangeStart = requestedRange.Key; RangeLength = 1 + RangeEnd - RangeStart; + Headers[HeaderNames.ContentLength] = RangeLength.ToString(CultureInfo.InvariantCulture); Headers[HeaderNames.ContentRange] = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}"; if (RangeStart > 0 && SourceStream.CanSeek) diff --git a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs index a53d9bf0b9..08f4247577 100644 --- a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs +++ b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs @@ -26,7 +26,7 @@ namespace Emby.Server.Implementations.HttpServer public void FilterResponse(IRequest req, IResponse res, object dto) { // Try to prevent compatibility view - res.AddHeader("Access-Control-Allow-Headers", "Accept, Accept-Language, Authorization, Cache-Control, Content-Disposition, Content-Encoding, Content-Language, Content-MD5, Content-Range, Content-Type, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, X-Emby-Authorization"); + res.AddHeader("Access-Control-Allow-Headers", "Accept, Accept-Language, Authorization, Cache-Control, Content-Disposition, Content-Encoding, Content-Language, Content-Length, Content-MD5, Content-Range, Content-Type, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, X-Emby-Authorization"); res.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); res.AddHeader("Access-Control-Allow-Origin", "*"); @@ -58,6 +58,7 @@ namespace Emby.Server.Implementations.HttpServer if (length > 0) { + res.OriginalResponse.ContentLength = length; res.SendChunked = false; } } diff --git a/Emby.Server.Implementations/HttpServer/StreamWriter.cs b/Emby.Server.Implementations/HttpServer/StreamWriter.cs index 324f9085e5..194d04441a 100644 --- a/Emby.Server.Implementations/HttpServer/StreamWriter.cs +++ b/Emby.Server.Implementations/HttpServer/StreamWriter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -49,6 +50,13 @@ namespace Emby.Server.Implementations.HttpServer SourceStream = source; + Headers["Content-Type"] = contentType; + + if (source.CanSeek) + { + Headers[HeaderNames.ContentLength] = source.Length.ToString(CultureInfo.InvariantCulture); + } + Headers[HeaderNames.ContentType] = contentType; } @@ -57,7 +65,7 @@ namespace Emby.Server.Implementations.HttpServer /// /// The source. /// Type of the content. - public StreamWriter(byte[] source, string contentType) + public StreamWriter(byte[] source, string contentType, int contentLength) { if (string.IsNullOrEmpty(contentType)) { @@ -66,6 +74,7 @@ namespace Emby.Server.Implementations.HttpServer SourceBytes = source; + Headers[HeaderNames.ContentLength] = contentLength.ToString(CultureInfo.InvariantCulture); Headers[HeaderNames.ContentType] = contentType; } diff --git a/Emby.Server.Implementations/Services/HttpResult.cs b/Emby.Server.Implementations/Services/HttpResult.cs index b6758486ce..2b5963a770 100644 --- a/Emby.Server.Implementations/Services/HttpResult.cs +++ b/Emby.Server.Implementations/Services/HttpResult.cs @@ -43,6 +43,11 @@ namespace Emby.Server.Implementations.Services { var contentLength = bytesResponse.Length; + if (response != null) + { + response.OriginalResponse.ContentLength = contentLength; + } + if (contentLength > 0) { await responseStream.WriteAsync(bytesResponse, 0, contentLength, cancellationToken).ConfigureAwait(false); diff --git a/Emby.Server.Implementations/Services/ResponseHelper.cs b/Emby.Server.Implementations/Services/ResponseHelper.cs index 0301ff335f..251ba3529a 100644 --- a/Emby.Server.Implementations/Services/ResponseHelper.cs +++ b/Emby.Server.Implementations/Services/ResponseHelper.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Net; using System.Text; @@ -20,6 +21,8 @@ namespace Emby.Server.Implementations.Services { response.StatusCode = (int)HttpStatusCode.NoContent; } + + response.OriginalResponse.ContentLength = 0; return Task.CompletedTask; } @@ -39,11 +42,6 @@ namespace Emby.Server.Implementations.Services response.StatusCode = httpResult.Status; response.StatusDescription = httpResult.StatusCode.ToString(); - //if (string.IsNullOrEmpty(httpResult.ContentType)) - //{ - // httpResult.ContentType = defaultContentType; - //} - //response.ContentType = httpResult.ContentType; } var responseOptions = result as IHasHeaders; @@ -53,6 +51,7 @@ namespace Emby.Server.Implementations.Services { if (string.Equals(responseHeaders.Key, "Content-Length", StringComparison.OrdinalIgnoreCase)) { + response.OriginalResponse.ContentLength = long.Parse(responseHeaders.Value, CultureInfo.InvariantCulture); continue; } @@ -72,52 +71,37 @@ namespace Emby.Server.Implementations.Services response.ContentType += "; charset=utf-8"; } - var asyncStreamWriter = result as IAsyncStreamWriter; - if (asyncStreamWriter != null) + switch (result) { - return asyncStreamWriter.WriteToAsync(response.OutputStream, cancellationToken); - } + case IAsyncStreamWriter asyncStreamWriter: + return asyncStreamWriter.WriteToAsync(response.OutputStream, cancellationToken); + case IStreamWriter streamWriter: + streamWriter.WriteTo(response.OutputStream); + return Task.CompletedTask; + case FileWriter fileWriter: + return fileWriter.WriteToAsync(response, cancellationToken); + case Stream stream: + return CopyStream(stream, response.OutputStream); + case byte[] bytes: + response.ContentType = "application/octet-stream"; + response.OriginalResponse.ContentLength = bytes.Length; - var streamWriter = result as IStreamWriter; - if (streamWriter != null) - { - streamWriter.WriteTo(response.OutputStream); - return Task.CompletedTask; - } + if (bytes.Length > 0) + { + return response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken); + } - var fileWriter = result as FileWriter; - if (fileWriter != null) - { - return fileWriter.WriteToAsync(response, cancellationToken); - } + return Task.CompletedTask; + case string responseText: + var responseTextAsBytes = Encoding.UTF8.GetBytes(responseText); + response.OriginalResponse.ContentLength = responseTextAsBytes.Length; - var stream = result as Stream; - if (stream != null) - { - return CopyStream(stream, response.OutputStream); - } + if (responseTextAsBytes.Length > 0) + { + return response.OutputStream.WriteAsync(responseTextAsBytes, 0, responseTextAsBytes.Length, cancellationToken); + } - var bytes = result as byte[]; - if (bytes != null) - { - response.ContentType = "application/octet-stream"; - - if (bytes.Length > 0) - { - return response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken); - } - return Task.CompletedTask; - } - - var responseText = result as string; - if (responseText != null) - { - bytes = Encoding.UTF8.GetBytes(responseText); - if (bytes.Length > 0) - { - return response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken); - } - return Task.CompletedTask; + return Task.CompletedTask; } return WriteObject(request, result, response); @@ -143,14 +127,13 @@ namespace Emby.Server.Implementations.Services ms.Position = 0; var contentLength = ms.Length; + response.OriginalResponse.ContentLength = contentLength; if (contentLength > 0) { await ms.CopyToAsync(response.OutputStream).ConfigureAwait(false); } } - - //serializer(result, outputStream); } } } diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index a2c20e38fd..1c36289c5b 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -13,6 +14,7 @@ using MediaBrowser.Controller.Net; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.Services; using Microsoft.Net.Http.Headers; namespace MediaBrowser.Api.Playback.Progressive @@ -279,10 +281,9 @@ namespace MediaBrowser.Api.Playback.Progressive /// Task{System.Object}. private async Task GetStaticRemoteStreamResult(StreamState state, Dictionary responseHeaders, bool isHeadRequest, CancellationTokenSource cancellationTokenSource) { - string useragent = null; - state.RemoteHttpHeaders.TryGetValue("User-Agent", out useragent); + state.RemoteHttpHeaders.TryGetValue(HeaderNames.UserAgent, out var useragent); - var trySupportSeek = false; + const bool trySupportSeek = false; var options = new HttpRequestOptions { @@ -317,6 +318,12 @@ namespace MediaBrowser.Api.Playback.Progressive responseHeaders[HeaderNames.AcceptRanges] = "none"; } + // Seeing cases of -1 here + if (response.ContentLength.HasValue && response.ContentLength.Value >= 0) + { + responseHeaders[HeaderNames.ContentLength] = response.ContentLength.Value.ToString(CultureInfo.InvariantCulture); + } + if (isHeadRequest) { using (response) @@ -356,10 +363,31 @@ namespace MediaBrowser.Api.Playback.Progressive var contentType = state.GetMimeType(outputPath); // TODO: The isHeadRequest is only here because ServiceStack will add Content-Length=0 to the response + var contentLength = state.EstimateContentLength || isHeadRequest ? GetEstimatedContentLength(state) : null; + + if (contentLength.HasValue) + { + responseHeaders[HeaderNames.ContentLength] = contentLength.Value.ToString(CultureInfo.InvariantCulture); + } + // Headers only if (isHeadRequest) { - return ResultFactory.GetResult(null, Array.Empty(), contentType, responseHeaders); + var streamResult = ResultFactory.GetResult(null, Array.Empty(), contentType, responseHeaders); + + if (streamResult is IHasHeaders hasHeaders) + { + if (contentLength.HasValue) + { + hasHeaders.Headers[HeaderNames.ContentLength] = contentLength.Value.ToString(CultureInfo.InvariantCulture); + } + else + { + hasHeaders.Headers.Remove(HeaderNames.ContentLength); + } + } + + return streamResult; } var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(outputPath); @@ -397,5 +425,22 @@ namespace MediaBrowser.Api.Playback.Progressive transcodingLock.Release(); } } + + /// + /// Gets the length of the estimated content. + /// + /// The state. + /// System.Nullable{System.Int64}. + private long? GetEstimatedContentLength(StreamState state) + { + var totalBitrate = state.TotalOutputBitrate ?? 0; + + if (totalBitrate > 0 && state.RunTimeTicks.HasValue) + { + return Convert.ToInt64(totalBitrate * TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalSeconds / 8); + } + + return null; + } } }