Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1bf3a26a61 | |||
| c5760b3a40 | |||
| 4de8bf3295 | |||
| 87c8f19f19 | |||
| 0ef52c739e | |||
| 0a9a6b949c | |||
| c22068d6b1 | |||
| bbc1a86b57 | |||
| cd83d80f2b | |||
| 12721eb7dd | |||
| b8a09339cd | |||
| 3634d367c1 | |||
| c1daea0ec7 | |||
| e8196fed7c | |||
| 477702fbb9 | |||
| 2216a271bb | |||
| 91cd7d2f6b | |||
| 4e681d25d9 | |||
| 06cc2891de | |||
| 682432f55a | |||
| 7c4cb5ec58 | |||
| 1df73fdeba | |||
| 21ba8a0593 | |||
| 08ed52eb72 | |||
| 99700e1b95 | |||
| 4e0be95368 | |||
| c8a59c8343 | |||
| 2b2a2ed708 | |||
| a827a2fbcc | |||
| f97f6b8061 | |||
| 844ea9d77e | |||
| 71479286e9 | |||
| 28c2ac528d | |||
| 696a36b4a5 | |||
| 5fb4922c6f | |||
| 3738f95871 | |||
| 6797bdec04 | |||
| 764c6d5461 | |||
| 6973182ade | |||
| f62af07381 |
+1
-1
@@ -21,7 +21,7 @@ RUN apt-get update \
|
|||||||
COPY --from=ffmpeg / /
|
COPY --from=ffmpeg / /
|
||||||
COPY --from=builder /jellyfin /jellyfin
|
COPY --from=builder /jellyfin /jellyfin
|
||||||
|
|
||||||
ARG JELLYFIN_WEB_VERSION=10.3.0
|
ARG JELLYFIN_WEB_VERSION=10.3.3
|
||||||
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
|
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
|
||||||
&& rm -rf /jellyfin/jellyfin-web \
|
&& rm -rf /jellyfin/jellyfin-web \
|
||||||
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
|
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
|
||||||
|
|||||||
+1
-1
@@ -30,7 +30,7 @@ RUN apt-get update \
|
|||||||
&& chmod 777 /cache /config /media
|
&& chmod 777 /cache /config /media
|
||||||
COPY --from=builder /jellyfin /jellyfin
|
COPY --from=builder /jellyfin /jellyfin
|
||||||
|
|
||||||
ARG JELLYFIN_WEB_VERSION=10.3.0
|
ARG JELLYFIN_WEB_VERSION=10.3.3
|
||||||
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
|
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
|
||||||
&& rm -rf /jellyfin/jellyfin-web \
|
&& rm -rf /jellyfin/jellyfin-web \
|
||||||
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
|
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
|
||||||
|
|||||||
+1
-1
@@ -31,7 +31,7 @@ RUN apt-get update \
|
|||||||
&& chmod 777 /cache /config /media
|
&& chmod 777 /cache /config /media
|
||||||
COPY --from=builder /jellyfin /jellyfin
|
COPY --from=builder /jellyfin /jellyfin
|
||||||
|
|
||||||
ARG JELLYFIN_WEB_VERSION=10.3.0
|
ARG JELLYFIN_WEB_VERSION=10.3.3
|
||||||
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
|
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
|
||||||
&& rm -rf /jellyfin/jellyfin-web \
|
&& rm -rf /jellyfin/jellyfin-web \
|
||||||
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
|
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
|
||||||
|
|||||||
@@ -920,8 +920,6 @@ namespace Emby.Dlna.Didl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AddImageResElement(item, writer, 160, 160, "jpg", "JPEG_TN");
|
|
||||||
|
|
||||||
if (!_profile.EnableSingleAlbumArtLimit || string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase))
|
if (!_profile.EnableSingleAlbumArtLimit || string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
AddImageResElement(item, writer, 4096, 4096, "jpg", "JPEG_LRG");
|
AddImageResElement(item, writer, 4096, 4096, "jpg", "JPEG_LRG");
|
||||||
@@ -930,6 +928,9 @@ namespace Emby.Dlna.Didl
|
|||||||
AddImageResElement(item, writer, 4096, 4096, "png", "PNG_LRG");
|
AddImageResElement(item, writer, 4096, 4096, "png", "PNG_LRG");
|
||||||
AddImageResElement(item, writer, 160, 160, "png", "PNG_TN");
|
AddImageResElement(item, writer, 160, 160, "png", "PNG_TN");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AddImageResElement(item, writer, 160, 160, "jpg", "JPEG_TN");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddEmbeddedImageAsCover(string name, XmlWriter writer)
|
private void AddEmbeddedImageAsCover(string name, XmlWriter writer)
|
||||||
|
|||||||
@@ -1167,7 +1167,7 @@ namespace Emby.Server.Implementations
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1181,10 +1181,32 @@ namespace Emby.Server.Implementations
|
|||||||
{
|
{
|
||||||
Logger.LogInformation("Loading assemblies");
|
Logger.LogInformation("Loading assemblies");
|
||||||
|
|
||||||
AllConcreteTypes = GetComposablePartAssemblies()
|
AllConcreteTypes = GetTypes(GetComposablePartAssemblies()).ToArray();
|
||||||
.SelectMany(x => x.ExportedTypes)
|
}
|
||||||
.Where(type => type.IsClass && !type.IsAbstract && !type.IsInterface && !type.IsGenericType)
|
|
||||||
.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; }
|
private CertificateInfo CertificateInfo { get; set; }
|
||||||
@@ -1348,8 +1370,19 @@ namespace Emby.Server.Implementations
|
|||||||
{
|
{
|
||||||
foreach (var file in Directory.EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.AllDirectories))
|
foreach (var file in Directory.EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.AllDirectories))
|
||||||
{
|
{
|
||||||
Logger.LogInformation("Loading assembly {Path}", file);
|
Assembly plugAss;
|
||||||
yield return Assembly.LoadFrom(file);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
{
|
{
|
||||||
// If the user password is the sha1 hash of the empty string, remove it
|
// If the user password is the sha1 hash of the empty string, remove it
|
||||||
if (!string.Equals(user.Password, "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal)
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(rangeHeader))
|
if (string.IsNullOrWhiteSpace(rangeHeader))
|
||||||
{
|
{
|
||||||
|
Headers[HeaderNames.ContentLength] = TotalContentLength.ToString(CultureInfo.InvariantCulture);
|
||||||
StatusCode = HttpStatusCode.OK;
|
StatusCode = HttpStatusCode.OK;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -99,10 +100,13 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
RangeStart = requestedRange.Key;
|
RangeStart = requestedRange.Key;
|
||||||
RangeLength = 1 + RangeEnd - RangeStart;
|
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}";
|
var rangeString = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}";
|
||||||
Headers[HeaderNames.ContentRange] = rangeString;
|
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>
|
/// <summary>
|
||||||
|
|||||||
@@ -638,6 +638,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
private static Task Write(IResponse response, string text)
|
private static Task Write(IResponse response, string text)
|
||||||
{
|
{
|
||||||
var bOutput = Encoding.UTF8.GetBytes(text);
|
var bOutput = Encoding.UTF8.GetBytes(text);
|
||||||
|
response.OriginalResponse.ContentLength = bOutput.Length;
|
||||||
return response.OutputStream.WriteAsync(bOutput, 0, bOutput.Length);
|
return response.OutputStream.WriteAsync(bOutput, 0, bOutput.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
content = Array.Empty<byte>();
|
content = Array.Empty<byte>();
|
||||||
}
|
}
|
||||||
|
|
||||||
result = new StreamWriter(content, contentType);
|
result = new StreamWriter(content, contentType, contentLength);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -176,7 +176,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
bytes = Array.Empty<byte>();
|
bytes = Array.Empty<byte>();
|
||||||
}
|
}
|
||||||
|
|
||||||
result = new StreamWriter(bytes, contentType);
|
result = new StreamWriter(bytes, contentType, contentLength);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -335,13 +335,13 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
|
|
||||||
if (isHeadRequest)
|
if (isHeadRequest)
|
||||||
{
|
{
|
||||||
var result = new StreamWriter(Array.Empty<byte>(), contentType);
|
var result = new StreamWriter(Array.Empty<byte>(), contentType, contentLength);
|
||||||
AddResponseHeaders(result, responseHeaders);
|
AddResponseHeaders(result, responseHeaders);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var result = new StreamWriter(content, contentType);
|
var result = new StreamWriter(content, contentType, contentLength);
|
||||||
AddResponseHeaders(result, responseHeaders);
|
AddResponseHeaders(result, responseHeaders);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -581,6 +581,11 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (totalContentLength.HasValue)
|
||||||
|
{
|
||||||
|
responseHeaders["Content-Length"] = totalContentLength.Value.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
if (isHeadRequest)
|
if (isHeadRequest)
|
||||||
{
|
{
|
||||||
using (stream)
|
using (stream)
|
||||||
@@ -624,7 +629,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
|
|
||||||
if (lastModifiedDate.HasValue)
|
if (lastModifiedDate.HasValue)
|
||||||
{
|
{
|
||||||
responseHeaders[HeaderNames.LastModified] = lastModifiedDate.ToString();
|
responseHeaders[HeaderNames.LastModified] = lastModifiedDate.Value.ToString(CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
RangeStart = requestedRange.Key;
|
RangeStart = requestedRange.Key;
|
||||||
RangeLength = 1 + RangeEnd - RangeStart;
|
RangeLength = 1 + RangeEnd - RangeStart;
|
||||||
|
|
||||||
|
Headers[HeaderNames.ContentLength] = RangeLength.ToString(CultureInfo.InvariantCulture);
|
||||||
Headers[HeaderNames.ContentRange] = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}";
|
Headers[HeaderNames.ContentRange] = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}";
|
||||||
|
|
||||||
if (RangeStart > 0 && SourceStream.CanSeek)
|
if (RangeStart > 0 && SourceStream.CanSeek)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
public void FilterResponse(IRequest req, IResponse res, object dto)
|
public void FilterResponse(IRequest req, IResponse res, object dto)
|
||||||
{
|
{
|
||||||
// Try to prevent compatibility view
|
// 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-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
|
||||||
res.AddHeader("Access-Control-Allow-Origin", "*");
|
res.AddHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
|
||||||
@@ -58,6 +58,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
|
|
||||||
if (length > 0)
|
if (length > 0)
|
||||||
{
|
{
|
||||||
|
res.OriginalResponse.ContentLength = length;
|
||||||
res.SendChunked = false;
|
res.SendChunked = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -49,6 +50,13 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
|
|
||||||
SourceStream = source;
|
SourceStream = source;
|
||||||
|
|
||||||
|
Headers["Content-Type"] = contentType;
|
||||||
|
|
||||||
|
if (source.CanSeek)
|
||||||
|
{
|
||||||
|
Headers[HeaderNames.ContentLength] = source.Length.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
Headers[HeaderNames.ContentType] = contentType;
|
Headers[HeaderNames.ContentType] = contentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +65,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="source">The source.</param>
|
/// <param name="source">The source.</param>
|
||||||
/// <param name="contentType">Type of the content.</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))
|
if (string.IsNullOrEmpty(contentType))
|
||||||
{
|
{
|
||||||
@@ -66,6 +74,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
|
|
||||||
SourceBytes = source;
|
SourceBytes = source;
|
||||||
|
|
||||||
|
Headers[HeaderNames.ContentLength] = contentLength.ToString(CultureInfo.InvariantCulture);
|
||||||
Headers[HeaderNames.ContentType] = contentType;
|
Headers[HeaderNames.ContentType] = contentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -550,7 +550,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
{
|
{
|
||||||
return string.IsNullOrEmpty(user.EasyPassword)
|
return string.IsNullOrEmpty(user.EasyPassword)
|
||||||
? null
|
? null
|
||||||
: user.EasyPassword;
|
: (new PasswordHash(user.EasyPassword)).Hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -596,7 +596,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool hasConfiguredPassword = GetAuthenticationProvider(user).HasPassword(user).Result;
|
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) ?
|
bool hasPassword = user.Configuration.EnableLocalPassword && !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ?
|
||||||
hasConfiguredEasyPassword :
|
hasConfiguredEasyPassword :
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ namespace Emby.Server.Implementations.Services
|
|||||||
{
|
{
|
||||||
var contentLength = bytesResponse.Length;
|
var contentLength = bytesResponse.Length;
|
||||||
|
|
||||||
|
if (response != null)
|
||||||
|
{
|
||||||
|
response.OriginalResponse.ContentLength = contentLength;
|
||||||
|
}
|
||||||
|
|
||||||
if (contentLength > 0)
|
if (contentLength > 0)
|
||||||
{
|
{
|
||||||
await responseStream.WriteAsync(bytesResponse, 0, contentLength, cancellationToken).ConfigureAwait(false);
|
await responseStream.WriteAsync(bytesResponse, 0, contentLength, cancellationToken).ConfigureAwait(false);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -20,6 +21,8 @@ namespace Emby.Server.Implementations.Services
|
|||||||
{
|
{
|
||||||
response.StatusCode = (int)HttpStatusCode.NoContent;
|
response.StatusCode = (int)HttpStatusCode.NoContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
response.OriginalResponse.ContentLength = 0;
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,11 +42,6 @@ namespace Emby.Server.Implementations.Services
|
|||||||
|
|
||||||
response.StatusCode = httpResult.Status;
|
response.StatusCode = httpResult.Status;
|
||||||
response.StatusDescription = httpResult.StatusCode.ToString();
|
response.StatusDescription = httpResult.StatusCode.ToString();
|
||||||
//if (string.IsNullOrEmpty(httpResult.ContentType))
|
|
||||||
//{
|
|
||||||
// httpResult.ContentType = defaultContentType;
|
|
||||||
//}
|
|
||||||
//response.ContentType = httpResult.ContentType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var responseOptions = result as IHasHeaders;
|
var responseOptions = result as IHasHeaders;
|
||||||
@@ -53,6 +51,7 @@ namespace Emby.Server.Implementations.Services
|
|||||||
{
|
{
|
||||||
if (string.Equals(responseHeaders.Key, "Content-Length", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(responseHeaders.Key, "Content-Length", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
response.OriginalResponse.ContentLength = long.Parse(responseHeaders.Value, CultureInfo.InvariantCulture);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,52 +71,37 @@ namespace Emby.Server.Implementations.Services
|
|||||||
response.ContentType += "; charset=utf-8";
|
response.ContentType += "; charset=utf-8";
|
||||||
}
|
}
|
||||||
|
|
||||||
var asyncStreamWriter = result as IAsyncStreamWriter;
|
switch (result)
|
||||||
if (asyncStreamWriter != null)
|
|
||||||
{
|
{
|
||||||
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 (bytes.Length > 0)
|
||||||
if (streamWriter != null)
|
{
|
||||||
{
|
return response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
|
||||||
streamWriter.WriteTo(response.OutputStream);
|
}
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
var fileWriter = result as FileWriter;
|
return Task.CompletedTask;
|
||||||
if (fileWriter != null)
|
case string responseText:
|
||||||
{
|
var responseTextAsBytes = Encoding.UTF8.GetBytes(responseText);
|
||||||
return fileWriter.WriteToAsync(response, cancellationToken);
|
response.OriginalResponse.ContentLength = responseTextAsBytes.Length;
|
||||||
}
|
|
||||||
|
|
||||||
var stream = result as Stream;
|
if (responseTextAsBytes.Length > 0)
|
||||||
if (stream != null)
|
{
|
||||||
{
|
return response.OutputStream.WriteAsync(responseTextAsBytes, 0, responseTextAsBytes.Length, cancellationToken);
|
||||||
return CopyStream(stream, response.OutputStream);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var bytes = result as byte[];
|
return Task.CompletedTask;
|
||||||
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 WriteObject(request, result, response);
|
return WriteObject(request, result, response);
|
||||||
@@ -143,14 +127,13 @@ namespace Emby.Server.Implementations.Services
|
|||||||
ms.Position = 0;
|
ms.Position = 0;
|
||||||
|
|
||||||
var contentLength = ms.Length;
|
var contentLength = ms.Length;
|
||||||
|
response.OriginalResponse.ContentLength = contentLength;
|
||||||
|
|
||||||
if (contentLength > 0)
|
if (contentLength > 0)
|
||||||
{
|
{
|
||||||
await ms.CopyToAsync(response.OutputStream).ConfigureAwait(false);
|
await ms.CopyToAsync(response.OutputStream).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//serializer(result, outputStream);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ namespace Emby.Server.Implementations.Services
|
|||||||
string propertyName = pair.Key;
|
string propertyName = pair.Key;
|
||||||
string propertyTextValue = pair.Value;
|
string propertyTextValue = pair.Value;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(propertyTextValue)
|
if (propertyTextValue == null
|
||||||
|| !propertySetterMap.TryGetValue(propertyName, out propertySerializerEntry)
|
|| !propertySetterMap.TryGetValue(propertyName, out propertySerializerEntry)
|
||||||
|| propertySerializerEntry.PropertySetFn == null)
|
|| propertySerializerEntry.PropertySetFn == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Api.Movies;
|
using MediaBrowser.Api.Movies;
|
||||||
@@ -828,7 +830,16 @@ namespace MediaBrowser.Api.Library
|
|||||||
var filename = (Path.GetFileName(path) ?? string.Empty).Replace("\"", string.Empty);
|
var filename = (Path.GetFileName(path) ?? string.Empty).Replace("\"", string.Empty);
|
||||||
if (!string.IsNullOrWhiteSpace(filename))
|
if (!string.IsNullOrWhiteSpace(filename))
|
||||||
{
|
{
|
||||||
headers[HeaderNames.ContentDisposition] = "attachment; filename=\"" + filename + "\"";
|
// Kestrel doesn't support non-ASCII characters in headers
|
||||||
|
if (Regex.IsMatch(filename, "[^[:ascii:]]"))
|
||||||
|
{
|
||||||
|
// Manually encoding non-ASCII characters, following https://tools.ietf.org/html/rfc5987#section-3.2.2
|
||||||
|
headers[HeaderNames.ContentDisposition] = "attachment; filename*=UTF-8''" + WebUtility.UrlEncode(filename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
headers[HeaderNames.ContentDisposition] = "attachment; filename=\"" + filename + "\"";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
|
return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -13,6 +14,7 @@ using MediaBrowser.Controller.Net;
|
|||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.MediaInfo;
|
using MediaBrowser.Model.MediaInfo;
|
||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
|
using MediaBrowser.Model.Services;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Playback.Progressive
|
namespace MediaBrowser.Api.Playback.Progressive
|
||||||
@@ -279,10 +281,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||||||
/// <returns>Task{System.Object}.</returns>
|
/// <returns>Task{System.Object}.</returns>
|
||||||
private async Task<object> GetStaticRemoteStreamResult(StreamState state, Dictionary<string, string> responseHeaders, bool isHeadRequest, CancellationTokenSource cancellationTokenSource)
|
private async Task<object> GetStaticRemoteStreamResult(StreamState state, Dictionary<string, string> responseHeaders, bool isHeadRequest, CancellationTokenSource cancellationTokenSource)
|
||||||
{
|
{
|
||||||
string useragent = null;
|
state.RemoteHttpHeaders.TryGetValue(HeaderNames.UserAgent, out var useragent);
|
||||||
state.RemoteHttpHeaders.TryGetValue("User-Agent", out useragent);
|
|
||||||
|
|
||||||
var trySupportSeek = false;
|
|
||||||
|
|
||||||
var options = new HttpRequestOptions
|
var options = new HttpRequestOptions
|
||||||
{
|
{
|
||||||
@@ -292,29 +291,14 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||||||
CancellationToken = cancellationTokenSource.Token
|
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);
|
var response = await HttpClient.GetResponse(options).ConfigureAwait(false);
|
||||||
|
|
||||||
if (trySupportSeek)
|
responseHeaders[HeaderNames.AcceptRanges] = "none";
|
||||||
|
|
||||||
|
// Seeing cases of -1 here
|
||||||
|
if (response.ContentLength.HasValue && response.ContentLength.Value >= 0)
|
||||||
{
|
{
|
||||||
foreach (var name in new[] { HeaderNames.ContentRange, HeaderNames.AcceptRanges })
|
responseHeaders[HeaderNames.ContentLength] = response.ContentLength.Value.ToString(CultureInfo.InvariantCulture);
|
||||||
{
|
|
||||||
var val = response.Headers[name];
|
|
||||||
if (!string.IsNullOrWhiteSpace(val))
|
|
||||||
{
|
|
||||||
responseHeaders[name] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
responseHeaders[HeaderNames.AcceptRanges] = "none";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isHeadRequest)
|
if (isHeadRequest)
|
||||||
@@ -356,10 +340,31 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||||||
var contentType = state.GetMimeType(outputPath);
|
var contentType = state.GetMimeType(outputPath);
|
||||||
|
|
||||||
// TODO: The isHeadRequest is only here because ServiceStack will add Content-Length=0 to the response
|
// 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
|
// Headers only
|
||||||
if (isHeadRequest)
|
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);
|
var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(outputPath);
|
||||||
@@ -397,5 +402,22 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||||||
transcodingLock.Release();
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,10 +78,25 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The trailer folder name
|
/// The trailer folder name
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string TrailerFolderName = "trailers";
|
public const string TrailerFolderName = "trailers";
|
||||||
public static string ThemeSongsFolderName = "theme-music";
|
public const string ThemeSongsFolderName = "theme-music";
|
||||||
public static string ThemeSongFilename = "theme";
|
public const string ThemeSongFilename = "theme";
|
||||||
public static string ThemeVideosFolderName = "backdrops";
|
public const string ThemeVideosFolderName = "backdrops";
|
||||||
|
public const string ExtrasFolderName = "extras";
|
||||||
|
public const string BehindTheScenesFolderName = "behind the scenes";
|
||||||
|
public const string DeletedScenesFolderName = "deleted scenes";
|
||||||
|
public const string InterviewFolderName = "interviews";
|
||||||
|
public const string SceneFolderName = "scenes";
|
||||||
|
public const string SampleFolderName = "samples";
|
||||||
|
|
||||||
|
public static readonly string[] AllExtrasTypesFolderNames = {
|
||||||
|
ExtrasFolderName,
|
||||||
|
BehindTheScenesFolderName,
|
||||||
|
DeletedScenesFolderName,
|
||||||
|
InterviewFolderName,
|
||||||
|
SceneFolderName,
|
||||||
|
SampleFolderName
|
||||||
|
};
|
||||||
|
|
||||||
[IgnoreDataMember]
|
[IgnoreDataMember]
|
||||||
public Guid[] ThemeSongIds { get; set; }
|
public Guid[] ThemeSongIds { get; set; }
|
||||||
@@ -1276,16 +1291,15 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
.Select(item =>
|
.Select(item =>
|
||||||
{
|
{
|
||||||
// Try to retrieve it from the db. If we don't find it, use the resolved version
|
// Try to retrieve it from the db. If we don't find it, use the resolved version
|
||||||
var dbItem = LibraryManager.GetItemById(item.Id) as Video;
|
|
||||||
|
|
||||||
if (dbItem != null)
|
if (LibraryManager.GetItemById(item.Id) is Video dbItem)
|
||||||
{
|
{
|
||||||
item = dbItem;
|
item = dbItem;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// item is new
|
// item is new
|
||||||
item.ExtraType = MediaBrowser.Model.Entities.ExtraType.ThemeVideo;
|
item.ExtraType = Model.Entities.ExtraType.ThemeVideo;
|
||||||
}
|
}
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
@@ -1296,33 +1310,38 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
protected virtual BaseItem[] LoadExtras(List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
|
protected virtual BaseItem[] LoadExtras(List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
|
||||||
{
|
{
|
||||||
var files = fileSystemChildren.Where(i => i.IsDirectory)
|
var extras = new List<Video>();
|
||||||
.SelectMany(i => FileSystem.GetFiles(i.FullName));
|
|
||||||
|
|
||||||
return LibraryManager.ResolvePaths(files, directoryService, null, new LibraryOptions())
|
var folders = fileSystemChildren.Where(i => i.IsDirectory).ToArray();
|
||||||
.OfType<Video>()
|
foreach (var extraFolderName in AllExtrasTypesFolderNames)
|
||||||
.Select(item =>
|
{
|
||||||
{
|
var files = folders
|
||||||
// Try to retrieve it from the db. If we don't find it, use the resolved version
|
.Where(i => string.Equals(i.Name, extraFolderName, StringComparison.OrdinalIgnoreCase))
|
||||||
var dbItem = LibraryManager.GetItemById(item.Id) as Video;
|
.SelectMany(i => FileSystem.GetFiles(i.FullName));
|
||||||
|
|
||||||
if (dbItem != null)
|
extras.AddRange(LibraryManager.ResolvePaths(files, directoryService, null, new LibraryOptions())
|
||||||
|
.OfType<Video>()
|
||||||
|
.Select(item =>
|
||||||
{
|
{
|
||||||
item = dbItem;
|
// Try to retrieve it from the db. If we don't find it, use the resolved version
|
||||||
}
|
if (LibraryManager.GetItemById(item.Id) is Video dbItem)
|
||||||
else
|
{
|
||||||
{
|
item = dbItem;
|
||||||
// item is new
|
}
|
||||||
item.ExtraType = MediaBrowser.Model.Entities.ExtraType.Clip;
|
|
||||||
}
|
|
||||||
|
|
||||||
return item;
|
// Use some hackery to get the extra type based on foldername
|
||||||
|
Enum.TryParse(extraFolderName.Replace(" ", ""), true, out ExtraType extraType);
|
||||||
|
item.ExtraType = extraType;
|
||||||
|
|
||||||
// Sort them so that the list can be easily compared for changes
|
return item;
|
||||||
}).OrderBy(i => i.Path).ToArray();
|
|
||||||
|
// Sort them so that the list can be easily compared for changes
|
||||||
|
}).OrderBy(i => i.Path));
|
||||||
|
}
|
||||||
|
|
||||||
|
return extras.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Task RefreshMetadata(CancellationToken cancellationToken)
|
public Task RefreshMetadata(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem)), cancellationToken);
|
return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem)), cancellationToken);
|
||||||
@@ -1481,7 +1500,13 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
private async Task<bool> RefreshExtras(BaseItem item, MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
|
private async Task<bool> RefreshExtras(BaseItem item, MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var newExtras = LoadExtras(fileSystemChildren, options.DirectoryService).Concat(LoadThemeVideos(fileSystemChildren, options.DirectoryService)).Concat(LoadThemeSongs(fileSystemChildren, options.DirectoryService));
|
var extras = LoadExtras(fileSystemChildren, options.DirectoryService);
|
||||||
|
var themeVideos = LoadThemeVideos(fileSystemChildren, options.DirectoryService);
|
||||||
|
var themeSongs = LoadThemeSongs(fileSystemChildren, options.DirectoryService);
|
||||||
|
var newExtras = new BaseItem[extras.Length + themeVideos.Length + themeSongs.Length];
|
||||||
|
extras.CopyTo(newExtras, 0);
|
||||||
|
themeVideos.CopyTo(newExtras, extras.Length);
|
||||||
|
themeSongs.CopyTo(newExtras, extras.Length + themeVideos.Length);
|
||||||
|
|
||||||
var newExtraIds = newExtras.Select(i => i.Id).ToArray();
|
var newExtraIds = newExtras.Select(i => i.Id).ToArray();
|
||||||
|
|
||||||
@@ -1493,7 +1518,15 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
var tasks = newExtras.Select(i =>
|
var tasks = newExtras.Select(i =>
|
||||||
{
|
{
|
||||||
return RefreshMetadataForOwnedItem(i, true, new MetadataRefreshOptions(options), cancellationToken);
|
var subOptions = new MetadataRefreshOptions(options);
|
||||||
|
if (i.OwnerId != ownerId || i.ParentId != Guid.Empty)
|
||||||
|
{
|
||||||
|
i.OwnerId = ownerId;
|
||||||
|
i.ParentId = Guid.Empty;
|
||||||
|
subOptions.ForceSave = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RefreshMetadataForOwnedItem(i, true, subOptions, cancellationToken);
|
||||||
});
|
});
|
||||||
|
|
||||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
private readonly int DefaultImageExtractionTimeoutMs;
|
private readonly int DefaultImageExtractionTimeoutMs;
|
||||||
private readonly string StartupOptionFFmpegPath;
|
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 List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
|
||||||
private readonly ILocalizationManager _localization;
|
private readonly ILocalizationManager _localization;
|
||||||
|
|
||||||
@@ -582,19 +582,27 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
{
|
{
|
||||||
bool ranToCompletion;
|
bool ranToCompletion;
|
||||||
|
|
||||||
StartProcess(processWrapper);
|
await _thumbnailResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
try
|
||||||
var timeoutMs = ConfigurationManager.Configuration.ImageExtractionTimeoutMs;
|
|
||||||
if (timeoutMs <= 0)
|
|
||||||
{
|
{
|
||||||
timeoutMs = DefaultImageExtractionTimeoutMs;
|
StartProcess(processWrapper);
|
||||||
|
|
||||||
|
var timeoutMs = ConfigurationManager.Configuration.ImageExtractionTimeoutMs;
|
||||||
|
if (timeoutMs <= 0)
|
||||||
|
{
|
||||||
|
timeoutMs = DefaultImageExtractionTimeoutMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
ranToCompletion = await process.WaitForExitAsync(timeoutMs).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (!ranToCompletion)
|
||||||
|
{
|
||||||
|
StopProcess(processWrapper, 1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
ranToCompletion = await process.WaitForExitAsync(timeoutMs).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (!ranToCompletion)
|
|
||||||
{
|
{
|
||||||
StopProcess(processWrapper, 1000);
|
_thumbnailResourcePool.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;
|
var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;
|
||||||
@@ -625,7 +633,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
return time.ToString(@"hh\:mm\:ss\.fff", UsCulture);
|
return time.ToString(@"hh\:mm\:ss\.fff", UsCulture);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ExtractVideoImagesOnInterval(string[] inputFiles,
|
public async Task ExtractVideoImagesOnInterval(
|
||||||
|
string[] inputFiles,
|
||||||
string container,
|
string container,
|
||||||
MediaStream videoStream,
|
MediaStream videoStream,
|
||||||
MediaProtocol protocol,
|
MediaProtocol protocol,
|
||||||
@@ -636,8 +645,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
int? maxWidth,
|
int? maxWidth,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var resourcePool = _thumbnailResourcePool;
|
|
||||||
|
|
||||||
var inputArgument = GetInputArgument(inputFiles, protocol);
|
var inputArgument = GetInputArgument(inputFiles, protocol);
|
||||||
|
|
||||||
var vf = "fps=fps=1/" + interval.TotalSeconds.ToString(UsCulture);
|
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);
|
_logger.LogInformation(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
|
||||||
|
|
||||||
await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
|
await _thumbnailResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
bool ranToCompletion = false;
|
bool ranToCompletion = false;
|
||||||
|
|
||||||
@@ -742,7 +749,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
resourcePool.Release();
|
_thumbnailResourcePool.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;
|
var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;
|
||||||
|
|||||||
@@ -336,7 +336,7 @@ namespace MediaBrowser.Providers.Music
|
|||||||
}
|
}
|
||||||
using (var subReader = reader.ReadSubtree())
|
using (var subReader = reader.ReadSubtree())
|
||||||
{
|
{
|
||||||
return ParseReleaseList(subReader);
|
return ParseReleaseList(subReader).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ namespace MediaBrowser.Providers.Music
|
|||||||
}
|
}
|
||||||
using (var subReader = reader.ReadSubtree())
|
using (var subReader = reader.ReadSubtree())
|
||||||
{
|
{
|
||||||
return ParseArtistList(subReader);
|
return ParseArtistList(subReader).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class RemoteEpisodeProvider
|
/// Class RemoteEpisodeProvider
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class TvdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IHasOrder
|
public class TvdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IHasOrder
|
||||||
{
|
{
|
||||||
private readonly IHttpClient _httpClient;
|
private readonly IHttpClient _httpClient;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|||||||
Submodule MediaBrowser.WebDashboard/jellyfin-web updated: 874b51234e...b0f7a9b67c
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
[assembly: AssemblyVersion("10.3.0")]
|
[assembly: AssemblyVersion("10.3.3")]
|
||||||
[assembly: AssemblyFileVersion("10.3.0")]
|
[assembly: AssemblyFileVersion("10.3.3")]
|
||||||
|
|||||||
+3
-1
@@ -1,12 +1,14 @@
|
|||||||
---
|
---
|
||||||
# We just wrap `build` so this is really it
|
# We just wrap `build` so this is really it
|
||||||
name: "jellyfin"
|
name: "jellyfin"
|
||||||
version: "10.3.0"
|
version: "10.3.3"
|
||||||
packages:
|
packages:
|
||||||
- debian-package-x64
|
- debian-package-x64
|
||||||
- debian-package-armhf
|
- debian-package-armhf
|
||||||
|
- debian-package-arm64
|
||||||
- ubuntu-package-x64
|
- ubuntu-package-x64
|
||||||
- ubuntu-package-armhf
|
- ubuntu-package-armhf
|
||||||
|
- ubuntu-package-arm64
|
||||||
- fedora-package-x64
|
- fedora-package-x64
|
||||||
- centos-package-x64
|
- centos-package-x64
|
||||||
- linux-x64
|
- linux-x64
|
||||||
|
|||||||
@@ -18,3 +18,4 @@ rpmbuild -bb SPECS/jellyfin.spec --define "_sourcedir ${SOURCE_DIR}/SOURCES/pkg-
|
|||||||
# Move the artifacts out
|
# Move the artifacts out
|
||||||
mkdir -p ${ARTIFACT_DIR}/rpm
|
mkdir -p ${ARTIFACT_DIR}/rpm
|
||||||
mv /root/rpmbuild/RPMS/x86_64/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/rpm/
|
mv /root/rpmbuild/RPMS/x86_64/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/rpm/
|
||||||
|
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
|
||||||
|
|||||||
@@ -72,9 +72,6 @@ fi
|
|||||||
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
|
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
|
||||||
# Build the RPMs and copy out to ${package_temporary_dir}
|
# Build the RPMs and copy out to ${package_temporary_dir}
|
||||||
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}"
|
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}"
|
||||||
# Correct ownership on the RPMs (as current user, then as root if that fails)
|
|
||||||
chown -R "${current_user}" "${package_temporary_dir}" \
|
|
||||||
|| sudo chown -R "${current_user}" "${package_temporary_dir}"
|
|
||||||
# Move the RPMs to the output directory
|
# Move the RPMs to the output directory
|
||||||
mkdir -p "${output_dir}"
|
mkdir -p "${output_dir}"
|
||||||
mv "${package_temporary_dir}"/rpm/* "${output_dir}"
|
mv "${package_temporary_dir}"/rpm/* "${output_dir}"
|
||||||
|
|||||||
@@ -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"]
|
||||||
@@ -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"]
|
||||||
Executable
+29
@@ -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
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
docker
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
#!/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/
|
||||||
|
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
|
||||||
Executable
+41
@@ -0,0 +1,41 @@
|
|||||||
|
#!/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
|
||||||
|
|
||||||
|
# Prepare temporary package dir
|
||||||
|
mkdir -p "${package_temporary_dir}"
|
||||||
|
# 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}"
|
||||||
|
# Move the DEBs to the output directory
|
||||||
|
mkdir -p "${output_dir}"
|
||||||
|
mv "${package_temporary_dir}"/deb/* "${output_dir}"
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../debian-package-x64/pkg-src
|
||||||
@@ -18,3 +18,4 @@ dpkg-buildpackage -us -uc -aarmhf
|
|||||||
# Move the artifacts out
|
# Move the artifacts out
|
||||||
mkdir -p ${ARTIFACT_DIR}/deb
|
mkdir -p ${ARTIFACT_DIR}/deb
|
||||||
mv /jellyfin_* ${ARTIFACT_DIR}/deb/
|
mv /jellyfin_* ${ARTIFACT_DIR}/deb/
|
||||||
|
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
|
||||||
|
|||||||
@@ -30,13 +30,12 @@ case $ARCH in
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
# Prepare temporary package dir
|
||||||
|
mkdir -p "${package_temporary_dir}"
|
||||||
# Set up the build environment Docker image
|
# Set up the build environment Docker image
|
||||||
${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE}
|
${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE}
|
||||||
# Build the DEBs and copy out to ${package_temporary_dir}
|
# Build the DEBs and copy out to ${package_temporary_dir}
|
||||||
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}"
|
${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
|
# Move the DEBs to the output directory
|
||||||
mkdir -p "${output_dir}"
|
mkdir -p "${output_dir}"
|
||||||
mv "${package_temporary_dir}"/deb/* "${output_dir}"
|
mv "${package_temporary_dir}"/deb/* "${output_dir}"
|
||||||
|
|||||||
@@ -17,3 +17,4 @@ dpkg-buildpackage -us -uc
|
|||||||
# Move the artifacts out
|
# Move the artifacts out
|
||||||
mkdir -p ${ARTIFACT_DIR}/deb
|
mkdir -p ${ARTIFACT_DIR}/deb
|
||||||
mv /jellyfin_* ${ARTIFACT_DIR}/deb/
|
mv /jellyfin_* ${ARTIFACT_DIR}/deb/
|
||||||
|
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
|
||||||
|
|||||||
@@ -19,13 +19,12 @@ else
|
|||||||
docker_sudo=""
|
docker_sudo=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Prepare temporary package dir
|
||||||
|
mkdir -p "${package_temporary_dir}"
|
||||||
# Set up the build environment Docker image
|
# Set up the build environment Docker image
|
||||||
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
|
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
|
||||||
# Build the DEBs and copy out to ${package_temporary_dir}
|
# Build the DEBs and copy out to ${package_temporary_dir}
|
||||||
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}"
|
${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
|
# Move the DEBs to the output directory
|
||||||
mkdir -p "${output_dir}"
|
mkdir -p "${output_dir}"
|
||||||
mv "${package_temporary_dir}"/deb/* "${output_dir}"
|
mv "${package_temporary_dir}"/deb/* "${output_dir}"
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
jellyfin (10.3.3-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* New upstream version 10.3.3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.3
|
||||||
|
|
||||||
|
-- Jellyfin Packaging Team <packaging@jellyfin.org> Fri, 17 May 2019 23:12:08 -0400
|
||||||
|
|
||||||
|
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
|
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
|
* New upstream version 10.3.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.0
|
||||||
|
|||||||
@@ -23,6 +23,6 @@ Depends: at,
|
|||||||
jellyfin-ffmpeg,
|
jellyfin-ffmpeg,
|
||||||
libfontconfig1,
|
libfontconfig1,
|
||||||
libfreetype6,
|
libfreetype6,
|
||||||
libssl1.0.0 | libssl1.0.2
|
libssl1.0.0 | libssl1.0.2 | libssl1.1
|
||||||
Description: Jellyfin is a home media server.
|
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.
|
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.
|
||||||
|
|||||||
@@ -6,18 +6,25 @@ SHELL := /bin/bash
|
|||||||
HOST_ARCH := $(shell arch)
|
HOST_ARCH := $(shell arch)
|
||||||
BUILD_ARCH := ${DEB_HOST_MULTIARCH}
|
BUILD_ARCH := ${DEB_HOST_MULTIARCH}
|
||||||
ifeq ($(HOST_ARCH),x86_64)
|
ifeq ($(HOST_ARCH),x86_64)
|
||||||
|
# Building AMD64
|
||||||
|
DOTNETRUNTIME := debian-x64
|
||||||
ifeq ($(BUILD_ARCH),arm-linux-gnueabihf)
|
ifeq ($(BUILD_ARCH),arm-linux-gnueabihf)
|
||||||
# Cross-building ARM on AMD64
|
# Cross-building ARM on AMD64
|
||||||
DOTNETRUNTIME := debian-arm
|
DOTNETRUNTIME := debian-arm
|
||||||
else
|
endif
|
||||||
# Building AMD64
|
ifeq ($(BUILD_ARCH),aarch64-linux-gnu)
|
||||||
DOTNETRUNTIME := debian-x64
|
# Cross-building ARM on AMD64
|
||||||
|
DOTNETRUNTIME := debian-arm64
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
ifeq ($(HOST_ARCH),armv7l)
|
ifeq ($(HOST_ARCH),armv7l)
|
||||||
# Building ARM
|
# Building ARM
|
||||||
DOTNETRUNTIME := debian-arm
|
DOTNETRUNTIME := debian-arm
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(HOST_ARCH),arm64)
|
||||||
|
# Building ARM
|
||||||
|
DOTNETRUNTIME := debian-arm64
|
||||||
|
endif
|
||||||
|
|
||||||
export DH_VERBOSE=1
|
export DH_VERBOSE=1
|
||||||
export DOTNET_CLI_TELEMETRY_OPTOUT=1
|
export DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||||
|
|||||||
@@ -18,3 +18,4 @@ rpmbuild -bb SPECS/jellyfin.spec --define "_sourcedir ${SOURCE_DIR}/SOURCES/pkg-
|
|||||||
# Move the artifacts out
|
# Move the artifacts out
|
||||||
mkdir -p ${ARTIFACT_DIR}/rpm
|
mkdir -p ${ARTIFACT_DIR}/rpm
|
||||||
mv /root/rpmbuild/RPMS/x86_64/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/rpm/
|
mv /root/rpmbuild/RPMS/x86_64/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/rpm/
|
||||||
|
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
|
||||||
|
|||||||
@@ -23,13 +23,12 @@ fi
|
|||||||
|
|
||||||
./create_tarball.sh
|
./create_tarball.sh
|
||||||
|
|
||||||
|
# Prepare temporary package dir
|
||||||
|
mkdir -p "${package_temporary_dir}"
|
||||||
# Set up the build environment Docker image
|
# Set up the build environment Docker image
|
||||||
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
|
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
|
||||||
# Build the RPMs and copy out to ${package_temporary_dir}
|
# Build the RPMs and copy out to ${package_temporary_dir}
|
||||||
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}"
|
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}"
|
||||||
# Correct ownership on the RPMs (as current user, then as root if that fails)
|
|
||||||
chown -R "${current_user}" "${package_temporary_dir}" \
|
|
||||||
|| sudo chown -R "${current_user}" "${package_temporary_dir}"
|
|
||||||
# Move the RPMs to the output directory
|
# Move the RPMs to the output directory
|
||||||
mkdir -p "${output_dir}"
|
mkdir -p "${output_dir}"
|
||||||
mv "${package_temporary_dir}"/rpm/* "${output_dir}"
|
mv "${package_temporary_dir}"/rpm/* "${output_dir}"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
%endif
|
%endif
|
||||||
|
|
||||||
Name: jellyfin
|
Name: jellyfin
|
||||||
Version: 10.3.0
|
Version: 10.3.3
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: The Free Software Media Browser
|
Summary: The Free Software Media Browser
|
||||||
License: GPLv2
|
License: GPLv2
|
||||||
@@ -140,6 +140,12 @@ fi
|
|||||||
%systemd_postun_with_restart jellyfin.service
|
%systemd_postun_with_restart jellyfin.service
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Fri May 17 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||||
|
- New upstream version 10.3.3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.3
|
||||||
|
* 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>
|
* 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
|
- 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>
|
* Thu Feb 28 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||||
|
|||||||
@@ -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"]
|
||||||
@@ -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"]
|
||||||
Executable
+29
@@ -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
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
docker
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
#!/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/
|
||||||
|
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
|
||||||
Executable
+41
@@ -0,0 +1,41 @@
|
|||||||
|
#!/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
|
||||||
|
|
||||||
|
# Prepare temporary package dir
|
||||||
|
mkdir -p "${package_temporary_dir}"
|
||||||
|
# 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}"
|
||||||
|
# Move the DEBs to the output directory
|
||||||
|
mkdir -p "${output_dir}"
|
||||||
|
mv "${package_temporary_dir}"/deb/* "${output_dir}"
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../debian-package-x64/pkg-src
|
||||||
@@ -18,3 +18,4 @@ dpkg-buildpackage -us -uc -aarmhf
|
|||||||
# Move the artifacts out
|
# Move the artifacts out
|
||||||
mkdir -p ${ARTIFACT_DIR}/deb
|
mkdir -p ${ARTIFACT_DIR}/deb
|
||||||
mv /jellyfin_* ${ARTIFACT_DIR}/deb/
|
mv /jellyfin_* ${ARTIFACT_DIR}/deb/
|
||||||
|
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
|
||||||
|
|||||||
@@ -30,13 +30,12 @@ case $ARCH in
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
# Prepare temporary package dir
|
||||||
|
mkdir -p "${package_temporary_dir}"
|
||||||
# Set up the build environment Docker image
|
# Set up the build environment Docker image
|
||||||
${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE}
|
${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE}
|
||||||
# Build the DEBs and copy out to ${package_temporary_dir}
|
# Build the DEBs and copy out to ${package_temporary_dir}
|
||||||
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}"
|
${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
|
# Move the DEBs to the output directory
|
||||||
mkdir -p "${output_dir}"
|
mkdir -p "${output_dir}"
|
||||||
mv "${package_temporary_dir}"/deb/* "${output_dir}"
|
mv "${package_temporary_dir}"/deb/* "${output_dir}"
|
||||||
|
|||||||
@@ -17,3 +17,4 @@ dpkg-buildpackage -us -uc
|
|||||||
# Move the artifacts out
|
# Move the artifacts out
|
||||||
mkdir -p ${ARTIFACT_DIR}/deb
|
mkdir -p ${ARTIFACT_DIR}/deb
|
||||||
mv /jellyfin_* ${ARTIFACT_DIR}/deb/
|
mv /jellyfin_* ${ARTIFACT_DIR}/deb/
|
||||||
|
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
|
||||||
|
|||||||
@@ -19,13 +19,12 @@ else
|
|||||||
docker_sudo=""
|
docker_sudo=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Prepare temporary package dir
|
||||||
|
mkdir -p "${package_temporary_dir}"
|
||||||
# Set up the build environment Docker image
|
# Set up the build environment Docker image
|
||||||
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
|
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
|
||||||
# Build the DEBs and copy out to ${package_temporary_dir}
|
# Build the DEBs and copy out to ${package_temporary_dir}
|
||||||
${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}"
|
${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
|
# Move the DEBs to the output directory
|
||||||
mkdir -p "${output_dir}"
|
mkdir -p "${output_dir}"
|
||||||
mv "${package_temporary_dir}"/deb/* "${output_dir}"
|
mv "${package_temporary_dir}"/deb/* "${output_dir}"
|
||||||
|
|||||||
Reference in New Issue
Block a user