aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Dockerfile2
-rw-r--r--Dockerfile.arm2
-rw-r--r--Dockerfile.arm642
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs47
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserRepository.cs2
-rw-r--r--Emby.Server.Implementations/HttpServer/FileWriter.cs6
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs1
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpResultFactory.cs15
-rw-r--r--Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs1
-rw-r--r--Emby.Server.Implementations/HttpServer/ResponseFilter.cs3
-rw-r--r--Emby.Server.Implementations/HttpServer/StreamWriter.cs11
-rw-r--r--Emby.Server.Implementations/Library/UserManager.cs2
-rw-r--r--Emby.Server.Implementations/Services/HttpResult.cs5
-rw-r--r--Emby.Server.Implementations/Services/ResponseHelper.cs81
-rw-r--r--Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs2
-rw-r--r--Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs110
-rw-r--r--MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs72
-rw-r--r--MediaBrowser.Common/Net/CustomHeaderNames.cs11
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs37
-rw-r--r--MediaBrowser.Model/Services/IHttpRequest.cs20
-rw-r--r--MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs2
-rw-r--r--MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs2
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs2
m---------MediaBrowser.WebDashboard/jellyfin-web0
-rw-r--r--README.md2
-rw-r--r--SharedVersion.cs4
-rw-r--r--build.yaml4
-rw-r--r--deployment/debian-package-arm64/Dockerfile.amd6443
-rw-r--r--deployment/debian-package-arm64/Dockerfile.arm6434
-rwxr-xr-xdeployment/debian-package-arm64/clean.sh29
-rw-r--r--deployment/debian-package-arm64/dependencies.txt1
-rwxr-xr-xdeployment/debian-package-arm64/docker-build.sh20
-rwxr-xr-xdeployment/debian-package-arm64/package.sh42
l---------deployment/debian-package-arm64/pkg-src1
-rw-r--r--deployment/debian-package-x64/pkg-src/changelog12
-rw-r--r--deployment/debian-package-x64/pkg-src/control2
-rw-r--r--deployment/debian-package-x64/pkg-src/rules13
-rw-r--r--deployment/fedora-package-x64/pkg-src/jellyfin.spec6
-rw-r--r--deployment/ubuntu-package-arm64/Dockerfile.amd6453
-rw-r--r--deployment/ubuntu-package-arm64/Dockerfile.arm6434
-rwxr-xr-xdeployment/ubuntu-package-arm64/clean.sh29
-rw-r--r--deployment/ubuntu-package-arm64/dependencies.txt1
-rwxr-xr-xdeployment/ubuntu-package-arm64/docker-build.sh20
-rwxr-xr-xdeployment/ubuntu-package-arm64/package.sh42
l---------deployment/ubuntu-package-arm64/pkg-src1
45 files changed, 594 insertions, 237 deletions
diff --git a/Dockerfile b/Dockerfile
index 050f8fc49..f414995f7 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.2
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 81089b519..66f731354 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.2
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 1ad4dc5a2..690d4b6e7 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.2
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/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index aa0fdde2d..53bc85b28 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/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;
}
}
diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs
index 182df0edc..5957b2903 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;
}
diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs
index c4d2a70e2..c6b7d31a8 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);
}
/// <summary>
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index 06d304077..b3d2b9cc2 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/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);
}
diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
index 134f3c841..0b2924a3b 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<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)
@@ -624,7 +629,7 @@ namespace Emby.Server.Implementations.HttpServer
if (lastModifiedDate.HasValue)
{
- responseHeaders[HeaderNames.LastModified] = lastModifiedDate.ToString();
+ responseHeaders[HeaderNames.LastModified] = lastModifiedDate.Value.ToString(CultureInfo.InvariantCulture);
}
}
diff --git a/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs
index 449159834..e27f794ba 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 a53d9bf0b..08f424757 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 324f9085e..194d04441 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
/// </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;
}
diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs
index 952cc6896..c33bb7740 100644
--- a/Emby.Server.Implementations/Library/UserManager.cs
+++ b/Emby.Server.Implementations/Library/UserManager.cs
@@ -596,7 +596,7 @@ namespace Emby.Server.Implementations.Library
}
bool hasConfiguredPassword = GetAuthenticationProvider(user).HasPassword(user).Result;
- bool hasConfiguredEasyPassword = string.IsNullOrEmpty(GetLocalPasswordHash(user));
+ bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(GetLocalPasswordHash(user));
bool hasPassword = user.Configuration.EnableLocalPassword && !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ?
hasConfiguredEasyPassword :
diff --git a/Emby.Server.Implementations/Services/HttpResult.cs b/Emby.Server.Implementations/Services/HttpResult.cs
index b6758486c..2b5963a77 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 0301ff335..251ba3529 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)
- {
- 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);
}
}
}
diff --git a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
index f835aa1b5..6a522fbef 100644
--- a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
+++ b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
@@ -71,7 +71,7 @@ namespace Emby.Server.Implementations.Services
string propertyName = pair.Key;
string propertyTextValue = pair.Value;
- if (string.IsNullOrEmpty(propertyTextValue)
+ if (propertyTextValue == null
|| !propertySetterMap.TryGetValue(propertyName, out propertySerializerEntry)
|| propertySerializerEntry.PropertySetFn == null)
{
diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs
index 792615a0f..00465b63e 100644
--- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs
+++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs
@@ -4,6 +4,7 @@ using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
+using MediaBrowser.Common.Net;
using MediaBrowser.Model.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
@@ -38,16 +39,9 @@ namespace Emby.Server.Implementations.SocketSharp
public string RawUrl => request.GetEncodedPathAndQuery();
public string AbsoluteUri => request.GetDisplayUrl().TrimEnd('/');
+ // Header[name] returns "" when undefined
- public string XForwardedFor
- => StringValues.IsNullOrEmpty(request.Headers["X-Forwarded-For"]) ? null : request.Headers["X-Forwarded-For"].ToString();
-
- public int? XForwardedPort
- => StringValues.IsNullOrEmpty(request.Headers["X-Forwarded-Port"]) ? (int?)null : int.Parse(request.Headers["X-Forwarded-Port"], CultureInfo.InvariantCulture);
-
- public string XForwardedProtocol => StringValues.IsNullOrEmpty(request.Headers["X-Forwarded-Proto"]) ? null : request.Headers["X-Forwarded-Proto"].ToString();
-
- public string XRealIp => StringValues.IsNullOrEmpty(request.Headers["X-Real-IP"]) ? null : request.Headers["X-Real-IP"].ToString();
+ private string GetHeader(string name) => request.Headers[name].ToString();
private string remoteIp;
public string RemoteIp
@@ -59,101 +53,27 @@ namespace Emby.Server.Implementations.SocketSharp
return remoteIp;
}
- var temp = CheckBadChars(XForwardedFor.AsSpan());
- if (temp.Length != 0)
- {
- return remoteIp = temp.ToString();
- }
+ IPAddress ip;
- temp = CheckBadChars(XRealIp.AsSpan());
- if (temp.Length != 0)
+ // "Real" remote ip might be in X-Forwarded-For of X-Real-Ip
+ // (if the server is behind a reverse proxy for example)
+ if (!IPAddress.TryParse(GetHeader(CustomHeaderNames.XForwardedFor), out ip))
{
- return remoteIp = NormalizeIp(temp).ToString();
- }
-
- return remoteIp = NormalizeIp(request.HttpContext.Connection.RemoteIpAddress.ToString().AsSpan()).ToString();
- }
- }
-
- private static readonly char[] HttpTrimCharacters = new char[] { (char)0x09, (char)0xA, (char)0xB, (char)0xC, (char)0xD, (char)0x20 };
-
- // CheckBadChars - throws on invalid chars to be not found in header name/value
- internal static ReadOnlySpan<char> CheckBadChars(ReadOnlySpan<char> name)
- {
- if (name.Length == 0)
- {
- return name;
- }
-
- // VALUE check
- // Trim spaces from both ends
- name = name.Trim(HttpTrimCharacters);
-
- // First, check for correctly formed multi-line value
- // Second, check for absence of CTL characters
- int crlf = 0;
- for (int i = 0; i < name.Length; ++i)
- {
- char c = (char)(0x000000ff & (uint)name[i]);
- switch (crlf)
- {
- case 0:
- if (c == '\r')
- {
- crlf = 1;
- }
- else if (c == '\n')
- {
- // Technically this is bad HTTP. But it would be a breaking change to throw here.
- // Is there an exploit?
- crlf = 2;
- }
- else if (c == 127 || (c < ' ' && c != '\t'))
- {
- throw new ArgumentException("net_WebHeaderInvalidControlChars", nameof(name));
- }
-
- break;
-
- case 1:
- if (c == '\n')
- {
- crlf = 2;
- break;
- }
-
- throw new ArgumentException("net_WebHeaderInvalidCRLFChars", nameof(name));
-
- case 2:
- if (c == ' ' || c == '\t')
- {
- crlf = 0;
- break;
- }
-
- throw new ArgumentException("net_WebHeaderInvalidCRLFChars", nameof(name));
+ if (!IPAddress.TryParse(GetHeader(CustomHeaderNames.XRealIP), out ip))
+ {
+ ip = request.HttpContext.Connection.RemoteIpAddress;
+ }
}
- }
- if (crlf != 0)
- {
- throw new ArgumentException("net_WebHeaderInvalidCRLFChars", nameof(name));
+ return remoteIp = NormalizeIp(ip).ToString();
}
-
- return name;
}
- private ReadOnlySpan<char> NormalizeIp(ReadOnlySpan<char> ip)
+ private static IPAddress NormalizeIp(IPAddress ip)
{
- if (ip.Length != 0 && !ip.IsWhiteSpace())
+ if (ip.IsIPv4MappedToIPv6)
{
- // Handle ipv4 mapped to ipv6
- const string srch = "::ffff:";
- var index = ip.IndexOf(srch.AsSpan(), StringComparison.OrdinalIgnoreCase);
- if (index == 0)
- {
- ip = ip.Slice(srch.Length);
- }
+ return ip.MapToIPv4();
}
return ip;
diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
index a2c20e38f..83a3f3e3c 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,7 @@ 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);
-
- var trySupportSeek = false;
+ state.RemoteHttpHeaders.TryGetValue(HeaderNames.UserAgent, out var useragent);
var options = new HttpRequestOptions
{
@@ -292,29 +291,14 @@ namespace MediaBrowser.Api.Playback.Progressive
CancellationToken = cancellationTokenSource.Token
};
- if (trySupportSeek)
- {
- if (!string.IsNullOrWhiteSpace(Request.QueryString[HeaderNames.Range]))
- {
- options.RequestHeaders[HeaderNames.Range] = Request.QueryString[HeaderNames.Range];
- }
- }
var response = await HttpClient.GetResponse(options).ConfigureAwait(false);
- if (trySupportSeek)
- {
- foreach (var name in new[] { HeaderNames.ContentRange, HeaderNames.AcceptRanges })
- {
- var val = response.Headers[name];
- if (!string.IsNullOrWhiteSpace(val))
- {
- responseHeaders[name] = val;
- }
- }
- }
- else
+ responseHeaders[HeaderNames.AcceptRanges] = "none";
+
+ // Seeing cases of -1 here
+ if (response.ContentLength.HasValue && response.ContentLength.Value >= 0)
{
- responseHeaders[HeaderNames.AcceptRanges] = "none";
+ responseHeaders[HeaderNames.ContentLength] = response.ContentLength.Value.ToString(CultureInfo.InvariantCulture);
}
if (isHeadRequest)
@@ -356,10 +340,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 +402,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;
+ }
}
}
diff --git a/MediaBrowser.Common/Net/CustomHeaderNames.cs b/MediaBrowser.Common/Net/CustomHeaderNames.cs
new file mode 100644
index 000000000..ff148dc80
--- /dev/null
+++ b/MediaBrowser.Common/Net/CustomHeaderNames.cs
@@ -0,0 +1,11 @@
+namespace MediaBrowser.Common.Net
+{
+ public static class CustomHeaderNames
+ {
+ // Other Headers
+ public const string XForwardedFor = "X-Forwarded-For";
+ public const string XForwardedPort = "X-Forwarded-Port";
+ public const string XForwardedProto = "X-Forwarded-Proto";
+ public const string XRealIP = "X-Real-IP";
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index b626600fa..a8874b6d0 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -53,7 +53,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
private readonly int DefaultImageExtractionTimeoutMs;
private readonly string StartupOptionFFmpegPath;
- private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(1, 1);
+ private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(2, 2);
private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
private readonly ILocalizationManager _localization;
@@ -582,19 +582,27 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
bool ranToCompletion;
- StartProcess(processWrapper);
-
- var timeoutMs = ConfigurationManager.Configuration.ImageExtractionTimeoutMs;
- if (timeoutMs <= 0)
+ await _thumbnailResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
+ try
{
- timeoutMs = DefaultImageExtractionTimeoutMs;
- }
+ StartProcess(processWrapper);
+
+ var timeoutMs = ConfigurationManager.Configuration.ImageExtractionTimeoutMs;
+ if (timeoutMs <= 0)
+ {
+ timeoutMs = DefaultImageExtractionTimeoutMs;
+ }
- ranToCompletion = await process.WaitForExitAsync(timeoutMs).ConfigureAwait(false);
+ ranToCompletion = await process.WaitForExitAsync(timeoutMs).ConfigureAwait(false);
- if (!ranToCompletion)
+ if (!ranToCompletion)
+ {
+ StopProcess(processWrapper, 1000);
+ }
+ }
+ finally
{
- StopProcess(processWrapper, 1000);
+ _thumbnailResourcePool.Release();
}
var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;
@@ -625,7 +633,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
return time.ToString(@"hh\:mm\:ss\.fff", UsCulture);
}
- public async Task ExtractVideoImagesOnInterval(string[] inputFiles,
+ public async Task ExtractVideoImagesOnInterval(
+ string[] inputFiles,
string container,
MediaStream videoStream,
MediaProtocol protocol,
@@ -636,8 +645,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
int? maxWidth,
CancellationToken cancellationToken)
{
- var resourcePool = _thumbnailResourcePool;
-
var inputArgument = GetInputArgument(inputFiles, protocol);
var vf = "fps=fps=1/" + interval.TotalSeconds.ToString(UsCulture);
@@ -701,7 +708,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
_logger.LogInformation(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
- await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
+ await _thumbnailResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
bool ranToCompletion = false;
@@ -742,7 +749,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
finally
{
- resourcePool.Release();
+ _thumbnailResourcePool.Release();
}
var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;
diff --git a/MediaBrowser.Model/Services/IHttpRequest.cs b/MediaBrowser.Model/Services/IHttpRequest.cs
index 50c6076f3..daf91488f 100644
--- a/MediaBrowser.Model/Services/IHttpRequest.cs
+++ b/MediaBrowser.Model/Services/IHttpRequest.cs
@@ -8,26 +8,6 @@ namespace MediaBrowser.Model.Services
string HttpMethod { get; }
/// <summary>
- /// The IP Address of the X-Forwarded-For header, null if null or empty
- /// </summary>
- string XForwardedFor { get; }
-
- /// <summary>
- /// The Port number of the X-Forwarded-Port header, null if null or empty
- /// </summary>
- int? XForwardedPort { get; }
-
- /// <summary>
- /// The http or https scheme of the X-Forwarded-Proto header, null if null or empty
- /// </summary>
- string XForwardedProtocol { get; }
-
- /// <summary>
- /// The value of the X-Real-IP header, null if null or empty
- /// </summary>
- string XRealIp { get; }
-
- /// <summary>
/// The value of the Accept HTTP Request Header
/// </summary>
string Accept { get; }
diff --git a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
index 3797f9039..179e953f4 100644
--- a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
+++ b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
@@ -336,7 +336,7 @@ namespace MediaBrowser.Providers.Music
}
using (var subReader = reader.ReadSubtree())
{
- return ParseReleaseList(subReader);
+ return ParseReleaseList(subReader).ToList();
}
}
default:
diff --git a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs
index 59280df89..728f7731a 100644
--- a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs
+++ b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs
@@ -110,7 +110,7 @@ namespace MediaBrowser.Providers.Music
}
using (var subReader = reader.ReadSubtree())
{
- return ParseArtistList(subReader);
+ return ParseArtistList(subReader).ToList();
}
}
default:
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs
index 5474a7c39..302d40c6b 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs
@@ -18,7 +18,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
/// <summary>
/// Class RemoteEpisodeProvider
/// </summary>
- class TvdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IHasOrder
+ public class TvdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IHasOrder
{
private readonly IHttpClient _httpClient;
private readonly ILogger _logger;
diff --git a/MediaBrowser.WebDashboard/jellyfin-web b/MediaBrowser.WebDashboard/jellyfin-web
-Subproject 874b51234ee4e1f01e2e7410980a1003f316d6a
+Subproject 1ba58b06b3dc28e07abae124cff78aa656fcb7e
diff --git a/README.md b/README.md
index 6a206eacf..643c59a73 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@
Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. It is an alternative to the proprietary Emby and Plex, to provide media from a dedicated server to end-user devices via multiple apps. Jellyfin is descended from Emby's 3.5.2 release and ported to the .NET Core framework to enable full cross-platform support. There are no strings attached, no premium licenses or features, and no hidden agendas: just a team who want to build something better and work together to achieve it. We welcome anyone who is interested in joining us in our quest!
-For further details, please see [our documentation page](https://jellyfin.readthedocs.io). To receive the latest updates, get help with Jellyfin, and join the community, please visit [one of our communication channels on Matrix/Riot or social media](https://jellyfin.readthedocs.io/en/latest/user-docs/getting-help).
+For further details, please see [our documentation page](https://jellyfin.readthedocs.io). To receive the latest updates, get help with Jellyfin, and join the community, please visit [one of our communication channels on Matrix/Riot or social media](https://jellyfin.readthedocs.io/en/latest/getting-help/).
For more information about the project, please see our [about page](https://jellyfin.readthedocs.io/en/latest/about/).
diff --git a/SharedVersion.cs b/SharedVersion.cs
index 3a0263bd6..700ef4549 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.2")]
+[assembly: AssemblyFileVersion("10.3.2")]
diff --git a/build.yaml b/build.yaml
index 47ca589f8..8ce6a00a8 100644
--- a/build.yaml
+++ b/build.yaml
@@ -1,12 +1,14 @@
---
# We just wrap `build` so this is really it
name: "jellyfin"
-version: "10.3.0"
+version: "10.3.2"
packages:
- debian-package-x64
- debian-package-armhf
+ - debian-package-arm64
- ubuntu-package-x64
- ubuntu-package-armhf
+ - ubuntu-package-arm64
- fedora-package-x64
- centos-package-x64
- linux-x64
diff --git a/deployment/debian-package-arm64/Dockerfile.amd64 b/deployment/debian-package-arm64/Dockerfile.amd64
new file mode 100644
index 000000000..2cb8bd856
--- /dev/null
+++ b/deployment/debian-package-arm64/Dockerfile.amd64
@@ -0,0 +1,43 @@
+FROM debian:9
+# Docker build arguments
+ARG SOURCE_DIR=/jellyfin
+ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-arm64
+ARG ARTIFACT_DIR=/dist
+ARG SDK_VERSION=2.2
+# Docker run environment
+ENV SOURCE_DIR=/jellyfin
+ENV ARTIFACT_DIR=/dist
+ENV DEB_BUILD_OPTIONS=noddebs
+ENV ARCH=amd64
+
+# Prepare Debian build environment
+RUN apt-get update \
+ && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv
+
+# Install dotnet repository
+# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
+RUN wget https://download.visualstudio.microsoft.com/download/pr/69937b49-a877-4ced-81e6-286620b390ab/8ab938cf6f5e83b2221630354160ef21/dotnet-sdk-2.2.104-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+ && mkdir -p dotnet-sdk \
+ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
+ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
+
+# Prepare the cross-toolchain
+RUN dpkg --add-architecture arm64 \
+ && apt-get update \
+ && apt-get install -y cross-gcc-dev \
+ && TARGET_LIST="arm64" cross-gcc-gensource 6 \
+ && cd cross-gcc-packages-amd64/cross-gcc-6-arm64 \
+ && apt-get install -y gcc-6-source libstdc++6-arm64-cross binutils-aarch64-linux-gnu bison flex libtool gdb sharutils netbase libcloog-isl-dev libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip libc6-dev:arm64 linux-libc-dev:arm64 libgcc1:arm64 libcurl4-openssl-dev:arm64 libfontconfig1-dev:arm64 libfreetype6-dev:arm64 liblttng-ust0:arm64 libstdc++6:arm64
+
+# Link to docker-build script
+RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh
+
+# Link to Debian source dir; mkdir needed or it fails, can't force dest
+RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian
+
+VOLUME ${ARTIFACT_DIR}/
+
+COPY . ${SOURCE_DIR}/
+
+ENTRYPOINT ["/docker-build.sh"]
+#ENTRYPOINT ["/bin/bash"]
diff --git a/deployment/debian-package-arm64/Dockerfile.arm64 b/deployment/debian-package-arm64/Dockerfile.arm64
new file mode 100644
index 000000000..bb9e28d0a
--- /dev/null
+++ b/deployment/debian-package-arm64/Dockerfile.arm64
@@ -0,0 +1,34 @@
+FROM debian:9
+# Docker build arguments
+ARG SOURCE_DIR=/jellyfin
+ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-arm64
+ARG ARTIFACT_DIR=/dist
+ARG SDK_VERSION=2.2
+# Docker run environment
+ENV SOURCE_DIR=/jellyfin
+ENV ARTIFACT_DIR=/dist
+ENV DEB_BUILD_OPTIONS=noddebs
+ENV ARCH=arm64
+
+# Prepare Debian build environment
+RUN apt-get update \
+ && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev liblttng-ust0
+
+# Install dotnet repository
+# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
+RUN wget https://download.visualstudio.microsoft.com/download/pr/d9f37b73-df8d-4dfa-a905-b7648d3401d0/6312573ac13d7a8ddc16e4058f7d7dc5/dotnet-sdk-2.2.104-linux-arm.tar.gz -O dotnet-sdk.tar.gz \
+ && mkdir -p dotnet-sdk \
+ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
+ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
+
+# Link to docker-build script
+RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh
+
+# Link to Debian source dir; mkdir needed or it fails, can't force dest
+RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian
+
+VOLUME ${ARTIFACT_DIR}/
+
+COPY . ${SOURCE_DIR}/
+
+ENTRYPOINT ["/docker-build.sh"]
diff --git a/deployment/debian-package-arm64/clean.sh b/deployment/debian-package-arm64/clean.sh
new file mode 100755
index 000000000..ac143c3d0
--- /dev/null
+++ b/deployment/debian-package-arm64/clean.sh
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+
+source ../common.build.sh
+
+keep_artifacts="${1}"
+
+WORKDIR="$( pwd )"
+
+package_temporary_dir="${WORKDIR}/pkg-dist-tmp"
+output_dir="${WORKDIR}/pkg-dist"
+current_user="$( whoami )"
+image_name="jellyfin-debian_arm64-build"
+
+rm -rf "${package_temporary_dir}" &>/dev/null \
+ || sudo rm -rf "${package_temporary_dir}" &>/dev/null
+
+rm -rf "${output_dir}" &>/dev/null \
+ || sudo rm -rf "${output_dir}" &>/dev/null
+
+if [[ ${keep_artifacts} == 'n' ]]; then
+ docker_sudo=""
+ if [[ ! -z $(id -Gn | grep -q 'docker') ]] \
+ && [[ ! ${EUID:-1000} -eq 0 ]] \
+ && [[ ! ${USER} == "root" ]] \
+ && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then
+ docker_sudo=sudo
+ fi
+ ${docker_sudo} docker image rm ${image_name} --force
+fi
diff --git a/deployment/debian-package-arm64/dependencies.txt b/deployment/debian-package-arm64/dependencies.txt
new file mode 100644
index 000000000..bdb967096
--- /dev/null
+++ b/deployment/debian-package-arm64/dependencies.txt
@@ -0,0 +1 @@
+docker
diff --git a/deployment/debian-package-arm64/docker-build.sh b/deployment/debian-package-arm64/docker-build.sh
new file mode 100755
index 000000000..308f3df15
--- /dev/null
+++ b/deployment/debian-package-arm64/docker-build.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Builds the DEB inside the Docker container
+
+set -o errexit
+set -o xtrace
+
+# Move to source directory
+pushd ${SOURCE_DIR}
+
+# Remove build-dep for dotnet-sdk-2.2, since it's not a package in this image
+sed -i '/dotnet-sdk-2.2,/d' debian/control
+
+# Build DEB
+export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH}
+dpkg-buildpackage -us -uc -aarm64
+
+# Move the artifacts out
+mkdir -p ${ARTIFACT_DIR}/deb
+mv /jellyfin_* ${ARTIFACT_DIR}/deb/
diff --git a/deployment/debian-package-arm64/package.sh b/deployment/debian-package-arm64/package.sh
new file mode 100755
index 000000000..19f70d7f6
--- /dev/null
+++ b/deployment/debian-package-arm64/package.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+
+source ../common.build.sh
+
+ARCH="$( arch )"
+WORKDIR="$( pwd )"
+
+package_temporary_dir="${WORKDIR}/pkg-dist-tmp"
+output_dir="${WORKDIR}/pkg-dist"
+current_user="$( whoami )"
+image_name="jellyfin-debian_arm64-build"
+
+# Determine if sudo should be used for Docker
+if [[ ! -z $(id -Gn | grep -q 'docker') ]] \
+ && [[ ! ${EUID:-1000} -eq 0 ]] \
+ && [[ ! ${USER} == "root" ]] \
+ && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then
+ docker_sudo="sudo"
+else
+ docker_sudo=""
+fi
+
+# Determine which Dockerfile to use
+case $ARCH in
+ 'x86_64')
+ DOCKERFILE="Dockerfile.amd64"
+ ;;
+ 'armv7l')
+ DOCKERFILE="Dockerfile.arm64"
+ ;;
+esac
+
+# Set up the build environment Docker image
+${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE}
+# Build the DEBs and copy out to ${package_temporary_dir}
+${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}"
+# Correct ownership on the DEBs (as current user, then as root if that fails)
+chown -R "${current_user}" "${package_temporary_dir}" &>/dev/null \
+ || sudo chown -R "${current_user}" "${package_temporary_dir}" &>/dev/null
+# Move the DEBs to the output directory
+mkdir -p "${output_dir}"
+mv "${package_temporary_dir}"/deb/* "${output_dir}"
diff --git a/deployment/debian-package-arm64/pkg-src b/deployment/debian-package-arm64/pkg-src
new file mode 120000
index 000000000..4c695fea1
--- /dev/null
+++ b/deployment/debian-package-arm64/pkg-src
@@ -0,0 +1 @@
+../debian-package-x64/pkg-src \ No newline at end of file
diff --git a/deployment/debian-package-x64/pkg-src/changelog b/deployment/debian-package-x64/pkg-src/changelog
index 3908c277b..61977c4e0 100644
--- a/deployment/debian-package-x64/pkg-src/changelog
+++ b/deployment/debian-package-x64/pkg-src/changelog
@@ -1,3 +1,15 @@
+jellyfin (10.3.2-1) unstable; urgency=medium
+
+ * New upstream version 10.3.2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.2
+
+ -- Jellyfin Packaging Team <packaging@jellyfin.org> Tue, 30 Apr 2019 20:18:44 -0400
+
+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
diff --git a/deployment/debian-package-x64/pkg-src/control b/deployment/debian-package-x64/pkg-src/control
index d96660590..4422f0fda 100644
--- a/deployment/debian-package-x64/pkg-src/control
+++ b/deployment/debian-package-x64/pkg-src/control
@@ -23,6 +23,6 @@ Depends: at,
jellyfin-ffmpeg,
libfontconfig1,
libfreetype6,
- libssl1.0.0 | libssl1.0.2
+ libssl1.0.0 | libssl1.0.2 | libssl1.1
Description: Jellyfin is a home media server.
It is built on top of other popular open source technologies such as Service Stack, jQuery, jQuery mobile, and Mono. It features a REST-based api with built-in documentation to facilitate client development. We also have client libraries for our api to enable rapid development.
diff --git a/deployment/debian-package-x64/pkg-src/rules b/deployment/debian-package-x64/pkg-src/rules
index 62f75bc6b..ee41e0e24 100644
--- a/deployment/debian-package-x64/pkg-src/rules
+++ b/deployment/debian-package-x64/pkg-src/rules
@@ -6,18 +6,25 @@ SHELL := /bin/bash
HOST_ARCH := $(shell arch)
BUILD_ARCH := ${DEB_HOST_MULTIARCH}
ifeq ($(HOST_ARCH),x86_64)
+ # Building AMD64
+ DOTNETRUNTIME := debian-x64
ifeq ($(BUILD_ARCH),arm-linux-gnueabihf)
# Cross-building ARM on AMD64
DOTNETRUNTIME := debian-arm
- else
- # Building AMD64
- DOTNETRUNTIME := debian-x64
+ endif
+ ifeq ($(BUILD_ARCH),aarch64-linux-gnu)
+ # Cross-building ARM on AMD64
+ DOTNETRUNTIME := debian-arm64
endif
endif
ifeq ($(HOST_ARCH),armv7l)
# Building ARM
DOTNETRUNTIME := debian-arm
endif
+ifeq ($(HOST_ARCH),arm64)
+ # Building ARM
+ DOTNETRUNTIME := debian-arm64
+endif
export DH_VERBOSE=1
export DOTNET_CLI_TELEMETRY_OPTOUT=1
diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.spec b/deployment/fedora-package-x64/pkg-src/jellyfin.spec
index 36fe78c62..58d643569 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.2
Release: 1%{?dist}
Summary: The Free Software Media Browser
License: GPLv2
@@ -140,6 +140,10 @@ fi
%systemd_postun_with_restart jellyfin.service
%changelog
+* Tue Apr 30 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
+- New upstream version 10.3.2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.2
+* 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>
diff --git a/deployment/ubuntu-package-arm64/Dockerfile.amd64 b/deployment/ubuntu-package-arm64/Dockerfile.amd64
new file mode 100644
index 000000000..5e51ef0f0
--- /dev/null
+++ b/deployment/ubuntu-package-arm64/Dockerfile.amd64
@@ -0,0 +1,53 @@
+FROM ubuntu:bionic
+# Docker build arguments
+ARG SOURCE_DIR=/jellyfin
+ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-arm64
+ARG ARTIFACT_DIR=/dist
+ARG SDK_VERSION=2.2
+# Docker run environment
+ENV SOURCE_DIR=/jellyfin
+ENV ARTIFACT_DIR=/dist
+ENV DEB_BUILD_OPTIONS=noddebs
+ENV ARCH=amd64
+
+# Prepare Debian build environment
+RUN apt-get update \
+ && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv
+
+# Install dotnet repository
+# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
+RUN wget https://download.visualstudio.microsoft.com/download/pr/69937b49-a877-4ced-81e6-286620b390ab/8ab938cf6f5e83b2221630354160ef21/dotnet-sdk-2.2.104-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+ && mkdir -p dotnet-sdk \
+ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
+ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
+
+# Prepare the cross-toolchain
+RUN rm /etc/apt/sources.list \
+ && export CODENAME="$( lsb_release -c -s )" \
+ && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
+ && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
+ && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
+ && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
+ && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \
+ && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \
+ && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \
+ && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \
+ && dpkg --add-architecture arm64 \
+ && apt-get update \
+ && apt-get install -y cross-gcc-dev \
+ && TARGET_LIST="arm64" cross-gcc-gensource 6 \
+ && cd cross-gcc-packages-amd64/cross-gcc-6-arm64 \
+ && ln -fs /usr/share/zoneinfo/America/Toronto /etc/localtime \
+ && apt-get install -y gcc-6-source libstdc++6-arm64-cross binutils-aarch64-linux-gnu bison flex libtool gdb sharutils netbase libcloog-isl-dev libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip libc6-dev:arm64 linux-libc-dev:arm64 libgcc1:arm64 libcurl4-openssl-dev:arm64 libfontconfig1-dev:arm64 libfreetype6-dev:arm64 liblttng-ust0:arm64 libstdc++6:arm64
+
+# Link to docker-build script
+RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh
+
+# Link to Debian source dir; mkdir needed or it fails, can't force dest
+RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian
+
+VOLUME ${ARTIFACT_DIR}/
+
+COPY . ${SOURCE_DIR}/
+
+ENTRYPOINT ["/docker-build.sh"]
diff --git a/deployment/ubuntu-package-arm64/Dockerfile.arm64 b/deployment/ubuntu-package-arm64/Dockerfile.arm64
new file mode 100644
index 000000000..646679328
--- /dev/null
+++ b/deployment/ubuntu-package-arm64/Dockerfile.arm64
@@ -0,0 +1,34 @@
+FROM ubuntu:bionic
+# Docker build arguments
+ARG SOURCE_DIR=/jellyfin
+ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-arm64
+ARG ARTIFACT_DIR=/dist
+ARG SDK_VERSION=2.2
+# Docker run environment
+ENV SOURCE_DIR=/jellyfin
+ENV ARTIFACT_DIR=/dist
+ENV DEB_BUILD_OPTIONS=noddebs
+ENV ARCH=arm64
+
+# Prepare Debian build environment
+RUN apt-get update \
+ && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev liblttng-ust0
+
+# Install dotnet repository
+# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
+RUN wget https://download.visualstudio.microsoft.com/download/pr/d9f37b73-df8d-4dfa-a905-b7648d3401d0/6312573ac13d7a8ddc16e4058f7d7dc5/dotnet-sdk-2.2.104-linux-arm.tar.gz -O dotnet-sdk.tar.gz \
+ && mkdir -p dotnet-sdk \
+ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
+ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
+
+# Link to docker-build script
+RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh
+
+# Link to Debian source dir; mkdir needed or it fails, can't force dest
+RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian
+
+VOLUME ${ARTIFACT_DIR}/
+
+COPY . ${SOURCE_DIR}/
+
+ENTRYPOINT ["/docker-build.sh"]
diff --git a/deployment/ubuntu-package-arm64/clean.sh b/deployment/ubuntu-package-arm64/clean.sh
new file mode 100755
index 000000000..c92c7fdec
--- /dev/null
+++ b/deployment/ubuntu-package-arm64/clean.sh
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+
+source ../common.build.sh
+
+keep_artifacts="${1}"
+
+WORKDIR="$( pwd )"
+
+package_temporary_dir="${WORKDIR}/pkg-dist-tmp"
+output_dir="${WORKDIR}/pkg-dist"
+current_user="$( whoami )"
+image_name="jellyfin-ubuntu-build"
+
+rm -rf "${package_temporary_dir}" &>/dev/null \
+ || sudo rm -rf "${package_temporary_dir}" &>/dev/null
+
+rm -rf "${output_dir}" &>/dev/null \
+ || sudo rm -rf "${output_dir}" &>/dev/null
+
+if [[ ${keep_artifacts} == 'n' ]]; then
+ docker_sudo=""
+ if [[ ! -z $(id -Gn | grep -q 'docker') ]] \
+ && [[ ! ${EUID:-1000} -eq 0 ]] \
+ && [[ ! ${USER} == "root" ]] \
+ && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then
+ docker_sudo=sudo
+ fi
+ ${docker_sudo} docker image rm ${image_name} --force
+fi
diff --git a/deployment/ubuntu-package-arm64/dependencies.txt b/deployment/ubuntu-package-arm64/dependencies.txt
new file mode 100644
index 000000000..bdb967096
--- /dev/null
+++ b/deployment/ubuntu-package-arm64/dependencies.txt
@@ -0,0 +1 @@
+docker
diff --git a/deployment/ubuntu-package-arm64/docker-build.sh b/deployment/ubuntu-package-arm64/docker-build.sh
new file mode 100755
index 000000000..308f3df15
--- /dev/null
+++ b/deployment/ubuntu-package-arm64/docker-build.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Builds the DEB inside the Docker container
+
+set -o errexit
+set -o xtrace
+
+# Move to source directory
+pushd ${SOURCE_DIR}
+
+# Remove build-dep for dotnet-sdk-2.2, since it's not a package in this image
+sed -i '/dotnet-sdk-2.2,/d' debian/control
+
+# Build DEB
+export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH}
+dpkg-buildpackage -us -uc -aarm64
+
+# Move the artifacts out
+mkdir -p ${ARTIFACT_DIR}/deb
+mv /jellyfin_* ${ARTIFACT_DIR}/deb/
diff --git a/deployment/ubuntu-package-arm64/package.sh b/deployment/ubuntu-package-arm64/package.sh
new file mode 100755
index 000000000..54fc38750
--- /dev/null
+++ b/deployment/ubuntu-package-arm64/package.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+
+source ../common.build.sh
+
+ARCH="$( arch )"
+WORKDIR="$( pwd )"
+
+package_temporary_dir="${WORKDIR}/pkg-dist-tmp"
+output_dir="${WORKDIR}/pkg-dist"
+current_user="$( whoami )"
+image_name="jellyfin-ubuntu_arm64-build"
+
+# Determine if sudo should be used for Docker
+if [[ ! -z $(id -Gn | grep -q 'docker') ]] \
+ && [[ ! ${EUID:-1000} -eq 0 ]] \
+ && [[ ! ${USER} == "root" ]] \
+ && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then
+ docker_sudo="sudo"
+else
+ docker_sudo=""
+fi
+
+# Determine which Dockerfile to use
+case $ARCH in
+ 'x86_64')
+ DOCKERFILE="Dockerfile.amd64"
+ ;;
+ 'armv7l')
+ DOCKERFILE="Dockerfile.arm64"
+ ;;
+esac
+
+# Set up the build environment Docker image
+${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE}
+# Build the DEBs and copy out to ${package_temporary_dir}
+${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}"
+# Correct ownership on the DEBs (as current user, then as root if that fails)
+chown -R "${current_user}" "${package_temporary_dir}" &>/dev/null \
+ || sudo chown -R "${current_user}" "${package_temporary_dir}" &>/dev/null
+# Move the DEBs to the output directory
+mkdir -p "${output_dir}"
+mv "${package_temporary_dir}"/deb/* "${output_dir}"
diff --git a/deployment/ubuntu-package-arm64/pkg-src b/deployment/ubuntu-package-arm64/pkg-src
new file mode 120000
index 000000000..4c695fea1
--- /dev/null
+++ b/deployment/ubuntu-package-arm64/pkg-src
@@ -0,0 +1 @@
+../debian-package-x64/pkg-src \ No newline at end of file