Browse Source

Merge pull request #1304 from jellyfin/release-10.3.z

Backmerge 10.3.1
Bond-009 6 years ago
parent
commit
61d7bed181

+ 1 - 1
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

+ 1 - 1
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

+ 1 - 1
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

+ 40 - 7
Emby.Server.Implementations/ApplicationHost.cs

@@ -1146,7 +1146,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;
             }
 
@@ -1160,10 +1160,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<Type> GetTypes(IEnumerable<Assembly> 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; }
@@ -1327,8 +1349,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 (FileLoadException ex)
+                    {
+                        Logger.LogError(ex, "Failed to load assembly {Path}", file);
+                        continue;
+                    }
+
+                    Logger.LogInformation("Loaded assembly {Assembly} from {Path}", plugAss.FullName, file);
+                    yield return plugAss;
                 }
             }
 

+ 1 - 1
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;
                 }

+ 5 - 1
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);
         }
 
         /// <summary>

+ 1 - 0
Emby.Server.Implementations/HttpServer/HttpListenerHost.cs

@@ -626,6 +626,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);
         }
 

+ 9 - 4
Emby.Server.Implementations/HttpServer/HttpResultFactory.cs

@@ -132,7 +132,7 @@ namespace Emby.Server.Implementations.HttpServer
                     content = Array.Empty<byte>();
                 }
 
-                result = new StreamWriter(content, contentType);
+                result = new StreamWriter(content, contentType, contentLength);
             }
             else
             {
@@ -176,7 +176,7 @@ namespace Emby.Server.Implementations.HttpServer
                     bytes = Array.Empty<byte>();
                 }
 
-                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<byte>(), contentType);
+                var result = new StreamWriter(Array.Empty<byte>(), 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)

+ 1 - 0
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)

+ 2 - 1
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;
                     }
                 }

+ 10 - 1
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
         /// </summary>
         /// <param name="source">The source.</param>
         /// <param name="contentType">Type of the content.</param>
-        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;
         }
 

+ 5 - 0
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);

+ 32 - 49
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)
-            {
-                return asyncStreamWriter.WriteToAsync(response.OutputStream, cancellationToken);
-            }
-
-            var streamWriter = result as IStreamWriter;
-            if (streamWriter != null)
+            switch (result)
             {
-                streamWriter.WriteTo(response.OutputStream);
-                return Task.CompletedTask;
-            }
-
-            var fileWriter = result as FileWriter;
-            if (fileWriter != null)
-            {
-                return fileWriter.WriteToAsync(response, cancellationToken);
-            }
-
-            var stream = result as Stream;
-            if (stream != null)
-            {
-                return CopyStream(stream, response.OutputStream);
-            }
+                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;
+
+                    if (bytes.Length > 0)
+                    {
+                        return response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
+                    }
 
-            var bytes = result as byte[];
-            if (bytes != null)
-            {
-                response.ContentType = "application/octet-stream";
+                    return Task.CompletedTask;
+                case string responseText:
+                    var responseTextAsBytes = Encoding.UTF8.GetBytes(responseText);
+                    response.OriginalResponse.ContentLength = responseTextAsBytes.Length;
 
-                if (bytes.Length > 0)
-                {
-                    return response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
-                }
-                return Task.CompletedTask;
-            }
+                    if (responseTextAsBytes.Length > 0)
+                    {
+                        return response.OutputStream.WriteAsync(responseTextAsBytes, 0, responseTextAsBytes.Length, cancellationToken);
+                    }
 
-            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);
         }
     }
 }

+ 49 - 4
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
         /// <returns>Task{System.Object}.</returns>
         private async Task<object> GetStaticRemoteStreamResult(StreamState state, Dictionary<string, string> 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<byte>(), contentType, responseHeaders);
+                var streamResult = ResultFactory.GetResult(null, Array.Empty<byte>(), 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();
             }
         }
+
+        /// <summary>
+        /// Gets the length of the estimated content.
+        /// </summary>
+        /// <param name="state">The state.</param>
+        /// <returns>System.Nullable{System.Int64}.</returns>
+        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;
+        }
     }
 }

+ 1 - 1
MediaBrowser.WebDashboard/jellyfin-web

@@ -1 +1 @@
-Subproject commit 874b51234ee4e1f01e2e7410980a1003f316d6a2
+Subproject commit 97f6808e12243bbb9b58344185511a9369913c0b

+ 2 - 2
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")]

+ 1 - 1
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

+ 6 - 0
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 <packaging@jellyfin.org>  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

+ 3 - 1
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 <packaging@jellyfin.org>
+- 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 <packaging@jellyfin.org>
 - 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 <packaging@jellyfin.org>