Merge branch 'dev' into reformat

This commit is contained in:
Erwin de Haan 2019-01-18 17:04:01 +01:00
commit d116efe1f7
13 changed files with 137 additions and 152 deletions

View file

@ -27,5 +27,9 @@ COPY --from=ffmpeg /ffmpeg-bin/* /usr/bin/
EXPOSE 8096 EXPOSE 8096
VOLUME /config /media VOLUME /config /media
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y libfontconfig1 --no-install-recommends # needed for Skia && apt-get install --no-install-recommends --no-install-suggests -y \
libfontconfig1 # Required for Skia \
&& apt-get clean autoclean \
&& apt-get autoremove \
&& rm -rf /var/lib/{apt,dpkg,cache,log}
ENTRYPOINT dotnet /jellyfin/jellyfin.dll -programdata /config ENTRYPOINT dotnet /jellyfin/jellyfin.dll -programdata /config

View file

@ -13,4 +13,11 @@
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" /> <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
</ItemGroup> </ItemGroup>
<PropertyGroup>
<Authors>Jellyfin Contributors</Authors>
<PackageId>Jellyfin.Naming</PackageId>
<PackageLicenseUrl>https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt</PackageLicenseUrl>
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
</PropertyGroup>
</Project> </Project>

View file

@ -230,7 +230,6 @@ namespace Emby.Server.Implementations
{ {
get get
{ {
#if BETA #if BETA
return PackageVersionClass.Beta; return PackageVersionClass.Beta;
#endif #endif
@ -510,7 +509,7 @@ namespace Emby.Server.Implementations
protected void RegisterSingleInstance<T>(T obj, bool manageLifetime = true) protected void RegisterSingleInstance<T>(T obj, bool manageLifetime = true)
where T : class where T : class
{ {
Container.RegisterSingleton(obj); Container.RegisterInstance<T>(obj);
if (manageLifetime) if (manageLifetime)
{ {
@ -575,7 +574,7 @@ namespace Emby.Server.Implementations
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex, "Error loading assembly {file}", file); Logger.LogError(ex, "Error loading assembly {File}", file);
return null; return null;
} }
} }

View file

@ -387,7 +387,7 @@ namespace Emby.Server.Implementations.HttpClientManager
{ {
options.ResourcePool?.Release(); options.ResourcePool?.Release();
throw new HttpException(string.Format("Connection to {0} timed out", options.Url)) { IsTimedOut = true }; throw new HttpException($"Connection to {options.Url} timed out") { IsTimedOut = true };
} }
if (options.LogRequest) if (options.LogRequest)

View file

@ -54,39 +54,33 @@ namespace Emby.Server.Implementations.HttpServer
private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>(); private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>();
private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>(); private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>();
public HttpListenerHost(IServerApplicationHost applicationHost, public HttpListenerHost(
IServerApplicationHost applicationHost,
ILogger logger, ILogger logger,
IServerConfigurationManager config, IServerConfigurationManager config,
string defaultRedirectPath, INetworkManager networkManager, ITextEncoding textEncoding, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer, Func<Type, Func<string, object>> funcParseFn) string defaultRedirectPath,
INetworkManager networkManager,
ITextEncoding textEncoding,
IJsonSerializer jsonSerializer,
IXmlSerializer xmlSerializer,
Func<Type, Func<string, object>> funcParseFn)
{ {
Instance = this;
_appHost = applicationHost; _appHost = applicationHost;
_logger = logger;
_config = config;
DefaultRedirectPath = defaultRedirectPath; DefaultRedirectPath = defaultRedirectPath;
_networkManager = networkManager; _networkManager = networkManager;
_textEncoding = textEncoding; _textEncoding = textEncoding;
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_xmlSerializer = xmlSerializer; _xmlSerializer = xmlSerializer;
_config = config;
_logger = logger;
_funcParseFn = funcParseFn; _funcParseFn = funcParseFn;
ResponseFilters = new Action<IRequest, IResponse, object>[] { }; Instance = this;
ResponseFilters = Array.Empty<Action<IRequest, IResponse, object>>();
} }
public string GlobalResponse { get; set; } public string GlobalResponse { get; set; }
readonly Dictionary<Type, int> _mapExceptionToStatusCode = new Dictionary<Type, int>
{
{typeof (ResourceNotFoundException), 404},
{typeof (RemoteServiceUnavailableException), 502},
{typeof (FileNotFoundException), 404},
//{typeof (DirectoryNotFoundException), 404},
{typeof (SecurityException), 401},
{typeof (ArgumentException), 400}
};
protected ILogger Logger => _logger; protected ILogger Logger => _logger;
public object CreateInstance(Type type) public object CreateInstance(Type type)
@ -103,9 +97,9 @@ namespace Emby.Server.Implementations.HttpServer
{ {
//Exec all RequestFilter attributes with Priority < 0 //Exec all RequestFilter attributes with Priority < 0
var attributes = GetRequestFilterAttributes(requestDto.GetType()); var attributes = GetRequestFilterAttributes(requestDto.GetType());
var i = 0;
var count = attributes.Count;
int count = attributes.Count;
int i = 0;
for (; i < count && attributes[i].Priority < 0; i++) for (; i < count && attributes[i].Priority < 0; i++)
{ {
var attribute = attributes[i]; var attribute = attributes[i];
@ -167,11 +161,8 @@ namespace Emby.Server.Implementations.HttpServer
_webSocketConnections.Add(connection); _webSocketConnections.Add(connection);
} }
if (WebSocketConnected != null)
{
WebSocketConnected?.Invoke(this, new GenericEventArgs<IWebSocketConnection>(connection)); WebSocketConnected?.Invoke(this, new GenericEventArgs<IWebSocketConnection>(connection));
} }
}
private void Connection_Closed(object sender, EventArgs e) private void Connection_Closed(object sender, EventArgs e)
{ {
@ -205,26 +196,16 @@ namespace Emby.Server.Implementations.HttpServer
private int GetStatusCode(Exception ex) private int GetStatusCode(Exception ex)
{ {
if (ex is ArgumentException) switch (ex)
{ {
return 400; case ArgumentException _: return 400;
case SecurityException _: return 401;
case DirectoryNotFoundException _:
case FileNotFoundException _:
case ResourceNotFoundException _: return 404;
case RemoteServiceUnavailableException _: return 502;
default: return 500;
} }
var exceptionType = ex.GetType();
if (!_mapExceptionToStatusCode.TryGetValue(exceptionType, out var statusCode))
{
if (ex is DirectoryNotFoundException)
{
statusCode = 404;
}
else
{
statusCode = 500;
}
}
return statusCode;
} }
private async Task ErrorHandler(Exception ex, IRequest httpReq, bool logExceptionStackTrace, bool logExceptionMessage) private async Task ErrorHandler(Exception ex, IRequest httpReq, bool logExceptionStackTrace, bool logExceptionMessage)
@ -310,29 +291,22 @@ namespace Emby.Server.Implementations.HttpServer
} }
} }
private readonly Dictionary<string, int> _skipLogExtensions = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase) private static readonly string[] _skipLogExtensions =
{ {
{".js", 0}, ".js",
{".css", 0}, ".css",
{".woff", 0}, ".woff",
{".woff2", 0}, ".woff2",
{".ttf", 0}, ".ttf",
{".html", 0} ".html"
}; };
private bool EnableLogging(string url, string localPath) private bool EnableLogging(string url, string localPath)
{ {
var extension = GetExtension(url); var extension = GetExtension(url);
if (string.IsNullOrEmpty(extension) || !_skipLogExtensions.ContainsKey(extension)) return ((string.IsNullOrEmpty(extension) || !_skipLogExtensions.Contains(extension))
{ && (string.IsNullOrEmpty(localPath) || localPath.IndexOf("system/ping", StringComparison.OrdinalIgnoreCase) == -1));
if (string.IsNullOrEmpty(localPath) || localPath.IndexOf("system/ping", StringComparison.OrdinalIgnoreCase) == -1)
{
return true;
}
}
return false;
} }
private static string GetExtension(string url) private static string GetExtension(string url)
@ -555,9 +529,7 @@ namespace Emby.Server.Implementations.HttpServer
return; return;
} }
if (string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase) || if (localPath.IndexOf("mediabrowser/web", StringComparison.OrdinalIgnoreCase) != -1)
string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase) ||
localPath.IndexOf("mediabrowser/web", StringComparison.OrdinalIgnoreCase) != -1)
{ {
httpRes.StatusCode = 200; httpRes.StatusCode = 200;
httpRes.ContentType = "text/html"; httpRes.ContentType = "text/html";
@ -711,7 +683,7 @@ namespace Emby.Server.Implementations.HttpServer
}; };
} }
_logger.LogError("Could not find handler for {pathInfo}", pathInfo); _logger.LogError("Could not find handler for {PathInfo}", pathInfo);
return null; return null;
} }
@ -725,13 +697,13 @@ namespace Emby.Server.Implementations.HttpServer
private void RedirectToSecureUrl(IHttpRequest httpReq, IResponse httpRes, string url) private void RedirectToSecureUrl(IHttpRequest httpReq, IResponse httpRes, string url)
{ {
int currentPort; if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri))
if (Uri.TryCreate(url, UriKind.Absolute, out var uri))
{ {
currentPort = uri.Port; var builder = new UriBuilder(uri)
var builder = new UriBuilder(uri); {
builder.Port = _config.Configuration.PublicHttpsPort; Port = _config.Configuration.PublicHttpsPort,
builder.Scheme = "https"; Scheme = "https"
};
url = builder.Uri.ToString(); url = builder.Uri.ToString();
RedirectToUrl(httpRes, url); RedirectToUrl(httpRes, url);
@ -831,12 +803,6 @@ namespace Emby.Server.Implementations.HttpServer
public Task<object> DeserializeJson(Type type, Stream stream) public Task<object> DeserializeJson(Type type, Stream stream)
{ {
//using (var reader = new StreamReader(stream))
//{
// var json = reader.ReadToEnd();
// logger.LogInformation(json);
// return _jsonSerializer.DeserializeFromString(json, type);
//}
return _jsonSerializer.DeserializeFromStreamAsync(stream, type); return _jsonSerializer.DeserializeFromStreamAsync(stream, type);
} }

View file

@ -410,11 +410,13 @@ namespace Emby.Server.Implementations.HttpServer
serializer.WriteObject(xw, from); serializer.WriteObject(xw, from);
xw.Flush(); xw.Flush();
ms.Seek(0, SeekOrigin.Begin); ms.Seek(0, SeekOrigin.Begin);
var reader = new StreamReader(ms); using (var reader = new StreamReader(ms))
{
return reader.ReadToEnd(); return reader.ReadToEnd();
} }
} }
} }
}
/// <summary> /// <summary>
/// Pres the process optimized result. /// Pres the process optimized result.
@ -423,7 +425,7 @@ namespace Emby.Server.Implementations.HttpServer
{ {
responseHeaders["ETag"] = string.Format("\"{0}\"", cacheKeyString); responseHeaders["ETag"] = string.Format("\"{0}\"", cacheKeyString);
var noCache = (requestContext.Headers.Get("Cache-Control") ?? string.Empty).IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1; bool noCache = (requestContext.Headers.Get("Cache-Control") ?? string.Empty).IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1;
if (!noCache) if (!noCache)
{ {
@ -461,8 +463,7 @@ namespace Emby.Server.Implementations.HttpServer
}); });
} }
public Task<object> GetStaticFileResult(IRequest requestContext, public Task<object> GetStaticFileResult(IRequest requestContext, StaticFileResultOptions options)
StaticFileResultOptions options)
{ {
var path = options.Path; var path = options.Path;
var fileShare = options.FileShare; var fileShare = options.FileShare;
@ -697,34 +698,28 @@ namespace Emby.Server.Implementations.HttpServer
var ifModifiedSinceHeader = requestContext.Headers.Get("If-Modified-Since"); var ifModifiedSinceHeader = requestContext.Headers.Get("If-Modified-Since");
if (!string.IsNullOrEmpty(ifModifiedSinceHeader)) if (!string.IsNullOrEmpty(ifModifiedSinceHeader)
{ && DateTime.TryParse(ifModifiedSinceHeader, out DateTime ifModifiedSince)
if (DateTime.TryParse(ifModifiedSinceHeader, out var ifModifiedSince)) && IsNotModified(ifModifiedSince.ToUniversalTime(), cacheDuration, lastDateModified))
{
if (IsNotModified(ifModifiedSince.ToUniversalTime(), cacheDuration, lastDateModified))
{ {
return true; return true;
} }
}
}
var ifNoneMatchHeader = requestContext.Headers.Get("If-None-Match"); var ifNoneMatchHeader = requestContext.Headers.Get("If-None-Match");
var hasCacheKey = !cacheKey.Equals(Guid.Empty); bool hasCacheKey = !cacheKey.Equals(Guid.Empty);
// Validate If-None-Match // Validate If-None-Match
if ((hasCacheKey || !string.IsNullOrEmpty(ifNoneMatchHeader))) if ((hasCacheKey && !string.IsNullOrEmpty(ifNoneMatchHeader)))
{ {
ifNoneMatchHeader = (ifNoneMatchHeader ?? string.Empty).Trim('\"'); ifNoneMatchHeader = (ifNoneMatchHeader ?? string.Empty).Trim('\"');
if (Guid.TryParse(ifNoneMatchHeader, out var ifNoneMatch)) if (Guid.TryParse(ifNoneMatchHeader, out Guid ifNoneMatch)
{ && cacheKey.Equals(ifNoneMatch))
if (hasCacheKey && cacheKey.Equals(ifNoneMatch))
{ {
return true; return true;
} }
} }
}
return false; return false;
} }

View file

@ -40,8 +40,6 @@ namespace Emby.Server.Implementations.HttpServer
/// </summary> /// </summary>
private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
public List<Cookie> Cookies { get; private set; }
/// <summary> /// <summary>
/// Additional HTTP Headers /// Additional HTTP Headers
/// </summary> /// </summary>
@ -72,7 +70,6 @@ namespace Emby.Server.Implementations.HttpServer
Headers["Accept-Ranges"] = "bytes"; Headers["Accept-Ranges"] = "bytes";
StatusCode = HttpStatusCode.PartialContent; StatusCode = HttpStatusCode.PartialContent;
Cookies = new List<Cookie>();
SetRangeValues(contentLength); SetRangeValues(contentLength);
} }
@ -220,7 +217,5 @@ namespace Emby.Server.Implementations.HttpServer
get => (HttpStatusCode)Status; get => (HttpStatusCode)Status;
set => Status = (int)value; set => Status = (int)value;
} }
public string StatusDescription { get; set; }
} }
} }

View file

@ -25,14 +25,11 @@ 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("X-UA-Compatible", "IE=Edge");
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-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", "*");
var exception = dto as Exception; if (dto is Exception exception)
if (exception != null)
{ {
_logger.LogError(exception, "Error processing request for {RawUrl}", req.RawUrl); _logger.LogError(exception, "Error processing request for {RawUrl}", req.RawUrl);
@ -45,42 +42,26 @@ namespace Emby.Server.Implementations.HttpServer
} }
} }
var hasHeaders = dto as IHasHeaders; if (dto is IHasHeaders hasHeaders)
if (hasHeaders != null)
{ {
if (!hasHeaders.Headers.ContainsKey("Server")) if (!hasHeaders.Headers.ContainsKey("Server"))
{ {
hasHeaders.Headers["Server"] = "Microsoft-NetCore/2.0, UPnP/1.0 DLNADOC/1.50"; hasHeaders.Headers["Server"] = "Microsoft-NetCore/2.0, UPnP/1.0 DLNADOC/1.50";
//hasHeaders.Headers["Server"] = "Mono-HTTPAPI/1.1";
} }
// Content length has to be explicitly set on on HttpListenerResponse or it won't be happy // Content length has to be explicitly set on on HttpListenerResponse or it won't be happy
if (hasHeaders.Headers.TryGetValue("Content-Length", out string contentLength)
if (hasHeaders.Headers.TryGetValue("Content-Length", out string contentLength) && !string.IsNullOrEmpty(contentLength)) && !string.IsNullOrEmpty(contentLength))
{ {
var length = long.Parse(contentLength, UsCulture); var length = long.Parse(contentLength, UsCulture);
if (length > 0) if (length > 0)
{ {
res.SetContentLength(length); res.SetContentLength(length);
//var listenerResponse = res.OriginalResponse as HttpListenerResponse;
//if (listenerResponse != null)
//{
// // Disable chunked encoding. Technically this is only needed when using Content-Range, but
// // anytime we know the content length there's no need for it
// listenerResponse.SendChunked = false;
// return;
//}
res.SendChunked = false; res.SendChunked = false;
} }
} }
} }
//res.KeepAlive = false;
} }
/// <summary> /// <summary>

View file

@ -15,7 +15,6 @@ namespace Emby.Server.Implementations.Services
public HttpResult(object response, string contentType, HttpStatusCode statusCode) public HttpResult(object response, string contentType, HttpStatusCode statusCode)
{ {
this.Headers = new Dictionary<string, string>(); this.Headers = new Dictionary<string, string>();
this.Cookies = new List<Cookie>();
this.Response = response; this.Response = response;
this.ContentType = contentType; this.ContentType = contentType;
@ -26,8 +25,6 @@ namespace Emby.Server.Implementations.Services
public IDictionary<string, string> Headers { get; private set; } public IDictionary<string, string> Headers { get; private set; }
public List<Cookie> Cookies { get; private set; }
public int Status { get; set; } public int Status { get; set; }
public HttpStatusCode StatusCode public HttpStatusCode StatusCode
@ -40,15 +37,16 @@ namespace Emby.Server.Implementations.Services
public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken) public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
{ {
var response = RequestContext != null ? RequestContext.Response : null; var response = RequestContext == null ? null : RequestContext.Response;
var bytesResponse = this.Response as byte[]; if (this.Response is byte[] bytesResponse)
if (bytesResponse != null)
{ {
var contentLength = bytesResponse.Length; var contentLength = bytesResponse.Length;
if (response != null) if (response != null)
{
response.SetContentLength(contentLength); response.SetContentLength(contentLength);
}
if (contentLength > 0) if (contentLength > 0)
{ {

View file

@ -6,6 +6,7 @@ using System.Net;
using System.Net.Security; using System.Net.Security;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Emby.Drawing; using Emby.Drawing;
using Emby.Drawing.Skia; using Emby.Drawing.Skia;
@ -29,35 +30,58 @@ namespace Jellyfin.Server
{ {
public static class Program public static class Program
{ {
private static readonly TaskCompletionSource<bool> ApplicationTaskCompletionSource = new TaskCompletionSource<bool>(); private static readonly CancellationTokenSource _tokenSource = new CancellationTokenSource();
private static ILoggerFactory _loggerFactory; private static readonly ILoggerFactory _loggerFactory = new SerilogLoggerFactory();
private static ILogger _logger; private static ILogger _logger;
private static bool _restartOnShutdown; private static bool _restartOnShutdown;
public static async Task<int> Main(string[] args) public static async Task Main(string[] args)
{ {
var options = new StartupOptions(args); StartupOptions options = new StartupOptions(args);
var version = Assembly.GetEntryAssembly().GetName().Version; Version version = Assembly.GetEntryAssembly().GetName().Version;
if (options.ContainsOption("-v") || options.ContainsOption("--version")) if (options.ContainsOption("-v") || options.ContainsOption("--version"))
{ {
Console.WriteLine(version.ToString()); Console.WriteLine(version.ToString());
return 0;
} }
var appPaths = createApplicationPaths(options); ServerApplicationPaths appPaths = createApplicationPaths(options);
// $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager // $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager
Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath); Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath);
await createLogger(appPaths); await createLogger(appPaths);
_loggerFactory = new SerilogLoggerFactory();
_logger = _loggerFactory.CreateLogger("Main"); _logger = _loggerFactory.CreateLogger("Main");
AppDomain.CurrentDomain.UnhandledException += (sender, e) AppDomain.CurrentDomain.UnhandledException += (sender, e)
=> _logger.LogCritical((Exception)e.ExceptionObject, "Unhandled Exception"); => _logger.LogCritical((Exception)e.ExceptionObject, "Unhandled Exception");
// Intercept Ctrl+C and Ctrl+Break
Console.CancelKeyPress += (sender, e) =>
{
if (_tokenSource.IsCancellationRequested)
{
return; // Already shutting down
}
e.Cancel = true;
_logger.LogInformation("Ctrl+C, shutting down");
Environment.ExitCode = 128 + 2;
Shutdown();
};
// Register a SIGTERM handler
AppDomain.CurrentDomain.ProcessExit += (sender, e) =>
{
if (_tokenSource.IsCancellationRequested)
{
return; // Already shutting down
}
_logger.LogInformation("Received a SIGTERM signal, shutting down");
Environment.ExitCode = 128 + 15;
Shutdown();
};
_logger.LogInformation("Jellyfin version: {Version}", version); _logger.LogInformation("Jellyfin version: {Version}", version);
var environmentInfo = new EnvironmentInfo(getOperatingSystem()); EnvironmentInfo environmentInfo = new EnvironmentInfo(getOperatingSystem());
ApplicationHost.LogEnvironmentInfo(_logger, appPaths, environmentInfo); ApplicationHost.LogEnvironmentInfo(_logger, appPaths, environmentInfo);
SQLitePCL.Batteries_V2.Init(); SQLitePCL.Batteries_V2.Init();
@ -86,8 +110,16 @@ namespace Jellyfin.Server
await appHost.RunStartupTasks(); await appHost.RunStartupTasks();
// TODO: read input for a stop command // TODO: read input for a stop command
try
{
// Block main thread until shutdown // Block main thread until shutdown
await ApplicationTaskCompletionSource.Task; await Task.Delay(-1, _tokenSource.Token);
}
catch (TaskCanceledException)
{
// Don't throw on cancellation
}
_logger.LogInformation("Disposing app host"); _logger.LogInformation("Disposing app host");
} }
@ -96,8 +128,6 @@ namespace Jellyfin.Server
{ {
StartNewInstance(options); StartNewInstance(options);
} }
return 0;
} }
private static ServerApplicationPaths createApplicationPaths(StartupOptions options) private static ServerApplicationPaths createApplicationPaths(StartupOptions options)
@ -173,7 +203,7 @@ namespace Jellyfin.Server
if (!File.Exists(configPath)) if (!File.Exists(configPath))
{ {
// For some reason the csproj name is used instead of the assembly name // For some reason the csproj name is used instead of the assembly name
using (var rscstr = typeof(Program).Assembly using (Stream rscstr = typeof(Program).Assembly
.GetManifestResourceStream("Jellyfin.Server.Resources.Configuration.logging.json")) .GetManifestResourceStream("Jellyfin.Server.Resources.Configuration.logging.json"))
using (Stream fstr = File.Open(configPath, FileMode.CreateNew)) using (Stream fstr = File.Open(configPath, FileMode.CreateNew))
{ {
@ -259,7 +289,10 @@ namespace Jellyfin.Server
public static void Shutdown() public static void Shutdown()
{ {
ApplicationTaskCompletionSource.SetResult(true); if (!_tokenSource.IsCancellationRequested)
{
_tokenSource.Cancel();
}
} }
public static void Restart() public static void Restart()

View file

@ -61,8 +61,7 @@ namespace MediaBrowser.Controller.Entities
} }
} }
width /= Height.Value; return width / height;
return width;
} }
return base.GetDefaultPrimaryImageAspectRatio(); return base.GetDefaultPrimaryImageAspectRatio();

View file

@ -1,4 +1,4 @@
using System.Reflection; using System.Reflection;
[assembly: AssemblyVersion("10.0.1")] [assembly: AssemblyVersion("10.0.2")]
[assembly: AssemblyFileVersion("10.0.1")] [assembly: AssemblyFileVersion("10.0.2")]

View file

@ -1,3 +1,11 @@
jellyfin (10.0.2-1) unstable; urgency=medium
* Hotfix release
* jellyfin/jellyfin-web#23: Update Chromecast app ID [via direct commit]
* #540: Update Emby API keys to our own
* #541: Change ItemId to Guid in ProviderManager
* #566: Avoid printing stacktrace when bind to port 1900 fails
jellyfin (10.0.1-1) unstable; urgency=medium jellyfin (10.0.1-1) unstable; urgency=medium
* Hotfix release, corrects several small bugs from 10.0.0 * Hotfix release, corrects several small bugs from 10.0.0