From 07a8e49c4b1e4a2dddbaa49ab6f1ff4f271fbf20 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 6 Jan 2019 20:35:36 +0100 Subject: [PATCH 01/21] Cleanup some small things --- .../ApplicationHost.cs | 5 +- .../HttpClientManager/HttpClientManager.cs | 2 +- .../HttpServer/HttpListenerHost.cs | 136 +++++++----------- .../HttpServer/HttpResultFactory.cs | 39 ++--- .../HttpServer/RangeRequestWriter.cs | 5 - .../HttpServer/ResponseFilter.cs | 28 +--- .../Services/HttpResult.cs | 10 +- .../Services/RequestHelper.cs | 2 +- 8 files changed, 77 insertions(+), 150 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 9b9c6146fa..8d161d3751 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -238,7 +238,6 @@ namespace Emby.Server.Implementations { get { - #if BETA return PackageVersionClass.Beta; #endif @@ -552,7 +551,7 @@ namespace Emby.Server.Implementations protected void RegisterSingleInstance(T obj, bool manageLifetime = true) where T : class { - Container.RegisterSingleton(obj); + Container.RegisterInstance(obj); if (manageLifetime) { @@ -617,7 +616,7 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - Logger.LogError(ex, "Error loading assembly {file}", file); + Logger.LogError(ex, "Error loading assembly {File}", file); return null; } } diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index d3ba1b683e..c95093fc58 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -389,7 +389,7 @@ namespace Emby.Server.Implementations.HttpClientManager { 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) diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 69ca0f85b6..91b2e9ded6 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -1,27 +1,28 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Net; -using Microsoft.Extensions.Logging; + using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Net.Sockets; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; +using Emby.Server.Implementations.Net; using Emby.Server.Implementations.Services; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Common.Security; using MediaBrowser.Controller; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Net; +using MediaBrowser.Model.Events; using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; using MediaBrowser.Model.Text; -using System.Net.Sockets; -using Emby.Server.Implementations.Net; -using MediaBrowser.Model.Events; +using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.HttpServer { @@ -55,40 +56,33 @@ namespace Emby.Server.Implementations.HttpServer private IWebSocketListener[] _webSocketListeners = Array.Empty(); private readonly List _webSocketConnections = new List(); - public HttpListenerHost(IServerApplicationHost applicationHost, + public HttpListenerHost( + IServerApplicationHost applicationHost, ILogger logger, IServerConfigurationManager config, - string defaultRedirectPath, INetworkManager networkManager, ITextEncoding textEncoding, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer, Func> funcParseFn) + string defaultRedirectPath, + INetworkManager networkManager, + ITextEncoding textEncoding, + IJsonSerializer jsonSerializer, + IXmlSerializer xmlSerializer, + Func> funcParseFn) { - Instance = this; - _appHost = applicationHost; + _logger = logger; + _config = config; DefaultRedirectPath = defaultRedirectPath; _networkManager = networkManager; _textEncoding = textEncoding; _jsonSerializer = jsonSerializer; _xmlSerializer = xmlSerializer; - _config = config; - - _logger = logger; _funcParseFn = funcParseFn; - ResponseFilters = new Action[] { }; + Instance = this; + ResponseFilters = Array.Empty>(); } public string GlobalResponse { get; set; } - readonly Dictionary _mapExceptionToStatusCode = new Dictionary - { - {typeof (ResourceNotFoundException), 404}, - {typeof (RemoteServiceUnavailableException), 502}, - {typeof (FileNotFoundException), 404}, - //{typeof (DirectoryNotFoundException), 404}, - {typeof (SecurityException), 401}, - {typeof (PaymentRequiredException), 402}, - {typeof (ArgumentException), 400} - }; - protected ILogger Logger { get @@ -111,9 +105,9 @@ namespace Emby.Server.Implementations.HttpServer { //Exec all RequestFilter attributes with Priority < 0 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++) { var attribute = attributes[i]; @@ -176,10 +170,7 @@ namespace Emby.Server.Implementations.HttpServer _webSocketConnections.Add(connection); } - if (WebSocketConnected != null) - { - WebSocketConnected?.Invoke(this, new GenericEventArgs(connection)); - } + WebSocketConnected?.Invoke(this, new GenericEventArgs(connection)); } private void Connection_Closed(object sender, EventArgs e) @@ -192,8 +183,7 @@ namespace Emby.Server.Implementations.HttpServer private Exception GetActualException(Exception ex) { - var agg = ex as AggregateException; - if (agg != null) + if (ex is AggregateException agg) { var inner = agg.InnerException; if (inner != null) @@ -215,27 +205,17 @@ namespace Emby.Server.Implementations.HttpServer private int GetStatusCode(Exception ex) { - if (ex is ArgumentException) + switch (ex) { - return 400; + case ArgumentException _: return 400; + case SecurityException _: return 401; + case PaymentRequiredException _: return 402; + case DirectoryNotFoundException _: + case FileNotFoundException _: + case ResourceNotFoundException _: return 404; + case RemoteServiceUnavailableException _: return 502; + default: return 500; } - - var exceptionType = ex.GetType(); - - int statusCode; - if (!_mapExceptionToStatusCode.TryGetValue(exceptionType, out statusCode)) - { - if (ex is DirectoryNotFoundException) - { - statusCode = 404; - } - else - { - statusCode = 500; - } - } - - return statusCode; } private async Task ErrorHandler(Exception ex, IRequest httpReq, bool logExceptionStackTrace, bool logExceptionMessage) @@ -321,29 +301,22 @@ namespace Emby.Server.Implementations.HttpServer } } - private readonly Dictionary _skipLogExtensions = new Dictionary(StringComparer.OrdinalIgnoreCase) + private static readonly string[] _skipLogExtensions = { - {".js", 0}, - {".css", 0}, - {".woff", 0}, - {".woff2", 0}, - {".ttf", 0}, - {".html", 0} + ".js", + ".css", + ".woff", + ".woff2", + ".ttf", + ".html" }; private bool EnableLogging(string url, string localPath) { var extension = GetExtension(url); - if (string.IsNullOrEmpty(extension) || !_skipLogExtensions.ContainsKey(extension)) - { - if (string.IsNullOrEmpty(localPath) || localPath.IndexOf("system/ping", StringComparison.OrdinalIgnoreCase) == -1) - { - return true; - } - } - - return false; + return ((string.IsNullOrEmpty(extension) || !_skipLogExtensions.Contains(extension)) + && (string.IsNullOrEmpty(localPath) || localPath.IndexOf("system/ping", StringComparison.OrdinalIgnoreCase) == -1)); } private string GetExtension(string url) @@ -566,9 +539,7 @@ namespace Emby.Server.Implementations.HttpServer return; } - if (string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase) || - string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase) || - localPath.IndexOf("mediabrowser/web", StringComparison.OrdinalIgnoreCase) != -1) + if (localPath.IndexOf("mediabrowser/web", StringComparison.OrdinalIgnoreCase) != -1) { httpRes.StatusCode = 200; httpRes.ContentType = "text/html"; @@ -723,7 +694,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; } @@ -737,14 +708,13 @@ namespace Emby.Server.Implementations.HttpServer private void RedirectToSecureUrl(IHttpRequest httpReq, IResponse httpRes, string url) { - int currentPort; - Uri uri; - if (Uri.TryCreate(url, UriKind.Absolute, out uri)) + if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)) { - currentPort = uri.Port; - var builder = new UriBuilder(uri); - builder.Port = _config.Configuration.PublicHttpsPort; - builder.Scheme = "https"; + var builder = new UriBuilder(uri) + { + Port = _config.Configuration.PublicHttpsPort, + Scheme = "https" + }; url = builder.Uri.ToString(); RedirectToUrl(httpRes, url); @@ -844,12 +814,6 @@ namespace Emby.Server.Implementations.HttpServer public Task 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); } diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index 73b2afe640..3d0fb577a2 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -412,8 +412,10 @@ namespace Emby.Server.Implementations.HttpServer serializer.WriteObject(xw, from); xw.Flush(); ms.Seek(0, SeekOrigin.Begin); - var reader = new StreamReader(ms); - return reader.ReadToEnd(); + using (var reader = new StreamReader(ms)) + { + return reader.ReadToEnd(); + } } } } @@ -425,7 +427,7 @@ namespace Emby.Server.Implementations.HttpServer { 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) { @@ -463,8 +465,7 @@ namespace Emby.Server.Implementations.HttpServer }); } - public Task GetStaticFileResult(IRequest requestContext, - StaticFileResultOptions options) + public Task GetStaticFileResult(IRequest requestContext, StaticFileResultOptions options) { var path = options.Path; var fileShare = options.FileShare; @@ -699,36 +700,26 @@ namespace Emby.Server.Implementations.HttpServer var ifModifiedSinceHeader = requestContext.Headers.Get("If-Modified-Since"); - if (!string.IsNullOrEmpty(ifModifiedSinceHeader)) + if (!string.IsNullOrEmpty(ifModifiedSinceHeader) + && DateTime.TryParse(ifModifiedSinceHeader, out DateTime ifModifiedSince) + && IsNotModified(ifModifiedSince.ToUniversalTime(), cacheDuration, lastDateModified)) { - DateTime ifModifiedSince; - - if (DateTime.TryParse(ifModifiedSinceHeader, out ifModifiedSince)) - { - if (IsNotModified(ifModifiedSince.ToUniversalTime(), cacheDuration, lastDateModified)) - { - return true; - } - } + return true; } var ifNoneMatchHeader = requestContext.Headers.Get("If-None-Match"); - var hasCacheKey = !cacheKey.Equals(Guid.Empty); + bool hasCacheKey = !cacheKey.Equals(Guid.Empty); // Validate If-None-Match - if ((hasCacheKey || !string.IsNullOrEmpty(ifNoneMatchHeader))) + if ((hasCacheKey && !string.IsNullOrEmpty(ifNoneMatchHeader))) { - Guid ifNoneMatch; - ifNoneMatchHeader = (ifNoneMatchHeader ?? string.Empty).Trim('\"'); - if (Guid.TryParse(ifNoneMatchHeader, out ifNoneMatch)) + if (Guid.TryParse(ifNoneMatchHeader, out Guid ifNoneMatch) + && cacheKey.Equals(ifNoneMatch)) { - if (hasCacheKey && cacheKey.Equals(ifNoneMatch)) - { - return true; - } + return true; } } diff --git a/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs index dc20ee1a24..795f96f2c2 100644 --- a/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs +++ b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs @@ -40,8 +40,6 @@ namespace Emby.Server.Implementations.HttpServer /// private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - public List Cookies { get; private set; } - /// /// Additional HTTP Headers /// @@ -75,7 +73,6 @@ namespace Emby.Server.Implementations.HttpServer Headers["Accept-Ranges"] = "bytes"; StatusCode = HttpStatusCode.PartialContent; - Cookies = new List(); SetRangeValues(contentLength); } @@ -223,7 +220,5 @@ namespace Emby.Server.Implementations.HttpServer get { return (HttpStatusCode)Status; } set { Status = (int)value; } } - - public string StatusDescription { get; set; } } } diff --git a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs index f38aa5ea06..afaee59f39 100644 --- a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs +++ b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs @@ -25,14 +25,11 @@ namespace Emby.Server.Implementations.HttpServer public void FilterResponse(IRequest req, IResponse res, object dto) { // 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-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); res.AddHeader("Access-Control-Allow-Origin", "*"); - var exception = dto as Exception; - - if (exception != null) + if (dto is Exception exception) { _logger.LogError(exception, "Error processing request for {RawUrl}", req.RawUrl); @@ -45,43 +42,26 @@ namespace Emby.Server.Implementations.HttpServer } } - var hasHeaders = dto as IHasHeaders; - - if (hasHeaders != null) + if (dto is IHasHeaders hasHeaders) { if (!hasHeaders.Headers.ContainsKey("Server")) { 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 - string contentLength; - - if (hasHeaders.Headers.TryGetValue("Content-Length", out contentLength) && !string.IsNullOrEmpty(contentLength)) + if (hasHeaders.Headers.TryGetValue("Content-Length", out string contentLength) + && !string.IsNullOrEmpty(contentLength)) { var length = long.Parse(contentLength, UsCulture); if (length > 0) { 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.KeepAlive = false; } /// diff --git a/Emby.Server.Implementations/Services/HttpResult.cs b/Emby.Server.Implementations/Services/HttpResult.cs index 91314c15ae..66813f4615 100644 --- a/Emby.Server.Implementations/Services/HttpResult.cs +++ b/Emby.Server.Implementations/Services/HttpResult.cs @@ -15,7 +15,6 @@ namespace Emby.Server.Implementations.Services public HttpResult(object response, string contentType, HttpStatusCode statusCode) { this.Headers = new Dictionary(); - this.Cookies = new List(); this.Response = response; this.ContentType = contentType; @@ -26,8 +25,6 @@ namespace Emby.Server.Implementations.Services public IDictionary Headers { get; private set; } - public List Cookies { get; private set; } - public int Status { get; set; } public HttpStatusCode StatusCode @@ -40,15 +37,16 @@ namespace Emby.Server.Implementations.Services 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 (bytesResponse != null) + if (this.Response is byte[] bytesResponse) { var contentLength = bytesResponse.Length; if (response != null) + { response.SetContentLength(contentLength); + } if (contentLength > 0) { diff --git a/Emby.Server.Implementations/Services/RequestHelper.cs b/Emby.Server.Implementations/Services/RequestHelper.cs index 711ba8bbce..7d2fbc3e04 100644 --- a/Emby.Server.Implementations/Services/RequestHelper.cs +++ b/Emby.Server.Implementations/Services/RequestHelper.cs @@ -49,4 +49,4 @@ namespace Emby.Server.Implementations.Services } } -} \ No newline at end of file +} From 0abdfbb5261ff5c74dd462cd46c11439a068ef07 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 12 Jan 2019 23:31:45 +0100 Subject: [PATCH 02/21] Shutdown gracefully when recieving a termination signal --- Jellyfin.Server/Program.cs | 634 +++++++++++++++++++------------------ 1 file changed, 321 insertions(+), 313 deletions(-) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index d743157558..5dc778fdce 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -1,313 +1,321 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Security; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using Emby.Drawing; -using Emby.Drawing.Skia; -using Emby.Server.Implementations; -using Emby.Server.Implementations.EnvironmentInfo; -using Emby.Server.Implementations.IO; -using Emby.Server.Implementations.Networking; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Drawing; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.System; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using Serilog; -using Serilog.AspNetCore; -using ILogger = Microsoft.Extensions.Logging.ILogger; - -namespace Jellyfin.Server -{ - public static class Program - { - private static readonly TaskCompletionSource ApplicationTaskCompletionSource = new TaskCompletionSource(); - private static ILoggerFactory _loggerFactory; - private static ILogger _logger; - private static bool _restartOnShutdown; - - public static async Task Main(string[] args) - { - StartupOptions options = new StartupOptions(args); - Version version = Assembly.GetEntryAssembly().GetName().Version; - - if (options.ContainsOption("-v") || options.ContainsOption("--version")) - { - Console.WriteLine(version.ToString()); - return 0; - } - - ServerApplicationPaths appPaths = createApplicationPaths(options); - // $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager - Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath); - await createLogger(appPaths); - _loggerFactory = new SerilogLoggerFactory(); - _logger = _loggerFactory.CreateLogger("Main"); - - AppDomain.CurrentDomain.UnhandledException += (sender, e) - => _logger.LogCritical((Exception)e.ExceptionObject, "Unhandled Exception"); - - _logger.LogInformation("Jellyfin version: {Version}", version); - - EnvironmentInfo environmentInfo = new EnvironmentInfo(getOperatingSystem()); - ApplicationHost.LogEnvironmentInfo(_logger, appPaths, environmentInfo); - - SQLitePCL.Batteries_V2.Init(); - - // Allow all https requests - ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; }); - - var fileSystem = new ManagedFileSystem(_loggerFactory.CreateLogger("FileSystem"), environmentInfo, null, appPaths.TempDirectory, true); - - using (var appHost = new CoreAppHost( - appPaths, - _loggerFactory, - options, - fileSystem, - environmentInfo, - new NullImageEncoder(), - new SystemEvents(_loggerFactory.CreateLogger("SystemEvents")), - new NetworkManager(_loggerFactory.CreateLogger("NetworkManager"), environmentInfo))) - { - appHost.Init(); - - appHost.ImageProcessor.ImageEncoder = getImageEncoder(_logger, fileSystem, options, () => appHost.HttpClient, appPaths, environmentInfo, appHost.LocalizationManager); - - _logger.LogInformation("Running startup tasks"); - - await appHost.RunStartupTasks(); - - // TODO: read input for a stop command - // Block main thread until shutdown - await ApplicationTaskCompletionSource.Task; - - _logger.LogInformation("Disposing app host"); - } - - if (_restartOnShutdown) - { - StartNewInstance(options); - } - - return 0; - } - - private static ServerApplicationPaths createApplicationPaths(StartupOptions options) - { - string programDataPath = Environment.GetEnvironmentVariable("JELLYFIN_DATA_PATH"); - if (string.IsNullOrEmpty(programDataPath)) - { - if (options.ContainsOption("-programdata")) - { - programDataPath = options.GetOption("-programdata"); - } - else - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - programDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); - } - else - { - // $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored. - programDataPath = Environment.GetEnvironmentVariable("XDG_DATA_HOME"); - // If $XDG_DATA_HOME is either not set or empty, $HOME/.local/share should be used. - if (string.IsNullOrEmpty(programDataPath)) - { - programDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share"); - } - } - programDataPath = Path.Combine(programDataPath, "jellyfin"); - // Ensure the dir exists - Directory.CreateDirectory(programDataPath); - } - } - - string configDir = Environment.GetEnvironmentVariable("JELLYFIN_CONFIG_DIR"); - if (string.IsNullOrEmpty(configDir)) - { - if (options.ContainsOption("-configdir")) - { - configDir = options.GetOption("-configdir"); - } - else - { - // Let BaseApplicationPaths set up the default value - configDir = null; - } - } - - string logDir = Environment.GetEnvironmentVariable("JELLYFIN_LOG_DIR"); - if (string.IsNullOrEmpty(logDir)) - { - if (options.ContainsOption("-logdir")) - { - logDir = options.GetOption("-logdir"); - } - else - { - // Let BaseApplicationPaths set up the default value - logDir = null; - } - } - - string appPath = AppContext.BaseDirectory; - - return new ServerApplicationPaths(programDataPath, appPath, appPath, logDir, configDir); - } - - private static async Task createLogger(IApplicationPaths appPaths) - { - try - { - string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, "logging.json"); - - if (!File.Exists(configPath)) - { - // For some reason the csproj name is used instead of the assembly name - using (Stream rscstr = typeof(Program).Assembly - .GetManifestResourceStream("Jellyfin.Server.Resources.Configuration.logging.json")) - using (Stream fstr = File.Open(configPath, FileMode.CreateNew)) - { - await rscstr.CopyToAsync(fstr); - } - } - var configuration = new ConfigurationBuilder() - .SetBasePath(appPaths.ConfigurationDirectoryPath) - .AddJsonFile("logging.json") - .AddEnvironmentVariables("JELLYFIN_") - .Build(); - - // Serilog.Log is used by SerilogLoggerFactory when no logger is specified - Serilog.Log.Logger = new LoggerConfiguration() - .ReadFrom.Configuration(configuration) - .Enrich.FromLogContext() - .CreateLogger(); - } - catch (Exception ex) - { - Serilog.Log.Logger = new LoggerConfiguration() - .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}") - .WriteTo.Async(x => x.File( - Path.Combine(appPaths.LogDirectoryPath, "log_.log"), - rollingInterval: RollingInterval.Day, - outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}")) - .Enrich.FromLogContext() - .CreateLogger(); - - Serilog.Log.Logger.Fatal(ex, "Failed to create/read logger configuration"); - } - } - - public static IImageEncoder getImageEncoder( - ILogger logger, - IFileSystem fileSystem, - StartupOptions startupOptions, - Func httpClient, - IApplicationPaths appPaths, - IEnvironmentInfo environment, - ILocalizationManager localizationManager) - { - try - { - return new SkiaEncoder(logger, appPaths, httpClient, fileSystem, localizationManager); - } - catch (Exception ex) - { - logger.LogInformation(ex, "Skia not available. Will fallback to NullIMageEncoder. {0}"); - } - - return new NullImageEncoder(); - } - - private static MediaBrowser.Model.System.OperatingSystem getOperatingSystem() { - switch (Environment.OSVersion.Platform) - { - case PlatformID.MacOSX: - return MediaBrowser.Model.System.OperatingSystem.OSX; - case PlatformID.Win32NT: - return MediaBrowser.Model.System.OperatingSystem.Windows; - case PlatformID.Unix: - default: - { - string osDescription = RuntimeInformation.OSDescription; - if (osDescription.Contains("linux", StringComparison.OrdinalIgnoreCase)) - { - return MediaBrowser.Model.System.OperatingSystem.Linux; - } - else if (osDescription.Contains("darwin", StringComparison.OrdinalIgnoreCase)) - { - return MediaBrowser.Model.System.OperatingSystem.OSX; - } - else if (osDescription.Contains("bsd", StringComparison.OrdinalIgnoreCase)) - { - return MediaBrowser.Model.System.OperatingSystem.BSD; - } - throw new Exception($"Can't resolve OS with description: '{osDescription}'"); - } - } - } - - public static void Shutdown() - { - ApplicationTaskCompletionSource.SetResult(true); - } - - public static void Restart() - { - _restartOnShutdown = true; - - Shutdown(); - } - - private static void StartNewInstance(StartupOptions startupOptions) - { - _logger.LogInformation("Starting new instance"); - - string module = startupOptions.GetOption("-restartpath"); - - if (string.IsNullOrWhiteSpace(module)) - { - module = Environment.GetCommandLineArgs().First(); - } - - string commandLineArgsString; - - if (startupOptions.ContainsOption("-restartargs")) - { - commandLineArgsString = startupOptions.GetOption("-restartargs") ?? string.Empty; - } - else - { - commandLineArgsString = string .Join(" ", - Environment.GetCommandLineArgs() - .Skip(1) - .Select(NormalizeCommandLineArgument) - ); - } - - _logger.LogInformation("Executable: {0}", module); - _logger.LogInformation("Arguments: {0}", commandLineArgsString); - - Process.Start(module, commandLineArgsString); - } - - private static string NormalizeCommandLineArgument(string arg) - { - if (!arg.Contains(" ", StringComparison.OrdinalIgnoreCase)) - { - return arg; - } - - return "\"" + arg + "\""; - } - } -} +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Security; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.Loader; +using System.Threading.Tasks; +using Emby.Drawing; +using Emby.Drawing.Skia; +using Emby.Server.Implementations; +using Emby.Server.Implementations.EnvironmentInfo; +using Emby.Server.Implementations.IO; +using Emby.Server.Implementations.Networking; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Drawing; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.System; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Serilog; +using Serilog.AspNetCore; +using ILogger = Microsoft.Extensions.Logging.ILogger; + +namespace Jellyfin.Server +{ + public static class Program + { + private static readonly TaskCompletionSource ApplicationTaskCompletionSource = new TaskCompletionSource(); + private static ILoggerFactory _loggerFactory; + private static ILogger _logger; + private static bool _restartOnShutdown; + + public static async Task Main(string[] args) + { + StartupOptions options = new StartupOptions(args); + Version version = Assembly.GetEntryAssembly().GetName().Version; + + if (options.ContainsOption("-v") || options.ContainsOption("--version")) + { + Console.WriteLine(version.ToString()); + return 0; + } + + ServerApplicationPaths appPaths = createApplicationPaths(options); + // $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager + Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath); + await createLogger(appPaths); + _loggerFactory = new SerilogLoggerFactory(); + _logger = _loggerFactory.CreateLogger("Main"); + + AppDomain.CurrentDomain.UnhandledException += (sender, e) + => _logger.LogCritical((Exception)e.ExceptionObject, "Unhandled Exception"); + + // Register a SIGTERM handler + AppDomain.CurrentDomain.ProcessExit += (sender, e) => + { + _logger.LogInformation("Received a SIGTERM signal, shutting down"); + Shutdown(); + }; + + _logger.LogInformation("Jellyfin version: {Version}", version); + + EnvironmentInfo environmentInfo = new EnvironmentInfo(getOperatingSystem()); + ApplicationHost.LogEnvironmentInfo(_logger, appPaths, environmentInfo); + + SQLitePCL.Batteries_V2.Init(); + + // Allow all https requests + ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; }); + + var fileSystem = new ManagedFileSystem(_loggerFactory.CreateLogger("FileSystem"), environmentInfo, null, appPaths.TempDirectory, true); + + using (var appHost = new CoreAppHost( + appPaths, + _loggerFactory, + options, + fileSystem, + environmentInfo, + new NullImageEncoder(), + new SystemEvents(_loggerFactory.CreateLogger("SystemEvents")), + new NetworkManager(_loggerFactory.CreateLogger("NetworkManager"), environmentInfo))) + { + appHost.Init(); + + appHost.ImageProcessor.ImageEncoder = getImageEncoder(_logger, fileSystem, options, () => appHost.HttpClient, appPaths, environmentInfo, appHost.LocalizationManager); + + _logger.LogInformation("Running startup tasks"); + + await appHost.RunStartupTasks(); + + // TODO: read input for a stop command + // Block main thread until shutdown + await ApplicationTaskCompletionSource.Task; + + _logger.LogInformation("Disposing app host"); + } + + if (_restartOnShutdown) + { + StartNewInstance(options); + } + + return 0; + } + + private static ServerApplicationPaths createApplicationPaths(StartupOptions options) + { + string programDataPath = Environment.GetEnvironmentVariable("JELLYFIN_DATA_PATH"); + if (string.IsNullOrEmpty(programDataPath)) + { + if (options.ContainsOption("-programdata")) + { + programDataPath = options.GetOption("-programdata"); + } + else + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + programDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); + } + else + { + // $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored. + programDataPath = Environment.GetEnvironmentVariable("XDG_DATA_HOME"); + // If $XDG_DATA_HOME is either not set or empty, $HOME/.local/share should be used. + if (string.IsNullOrEmpty(programDataPath)) + { + programDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share"); + } + } + programDataPath = Path.Combine(programDataPath, "jellyfin"); + // Ensure the dir exists + Directory.CreateDirectory(programDataPath); + } + } + + string configDir = Environment.GetEnvironmentVariable("JELLYFIN_CONFIG_DIR"); + if (string.IsNullOrEmpty(configDir)) + { + if (options.ContainsOption("-configdir")) + { + configDir = options.GetOption("-configdir"); + } + else + { + // Let BaseApplicationPaths set up the default value + configDir = null; + } + } + + string logDir = Environment.GetEnvironmentVariable("JELLYFIN_LOG_DIR"); + if (string.IsNullOrEmpty(logDir)) + { + if (options.ContainsOption("-logdir")) + { + logDir = options.GetOption("-logdir"); + } + else + { + // Let BaseApplicationPaths set up the default value + logDir = null; + } + } + + string appPath = AppContext.BaseDirectory; + + return new ServerApplicationPaths(programDataPath, appPath, appPath, logDir, configDir); + } + + private static async Task createLogger(IApplicationPaths appPaths) + { + try + { + string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, "logging.json"); + + if (!File.Exists(configPath)) + { + // For some reason the csproj name is used instead of the assembly name + using (Stream rscstr = typeof(Program).Assembly + .GetManifestResourceStream("Jellyfin.Server.Resources.Configuration.logging.json")) + using (Stream fstr = File.Open(configPath, FileMode.CreateNew)) + { + await rscstr.CopyToAsync(fstr); + } + } + var configuration = new ConfigurationBuilder() + .SetBasePath(appPaths.ConfigurationDirectoryPath) + .AddJsonFile("logging.json") + .AddEnvironmentVariables("JELLYFIN_") + .Build(); + + // Serilog.Log is used by SerilogLoggerFactory when no logger is specified + Serilog.Log.Logger = new LoggerConfiguration() + .ReadFrom.Configuration(configuration) + .Enrich.FromLogContext() + .CreateLogger(); + } + catch (Exception ex) + { + Serilog.Log.Logger = new LoggerConfiguration() + .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}") + .WriteTo.Async(x => x.File( + Path.Combine(appPaths.LogDirectoryPath, "log_.log"), + rollingInterval: RollingInterval.Day, + outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}")) + .Enrich.FromLogContext() + .CreateLogger(); + + Serilog.Log.Logger.Fatal(ex, "Failed to create/read logger configuration"); + } + } + + public static IImageEncoder getImageEncoder( + ILogger logger, + IFileSystem fileSystem, + StartupOptions startupOptions, + Func httpClient, + IApplicationPaths appPaths, + IEnvironmentInfo environment, + ILocalizationManager localizationManager) + { + try + { + return new SkiaEncoder(logger, appPaths, httpClient, fileSystem, localizationManager); + } + catch (Exception ex) + { + logger.LogInformation(ex, "Skia not available. Will fallback to NullIMageEncoder. {0}"); + } + + return new NullImageEncoder(); + } + + private static MediaBrowser.Model.System.OperatingSystem getOperatingSystem() { + switch (Environment.OSVersion.Platform) + { + case PlatformID.MacOSX: + return MediaBrowser.Model.System.OperatingSystem.OSX; + case PlatformID.Win32NT: + return MediaBrowser.Model.System.OperatingSystem.Windows; + case PlatformID.Unix: + default: + { + string osDescription = RuntimeInformation.OSDescription; + if (osDescription.Contains("linux", StringComparison.OrdinalIgnoreCase)) + { + return MediaBrowser.Model.System.OperatingSystem.Linux; + } + else if (osDescription.Contains("darwin", StringComparison.OrdinalIgnoreCase)) + { + return MediaBrowser.Model.System.OperatingSystem.OSX; + } + else if (osDescription.Contains("bsd", StringComparison.OrdinalIgnoreCase)) + { + return MediaBrowser.Model.System.OperatingSystem.BSD; + } + throw new Exception($"Can't resolve OS with description: '{osDescription}'"); + } + } + } + + public static void Shutdown() + { + ApplicationTaskCompletionSource.SetResult(true); + } + + public static void Restart() + { + _restartOnShutdown = true; + + Shutdown(); + } + + private static void StartNewInstance(StartupOptions startupOptions) + { + _logger.LogInformation("Starting new instance"); + + string module = startupOptions.GetOption("-restartpath"); + + if (string.IsNullOrWhiteSpace(module)) + { + module = Environment.GetCommandLineArgs().First(); + } + + string commandLineArgsString; + + if (startupOptions.ContainsOption("-restartargs")) + { + commandLineArgsString = startupOptions.GetOption("-restartargs") ?? string.Empty; + } + else + { + commandLineArgsString = string .Join(" ", + Environment.GetCommandLineArgs() + .Skip(1) + .Select(NormalizeCommandLineArgument) + ); + } + + _logger.LogInformation("Executable: {0}", module); + _logger.LogInformation("Arguments: {0}", commandLineArgsString); + + Process.Start(module, commandLineArgsString); + } + + private static string NormalizeCommandLineArgument(string arg) + { + if (!arg.Contains(" ", StringComparison.OrdinalIgnoreCase)) + { + return arg; + } + + return "\"" + arg + "\""; + } + } +} From debb8e56fd32184c9d968e0450ceca437dea7d11 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 12 Jan 2019 23:58:58 +0100 Subject: [PATCH 03/21] Handle Ctrl+C and Ctrl+Break --- Jellyfin.Server/Program.cs | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 5dc778fdce..7079ca7f5c 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -6,7 +6,7 @@ using System.Net; using System.Net.Security; using System.Reflection; using System.Runtime.InteropServices; -using System.Runtime.Loader; +using System.Threading; using System.Threading.Tasks; using Emby.Drawing; using Emby.Drawing.Skia; @@ -30,12 +30,12 @@ namespace Jellyfin.Server { public static class Program { - private static readonly TaskCompletionSource ApplicationTaskCompletionSource = new TaskCompletionSource(); - private static ILoggerFactory _loggerFactory; + private static readonly CancellationTokenSource _tokenSource = new CancellationTokenSource(); + private static readonly ILoggerFactory _loggerFactory = new SerilogLoggerFactory(); private static ILogger _logger; private static bool _restartOnShutdown; - public static async Task Main(string[] args) + public static async Task Main(string[] args) { StartupOptions options = new StartupOptions(args); Version version = Assembly.GetEntryAssembly().GetName().Version; @@ -43,22 +43,33 @@ namespace Jellyfin.Server if (options.ContainsOption("-v") || options.ContainsOption("--version")) { Console.WriteLine(version.ToString()); - return 0; } ServerApplicationPaths appPaths = createApplicationPaths(options); // $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath); await createLogger(appPaths); - _loggerFactory = new SerilogLoggerFactory(); _logger = _loggerFactory.CreateLogger("Main"); AppDomain.CurrentDomain.UnhandledException += (sender, e) => _logger.LogCritical((Exception)e.ExceptionObject, "Unhandled Exception"); + // Intercept Ctrl+C and Ctrl+Break + Console.CancelKeyPress += (sender, e) => + { + e.Cancel = true; + _logger.LogInformation("Ctrl+C, shutting down"); + Environment.ExitCode = 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"); Shutdown(); }; @@ -95,7 +106,8 @@ namespace Jellyfin.Server // TODO: read input for a stop command // Block main thread until shutdown - await ApplicationTaskCompletionSource.Task; + await Task.Delay(-1, _tokenSource.Token) + .ContinueWith(delegate{}); // Don't throw on cancellation _logger.LogInformation("Disposing app host"); } @@ -104,8 +116,6 @@ namespace Jellyfin.Server { StartNewInstance(options); } - - return 0; } private static ServerApplicationPaths createApplicationPaths(StartupOptions options) @@ -266,7 +276,10 @@ namespace Jellyfin.Server public static void Shutdown() { - ApplicationTaskCompletionSource.SetResult(true); + if (!_tokenSource.IsCancellationRequested) + { + _tokenSource.Cancel(); + } } public static void Restart() From bf4de012da46a7ee875c5439c4a0f1355d8978d2 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 13 Jan 2019 00:55:37 +0100 Subject: [PATCH 04/21] Catch exception instead of masking it --- Jellyfin.Server/Program.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 7079ca7f5c..d370cdc691 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -105,9 +105,16 @@ namespace Jellyfin.Server await appHost.RunStartupTasks(); // TODO: read input for a stop command - // Block main thread until shutdown - await Task.Delay(-1, _tokenSource.Token) - .ContinueWith(delegate{}); // Don't throw on cancellation + + try + { + // Block main thread until shutdown + await Task.Delay(-1, _tokenSource.Token); + } + catch (TaskCanceledException) + { + // Don't throw on cancellation + } _logger.LogInformation("Disposing app host"); } From 50c127fd9af0078556bdf74b6a96f0d5e0478d5c Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 13 Jan 2019 01:05:25 +0100 Subject: [PATCH 05/21] Add proper exit codes --- Jellyfin.Server/Program.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index d370cdc691..acbe5c7144 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -57,9 +57,13 @@ namespace Jellyfin.Server // 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 = 2; + Environment.ExitCode = 128 + 2; Shutdown(); }; @@ -71,6 +75,7 @@ namespace Jellyfin.Server return; // Already shutting down } _logger.LogInformation("Received a SIGTERM signal, shutting down"); + Environment.ExitCode = 128 + 15; Shutdown(); }; From 351bac44c5b629ef40102f6219572c8bde58b2e4 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 14 Jan 2019 22:02:51 +0100 Subject: [PATCH 06/21] Add nuget info to Emby.Naming --- Emby.Naming/Emby.Naming.csproj | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index 360ffaabe4..de40a39d20 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -5,12 +5,19 @@ false + + Jellyfin Contributors + Jellyfin.Naming + https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + https://github.com/jellyfin/jellyfin + + - + - + From bddfca621529ff894d44e07dc4ca546dfce75e2d Mon Sep 17 00:00:00 2001 From: hawken Date: Tue, 8 Jan 2019 22:48:08 +0000 Subject: [PATCH 07/21] Fix potential problem where aspect ratio would be incorrectly calculated --- MediaBrowser.Controller/Entities/Photo.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MediaBrowser.Controller/Entities/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs index 662aa42495..074a1f4f76 100644 --- a/MediaBrowser.Controller/Entities/Photo.cs +++ b/MediaBrowser.Controller/Entities/Photo.cs @@ -79,8 +79,7 @@ namespace MediaBrowser.Controller.Entities } } - width /= Height.Value; - return width; + return width / height; } return base.GetDefaultPrimaryImageAspectRatio(); From 036dfc416878b401b379446add505ea47df2096a Mon Sep 17 00:00:00 2001 From: Joshua Boniface Date: Wed, 9 Jan 2019 12:09:40 -0500 Subject: [PATCH 08/21] Update TheMovieDB API key to our own --- MediaBrowser.Providers/Movies/MovieDbProvider.cs | 2 +- MediaBrowser.Providers/Movies/MovieDbSearch.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs index 1ebf56e23c..8465634bcf 100644 --- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs @@ -168,7 +168,7 @@ namespace MediaBrowser.Providers.Movies private const string TmdbConfigUrl = BaseMovieDbUrl + "3/configuration?api_key={0}"; private const string GetMovieInfo3 = BaseMovieDbUrl + @"3/movie/{0}?api_key={1}&append_to_response=casts,releases,images,keywords,trailers"; - internal static string ApiKey = "f6bd687ffa63cd282b6ff2c6877f2669"; + internal static string ApiKey = "4219e299c89411838049ab0dab19ebd5"; internal static string AcceptHeader = "application/json,image/*"; /// diff --git a/MediaBrowser.Providers/Movies/MovieDbSearch.cs b/MediaBrowser.Providers/Movies/MovieDbSearch.cs index b332207e15..f92417b168 100644 --- a/MediaBrowser.Providers/Movies/MovieDbSearch.cs +++ b/MediaBrowser.Providers/Movies/MovieDbSearch.cs @@ -20,7 +20,7 @@ namespace MediaBrowser.Providers.Movies private static readonly CultureInfo EnUs = new CultureInfo("en-US"); private const string Search3 = MovieDbProvider.BaseMovieDbUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}"; - internal static string ApiKey = "f6bd687ffa63cd282b6ff2c6877f2669"; + internal static string ApiKey = "4219e299c89411838049ab0dab19ebd5"; internal static string AcceptHeader = "application/json,image/*"; private readonly ILogger _logger; From 18fae029d643d0a3031de1804406afbad43a8d68 Mon Sep 17 00:00:00 2001 From: Joshua Boniface Date: Wed, 9 Jan 2019 12:19:10 -0500 Subject: [PATCH 09/21] Update TheAudioDB API key to our own --- MediaBrowser.Providers/Music/AudioDbArtistProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs b/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs index e0d193ff16..1769bc1aeb 100644 --- a/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs +++ b/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs @@ -27,7 +27,7 @@ namespace MediaBrowser.Providers.Music public static AudioDbArtistProvider Current; - private const string ApiKey = "49jhsf8248yfahka89724011"; + private const string ApiKey = "195003"; public const string BaseUrl = "https://www.theaudiodb.com/api/v1/json/" + ApiKey; public AudioDbArtistProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClient httpClient, IJsonSerializer json) From 44ec5d263119ba43942b6f7866f5bb204b2d0bcc Mon Sep 17 00:00:00 2001 From: Joshua Boniface Date: Wed, 9 Jan 2019 12:24:16 -0500 Subject: [PATCH 10/21] Update FanArt API key to our own --- MediaBrowser.Providers/Music/FanArtArtistProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs index 99a717ca18..041a2690f8 100644 --- a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs +++ b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs @@ -28,7 +28,7 @@ namespace MediaBrowser.Providers.Music { public class FanartArtistProvider : IRemoteImageProvider, IHasOrder { - internal const string ApiKey = "5c6b04c68e904cfed1e6cbc9a9e683d4"; + internal const string ApiKey = "184e1a2b1fe3b94935365411f919f638"; private const string FanArtBaseUrl = "https://webservice.fanart.tv/v3.1/music/{1}?api_key={0}"; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); From a90a85c3066e85263cb63b800cc6b61a005516cf Mon Sep 17 00:00:00 2001 From: Joshua Boniface Date: Wed, 9 Jan 2019 12:44:48 -0500 Subject: [PATCH 11/21] Update TheTVDB API key to our own --- MediaBrowser.Controller/Library/TVUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/Library/TVUtils.cs b/MediaBrowser.Controller/Library/TVUtils.cs index dc95ba1127..162ebc75e6 100644 --- a/MediaBrowser.Controller/Library/TVUtils.cs +++ b/MediaBrowser.Controller/Library/TVUtils.cs @@ -11,7 +11,7 @@ namespace MediaBrowser.Controller.Library /// /// The TVDB API key /// - public static readonly string TvdbApiKey = "B89CE93890E9419B"; + public static readonly string TvdbApiKey = "OG4V3YJ3FAP7FP2K"; public static readonly string TvdbBaseUrl = "https://www.thetvdb.com/"; /// /// The banner URL From 478306ca1518d15e82a2e01922fa7c373d2169cb Mon Sep 17 00:00:00 2001 From: Anthony Lavado Date: Thu, 10 Jan 2019 13:26:52 -0500 Subject: [PATCH 12/21] Update OpenSubtitles User Agent to our own --- MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs b/MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs index 2d29f29e3d..b62fa83985 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs @@ -46,7 +46,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles _config.NamedConfigurationUpdating += _config_NamedConfigurationUpdating; Utilities.HttpClient = httpClient; - OpenSubtitles.SetUserAgent("mediabrowser.tv"); + OpenSubtitles.SetUserAgent("jellyfin"); } private const string PasswordHashPrefix = "h:"; From ce0c928ddb63b983bef3788c2bf0596558ee96f7 Mon Sep 17 00:00:00 2001 From: Joshua Boniface Date: Thu, 10 Jan 2019 15:16:01 -0500 Subject: [PATCH 13/21] Update Omdb API key to our own --- MediaBrowser.Providers/Omdb/OmdbImageProvider.cs | 2 +- MediaBrowser.Providers/Omdb/OmdbProvider.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs index 1eb9b31039..a138d4b8c4 100644 --- a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs @@ -69,7 +69,7 @@ namespace MediaBrowser.Providers.Omdb list.Add(new RemoteImageInfo { ProviderName = Name, - Url = string.Format("https://img.omdbapi.com/?i={0}&apikey=fe53f97e", imdbId) + Url = string.Format("https://img.omdbapi.com/?i={0}&apikey=2c9d9507", imdbId) }); } } diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs index bb4624b5c0..e8e1e3a293 100644 --- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs @@ -270,7 +270,7 @@ namespace MediaBrowser.Providers.Omdb public static string GetOmdbUrl(string query, IApplicationHost appHost, CancellationToken cancellationToken) { - const string url = "https://www.omdbapi.com?apikey=fe53f97e"; + const string url = "https://www.omdbapi.com?apikey=2c9d9507"; if (string.IsNullOrWhiteSpace(query)) { From 58af3d4f5c3d850fd5068020090206708ff92b2a Mon Sep 17 00:00:00 2001 From: Joshua Boniface Date: Thu, 10 Jan 2019 15:16:51 -0500 Subject: [PATCH 14/21] Update Fanart commented-out API key to our own --- MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs | 1 - MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs index 6d489498e0..9ac9931186 100644 --- a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs +++ b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs @@ -37,7 +37,6 @@ namespace MediaBrowser.Providers.Movies private readonly IJsonSerializer _json; private const string FanArtBaseUrl = "https://webservice.fanart.tv/v3/movies/{1}?api_key={0}"; - // &client_key=52c813aa7b8c8b3bb87f4797532a2f8c internal static FanartMovieImageProvider Current; diff --git a/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs b/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs index 3ca0137300..62fd82d350 100644 --- a/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs @@ -36,7 +36,6 @@ namespace MediaBrowser.Providers.TV private readonly IJsonSerializer _json; private const string FanArtBaseUrl = "https://webservice.fanart.tv/v3/tv/{1}?api_key={0}"; - // &client_key=52c813aa7b8c8b3bb87f4797532a2f8c internal static FanartSeriesProvider Current { get; private set; } From 30fef791209e04a79cf988046194aab6956f9a1b Mon Sep 17 00:00:00 2001 From: cvium Date: Thu, 10 Jan 2019 15:30:26 +0100 Subject: [PATCH 15/21] Change ItemId to Guid like all the others --- MediaBrowser.Controller/Providers/RemoteSearchQuery.cs | 4 +++- MediaBrowser.Providers/Manager/ProviderManager.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs b/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs index 77cf9e2559..9fe6f96bc3 100644 --- a/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs +++ b/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs @@ -1,3 +1,5 @@ +using System; + namespace MediaBrowser.Controller.Providers { public class RemoteSearchQuery @@ -5,7 +7,7 @@ namespace MediaBrowser.Controller.Providers { public T SearchInfo { get; set; } - public string ItemId { get; set; } + public Guid ItemId { get; set; } /// /// If set will only search within the given provider diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 8eed5e6266..ae3788f342 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -156,7 +156,7 @@ namespace MediaBrowser.Providers.Manager }).ConfigureAwait(false)) { - // Workaround for tvheadend channel icons + // Workaround for tvheadend channel icons // TODO: Isolate this hack into the tvh plugin if (string.IsNullOrEmpty(response.ContentType)) { From 5348fddc9eb9584cbec4449273c0c356becfe354 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 12 Jan 2019 22:25:55 +0100 Subject: [PATCH 16/21] Don't print stacktrace when failing to bind to 1900 --- RSSDP/SsdpCommunicationsServer.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index 65ec0ca714..fb13298eef 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net; using System.Net.Http; +using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -129,6 +129,10 @@ namespace Rssdp.Infrastructure { _BroadcastListenSocket = ListenForBroadcastsAsync(); } + catch (SocketException) + { + _logger.LogError("Failed to bind to port 1900. DLNA will be unavailable"); + } catch (Exception ex) { _logger.LogError(ex, "Error in BeginListeningForBroadcasts"); @@ -148,7 +152,7 @@ namespace Rssdp.Infrastructure { if (_BroadcastListenSocket != null) { - _logger.LogInformation("{0} disposing _BroadcastListenSocket.", GetType().Name); + _logger.LogInformation("{0} disposing _BroadcastListenSocket", GetType().Name); _BroadcastListenSocket.Dispose(); _BroadcastListenSocket = null; } From 0ca76597e507bae3ee8713ccf55ff530a7a98978 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Sun, 13 Jan 2019 01:30:43 +0100 Subject: [PATCH 17/21] Add exception message to log. Output will be something line: `Failed to bind to port 1900: Address already in use. DLNA will be unavailable` --- RSSDP/SsdpCommunicationsServer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index fb13298eef..f526673660 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -129,9 +129,9 @@ namespace Rssdp.Infrastructure { _BroadcastListenSocket = ListenForBroadcastsAsync(); } - catch (SocketException) + catch (SocketException ex) { - _logger.LogError("Failed to bind to port 1900. DLNA will be unavailable"); + _logger.LogError("Failed to bind to port 1900: {Message}. DLNA will be unavailable", ex.Message); } catch (Exception ex) { From 3051b2a1e431297394b9f8430c6422be63438bce Mon Sep 17 00:00:00 2001 From: Joshua Boniface Date: Tue, 15 Jan 2019 19:19:07 -0500 Subject: [PATCH 18/21] Bump version for 10.0.2 --- SharedVersion.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SharedVersion.cs b/SharedVersion.cs index 0fbf7b9258..963ffad230 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("10.0.1")] +[assembly: AssemblyVersion("10.0.2")] From 09606d0353ac11b54fe59e6991ff61b301d4410f Mon Sep 17 00:00:00 2001 From: Joshua Boniface Date: Tue, 15 Jan 2019 19:52:17 -0500 Subject: [PATCH 19/21] Update Debian version --- debian/changelog | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/debian/changelog b/debian/changelog index 685d31a5eb..f7a1994b76 100644 --- a/debian/changelog +++ b/debian/changelog @@ -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 * Hotfix release, corrects several small bugs from 10.0.0 From d0980f0da57c14966e9e90b00f1880b1b82f4fc8 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Wed, 16 Jan 2019 19:13:13 +0100 Subject: [PATCH 20/21] Update HttpListenerHost.cs --- .../HttpServer/HttpListenerHost.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index fb5f53367f..6b4f2bced5 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -81,13 +81,7 @@ namespace Emby.Server.Implementations.HttpServer public string GlobalResponse { get; set; } - protected ILogger Logger - { - get - { - return _logger; - } - } + protected ILogger Logger => _logger; public object CreateInstance(Type type) { From fe4c3fddb4627bddfffd99729d90f99c3c12449b Mon Sep 17 00:00:00 2001 From: Sparky Date: Thu, 17 Jan 2019 18:59:47 -0500 Subject: [PATCH 21/21] Slim down docker image After installing, remove package caches and clean up after installation of stuff. --- Dockerfile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 02332d40b4..f21249e54e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,5 +27,9 @@ COPY --from=ffmpeg /ffmpeg-bin/* /usr/bin/ EXPOSE 8096 VOLUME /config /media 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