diff options
130 files changed, 1278 insertions, 1231 deletions
diff --git a/.dockerignore b/.dockerignore index fc4d8ed26..54b0aa3a7 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,3 +3,6 @@ Dockerfile CONTRIBUTORS.md README.md +deployment/*/dist +deployment/*/pkg-dist +deployment/collect-dist/ diff --git a/.drone.yml b/.drone.yml index 13762ba0a..98db4884b 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,32 +1,12 @@ kind: pipeline -name: build:debian +name: build steps: - name: submodules image: docker:git commands: - git submodule update --init --recursive -- name: build:debian - image: plugins/docker - group: build - settings: - repo: jellyfin/jellyfin - dry_run: true - dockerfile: Dockerfile.debian_package - - ---- -kind: pipeline -name: build:docker - -steps: -- name: submodules - image: docker:git +- name: build + image: microsoft/dotnet:2-sdk commands: - - git submodule update --init --recursive -- name: build:docker - image: plugins/docker - group: build - settings: - repo: jellyfin/jellyfin - dry_run: true + - dotnet publish --configuration release --output /release Jellyfin.Server diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..a3b72c9af --- /dev/null +++ b/.editorconfig @@ -0,0 +1,133 @@ +# With more recent updates Visual Studio 2017 supports EditorConfig files out of the box +# Visual Studio Code needs an extension: https://github.com/editorconfig/editorconfig-vscode +# For emacs, vim, np++ and other editors, see here: https://github.com/editorconfig +############################### +# Core EditorConfig Options # +############################### +# All files +[*] +indent_style = space +root = true +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +end_of_line = lf +max_line_length = null + +# Code files +[*.{cs,csx,vb,vbx}] +indent_size = 4 +insert_final_newline = true +charset = utf-8 +############################### +# .NET Coding Conventions # +############################### +[*.{cs,vb}] +# Organize usings +dotnet_sort_system_directives_first = true +# this. preferences +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_readonly_field = true:suggestion +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent +dotnet_prefer_inferred_tuple_names = true:suggestion +dotnet_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +############################### +# Naming Conventions # +############################### +# Style Definitions +dotnet_naming_style.pascal_case_style.capitalization = pascal_case +# Use PascalCase for constant fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const +############################### +# C# Coding Conventions # +############################### +[*.cs] +# var preferences +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = true:silent +# Expression-bodied members +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +# Pattern matching preferences +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +# Null-checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion +# Expression-level preferences +csharp_prefer_braces = true:silent +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +############################### +# C# Formatting Rules # +############################### +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true +# Indentation preferences +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left +# Space preferences +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +# Wrapping preferences +csharp_preserve_single_line_statements = true +csharp_preserve_single_line_blocks = true +############################### +# VB Coding Conventions # +############################### +[*.vb] +# Modifier preferences +visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion diff --git a/.gitignore b/.gitignore index 9bbfd9b74..b4b928ef2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ -!* - .directory ################# @@ -49,6 +47,8 @@ ProgramData-UI*/ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. +.vs/ + # User-specific files *.suo *.user @@ -204,7 +204,6 @@ $RECYCLE.BIN/ # Mac crap .DS_Store - ############# ## Python ############# @@ -234,22 +233,32 @@ pip-log.txt #Mr Developer .mr.developer.cfg -MediaBrowser.WebDashboard/dashboard-ui/.idea/ -/.vs ########## # Rider ########## .idea/ +########## +# Visual Studio Code +########## +.vscode/ + ######################### -# Debian build artifacts +# Build artifacts ######################### -debian/.debhelper/ -debian/files -debian/jellyfin.substvars -debian/jellyfin/ - +# Artifacts for debian-x64 +deployment/debian-x64/pkg-src/.debhelper/ +deployment/debian-x64/pkg-src/*.debhelper +deployment/debian-x64/pkg-src/debhelper-build-stamp +deployment/debian-x64/pkg-src/files +deployment/debian-x64/pkg-src/jellyfin.substvars +deployment/debian-x64/pkg-src/jellyfin/ # Don't ignore the debian/bin folder -!debian/bin/ +!deployment/debian-x64/pkg-src/bin/ + +deployment/**/dist/ +deployment/**/pkg-dist/ +deployment/collect-dist/ + diff --git a/.gitmodules b/.gitmodules index 31a58ecf1..7aeb94dfc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "ThirdParty/taglib-sharp"] path = ThirdParty/taglib-sharp url = https://github.com/mono/taglib-sharp.git +[submodule "MediaBrowser.WebDashboard/jellyfin-web"] + path = MediaBrowser.WebDashboard/jellyfin-web + url = https://github.com/jellyfin/jellyfin-web.git diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 6517b63cd..80c172309 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -10,7 +10,7 @@ - [Bond_009](https://github.com/Bond-009) - [AnthonyLavado](https://github.com/anthonylavado) - [sparky8251](https://github.com/sparky8251) - - [LeoVerto](https://github.com/LeoVerto] + - [LeoVerto](https://github.com/LeoVerto) # Emby Contributors diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 47414ad12..000000000 --- a/Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -ARG DOTNET_VERSION=2 - -FROM microsoft/dotnet:${DOTNET_VERSION}-sdk as builder -WORKDIR /repo -COPY . . -RUN export DOTNET_CLI_TELEMETRY_OPTOUT=1 \ - && dotnet clean \ - && dotnet publish --configuration release --output /jellyfin - -FROM microsoft/dotnet:${DOTNET_VERSION}-runtime -COPY --from=builder /jellyfin /jellyfin -EXPOSE 8096 - -VOLUME /config /media - -ARG FFMPEG_URL=https://www.johnvansickle.com/ffmpeg/old-releases/ffmpeg-4.0.3-64bit-static.tar.xz -RUN apt update \ - && apt install -y xz-utils \ - && curl ${FFMPEG_URL} | tar Jxf - -C /usr/bin --wildcards --strip-components=1 ffmpeg*/ffmpeg ffmpeg*/ffprobe \ - && apt remove -y xz-utils - -ENTRYPOINT if [ -n "$PUID$PGUID" ]; \ - then echo "PUID/PGID are deprecated. Use Docker user param." >&2; exit 1; \ - else dotnet /jellyfin/jellyfin.dll -programdata /config; fi diff --git a/Emby.Dlna/Images/logo120.jpg b/Emby.Dlna/Images/logo120.jpg Binary files differindex 9a0e199a3..c70f4db0d 100644 --- a/Emby.Dlna/Images/logo120.jpg +++ b/Emby.Dlna/Images/logo120.jpg diff --git a/Emby.Dlna/Images/logo120.png b/Emby.Dlna/Images/logo120.png Binary files differindex c7fc5c1ce..321c47729 100644 --- a/Emby.Dlna/Images/logo120.png +++ b/Emby.Dlna/Images/logo120.png diff --git a/Emby.Dlna/Images/logo240.jpg b/Emby.Dlna/Images/logo240.jpg Binary files differindex 74cb2c429..da1cb5e07 100644 --- a/Emby.Dlna/Images/logo240.jpg +++ b/Emby.Dlna/Images/logo240.jpg diff --git a/Emby.Dlna/Images/logo240.png b/Emby.Dlna/Images/logo240.png Binary files differindex d229ed24c..64c828129 100644 --- a/Emby.Dlna/Images/logo240.png +++ b/Emby.Dlna/Images/logo240.png diff --git a/Emby.Dlna/Images/logo48.jpg b/Emby.Dlna/Images/logo48.jpg Binary files differindex 9822a8e2f..269bcf589 100644 --- a/Emby.Dlna/Images/logo48.jpg +++ b/Emby.Dlna/Images/logo48.jpg diff --git a/Emby.Dlna/Images/logo48.png b/Emby.Dlna/Images/logo48.png Binary files differindex 23007e155..b8fc14564 100644 --- a/Emby.Dlna/Images/logo48.png +++ b/Emby.Dlna/Images/logo48.png diff --git a/Emby.Drawing/Common/ImageHeader.cs b/Emby.Drawing/Common/ImageHeader.cs index 7b819c2fd..f37f396f5 100644 --- a/Emby.Drawing/Common/ImageHeader.cs +++ b/Emby.Drawing/Common/ImageHeader.cs @@ -121,7 +121,7 @@ namespace Emby.Drawing.Common /// </summary> /// <param name="binaryReader">The binary reader.</param> /// <returns>System.Int16.</returns> - private static short ReadLittleEndianInt16(BinaryReader binaryReader) + private static short ReadLittleEndianInt16(this BinaryReader binaryReader) { var bytes = new byte[sizeof(short)]; @@ -137,7 +137,7 @@ namespace Emby.Drawing.Common /// </summary> /// <param name="binaryReader">The binary reader.</param> /// <returns>System.Int32.</returns> - private static int ReadLittleEndianInt32(BinaryReader binaryReader) + private static int ReadLittleEndianInt32(this BinaryReader binaryReader) { var bytes = new byte[sizeof(int)]; for (int i = 0; i < sizeof(int); i += 1) @@ -205,25 +205,30 @@ namespace Emby.Drawing.Common /// <exception cref="System.ArgumentException"></exception> private static ImageSize DecodeJfif(BinaryReader binaryReader) { + // A JPEG image consists of a sequence of segments, + // each beginning with a marker, each of which begins with a 0xFF byte + // followed by a byte indicating what kind of marker it is. + // Source: https://en.wikipedia.org/wiki/JPEG#Syntax_and_structure while (binaryReader.ReadByte() == 0xff) { byte marker = binaryReader.ReadByte(); - short chunkLength = ReadLittleEndianInt16(binaryReader); - if (marker == 0xc0) + short chunkLength = binaryReader.ReadLittleEndianInt16(); + // SOF0: Indicates that this is a baseline DCT-based JPEG, + // and specifies the width, height, number of components, and component subsampling + // SOF2: Indicates that this is a progressive DCT-based JPEG, + // and specifies the width, height, number of components, and component subsampling + if (marker == 0xc0 || marker == 0xc2) { - binaryReader.ReadByte(); - int height = ReadLittleEndianInt16(binaryReader); - int width = ReadLittleEndianInt16(binaryReader); - return new ImageSize - { - Width = width, - Height = height - }; + // https://help.accusoft.com/ImageGear/v18.2/Windows/ActiveX/IGAX-10-12.html + binaryReader.ReadByte(); // We don't care about the first byte + int height = binaryReader.ReadLittleEndianInt16(); + int width = binaryReader.ReadLittleEndianInt16(); + return new ImageSize(width, height); } if (chunkLength < 0) { - var uchunkLength = (ushort)chunkLength; + ushort uchunkLength = (ushort)chunkLength; binaryReader.ReadBytes(uchunkLength - 2); } else diff --git a/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs b/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs index faaed482a..d82d2f2fb 100644 --- a/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs +++ b/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs @@ -44,13 +44,13 @@ namespace IsoMounter _logger.LogDebug( "[{0}] System PATH is currently set to [{1}].", Name, - EnvironmentInfo.GetEnvironmentVariable("PATH") ?? "" + Environment.GetEnvironmentVariable("PATH") ?? "" ); _logger.LogDebug( "[{0}] System path separator is [{1}].", Name, - EnvironmentInfo.PathSeparator + Path.PathSeparator ); _logger.LogDebug( @@ -118,25 +118,27 @@ namespace IsoMounter public bool CanMount(string path) { - if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Linux) { - _logger.LogInformation( - "[{0}] Checking we can attempt to mount [{1}], Extension = [{2}], Operating System = [{3}], Executables Available = [{4}].", - Name, - path, - Path.GetExtension(path), - EnvironmentInfo.OperatingSystem, - ExecutablesAvailable.ToString() - ); - - if (ExecutablesAvailable) { - return string.Equals(Path.GetExtension(path), ".iso", StringComparison.OrdinalIgnoreCase); - } else { - return false; - } - } else { + if (EnvironmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Linux) + { return false; } + _logger.LogInformation( + "[{0}] Checking we can attempt to mount [{1}], Extension = [{2}], Operating System = [{3}], Executables Available = [{4}].", + Name, + path, + Path.GetExtension(path), + EnvironmentInfo.OperatingSystem, + ExecutablesAvailable.ToString() + ); + if (ExecutablesAvailable) + { + return string.Equals(Path.GetExtension(path), ".iso", StringComparison.OrdinalIgnoreCase); + } + else + { + return false; + } } public Task Install(CancellationToken cancellationToken) @@ -211,18 +213,16 @@ namespace IsoMounter private string GetFullPathForExecutable(string name) { - foreach (string test in (EnvironmentInfo.GetEnvironmentVariable("PATH") ?? "").Split(EnvironmentInfo.PathSeparator)) { - + foreach (string test in (Environment.GetEnvironmentVariable("PATH") ?? "").Split(Path.PathSeparator)) + { string path = test.Trim(); if (!String.IsNullOrEmpty(path) && FileSystem.FileExists(path = Path.Combine(path, name))) { return FileSystem.GetFullPath(path); } - } - return String.Empty; - + return string.Empty; } private uint GetUID() @@ -315,9 +315,9 @@ namespace IsoMounter ); } else { - + throw new ArgumentNullException(nameof(isoPath)); - + } try @@ -397,9 +397,9 @@ namespace IsoMounter ); } else { - + throw new ArgumentNullException(nameof(mount)); - + } if (GetUID() == 0) { @@ -444,7 +444,7 @@ namespace IsoMounter } #endregion - + #region Internal Methods internal void OnUnmount(LinuxMount mount) diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs index 76d0076a6..3e12bc0b6 100644 --- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs +++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs @@ -12,11 +12,16 @@ namespace Emby.Server.Implementations.AppBase /// <summary> /// Initializes a new instance of the <see cref="BaseApplicationPaths"/> class. /// </summary> - protected BaseApplicationPaths(string programDataPath, string appFolderPath, string logDirectoryPath) + protected BaseApplicationPaths( + string programDataPath, + string appFolderPath, + string logDirectoryPath = null, + string configurationDirectoryPath = null) { ProgramDataPath = programDataPath; ProgramSystemPath = appFolderPath; LogDirectoryPath = logDirectoryPath; + ConfigurationDirectoryPath = configurationDirectoryPath; } public string ProgramDataPath { get; private set; } @@ -135,6 +140,11 @@ namespace Emby.Server.Implementations.AppBase } /// <summary> + /// The _config directory + /// </summary> + private string _configurationDirectoryPath; + + /// <summary> /// Gets the path to the application configuration root directory /// </summary> /// <value>The configuration directory path.</value> @@ -142,7 +152,18 @@ namespace Emby.Server.Implementations.AppBase { get { - return Path.Combine(ProgramDataPath, "config"); + if (string.IsNullOrEmpty(_configurationDirectoryPath)) + { + _configurationDirectoryPath = Path.Combine(ProgramDataPath, "config"); + + Directory.CreateDirectory(_configurationDirectoryPath); + } + + return _configurationDirectoryPath; + } + set + { + _configurationDirectoryPath = value; } } diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 236851968..b020b33c3 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -80,7 +80,6 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Net; -using MediaBrowser.Model.News; using MediaBrowser.Model.Reflection; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; @@ -369,7 +368,6 @@ namespace Emby.Server.Implementations public StartupOptions StartupOptions { get; private set; } - internal IPowerManagement PowerManagement { get; private set; } internal IImageEncoder ImageEncoder { get; private set; } protected IProcessFactory ProcessFactory { get; private set; } @@ -391,7 +389,6 @@ namespace Emby.Server.Implementations ILoggerFactory loggerFactory, StartupOptions options, IFileSystem fileSystem, - IPowerManagement powerManagement, IEnvironmentInfo environmentInfo, IImageEncoder imageEncoder, ISystemEvents systemEvents, @@ -417,12 +414,9 @@ namespace Emby.Server.Implementations Logger = LoggerFactory.CreateLogger("App"); StartupOptions = options; - PowerManagement = powerManagement; ImageEncoder = imageEncoder; - //SetBaseExceptionMessage(); - fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); NetworkManager.NetworkChanged += NetworkManager_NetworkChanged; @@ -506,14 +500,6 @@ namespace Emby.Server.Implementations return new StreamHelper(); } - public virtual bool SupportsAutoRunAtStartup - { - get - { - return EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows; - } - } - /// <summary> /// Creates an instance of type and resolves all constructor dependancies /// </summary> @@ -687,18 +673,6 @@ namespace Emby.Server.Implementations return parts; } - // TODO: @bond - /* - private void SetBaseExceptionMessage() - { - var builder = GetBaseExceptionMessage(ApplicationPaths); - - builder.Insert(0, string.Format("Version: {0}{1}", ApplicationVersion, Environment.NewLine)); - builder.Insert(0, "*** Error Report ***" + Environment.NewLine); - - LoggerFactory.ExceptionMessagePrefix = builder.ToString(); - }*/ - /// <summary> /// Runs the startup tasks. /// </summary> @@ -706,8 +680,6 @@ namespace Emby.Server.Implementations { Resolve<ITaskManager>().AddTasks(GetExports<IScheduledTask>(false)); - ConfigureAutorun(); - ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated; MediaEncoder.Init(); @@ -734,8 +706,6 @@ namespace Emby.Server.Implementations RunEntryPoints(entryPoints, false); Logger.LogInformation("All entry points have started"); - //LoggerFactory.RemoveConsoleOutput(); - return Task.CompletedTask; } @@ -749,7 +719,7 @@ namespace Emby.Server.Implementations } var name = entryPoint.GetType().FullName; - Logger.LogInformation("Starting entry point {0}", name); + Logger.LogInformation("Starting entry point {Name}", name); var now = DateTime.UtcNow; try { @@ -757,24 +727,9 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - Logger.LogError(ex, "Error in {name}", name); + Logger.LogError(ex, "Error while running entrypoint {Name}", name); } - Logger.LogInformation("Entry point completed: {0}. Duration: {1} seconds", name, (DateTime.UtcNow - now).TotalSeconds.ToString(CultureInfo.InvariantCulture), "ImageInfos"); - } - } - - /// <summary> - /// Configures the autorun. - /// </summary> - private void ConfigureAutorun() - { - try - { - ConfigureAutoRunAtStartup(ConfigurationManager.CommonConfiguration.RunAtStartup); - } - catch (Exception ex) - { - Logger.LogError(ex, "Error configuring autorun"); + Logger.LogInformation("Entry point completed: {Name}. Duration: {Duration} seconds", name, (DateTime.UtcNow - now).TotalSeconds.ToString(CultureInfo.InvariantCulture), "ImageInfos"); } } @@ -873,8 +828,6 @@ namespace Emby.Server.Implementations SocketFactory = new SocketFactory(LoggerFactory.CreateLogger("SocketFactory")); RegisterSingleInstance(SocketFactory); - RegisterSingleInstance(PowerManagement); - SecurityManager = new PluginSecurityManager(this, HttpClient, JsonSerializer, ApplicationPaths, LoggerFactory, FileSystemManager, CryptographyProvider); RegisterSingleInstance(SecurityManager); @@ -1235,8 +1188,7 @@ namespace Emby.Server.Implementations HttpClient, ZipClient, ProcessFactory, - 5000, - EnvironmentInfo); + 5000); MediaEncoder = mediaEncoder; RegisterSingleInstance(MediaEncoder); @@ -1310,7 +1262,6 @@ namespace Emby.Server.Implementations { if (!ServerConfigurationManager.Configuration.IsPortAuthorized) { - RegisterServerWithAdministratorAccess(); ServerConfigurationManager.Configuration.IsPortAuthorized = true; ConfigurationManager.SaveConfiguration(); } @@ -1381,12 +1332,11 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - Logger.LogError(ex, "Error getting plugin Id from {pluginName}.", plugin.GetType().FullName); + Logger.LogError(ex, "Error getting plugin Id from {PluginName}.", plugin.GetType().FullName); } } - var hasPluginConfiguration = plugin as IHasPluginConfiguration; - if (hasPluginConfiguration != null) + if (plugin is IHasPluginConfiguration hasPluginConfiguration) { hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s)); } @@ -1591,8 +1541,6 @@ namespace Emby.Server.Implementations /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> protected void OnConfigurationUpdated(object sender, EventArgs e) { - ConfigureAutorun(); - var requiresRestart = false; // Don't do anything if these haven't been set yet @@ -1698,25 +1646,25 @@ namespace Emby.Server.Implementations // Gets all plugin assemblies by first reading all bytes of the .dll and calling Assembly.Load against that // This will prevent the .dll file from getting locked, and allow us to replace it when needed - // Include composable parts in the Api assembly + // Include composable parts in the Api assembly list.Add(GetAssembly(typeof(ApiEntryPoint))); - // Include composable parts in the Dashboard assembly + // Include composable parts in the Dashboard assembly list.Add(GetAssembly(typeof(DashboardService))); - // Include composable parts in the Model assembly + // Include composable parts in the Model assembly list.Add(GetAssembly(typeof(SystemInfo))); - // Include composable parts in the Common assembly + // Include composable parts in the Common assembly list.Add(GetAssembly(typeof(IApplicationHost))); - // Include composable parts in the Controller assembly + // Include composable parts in the Controller assembly list.Add(GetAssembly(typeof(IServerApplicationHost))); - // Include composable parts in the Providers assembly + // Include composable parts in the Providers assembly list.Add(GetAssembly(typeof(ProviderUtils))); - // Include composable parts in the Photos assembly + // Include composable parts in the Photos assembly list.Add(GetAssembly(typeof(PhotoProvider))); // Emby.Server implementations @@ -1725,16 +1673,16 @@ namespace Emby.Server.Implementations // MediaEncoding list.Add(GetAssembly(typeof(MediaBrowser.MediaEncoding.Encoder.MediaEncoder))); - // Dlna + // Dlna list.Add(GetAssembly(typeof(DlnaEntryPoint))); - // Local metadata + // Local metadata list.Add(GetAssembly(typeof(BoxSetXmlSaver))); // Notifications list.Add(GetAssembly(typeof(NotificationManager))); - // Xbmc + // Xbmc list.Add(GetAssembly(typeof(ArtistNfoProvider))); list.AddRange(GetAssembliesWithPartsInternal().Select(i => new Tuple<Assembly, string>(i, null))); @@ -1805,13 +1753,13 @@ namespace Emby.Server.Implementations { var result = Version.Parse(FileVersionInfo.GetVersionInfo(path).FileVersion); - Logger.LogInformation("File {0} has version {1}", path, result); + Logger.LogInformation("File {Path} has version {Version}", path, result); return result; } catch (Exception ex) { - Logger.LogError(ex, "Error getting version number from {path}", path); + Logger.LogError(ex, "Error getting version number from {Path}", path); return new Version(1, 0); } @@ -1943,7 +1891,6 @@ namespace Emby.Server.Implementations CanLaunchWebBrowser = CanLaunchWebBrowser, WanAddress = wanAddress, HasUpdateAvailable = HasUpdateAvailable, - SupportsAutoRunAtStartup = SupportsAutoRunAtStartup, TranscodingTempPath = ApplicationPaths.TranscodingTempPath, ServerName = FriendlyName, LocalAddress = localAddress, @@ -2225,32 +2172,6 @@ namespace Emby.Server.Implementations protected abstract void ShutdownInternal(); - /// <summary> - /// Registers the server with administrator access. - /// </summary> - private void RegisterServerWithAdministratorAccess() - { - Logger.LogInformation("Requesting administrative access to authorize http server"); - - try - { - AuthorizeServer(); - } - catch (NotImplementedException) - { - - } - catch (Exception ex) - { - Logger.LogError(ex, "Error authorizing server"); - } - } - - protected virtual void AuthorizeServer() - { - throw new NotImplementedException(); - } - public event EventHandler HasUpdateAvailableChanged; private bool _hasUpdateAvailable; @@ -2297,24 +2218,7 @@ namespace Emby.Server.Implementations } /// <summary> - /// Configures the automatic run at startup. - /// </summary> - /// <param name="autorun">if set to <c>true</c> [autorun].</param> - protected void ConfigureAutoRunAtStartup(bool autorun) - { - if (SupportsAutoRunAtStartup) - { - ConfigureAutoRunInternal(autorun); - } - } - - protected virtual void ConfigureAutoRunInternal(bool autorun) - { - throw new NotImplementedException(); - } - - /// <summary> - /// This returns localhost in the case of no external dns, and the hostname if the + /// This returns localhost in the case of no external dns, and the hostname if the /// dns is prefixed with a valid Uri prefix. /// </summary> /// <param name="externalDns">The external dns prefix to get the hostname of.</param> @@ -2413,15 +2317,14 @@ namespace Emby.Server.Implementations { var type = GetType(); - //LoggerFactory.AddConsoleOutput(); - Logger.LogInformation("Disposing " + type.Name); + Logger.LogInformation("Disposing {Type}", type.Name); var parts = DisposableParts.Distinct().Where(i => i.GetType() != type).ToList(); DisposableParts.Clear(); foreach (var part in parts) { - Logger.LogInformation("Disposing " + part.GetType().Name); + Logger.LogInformation("Disposing {Type}", part.GetType().Name); try { @@ -2429,57 +2332,10 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - Logger.LogError(ex, "Error disposing {0}", part.GetType().Name); - } - } - } - } - - private Dictionary<string, string> _values; - public string GetValue(string name) - { - if (_values == null) - { - _values = LoadValues(); - } - - string value; - - if (_values.TryGetValue(name, out value)) - { - return value; - } - - return null; - } - - // TODO: @bond Remove? - private Dictionary<string, string> LoadValues() - { - Dictionary<string, string> values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); - - using (var stream = typeof(ApplicationHost).Assembly.GetManifestResourceStream(typeof(ApplicationHost).Namespace + ".values.txt")) - { - using (var reader = new StreamReader(stream)) - { - while (!reader.EndOfStream) - { - var line = reader.ReadLine(); - if (string.IsNullOrEmpty(line)) - { - continue; - } - - var index = line.IndexOf('='); - if (index != -1) - { - values[line.Substring(0, index)] = line.Substring(index + 1); - } + Logger.LogError(ex, "Error disposing {Type}", part.GetType().Name); } } } - - return values; } } diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 241505019..da3a4da07 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -45,7 +45,6 @@ <EmbeddedResource Include="TextEncoding\NLangDetect\Profiles\*" /> <EmbeddedResource Include="TextEncoding\NLangDetect\Utils\messages.properties" /> <EmbeddedResource Include="Localization\Ratings\*.txt" /> - <EmbeddedResource Include="values.txt" /> </ItemGroup> </Project> diff --git a/Emby.Server.Implementations/EntryPoints/KeepServerAwake.cs b/Emby.Server.Implementations/EntryPoints/KeepServerAwake.cs deleted file mode 100644 index a6dadcef0..000000000 --- a/Emby.Server.Implementations/EntryPoints/KeepServerAwake.cs +++ /dev/null @@ -1,65 +0,0 @@ -using MediaBrowser.Controller; -using MediaBrowser.Controller.Plugins; -using MediaBrowser.Controller.Session; -using Microsoft.Extensions.Logging; -using System; -using System.Linq; -using MediaBrowser.Model.System; -using MediaBrowser.Model.Threading; - -namespace Emby.Server.Implementations.EntryPoints -{ - public class KeepServerAwake : IServerEntryPoint - { - private readonly ISessionManager _sessionManager; - private readonly ILogger _logger; - private ITimer _timer; - private readonly IServerApplicationHost _appHost; - private readonly ITimerFactory _timerFactory; - private readonly IPowerManagement _powerManagement; - - public KeepServerAwake(ISessionManager sessionManager, ILogger logger, IServerApplicationHost appHost, ITimerFactory timerFactory, IPowerManagement powerManagement) - { - _sessionManager = sessionManager; - _logger = logger; - _appHost = appHost; - _timerFactory = timerFactory; - _powerManagement = powerManagement; - } - - public void Run() - { - _timer = _timerFactory.Create(OnTimerCallback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5)); - } - - private void OnTimerCallback(object state) - { - var now = DateTime.UtcNow; - - try - { - if (_sessionManager.Sessions.Any(i => (now - i.LastActivityDate).TotalMinutes < 15)) - { - _powerManagement.PreventSystemStandby(); - } - else - { - _powerManagement.AllowSystemStandby(); - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Error resetting system standby timer"); - } - } - - public void Dispose() - { - if (_timer != null) - { - _timer.Dispose(); - _timer = null; - } - } - } -} diff --git a/Emby.Server.Implementations/EnvironmentInfo/EnvironmentInfo.cs b/Emby.Server.Implementations/EnvironmentInfo/EnvironmentInfo.cs index 03e10e7ea..655867577 100644 --- a/Emby.Server.Implementations/EnvironmentInfo/EnvironmentInfo.cs +++ b/Emby.Server.Implementations/EnvironmentInfo/EnvironmentInfo.cs @@ -1,11 +1,9 @@ using System; -using System.IO; using MediaBrowser.Model.System; using System.Runtime.InteropServices; namespace Emby.Server.Implementations.EnvironmentInfo { - // TODO: Rework @bond public class EnvironmentInfo : IEnvironmentInfo { public EnvironmentInfo(MediaBrowser.Model.System.OperatingSystem operatingSystem) @@ -39,29 +37,6 @@ namespace Emby.Server.Implementations.EnvironmentInfo } } - public char PathSeparator - { - get - { - return Path.PathSeparator; - } - } - public Architecture SystemArchitecture { get { return RuntimeInformation.OSArchitecture; } } - - public string GetEnvironmentVariable(string name) - { - return Environment.GetEnvironmentVariable(name); - } - - public string StackTrace - { - get { return Environment.StackTrace; } - } - - public void SetProcessEnvironmentVariable(string name, string value) - { - Environment.SetEnvironmentVariable(name, value); - } } } diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index e5fd28997..ddda4b2c3 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -160,9 +160,9 @@ namespace Emby.Server.Implementations.Library list.Add(source); } - foreach (var source in list) + if (user != null) { - if (user != null) + foreach (var source in list) { if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)) { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 59f9fe86f..81a47bfea 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -75,7 +75,23 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private readonly IStreamHelper _streamHelper; - public EmbyTV(IServerApplicationHost appHost, IStreamHelper streamHelper, IMediaSourceManager mediaSourceManager, IAssemblyInfo assemblyInfo, ILogger logger, IJsonSerializer jsonSerializer, IPowerManagement powerManagement, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IMediaEncoder mediaEncoder, ITimerFactory timerFactory, IProcessFactory processFactory, ISystemEvents systemEvents) + public EmbyTV(IServerApplicationHost appHost, + IStreamHelper streamHelper, + IMediaSourceManager mediaSourceManager, + IAssemblyInfo assemblyInfo, + ILogger logger, + IJsonSerializer jsonSerializer, + IHttpClient httpClient, + IServerConfigurationManager config, + ILiveTvManager liveTvManager, + IFileSystem fileSystem, + ILibraryManager libraryManager, + ILibraryMonitor libraryMonitor, + IProviderManager providerManager, + IMediaEncoder mediaEncoder, + ITimerFactory timerFactory, + IProcessFactory processFactory, + ISystemEvents systemEvents) { Current = this; @@ -97,7 +113,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV _streamHelper = streamHelper; _seriesTimerProvider = new SeriesTimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers")); - _timerProvider = new TimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "timers"), _logger, timerFactory, powerManagement); + _timerProvider = new TimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "timers"), _logger, timerFactory); _timerProvider.TimerFired += _timerProvider_TimerFired; _config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs index 76a044c02..e4ab34770 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs @@ -9,7 +9,6 @@ using MediaBrowser.Model.Serialization; using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Threading; -using MediaBrowser.Model.System; namespace Emby.Server.Implementations.LiveTv.EmbyTV { @@ -20,14 +19,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV public event EventHandler<GenericEventArgs<TimerInfo>> TimerFired; private readonly ITimerFactory _timerFactory; - private readonly IPowerManagement _powerManagement; - public TimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath, ILogger logger1, ITimerFactory timerFactory, IPowerManagement powerManagement) + public TimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath, ILogger logger1, ITimerFactory timerFactory) : base(fileSystem, jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase)) { _logger = logger1; _timerFactory = timerFactory; - _powerManagement = powerManagement; } public void RestartTimers() @@ -36,7 +33,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV foreach (var item in GetAll().ToList()) { - AddOrUpdateSystemTimer(item, false); + AddOrUpdateSystemTimer(item); } } @@ -59,7 +56,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV public override void Update(TimerInfo item) { base.Update(item); - AddOrUpdateSystemTimer(item, false); + AddOrUpdateSystemTimer(item); } public void AddOrUpdate(TimerInfo item, bool resetTimer) @@ -90,7 +87,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } base.Add(item); - AddOrUpdateSystemTimer(item, true); + AddOrUpdateSystemTimer(item); } private bool ShouldStartTimer(TimerInfo item) @@ -104,7 +101,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return true; } - private void AddOrUpdateSystemTimer(TimerInfo item, bool scheduleSystemWakeTimer) + private void AddOrUpdateSystemTimer(TimerInfo item) { StopTimer(item); @@ -124,23 +121,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV var dueTime = startDate - now; StartTimer(item, dueTime); - - if (scheduleSystemWakeTimer && dueTime >= TimeSpan.FromMinutes(15)) - { - ScheduleSystemWakeTimer(startDate, item.Name); - } - } - - private void ScheduleSystemWakeTimer(DateTime startDate, string displayName) - { - try - { - _powerManagement.ScheduleWake(startDate.AddMinutes(-5), displayName); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error scheduling wake timer"); - } } private void StartTimer(TimerInfo item, TimeSpan dueTime) diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index b597a935a..a81a0bcbb 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1056,7 +1056,7 @@ namespace Emby.Server.Implementations.LiveTv var numComplete = 0; double progressPerService = _services.Length == 0 ? 0 - : 1 / _services.Length; + : 1.0 / _services.Length; var newChannelIdList = new List<Guid>(); var newProgramIdList = new List<Guid>(); @@ -1262,7 +1262,7 @@ namespace Emby.Server.Implementations.LiveTv } numComplete++; - double percent = numComplete / allChannelsList.Count; + double percent = numComplete / (double) allChannelsList.Count; progress.Report(85 * percent + 15); } @@ -1307,7 +1307,7 @@ namespace Emby.Server.Implementations.LiveTv } numComplete++; - double percent = numComplete / list.Count; + double percent = numComplete / (double) list.Count; progress.Report(100 * percent); } diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs index b754d7cb5..95395f96c 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs @@ -65,7 +65,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks foreach (var file in filesToDelete) { - double percent = index / filesToDelete.Count; + double percent = index / (double) filesToDelete.Count; progress.Report(100 * percent); diff --git a/Emby.Server.Implementations/ServerApplicationPaths.cs b/Emby.Server.Implementations/ServerApplicationPaths.cs index f5986f943..8a0f2671a 100644 --- a/Emby.Server.Implementations/ServerApplicationPaths.cs +++ b/Emby.Server.Implementations/ServerApplicationPaths.cs @@ -13,8 +13,13 @@ namespace Emby.Server.Implementations /// <summary> /// Initializes a new instance of the <see cref="BaseApplicationPaths" /> class. /// </summary> - public ServerApplicationPaths(string programDataPath, string appFolderPath, string applicationResourcesPath, string logDirectoryPath = null) - : base(programDataPath, appFolderPath, logDirectoryPath) + public ServerApplicationPaths( + string programDataPath, + string appFolderPath, + string applicationResourcesPath, + string logDirectoryPath = null, + string configurationDirectoryPath = null) + : base(programDataPath, appFolderPath, logDirectoryPath, configurationDirectoryPath) { ApplicationResourcesPath = applicationResourcesPath; } diff --git a/Emby.Server.Implementations/Session/FirebaseSessionController.cs b/Emby.Server.Implementations/Session/FirebaseSessionController.cs deleted file mode 100644 index cfe513305..000000000 --- a/Emby.Server.Implementations/Session/FirebaseSessionController.cs +++ /dev/null @@ -1,131 +0,0 @@ -using MediaBrowser.Controller.Session; -using MediaBrowser.Model.Net; -using MediaBrowser.Common.Net; -using MediaBrowser.Model.Serialization; -using System; -using System.Threading; -using System.Threading.Tasks; -using System.Text; -using MediaBrowser.Common; - -namespace Emby.Server.Implementations.Session -{ - public class FirebaseSessionController : ISessionController - { - private readonly IHttpClient _httpClient; - private readonly IJsonSerializer _json; - private readonly ISessionManager _sessionManager; - - public SessionInfo Session { get; private set; } - - private readonly string _token; - - private IApplicationHost _appHost; - private string _senderId; - private string _applicationId; - - public FirebaseSessionController(IHttpClient httpClient, - IApplicationHost appHost, - IJsonSerializer json, - SessionInfo session, - string token, ISessionManager sessionManager) - { - _httpClient = httpClient; - _json = json; - _appHost = appHost; - Session = session; - _token = token; - _sessionManager = sessionManager; - - _applicationId = _appHost.GetValue("firebase_applicationid"); - _senderId = _appHost.GetValue("firebase_senderid"); - } - - public static bool IsSupported(IApplicationHost appHost) - { - return !string.IsNullOrEmpty(appHost.GetValue("firebase_applicationid")) && !string.IsNullOrEmpty(appHost.GetValue("firebase_senderid")); - } - - public bool IsSessionActive - { - get - { - return (DateTime.UtcNow - Session.LastActivityDate).TotalDays <= 3; - } - } - - public bool SupportsMediaControl - { - get { return true; } - } - - public async Task SendMessage<T>(string name, string messageId, T data, ISessionController[] allControllers, CancellationToken cancellationToken) - { - if (!IsSessionActive) - { - return; - } - - if (string.IsNullOrEmpty(_senderId) || string.IsNullOrEmpty(_applicationId)) - { - return; - } - - foreach (var controller in allControllers) - { - // Don't send if there's an active web socket connection - if ((controller is WebSocketController) && controller.IsSessionActive) - { - return; - } - } - - var msg = new WebSocketMessage<T> - { - Data = data, - MessageType = name, - MessageId = messageId, - ServerId = _appHost.SystemId - }; - - var req = new FirebaseBody<T> - { - to = _token, - data = msg - }; - - var byteArray = Encoding.UTF8.GetBytes(_json.SerializeToString(req)); - - var enableLogging = false; - -#if DEBUG - enableLogging = true; -#endif - - var options = new HttpRequestOptions - { - Url = "https://fcm.googleapis.com/fcm/send", - RequestContentType = "application/json", - RequestContentBytes = byteArray, - CancellationToken = cancellationToken, - LogRequest = enableLogging, - LogResponse = enableLogging, - LogErrors = enableLogging - }; - - options.RequestHeaders["Authorization"] = string.Format("key={0}", _applicationId); - options.RequestHeaders["Sender"] = string.Format("id={0}", _senderId); - - using (var response = await _httpClient.Post(options).ConfigureAwait(false)) - { - - } - } - } - - internal class FirebaseBody<T> - { - public string to { get; set; } - public WebSocketMessage<T> data { get; set; } - } -} diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 419c24f13..7321e9f86 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1574,14 +1574,6 @@ namespace Emby.Server.Implementations.Session { session.Capabilities = capabilities; - if (!string.IsNullOrEmpty(capabilities.PushToken)) - { - if (string.Equals(capabilities.PushTokenType, "firebase", StringComparison.OrdinalIgnoreCase) && FirebaseSessionController.IsSupported(_appHost)) - { - EnsureFirebaseController(session, capabilities.PushToken); - } - } - if (saveCapabilities) { CapabilitiesChanged?.Invoke(this, new SessionEventArgs @@ -1600,11 +1592,6 @@ namespace Emby.Server.Implementations.Session } } - private void EnsureFirebaseController(SessionInfo session, string token) - { - session.EnsureController<FirebaseSessionController>(s => new FirebaseSessionController(_httpClient, _appHost, _jsonSerializer, s, token, this)); - } - private ClientCapabilities GetSavedCapabilities(string deviceId) { return _deviceManager.GetCapabilities(deviceId); diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs index 8cacc1124..275bd83ea 100644 --- a/Emby.Server.Implementations/Udp/UdpServer.cs +++ b/Emby.Server.Implementations/Udp/UdpServer.cs @@ -40,6 +40,7 @@ namespace Emby.Server.Implementations.Udp _json = json; _socketFactory = socketFactory; + AddMessageResponder("who is JellyfinServer?", true, RespondToV2Message); AddMessageResponder("who is EmbyServer?", true, RespondToV2Message); AddMessageResponder("who is MediaBrowserServer_v2?", false, RespondToV2Message); } diff --git a/Emby.Server.Implementations/values.txt b/Emby.Server.Implementations/values.txt deleted file mode 100644 index e69de29bb..000000000 --- a/Emby.Server.Implementations/values.txt +++ /dev/null diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index b54634387..64e03f22e 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -11,8 +11,8 @@ namespace Jellyfin.Server { public class CoreAppHost : ApplicationHost { - public CoreAppHost(ServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory, StartupOptions options, IFileSystem fileSystem, IPowerManagement powerManagement, IEnvironmentInfo environmentInfo, MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, ISystemEvents systemEvents, MediaBrowser.Common.Net.INetworkManager networkManager) - : base(applicationPaths, loggerFactory, options, fileSystem, powerManagement, environmentInfo, imageEncoder, systemEvents, networkManager) + public CoreAppHost(ServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory, StartupOptions options, IFileSystem fileSystem, IEnvironmentInfo environmentInfo, MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, ISystemEvents systemEvents, MediaBrowser.Common.Net.INetworkManager networkManager) + : base(applicationPaths, loggerFactory, options, fileSystem, environmentInfo, imageEncoder, systemEvents, networkManager) { } diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 8ecae1b5b..98c578e83 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -25,6 +25,7 @@ <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" /> <PackageReference Include="Serilog.AspNetCore" Version="2.1.1" /> <PackageReference Include="Serilog.Settings.Configuration" Version="3.0.1" /> + <PackageReference Include="Serilog.Sinks.Async" Version="1.3.0" /> <PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" /> <PackageReference Include="Serilog.Sinks.File" Version="4.0.0" /> <PackageReference Include="SkiaSharp" Version="1.68.0" /> diff --git a/Jellyfin.Server/PowerManagement.cs b/Jellyfin.Server/PowerManagement.cs deleted file mode 100644 index c27c51893..000000000 --- a/Jellyfin.Server/PowerManagement.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using MediaBrowser.Model.System; - -namespace Jellyfin.Server.Native -{ - public class PowerManagement : IPowerManagement - { - public void PreventSystemStandby() - { - - } - - public void AllowSystemStandby() - { - - } - - public void ScheduleWake(DateTime wakeTimeUtc, string displayName) - { - - } - } -} diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 2dd4d9af6..d74315755 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -13,7 +13,6 @@ 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;
@@ -47,6 +46,8 @@ namespace Jellyfin.Server }
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");
@@ -71,7 +72,6 @@ namespace Jellyfin.Server _loggerFactory,
options,
fileSystem,
- new PowerManagement(),
environmentInfo,
new NullImageEncoder(),
new SystemEvents(_loggerFactory.CreateLogger("SystemEvents")),
@@ -102,43 +102,66 @@ namespace Jellyfin.Server private static ServerApplicationPaths createApplicationPaths(StartupOptions options)
{
- string programDataPath;
- if (options.ContainsOption("-programdata"))
+ string programDataPath = Environment.GetEnvironmentVariable("JELLYFIN_DATA_PATH");
+ if (string.IsNullOrEmpty(programDataPath))
{
- programDataPath = options.GetOption("-programdata");
- }
- else
- {
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ if (options.ContainsOption("-programdata"))
{
- programDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
+ programDataPath = options.GetOption("-programdata");
}
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))
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ programDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
+ }
+ else
{
- programDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share");
+ // $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;
}
- programDataPath = Path.Combine(programDataPath, "jellyfin");
}
string logDir = Environment.GetEnvironmentVariable("JELLYFIN_LOG_DIR");
if (string.IsNullOrEmpty(logDir))
{
- logDir = Path.Combine(programDataPath, "logs");
- // Ensure logDir exists
- Directory.CreateDirectory(logDir);
- // $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager
- Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", 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);
+ return new ServerApplicationPaths(programDataPath, appPath, appPath, logDir, configDir);
}
private static async Task createLogger(IApplicationPaths appPaths)
@@ -173,10 +196,10 @@ namespace Jellyfin.Server {
Serilog.Log.Logger = new LoggerConfiguration()
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}")
- .WriteTo.File(
+ .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}")
+ outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}"))
.Enrich.FromLogContext()
.CreateLogger();
@@ -233,7 +256,7 @@ namespace Jellyfin.Server }
}
- public static void Shutdown()
+ public static void Shutdown()
{
ApplicationTaskCompletionSource.SetResult(true);
}
diff --git a/Jellyfin.Server/Resources/Configuration/logging.json b/Jellyfin.Server/Resources/Configuration/logging.json index 78f99b2ad..d16991277 100644 --- a/Jellyfin.Server/Resources/Configuration/logging.json +++ b/Jellyfin.Server/Resources/Configuration/logging.json @@ -2,16 +2,25 @@ "Serilog": { "MinimumLevel": "Information", "WriteTo": [ - { "Name": "Console", + { + "Name": "Console", "Args": { "outputTemplate": "[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}" } }, - { "Name": "File", + { + "Name": "Async", "Args": { - "path": "%JELLYFIN_LOG_DIR%//log_.log", - "rollingInterval": "Day", - "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}" + "configure": [ + { + "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.Api/NewsService.cs b/MediaBrowser.Api/NewsService.cs deleted file mode 100644 index cbc77bfc9..000000000 --- a/MediaBrowser.Api/NewsService.cs +++ /dev/null @@ -1,46 +0,0 @@ -using MediaBrowser.Model.News; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Services; - -namespace MediaBrowser.Api -{ - [Route("/News/Product", "GET", Summary = "Gets the latest product news.")] - public class GetProductNews : IReturn<QueryResult<NewsItem>> - { - /// <summary> - /// Skips over a given number of items within the results. Use for paging. - /// </summary> - /// <value>The start index.</value> - [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? StartIndex { get; set; } - - /// <summary> - /// The maximum number of items to return - /// </summary> - /// <value>The limit.</value> - [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? Limit { get; set; } - } - - public class NewsService : BaseApiService - { - private readonly INewsService _newsService; - - public NewsService(INewsService newsService) - { - _newsService = newsService; - } - - public object Get(GetProductNews request) - { - var result = _newsService.GetProductNews(new NewsQuery - { - StartIndex = request.StartIndex, - Limit = request.Limit - - }); - - return ToOptimizedResult(result); - } - } -} diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 28e75bf55..fa6502bda 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -590,6 +590,22 @@ namespace MediaBrowser.Api.Playback } /// <summary> + /// Parses query parameters as StreamOptions + /// <summary> + /// <param name="request">The stream request.</param> + private void ParseStreamOptions(StreamRequest request) + { + foreach (var param in Request.QueryString) { + if (Char.IsLower(param.Name[0])) { + // This was probably not parsed initially and should be a StreamOptions + // TODO: This should be incorporated either in the lower framework for parsing requests + // or the generated URL should correctly serialize it + request.StreamOptions[param.Name] = param.Value; + } + } + } + + /// <summary> /// Parses the dlna headers. /// </summary> /// <param name="request">The request.</param> @@ -667,6 +683,8 @@ namespace MediaBrowser.Api.Playback ParseParams(request); } + ParseStreamOptions(request); + var url = Request.PathInfo; if (string.IsNullOrEmpty(request.AudioCodec)) diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs index 39d69ea15..c4f760b15 100644 --- a/MediaBrowser.Common/IApplicationHost.cs +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -135,7 +135,5 @@ namespace MediaBrowser.Common object CreateInstance(Type type); PackageVersionClass SystemUpdateLevel { get; } - - string GetValue(string name); } } diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index f625a17ee..a485fa8be 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <Authors>Jellyfin Contributors</Authors> + <PackageId>Jellyfin.Common</PackageId> + </PropertyGroup> + <ItemGroup> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> </ItemGroup> diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index e888a5582..51fb73df7 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -64,7 +64,7 @@ namespace MediaBrowser.Controller.Entities.Movies } public override double GetDefaultPrimaryImageAspectRatio() - => 2 / 3; + => 2.0 / 3; public override UnratedItem GetBlockUnratedType() { diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index 4f743991b..1d7e1cff1 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -51,7 +51,7 @@ namespace MediaBrowser.Controller.Entities.Movies return 0; } - return 2 / 3; + return 2.0 / 3; } protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken) diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index 00e055c51..0a367c594 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -111,7 +111,7 @@ namespace MediaBrowser.Controller.Entities.TV return 0; } - return 16 / 9; + return 16.0 / 9; } public override List<string> GetUserDataKeys() diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs index 2ef268ed1..d84ac187b 100644 --- a/MediaBrowser.Controller/Entities/Trailer.cs +++ b/MediaBrowser.Controller/Entities/Trailer.cs @@ -21,7 +21,7 @@ namespace MediaBrowser.Controller.Entities public TrailerType[] TrailerTypes { get; set; } public override double GetDefaultPrimaryImageAspectRatio() - => 2 / 3; + => 2.0 / 3; public override UnratedItem GetBlockUnratedType() { diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 830c160e3..4dc559031 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -24,12 +24,6 @@ namespace MediaBrowser.Controller Task<PublicSystemInfo> GetPublicSystemInfo(CancellationToken cancellationToken); - /// <summary> - /// Gets a value indicating whether [supports automatic run at startup]. - /// </summary> - /// <value><c>true</c> if [supports automatic run at startup]; otherwise, <c>false</c>.</value> - bool SupportsAutoRunAtStartup { get; } - bool CanLaunchWebBrowser { get; } /// <summary> diff --git a/MediaBrowser.Controller/IServerApplicationPaths.cs b/MediaBrowser.Controller/IServerApplicationPaths.cs index 5fb7968dd..e97943cbe 100644 --- a/MediaBrowser.Controller/IServerApplicationPaths.cs +++ b/MediaBrowser.Controller/IServerApplicationPaths.cs @@ -12,7 +12,6 @@ namespace MediaBrowser.Controller /// <summary> /// Gets the application resources path. This is the path to the folder containing resources that are deployed as part of the application - /// For example, this folder contains dashboard-ui and swagger-ui /// </summary> /// <value>The application resources path.</value> string ApplicationResourcesPath { get; } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs index 4dc6c7517..c4a75d199 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs @@ -54,11 +54,11 @@ namespace MediaBrowser.Controller.LiveTv if (string.Equals(serviceName, EmbyServiceName, StringComparison.OrdinalIgnoreCase) || string.Equals(serviceName, "Next Pvr", StringComparison.OrdinalIgnoreCase)) { - return 2 / 3; + return 2.0 / 3; } else { - return 16 / 9; + return 16.0 / 9; } } diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 4567f62dd..3decbc42f 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <Authors>Jellyfin Contributors</Authors> + <PackageId>Jellyfin.Controller</PackageId> + </PropertyGroup> + <ItemGroup> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" /> diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index b68961889..578e9f264 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -51,7 +51,7 @@ namespace MediaBrowser.MediaEncoding.Encoder return false; } - _logger.LogInformation("ffmpeg info: {0}", output); + _logger.LogDebug("ffmpeg output: {Output}", output); if (output.IndexOf("Libav developers", StringComparison.OrdinalIgnoreCase) != -1) { @@ -160,10 +160,12 @@ namespace MediaBrowser.MediaEncoding.Encoder Arguments = arguments, IsHidden = true, ErrorDialog = false, - RedirectStandardOutput = true + RedirectStandardOutput = true, + // ffmpeg uses stderr to log info, don't show this + RedirectStandardError = true }); - _logger.LogInformation("Running {Path} {Arguments}", path, arguments); + _logger.LogDebug("Running {Path} {Arguments}", path, arguments); using (process) { @@ -175,7 +177,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } catch { - _logger.LogInformation("Killing process {path} {arguments}", path, arguments); + _logger.LogWarning("Killing process {Path} {Arguments}", path, arguments); // Hate having to do this try diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index a93dd9742..a13795548 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -70,7 +70,6 @@ namespace MediaBrowser.MediaEncoding.Encoder private readonly string _originalFFMpegPath; private readonly string _originalFFProbePath; private readonly int DefaultImageExtractionTimeoutMs; - private readonly IEnvironmentInfo _environmentInfo; public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, @@ -89,8 +88,7 @@ namespace MediaBrowser.MediaEncoding.Encoder IHttpClient httpClient, IZipClient zipClient, IProcessFactory processFactory, - int defaultImageExtractionTimeoutMs, - IEnvironmentInfo environmentInfo) + int defaultImageExtractionTimeoutMs) { _logger = logger; _jsonSerializer = jsonSerializer; @@ -107,46 +105,13 @@ namespace MediaBrowser.MediaEncoding.Encoder _zipClient = zipClient; _processFactory = processFactory; DefaultImageExtractionTimeoutMs = defaultImageExtractionTimeoutMs; - _environmentInfo = environmentInfo; FFProbePath = ffProbePath; FFMpegPath = ffMpegPath; _originalFFProbePath = ffProbePath; _originalFFMpegPath = ffMpegPath; - _hasExternalEncoder = hasExternalEncoder; } - private readonly object _logLock = new object(); - public void SetLogFilename(string name) - { - lock (_logLock) - { - try - { - _environmentInfo.SetProcessEnvironmentVariable("FFREPORT", "file=" + name + ":level=32"); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error setting FFREPORT environment variable"); - } - } - } - - public void ClearLogFilename() - { - lock (_logLock) - { - try - { - _environmentInfo.SetProcessEnvironmentVariable("FFREPORT", null); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error setting FFREPORT environment variable"); - } - } - } - public string EncoderLocationType { get @@ -362,7 +327,7 @@ namespace MediaBrowser.MediaEncoding.Encoder private Tuple<string, string> GetPathsFromDirectory(string path) { - // Since we can't predict the file extension, first try directly within the folder + // Since we can't predict the file extension, first try directly within the folder // If that doesn't pan out, then do a recursive search var files = FileSystem.GetFilePaths(path); @@ -525,7 +490,7 @@ namespace MediaBrowser.MediaEncoding.Encoder CreateNoWindow = true, UseShellExecute = false, - // Must consume both or ffmpeg may hang due to deadlocks. See comments below. + // Must consume both or ffmpeg may hang due to deadlocks. See comments below. RedirectStandardOutput = true, FileName = FFProbePath, Arguments = string.Format(args, probeSizeArgument, inputPath).Trim(), @@ -648,7 +613,7 @@ namespace MediaBrowser.MediaEncoding.Encoder var tempExtractPath = Path.Combine(ConfigurationManager.ApplicationPaths.TempDirectory, Guid.NewGuid() + ".jpg"); FileSystem.CreateDirectory(FileSystem.GetDirectoryName(tempExtractPath)); - // apply some filters to thumbnail extracted below (below) crop any black lines that we made and get the correct ar then scale to width 600. + // apply some filters to thumbnail extracted below (below) crop any black lines that we made and get the correct ar then scale to width 600. // This filter chain may have adverse effects on recorded tv thumbnails if ar changes during presentation ex. commercials @ diff ar var vf = "scale=600:trunc(600/dar/2)*2"; @@ -676,7 +641,7 @@ namespace MediaBrowser.MediaEncoding.Encoder break; } } - + var mapArg = imageStreamIndex.HasValue ? (" -map 0:v:" + imageStreamIndex.Value.ToString(CultureInfo.InvariantCulture)) : string.Empty; var enableThumbnail = !new List<string> { "wtv" }.Contains(container ?? string.Empty, StringComparer.OrdinalIgnoreCase); diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 43f775392..68faffa91 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -1,26 +1,25 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Model.Entities; -using Microsoft.Extensions.Logging; -using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Serialization; -using System; +using System; using System.Collections.Concurrent; -using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.IO; using MediaBrowser.Model.Diagnostics; using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Text; +using Microsoft.Extensions.Logging; namespace MediaBrowser.MediaEncoding.Subtitles { @@ -37,7 +36,17 @@ namespace MediaBrowser.MediaEncoding.Subtitles private readonly IProcessFactory _processFactory; private readonly ITextEncoding _textEncoding; - public SubtitleEncoder(ILibraryManager libraryManager, ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IJsonSerializer json, IHttpClient httpClient, IMediaSourceManager mediaSourceManager, IProcessFactory processFactory, ITextEncoding textEncoding) + public SubtitleEncoder( + ILibraryManager libraryManager, + ILogger logger, + IApplicationPaths appPaths, + IFileSystem fileSystem, + IMediaEncoder mediaEncoder, + IJsonSerializer json, + IHttpClient httpClient, + IMediaSourceManager mediaSourceManager, + IProcessFactory processFactory, + ITextEncoding textEncoding) { _libraryManager = libraryManager; _logger = logger; @@ -46,6 +55,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles _mediaEncoder = mediaEncoder; _json = json; _httpClient = httpClient; + _mediaSourceManager = mediaSourceManager; _processFactory = processFactory; _textEncoding = textEncoding; } @@ -99,7 +109,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles if (endTimeTicks.HasValue) { - var endTime = endTimeTicks.Value; + long endTime = endTimeTicks.Value; track.TrackEvents = track.TrackEvents .TakeWhile(i => i.StartPositionTicks <= endTime) @@ -139,48 +149,51 @@ namespace MediaBrowser.MediaEncoding.Subtitles var subtitle = await GetSubtitleStream(mediaSource, subtitleStream, cancellationToken) .ConfigureAwait(false); - var inputFormat = subtitle.Item2; + var inputFormat = subtitle.format; var writer = TryGetWriter(outputFormat); // Return the original if we don't have any way of converting it if (writer == null) { - return subtitle.Item1; + return subtitle.stream; } // Return the original if the same format is being requested // Character encoding was already handled in GetSubtitleStream if (string.Equals(inputFormat, outputFormat, StringComparison.OrdinalIgnoreCase)) { - return subtitle.Item1; + return subtitle.stream; } - using (var stream = subtitle.Item1) + using (var stream = subtitle.stream) { - return ConvertSubtitles(stream, inputFormat, outputFormat, startTimeTicks, endTimeTicks, preserveOriginalTimestamps, cancellationToken); + return ConvertSubtitles(stream, inputFormat, outputFormat, startTimeTicks, endTimeTicks, preserveOriginalTimestamps, cancellationToken); } } - private async Task<Tuple<Stream, string>> GetSubtitleStream(MediaSourceInfo mediaSource, + private async Task<(Stream stream, string format)> GetSubtitleStream( + MediaSourceInfo mediaSource, MediaStream subtitleStream, CancellationToken cancellationToken) { - var inputFiles = new[] { mediaSource.Path }; + string[] inputFiles; - if (mediaSource.VideoType.HasValue) + if (mediaSource.VideoType.HasValue + && (mediaSource.VideoType.Value == VideoType.BluRay || mediaSource.VideoType.Value == VideoType.Dvd)) { - if (mediaSource.VideoType.Value == VideoType.BluRay || mediaSource.VideoType.Value == VideoType.Dvd) - { - var mediaSourceItem = (Video)_libraryManager.GetItemById(new Guid(mediaSource.Id)); - inputFiles = mediaSourceItem.GetPlayableStreamFileNames(_mediaEncoder).ToArray(); - } + var mediaSourceItem = (Video)_libraryManager.GetItemById(new Guid(mediaSource.Id)); + inputFiles = mediaSourceItem.GetPlayableStreamFileNames(_mediaEncoder); + } + else + { + inputFiles = new[] { mediaSource.Path }; } var fileInfo = await GetReadableFile(mediaSource.Path, inputFiles, mediaSource.Protocol, subtitleStream, cancellationToken).ConfigureAwait(false); - var stream = await GetSubtitleStream(fileInfo.Item1, subtitleStream.Language, fileInfo.Item2, fileInfo.Item4, cancellationToken).ConfigureAwait(false); + var stream = await GetSubtitleStream(fileInfo.Path, subtitleStream.Language, fileInfo.Protocol, fileInfo.IsExternal, cancellationToken).ConfigureAwait(false); - return new Tuple<Stream, string>(stream, fileInfo.Item3); + return (stream, fileInfo.Format); } private async Task<Stream> GetSubtitleStream(string path, string language, MediaProtocol protocol, bool requiresCharset, CancellationToken cancellationToken) @@ -195,15 +208,13 @@ namespace MediaBrowser.MediaEncoding.Subtitles if (!string.IsNullOrEmpty(charset)) { using (var inputStream = new MemoryStream(bytes)) + using (var reader = new StreamReader(inputStream, _textEncoding.GetEncodingFromCharset(charset))) { - using (var reader = new StreamReader(inputStream, _textEncoding.GetEncodingFromCharset(charset))) - { - var text = await reader.ReadToEndAsync().ConfigureAwait(false); + var text = await reader.ReadToEndAsync().ConfigureAwait(false); - bytes = Encoding.UTF8.GetBytes(text); + bytes = Encoding.UTF8.GetBytes(text); - return new MemoryStream(bytes); - } + return new MemoryStream(bytes); } } } @@ -211,7 +222,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles return _fileSystem.OpenRead(path); } - private async Task<Tuple<string, MediaProtocol, string, bool>> GetReadableFile(string mediaPath, + private async Task<SubtitleInfo> GetReadableFile( + string mediaPath, string[] inputFiles, MediaProtocol protocol, MediaStream subtitleStream, @@ -226,30 +238,30 @@ namespace MediaBrowser.MediaEncoding.Subtitles string.Equals(subtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase) || string.Equals(subtitleStream.Codec, "srt", StringComparison.OrdinalIgnoreCase)) { - // Extract + // Extract outputCodec = "copy"; outputFormat = subtitleStream.Codec; } else if (string.Equals(subtitleStream.Codec, "subrip", StringComparison.OrdinalIgnoreCase)) { - // Extract + // Extract outputCodec = "copy"; outputFormat = "srt"; } else { - // Extract + // Extract outputCodec = "srt"; outputFormat = "srt"; } - // Extract + // Extract var outputPath = GetSubtitleCachePath(mediaPath, protocol, subtitleStream.Index, "." + outputFormat); await ExtractTextSubtitle(inputFiles, protocol, subtitleStream.Index, outputCodec, outputPath, cancellationToken) .ConfigureAwait(false); - return new Tuple<string, MediaProtocol, string, bool>(outputPath, MediaProtocol.File, outputFormat, false); + return new SubtitleInfo(outputPath, MediaProtocol.File, outputFormat, false); } var currentFormat = (Path.GetExtension(subtitleStream.Path) ?? subtitleStream.Codec) @@ -257,15 +269,31 @@ namespace MediaBrowser.MediaEncoding.Subtitles if (GetReader(currentFormat, false) == null) { - // Convert + // Convert var outputPath = GetSubtitleCachePath(mediaPath, protocol, subtitleStream.Index, ".srt"); await ConvertTextSubtitleToSrt(subtitleStream.Path, subtitleStream.Language, protocol, outputPath, cancellationToken).ConfigureAwait(false); - return new Tuple<string, MediaProtocol, string, bool>(outputPath, MediaProtocol.File, "srt", true); + return new SubtitleInfo(outputPath, MediaProtocol.File, "srt", true); + } + + return new SubtitleInfo(subtitleStream.Path, protocol, currentFormat, true); + } + + private struct SubtitleInfo + { + public SubtitleInfo(string path, MediaProtocol protocol, string format, bool isExternal) + { + Path = path; + Protocol = protocol; + Format = format; + IsExternal = isExternal; } - return new Tuple<string, MediaProtocol, string, bool>(subtitleStream.Path, protocol, currentFormat, true); + public string Path { get; set; } + public MediaProtocol Protocol { get; set; } + public string Format { get; set; } + public bool IsExternal { get; set; } } private ISubtitleParser GetReader(string format, bool throwIfMissing) @@ -471,7 +499,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles } catch (IOException ex) { - _logger.LogError(ex, "Error deleting converted subtitle {0}", outputPath); + _logger.LogError(ex, "Error deleting converted subtitle {Path}", outputPath); } } } @@ -482,7 +510,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles if (failed) { - var msg = string.Format("ffmpeg subtitle conversion failed for {0}", inputPath); + var msg = string.Format("ffmpeg subtitle conversion failed for {Path}", inputPath); _logger.LogError(msg); @@ -490,7 +518,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles } await SetAssFont(outputPath).ConfigureAwait(false); - _logger.LogInformation("ffmpeg subtitle conversion succeeded for {0}", inputPath); + _logger.LogInformation("ffmpeg subtitle conversion succeeded for {Path}", inputPath); } /// <summary> @@ -504,8 +532,13 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> /// <exception cref="System.ArgumentException">Must use inputPath list overload</exception> - private async Task ExtractTextSubtitle(string[] inputFiles, MediaProtocol protocol, int subtitleStreamIndex, - string outputCodec, string outputPath, CancellationToken cancellationToken) + private async Task ExtractTextSubtitle( + string[] inputFiles, + MediaProtocol protocol, + int subtitleStreamIndex, + string outputCodec, + string outputPath, + CancellationToken cancellationToken) { var semaphore = GetLock(outputPath); @@ -524,8 +557,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles } } - private async Task ExtractTextSubtitleInternal(string inputPath, int subtitleStreamIndex, - string outputCodec, string outputPath, CancellationToken cancellationToken) + private async Task ExtractTextSubtitleInternal( + string inputPath, + int subtitleStreamIndex, + string outputCodec, + string outputPath, + CancellationToken cancellationToken) { if (string.IsNullOrEmpty(inputPath)) { @@ -553,7 +590,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles ErrorDialog = false }); - _logger.LogInformation("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); + _logger.LogInformation("{File} {Arguments}", process.StartInfo.FileName, process.StartInfo.Arguments); try { @@ -572,7 +609,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles { try { - _logger.LogInformation("Killing ffmpeg subtitle extraction process"); + _logger.LogWarning("Killing ffmpeg subtitle extraction process"); process.Kill(); } @@ -594,7 +631,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles try { - _logger.LogInformation("Deleting extracted subtitle due to failure: {0}", outputPath); + _logger.LogWarning("Deleting extracted subtitle due to failure: {Path}", outputPath); _fileSystem.DeleteFile(outputPath); } catch (FileNotFoundException) @@ -603,7 +640,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles } catch (IOException ex) { - _logger.LogError(ex, "Error deleting extracted subtitle {0}", outputPath); + _logger.LogError(ex, "Error deleting extracted subtitle {Path}", outputPath); } } else if (!_fileSystem.FileExists(outputPath)) @@ -613,7 +650,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles if (failed) { - var msg = string.Format("ffmpeg subtitle extraction failed for {0} to {1}", inputPath, outputPath); + var msg = $"ffmpeg subtitle extraction failed for {inputPath} to {outputPath}"; _logger.LogError(msg); @@ -621,7 +658,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles } else { - var msg = string.Format("ffmpeg subtitle extraction completed for {0} to {1}", inputPath, outputPath); + var msg = $"ffmpeg subtitle extraction completed for {inputPath} to {outputPath}"; _logger.LogInformation(msg); } @@ -639,19 +676,17 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// <returns>Task.</returns> private async Task SetAssFont(string file) { - _logger.LogInformation("Setting ass font within {0}", file); + _logger.LogInformation("Setting ass font within {File}", file); string text; Encoding encoding; using (var fileStream = _fileSystem.OpenRead(file)) + using (var reader = new StreamReader(fileStream, true)) { - using (var reader = new StreamReader(fileStream, true)) - { - encoding = reader.CurrentEncoding; + encoding = reader.CurrentEncoding; - text = await reader.ReadToEndAsync().ConfigureAwait(false); - } + text = await reader.ReadToEndAsync().ConfigureAwait(false); } var newText = text.Replace(",Arial,", ",Arial Unicode MS,"); @@ -696,7 +731,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles var charset = _textEncoding.GetDetectedEncodingName(bytes, bytes.Length, language, true); - _logger.LogDebug("charset {0} detected for {1}", charset ?? "null", path); + _logger.LogDebug("charset {0} detected for {Path}", charset ?? "null", path); return charset; } @@ -705,18 +740,18 @@ namespace MediaBrowser.MediaEncoding.Subtitles { if (protocol == MediaProtocol.Http) { - HttpRequestOptions opts = new HttpRequestOptions(); - opts.Url = path; - opts.CancellationToken = cancellationToken; + HttpRequestOptions opts = new HttpRequestOptions() + { + Url = path, + CancellationToken = cancellationToken + }; using (var file = await _httpClient.Get(opts).ConfigureAwait(false)) + using (var memoryStream = new MemoryStream()) { - using (var memoryStream = new MemoryStream()) - { - await file.CopyToAsync(memoryStream).ConfigureAwait(false); - memoryStream.Position = 0; + await file.CopyToAsync(memoryStream).ConfigureAwait(false); + memoryStream.Position = 0; - return memoryStream.ToArray(); - } + return memoryStream.ToArray(); } } if (protocol == MediaProtocol.File) @@ -726,6 +761,5 @@ namespace MediaBrowser.MediaEncoding.Subtitles throw new ArgumentOutOfRangeException("protocol"); } - } } diff --git a/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs b/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs index f84735bed..08bf2379f 100644 --- a/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs +++ b/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs @@ -30,12 +30,6 @@ namespace MediaBrowser.Model.Configuration public int LogFileRetentionDays { get; set; } /// <summary> - /// Gets or sets a value indicating whether [run at startup]. - /// </summary> - /// <value><c>true</c> if [run at startup]; otherwise, <c>false</c>.</value> - public bool RunAtStartup { get; set; } - - /// <summary> /// Gets or sets a value indicating whether this instance is first run. /// </summary> /// <value><c>true</c> if this instance is first run; otherwise, <c>false</c>.</value> diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index fe1068842..e223f2cc8 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -1,6 +1,11 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> + <Authors>Jellyfin Contributors</Authors> + <PackageId>Jellyfin.Model</PackageId> + </PropertyGroup> + + <PropertyGroup> <TargetFramework>netstandard2.0</TargetFramework> <GenerateAssemblyInfo>false</GenerateAssemblyInfo> </PropertyGroup> diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs index d66d62fea..c22e77beb 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -183,6 +183,10 @@ namespace MediaBrowser.Model.Net { return "text/plain"; } + if (StringHelper.EqualsIgnoreCase(ext, ".log")) + { + return "text/plain"; + } if (StringHelper.EqualsIgnoreCase(ext, ".xml")) { return "application/xml"; @@ -228,10 +232,10 @@ namespace MediaBrowser.Model.Net if (StringHelper.EqualsIgnoreCase(ext, ".oga")) { return "audio/ogg"; - } - if (StringHelper.EqualsIgnoreCase(ext, ".opus")) - { - return "audio/ogg"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".opus")) + { + return "audio/ogg"; } if (StringHelper.EqualsIgnoreCase(ext, ".ac3")) { diff --git a/MediaBrowser.Model/News/INewsService.cs b/MediaBrowser.Model/News/INewsService.cs deleted file mode 100644 index 4c92664d9..000000000 --- a/MediaBrowser.Model/News/INewsService.cs +++ /dev/null @@ -1,17 +0,0 @@ -using MediaBrowser.Model.Querying; - -namespace MediaBrowser.Model.News -{ - /// <summary> - /// Interface INewsFeed - /// </summary> - public interface INewsService - { - /// <summary> - /// Gets the product news. - /// </summary> - /// <param name="query">The query.</param> - /// <returns>QueryResult{NewsItem}.</returns> - QueryResult<NewsItem> GetProductNews(NewsQuery query); - } -} diff --git a/MediaBrowser.Model/News/NewsItem.cs b/MediaBrowser.Model/News/NewsItem.cs deleted file mode 100644 index 2a05c420a..000000000 --- a/MediaBrowser.Model/News/NewsItem.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace MediaBrowser.Model.News -{ - public class NewsItem - { - public string Title { get; set; } - public string Link { get; set; } - public string Description { get; set; } - public string DescriptionHtml { get; set; } - public string Guid { get; set; } - public DateTime Date { get; set; } - } -} diff --git a/MediaBrowser.Model/News/NewsQuery.cs b/MediaBrowser.Model/News/NewsQuery.cs deleted file mode 100644 index 567888921..000000000 --- a/MediaBrowser.Model/News/NewsQuery.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace MediaBrowser.Model.News -{ - public class NewsQuery - { - public int? StartIndex { get; set; } - - public int? Limit { get; set; } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Model/Session/ClientCapabilities.cs b/MediaBrowser.Model/Session/ClientCapabilities.cs index ded4c0e84..0ba9988bb 100644 --- a/MediaBrowser.Model/Session/ClientCapabilities.cs +++ b/MediaBrowser.Model/Session/ClientCapabilities.cs @@ -12,8 +12,6 @@ namespace MediaBrowser.Model.Session public bool SupportsMediaControl { get; set; } public bool SupportsContentUploading { get; set; } public string MessageCallbackUrl { get; set; } - public string PushToken { get; set; } - public string PushTokenType { get; set; } public bool SupportsPersistentIdentifier { get; set; } public bool SupportsSync { get; set; } diff --git a/MediaBrowser.Model/System/IEnvironmentInfo.cs b/MediaBrowser.Model/System/IEnvironmentInfo.cs index 6af514dc8..faf9f0a42 100644 --- a/MediaBrowser.Model/System/IEnvironmentInfo.cs +++ b/MediaBrowser.Model/System/IEnvironmentInfo.cs @@ -8,10 +8,6 @@ namespace MediaBrowser.Model.System string OperatingSystemName { get; } string OperatingSystemVersion { get; } Architecture SystemArchitecture { get; } - string GetEnvironmentVariable(string name); - void SetProcessEnvironmentVariable(string name, string value); - string StackTrace { get; } - char PathSeparator { get; } } public enum OperatingSystem diff --git a/MediaBrowser.Model/System/IPowerManagement.cs b/MediaBrowser.Model/System/IPowerManagement.cs deleted file mode 100644 index 03907568c..000000000 --- a/MediaBrowser.Model/System/IPowerManagement.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace MediaBrowser.Model.System -{ - public interface IPowerManagement - { - void PreventSystemStandby(); - void AllowSystemStandby(); - void ScheduleWake(DateTime wakeTimeUtc, string displayName); - } -} diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index 031222b75..a63ce5e66 100644 --- a/MediaBrowser.Model/System/SystemInfo.cs +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -118,12 +118,6 @@ namespace MediaBrowser.Model.System /// <value><c>true</c> if this instance has update available; otherwise, <c>false</c>.</value> public bool HasUpdateAvailable { get; set; } - /// <summary> - /// Gets or sets a value indicating whether [supports automatic run at startup]. - /// </summary> - /// <value><c>true</c> if [supports automatic run at startup]; otherwise, <c>false</c>.</value> - public bool SupportsAutoRunAtStartup { get; set; } - public string EncoderLocationType { get; set; } public Architecture SystemArchitecture { get; set; } diff --git a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs index 0dbc43313..2c94d6a07 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs @@ -720,82 +720,6 @@ namespace MediaBrowser.Providers.Music return null; } - private long _lastMbzUrlQueryTicks = 0; - private List<MbzUrl> _mbzUrls = null; - private MbzUrl _chosenUrl; - - private async Task<MbzUrl> GetMbzUrl(bool forceMusicBrainzProper = false) - { - if (_chosenUrl == null || _mbzUrls == null || (DateTime.UtcNow.Ticks - _lastMbzUrlQueryTicks) > TimeSpan.FromHours(12).Ticks) - { - var urls = await RefreshMzbUrls(forceMusicBrainzProper).ConfigureAwait(false); - - if (urls.Count > 1) - { - _chosenUrl = urls[new Random().Next(0, urls.Count)]; - } - else - { - _chosenUrl = urls[0]; - } - } - - return _chosenUrl; - } - - private async Task<List<MbzUrl>> RefreshMzbUrls(bool forceMusicBrainzProper = false) - { - List<MbzUrl> list = null; - - if (!forceMusicBrainzProper) - { - var musicbrainzadminurl = _appHost.GetValue("musicbrainzadminurl"); - - if (!string.IsNullOrEmpty(musicbrainzadminurl)) - { - try - { - var options = new HttpRequestOptions - { - Url = musicbrainzadminurl, - UserAgent = _appHost.Name + "/" + _appHost.ApplicationVersion - }; - - using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false)) - { - using (var stream = response.Content) - { - var results = await _json.DeserializeFromStreamAsync<List<MbzUrl>>(stream).ConfigureAwait(false); - - list = results; - } - } - _lastMbzUrlQueryTicks = DateTime.UtcNow.Ticks; - } - catch (Exception ex) - { - _logger.LogError(ex, "Error getting music brainz info"); - } - } - } - - if (list == null) - { - list = new List<MbzUrl> - { - new MbzUrl - { - url = MusicBrainzBaseUrl, - throttleMs = 1000 - } - }; - } - - _mbzUrls = list.ToList(); - - return list; - } - internal Task<HttpResponseInfo> GetMusicBrainzResponse(string url, bool isSearch, CancellationToken cancellationToken) { return GetMusicBrainzResponse(url, isSearch, false, cancellationToken); @@ -806,7 +730,7 @@ namespace MediaBrowser.Providers.Music /// </summary> internal async Task<HttpResponseInfo> GetMusicBrainzResponse(string url, bool isSearch, bool forceMusicBrainzProper, CancellationToken cancellationToken) { - var urlInfo = await GetMbzUrl(forceMusicBrainzProper).ConfigureAwait(false); + var urlInfo = new MbzUrl(MusicBrainzBaseUrl, 1000); var throttleMs = urlInfo.throttleMs; if (throttleMs > 0) @@ -841,6 +765,12 @@ namespace MediaBrowser.Providers.Music internal class MbzUrl { + internal MbzUrl(string url, int throttleMs) + { + this.url = url; + this.throttleMs = throttleMs; + } + public string url { get; set; } public int throttleMs { get; set; } } diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs index 5c4eb62a8..bb4624b5c 100644 --- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs @@ -270,21 +270,13 @@ namespace MediaBrowser.Providers.Omdb public static string GetOmdbUrl(string query, IApplicationHost appHost, CancellationToken cancellationToken) { - var baseUrl = appHost.GetValue("omdb_baseurl"); + const string url = "https://www.omdbapi.com?apikey=fe53f97e"; - if (string.IsNullOrEmpty(baseUrl)) + if (string.IsNullOrWhiteSpace(query)) { - baseUrl = "https://www.omdbapi.com"; + return url; } - - var url = baseUrl + "?apikey=fe53f97e"; - - if (!string.IsNullOrWhiteSpace(query)) - { - url += "&" + query; - } - - return url; + return url + "&" + query; } private async Task<string> EnsureItemInfo(string imdbId, CancellationToken cancellationToken) diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 58d02ef04..38a789580 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -138,9 +138,9 @@ namespace MediaBrowser.WebDashboard.Api } /// <summary> - /// Gets the dashboard UI path. + /// Gets the path for the web interface. /// </summary> - /// <value>The dashboard UI path.</value> + /// <value>The path for the web interface.</value> public string DashboardUIPath { get @@ -150,7 +150,7 @@ namespace MediaBrowser.WebDashboard.Api return _serverConfigurationManager.Configuration.DashboardSourcePath; } - return Path.Combine(_serverConfigurationManager.ApplicationPaths.ApplicationResourcesPath, "dashboard-ui"); + return Path.Combine(_serverConfigurationManager.ApplicationPaths.ApplicationResourcesPath, "jellyfin-web", "src"); } } diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 855b8b627..c5367ba75 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -10,7 +10,7 @@ </ItemGroup>
<ItemGroup>
- <None Include="dashboard-ui\**\*.*">
+ <None Include="jellyfin-web\src\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
diff --git a/MediaBrowser.WebDashboard/jellyfin-web b/MediaBrowser.WebDashboard/jellyfin-web new file mode 160000 +Subproject 4678528d0028685b45c7c6daa2e24b72a363535 diff --git a/MediaBrowser.sln b/MediaBrowser.sln index dfc3b253e..13d1e26db 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -150,10 +150,6 @@ Global {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Release|Any CPU.Build.0 = Release|Any CPU - {D45FC504-D06B-41A0-A220-C20B7E8F1304}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D45FC504-D06B-41A0-A220-C20B7E8F1304}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D45FC504-D06B-41A0-A220-C20B7E8F1304}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D45FC504-D06B-41A0-A220-C20B7E8F1304}.Release|Any CPU.Build.0 = Release|Any CPU {6EAFA7F0-8A82-49E6-B2FA-086C5CAEA95B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6EAFA7F0-8A82-49E6-B2FA-086C5CAEA95B}.Debug|Any CPU.Build.0 = Debug|Any CPU {6EAFA7F0-8A82-49E6-B2FA-086C5CAEA95B}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -1,53 +1,36 @@ <h1 align="center">Jellyfin</h1> <h3 align="center">The Free Software Media System</h3> -<p align="center"> -Jellyfin is a free software media system that puts you in control of managing and streaming your media. -</p> - ------ - -<p align="center"> -<strong>Want to get started?</strong> Choose from <a href="https://github.com/jellyfin/jellyfin/wiki/Prebuilt-Packages">Prebuilt Packages</a>, or <a href="https://github.com/jellyfin/jellyfin/wiki/Building-from-Source">Build from Source</a>. -</p> -<p align="center"> -<strong>Want to contribute?</strong> Check out <a href="https://github.com/jellyfin/jellyfin/wiki/Contributing-to-Jellyfin">our wiki for guidelines</a>. -</p> - --- <p align="center"> +<img alt="Logo banner" src="https://raw.githubusercontent.com/jellyfin/jellyfin-ux/master/branding/SVG/banner-logo-solid.svg?sanitize=true"/> +<br/></br> <a href="https://github.com/jellyfin/jellyfin"><img alt="GPL 2.0 License" src="https://img.shields.io/github/license/jellyfin/jellyfin.svg"></a> <a href="https://cloud.drone.io/jellyfin/jellyfin"><img alt="Build Status" src="https://cloud.drone.io/api/badges/jellyfin/jellyfin/status.svg"></a> <a href="https://hub.docker.com/r/jellyfin/jellyfin"><img alt="Docker Pull Count" src="https://img.shields.io/docker/pulls/jellyfin/jellyfin.svg"></a> -<a href="https://matrix.to/#/#jellyfin:matrix.org"><img alt="Chat on Matrix" src="https://img.shields.io/matrix/jellyfin:matrix.org.svg?logo=matrix"></a> +<a href="https://jellyfin.readthedocs.io"><img alt="Documentation" src="https://img.shields.io/readthedocs/jellyfin.svg"></a> +<a href="https://matrix.to/#/+jellyfin:matrix.org"><img alt="Chat on Matrix" src="https://img.shields.io/matrix/jellyfin:matrix.org.svg?logo=matrix"></a> <a href="https://www.reddit.com/r/jellyfin/"><img alt="Join our Subreddit" src="https://img.shields.io/badge/reddit-r%2Fjellyfin-%23FF5700.svg"></a> </p> -For further details, please see [our wiki](https://github.com/jellyfin/jellyfin/wiki). - -To receive the latest updates, please visit [our public chat on Matrix/Riot](https://matrix.to/#/#jellyfin:matrix.org), our [announce chat](https://matrix.to/#/#jellyfin-announce:matrix.org) for release info, and follow us on [Social Media](https://github.com/jellyfin/jellyfin/wiki/Social-Media). - -## About - -The Jellyfin project was started as a result of Emby's decision to take their code closed-source, as well as various philosophical differences with the core developers. Jellyfin seeks to be the free software alternative to Emby and Plex to provide media management and streaming from a dedicated server to end-user devices. - -Jellyfin is descended from Emby 3.5.2, ported to the .NET Core framework, and aims to contain build facilities for every platform. +--- -## Issues and Feature Requests +Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. It is an alternative to the proprietary Emby and Plex, to provide media from a dedicated server to end-user devices via multiple apps. Jellyfin is descended from Emby's 3.5.2 release and ported to the .NET Core framework to enable full cross-platform support. There are no strings attached, no premium licenses or features, and no hidden agendas: just a team who want to build something better and work together to achieve it. We welcome anyone who is interested in joining us in our quest! +For further details, please see [our documentation page](https://jellyfin.readthedocs.io). To receive the latest updates, get help with Jellyfin, and join the community, please visit [one of our communication channels on Matrix/Riot or social media](https://jellyfin.readthedocs.io/en/latest/user-docs/getting-help). +For more information about the project, please see our [about page](https://jellyfin.readthedocs.io/en/latest/about/). <p align="center"> - <strong>New idea or improvement?</strong> -<em>Open a <a href="https://github.com/jellyfin/jellyfin/wiki/Feature-Requests">Feature Request</a>.</em> +<strong>Want to get started?</strong> +<em>Choose from <a href="https://jellyfin.readthedocs.io/en/latest/user-docs/installing/">Prebuilt Packages</a> or <a href="https://jellyfin.readthedocs.io/en/latest/user-docs/building/">Build from Source</a>, then see our <a href="https://jellyfin.readthedocs.io/en/latest/user-docs/quick-start/">Quickstart guide</a>.</em> </p> <p align="center"> - <Strong>Something not working right?</strong> -<em>Open an <a href="https://github.com/jellyfin/jellyfin/wiki/Issue-Guidelines">Issue</a>.</em> +<strong>Want to contribute?</strong> +<em>Check out <a href="https://jellyfin.readthedocs.io/en/latest/developer-docs/contributing/">our documentation for guidelines</a>.</em> +</p> +<p align="center"> +<strong>New idea or improvement? Something not working right?</strong> +<em>Open an <a href="https://jellyfin.readthedocs.io/en/latest/developer-docs/issues/">Issue</a>.</em> </p> - - -## Contributing to Jellyfin - -If you're interested in contributing, please see [our wiki for guidelines](https://github.com/jellyfin/jellyfin/wiki/Contributing-to-Jellyfin). diff --git a/SharedVersion.cs b/SharedVersion.cs index 379b5b43c..0fbf7b925 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.5.2.0")] +[assembly: AssemblyVersion("10.0.1")] diff --git a/build-deb.sh b/build-deb.sh deleted file mode 100755 index fd14fc17d..000000000 --- a/build-deb.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env sh - -# Build a Jellyfin .deb file with Docker on Linux -# Places the output .deb file in the parent directory - -set -o errexit -set -o xtrace -set -o nounset - -package_temporary_dir="`mktemp -d`" -current_user="`whoami`" -image_name="jellyfin-debuild" - -cleanup() { - set +o errexit - docker image rm $image_name --force - rm -rf "$package_temporary_dir" -} -trap cleanup EXIT INT - -docker build . -t "$image_name" -f ./Dockerfile.debian_package -docker run --rm -v "$package_temporary_dir:/temp" "$image_name" cp -r /dist /temp/ -sudo chown -R "$current_user" "$package_temporary_dir" -mv "$package_temporary_dir"/dist/*.deb ../ diff --git a/debian/bin/jellyfin-sudoers b/debian/bin/jellyfin-sudoers deleted file mode 100644 index 4eb91366b..000000000 --- a/debian/bin/jellyfin-sudoers +++ /dev/null @@ -1,37 +0,0 @@ -#Allow jellyfin group to start, stop and restart itself -Cmnd_Alias RESTARTSERVER_SYSV = /sbin/service jellyfin restart, /usr/sbin/service jellyfin restart -Cmnd_Alias STARTSERVER_SYSV = /sbin/service jellyfin start, /usr/sbin/service jellyfin start -Cmnd_Alias STOPSERVER_SYSV = /sbin/service jellyfin stop, /usr/sbin/service jellyfin stop -Cmnd_Alias RESTARTSERVER_SYSTEMD = /usr/bin/systemctl restart jellyfin, /bin/systemctl restart jellyfin -Cmnd_Alias STARTSERVER_SYSTEMD = /usr/bin/systemctl start jellyfin, /bin/systemctl start jellyfin -Cmnd_Alias STOPSERVER_SYSTEMD = /usr/bin/systemctl stop jellyfin, /bin/systemctl stop jellyfin -Cmnd_Alias RESTARTSERVER_INITD = /etc/init.d/jellyfin restart -Cmnd_Alias STARTSERVER_INITD = /etc/init.d/jellyfin start -Cmnd_Alias STOPSERVER_INITD = /etc/init.d/jellyfin stop - - -%jellyfin ALL=(ALL) NOPASSWD: RESTARTSERVER_SYSV -%jellyfin ALL=(ALL) NOPASSWD: STARTSERVER_SYSV -%jellyfin ALL=(ALL) NOPASSWD: STOPSERVER_SYSV -%jellyfin ALL=(ALL) NOPASSWD: RESTARTSERVER_SYSTEMD -%jellyfin ALL=(ALL) NOPASSWD: STARTSERVER_SYSTEMD -%jellyfin ALL=(ALL) NOPASSWD: STOPSERVER_SYSTEMD -%jellyfin ALL=(ALL) NOPASSWD: RESTARTSERVER_INITD -%jellyfin ALL=(ALL) NOPASSWD: STARTSERVER_INITD -%jellyfin ALL=(ALL) NOPASSWD: STOPSERVER_INITD - -Defaults!RESTARTSERVER_SYSV !requiretty -Defaults!STARTSERVER_SYSV !requiretty -Defaults!STOPSERVER_SYSV !requiretty -Defaults!RESTARTSERVER_SYSTEMD !requiretty -Defaults!STARTSERVER_SYSTEMD !requiretty -Defaults!STOPSERVER_SYSTEMD !requiretty -Defaults!RESTARTSERVER_INITD !requiretty -Defaults!STARTSERVER_INITD !requiretty -Defaults!STOPSERVER_INITD !requiretty - -#Allow the server to mount iso images -%jellyfin ALL=(ALL) NOPASSWD: /bin/mount -%jellyfin ALL=(ALL) NOPASSWD: /bin/umount - -Defaults:%jellyfin !requiretty diff --git a/debian/bin/restart.sh b/debian/bin/restart.sh deleted file mode 100644 index a6f4632ba..000000000 --- a/debian/bin/restart.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -NAME=jellyfin - -restart_cmds=("s6-svc -t /var/run/s6/services/${NAME}" \ - "systemctl restart ${NAME}" \ - "service ${NAME} restart" \ - "/etc/init.d/${NAME} restart") - -for restart_cmd in "${restart_cmds[@]}"; do - cmd=$(echo "$restart_cmd" | awk '{print $1}') - cmd_loc=$(command -v ${cmd}) - if [[ -n "$cmd_loc" ]]; then - restart_cmd=$(echo "$restart_cmd" | sed -e "s%${cmd}%${cmd_loc}%") - echo "sleep 2; sudo $restart_cmd > /dev/null 2>&1" | at now > /dev/null 2>&1 - exit 0 - fi -done diff --git a/deployment/README.md b/deployment/README.md new file mode 100644 index 000000000..3400fd840 --- /dev/null +++ b/deployment/README.md @@ -0,0 +1,8 @@ +# Build scripts + +All `build.sh` and `package.sh` scripts are for *nix platforms (or WSL on Windows 10). + +After running both, check the `*/pkg-dist/` folders for the archives and packages. + +`build_all.sh` will invoke every build and package script. +Use `collect_all.sh` to copy all artifact to one directory for easy uploading. diff --git a/deployment/build.sh b/deployment/build.sh new file mode 100644 index 000000000..0b5dae84a --- /dev/null +++ b/deployment/build.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +# Execute all build.sh and package.sh and sign.sh scripts in every folder. In that order. Script should check for artifacts themselves. +echo "Running for platforms '$@'." +for directory in */ ; do + platform=`basename "${directory}"` + if [[ $@ == *"$platform"* || $@ = *"all"* ]]; then + echo "Processing ${platform}" + pushd "$platform" + if [ -f build.sh ]; then + echo ./build.sh + fi + if [ -f package.sh ]; then + echo ./package.sh + fi + if [ -f sign.sh ]; then + echo ./sign.sh + fi + popd + else + echo "Skipping $platform." + fi +done diff --git a/deployment/clean.sh b/deployment/clean.sh new file mode 100644 index 000000000..7517cf849 --- /dev/null +++ b/deployment/clean.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +set -e + +# Execute every clean.sh scripts in every folder. +echo "Running for platforms '$@'." +for directory in */ ; do + platform=`basename "${directory}"` + if [[ $@ == *"$platform"* || $@ = *"all"* ]]; then + echo "Processing ${platform}" + pushd "$platform" + if [ -f clean.sh ]; then + echo ./clean.sh + fi + popd + else + echo "Skipping $platform." + fi +done + +rm -rf ./collect-dist diff --git a/deployment/collect_all.sh b/deployment/collect_all.sh new file mode 100644 index 000000000..d625002db --- /dev/null +++ b/deployment/collect_all.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +source common.build.sh + +VERSION=`get_version ..` + +COLLECT_DIR="./collect-dist" + +mkdir -p ./collect-dist + +DIRS=`find . -type d -name "pkg-dist"` + +while read directory +do + echo "Collecting everything from '$directory'.." + PLATFORM=$(basename "$(dirname "$directory")") + # Copy all artifacts with extensions tar.gz, deb, exe, zip, rpm and add the platform name to resolve any duplicates. + find $directory \( -name "jellyfin*.tar.gz" -o -name "jellyfin*.deb" -o -name "jellyfin*.rpm" -o -name "jellyfin*.zip" -o -name "jellyfin*.exe" \) -exec sh -c 'cp "$1" "'${COLLECT_DIR}'/jellyfin_'${PLATFORM}'_${1#*jellyfin_}"' _ {} \; + +done <<< "${DIRS}" diff --git a/deployment/common.build.sh b/deployment/common.build.sh new file mode 100644 index 000000000..5996a4ec9 --- /dev/null +++ b/deployment/common.build.sh @@ -0,0 +1,108 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset + +RED='\033[0;31m' +GREEN='\033[0;32m' +CYAN='\033[0;36m' +NC='\033[0m' # No Color + +DEFAULT_BUILD_CONTEXT="../.." +DEFAULT_ROOT="." +DEFAULT_DOTNETRUNTIME="framework" +DEFAULT_CONFIG="Release" +DEFAULT_OUTPUT_DIR="dist/jellyfin-git" +DEFAULT_PKG_DIR="pkg-dist" +DEFAULT_DOCKERFILE="Dockerfile" +DEFAULT_IMAGE_TAG="jellyfin:"`git rev-parse --abbrev-ref HEAD` + +# Run a build +build_jellyfin() +( + ROOT=${1-$DEFAULT_ROOT} + CONFIG=${2-$DEFAULT_CONFIG} + DOTNETRUNTIME=${3-$DEFAULT_DOTNETRUNTIME} + OUTPUT_DIR=${4-$DEFAULT_OUTPUT_DIR} + + echo -e "${CYAN}Building jellyfin in '${ROOT}' for ${DOTNETRUNTIME} with configuration ${CONFIG} and output directory '${OUTPUT_DIR}'.${NC}" + if [[ $DOTNETRUNTIME == 'framework' ]]; then + dotnet publish "${ROOT}" --configuration "${CONFIG}" --output="${OUTPUT_DIR}" + else + dotnet publish "${ROOT}" --configuration "${CONFIG}" --output="${OUTPUT_DIR}" --self-contained --runtime ${DOTNETRUNTIME} + fi + EXIT_CODE=$? + if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}[DONE] Build jellyfin in '${ROOT}' for ${DOTNETRUNTIME} with configuration ${CONFIG} and output directory '${OUTPUT_DIR}' complete.${NC}" + else + echo -e "${RED}[FAIL] Build jellyfin in '${ROOT}' for ${DOTNETRUNTIME} with configuration ${CONFIG} and output directory '${OUTPUT_DIR}' FAILED.${NC}" + fi +) + +# Run a docker +build_jellyfin_docker() +( + BUILD_CONTEXT=${1-$DEFAULT_BUILD_CONTEXT} + DOCKERFILE=${2-$DEFAULT_DOCKERFILE} + IMAGE_TAG=${3-$DEFAULT_IMAGE_TAG} + + echo -e "${CYAN}Building jellyfin docker image in '${ROOT}' with Dockerfile ${CONFIG} and tag '${IMAGE_TAG}'.${NC}" + docker build -t ${IMAGE_TAG} -f ${DOCKERFILE} ${ROOT} + EXIT_CODE=$? + if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}[DONE] Building jellyfin docker image in '${ROOT}' with Dockerfile ${CONFIG} and tag '${IMAGE_TAG}' complete.${NC}" + else + echo -e "${RED}[FAIL] Building jellyfin docker image in '${ROOT}' with Dockerfile ${CONFIG} and tag '${IMAGE_TAG}' FAILED.${NC}" + fi +) + +# Clean a build +clean_jellyfin() +( + local ROOT=${1-$DEFAULT_ROOT} + local CONFIG=${2-$DEFAULT_CONFIG} + local OUTPUT_DIR=${3-$DEFAULT_OUTPUT_DIR} + local PKG_DIR=${4-$DEFAULT_PKG_DIR} + echo -e "${CYAN}Cleaning jellyfin in '${ROOT}'' with configuration ${CONFIG} and output directory '${OUTPUT_DIR}'.${NC}" + echo -e "${CYAN}Deleting '${OUTPUT_DIR}'${NC}" + rm -rf "$OUTPUT_DIR" + echo -e "${CYAN}Deleting '${PKG_DIR}'${NC}" + rm -rf "$PKG_DIR" + dotnet clean "${ROOT}" -maxcpucount:1 --configuration ${CONFIG} + local EXIT_CODE=$? + if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}[DONE] Clean jellyfin in '${ROOT}' with configuration ${CONFIG} and output directory '${OUTPUT_DIR}' complete.${NC}" + else + echo -e "${RED}[FAIL] Clean jellyfin in '${ROOT}' with configuration ${CONFIG} and output directory '${OUTPUT_DIR}' failed.${NC}" + fi +) + +# Parse the version from the AssemblyVersion +get_version() +( + local ROOT=${1-$DEFAULT_ROOT} + grep "AssemblyVersion" ${ROOT}/SharedVersion.cs | sed -E 's/\[assembly: ?AssemblyVersion\("([0-9\.]+)"\)\]/\1/' | sed -E 's/.0$//' +) + +# Packages the output folder into an archive. +package_portable() +( + local ROOT=${1-$DEFAULT_ROOT} + local OUTPUT_DIR=${2-$DEFAULT_OUTPUT_DIR} + local PKG_DIR=${3-$DEFAULT_PKG_DIR} + # Package portable build result + if [ -d ${OUTPUT_DIR} ]; then + echo -e "${CYAN}Packaging build in '${OUTPUT_DIR}' for `basename "${OUTPUT_DIR}"` to '${PKG_DIR}' with root '${ROOT}'.${NC}" + mkdir -p ${PKG_DIR} + tar -zcvf "${PKG_DIR}/`basename "${OUTPUT_DIR}"`.portable.tar.gz" -C "`dirname "${OUTPUT_DIR}"`" "`basename "${OUTPUT_DIR}"`" + local EXIT_CODE=$? + if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}[DONE] Packaging build in '${OUTPUT_DIR}' for `basename "${OUTPUT_DIR}"` to '${PKG_DIR}' with root '${ROOT}' complete.${NC}" + else + echo -e "${RED}[FAIL] Packaging build in '${OUTPUT_DIR}' for `basename "${OUTPUT_DIR}"` to '${PKG_DIR}' with root '${ROOT}' FAILED.${NC}" + fi + else + echo -e "${RED}[FAIL] Build artifacts do not exist for ${OUTPUT_DIR}. Run build.sh first.${NC}" + fi +) + diff --git a/Dockerfile.debian_package b/deployment/debian-package-x64/Dockerfile index c5c631b71..0de62f72b 100644 --- a/Dockerfile.debian_package +++ b/deployment/debian-package-x64/Dockerfile @@ -1,4 +1,6 @@ FROM debian:9 +ARG SOURCEDIR=/repo +ENV DEB_BUILD_OPTIONS=noddebs # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current RUN apt-get update \ @@ -11,12 +13,11 @@ RUN apt-get update \ && chown root:root /etc/apt/sources.list.d/microsoft-prod.list \ && apt-get update -WORKDIR /repo +WORKDIR ${SOURCEDIR} COPY . . +COPY ./deployment/debian-x64/pkg-src ./debian -RUN yes|mk-build-deps -i \ - && dpkg-buildpackage -us -uc \ - && mkdir /dist \ - && mv /jellyfin*deb /dist +RUN yes | mk-build-deps -i debian/control \ + && dpkg-buildpackage -us -uc -WORKDIR /dist +WORKDIR / diff --git a/deployment/debian-package-x64/clean.sh b/deployment/debian-package-x64/clean.sh new file mode 100644 index 000000000..3df2d7796 --- /dev/null +++ b/deployment/debian-package-x64/clean.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION} diff --git a/deployment/debian-package-x64/package.sh b/deployment/debian-package-x64/package.sh new file mode 100644 index 000000000..7909b0a84 --- /dev/null +++ b/deployment/debian-package-x64/package.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +# TODO get the version in the package automatically. And using the changelog to decide the debian package suffix version. + +# Build a Jellyfin .deb file with Docker on Linux +# Places the output .deb file in the parent directory + +package_temporary_dir="`pwd`/pkg-dist-tmp" +output_dir="`pwd`/pkg-dist" +current_user="`whoami`" +image_name="jellyfin-debuild" + +cleanup() { + set +o errexit + docker image rm $image_name --force + rm -rf "$package_temporary_dir" +} +trap cleanup EXIT INT + +docker build ../.. -t "$image_name" -f ./Dockerfile --build-arg SOURCEDIR="/jellyfin-${VERSION}" +mkdir -p "$package_temporary_dir" +mkdir -p "$output_dir" +docker run --rm -v "$package_temporary_dir:/temp" "$image_name" sh -c 'find / -maxdepth 1 -type f -name "jellyfin*" -exec mv {} /temp \;' +chown -R "$current_user" "$package_temporary_dir" +if [ $? -ne 0 ]; then + # Some platforms need this to chown the file properly. (Platforms with native docker, not just the client) + sudo chown -R "$current_user" "$package_temporary_dir" +fi +mv "$package_temporary_dir"/* "$output_dir" diff --git a/debian/changelog b/deployment/debian-package-x64/pkg-src/changelog index 9dc21d467..685d31a5e 100644 --- a/debian/changelog +++ b/deployment/debian-package-x64/pkg-src/changelog @@ -1,3 +1,26 @@ +jellyfin (10.0.1-1) unstable; urgency=medium + + * Hotfix release, corrects several small bugs from 10.0.0 + * #512: Fix CONTRIBUTORS.md formatting + * #501: Fix regression in integer divisions in latest movies category + * #498: Change contributing link in settings to readthedocs.io + * #493: Remove unused values.txt resource + * #491: Fix userprofile.js crash + * #519: Fix the DecodeJfif function to get proper image sizes + * #486: Add NuGet package info to plugin projects + + -- Joshua Boniface <joshua@boniface.me> Tue, 08 Jan 2019 20:06:01 -0500 + +jellyfin (10.0.0-1) unstable; urgency=medium + + * The first Jellyfin release under our new versioning scheme + * Numerous bugfixes and code readability improvements + * Updated logging configuration, including flag for it and configdir + * Updated theming including logo + * Dozens of other improvements as documented in GitHub pull request #419 + + -- Joshua Boniface <joshua@boniface.me> Sat, 05 Jan 2019 15:39:25 -0500 + jellyfin (3.5.2-5) unstable; urgency=medium * Fully GPL'd release - remove tainted code from MediaBrowser.Common @@ -34,16 +57,3 @@ jellyfin (3.5.2) unstable; urgency=medium * Rename from emby-server on version 3.5.2 -- Joshua Boniface <joshua@boniface.me> Sun, 9 Dec 2018 15:20:58 -0400 - -emby-server (3.5.2-unlocked) unstable; urgency=medium - - * Taking changes from upstream 3.5.2, beautifying some JS files - - -- Vasily <just.one.man@yandex.ru> Mon, 22 Oct 2018 03:45:13 +0400 - -emby-server (3.4.1.18-unlocked) unstable; urgency=medium - - * Hard fork of Emby 3.4.1.18 including premium unlock - - -- Joshua Boniface <joshua@boniface.me> Thu, 9 Aug 2018 00:33:19 -0400 - diff --git a/debian/compat b/deployment/debian-package-x64/pkg-src/compat index 45a4fb75d..45a4fb75d 100644 --- a/debian/compat +++ b/deployment/debian-package-x64/pkg-src/compat diff --git a/debian/conf/jellyfin b/deployment/debian-package-x64/pkg-src/conf/jellyfin index fb00e7f65..861865aae 100644 --- a/debian/conf/jellyfin +++ b/deployment/debian-package-x64/pkg-src/conf/jellyfin @@ -15,8 +15,10 @@ # General options # -# Data directory -JELLYFIN_DATA="/var/lib/jellyfin" +# Program directories +JELLYFIN_DATA_DIRECTORY="/var/lib/jellyfin" +JELLYFIN_CONFIG_DIRECTORY="/etc/jellyfin" +JELLYFIN_LOG_DIRECTORY="/var/log/jellyfin" # Restart script for in-app server control JELLYFIN_RESTART_SCRIPT="/usr/lib/jellyfin/restart.sh" # Additional options for the binary @@ -29,4 +31,4 @@ JELLYFIN_ADD_OPTS="" # Application username JELLYFIN_USER="jellyfin" # Full application command -JELLYFIN_ARGS="-programdata $JELLYFIN_DATA -restartpath $JELLYFIN_RESTART_SCRIPT $JELLYFIN_ADD_OPTS" +JELLYFIN_ARGS="-programdata $JELLYFIN_DATA_DIRECTORY -configdir $JELLYFIN_CONFIG_DIRECTORY -logdir $JELLYFIN_LOG_DIRECTORY -restartpath $JELLYFIN_RESTART_SCRIPT $JELLYFIN_ADD_OPTS" diff --git a/debian/conf/jellyfin.service.conf b/deployment/debian-package-x64/pkg-src/conf/jellyfin.service.conf index 1b69dd74e..1b69dd74e 100644 --- a/debian/conf/jellyfin.service.conf +++ b/deployment/debian-package-x64/pkg-src/conf/jellyfin.service.conf diff --git a/deployment/debian-package-x64/pkg-src/conf/logging.json b/deployment/debian-package-x64/pkg-src/conf/logging.json new file mode 100644 index 000000000..5d98484cd --- /dev/null +++ b/deployment/debian-package-x64/pkg-src/conf/logging.json @@ -0,0 +1,29 @@ +{ + "Serilog": { + "MinimumLevel": "Information", + "WriteTo": [ + { "Name": "Console", + "Args": { + "outputTemplate": "[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "Async", + "Args": { + "configure": [ + { + "Name": "File", + "Args": { + "path": "%JELLYFIN_LOG_DIR%//jellyfin.log", + "fileSizeLimitBytes": 10485700, + "rollOnFileSizeLimit": true, + "retainedFileCountLimit": 10, + "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}" + } + } + ] + } + } + ] + } +} diff --git a/debian/control b/deployment/debian-package-x64/pkg-src/control index 13c62c806..74bebeaf1 100644 --- a/debian/control +++ b/deployment/debian-package-x64/pkg-src/control @@ -9,6 +9,9 @@ Build-Depends: debhelper (>= 9), libfontconfig1-dev, libfreetype6-dev Standards-Version: 3.9.4 +Homepage: https://jellyfin.media/ +Vcs-Git: https://github.org/jellyfin/jellyfin.git +Vcs-Browser: https://github.org/jellyfin/jellyfin Package: jellyfin Replaces: mediabrowser, emby, emby-server-beta, jellyfin-dev, emby-server diff --git a/debian/copyright b/deployment/debian-package-x64/pkg-src/copyright index 0d7a2a600..0d7a2a600 100644 --- a/debian/copyright +++ b/deployment/debian-package-x64/pkg-src/copyright diff --git a/debian/gbp.conf b/deployment/debian-package-x64/pkg-src/gbp.conf index f131b973c..60b3d2872 100644 --- a/debian/gbp.conf +++ b/deployment/debian-package-x64/pkg-src/gbp.conf @@ -3,4 +3,4 @@ pristine-tar = False cleaner = fakeroot debian/rules clean [import-orig] -filter = [ ".git*", ".hg*" ] +filter = [ ".git*", ".hg*", ".vs*", ".vscode*" ] diff --git a/debian/install b/deployment/debian-package-x64/pkg-src/install index bc26f9f09..adaff7b26 100644 --- a/debian/install +++ b/deployment/debian-package-x64/pkg-src/install @@ -1,5 +1,6 @@ usr/lib/jellyfin usr/lib/ debian/conf/jellyfin etc/default/ +debian/conf/logging.json etc/jellyfin/ debian/conf/jellyfin.service.conf etc/systemd/system/jellyfin.service.d/ debian/bin/jellyfin-sudoers etc/sudoers.d/ debian/bin/restart.sh usr/lib/jellyfin/ diff --git a/debian/jellyfin.init b/deployment/debian-package-x64/pkg-src/jellyfin.init index d103fb0f1..d103fb0f1 100644 --- a/debian/jellyfin.init +++ b/deployment/debian-package-x64/pkg-src/jellyfin.init diff --git a/debian/jellyfin.service b/deployment/debian-package-x64/pkg-src/jellyfin.service index 4c3739909..c17422029 100644 --- a/debian/jellyfin.service +++ b/deployment/debian-package-x64/pkg-src/jellyfin.service @@ -6,7 +6,7 @@ After = network.target Type = simple EnvironmentFile = /etc/default/jellyfin User = jellyfin -ExecStart = /usr/bin/jellyfin -programdata ${JELLYFIN_DATA} -restartpath ${JELLYFIN_RESTART_SCRIPT} ${JELLYFIN_ADD_OPTS} +ExecStart = /usr/bin/jellyfin -programdata ${JELLYFIN_DATA_DIRECTORY} -configdir ${JELLYFIN_CONFIG_DIRECTORY} -logdir ${JELLYFIN_LOG_DIRECTORY} -restartpath ${JELLYFIN_RESTART_SCRIPT} ${JELLYFIN_ADD_OPTS} Restart = on-failure TimeoutSec = 15 diff --git a/debian/jellyfin.upstart b/deployment/debian-package-x64/pkg-src/jellyfin.upstart index ef5bc9bca..ef5bc9bca 100644 --- a/debian/jellyfin.upstart +++ b/deployment/debian-package-x64/pkg-src/jellyfin.upstart diff --git a/debian/po/POTFILES.in b/deployment/debian-package-x64/pkg-src/po/POTFILES.in index cef83a340..cef83a340 100644 --- a/debian/po/POTFILES.in +++ b/deployment/debian-package-x64/pkg-src/po/POTFILES.in diff --git a/debian/po/templates.pot b/deployment/debian-package-x64/pkg-src/po/templates.pot index 2cdcae417..2cdcae417 100644 --- a/debian/po/templates.pot +++ b/deployment/debian-package-x64/pkg-src/po/templates.pot diff --git a/debian/postinst b/deployment/debian-package-x64/pkg-src/postinst index 502bba342..3690d20ba 100644 --- a/debian/postinst +++ b/deployment/debian-package-x64/pkg-src/postinst @@ -2,7 +2,6 @@ set -e NAME=jellyfin -CONF_FILE=/etc/${NAME}.conf DEFAULT_FILE=/etc/default/${NAME} # Source Jellyfin default configuration @@ -10,13 +9,10 @@ if [[ -f $DEFAULT_FILE ]]; then . $DEFAULT_FILE fi -# Source Jellyfin user configuration overrides -if [[ -f $CONF_FILE ]]; then - . $CONF_FILE -fi - -# Data directory where Jellyfin database, cache and logs are stored -PROGRAMDATA=${JELLYFIN_DATA-/var/lib/$NAME} +# Data directories for program data (cache, db), configs, and logs +PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME} +CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME} +LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME} case "$1" in configure) @@ -29,13 +25,24 @@ case "$1" in adduser --system --ingroup jellyfin --shell /bin/false jellyfin --no-create-home --home ${PROGRAMDATA} \ --gecos "Jellyfin default user" > /dev/null 2>&1 fi - # ensure $PROGRAMDATA has appropriate permissions + # ensure $PROGRAMDATA exists if [[ ! -d $PROGRAMDATA ]]; then mkdir $PROGRAMDATA - chown -R jellyfin:jellyfin $PROGRAMDATA fi + # ensure $CONFIGDATA exists + if [[ ! -d $CONFIGDATA ]]; then + mkdir $CONFIGDATA + fi + # ensure $LOGDATA exists + if [[ ! -d $LOGDATA ]]; then + mkdir $LOGDATA + fi + # Ensure permissions are correct on all config directories + chown -R jellyfin:jellyfin $PROGRAMDATA + chown -R jellyfin:jellyfin $CONFIGDATA + chown -R jellyfin:jellyfin $LOGDATA - chmod +x ${JELLYFIN_DIR}/restart.sh > /dev/null 2>&1 || true + chmod +x /usr/lib/jellyfin/restart.sh > /dev/null 2>&1 || true # Install jellyfin symlink into /usr/bin ln -sf /usr/lib/jellyfin/bin/jellyfin /usr/bin/jellyfin diff --git a/debian/postrm b/deployment/debian-package-x64/pkg-src/postrm index 852841b16..690f5d587 100644 --- a/debian/postrm +++ b/deployment/debian-package-x64/pkg-src/postrm @@ -2,7 +2,6 @@ set -e NAME=jellyfin -CONF_FILE=/etc/${NAME}.conf DEFAULT_FILE=/etc/default/${NAME} # Source Jellyfin default configuration @@ -10,13 +9,10 @@ if [[ -f $DEFAULT_FILE ]]; then . $DEFAULT_FILE fi -# Source Jellyfin user configuration overrides -if [[ -f $CONF_FILE ]]; then - . $CONF_FILE -fi - -# Data directory where Jellyfin database, cache and logs are stored -PROGRAMDATA=${JELLYFIN_DATA-/var/lib/$NAME} +# Data directories for program data (cache, db), configs, and logs +PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME} +CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME} +LOGDATA=${JELLYFIN_DATA_DIRECTORY-/var/log/$NAME} # In case this system is running systemd, we make systemd reload the unit files # to pick up changes. @@ -37,14 +33,29 @@ case "$1" in deb-systemd-helper unmask jellyfin.service >/dev/null fi + # Remove user and group userdel jellyfin > /dev/null 2>&1 || true delgroup --quiet jellyfin > /dev/null 2>&1 || true + # Remove config dir + if [[ -d $CONFIGDATA ]]; then + rm -rf $CONFIGDATA + fi + # Remove log dir + if [[ -d $LOGDATA ]]; then + rm -rf $LOGDATA + fi + # Remove program data dir if [[ -d $PROGRAMDATA ]]; then rm -rf $PROGRAMDATA fi + # Remove binary symlink [[ -f /usr/bin/jellyfin ]] && rm /usr/bin/jellyfin + # Remove sudoers config [[ -f /etc/sudoers.d/jellyfin-sudoers ]] && rm /etc/sudoers.d/jellyfin-sudoers - [[ -d /var/lib/jellyfin ]] && rm -rf /var/lib/jellyfin + # Remove anything at the default locations; catches situations where the user moved the defaults + [[ -e /etc/jellyfin ]] && rm -rf /etc/jellyfin + [[ -e /var/log/jellyfin ]] && rm -rf /var/log/jellyfin + [[ -e /var/lib/jellyfin ]] && rm -rf /var/lib/jellyfin ;; remove) if [[ -x "/usr/bin/deb-systemd-helper" ]]; then diff --git a/debian/preinst b/deployment/debian-package-x64/pkg-src/preinst index 2ce29a0cf..0063e0e63 100644 --- a/debian/preinst +++ b/deployment/debian-package-x64/pkg-src/preinst @@ -2,7 +2,6 @@ set -e NAME=jellyfin -CONF_FILE=/etc/${NAME}.conf DEFAULT_FILE=/etc/default/${NAME} # Source Jellyfin default configuration @@ -10,13 +9,10 @@ if [[ -f $DEFAULT_FILE ]]; then . $DEFAULT_FILE fi -# Source Jellyfin user configuration overrides -if [[ -f $CONF_FILE ]]; then - . $CONF_FILE -fi - -# Data directory where Jellyfin database, cache and logs are stored -PROGRAMDATA=${JELLYFIN_DATA-/var/lib/$NAME} +# Data directories for program data (cache, db), configs, and logs +PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME} +CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME} +LOGDATA=${JELLYFIN_DATA_DIRECTORY-/var/log/$NAME} # In case this system is running systemd, we make systemd reload the unit files # to pick up changes. @@ -57,6 +53,14 @@ case "$1" in # Clean up old Emby cruft that can break the user's system [[ -f /etc/sudoers.d/emby ]] && rm -f /etc/sudoers.d/emby + # If we have existing config or log dirs in /var/lib/jellyfin, move them into the right place + if [[ -d $PROGRAMDATA/config ]]; then + mv $PROGRAMDATA/config $CONFIGDATA + fi + if [[ -d $PROGRAMDATA/logs ]]; then + mv $PROGRAMDATA/logs $LOGDATA + fi + ;; abort-upgrade) ;; diff --git a/debian/prerm b/deployment/debian-package-x64/pkg-src/prerm index 3cdffed79..4770c03c4 100644 --- a/debian/prerm +++ b/deployment/debian-package-x64/pkg-src/prerm @@ -2,7 +2,6 @@ set -e NAME=jellyfin -CONF_FILE=/etc/${NAME}.conf DEFAULT_FILE=/etc/default/${NAME} # Source Jellyfin default configuration @@ -10,13 +9,10 @@ if [[ -f $DEFAULT_FILE ]]; then . $DEFAULT_FILE fi -# Source Jellyfin user configuration overrides -if [[ -f $CONF_FILE ]]; then - . $CONF_FILE -fi - -# Data directory where Jellyfin database, cache and logs are stored -PROGRAMDATA=${JELLYFIN_DATA-/var/lib/$NAME} +# Data directories for program data (cache, db), configs, and logs +PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME} +CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME} +LOGDATA=${JELLYFIN_DATA_DIRECTORY-/var/log/$NAME} case "$1" in remove|upgrade|deconfigure) diff --git a/debian/rules b/deployment/debian-package-x64/pkg-src/rules index 10a3a8486..ce98cb8f8 100755..100644 --- a/debian/rules +++ b/deployment/debian-package-x64/pkg-src/rules @@ -2,7 +2,7 @@ CONFIG := Release TERM := xterm SHELL := /bin/bash -DOTNETRUNTIME := linux-x64 +DOTNETRUNTIME := debian-x64 export DH_VERBOSE=1 export DOTNET_CLI_TELEMETRY_OPTOUT=1 @@ -16,8 +16,8 @@ override_dh_auto_test: override_dh_clistrip: override_dh_auto_build: - dotnet publish --configuration $(CONFIG) --output='$(CURDIR)/usr/lib/jellyfin/bin' --self-contained --runtime $(DOTNETRUNTIME) + dotnet publish --configuration $(CONFIG) --output='$(CURDIR)/usr/lib/jellyfin/bin' --self-contained --runtime $(DOTNETRUNTIME) Jellyfin.Server override_dh_auto_clean: - dotnet clean -maxcpucount:1 --configuration $(CONFIG) || true + dotnet clean -maxcpucount:1 --configuration $(CONFIG) Jellyfin.Server || true rm -rf '$(CURDIR)/usr' diff --git a/debian/source.lintian-overrides b/deployment/debian-package-x64/pkg-src/source.lintian-overrides index aeb332f13..aeb332f13 100644 --- a/debian/source.lintian-overrides +++ b/deployment/debian-package-x64/pkg-src/source.lintian-overrides diff --git a/debian/source/format b/deployment/debian-package-x64/pkg-src/source/format index d3827e75a..d3827e75a 100644 --- a/debian/source/format +++ b/deployment/debian-package-x64/pkg-src/source/format diff --git a/deployment/debian-package-x64/pkg-src/source/options b/deployment/debian-package-x64/pkg-src/source/options new file mode 100644 index 000000000..17b5373d5 --- /dev/null +++ b/deployment/debian-package-x64/pkg-src/source/options @@ -0,0 +1,11 @@ +tar-ignore='.git*' +tar-ignore='**/.git' +tar-ignore='**/.hg' +tar-ignore='**/.vs' +tar-ignore='**/.vscode' +tar-ignore='deployment' +tar-ignore='**/bin' +tar-ignore='**/obj' +tar-ignore='**/.nuget' +tar-ignore='*.deb' +tar-ignore='ThirdParty' diff --git a/deployment/debian-x64/build.sh b/deployment/debian-x64/build.sh new file mode 100644 index 000000000..47cfb5327 --- /dev/null +++ b/deployment/debian-x64/build.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +build_jellyfin ../../Jellyfin.Server Release debian-x64 `pwd`/dist/jellyfin_${VERSION} diff --git a/deployment/debian-x64/clean.sh b/deployment/debian-x64/clean.sh new file mode 100644 index 000000000..3df2d7796 --- /dev/null +++ b/deployment/debian-x64/clean.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION} diff --git a/deployment/debian-x64/package.sh b/deployment/debian-x64/package.sh new file mode 100644 index 000000000..13b943ea8 --- /dev/null +++ b/deployment/debian-x64/package.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +package_portable ../.. `pwd`/dist/jellyfin_${VERSION} diff --git a/deployment/docker/Dockerfile b/deployment/docker/Dockerfile new file mode 100644 index 000000000..c32cdcf9b --- /dev/null +++ b/deployment/docker/Dockerfile @@ -0,0 +1,28 @@ +ARG DOTNET_VERSION=2 + + +# Download ffmpeg first to allow quicker rebuild of other layers +FROM alpine as ffmpeg +ARG FFMPEG_URL=https://www.johnvansickle.com/ffmpeg/old-releases/ffmpeg-4.0.3-64bit-static.tar.xz +RUN wget ${FFMPEG_URL} -O - | tar Jxf - \ + && mkdir ffmpeg-bin \ + && mv ffmpeg*/ffmpeg ffmpeg-bin \ + && mv ffmpeg*/ffprobe ffmpeg-bin + + +FROM microsoft/dotnet:${DOTNET_VERSION}-sdk as builder +WORKDIR /repo +COPY . . +RUN export DOTNET_CLI_TELEMETRY_OPTOUT=1 \ + && dotnet clean \ + && dotnet publish --configuration release --output /jellyfin Jellyfin.Server + + +FROM microsoft/dotnet:${DOTNET_VERSION}-runtime +COPY --from=builder /jellyfin /jellyfin +COPY --from=ffmpeg /ffmpeg-bin/* /usr/bin/ +EXPOSE 8096 +VOLUME /config /media +RUN apt update \ + && apt install -y libfontconfig1 --no-install-recommends # needed for Skia +ENTRYPOINT dotnet /jellyfin/jellyfin.dll -programdata /config diff --git a/Dockerfile.aarch64 b/deployment/docker/Dockerfile.aarch64 index da4acc841..d1423dda2 100644 --- a/Dockerfile.aarch64 +++ b/deployment/docker/Dockerfile.aarch64 @@ -6,7 +6,7 @@ COPY . . RUN export DOTNET_CLI_TELEMETRY_OPTOUT=1 \ && find . -type f -exec sed -i 's/netcoreapp2.1/netcoreapp3.0/g' {} \; \ && dotnet clean \ - && dotnet publish --configuration release --output /jellyfin + && dotnet publish --configuration release --output /jellyfin Jellyfin.Server FROM microsoft/dotnet:${DOTNET_VERSION}-runtime COPY --from=builder /jellyfin /jellyfin diff --git a/deployment/docker/build.sh b/deployment/docker/build.sh new file mode 100644 index 000000000..b75cedc01 --- /dev/null +++ b/deployment/docker/build.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +build_jellyfin_docker ../.. Dockerfile jellyfin:${VERSION}
\ No newline at end of file diff --git a/deployment/framework/build.sh b/deployment/framework/build.sh new file mode 100644 index 000000000..4f2e6363e --- /dev/null +++ b/deployment/framework/build.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +#Magic word framework will create a non self contained build +build_jellyfin ../../Jellyfin.Server Release framework `pwd`/dist/jellyfin_${VERSION} diff --git a/deployment/framework/clean.sh b/deployment/framework/clean.sh new file mode 100644 index 000000000..3df2d7796 --- /dev/null +++ b/deployment/framework/clean.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION} diff --git a/deployment/framework/package.sh b/deployment/framework/package.sh new file mode 100644 index 000000000..13b943ea8 --- /dev/null +++ b/deployment/framework/package.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +package_portable ../.. `pwd`/dist/jellyfin_${VERSION} diff --git a/deployment/linux-x64/build.sh b/deployment/linux-x64/build.sh new file mode 100644 index 000000000..1f0fb62d3 --- /dev/null +++ b/deployment/linux-x64/build.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +build_jellyfin ../../Jellyfin.Server Release linux-x64 `pwd`/dist/jellyfin_${VERSION} diff --git a/deployment/linux-x64/clean.sh b/deployment/linux-x64/clean.sh new file mode 100644 index 000000000..3df2d7796 --- /dev/null +++ b/deployment/linux-x64/clean.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION} diff --git a/deployment/linux-x64/package.sh b/deployment/linux-x64/package.sh new file mode 100644 index 000000000..13b943ea8 --- /dev/null +++ b/deployment/linux-x64/package.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +package_portable ../.. `pwd`/dist/jellyfin_${VERSION} diff --git a/deployment/osx-x64/build.sh b/deployment/osx-x64/build.sh new file mode 100644 index 000000000..d6bfb9f5e --- /dev/null +++ b/deployment/osx-x64/build.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +build_jellyfin ../../Jellyfin.Server Release osx-x64 `pwd`/dist/jellyfin_${VERSION} diff --git a/deployment/osx-x64/clean.sh b/deployment/osx-x64/clean.sh new file mode 100644 index 000000000..3df2d7796 --- /dev/null +++ b/deployment/osx-x64/clean.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION} diff --git a/deployment/osx-x64/package.sh b/deployment/osx-x64/package.sh new file mode 100644 index 000000000..13b943ea8 --- /dev/null +++ b/deployment/osx-x64/package.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +package_portable ../.. `pwd`/dist/jellyfin_${VERSION} diff --git a/deployment/ubuntu-x64/build.sh b/deployment/ubuntu-x64/build.sh new file mode 100644 index 000000000..870bac780 --- /dev/null +++ b/deployment/ubuntu-x64/build.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +build_jellyfin ../../Jellyfin.Server Release ubuntu-x64 `pwd`/dist/jellyfin_${VERSION} diff --git a/deployment/ubuntu-x64/clean.sh b/deployment/ubuntu-x64/clean.sh new file mode 100644 index 000000000..3df2d7796 --- /dev/null +++ b/deployment/ubuntu-x64/clean.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION} diff --git a/deployment/ubuntu-x64/package.sh b/deployment/ubuntu-x64/package.sh new file mode 100644 index 000000000..13b943ea8 --- /dev/null +++ b/deployment/ubuntu-x64/package.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +package_portable ../.. `pwd`/dist/jellyfin_${VERSION} diff --git a/deployment/unraid/docker-templates/README.md b/deployment/unraid/docker-templates/README.md new file mode 100644 index 000000000..2c268e8b3 --- /dev/null +++ b/deployment/unraid/docker-templates/README.md @@ -0,0 +1,15 @@ +# docker-templates + +### Installation: + +Open unRaid GUI (at least unRaid 6.5) + +Click on the Docker tab + +Add the following line under "Template Repositories" + +https://github.com/jellyfin/jellyfin/blob/master/deployment/unraid/docker-templates + +Click save than click on Add Container and select jellyfin. + +Adjust to your paths to your liking and off you go! diff --git a/deployment/unraid/docker-templates/jellyfin.xml b/deployment/unraid/docker-templates/jellyfin.xml new file mode 100644 index 000000000..be1188424 --- /dev/null +++ b/deployment/unraid/docker-templates/jellyfin.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> +<Containers> + <TemplateURL>https://raw.githubusercontent.com/jellyfin/jellyfin/deployment/unraid/docker-templates/jellyfin.xml</TemplateURL> + <Beta>False</Beta> + <Category>MediaApp:Video MediaApp:Music MediaApp:Photos MediaServer:Video MediaServer:Music MediaServer:Photos</Category> + <Name>JellyFin</Name> + <Description> + JellyFin is The Free Software Media Browser Converted By Community Applications Always verify this template (and values) against the dockerhub support page for the container!![br][br] + You can add as many mount points as needed for recordings, movies ,etc. [br][br] + [b][span style='color: #E80000;']Directions:[/span][/b][br] + [b]/config[/b] : this is where Jellyfin will store it's databases and configuration.[br][br] + [b]Port[/b] : This is the default port for Jellyfin. (Will add ssl port later)[br][br] + [b]Media[/b] : This is the mounting point of your media. When you access it in Jellyfin it will be /media or whatever you chose for a mount point + [b]Tip:[/b] You can add more volume mappings if you wish Jellyfin has access to it. + </Description> + <Overview> + Jellyfin Server is a home media server built on top of other popular open source technologies such as Service Stack, jQuery, jQuery mobile, and Mono and will remain completely free! + </Overview> + <Support>https://www.reddit.com/r/jellyfin/</Support> + <Registry>https://hub.docker.com/r/jellyfin/jellyfin/</Registry> + <GitHub>https://github.com/jellyfin/jellyfin/></GitHub> + <Repository>jellyfin/jellyfin</Repository> + <Project>https://jellyfin.media/</Project> + <BindTime>true</BindTime> + <Privileged>false</Privileged> + <Networking> + <Mode>host</Mode> + <Publish> + <Port> + <HostPort>8096</HostPort> + <ContainerPort>8096</ContainerPort> + <Protocol>tcp</Protocol> + </Port> + </Publish> + </Networking> + <Data> + <Volume> + <HostDir>/mnt/cache/appdata/config</HostDir> + <ContainerDir>/config</ContainerDir> + <Mode>rw</Mode> + </Volume> + <Volume> + <HostDir>/mnt/user</HostDir> + <ContainerDir>/media</ContainerDir> + <Mode>rw</Mode> + </Volume> + </Data> + <WebUI>http://[IP]:[PORT:8096]/</WebUI> + <Icon>https://raw.githubusercontent.com/binhex/docker-templates/master/binhex/images/emby-icon.png</Icon> + <ExtraParams></ExtraParams> +</Containers> diff --git a/build-jellyfin.ps1 b/deployment/win-generic/build-jellyfin.ps1 index 224716621..4f0f92525 100644 --- a/build-jellyfin.ps1 +++ b/deployment/win-generic/build-jellyfin.ps1 @@ -1,110 +1,110 @@ -[CmdletBinding()]
-param(
- [switch]$InstallFFMPEG,
- [switch]$InstallNSSM,
- [switch]$GenerateZip,
- [string]$InstallLocation = "$Env:AppData/Jellyfin-Server/",
- [ValidateSet('Debug','Release')][string]$BuildType = 'Release',
- [ValidateSet('Quiet','Minimal', 'Normal')][string]$DotNetVerbosity = 'Minimal',
- [ValidateSet('win','win7', 'win8','win81','win10')][string]$WindowsVersion = 'win',
- [ValidateSet('x64','x86', 'arm', 'arm64')][string]$Architecture = 'x64'
-)
-
-#PowershellCore and *nix check to make determine which temp dir to use.
-if(($PSVersionTable.PSEdition -eq 'Core') -and (-not $IsWindows)){
- $TempDir = mktemp -d
-}else{
- $TempDir = $env:Temp
-}
-
-function Build-JellyFin {
- if(($Architecture -eq 'arm64') -and ($WindowsVersion -ne 'win10')){
- Write-Error "arm64 only supported with Windows10 Version"
- exit
- }
- if(($Architecture -eq 'arm') -and ($WindowsVersion -notin @('win10','win81','win8'))){
- Write-Error "arm only supported with Windows 8 or higher"
- exit
- }
- dotnet publish -c $BuildType -r "$windowsversion-$Architecture" MediaBrowser.sln -o $InstallLocation -v $DotNetVerbosity
-}
-
-function Install-FFMPEG {
- param(
- [string]$InstallLocation,
- [string]$Architecture
- )
- Write-Verbose "Checking Architecture"
- if($Architecture -notin @('x86','x64')){
- Write-Warning "No builds available for your selected architecture of $Architecture"
- Write-Warning "FFMPEG will not be installed"
- }elseif($Architecture -eq 'x64'){
- Write-Verbose "Downloading 64 bit FFMPEG"
- Invoke-WebRequest -Uri https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-4.1-win64-static.zip -UseBasicParsing -OutFile "$tempdir/fmmpeg.zip" | Write-Verbose
- }else{
- Write-Verbose "Downloading 32 bit FFMPEG"
- Invoke-WebRequest -Uri https://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-4.1-win32-static.zip -UseBasicParsing -OutFile "$tempdir/fmmpeg.zip" | Write-Verbose
- }
-
- Expand-Archive "$tempdir/fmmpeg.zip" -DestinationPath "$tempdir/ffmpeg/" | Write-Verbose
- if($Architecture -eq 'x64'){
- Write-Verbose "Copying Binaries to Jellyfin location"
- Get-ChildItem "$tempdir/ffmpeg/ffmpeg-4.1-win64-static/bin" | ForEach-Object {
- Copy-Item $_.FullName -Destination $installLocation | Write-Verbose
- }
- }else{
- Write-Verbose "Copying Binaries to Jellyfin location"
- Get-ChildItem "$tempdir/ffmpeg/ffmpeg-4.1-win32-static/bin" | ForEach-Object {
- Copy-Item $_.FullName -Destination $installLocation | Write-Verbose
- }
- }
- Remove-Item "$tempdir/ffmpeg/" -Recurse -Force -ErrorAction Continue | Write-Verbose
- Remove-Item "$tempdir/fmmpeg.zip" -Force -ErrorAction Continue | Write-Verbose
-}
-
-function Install-NSSM {
- param(
- [string]$InstallLocation,
- [string]$Architecture
- )
- Write-Verbose "Checking Architecture"
- if($Architecture -notin @('x86','x64')){
- Write-Warning "No builds available for your selected architecture of $Architecture"
- Write-Warning "NSSM will not be installed"
- }else{
- Write-Verbose "Downloading NSSM"
- Invoke-WebRequest -Uri https://nssm.cc/ci/nssm-2.24-101-g897c7ad.zip -UseBasicParsing -OutFile "$tempdir/nssm.zip" | Write-Verbose
- }
-
- Expand-Archive "$tempdir/nssm.zip" -DestinationPath "$tempdir/nssm/" | Write-Verbose
- if($Architecture -eq 'x64'){
- Write-Verbose "Copying Binaries to Jellyfin location"
- Get-ChildItem "$tempdir/nssm/nssm-2.24-101-g897c7ad/win64" | ForEach-Object {
- Copy-Item $_.FullName -Destination $installLocation | Write-Verbose
- }
- }else{
- Write-Verbose "Copying Binaries to Jellyfin location"
- Get-ChildItem "$tempdir/nssm/nssm-2.24-101-g897c7ad/win32" | ForEach-Object {
- Copy-Item $_.FullName -Destination $installLocation | Write-Verbose
- }
- }
- Remove-Item "$tempdir/nssm/" -Recurse -Force -ErrorAction Continue | Write-Verbose
- Remove-Item "$tempdir/nssm.zip" -Force -ErrorAction Continue | Write-Verbose
-}
-
-Write-Verbose "Starting Build Process: Selected Environment is $WindowsVersion-$Architecture"
-Build-JellyFin
-if($InstallFFMPEG.IsPresent -or ($InstallFFMPEG -eq $true)){
- Write-Verbose "Starting FFMPEG Install"
- Install-FFMPEG $InstallLocation $Architecture
-}
-if($InstallNSSM.IsPresent -or ($InstallNSSM -eq $true)){
- Write-Verbose "Starting NSSM Install"
- Install-NSSM $InstallLocation $Architecture
-}
-Copy-Item .\install-jellyfin.ps1 $InstallLocation\install-jellyfin.ps1
-Copy-Item .\install.bat $InstallLocation\install.bat
-if($GenerateZip.IsPresent -or ($GenerateZip -eq $true)){
- Compress-Archive -Path $InstallLocation -DestinationPath "$InstallLocation/jellyfin.zip" -Force
-}
-Write-Verbose "Finished"
+[CmdletBinding()] +param( + [switch]$InstallFFMPEG, + [switch]$InstallNSSM, + [switch]$GenerateZip, + [string]$InstallLocation = "$Env:AppData/Jellyfin-Server/", + [ValidateSet('Debug','Release')][string]$BuildType = 'Release', + [ValidateSet('Quiet','Minimal', 'Normal')][string]$DotNetVerbosity = 'Minimal', + [ValidateSet('win','win7', 'win8','win81','win10')][string]$WindowsVersion = 'win', + [ValidateSet('x64','x86', 'arm', 'arm64')][string]$Architecture = 'x64' +) + +#PowershellCore and *nix check to make determine which temp dir to use. +if(($PSVersionTable.PSEdition -eq 'Core') -and (-not $IsWindows)){ + $TempDir = mktemp -d +}else{ + $TempDir = $env:Temp +} + +function Build-JellyFin { + if(($Architecture -eq 'arm64') -and ($WindowsVersion -ne 'win10')){ + Write-Error "arm64 only supported with Windows10 Version" + exit + } + if(($Architecture -eq 'arm') -and ($WindowsVersion -notin @('win10','win81','win8'))){ + Write-Error "arm only supported with Windows 8 or higher" + exit + } + dotnet publish -c $BuildType -r "$windowsversion-$Architecture" MediaBrowser.sln -o $InstallLocation -v $DotNetVerbosity +} + +function Install-FFMPEG { + param( + [string]$InstallLocation, + [string]$Architecture + ) + Write-Verbose "Checking Architecture" + if($Architecture -notin @('x86','x64')){ + Write-Warning "No builds available for your selected architecture of $Architecture" + Write-Warning "FFMPEG will not be installed" + }elseif($Architecture -eq 'x64'){ + Write-Verbose "Downloading 64 bit FFMPEG" + Invoke-WebRequest -Uri https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-4.1-win64-static.zip -UseBasicParsing -OutFile "$tempdir/fmmpeg.zip" | Write-Verbose + }else{ + Write-Verbose "Downloading 32 bit FFMPEG" + Invoke-WebRequest -Uri https://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-4.1-win32-static.zip -UseBasicParsing -OutFile "$tempdir/fmmpeg.zip" | Write-Verbose + } + + Expand-Archive "$tempdir/fmmpeg.zip" -DestinationPath "$tempdir/ffmpeg/" | Write-Verbose + if($Architecture -eq 'x64'){ + Write-Verbose "Copying Binaries to Jellyfin location" + Get-ChildItem "$tempdir/ffmpeg/ffmpeg-4.1-win64-static/bin" | ForEach-Object { + Copy-Item $_.FullName -Destination $installLocation | Write-Verbose + } + }else{ + Write-Verbose "Copying Binaries to Jellyfin location" + Get-ChildItem "$tempdir/ffmpeg/ffmpeg-4.1-win32-static/bin" | ForEach-Object { + Copy-Item $_.FullName -Destination $installLocation | Write-Verbose + } + } + Remove-Item "$tempdir/ffmpeg/" -Recurse -Force -ErrorAction Continue | Write-Verbose + Remove-Item "$tempdir/fmmpeg.zip" -Force -ErrorAction Continue | Write-Verbose +} + +function Install-NSSM { + param( + [string]$InstallLocation, + [string]$Architecture + ) + Write-Verbose "Checking Architecture" + if($Architecture -notin @('x86','x64')){ + Write-Warning "No builds available for your selected architecture of $Architecture" + Write-Warning "NSSM will not be installed" + }else{ + Write-Verbose "Downloading NSSM" + Invoke-WebRequest -Uri https://nssm.cc/ci/nssm-2.24-101-g897c7ad.zip -UseBasicParsing -OutFile "$tempdir/nssm.zip" | Write-Verbose + } + + Expand-Archive "$tempdir/nssm.zip" -DestinationPath "$tempdir/nssm/" | Write-Verbose + if($Architecture -eq 'x64'){ + Write-Verbose "Copying Binaries to Jellyfin location" + Get-ChildItem "$tempdir/nssm/nssm-2.24-101-g897c7ad/win64" | ForEach-Object { + Copy-Item $_.FullName -Destination $installLocation | Write-Verbose + } + }else{ + Write-Verbose "Copying Binaries to Jellyfin location" + Get-ChildItem "$tempdir/nssm/nssm-2.24-101-g897c7ad/win32" | ForEach-Object { + Copy-Item $_.FullName -Destination $installLocation | Write-Verbose + } + } + Remove-Item "$tempdir/nssm/" -Recurse -Force -ErrorAction Continue | Write-Verbose + Remove-Item "$tempdir/nssm.zip" -Force -ErrorAction Continue | Write-Verbose +} + +Write-Verbose "Starting Build Process: Selected Environment is $WindowsVersion-$Architecture" +Build-JellyFin +if($InstallFFMPEG.IsPresent -or ($InstallFFMPEG -eq $true)){ + Write-Verbose "Starting FFMPEG Install" + Install-FFMPEG $InstallLocation $Architecture +} +if($InstallNSSM.IsPresent -or ($InstallNSSM -eq $true)){ + Write-Verbose "Starting NSSM Install" + Install-NSSM $InstallLocation $Architecture +} +Copy-Item .\install-jellyfin.ps1 $InstallLocation\install-jellyfin.ps1 +Copy-Item .\install.bat $InstallLocation\install.bat +if($GenerateZip.IsPresent -or ($GenerateZip -eq $true)){ + Compress-Archive -Path $InstallLocation -DestinationPath "$InstallLocation/jellyfin.zip" -Force +} +Write-Verbose "Finished" diff --git a/install-jellyfin.ps1 b/deployment/win-generic/install-jellyfin.ps1 index 56c098462..56c098462 100644 --- a/install-jellyfin.ps1 +++ b/deployment/win-generic/install-jellyfin.ps1 diff --git a/install.bat b/deployment/win-generic/install.bat index e21479a79..e21479a79 100644 --- a/install.bat +++ b/deployment/win-generic/install.bat diff --git a/deployment/win-x64/build.sh b/deployment/win-x64/build.sh new file mode 100644 index 000000000..0b3046203 --- /dev/null +++ b/deployment/win-x64/build.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +build_jellyfin ../../Jellyfin.Server Release win-x64 `pwd`/dist/jellyfin_${VERSION} diff --git a/deployment/win-x64/clean.sh b/deployment/win-x64/clean.sh new file mode 100644 index 000000000..3df2d7796 --- /dev/null +++ b/deployment/win-x64/clean.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION} diff --git a/deployment/win-x64/package.sh b/deployment/win-x64/package.sh new file mode 100644 index 000000000..e8410e8c2 --- /dev/null +++ b/deployment/win-x64/package.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +package_portable ../.. `pwd`/dist/jellyfin_${VERSION} + +#TODO setup and maybe change above code to produce the Windows native zip format. diff --git a/deployment/win-x86/build.sh b/deployment/win-x86/build.sh new file mode 100644 index 000000000..610db356a --- /dev/null +++ b/deployment/win-x86/build.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +build_jellyfin ../../Jellyfin.Server Release win-x86 `pwd`/dist/jellyfin_${VERSION} diff --git a/deployment/win-x86/clean.sh b/deployment/win-x86/clean.sh new file mode 100644 index 000000000..3df2d7796 --- /dev/null +++ b/deployment/win-x86/clean.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION} diff --git a/deployment/win-x86/package.sh b/deployment/win-x86/package.sh new file mode 100644 index 000000000..e8410e8c2 --- /dev/null +++ b/deployment/win-x86/package.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +source ../common.build.sh + +VERSION=`get_version ../..` + +package_portable ../.. `pwd`/dist/jellyfin_${VERSION} + +#TODO setup and maybe change above code to produce the Windows native zip format. |
