Compare commits
52 Commits
v10.3.0-rc2
...
v10.3.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 46c37c0ae8 | |||
| 4ad71766fc | |||
| 9d60cc8c66 | |||
| 4a6243096a | |||
| 10cbdc8e8e | |||
| 4886fc467c | |||
| 8e2827cc39 | |||
| 395d2e4917 | |||
| d31f5229da | |||
| d6622818dc | |||
| ba684d6d3a | |||
| 90c04a4640 | |||
| 06a1e1f166 | |||
| 31ad366aa9 | |||
| 10f33b0273 | |||
| eaa1ac8013 | |||
| ca3bb308b3 | |||
| e790f024c2 | |||
| 250e0c75df | |||
| cde7375049 | |||
| c7fedfbca3 | |||
| f520831025 | |||
| 2f0719a883 | |||
| 34ab99caf1 | |||
| b2f94c0e40 |
+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-rc2
|
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 - \
|
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-rc2
|
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 - \
|
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-rc2
|
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 - \
|
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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1425,7 +1458,6 @@ namespace Emby.Server.Implementations
|
|||||||
HasPendingRestart = HasPendingRestart,
|
HasPendingRestart = HasPendingRestart,
|
||||||
IsShuttingDown = IsShuttingDown,
|
IsShuttingDown = IsShuttingDown,
|
||||||
Version = ApplicationVersion,
|
Version = ApplicationVersion,
|
||||||
ProductName = ApplicationProductName,
|
|
||||||
WebSocketPortNumber = HttpPort,
|
WebSocketPortNumber = HttpPort,
|
||||||
CompletedInstallations = InstallationManager.CompletedInstallations.ToArray(),
|
CompletedInstallations = InstallationManager.CompletedInstallations.ToArray(),
|
||||||
Id = SystemId,
|
Id = SystemId,
|
||||||
@@ -1482,6 +1514,7 @@ namespace Emby.Server.Implementations
|
|||||||
return new PublicSystemInfo
|
return new PublicSystemInfo
|
||||||
{
|
{
|
||||||
Version = ApplicationVersion,
|
Version = ApplicationVersion,
|
||||||
|
ProductName = ApplicationProductName,
|
||||||
Id = SystemId,
|
Id = SystemId,
|
||||||
OperatingSystem = OperatingSystem.Id.ToString(),
|
OperatingSystem = OperatingSystem.Id.ToString(),
|
||||||
WanAddress = wanAddress,
|
WanAddress = wanAddress,
|
||||||
|
|||||||
@@ -74,23 +74,14 @@ namespace Emby.Server.Implementations.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateMetadataPath()
|
private void UpdateMetadataPath()
|
||||||
{
|
{
|
||||||
string metadataPath;
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(Configuration.MetadataPath))
|
if (string.IsNullOrWhiteSpace(Configuration.MetadataPath))
|
||||||
{
|
{
|
||||||
metadataPath = GetInternalMetadataPath();
|
((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = Path.Combine(ApplicationPaths.ProgramDataPath, "metadata");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
metadataPath = Path.Combine(Configuration.MetadataPath, "metadata");
|
((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = Configuration.MetadataPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = metadataPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetInternalMetadataPath()
|
|
||||||
{
|
|
||||||
return Path.Combine(ApplicationPaths.ProgramDataPath, "metadata");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -203,6 +203,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
case DirectoryNotFoundException _:
|
case DirectoryNotFoundException _:
|
||||||
case FileNotFoundException _:
|
case FileNotFoundException _:
|
||||||
case ResourceNotFoundException _: return 404;
|
case ResourceNotFoundException _: return 404;
|
||||||
|
case MethodNotAllowedException _: return 405;
|
||||||
case RemoteServiceUnavailableException _: return 502;
|
case RemoteServiceUnavailableException _: return 502;
|
||||||
default: return 500;
|
default: return 500;
|
||||||
}
|
}
|
||||||
@@ -637,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 :
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"Artists": "Umělci",
|
"Artists": "Umělci",
|
||||||
"AuthenticationSucceededWithUserName": "{0} úspěšně ověřen",
|
"AuthenticationSucceededWithUserName": "{0} úspěšně ověřen",
|
||||||
"Books": "Knihy",
|
"Books": "Knihy",
|
||||||
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
|
"CameraImageUploadedFrom": "Z {0} byla nahrána nová fotografie",
|
||||||
"Channels": "Kanály",
|
"Channels": "Kanály",
|
||||||
"ChapterNameValue": "Kapitola {0}",
|
"ChapterNameValue": "Kapitola {0}",
|
||||||
"Collections": "Kolekce",
|
"Collections": "Kolekce",
|
||||||
@@ -16,14 +16,14 @@
|
|||||||
"Folders": "Složky",
|
"Folders": "Složky",
|
||||||
"Genres": "Žánry",
|
"Genres": "Žánry",
|
||||||
"HeaderAlbumArtists": "Umělci alba",
|
"HeaderAlbumArtists": "Umělci alba",
|
||||||
"HeaderCameraUploads": "Camera Uploads",
|
"HeaderCameraUploads": "Nahrané fotografie",
|
||||||
"HeaderContinueWatching": "Pokračovat ve sledování",
|
"HeaderContinueWatching": "Pokračovat ve sledování",
|
||||||
"HeaderFavoriteAlbums": "Oblíbená alba",
|
"HeaderFavoriteAlbums": "Oblíbená alba",
|
||||||
"HeaderFavoriteArtists": "Oblíbení umělci",
|
"HeaderFavoriteArtists": "Oblíbení interpreti",
|
||||||
"HeaderFavoriteEpisodes": "Oblíbené epizody",
|
"HeaderFavoriteEpisodes": "Oblíbené epizody",
|
||||||
"HeaderFavoriteShows": "Oblíbené seriály",
|
"HeaderFavoriteShows": "Oblíbené seriály",
|
||||||
"HeaderFavoriteSongs": "Oblíbené písně",
|
"HeaderFavoriteSongs": "Oblíbená hudba",
|
||||||
"HeaderLiveTV": "Živá TV",
|
"HeaderLiveTV": "Live TV",
|
||||||
"HeaderNextUp": "Nadcházející",
|
"HeaderNextUp": "Nadcházející",
|
||||||
"HeaderRecordingGroups": "Skupiny nahrávek",
|
"HeaderRecordingGroups": "Skupiny nahrávek",
|
||||||
"HomeVideos": "Domáci videa",
|
"HomeVideos": "Domáci videa",
|
||||||
@@ -34,17 +34,17 @@
|
|||||||
"LabelRunningTimeValue": "Délka média: {0}",
|
"LabelRunningTimeValue": "Délka média: {0}",
|
||||||
"Latest": "Nejnovější",
|
"Latest": "Nejnovější",
|
||||||
"MessageApplicationUpdated": "Jellyfin Server byl aktualizován",
|
"MessageApplicationUpdated": "Jellyfin Server byl aktualizován",
|
||||||
"MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
|
"MessageApplicationUpdatedTo": "Jellyfin server byl aktualizován na verzi {0}",
|
||||||
"MessageNamedServerConfigurationUpdatedWithValue": "Konfigurace sekce {0} na serveru byla aktualizována",
|
"MessageNamedServerConfigurationUpdatedWithValue": "Konfigurace sekce {0} na serveru byla aktualizována",
|
||||||
"MessageServerConfigurationUpdated": "Konfigurace serveru aktualizována",
|
"MessageServerConfigurationUpdated": "Konfigurace serveru aktualizována",
|
||||||
"MixedContent": "Smíšený obsah",
|
"MixedContent": "Smíšený obsah",
|
||||||
"Movies": "Filmy",
|
"Movies": "Filmy",
|
||||||
"Music": "Hudba",
|
"Music": "Hudba",
|
||||||
"MusicVideos": "Hudební klipy",
|
"MusicVideos": "Hudební klipy",
|
||||||
"NameInstallFailed": "{0} installation failed",
|
"NameInstallFailed": "Instalace {0} selhala",
|
||||||
"NameSeasonNumber": "Sezóna {0}",
|
"NameSeasonNumber": "Sezóna {0}",
|
||||||
"NameSeasonUnknown": "Neznámá sezóna",
|
"NameSeasonUnknown": "Neznámá sezóna",
|
||||||
"NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
|
"NewVersionIsAvailable": "Nová verze Jellyfin serveru je k dispozici ke stažení.",
|
||||||
"NotificationOptionApplicationUpdateAvailable": "Dostupná aktualizace aplikace",
|
"NotificationOptionApplicationUpdateAvailable": "Dostupná aktualizace aplikace",
|
||||||
"NotificationOptionApplicationUpdateInstalled": "Aktualizace aplikace instalována",
|
"NotificationOptionApplicationUpdateInstalled": "Aktualizace aplikace instalována",
|
||||||
"NotificationOptionAudioPlayback": "Přehrávání audia zahájeno",
|
"NotificationOptionAudioPlayback": "Přehrávání audia zahájeno",
|
||||||
@@ -70,12 +70,12 @@
|
|||||||
"ProviderValue": "Poskytl: {0}",
|
"ProviderValue": "Poskytl: {0}",
|
||||||
"ScheduledTaskFailedWithName": "{0} selhalo",
|
"ScheduledTaskFailedWithName": "{0} selhalo",
|
||||||
"ScheduledTaskStartedWithName": "{0} zahájeno",
|
"ScheduledTaskStartedWithName": "{0} zahájeno",
|
||||||
"ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
|
"ServerNameNeedsToBeRestarted": "{0} vyžaduje restart",
|
||||||
"Shows": "Seriály",
|
"Shows": "Seriály",
|
||||||
"Songs": "Skladby",
|
"Songs": "Skladby",
|
||||||
"StartupEmbyServerIsLoading": "Jellyfin Server je spouštěn. Zkuste to prosím v brzké době znovu.",
|
"StartupEmbyServerIsLoading": "Jellyfin Server je spouštěn. Zkuste to prosím v brzké době znovu.",
|
||||||
"SubtitleDownloadFailureForItem": "Stahování titulků selhalo pro {0}",
|
"SubtitleDownloadFailureForItem": "Stahování titulků selhalo pro {0}",
|
||||||
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
|
"SubtitleDownloadFailureFromForItem": "Stažení titulků pro {1} z {0} selhalo",
|
||||||
"SubtitlesDownloadedForItem": "Staženy titulky pro {0}",
|
"SubtitlesDownloadedForItem": "Staženy titulky pro {0}",
|
||||||
"Sync": "Synchronizace",
|
"Sync": "Synchronizace",
|
||||||
"System": "Systém",
|
"System": "Systém",
|
||||||
@@ -88,10 +88,10 @@
|
|||||||
"UserOfflineFromDevice": "{0} se odpojil od {1}",
|
"UserOfflineFromDevice": "{0} se odpojil od {1}",
|
||||||
"UserOnlineFromDevice": "{0} se připojil z {1}",
|
"UserOnlineFromDevice": "{0} se připojil z {1}",
|
||||||
"UserPasswordChangedWithName": "Provedena změna hesla pro uživatele {0}",
|
"UserPasswordChangedWithName": "Provedena změna hesla pro uživatele {0}",
|
||||||
"UserPolicyUpdatedWithName": "User policy has been updated for {0}",
|
"UserPolicyUpdatedWithName": "Zásady uživatele pro {0} byly aktualizovány",
|
||||||
"UserStartedPlayingItemWithValues": "{0} spustil přehrávání {1}",
|
"UserStartedPlayingItemWithValues": "{0} spustil přehrávání {1}",
|
||||||
"UserStoppedPlayingItemWithValues": "{0} zastavil přehrávání {1}",
|
"UserStoppedPlayingItemWithValues": "{0} zastavil přehrávání {1}",
|
||||||
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
|
"ValueHasBeenAddedToLibrary": "{0} byl přidán do vaší knihovny médií",
|
||||||
"ValueSpecialEpisodeName": "Speciál - {0}",
|
"ValueSpecialEpisodeName": "Speciál - {0}",
|
||||||
"VersionNumber": "Verze {0}"
|
"VersionNumber": "Verze {0}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,8 +61,8 @@
|
|||||||
"NotificationOptionUserLockedOut": "Bruger låst ude",
|
"NotificationOptionUserLockedOut": "Bruger låst ude",
|
||||||
"NotificationOptionVideoPlayback": "Videoafspilning påbegyndt",
|
"NotificationOptionVideoPlayback": "Videoafspilning påbegyndt",
|
||||||
"NotificationOptionVideoPlaybackStopped": "Videoafspilning stoppet",
|
"NotificationOptionVideoPlaybackStopped": "Videoafspilning stoppet",
|
||||||
"Photos": "Fotos",
|
"Photos": "Fotoer",
|
||||||
"Playlists": "Spillelister",
|
"Playlists": "Afspilningslister",
|
||||||
"Plugin": "Plugin",
|
"Plugin": "Plugin",
|
||||||
"PluginInstalledWithName": "{0} blev installeret",
|
"PluginInstalledWithName": "{0} blev installeret",
|
||||||
"PluginUninstalledWithName": "{0} blev afinstalleret",
|
"PluginUninstalledWithName": "{0} blev afinstalleret",
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
"Folders": "Φάκελοι",
|
"Folders": "Φάκελοι",
|
||||||
"Genres": "Είδη",
|
"Genres": "Είδη",
|
||||||
"HeaderAlbumArtists": "Άλμπουμ Καλλιτεχνών",
|
"HeaderAlbumArtists": "Άλμπουμ Καλλιτεχνών",
|
||||||
"HeaderCameraUploads": "Camera Uploads",
|
"HeaderCameraUploads": "Μεταφορτώσεις Κάμερας",
|
||||||
"HeaderContinueWatching": "Συνεχίστε να παρακολουθείτε",
|
"HeaderContinueWatching": "Συνεχίστε να παρακολουθείτε",
|
||||||
"HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ",
|
"HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ",
|
||||||
"HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες",
|
"HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες",
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
"LabelRunningTimeValue": "Διάρκεια: {0}",
|
"LabelRunningTimeValue": "Διάρκεια: {0}",
|
||||||
"Latest": "Πρόσφατα",
|
"Latest": "Πρόσφατα",
|
||||||
"MessageApplicationUpdated": "Ο Jellyfin Server έχει ενημερωθεί",
|
"MessageApplicationUpdated": "Ο Jellyfin Server έχει ενημερωθεί",
|
||||||
"MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
|
"MessageApplicationUpdatedTo": "Ο server Jellyfin αναβαθμίστηκε σε έκδοση {0}",
|
||||||
"MessageNamedServerConfigurationUpdatedWithValue": "Η ενότητα {0} ρύθμισης παραμέτρων του server έχει ενημερωθεί",
|
"MessageNamedServerConfigurationUpdatedWithValue": "Η ενότητα {0} ρύθμισης παραμέτρων του server έχει ενημερωθεί",
|
||||||
"MessageServerConfigurationUpdated": "Η ρύθμιση παραμέτρων του server έχει ενημερωθεί",
|
"MessageServerConfigurationUpdated": "Η ρύθμιση παραμέτρων του server έχει ενημερωθεί",
|
||||||
"MixedContent": "Ανάμεικτο Περιεχόμενο",
|
"MixedContent": "Ανάμεικτο Περιεχόμενο",
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
"NotificationOptionApplicationUpdateInstalled": "Η ενημέρωση εφαρμογής εγκαταστάθηκε",
|
"NotificationOptionApplicationUpdateInstalled": "Η ενημέρωση εφαρμογής εγκαταστάθηκε",
|
||||||
"NotificationOptionAudioPlayback": "Η αναπαραγωγή ήχου ξεκίνησε",
|
"NotificationOptionAudioPlayback": "Η αναπαραγωγή ήχου ξεκίνησε",
|
||||||
"NotificationOptionAudioPlaybackStopped": "Η αναπαραγωγή ήχου σταμάτησε",
|
"NotificationOptionAudioPlaybackStopped": "Η αναπαραγωγή ήχου σταμάτησε",
|
||||||
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
|
"NotificationOptionCameraImageUploaded": "Μεταφορτώθηκε φωτογραφία απο κάμερα",
|
||||||
"NotificationOptionInstallationFailed": "Αποτυχία εγκατάστασης",
|
"NotificationOptionInstallationFailed": "Αποτυχία εγκατάστασης",
|
||||||
"NotificationOptionNewLibraryContent": "Προστέθηκε νέο περιεχόμενο",
|
"NotificationOptionNewLibraryContent": "Προστέθηκε νέο περιεχόμενο",
|
||||||
"NotificationOptionPluginError": "Αποτυχία του plugin",
|
"NotificationOptionPluginError": "Αποτυχία του plugin",
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
"Songs": "Τραγούδια",
|
"Songs": "Τραγούδια",
|
||||||
"StartupEmbyServerIsLoading": "Ο Jellyfin Server φορτώνει. Παρακαλώ δοκιμάστε σε λίγο.",
|
"StartupEmbyServerIsLoading": "Ο Jellyfin Server φορτώνει. Παρακαλώ δοκιμάστε σε λίγο.",
|
||||||
"SubtitleDownloadFailureForItem": "Οι υπότιτλοι απέτυχαν να κατέβουν για {0}",
|
"SubtitleDownloadFailureForItem": "Οι υπότιτλοι απέτυχαν να κατέβουν για {0}",
|
||||||
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
|
"SubtitleDownloadFailureFromForItem": "Αποτυχίες μεταφόρτωσης υποτίτλων από {0} για {1}",
|
||||||
"SubtitlesDownloadedForItem": "Οι υπότιτλοι κατέβηκαν για {0}",
|
"SubtitlesDownloadedForItem": "Οι υπότιτλοι κατέβηκαν για {0}",
|
||||||
"Sync": "Συγχρονισμός",
|
"Sync": "Συγχρονισμός",
|
||||||
"System": "Σύστημα",
|
"System": "Σύστημα",
|
||||||
|
|||||||
@@ -1,97 +1,97 @@
|
|||||||
{
|
{
|
||||||
"Albums": "Albums",
|
"Albums": "Albom",
|
||||||
"AppDeviceValues": "App: {0}, Device: {1}",
|
"AppDeviceValues": "App: {0}, Grät: {1}",
|
||||||
"Application": "Application",
|
"Application": "Aawändig",
|
||||||
"Artists": "Artists",
|
"Artists": "Könstler",
|
||||||
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
|
"AuthenticationSucceededWithUserName": "{0} het sech aagmäudet",
|
||||||
"Books": "Büecher",
|
"Books": "Büecher",
|
||||||
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
|
"CameraImageUploadedFrom": "Es nöis Foti esch ufeglade worde vo {0}",
|
||||||
"Channels": "Channels",
|
"Channels": "Kanäu",
|
||||||
"ChapterNameValue": "Chapter {0}",
|
"ChapterNameValue": "Kapitu {0}",
|
||||||
"Collections": "Collections",
|
"Collections": "Sammlige",
|
||||||
"DeviceOfflineWithName": "{0} has disconnected",
|
"DeviceOfflineWithName": "{0} esch offline gange",
|
||||||
"DeviceOnlineWithName": "{0} is connected",
|
"DeviceOnlineWithName": "{0} esch online cho",
|
||||||
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
|
"FailedLoginAttemptWithUserName": "Fäugschlagne Aamäudeversuech vo {0}",
|
||||||
"Favorites": "Favorites",
|
"Favorites": "Favorite",
|
||||||
"Folders": "Folders",
|
"Folders": "Ordner",
|
||||||
"Genres": "Genres",
|
"Genres": "Genres",
|
||||||
"HeaderAlbumArtists": "Albuminterprete",
|
"HeaderAlbumArtists": "Albom-Könstler",
|
||||||
"HeaderCameraUploads": "Camera Uploads",
|
"HeaderCameraUploads": "Kamera-Uploads",
|
||||||
"HeaderContinueWatching": "Wiiterluege",
|
"HeaderContinueWatching": "Wiiterluege",
|
||||||
"HeaderFavoriteAlbums": "Favorite Albums",
|
"HeaderFavoriteAlbums": "Lieblingsalbe",
|
||||||
"HeaderFavoriteArtists": "Besti Interpret",
|
"HeaderFavoriteArtists": "Lieblings-Interprete",
|
||||||
"HeaderFavoriteEpisodes": "Favorite Episodes",
|
"HeaderFavoriteEpisodes": "Lieblingsepisode",
|
||||||
"HeaderFavoriteShows": "Favorite Shows",
|
"HeaderFavoriteShows": "Lieblingsserie",
|
||||||
"HeaderFavoriteSongs": "Besti Lieder",
|
"HeaderFavoriteSongs": "Lieblingslieder",
|
||||||
"HeaderLiveTV": "Live TV",
|
"HeaderLiveTV": "Live-Färnseh",
|
||||||
"HeaderNextUp": "Next Up",
|
"HeaderNextUp": "Als nächts",
|
||||||
"HeaderRecordingGroups": "Ufnahmegruppe",
|
"HeaderRecordingGroups": "Ufnahmegruppe",
|
||||||
"HomeVideos": "Heimfilmli",
|
"HomeVideos": "Heimfilmli",
|
||||||
"Inherit": "Hinzuefüege",
|
"Inherit": "Hinzuefüege",
|
||||||
"ItemAddedWithName": "{0} was added to the library",
|
"ItemAddedWithName": "{0} esch de Bibliothek dezuegfüegt worde",
|
||||||
"ItemRemovedWithName": "{0} was removed from the library",
|
"ItemRemovedWithName": "{0} esch vo de Bibliothek entfärnt worde",
|
||||||
"LabelIpAddressValue": "Ip address: {0}",
|
"LabelIpAddressValue": "IP-Adrässe: {0}",
|
||||||
"LabelRunningTimeValue": "Running time: {0}",
|
"LabelRunningTimeValue": "Loufziit: {0}",
|
||||||
"Latest": "Letschte",
|
"Latest": "Nöischti",
|
||||||
"MessageApplicationUpdated": "Jellyfin Server has been updated",
|
"MessageApplicationUpdated": "Jellyfin Server esch aktualisiert worde",
|
||||||
"MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
|
"MessageApplicationUpdatedTo": "Jellyfin Server esch of Version {0} aktualisiert worde",
|
||||||
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
|
"MessageNamedServerConfigurationUpdatedWithValue": "De Serveriistöuigsberiich {0} esch aktualisiert worde",
|
||||||
"MessageServerConfigurationUpdated": "Server configuration has been updated",
|
"MessageServerConfigurationUpdated": "Serveriistöuige send aktualisiert worde",
|
||||||
"MixedContent": "Gmischte Inhalt",
|
"MixedContent": "Gmeschti Inhäut",
|
||||||
"Movies": "Movies",
|
"Movies": "Film",
|
||||||
"Music": "Musig",
|
"Music": "Musig",
|
||||||
"MusicVideos": "Musigfilm",
|
"MusicVideos": "Musigvideos",
|
||||||
"NameInstallFailed": "{0} installation failed",
|
"NameInstallFailed": "Installation vo {0} fäugschlage",
|
||||||
"NameSeasonNumber": "Season {0}",
|
"NameSeasonNumber": "Staffle {0}",
|
||||||
"NameSeasonUnknown": "Season Unknown",
|
"NameSeasonUnknown": "Staffle unbekannt",
|
||||||
"NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
|
"NewVersionIsAvailable": "E nöi Version vo Jellyfin Server esch zom Download parat.",
|
||||||
"NotificationOptionApplicationUpdateAvailable": "Application update available",
|
"NotificationOptionApplicationUpdateAvailable": "Aawändigsupdate verfüegbar",
|
||||||
"NotificationOptionApplicationUpdateInstalled": "Application update installed",
|
"NotificationOptionApplicationUpdateInstalled": "Aawändigsupdate installiert",
|
||||||
"NotificationOptionAudioPlayback": "Audio playback started",
|
"NotificationOptionAudioPlayback": "Audiowedergab gstartet",
|
||||||
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
|
"NotificationOptionAudioPlaybackStopped": "Audiwedergab gstoppt",
|
||||||
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
|
"NotificationOptionCameraImageUploaded": "Foti ueglade",
|
||||||
"NotificationOptionInstallationFailed": "Installation failure",
|
"NotificationOptionInstallationFailed": "Installationsfäuer",
|
||||||
"NotificationOptionNewLibraryContent": "New content added",
|
"NotificationOptionNewLibraryContent": "Nöie Inhaut hinzuegfüegt",
|
||||||
"NotificationOptionPluginError": "Plugin failure",
|
"NotificationOptionPluginError": "Plugin-Fäuer",
|
||||||
"NotificationOptionPluginInstalled": "Plugin installed",
|
"NotificationOptionPluginInstalled": "Plugin installiert",
|
||||||
"NotificationOptionPluginUninstalled": "Plugin uninstalled",
|
"NotificationOptionPluginUninstalled": "Plugin deinstalliert",
|
||||||
"NotificationOptionPluginUpdateInstalled": "Plugin update installed",
|
"NotificationOptionPluginUpdateInstalled": "Pluginupdate installiert",
|
||||||
"NotificationOptionServerRestartRequired": "Server restart required",
|
"NotificationOptionServerRestartRequired": "Serverneustart notwändig",
|
||||||
"NotificationOptionTaskFailed": "Scheduled task failure",
|
"NotificationOptionTaskFailed": "Planti Uufgab fäugschlage",
|
||||||
"NotificationOptionUserLockedOut": "User locked out",
|
"NotificationOptionUserLockedOut": "Benotzer usgschlosse",
|
||||||
"NotificationOptionVideoPlayback": "Video playback started",
|
"NotificationOptionVideoPlayback": "Videowedergab gstartet",
|
||||||
"NotificationOptionVideoPlaybackStopped": "Video playback stopped",
|
"NotificationOptionVideoPlaybackStopped": "Videowedergab gstoppt",
|
||||||
"Photos": "Fotis",
|
"Photos": "Fotis",
|
||||||
"Playlists": "Abspielliste",
|
"Playlists": "Wedergabeliste",
|
||||||
"Plugin": "Plugin",
|
"Plugin": "Plugin",
|
||||||
"PluginInstalledWithName": "{0} was installed",
|
"PluginInstalledWithName": "{0} esch installiert worde",
|
||||||
"PluginUninstalledWithName": "{0} was uninstalled",
|
"PluginUninstalledWithName": "{0} esch deinstalliert worde",
|
||||||
"PluginUpdatedWithName": "{0} was updated",
|
"PluginUpdatedWithName": "{0} esch updated worde",
|
||||||
"ProviderValue": "Provider: {0}",
|
"ProviderValue": "Aabieter: {0}",
|
||||||
"ScheduledTaskFailedWithName": "{0} failed",
|
"ScheduledTaskFailedWithName": "{0} esch fäugschlage",
|
||||||
"ScheduledTaskStartedWithName": "{0} started",
|
"ScheduledTaskStartedWithName": "{0} het gstartet",
|
||||||
"ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
|
"ServerNameNeedsToBeRestarted": "{0} mues nöi gstartet wärde",
|
||||||
"Shows": "Shows",
|
"Shows": "Serie",
|
||||||
"Songs": "Songs",
|
"Songs": "Lieder",
|
||||||
"StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.",
|
"StartupEmbyServerIsLoading": "Jellyfin Server ladt. Bitte grad noeinisch probiere.",
|
||||||
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
|
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
|
||||||
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
|
"SubtitleDownloadFailureFromForItem": "Ondertetle vo {0} för {1} hend ned chönne abeglade wärde",
|
||||||
"SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
|
"SubtitlesDownloadedForItem": "Ondertetle abeglade för {0}",
|
||||||
"Sync": "Sync",
|
"Sync": "Synchronisation",
|
||||||
"System": "System",
|
"System": "System",
|
||||||
"TvShows": "TV Shows",
|
"TvShows": "Färnsehserie",
|
||||||
"User": "User",
|
"User": "Benotzer",
|
||||||
"UserCreatedWithName": "User {0} has been created",
|
"UserCreatedWithName": "Benotzer {0} esch erstöut worde",
|
||||||
"UserDeletedWithName": "User {0} has been deleted",
|
"UserDeletedWithName": "Benotzer {0} esch glösche worde",
|
||||||
"UserDownloadingItemWithValues": "{0} is downloading {1}",
|
"UserDownloadingItemWithValues": "{0} ladt {1} abe",
|
||||||
"UserLockedOutWithName": "User {0} has been locked out",
|
"UserLockedOutWithName": "Benotzer {0} esch usgschlosse worde",
|
||||||
"UserOfflineFromDevice": "{0} has disconnected from {1}",
|
"UserOfflineFromDevice": "{0} esch vo {1} trennt worde",
|
||||||
"UserOnlineFromDevice": "{0} is online from {1}",
|
"UserOnlineFromDevice": "{0} esch online vo {1}",
|
||||||
"UserPasswordChangedWithName": "Password has been changed for user {0}",
|
"UserPasswordChangedWithName": "S'Passwort för Benotzer {0} esch gänderet worde",
|
||||||
"UserPolicyUpdatedWithName": "User policy has been updated for {0}",
|
"UserPolicyUpdatedWithName": "Benotzerrechtlinie för {0} esch aktualisiert worde",
|
||||||
"UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
|
"UserStartedPlayingItemWithValues": "{0} hed d'Wedergab vo {1} of {2} gstartet",
|
||||||
"UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
|
"UserStoppedPlayingItemWithValues": "{0} het d'Wedergab vo {1} of {2} gstoppt",
|
||||||
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
|
"ValueHasBeenAddedToLibrary": "{0} esch dinnere Biblithek hinzuegfüegt worde",
|
||||||
"ValueSpecialEpisodeName": "Spezial - {0}",
|
"ValueSpecialEpisodeName": "Extra - {0}",
|
||||||
"VersionNumber": "Version {0}"
|
"VersionNumber": "Version {0}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"Artists": "Oryndaýshylar",
|
"Artists": "Oryndaýshylar",
|
||||||
"AuthenticationSucceededWithUserName": "{0} túpnusqalyq rastalýy sátti aıaqtaldy",
|
"AuthenticationSucceededWithUserName": "{0} túpnusqalyq rastalýy sátti aıaqtaldy",
|
||||||
"Books": "Kitaptar",
|
"Books": "Kitaptar",
|
||||||
"CameraImageUploadedFrom": "{0} kamerasynan jańa sýret júktep alyndy",
|
"CameraImageUploadedFrom": "{0} kamerasynan jańa sýret júktep salyndy",
|
||||||
"Channels": "Arnalar",
|
"Channels": "Arnalar",
|
||||||
"ChapterNameValue": "{0}-sahna",
|
"ChapterNameValue": "{0}-sahna",
|
||||||
"Collections": "Jıyntyqtar",
|
"Collections": "Jıyntyqtar",
|
||||||
@@ -35,8 +35,8 @@
|
|||||||
"Latest": "Eń keıingi",
|
"Latest": "Eń keıingi",
|
||||||
"MessageApplicationUpdated": "Jellyfin Serveri jańartyldy",
|
"MessageApplicationUpdated": "Jellyfin Serveri jańartyldy",
|
||||||
"MessageApplicationUpdatedTo": "Jellyfin Serveri {0} nusqasyna jańartyldy",
|
"MessageApplicationUpdatedTo": "Jellyfin Serveri {0} nusqasyna jańartyldy",
|
||||||
"MessageNamedServerConfigurationUpdatedWithValue": "Server teńsheliminiń {0} bólimi jańartyldy",
|
"MessageNamedServerConfigurationUpdatedWithValue": "Server konfıgýrasýasynyń {0} bólimi jańartyldy",
|
||||||
"MessageServerConfigurationUpdated": "Server teńshelimi jańartyldy",
|
"MessageServerConfigurationUpdated": "Server konfıgýrasıasy jańartyldy",
|
||||||
"MixedContent": "Aralas mazmun",
|
"MixedContent": "Aralas mazmun",
|
||||||
"Movies": "Fılmder",
|
"Movies": "Fılmder",
|
||||||
"Music": "Mýzyka",
|
"Music": "Mýzyka",
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
"NotificationOptionApplicationUpdateInstalled": "Qoldanba jańartýy ornatyldy",
|
"NotificationOptionApplicationUpdateInstalled": "Qoldanba jańartýy ornatyldy",
|
||||||
"NotificationOptionAudioPlayback": "Dybys oınatýy bastaldy",
|
"NotificationOptionAudioPlayback": "Dybys oınatýy bastaldy",
|
||||||
"NotificationOptionAudioPlaybackStopped": "Dybys oınatýy toqtatyldy",
|
"NotificationOptionAudioPlaybackStopped": "Dybys oınatýy toqtatyldy",
|
||||||
"NotificationOptionCameraImageUploaded": "Kameradan fotosýret keri qotarylǵan",
|
"NotificationOptionCameraImageUploaded": "Kameradan fotosýret júktep salynǵan",
|
||||||
"NotificationOptionInstallationFailed": "Ornatý sátsizdigi",
|
"NotificationOptionInstallationFailed": "Ornatý sátsizdigi",
|
||||||
"NotificationOptionNewLibraryContent": "Jańa mazmun ústelgen",
|
"NotificationOptionNewLibraryContent": "Jańa mazmun ústelgen",
|
||||||
"NotificationOptionPluginError": "Plagın sátsizdigi",
|
"NotificationOptionPluginError": "Plagın sátsizdigi",
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"Channels": "Kanali",
|
"Channels": "Kanali",
|
||||||
"ChapterNameValue": "Poglavje {0}",
|
"ChapterNameValue": "Poglavje {0}",
|
||||||
"Collections": "Zbirke",
|
"Collections": "Zbirke",
|
||||||
"DeviceOfflineWithName": "{0} has disconnected",
|
"DeviceOfflineWithName": "{0} je prekinil povezavo",
|
||||||
"DeviceOnlineWithName": "{0} je povezan",
|
"DeviceOnlineWithName": "{0} je povezan",
|
||||||
"FailedLoginAttemptWithUserName": "Neuspešen poskus prijave z {0}",
|
"FailedLoginAttemptWithUserName": "Neuspešen poskus prijave z {0}",
|
||||||
"Favorites": "Priljubljeni",
|
"Favorites": "Priljubljeni",
|
||||||
@@ -33,9 +33,9 @@
|
|||||||
"LabelIpAddressValue": "IP naslov: {0}",
|
"LabelIpAddressValue": "IP naslov: {0}",
|
||||||
"LabelRunningTimeValue": "Čas trajanja: {0}",
|
"LabelRunningTimeValue": "Čas trajanja: {0}",
|
||||||
"Latest": "Najnovejše",
|
"Latest": "Najnovejše",
|
||||||
"MessageApplicationUpdated": "Jellyfin strežnik je bil posodobljen",
|
"MessageApplicationUpdated": "Jellyfin Server je bil posodobljen",
|
||||||
"MessageApplicationUpdatedTo": "Jellyfin strežnik je bil posodobljen na {0}",
|
"MessageApplicationUpdatedTo": "Jellyfin Server je bil posodobljen na {0}",
|
||||||
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
|
"MessageNamedServerConfigurationUpdatedWithValue": "Oddelek nastavitve strežnika {0} je bil posodobljen",
|
||||||
"MessageServerConfigurationUpdated": "Nastavitve strežnika so bile posodobljene",
|
"MessageServerConfigurationUpdated": "Nastavitve strežnika so bile posodobljene",
|
||||||
"MixedContent": "Razne vsebine",
|
"MixedContent": "Razne vsebine",
|
||||||
"Movies": "Filmi",
|
"Movies": "Filmi",
|
||||||
@@ -57,41 +57,41 @@
|
|||||||
"NotificationOptionPluginUninstalled": "Dodatek odstranjen",
|
"NotificationOptionPluginUninstalled": "Dodatek odstranjen",
|
||||||
"NotificationOptionPluginUpdateInstalled": "Posodobitev dodatka nameščena",
|
"NotificationOptionPluginUpdateInstalled": "Posodobitev dodatka nameščena",
|
||||||
"NotificationOptionServerRestartRequired": "Potreben je ponovni zagon strežnika",
|
"NotificationOptionServerRestartRequired": "Potreben je ponovni zagon strežnika",
|
||||||
"NotificationOptionTaskFailed": "Scheduled task failure",
|
"NotificationOptionTaskFailed": "Razporejena naloga neuspešna",
|
||||||
"NotificationOptionUserLockedOut": "User locked out",
|
"NotificationOptionUserLockedOut": "Uporabnik zaklenjen",
|
||||||
"NotificationOptionVideoPlayback": "Video playback started",
|
"NotificationOptionVideoPlayback": "Predvajanje videa se je začelo",
|
||||||
"NotificationOptionVideoPlaybackStopped": "Video playback stopped",
|
"NotificationOptionVideoPlaybackStopped": "Predvajanje videa se je ustavilo",
|
||||||
"Photos": "Photos",
|
"Photos": "Fotografije",
|
||||||
"Playlists": "Playlists",
|
"Playlists": "Seznami predvajanja",
|
||||||
"Plugin": "Plugin",
|
"Plugin": "Plugin",
|
||||||
"PluginInstalledWithName": "{0} was installed",
|
"PluginInstalledWithName": "{0} je bil nameščen",
|
||||||
"PluginUninstalledWithName": "{0} was uninstalled",
|
"PluginUninstalledWithName": "{0} je bil odstranjen",
|
||||||
"PluginUpdatedWithName": "{0} was updated",
|
"PluginUpdatedWithName": "{0} je bil posodobljen",
|
||||||
"ProviderValue": "Provider: {0}",
|
"ProviderValue": "Provider: {0}",
|
||||||
"ScheduledTaskFailedWithName": "{0} failed",
|
"ScheduledTaskFailedWithName": "{0} ni uspelo",
|
||||||
"ScheduledTaskStartedWithName": "{0} started",
|
"ScheduledTaskStartedWithName": "{0} začeto",
|
||||||
"ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
|
"ServerNameNeedsToBeRestarted": "{0} mora biti ponovno zagnan",
|
||||||
"Shows": "Serije",
|
"Shows": "Serije",
|
||||||
"Songs": "Songs",
|
"Songs": "Pesmi",
|
||||||
"StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.",
|
"StartupEmbyServerIsLoading": "Jellyfin Server se nalaga. Poskusi ponovno kasneje.",
|
||||||
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
|
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
|
||||||
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
|
"SubtitleDownloadFailureFromForItem": "Neuspešen prenos podnapisov iz {0} za {1}",
|
||||||
"SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
|
"SubtitlesDownloadedForItem": "Podnapisi preneseni za {0}",
|
||||||
"Sync": "Sync",
|
"Sync": "Sinhroniziraj",
|
||||||
"System": "System",
|
"System": "System",
|
||||||
"TvShows": "TV Shows",
|
"TvShows": "TV serije",
|
||||||
"User": "User",
|
"User": "User",
|
||||||
"UserCreatedWithName": "User {0} has been created",
|
"UserCreatedWithName": "Uporabnik {0} je bil ustvarjen",
|
||||||
"UserDeletedWithName": "User {0} has been deleted",
|
"UserDeletedWithName": "Uporabnik {0} je bil izbrisan",
|
||||||
"UserDownloadingItemWithValues": "{0} is downloading {1}",
|
"UserDownloadingItemWithValues": "{0} prenaša {1}",
|
||||||
"UserLockedOutWithName": "User {0} has been locked out",
|
"UserLockedOutWithName": "Uporabnik {0} je bil zaklenjen",
|
||||||
"UserOfflineFromDevice": "{0} has disconnected from {1}",
|
"UserOfflineFromDevice": "{0} je prekinil povezavo z {1}",
|
||||||
"UserOnlineFromDevice": "{0} is online from {1}",
|
"UserOnlineFromDevice": "{0} je aktiven iz {1}",
|
||||||
"UserPasswordChangedWithName": "Password has been changed for user {0}",
|
"UserPasswordChangedWithName": "Geslo za uporabnika {0} je bilo spremenjeno",
|
||||||
"UserPolicyUpdatedWithName": "User policy has been updated for {0}",
|
"UserPolicyUpdatedWithName": "Pravilnik uporabe je bil posodobljen za uporabnika {0}",
|
||||||
"UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
|
"UserStartedPlayingItemWithValues": "{0} predvaja {1} na {2}",
|
||||||
"UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
|
"UserStoppedPlayingItemWithValues": "{0} je nehal predvajati {1} na {2}",
|
||||||
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
|
"ValueHasBeenAddedToLibrary": "{0} je bil dodan vaši knjižnici",
|
||||||
"ValueSpecialEpisodeName": "Special - {0}",
|
"ValueSpecialEpisodeName": "Special - {0}",
|
||||||
"VersionNumber": "Version {0}"
|
"VersionNumber": "Version {0}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
{
|
||||||
|
"Albums": "專輯",
|
||||||
|
"AppDeviceValues": "應用: {0}, 裝置: {1}",
|
||||||
|
"Application": "應用程式",
|
||||||
|
"Artists": "演出者",
|
||||||
|
"AuthenticationSucceededWithUserName": "{0} 成功授權",
|
||||||
|
"Books": "圖書",
|
||||||
|
"CameraImageUploadedFrom": "{0} 已經成功上傳一張相片",
|
||||||
|
"Channels": "頻道",
|
||||||
|
"ChapterNameValue": "章節 {0}",
|
||||||
|
"Collections": "合輯",
|
||||||
|
"DeviceOfflineWithName": "{0} 已經斷線",
|
||||||
|
"DeviceOnlineWithName": "{0} 已經連線",
|
||||||
|
"FailedLoginAttemptWithUserName": "來自 {0} 的失敗登入嘗試",
|
||||||
|
"Favorites": "我的最愛",
|
||||||
|
"Folders": "資料夾",
|
||||||
|
"Genres": "風格",
|
||||||
|
"HeaderAlbumArtists": "專輯演出者",
|
||||||
|
"HeaderCameraUploads": "相機上傳",
|
||||||
|
"HeaderContinueWatching": "繼續觀賞",
|
||||||
|
"HeaderFavoriteAlbums": "最愛專輯",
|
||||||
|
"HeaderFavoriteArtists": "最愛演出者",
|
||||||
|
"HeaderFavoriteEpisodes": "最愛級數",
|
||||||
|
"HeaderFavoriteShows": "最愛節目",
|
||||||
|
"HeaderFavoriteSongs": "最愛歌曲",
|
||||||
|
"HeaderLiveTV": "電視直播",
|
||||||
|
"HeaderNextUp": "接下來",
|
||||||
|
"HomeVideos": "自製影片",
|
||||||
|
"ItemAddedWithName": "{0} 已新增至媒體庫",
|
||||||
|
"ItemRemovedWithName": "{0} 已從媒體庫移除",
|
||||||
|
"LabelIpAddressValue": "IP 位置: {0}",
|
||||||
|
"LabelRunningTimeValue": "運行時間: {0}",
|
||||||
|
"Latest": "最新",
|
||||||
|
"MessageApplicationUpdated": "Jellyfin Server 已經更新",
|
||||||
|
"MessageApplicationUpdatedTo": "Jellyfin Server 已經更新至 {0}",
|
||||||
|
"MessageNamedServerConfigurationUpdatedWithValue": "伺服器設定 {0} 部分已經更新",
|
||||||
|
"MessageServerConfigurationUpdated": "伺服器設定已經更新",
|
||||||
|
"MixedContent": "混合內容",
|
||||||
|
"Movies": "電影",
|
||||||
|
"Music": "音樂",
|
||||||
|
"MusicVideos": "音樂MV",
|
||||||
|
"NameInstallFailed": "{0} 安裝失敗",
|
||||||
|
"NameSeasonNumber": "第 {0} 季",
|
||||||
|
"NameSeasonUnknown": "未知季數",
|
||||||
|
"NewVersionIsAvailable": "新版本的Jellyfin Server 軟體已經推出可供下載。",
|
||||||
|
"NotificationOptionApplicationUpdateAvailable": "有可用的應用程式更新",
|
||||||
|
"NotificationOptionApplicationUpdateInstalled": "應用程式已更新",
|
||||||
|
"NotificationOptionAudioPlayback": "音樂開始播放",
|
||||||
|
"NotificationOptionAudioPlaybackStopped": "音樂停止播放",
|
||||||
|
"NotificationOptionCameraImageUploaded": "相機相片已上傳",
|
||||||
|
"NotificationOptionInstallationFailed": "安裝失敗",
|
||||||
|
"NotificationOptionNewLibraryContent": "已新增新內容",
|
||||||
|
"NotificationOptionPluginError": "外掛失敗",
|
||||||
|
"NotificationOptionPluginInstalled": "外掛已安裝",
|
||||||
|
"NotificationOptionPluginUninstalled": "外掛已移除",
|
||||||
|
"NotificationOptionPluginUpdateInstalled": "已更新外掛",
|
||||||
|
"NotificationOptionServerRestartRequired": "伺服器需要重新啟動",
|
||||||
|
"NotificationOptionTaskFailed": "排程任務失敗",
|
||||||
|
"NotificationOptionUserLockedOut": "使用者已鎖定",
|
||||||
|
"NotificationOptionVideoPlayback": "影片開始播放",
|
||||||
|
"NotificationOptionVideoPlaybackStopped": "影片停止播放",
|
||||||
|
"Photos": "相片",
|
||||||
|
"Playlists": "播放清單",
|
||||||
|
"Plugin": "外掛",
|
||||||
|
"PluginInstalledWithName": "{0} 已安裝",
|
||||||
|
"PluginUninstalledWithName": "{0} 已移除",
|
||||||
|
"PluginUpdatedWithName": "{0} 已更新",
|
||||||
|
"ProviderValue": "提供商: {0}",
|
||||||
|
"ScheduledTaskFailedWithName": "{0} 已失敗",
|
||||||
|
"ScheduledTaskStartedWithName": "{0} 已開始",
|
||||||
|
"ServerNameNeedsToBeRestarted": "{0} 需要重新啟動",
|
||||||
|
"Shows": "節目",
|
||||||
|
"Songs": "歌曲",
|
||||||
|
"StartupEmbyServerIsLoading": "Jellyfin Server正在啟動,請稍後再試一次。",
|
||||||
|
"SubtitlesDownloadedForItem": "已為 {0} 下載字幕",
|
||||||
|
"Sync": "同步",
|
||||||
|
"System": "系統",
|
||||||
|
"TvShows": "電視節目",
|
||||||
|
"User": "使用者",
|
||||||
|
"UserCreatedWithName": "使用者 {0} 已建立",
|
||||||
|
"UserDeletedWithName": "使用者 {0} 已移除",
|
||||||
|
"UserDownloadingItemWithValues": "{0} 正在下載 {1}",
|
||||||
|
"UserLockedOutWithName": "使用者 {0} 已鎖定",
|
||||||
|
"UserOfflineFromDevice": "{0} 已從 {1} 斷線",
|
||||||
|
"UserOnlineFromDevice": "{0} 已連線,來自 {1}",
|
||||||
|
"UserPasswordChangedWithName": "使用者 {0} 的密碼已變更",
|
||||||
|
"UserPolicyUpdatedWithName": "使用者條約已更新為 {0}",
|
||||||
|
"UserStartedPlayingItemWithValues": "{0}正在使用 {2} 播放 {1}",
|
||||||
|
"UserStoppedPlayingItemWithValues": "{0} 已停止在 {2} 播放 {1}",
|
||||||
|
"ValueHasBeenAddedToLibrary": "{0} 已新增至您的媒體庫",
|
||||||
|
"ValueSpecialEpisodeName": "特典 - {0}",
|
||||||
|
"VersionNumber": "版本 {0}"
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -41,8 +41,6 @@ namespace Emby.Server.Implementations.Udp
|
|||||||
_socketFactory = socketFactory;
|
_socketFactory = socketFactory;
|
||||||
|
|
||||||
AddMessageResponder("who is JellyfinServer?", true, RespondToV2Message);
|
AddMessageResponder("who is JellyfinServer?", true, RespondToV2Message);
|
||||||
AddMessageResponder("who is EmbyServer?", true, RespondToV2Message);
|
|
||||||
AddMessageResponder("who is MediaBrowserServer_v2?", false, RespondToV2Message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddMessageResponder(string message, bool isSubstring, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task> responder)
|
private void AddMessageResponder(string message, bool isSubstring, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task> responder)
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -379,10 +379,15 @@ namespace MediaBrowser.Api
|
|||||||
throw new ResourceNotFoundException("User not found");
|
throw new ResourceNotFoundException("User not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(request.Password) && string.IsNullOrEmpty(request.Pw))
|
||||||
|
{
|
||||||
|
throw new MethodNotAllowedException("Hashed-only passwords are not valid for this API.");
|
||||||
|
}
|
||||||
|
|
||||||
return Post(new AuthenticateUserByName
|
return Post(new AuthenticateUserByName
|
||||||
{
|
{
|
||||||
Username = user.Name,
|
Username = user.Name,
|
||||||
Password = request.Password,
|
Password = null, // This should always be null
|
||||||
Pw = request.Pw
|
Pw = request.Pw
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,30 @@ namespace MediaBrowser.Common.Extensions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Class MethodNotAllowedException
|
||||||
|
/// </summary>
|
||||||
|
public class MethodNotAllowedException : Exception
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="MethodNotAllowedException" /> class.
|
||||||
|
/// </summary>
|
||||||
|
public MethodNotAllowedException()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="MethodNotAllowedException" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">The message.</param>
|
||||||
|
public MethodNotAllowedException(string message)
|
||||||
|
: base(message)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class RemoteServiceUnavailableException : Exception
|
public class RemoteServiceUnavailableException : Exception
|
||||||
{
|
{
|
||||||
public RemoteServiceUnavailableException()
|
public RemoteServiceUnavailableException()
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ namespace MediaBrowser.Model.System
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The version.</value>
|
/// <value>The version.</value>
|
||||||
public string Version { get; set; }
|
public string Version { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The product name. This is the AssemblyProduct name.
|
||||||
|
/// </summary>
|
||||||
|
public string ProductName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the operating system.
|
/// Gets or sets the operating system.
|
||||||
|
|||||||
@@ -32,10 +32,6 @@ namespace MediaBrowser.Model.System
|
|||||||
/// <value>The display name of the operating system.</value>
|
/// <value>The display name of the operating system.</value>
|
||||||
public string OperatingSystemDisplayName { get; set; }
|
public string OperatingSystemDisplayName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The product name. This is the AssemblyProduct name.
|
|
||||||
/// </summary>
|
|
||||||
public string ProductName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get or sets the package name.
|
/// Get or sets the package name.
|
||||||
|
|||||||
@@ -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: e07edea5bf...1ba58b06b3
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
[assembly: AssemblyVersion("10.3.0")]
|
[assembly: AssemblyVersion("10.3.2")]
|
||||||
[assembly: AssemblyFileVersion("10.3.0")]
|
[assembly: AssemblyFileVersion("10.3.2")]
|
||||||
|
|||||||
+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-rc2"
|
version: "10.3.2"
|
||||||
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
|
||||||
|
|||||||
@@ -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
|
||||||
+20
@@ -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/
|
||||||
Executable
+42
@@ -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}"
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../debian-package-x64/pkg-src
|
||||||
@@ -1,14 +1,20 @@
|
|||||||
jellyfin (10.3.0~rc2) unstable; urgency=medium
|
jellyfin (10.3.2-1) unstable; urgency=medium
|
||||||
|
|
||||||
* New upstream version 10.3.0-rc2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.0-rc2
|
* 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> Wed, 10 Apr 2019 00:51:14 -0400
|
-- Jellyfin Packaging Team <packaging@jellyfin.org> Tue, 30 Apr 2019 20:18:44 -0400
|
||||||
|
|
||||||
jellyfin (10.3.0~rc1) unstable; urgency=medium
|
jellyfin (10.3.1-1) unstable; urgency=medium
|
||||||
|
|
||||||
* New upstream version 10.3.0-rc1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.0-rc1
|
* 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, 30 Mar 2019 15:47:24 -0400
|
-- 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
|
||||||
|
|
||||||
|
-- Jellyfin Packaging Team <packaging@jellyfin.org> Fri, 19 Apr 2019 14:24:29 -0400
|
||||||
|
|
||||||
jellyfin (10.2.2-1) unstable; urgency=medium
|
jellyfin (10.2.2-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
%endif
|
%endif
|
||||||
|
|
||||||
Name: jellyfin
|
Name: jellyfin
|
||||||
Version: 10.3.0
|
Version: 10.3.2
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: The Free Software Media Browser
|
Summary: The Free Software Media Browser
|
||||||
License: GPLv2
|
License: GPLv2
|
||||||
@@ -140,10 +140,12 @@ fi
|
|||||||
%systemd_postun_with_restart jellyfin.service
|
%systemd_postun_with_restart jellyfin.service
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* Wed Apr 10 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
|
* Tue Apr 30 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||||
- New upstream version 10.3.0-rc2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.0-rc2
|
- New upstream version 10.3.2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.2
|
||||||
* Sat Mar 30 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
|
* Sat Apr 20 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||||
- New upstream version 10.3.0-rc1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.0-rc1
|
- 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>
|
* Thu Feb 28 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||||
- jellyfin:
|
- jellyfin:
|
||||||
- PR968 Release 10.2.z copr autobuild
|
- PR968 Release 10.2.z copr autobuild
|
||||||
|
|||||||
@@ -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
|
||||||
+20
@@ -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/
|
||||||
Executable
+42
@@ -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}"
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../debian-package-x64/pkg-src
|
||||||
Reference in New Issue
Block a user