diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index a4a24dda0a..060898684e 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -416,7 +416,6 @@ namespace Emby.Server.Implementations ConfigurationManager = GetConfigurationManager(); - // Initialize this early in case the -v command line option is used Logger = LoggerFactory.CreateLogger("App"); StartupOptions = options; @@ -467,7 +466,7 @@ namespace Emby.Server.Implementations { get { - return _version ?? (_version = GetType().GetTypeInfo().Assembly.GetName().Version); + return _version ?? (_version = typeof(ApplicationHost).Assembly.GetName().Version); } } @@ -1772,7 +1771,7 @@ namespace Emby.Server.Implementations return list.ToList(); } - protected abstract List GetAssembliesWithPartsInternal(); + protected abstract IEnumerable GetAssembliesWithPartsInternal(); /// /// Gets the plugin assemblies. diff --git a/Emby.Server.Implementations/EnvironmentInfo/EnvironmentInfo.cs b/Emby.Server.Implementations/EnvironmentInfo/EnvironmentInfo.cs index 7655051095..ad941de550 100644 --- a/Emby.Server.Implementations/EnvironmentInfo/EnvironmentInfo.cs +++ b/Emby.Server.Implementations/EnvironmentInfo/EnvironmentInfo.cs @@ -1,12 +1,12 @@ using System; using System.IO; using MediaBrowser.Model.System; +using System.Runtime.InteropServices; namespace Emby.Server.Implementations.EnvironmentInfo { public class EnvironmentInfo : IEnvironmentInfo { - private Architecture? _customArchitecture; private MediaBrowser.Model.System.OperatingSystem? _customOperatingSystem; public virtual MediaBrowser.Model.System.OperatingSystem OperatingSystem @@ -60,22 +60,7 @@ namespace Emby.Server.Implementations.EnvironmentInfo } } - public Architecture SystemArchitecture - { - get - { - if (_customArchitecture.HasValue) - { - return _customArchitecture.Value; - } - - return Environment.Is64BitOperatingSystem ? MediaBrowser.Model.System.Architecture.X64 : MediaBrowser.Model.System.Architecture.X86; - } - set - { - _customArchitecture = value; - } - } + public Architecture SystemArchitecture { get; set; } public string GetEnvironmentVariable(string name) { diff --git a/Emby.Server.Implementations/StartupOptions.cs b/Emby.Server.Implementations/StartupOptions.cs index 159c36248c..2114d85bf8 100644 --- a/Emby.Server.Implementations/StartupOptions.cs +++ b/Emby.Server.Implementations/StartupOptions.cs @@ -1,33 +1,30 @@ using System; -using System.Collections.Generic; using System.Linq; namespace Emby.Server.Implementations { public class StartupOptions { - private readonly List _options; + private readonly string[] _options; public StartupOptions(string[] commandLineArgs) { - _options = commandLineArgs.ToList(); + _options = commandLineArgs; } public bool ContainsOption(string option) - { - return _options.Contains(option, StringComparer.OrdinalIgnoreCase); - } + => _options.Contains(option, StringComparer.OrdinalIgnoreCase); public string GetOption(string name) { - var index = _options.IndexOf(name); + int index = Array.IndexOf(_options, name); - if (index != -1) + if (index == -1) { - return _options.ElementAtOrDefault(index + 1); + return null; } - return null; + return _options.ElementAtOrDefault(index + 1); } } } diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs new file mode 100644 index 0000000000..2fb106b3c0 --- /dev/null +++ b/Jellyfin.Server/CoreAppHost.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.Reflection; +using Emby.Server.Implementations; +using Emby.Server.Implementations.HttpServer; +using Jellyfin.SocketSharp; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.System; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Server +{ + public class CoreAppHost : ApplicationHost + { + public CoreAppHost(ServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory, StartupOptions options, IFileSystem fileSystem, IPowerManagement powerManagement, string releaseAssetFilename, IEnvironmentInfo environmentInfo, MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, ISystemEvents systemEvents, MediaBrowser.Common.Net.INetworkManager networkManager) + : base(applicationPaths, loggerFactory, options, fileSystem, powerManagement, releaseAssetFilename, environmentInfo, imageEncoder, systemEvents, networkManager) + { + } + + public override bool CanSelfRestart + { + get + { + // A restart script must be provided + return StartupOptions.ContainsOption("-restartpath"); + } + } + + protected override void RestartInternal() => Program.Restart(); + + protected override IEnumerable GetAssembliesWithPartsInternal() + => new [] { typeof(CoreAppHost).Assembly }; + + protected override void ShutdownInternal() => Program.Shutdown(); + + protected override bool SupportsDualModeSockets + { + get + { + return true; + } + } + + protected override IHttpListener CreateHttpListener() + => new WebSocketSharpListener( + Logger, + Certificate, + StreamHelper, + TextEncoding, + NetworkManager, + SocketFactory, + CryptographyProvider, + SupportsDualModeSockets, + FileSystemManager, + EnvironmentInfo + ); + } +} diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj new file mode 100644 index 0000000000..cf83967855 --- /dev/null +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -0,0 +1,39 @@ + + + + Exe + netcoreapp2.1 + false + + + + + latest + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MediaBrowser.Server.Mono/Native/PowerManagement.cs b/Jellyfin.Server/PowerManagement.cs similarity index 78% rename from MediaBrowser.Server.Mono/Native/PowerManagement.cs rename to Jellyfin.Server/PowerManagement.cs index 219a69d652..c27c518937 100644 --- a/MediaBrowser.Server.Mono/Native/PowerManagement.cs +++ b/Jellyfin.Server/PowerManagement.cs @@ -1,21 +1,23 @@ -using System; +using System; using MediaBrowser.Model.System; -namespace MediaBrowser.Server.Mono.Native +namespace Jellyfin.Server.Native { public class PowerManagement : IPowerManagement { public void PreventSystemStandby() { + } public void AllowSystemStandby() { + } public void ScheduleWake(DateTime wakeTimeUtc, string displayName) { - // nothing to Do + } } } diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs new file mode 100644 index 0000000000..b768e10323 --- /dev/null +++ b/Jellyfin.Server/Program.cs @@ -0,0 +1,302 @@ +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.ImageMagick; +using Emby.Drawing.Skia; +using Emby.Server.Implementations; +using Emby.Server.Implementations.EnvironmentInfo; +using Emby.Server.Implementations.IO; +using Emby.Server.Implementations.Networking; +using Jellyfin.Server.Native; +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); + + Assembly entryAssembly = Assembly.GetEntryAssembly(); + + if (options.ContainsOption("-v") || options.ContainsOption("--version")) + { + Console.WriteLine(entryAssembly.GetName().Version.ToString()); + return 0; + } + + string dataPath = options.GetOption("-programdata"); + string binPath = entryAssembly.Location; + ServerApplicationPaths appPaths = CreateApplicationPaths(binPath, dataPath); + + await createLogger(appPaths); + _loggerFactory = new SerilogLoggerFactory(); + _logger = _loggerFactory.CreateLogger("Main"); + + AppDomain.CurrentDomain.UnhandledException += (sender, e) + => _logger.LogCritical((Exception)e.ExceptionObject, "Unhandled Exception");; + + ApplicationHost.LogEnvironmentInfo(_logger, appPaths, true); + + SQLitePCL.Batteries_V2.Init(); + + // Allow all https requests + ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; }); + + EnvironmentInfo environmentInfo = getEnvironmentInfo(); + var fileSystem = new ManagedFileSystem(_loggerFactory.CreateLogger("FileSystem"), environmentInfo, null, appPaths.TempDirectory, true); + + using (var appHost = new CoreAppHost( + appPaths, + _loggerFactory, + options, + fileSystem, + new PowerManagement(), + "embyserver-mono_{version}.zip", + 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(string applicationPath, string programDataPath) + { + if (string.IsNullOrEmpty(programDataPath)) + { + 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"); + } + + string appFolderPath = Path.GetDirectoryName(applicationPath); + + return new ServerApplicationPaths(programDataPath, appFolderPath, appFolderPath); + } + + private static async Task createLogger(IApplicationPaths appPaths) + { + string logDir = Environment.GetEnvironmentVariable("JELLYFIN_LOG_DIR"); + if (string.IsNullOrEmpty(logDir)){ + logDir = Path.Combine(appPaths.ProgramDataPath, "logs"); + Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", logDir); + } + try + { + string path = Path.Combine(appPaths.ConfigurationDirectoryPath, "logging.json"); + + if (!File.Exists(path)) + { + // For some reason the csproj name is used instead of the assembly name + using (Stream rscstr = typeof(Program).Assembly + .GetManifestResourceStream("EmbyServer.Resources.Configuration.logging.json")) + using (Stream fstr = File.Open(path, 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.File( + Path.Combine(logDir, "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) + { + if (!startupOptions.ContainsOption("-enablegdi")) + { + try + { + return new SkiaEncoder(logger, appPaths, httpClient, fileSystem, localizationManager); + } + catch (Exception ex) + { + logger.LogInformation("Skia not available. Will try next image processor. {0}", ex.Message); + } + + try + { + return new ImageMagickEncoder(logger, appPaths, httpClient, fileSystem, environment); + } + catch + { + logger.LogInformation("ImageMagick not available. Will try next image processor."); + } + } + + return new NullImageEncoder(); + } + + private static EnvironmentInfo getEnvironmentInfo() + => new EnvironmentInfo() + { + SystemArchitecture = RuntimeInformation.OSArchitecture, + OperatingSystem = getOperatingSystem() + }; + + 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: + { + 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: {Environment.OSVersion.Platform}"); + } + default: throw new Exception($"Can't resolve OS with description: {Environment.OSVersion.Platform}"); + } + } + + 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 + "\""; + } + } +} diff --git a/MediaBrowser.Server.Mono/SocketSharp/HttpFile.cs b/Jellyfin.Server/SocketSharp/HttpFile.cs similarity index 76% rename from MediaBrowser.Server.Mono/SocketSharp/HttpFile.cs rename to Jellyfin.Server/SocketSharp/HttpFile.cs index 1e7c93debb..4a798062dd 100644 --- a/MediaBrowser.Server.Mono/SocketSharp/HttpFile.cs +++ b/Jellyfin.Server/SocketSharp/HttpFile.cs @@ -1,7 +1,7 @@ -using MediaBrowser.Model.Services; -using System.IO; +using System.IO; +using MediaBrowser.Model.Services; -namespace EmbyServer.SocketSharp +namespace Jellyfin.SocketSharp { public class HttpFile : IHttpFile { diff --git a/MediaBrowser.Server.Mono/SocketSharp/RequestMono.cs b/Jellyfin.Server/SocketSharp/RequestMono.cs similarity index 99% rename from MediaBrowser.Server.Mono/SocketSharp/RequestMono.cs rename to Jellyfin.Server/SocketSharp/RequestMono.cs index 9d23543169..31f2894977 100644 --- a/MediaBrowser.Server.Mono/SocketSharp/RequestMono.cs +++ b/Jellyfin.Server/SocketSharp/RequestMono.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Collections.Specialized; using System.Globalization; using System.IO; using System.Net; @@ -8,7 +7,7 @@ using System.Text; using System.Threading.Tasks; using MediaBrowser.Model.Services; -namespace EmbyServer.SocketSharp +namespace Jellyfin.SocketSharp { public partial class WebSocketSharpRequest : IHttpRequest { @@ -52,7 +51,7 @@ namespace EmbyServer.SocketSharp input.Position = 0; - //Uncomment to debug + // Uncomment to debug //var content = new StreamReader(ms).ReadToEnd(); //Console.WriteLine(boundary + "::" + content); //input.Position = 0; @@ -802,6 +801,5 @@ namespace EmbyServer.SocketSharp return path.Substring(path.LastIndexOf('\\') + 1); } } - } } diff --git a/MediaBrowser.Server.Mono/SocketSharp/SharpWebSocket.cs b/Jellyfin.Server/SocketSharp/SharpWebSocket.cs similarity index 98% rename from MediaBrowser.Server.Mono/SocketSharp/SharpWebSocket.cs rename to Jellyfin.Server/SocketSharp/SharpWebSocket.cs index fd32640a25..1c72035a55 100644 --- a/MediaBrowser.Server.Mono/SocketSharp/SharpWebSocket.cs +++ b/Jellyfin.Server/SocketSharp/SharpWebSocket.cs @@ -1,12 +1,12 @@ -using MediaBrowser.Common.Events; -using Microsoft.Extensions.Logging; -using System; +using System; using System.Threading; using System.Threading.Tasks; using System.Net.WebSockets; using Emby.Server.Implementations.Net; +using MediaBrowser.Common.Events; +using Microsoft.Extensions.Logging; -namespace EmbyServer.SocketSharp +namespace Jellyfin.SocketSharp { public class SharpWebSocket : IWebSocket { diff --git a/MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpListener.cs b/Jellyfin.Server/SocketSharp/WebSocketSharpListener.cs similarity index 98% rename from MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpListener.cs rename to Jellyfin.Server/SocketSharp/WebSocketSharpListener.cs index 993863e8c8..c360a8fcea 100644 --- a/MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpListener.cs +++ b/Jellyfin.Server/SocketSharp/WebSocketSharpListener.cs @@ -1,23 +1,22 @@ -using MediaBrowser.Controller.Net; -using Microsoft.Extensions.Logging; -using SocketHttpListener.Net; -using System; +using System; using System.Collections.Generic; -using System.Linq; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; +using Emby.Server.Implementations.Net; +using Emby.Server.Implementations.HttpServer; using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using MediaBrowser.Model.Services; using MediaBrowser.Model.System; using MediaBrowser.Model.Text; -using Emby.Server.Implementations.Net; -using Emby.Server.Implementations.HttpServer; +using Microsoft.Extensions.Logging; +using SocketHttpListener.Net; -namespace EmbyServer.SocketSharp +namespace Jellyfin.SocketSharp { public class WebSocketSharpListener : IHttpListener { @@ -258,5 +257,4 @@ namespace EmbyServer.SocketSharp } } } - } diff --git a/MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpRequest.cs b/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs similarity index 99% rename from MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpRequest.cs rename to Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs index 1e1c3db7c4..7c9dc8f88c 100644 --- a/MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpRequest.cs +++ b/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs @@ -3,17 +3,15 @@ using System.Collections.Generic; using System.IO; using System.Text; using Emby.Server.Implementations.HttpServer; -using MediaBrowser.Model.IO; -using Microsoft.Extensions.Logging; using MediaBrowser.Model.Services; +using Microsoft.Extensions.Logging; using SocketHttpListener.Net; using IHttpFile = MediaBrowser.Model.Services.IHttpFile; using IHttpRequest = MediaBrowser.Model.Services.IHttpRequest; using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse; using IResponse = MediaBrowser.Model.Services.IResponse; -using System.Threading.Tasks; -namespace EmbyServer.SocketSharp +namespace Jellyfin.SocketSharp { public partial class WebSocketSharpRequest : IHttpRequest { diff --git a/MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpResponse.cs b/Jellyfin.Server/SocketSharp/WebSocketSharpResponse.cs similarity index 99% rename from MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpResponse.cs rename to Jellyfin.Server/SocketSharp/WebSocketSharpResponse.cs index 08fa4cbd62..c7437c8254 100644 --- a/MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpResponse.cs +++ b/Jellyfin.Server/SocketSharp/WebSocketSharpResponse.cs @@ -2,18 +2,19 @@ using System.Collections.Generic; using System.IO; using System.Net; +using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.IO; -using Microsoft.Extensions.Logging; using MediaBrowser.Model.Services; +using Microsoft.Extensions.Logging; using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse; using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse; using IRequest = MediaBrowser.Model.Services.IRequest; -using System.Net.Sockets; -namespace EmbyServer.SocketSharp + +namespace Jellyfin.SocketSharp { public class WebSocketSharpResponse : IHttpResponse { diff --git a/MediaBrowser.Model/System/Architecture.cs b/MediaBrowser.Model/System/Architecture.cs deleted file mode 100644 index 73f78cd582..0000000000 --- a/MediaBrowser.Model/System/Architecture.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace MediaBrowser.Model.System -{ - public enum Architecture - { - X86 = 0, - X64 = 1, - Arm = 2, - Arm64 = 3 - } -} diff --git a/MediaBrowser.Model/System/IEnvironmentInfo.cs b/MediaBrowser.Model/System/IEnvironmentInfo.cs index 8cf25a365f..6af514dc8e 100644 --- a/MediaBrowser.Model/System/IEnvironmentInfo.cs +++ b/MediaBrowser.Model/System/IEnvironmentInfo.cs @@ -1,4 +1,5 @@ - +using System.Runtime.InteropServices; + namespace MediaBrowser.Model.System { public interface IEnvironmentInfo diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index c790731c6f..031222b751 100644 --- a/MediaBrowser.Model/System/SystemInfo.cs +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -1,6 +1,5 @@ using MediaBrowser.Model.Updates; -using System.Collections.Generic; -using System; +using System.Runtime.InteropServices; namespace MediaBrowser.Model.System { diff --git a/MediaBrowser.Server.Mono/EmbyServer.csproj b/MediaBrowser.Server.Mono/EmbyServer.csproj deleted file mode 100644 index 609af9674b..0000000000 --- a/MediaBrowser.Server.Mono/EmbyServer.csproj +++ /dev/null @@ -1,66 +0,0 @@ - - - - Exe - jellyfin - netcoreapp2.1 - false - - - - None - ubuntu.16.04-x64 - - - - AnyCPU - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/MediaBrowser.Server.Mono/ImageEncoderHelper.cs b/MediaBrowser.Server.Mono/ImageEncoderHelper.cs deleted file mode 100644 index 29760ec55b..0000000000 --- a/MediaBrowser.Server.Mono/ImageEncoderHelper.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using Emby.Drawing; -using Emby.Drawing.ImageMagick; -using Emby.Server.Implementations; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Drawing; -using MediaBrowser.Model.IO; -using Microsoft.Extensions.Logging; -using Emby.Drawing.Skia; -using MediaBrowser.Model.System; -using MediaBrowser.Model.Globalization; - -namespace MediaBrowser.Server.Startup.Common -{ - public class ImageEncoderHelper - { - public static IImageEncoder GetImageEncoder(ILogger logger, - IFileSystem fileSystem, - StartupOptions startupOptions, - Func httpClient, - IApplicationPaths appPaths, - IEnvironmentInfo environment, - ILocalizationManager localizationManager) - { - if (!startupOptions.ContainsOption("-enablegdi")) - { - try - { - return new SkiaEncoder(logger, appPaths, httpClient, fileSystem, localizationManager); - } - catch (Exception ex) - { - logger.LogInformation("Skia not available. Will try next image processor. {0}", ex.Message); - } - - try - { - return new ImageMagickEncoder(logger, appPaths, httpClient, fileSystem, environment); - } - catch - { - logger.LogInformation("ImageMagick not available. Will try next image processor."); - } - } - - return new NullImageEncoder(); - } - } -} diff --git a/MediaBrowser.Server.Mono/MonoAppHost.cs b/MediaBrowser.Server.Mono/MonoAppHost.cs deleted file mode 100644 index c2ec423f84..0000000000 --- a/MediaBrowser.Server.Mono/MonoAppHost.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; -//using Emby.Server.CinemaMode; -using Emby.Server.Implementations; -using Emby.Server.Implementations.Library; -using Emby.Server.Implementations.HttpServer; -using Emby.Server.Implementations.Net; -using MediaBrowser.Controller.Connect; -using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Sync; -using IsoMounter; -using MediaBrowser.Model.IO; -using Microsoft.Extensions.Logging; -using MediaBrowser.Model.Services; -using MediaBrowser.Model.System; - -namespace MediaBrowser.Server.Mono -{ - public class MonoAppHost : ApplicationHost - { - public MonoAppHost(ServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory, StartupOptions options, IFileSystem fileSystem, IPowerManagement powerManagement, string releaseAssetFilename, IEnvironmentInfo environmentInfo, MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, ISystemEvents systemEvents, MediaBrowser.Common.Net.INetworkManager networkManager) - : base(applicationPaths, loggerFactory, options, fileSystem, powerManagement, releaseAssetFilename, environmentInfo, imageEncoder, systemEvents, networkManager) - { - } - - public override bool CanSelfRestart - { - get - { - // A restart script must be provided - return StartupOptions.ContainsOption("-restartpath"); - } - } - - //protected override ISyncManager CreateSyncManager() - //{ - // return new SyncManager(); - //} - - protected override void RestartInternal() - { - MainClass.Restart(); - } - - protected override List GetAssembliesWithPartsInternal() - { - var list = new List(); - - list.Add(GetType().Assembly); - - return list; - } - - protected override void ShutdownInternal() - { - MainClass.Shutdown(); - } - - protected override bool SupportsDualModeSockets - { - get - { - return GetMonoVersion() >= new Version(4, 6); - } - } - - private static Version GetMonoVersion() - { - Type type = Type.GetType("Mono.Runtime"); - if (type != null) - { - MethodInfo displayName = type.GetTypeInfo().GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static); - var displayNameValue = displayName.Invoke(null, null).ToString().Trim().Split(' ')[0]; - - Version version; - if (Version.TryParse(displayNameValue, out version)) - { - return version; - } - } - - return new Version(1, 0); - } - - protected override IHttpListener CreateHttpListener() - { - return new EmbyServer.SocketSharp.WebSocketSharpListener( - Logger, - Certificate, - StreamHelper, - TextEncoding, - NetworkManager, - SocketFactory, - CryptographyProvider, - SupportsDualModeSockets, - FileSystemManager, - EnvironmentInfo); - } - - } -} diff --git a/MediaBrowser.Server.Mono/Native/MonoFileSystem.cs b/MediaBrowser.Server.Mono/Native/MonoFileSystem.cs deleted file mode 100644 index 2499d8bb51..0000000000 --- a/MediaBrowser.Server.Mono/Native/MonoFileSystem.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Emby.Server.Implementations.IO; -using Microsoft.Extensions.Logging; -using MediaBrowser.Model.System; -using Mono.Unix.Native; - -namespace MediaBrowser.Server.Mono.Native -{ - public class MonoFileSystem : ManagedFileSystem - { - public MonoFileSystem(ILogger logger, IEnvironmentInfo environment, string defaultDirectory, string tempPath, bool enableSeperateFileAndDirectoryQueries) - : base(logger, environment,defaultDirectory, tempPath, enableSeperateFileAndDirectoryQueries) - { - } - - public override void SetExecutable(string path) - { - // Linux: File permission to 666, and user's execute bit - Logger.LogInformation("Syscall.chmod {0} FilePermissions.DEFFILEMODE | FilePermissions.S_IRWXU | FilePermissions.S_IXGRP | FilePermissions.S_IXOTH", path); - - Syscall.chmod(path, FilePermissions.DEFFILEMODE | FilePermissions.S_IRWXU | FilePermissions.S_IXGRP | FilePermissions.S_IXOTH); - } - } -} diff --git a/MediaBrowser.Server.Mono/Program.cs b/MediaBrowser.Server.Mono/Program.cs deleted file mode 100644 index f512fc98e0..0000000000 --- a/MediaBrowser.Server.Mono/Program.cs +++ /dev/null @@ -1,380 +0,0 @@ -using MediaBrowser.Server.Mono.Native; -using MediaBrowser.Server.Startup.Common; -using System; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Security; -using System.Reflection; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using Emby.Drawing; -using Emby.Server.Implementations; -using Emby.Server.Implementations.EnvironmentInfo; -using Emby.Server.Implementations.IO; -using Emby.Server.Implementations.Networking; -using MediaBrowser.Controller; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.System; -using Mono.Unix.Native; -using X509Certificate = System.Security.Cryptography.X509Certificates.X509Certificate; -using System.Threading; -using InteropServices = System.Runtime.InteropServices; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Configuration; -using ILogger = Microsoft.Extensions.Logging.ILogger; -using Serilog; -using Serilog.AspNetCore; - -namespace MediaBrowser.Server.Mono -{ - public class MainClass - { - private static ILogger _logger; - private static IFileSystem FileSystem; - private static IServerApplicationPaths _appPaths; - private static ILoggerFactory _loggerFactory; - - private static readonly TaskCompletionSource ApplicationTaskCompletionSource = new TaskCompletionSource(); - private static bool _restartOnShutdown; - - public static void Main(string[] args) - { - var applicationPath = Assembly.GetEntryAssembly().Location; - - SetSqliteProvider(); - - var options = new StartupOptions(Environment.GetCommandLineArgs()); - - // Allow this to be specified on the command line. - var customProgramDataPath = options.GetOption("-programdata"); - - var appPaths = CreateApplicationPaths(applicationPath, customProgramDataPath); - _appPaths = appPaths; - - createLogger(); - - using (var loggerFactory = new SerilogLoggerFactory()) - { - _loggerFactory = loggerFactory; - - _logger = loggerFactory.CreateLogger("Main"); - - ApplicationHost.LogEnvironmentInfo(_logger, appPaths, true); - - AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; - - RunApplication(appPaths, loggerFactory, options); - - _logger.LogInformation("Disposing app host"); - - if (_restartOnShutdown) - { - StartNewInstance(options); - } - } - } - - private static void SetSqliteProvider() - { - // SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_sqlite3()); - //SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_sqlite3()); - SQLitePCL.Batteries_V2.Init(); - } - - private static ServerApplicationPaths CreateApplicationPaths(string applicationPath, string programDataPath) - { - if (string.IsNullOrEmpty(programDataPath)) - { - if (InteropServices.RuntimeInformation.IsOSPlatform(InteropServices.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"); - } - - var appFolderPath = Path.GetDirectoryName(applicationPath); - - return new ServerApplicationPaths(programDataPath, appFolderPath, appFolderPath); - } - - private static void RunApplication(ServerApplicationPaths appPaths, ILoggerFactory loggerFactory, StartupOptions options) - { - // Allow all https requests - ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; }); - - var environmentInfo = GetEnvironmentInfo(); - - var fileSystem = new ManagedFileSystem(loggerFactory.CreateLogger("FileSystem"), environmentInfo, null, appPaths.TempDirectory, true); - - FileSystem = fileSystem; - - using (var appHost = new MonoAppHost(appPaths, - loggerFactory, - options, - fileSystem, - new PowerManagement(), - "embyserver-mono_{version}.zip", - environmentInfo, - new NullImageEncoder(), - new SystemEvents(loggerFactory.CreateLogger("SystemEvents")), - new NetworkManager(loggerFactory.CreateLogger("NetworkManager"), environmentInfo))) - { - if (options.ContainsOption("-v")) - { - Console.WriteLine(appHost.ApplicationVersion.ToString()); - return; - } - - //Console.WriteLine("appHost.Init"); - - appHost.Init(); - - appHost.ImageProcessor.ImageEncoder = ImageEncoderHelper.GetImageEncoder(_logger, fileSystem, options, () => appHost.HttpClient, appPaths, environmentInfo, appHost.LocalizationManager); - - //Console.WriteLine("Running startup tasks"); - _logger.LogInformation("Running startup tasks"); - - var task = appHost.RunStartupTasks(); - Task.WaitAll(task); - - task = ApplicationTaskCompletionSource.Task; - - Task.WaitAll(task); - } - } - - private static void createLogger() - { - var logDir = Environment.GetEnvironmentVariable("JELLYFIN_LOG_DIR"); - if (string.IsNullOrEmpty(logDir)){ - logDir = Path.Combine(_appPaths.ProgramDataPath, "logs"); - Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", logDir); - } - try - { - string path = Path.Combine(_appPaths.ConfigurationDirectoryPath, "logging.json"); - - if (!File.Exists(path)) - { - var assembly = typeof(MainClass).Assembly; - // For some reason the csproj name is used instead of the assembly name - var resourcePath = "EmbyServer.Resources.Configuration.logging.json"; - using (Stream rscstr = assembly.GetManifestResourceStream(resourcePath)) - using (Stream fstr = File.Open(path, FileMode.CreateNew)) - { - rscstr.CopyTo(fstr); - } - } - var configuration = new ConfigurationBuilder() - .SetBasePath(_appPaths.ConfigurationDirectoryPath) - .AddJsonFile("logging.json") - .AddEnvironmentVariables("JELLYFIN_") - .Build(); - - 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.File( - Path.Combine(logDir, "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 read logger config"); - } - } - - private static MonoEnvironmentInfo GetEnvironmentInfo() - { - var info = new MonoEnvironmentInfo(); - - var uname = GetUnixName(); - - var sysName = uname.sysname ?? string.Empty; - - if (string.Equals(sysName, "Darwin", StringComparison.OrdinalIgnoreCase)) - { - info.OperatingSystem = Model.System.OperatingSystem.OSX; - } - else if (string.Equals(sysName, "Linux", StringComparison.OrdinalIgnoreCase)) - { - info.OperatingSystem = Model.System.OperatingSystem.Linux; - } - else if (string.Equals(sysName, "BSD", StringComparison.OrdinalIgnoreCase)) - { - info.OperatingSystem = Model.System.OperatingSystem.BSD; - } - - var archX86 = new Regex("(i|I)[3-6]86"); - - if (archX86.IsMatch(uname.machine)) - { - info.SystemArchitecture = Architecture.X86; - } - else if (string.Equals(uname.machine, "x86_64", StringComparison.OrdinalIgnoreCase)) - { - info.SystemArchitecture = Architecture.X64; - } - else if (uname.machine.StartsWith("arm", StringComparison.OrdinalIgnoreCase)) - { - info.SystemArchitecture = Architecture.Arm; - } - else if (System.Environment.Is64BitOperatingSystem) - { - info.SystemArchitecture = Architecture.X64; - } - else - { - info.SystemArchitecture = Architecture.X86; - } - - return info; - } - - private static Uname _unixName; - - private static Uname GetUnixName() - { - if (_unixName == null) - { - var uname = new Uname(); - try - { - Utsname utsname; - var callResult = Syscall.uname(out utsname); - if (callResult == 0) - { - uname.sysname = utsname.sysname ?? string.Empty; - uname.machine = utsname.machine ?? string.Empty; - } - - } - catch (Exception ex) - { - _logger.LogError(ex, "Error getting unix name"); - } - _unixName = uname; - } - return _unixName; - } - - public class Uname - { - public string sysname = string.Empty; - public string machine = string.Empty; - } - - /// - /// Handles the UnhandledException event of the CurrentDomain control. - /// - /// The source of the event. - /// The instance containing the event data. - static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) - { - var exception = (Exception)e.ExceptionObject; - - //new UnhandledExceptionWriter(_appPaths, _logger, _logManager, FileSystem, new ConsoleLogger()).Log(exception); - - _logger.LogCritical(exception, "Unhandled Exception"); - - // TODO: @bond - /* - if (!Debugger.IsAttached) - { - var message = LogHelper.GetLogMessage(exception).ToString(); - - if (message.IndexOf("InotifyWatcher", StringComparison.OrdinalIgnoreCase) == -1 && - message.IndexOf("_IOCompletionCallback", StringComparison.OrdinalIgnoreCase) == -1) - { - Environment.Exit(System.Runtime.InteropServices.Marshal.GetHRForException(exception)); - } - } - */ - } - - 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"); - string commandLineArgsString = startupOptions.GetOption("-restartargs") ?? string.Empty; - - if (string.IsNullOrWhiteSpace(module)) - { - module = Environment.GetCommandLineArgs().First(); - } - if (!startupOptions.ContainsOption("-restartargs")) - { - var args = Environment.GetCommandLineArgs() - .Skip(1) - .Select(NormalizeCommandLineArgument) - .ToArray(); - - commandLineArgsString = string.Join(" ", args); - } - - _logger.LogInformation("Executable: {0}", module); - _logger.LogInformation("Arguments: {0}", commandLineArgsString); - - Process.Start(module, commandLineArgsString); - } - - private static string NormalizeCommandLineArgument(string arg) - { - if (arg.IndexOf(" ", StringComparison.OrdinalIgnoreCase) == -1) - { - return arg; - } - - return "\"" + arg + "\""; - } - } - - // class NoCheckCertificatePolicy : ICertificatePolicy - // { - // public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) - // { - // return true; - // } - // } - - public class MonoEnvironmentInfo : EnvironmentInfo - { - - //public override string GetUserId() - //{ - // return Syscall.getuid().ToString(CultureInfo.InvariantCulture); - //} - } -} diff --git a/MediaBrowser.Server.Mono/Properties/AssemblyInfo.cs b/MediaBrowser.Server.Mono/Properties/AssemblyInfo.cs deleted file mode 100644 index eda3bcf97e..0000000000 --- a/MediaBrowser.Server.Mono/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Reflection; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. -[assembly: AssemblyTitle ("MediaBrowser.Server.Mono")] -[assembly: AssemblyDescription ("")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany ("")] -[assembly: AssemblyProduct ("")] -[assembly: AssemblyCopyright ("Emby")] -[assembly: AssemblyTrademark ("")] -[assembly: AssemblyCulture ("")] -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. \ No newline at end of file diff --git a/MediaBrowser.Server.Mono/Properties/launchSettings.json b/MediaBrowser.Server.Mono/Properties/launchSettings.json deleted file mode 100644 index 058ad7adca..0000000000 --- a/MediaBrowser.Server.Mono/Properties/launchSettings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "profiles": { - "EmbyServer": { - "commandName": "Project" - } - } -} diff --git a/MediaBrowser.Server.Mono/Resources/Configuration/logging.json b/MediaBrowser.Server.Mono/Resources/Configuration/logging.json deleted file mode 100644 index 78f99b2ad9..0000000000 --- a/MediaBrowser.Server.Mono/Resources/Configuration/logging.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "Serilog": { - "MinimumLevel": "Information", - "WriteTo": [ - { "Name": "Console", - "Args": { - "outputTemplate": "[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}" - } - }, - { "Name": "File", - "Args": { - "path": "%JELLYFIN_LOG_DIR%//log_.log", - "rollingInterval": "Day", - "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}" - } - } - ] - } -} diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 1f4b01b314..07b9fc4c8a 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -28,8 +28,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.XbmcMetadata", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.LocalMetadata", "MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj", "{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EmbyServer", "MediaBrowser.Server.Mono\EmbyServer.csproj", "{175A9388-F352-4586-A6B4-070DED62B644}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Drawing", "Emby.Drawing\Emby.Drawing.csproj", "{08FFF49B-F175-4807-A2B5-73B0EBD9F716}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Photos", "Emby.Photos\Emby.Photos.csproj", "{89AB4548-770D-41FD-A891-8DAFF44F452C}" @@ -66,6 +64,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IsoMounter", "Emby.IsoMount EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.MediaEncoding", "MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj", "{960295EE-4AF4-4440-A525-B4C295B01A61}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server", "Jellyfin.Server\Jellyfin.Server.csproj", "{07E39F42-A2C6-4B32-AF8C-725F957A73FF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -216,23 +216,6 @@ Global {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|Win32.ActiveCfg = Release|Any CPU {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|x64.ActiveCfg = Release|Any CPU {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|x86.ActiveCfg = Release|Any CPU - {175A9388-F352-4586-A6B4-070DED62B644}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {175A9388-F352-4586-A6B4-070DED62B644}.Debug|Any CPU.Build.0 = Debug|Any CPU - {175A9388-F352-4586-A6B4-070DED62B644}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {175A9388-F352-4586-A6B4-070DED62B644}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {175A9388-F352-4586-A6B4-070DED62B644}.Debug|Win32.ActiveCfg = Debug|Any CPU - {175A9388-F352-4586-A6B4-070DED62B644}.Debug|Win32.Build.0 = Debug|Any CPU - {175A9388-F352-4586-A6B4-070DED62B644}.Debug|x64.ActiveCfg = Debug|Any CPU - {175A9388-F352-4586-A6B4-070DED62B644}.Debug|x86.ActiveCfg = Debug|Any CPU - {175A9388-F352-4586-A6B4-070DED62B644}.Debug|x86.Build.0 = Debug|Any CPU - {175A9388-F352-4586-A6B4-070DED62B644}.Release|Any CPU.ActiveCfg = Release|Any CPU - {175A9388-F352-4586-A6B4-070DED62B644}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {175A9388-F352-4586-A6B4-070DED62B644}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {175A9388-F352-4586-A6B4-070DED62B644}.Release|Win32.ActiveCfg = Release|Any CPU - {175A9388-F352-4586-A6B4-070DED62B644}.Release|Win32.Build.0 = Release|Any CPU - {175A9388-F352-4586-A6B4-070DED62B644}.Release|x64.ActiveCfg = Release|Any CPU - {175A9388-F352-4586-A6B4-070DED62B644}.Release|x86.ActiveCfg = Release|Any CPU - {175A9388-F352-4586-A6B4-070DED62B644}.Release|x86.Build.0 = Release|Any CPU {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Debug|Any CPU.Build.0 = Debug|Any CPU {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU @@ -567,6 +550,26 @@ Global {960295EE-4AF4-4440-A525-B4C295B01A61}.Release|x64.Build.0 = Release|Any CPU {960295EE-4AF4-4440-A525-B4C295B01A61}.Release|x86.ActiveCfg = Release|Any CPU {960295EE-4AF4-4440-A525-B4C295B01A61}.Release|x86.Build.0 = Release|Any CPU + {07E39F42-A2C6-4B32-AF8C-725F957A73FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {07E39F42-A2C6-4B32-AF8C-725F957A73FF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {07E39F42-A2C6-4B32-AF8C-725F957A73FF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {07E39F42-A2C6-4B32-AF8C-725F957A73FF}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {07E39F42-A2C6-4B32-AF8C-725F957A73FF}.Debug|Win32.ActiveCfg = Debug|Any CPU + {07E39F42-A2C6-4B32-AF8C-725F957A73FF}.Debug|Win32.Build.0 = Debug|Any CPU + {07E39F42-A2C6-4B32-AF8C-725F957A73FF}.Debug|x64.ActiveCfg = Debug|Any CPU + {07E39F42-A2C6-4B32-AF8C-725F957A73FF}.Debug|x64.Build.0 = Debug|Any CPU + {07E39F42-A2C6-4B32-AF8C-725F957A73FF}.Debug|x86.ActiveCfg = Debug|Any CPU + {07E39F42-A2C6-4B32-AF8C-725F957A73FF}.Debug|x86.Build.0 = Debug|Any CPU + {07E39F42-A2C6-4B32-AF8C-725F957A73FF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {07E39F42-A2C6-4B32-AF8C-725F957A73FF}.Release|Any CPU.Build.0 = Release|Any CPU + {07E39F42-A2C6-4B32-AF8C-725F957A73FF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {07E39F42-A2C6-4B32-AF8C-725F957A73FF}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {07E39F42-A2C6-4B32-AF8C-725F957A73FF}.Release|Win32.ActiveCfg = Release|Any CPU + {07E39F42-A2C6-4B32-AF8C-725F957A73FF}.Release|Win32.Build.0 = Release|Any CPU + {07E39F42-A2C6-4B32-AF8C-725F957A73FF}.Release|x64.ActiveCfg = Release|Any CPU + {07E39F42-A2C6-4B32-AF8C-725F957A73FF}.Release|x64.Build.0 = Release|Any CPU + {07E39F42-A2C6-4B32-AF8C-725F957A73FF}.Release|x86.ActiveCfg = Release|Any CPU + {07E39F42-A2C6-4B32-AF8C-725F957A73FF}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE