aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Server
diff options
context:
space:
mode:
Diffstat (limited to 'Jellyfin.Server')
-rw-r--r--Jellyfin.Server/CoreAppHost.cs17
-rw-r--r--Jellyfin.Server/Jellyfin.Server.csproj2
-rw-r--r--Jellyfin.Server/Migrations/MigrationRunner.cs4
-rw-r--r--Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs174
-rw-r--r--Jellyfin.Server/Migrations/Routines/ReaddDefaultPluginRepository.cs49
-rw-r--r--Jellyfin.Server/Program.cs15
6 files changed, 252 insertions, 9 deletions
diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs
index 207eaa98d..29a59e1c8 100644
--- a/Jellyfin.Server/CoreAppHost.cs
+++ b/Jellyfin.Server/CoreAppHost.cs
@@ -9,6 +9,7 @@ using Jellyfin.Server.Implementations;
using Jellyfin.Server.Implementations.Activity;
using Jellyfin.Server.Implementations.Users;
using MediaBrowser.Common.Net;
+using MediaBrowser.Controller;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Activity;
@@ -33,9 +34,9 @@ namespace Jellyfin.Server
/// <param name="fileSystem">The <see cref="IFileSystem" /> to be used by the <see cref="CoreAppHost" />.</param>
/// <param name="networkManager">The <see cref="INetworkManager" /> to be used by the <see cref="CoreAppHost" />.</param>
public CoreAppHost(
- ServerApplicationPaths applicationPaths,
+ IServerApplicationPaths applicationPaths,
ILoggerFactory loggerFactory,
- StartupOptions options,
+ IStartupOptions options,
IFileSystem fileSystem,
INetworkManager networkManager)
: base(
@@ -63,16 +64,18 @@ namespace Jellyfin.Server
Logger.LogWarning($"Skia not available. Will fallback to {nameof(NullImageEncoder)}.");
}
- // TODO: Set up scoping and use AddDbContextPool
- serviceCollection.AddDbContext<JellyfinDb>(
- options => options
- .UseSqlite($"Filename={Path.Combine(ApplicationPaths.DataPath, "jellyfin.db")}"),
- ServiceLifetime.Transient);
+ // TODO: Set up scoping and use AddDbContextPool,
+ // can't register as Transient since tracking transient in GC is funky
+ // serviceCollection.AddDbContext<JellyfinDb>(
+ // options => options
+ // .UseSqlite($"Filename={Path.Combine(ApplicationPaths.DataPath, "jellyfin.db")}"),
+ // ServiceLifetime.Transient);
serviceCollection.AddSingleton<JellyfinDbProvider>();
serviceCollection.AddSingleton<IActivityManager, ActivityManager>();
serviceCollection.AddSingleton<IUserManager, UserManager>();
+ serviceCollection.AddSingleton<IDisplayPreferencesManager, DisplayPreferencesManager>();
base.RegisterServices(serviceCollection);
}
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index b1bd38cff..7541707d9 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -45,7 +45,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.6" />
<PackageReference Include="prometheus-net" Version="3.6.0" />
<PackageReference Include="prometheus-net.AspNetCore" Version="3.6.0" />
- <PackageReference Include="Serilog.AspNetCore" Version="3.2.0" />
+ <PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.4.0" />
diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs
index fe8910c3c..844dae61f 100644
--- a/Jellyfin.Server/Migrations/MigrationRunner.cs
+++ b/Jellyfin.Server/Migrations/MigrationRunner.cs
@@ -21,7 +21,9 @@ namespace Jellyfin.Server.Migrations
typeof(Routines.MigrateActivityLogDb),
typeof(Routines.RemoveDuplicateExtras),
typeof(Routines.AddDefaultPluginRepository),
- typeof(Routines.MigrateUserDb)
+ typeof(Routines.MigrateUserDb),
+ typeof(Routines.ReaddDefaultPluginRepository),
+ typeof(Routines.MigrateDisplayPreferencesDb)
};
/// <summary>
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
new file mode 100644
index 000000000..b15ccf01e
--- /dev/null
+++ b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
@@ -0,0 +1,174 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
+using Jellyfin.Server.Implementations;
+using MediaBrowser.Controller;
+using MediaBrowser.Model.Entities;
+using Microsoft.Extensions.Logging;
+using SQLitePCL.pretty;
+
+namespace Jellyfin.Server.Migrations.Routines
+{
+ /// <summary>
+ /// The migration routine for migrating the display preferences database to EF Core.
+ /// </summary>
+ public class MigrateDisplayPreferencesDb : IMigrationRoutine
+ {
+ private const string DbFilename = "displaypreferences.db";
+
+ private readonly ILogger<MigrateDisplayPreferencesDb> _logger;
+ private readonly IServerApplicationPaths _paths;
+ private readonly JellyfinDbProvider _provider;
+ private readonly JsonSerializerOptions _jsonOptions;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MigrateDisplayPreferencesDb"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="paths">The server application paths.</param>
+ /// <param name="provider">The database provider.</param>
+ public MigrateDisplayPreferencesDb(ILogger<MigrateDisplayPreferencesDb> logger, IServerApplicationPaths paths, JellyfinDbProvider provider)
+ {
+ _logger = logger;
+ _paths = paths;
+ _provider = provider;
+ _jsonOptions = new JsonSerializerOptions();
+ _jsonOptions.Converters.Add(new JsonStringEnumConverter());
+ }
+
+ /// <inheritdoc />
+ public Guid Id => Guid.Parse("06387815-C3CC-421F-A888-FB5F9992BEA8");
+
+ /// <inheritdoc />
+ public string Name => "MigrateDisplayPreferencesDatabase";
+
+ /// <inheritdoc />
+ public bool PerformOnNewInstall => false;
+
+ /// <inheritdoc />
+ public void Perform()
+ {
+ HomeSectionType[] defaults =
+ {
+ HomeSectionType.SmallLibraryTiles,
+ HomeSectionType.Resume,
+ HomeSectionType.ResumeAudio,
+ HomeSectionType.LiveTv,
+ HomeSectionType.NextUp,
+ HomeSectionType.LatestMedia,
+ HomeSectionType.None,
+ };
+
+ var chromecastDict = new Dictionary<string, ChromecastVersion>(StringComparer.OrdinalIgnoreCase)
+ {
+ { "stable", ChromecastVersion.Stable },
+ { "nightly", ChromecastVersion.Unstable },
+ { "unstable", ChromecastVersion.Unstable }
+ };
+
+ var dbFilePath = Path.Combine(_paths.DataPath, DbFilename);
+ using (var connection = SQLite3.Open(dbFilePath, ConnectionFlags.ReadOnly, null))
+ {
+ using var dbContext = _provider.CreateContext();
+
+ var results = connection.Query("SELECT * FROM userdisplaypreferences");
+ foreach (var result in results)
+ {
+ var dto = JsonSerializer.Deserialize<DisplayPreferencesDto>(result[3].ToString(), _jsonOptions);
+ var chromecastVersion = dto.CustomPrefs.TryGetValue("chromecastVersion", out var version)
+ ? chromecastDict[version]
+ : ChromecastVersion.Stable;
+
+ var displayPreferences = new DisplayPreferences(new Guid(result[1].ToBlob()), result[2].ToString())
+ {
+ IndexBy = Enum.TryParse<IndexingKind>(dto.IndexBy, true, out var indexBy) ? indexBy : (IndexingKind?)null,
+ ShowBackdrop = dto.ShowBackdrop,
+ ShowSidebar = dto.ShowSidebar,
+ ScrollDirection = dto.ScrollDirection,
+ ChromecastVersion = chromecastVersion,
+ SkipForwardLength = dto.CustomPrefs.TryGetValue("skipForwardLength", out var length)
+ ? int.Parse(length, CultureInfo.InvariantCulture)
+ : 30000,
+ SkipBackwardLength = dto.CustomPrefs.TryGetValue("skipBackLength", out length)
+ ? int.Parse(length, CultureInfo.InvariantCulture)
+ : 10000,
+ EnableNextVideoInfoOverlay = dto.CustomPrefs.TryGetValue("enableNextVideoInfoOverlay", out var enabled)
+ ? bool.Parse(enabled)
+ : true,
+ DashboardTheme = dto.CustomPrefs.TryGetValue("dashboardtheme", out var theme) ? theme : string.Empty,
+ TvHome = dto.CustomPrefs.TryGetValue("tvhome", out var home) ? home : string.Empty
+ };
+
+ for (int i = 0; i < 7; i++)
+ {
+ dto.CustomPrefs.TryGetValue("homesection" + i, out var homeSection);
+
+ displayPreferences.HomeSections.Add(new HomeSection
+ {
+ Order = i,
+ Type = Enum.TryParse<HomeSectionType>(homeSection, true, out var type) ? type : defaults[i]
+ });
+ }
+
+ var defaultLibraryPrefs = new ItemDisplayPreferences(displayPreferences.UserId, Guid.Empty, displayPreferences.Client)
+ {
+ SortBy = dto.SortBy ?? "SortName",
+ SortOrder = dto.SortOrder,
+ RememberIndexing = dto.RememberIndexing,
+ RememberSorting = dto.RememberSorting,
+ };
+
+ dbContext.Add(defaultLibraryPrefs);
+
+ foreach (var key in dto.CustomPrefs.Keys.Where(key => key.StartsWith("landing-", StringComparison.Ordinal)))
+ {
+ if (!Guid.TryParse(key.AsSpan().Slice("landing-".Length), out var itemId))
+ {
+ continue;
+ }
+
+ var libraryDisplayPreferences = new ItemDisplayPreferences(displayPreferences.UserId, itemId, displayPreferences.Client)
+ {
+ SortBy = dto.SortBy ?? "SortName",
+ SortOrder = dto.SortOrder,
+ RememberIndexing = dto.RememberIndexing,
+ RememberSorting = dto.RememberSorting,
+ };
+
+ if (Enum.TryParse<ViewType>(dto.ViewType, true, out var viewType))
+ {
+ libraryDisplayPreferences.ViewType = viewType;
+ }
+
+ dbContext.ItemDisplayPreferences.Add(libraryDisplayPreferences);
+ }
+
+ dbContext.Add(displayPreferences);
+ }
+
+ dbContext.SaveChanges();
+ }
+
+ try
+ {
+ File.Move(dbFilePath, dbFilePath + ".old");
+
+ var journalPath = dbFilePath + "-journal";
+ if (File.Exists(journalPath))
+ {
+ File.Move(journalPath, dbFilePath + ".old-journal");
+ }
+ }
+ catch (IOException e)
+ {
+ _logger.LogError(e, "Error renaming legacy display preferences database to 'displaypreferences.db.old'");
+ }
+ }
+ }
+}
diff --git a/Jellyfin.Server/Migrations/Routines/ReaddDefaultPluginRepository.cs b/Jellyfin.Server/Migrations/Routines/ReaddDefaultPluginRepository.cs
new file mode 100644
index 000000000..b281b5cc0
--- /dev/null
+++ b/Jellyfin.Server/Migrations/Routines/ReaddDefaultPluginRepository.cs
@@ -0,0 +1,49 @@
+using System;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.Updates;
+
+namespace Jellyfin.Server.Migrations.Routines
+{
+ /// <summary>
+ /// Migration to initialize system configuration with the default plugin repository.
+ /// </summary>
+ public class ReaddDefaultPluginRepository : IMigrationRoutine
+ {
+ private readonly IServerConfigurationManager _serverConfigurationManager;
+
+ private readonly RepositoryInfo _defaultRepositoryInfo = new RepositoryInfo
+ {
+ Name = "Jellyfin Stable",
+ Url = "https://repo.jellyfin.org/releases/plugin/manifest-stable.json"
+ };
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ReaddDefaultPluginRepository"/> class.
+ /// </summary>
+ /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
+ public ReaddDefaultPluginRepository(IServerConfigurationManager serverConfigurationManager)
+ {
+ _serverConfigurationManager = serverConfigurationManager;
+ }
+
+ /// <inheritdoc/>
+ public Guid Id => Guid.Parse("5F86E7F6-D966-4C77-849D-7A7B40B68C4E");
+
+ /// <inheritdoc/>
+ public string Name => "ReaddDefaultPluginRepository";
+
+ /// <inheritdoc/>
+ public bool PerformOnNewInstall => true;
+
+ /// <inheritdoc/>
+ public void Perform()
+ {
+ // Only add if repository list is empty
+ if (_serverConfigurationManager.Configuration.PluginRepositories.Count == 0)
+ {
+ _serverConfigurationManager.Configuration.PluginRepositories.Add(_defaultRepositoryInfo);
+ _serverConfigurationManager.SaveConfiguration();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index 288a5c259..9fd706d36 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -343,6 +343,21 @@ namespace Jellyfin.Server
}
}
}
+
+ // Bind to unix socket (only on OSX and Linux)
+ if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ // TODO: allow configuration of socket path
+ var socketPath = $"{appPaths.DataPath}/socket.sock";
+ // Workaround for https://github.com/aspnet/AspNetCore/issues/14134
+ if (File.Exists(socketPath))
+ {
+ File.Delete(socketPath);
+ }
+
+ options.ListenUnixSocket(socketPath);
+ _logger.LogInformation("Kestrel listening to unix socket {SocketPath}", socketPath);
+ }
})
.ConfigureAppConfiguration(config => config.ConfigureAppConfiguration(commandLineOpts, appPaths, startupConfig))
.UseSerilog()