remove JellyfinDbProvider and add second level caching
This commit is contained in:
parent
509c6ec24c
commit
b836fe9685
|
@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
|
|||
{
|
||||
private readonly ILogger<OptimizeDatabaseTask> _logger;
|
||||
private readonly ILocalizationManager _localization;
|
||||
private readonly JellyfinDbProvider _provider;
|
||||
private readonly IDbContextFactory<JellyfinDb> _provider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OptimizeDatabaseTask" /> class.
|
||||
|
@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
|
|||
public OptimizeDatabaseTask(
|
||||
ILogger<OptimizeDatabaseTask> logger,
|
||||
ILocalizationManager localization,
|
||||
JellyfinDbProvider provider)
|
||||
IDbContextFactory<JellyfinDb> provider)
|
||||
{
|
||||
_logger = logger;
|
||||
_localization = localization;
|
||||
|
@ -70,17 +70,19 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
|
||||
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation("Optimizing and vacuuming jellyfin.db...");
|
||||
|
||||
try
|
||||
{
|
||||
using var context = _provider.CreateContext();
|
||||
var context = await _provider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
|
||||
await using (context.ConfigureAwait(false))
|
||||
{
|
||||
if (context.Database.IsSqlite())
|
||||
{
|
||||
context.Database.ExecuteSqlRaw("PRAGMA optimize");
|
||||
context.Database.ExecuteSqlRaw("VACUUM");
|
||||
await context.Database.ExecuteSqlRawAsync("PRAGMA optimize", cancellationToken).ConfigureAwait(false);
|
||||
await context.Database.ExecuteSqlRawAsync("VACUUM", cancellationToken).ConfigureAwait(false);
|
||||
_logger.LogInformation("jellyfin.db optimized successfully!");
|
||||
}
|
||||
else
|
||||
|
@ -88,12 +90,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
|
|||
_logger.LogInformation("This database doesn't support optimization");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error while optimizing jellyfin.db");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,13 +15,13 @@ namespace Jellyfin.Server.Implementations.Activity
|
|||
/// </summary>
|
||||
public class ActivityManager : IActivityManager
|
||||
{
|
||||
private readonly JellyfinDbProvider _provider;
|
||||
private readonly IDbContextFactory<JellyfinDb> _provider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActivityManager"/> class.
|
||||
/// </summary>
|
||||
/// <param name="provider">The Jellyfin database provider.</param>
|
||||
public ActivityManager(JellyfinDbProvider provider)
|
||||
public ActivityManager(IDbContextFactory<JellyfinDb> provider)
|
||||
{
|
||||
_provider = provider;
|
||||
}
|
||||
|
@ -32,10 +32,12 @@ namespace Jellyfin.Server.Implementations.Activity
|
|||
/// <inheritdoc/>
|
||||
public async Task CreateAsync(ActivityLog entry)
|
||||
{
|
||||
await using var dbContext = _provider.CreateContext();
|
||||
|
||||
var dbContext = await _provider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
dbContext.ActivityLogs.Add(entry);
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
EntryCreated?.Invoke(this, new GenericEventArgs<ActivityLogEntry>(ConvertToOldModel(entry)));
|
||||
}
|
||||
|
@ -43,10 +45,10 @@ namespace Jellyfin.Server.Implementations.Activity
|
|||
/// <inheritdoc/>
|
||||
public async Task<QueryResult<ActivityLogEntry>> GetPagedResultAsync(ActivityLogQuery query)
|
||||
{
|
||||
await using var dbContext = _provider.CreateContext();
|
||||
|
||||
var dbContext = await _provider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
IQueryable<ActivityLog> entries = dbContext.ActivityLogs
|
||||
.AsQueryable()
|
||||
.OrderByDescending(entry => entry.DateCreated);
|
||||
|
||||
if (query.MinDate.HasValue)
|
||||
|
@ -70,18 +72,21 @@ namespace Jellyfin.Server.Implementations.Activity
|
|||
.ToListAsync()
|
||||
.ConfigureAwait(false));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task CleanAsync(DateTime startDate)
|
||||
{
|
||||
await using var dbContext = _provider.CreateContext();
|
||||
var dbContext = await _provider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
var entries = dbContext.ActivityLogs
|
||||
.AsQueryable()
|
||||
.Where(entry => entry.DateCreated <= startDate);
|
||||
|
||||
dbContext.RemoveRange(entries);
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static ActivityLogEntry ConvertToOldModel(ActivityLog entry)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Data.Entities;
|
||||
|
@ -22,7 +23,7 @@ namespace Jellyfin.Server.Implementations.Devices
|
|||
/// </summary>
|
||||
public class DeviceManager : IDeviceManager
|
||||
{
|
||||
private readonly JellyfinDbProvider _dbProvider;
|
||||
private readonly IDbContextFactory<JellyfinDb> _dbProvider;
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly ConcurrentDictionary<string, ClientCapabilities> _capabilitiesMap = new();
|
||||
|
||||
|
@ -31,7 +32,7 @@ namespace Jellyfin.Server.Implementations.Devices
|
|||
/// </summary>
|
||||
/// <param name="dbProvider">The database provider.</param>
|
||||
/// <param name="userManager">The user manager.</param>
|
||||
public DeviceManager(JellyfinDbProvider dbProvider, IUserManager userManager)
|
||||
public DeviceManager(IDbContextFactory<JellyfinDb> dbProvider, IUserManager userManager)
|
||||
{
|
||||
_dbProvider = dbProvider;
|
||||
_userManager = userManager;
|
||||
|
@ -49,8 +50,11 @@ namespace Jellyfin.Server.Implementations.Devices
|
|||
/// <inheritdoc />
|
||||
public async Task UpdateDeviceOptions(string deviceId, string deviceName)
|
||||
{
|
||||
await using var dbContext = _dbProvider.CreateContext();
|
||||
var deviceOptions = await dbContext.DeviceOptions.AsQueryable().FirstOrDefaultAsync(dev => dev.DeviceId == deviceId).ConfigureAwait(false);
|
||||
DeviceOptions? deviceOptions;
|
||||
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
deviceOptions = await dbContext.DeviceOptions.AsQueryable().FirstOrDefaultAsync(dev => dev.DeviceId == deviceId).ConfigureAwait(false);
|
||||
if (deviceOptions == null)
|
||||
{
|
||||
deviceOptions = new DeviceOptions(deviceId);
|
||||
|
@ -59,6 +63,7 @@ namespace Jellyfin.Server.Implementations.Devices
|
|||
|
||||
deviceOptions.CustomName = deviceName;
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
DeviceOptionsUpdated?.Invoke(this, new GenericEventArgs<Tuple<string, DeviceOptions>>(new Tuple<string, DeviceOptions>(deviceId, deviceOptions)));
|
||||
}
|
||||
|
@ -66,22 +71,29 @@ namespace Jellyfin.Server.Implementations.Devices
|
|||
/// <inheritdoc />
|
||||
public async Task<Device> CreateDevice(Device device)
|
||||
{
|
||||
await using var dbContext = _dbProvider.CreateContext();
|
||||
|
||||
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
dbContext.Devices.Add(device);
|
||||
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<DeviceOptions> GetDeviceOptions(string deviceId)
|
||||
{
|
||||
await using var dbContext = _dbProvider.CreateContext();
|
||||
var deviceOptions = await dbContext.DeviceOptions
|
||||
.AsQueryable()
|
||||
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
DeviceOptions? deviceOptions;
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
deviceOptions = await dbContext.DeviceOptions
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(d => d.DeviceId == deviceId)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return deviceOptions ?? new DeviceOptions(deviceId);
|
||||
}
|
||||
|
@ -97,14 +109,17 @@ namespace Jellyfin.Server.Implementations.Devices
|
|||
/// <inheritdoc />
|
||||
public async Task<DeviceInfo?> GetDevice(string id)
|
||||
{
|
||||
await using var dbContext = _dbProvider.CreateContext();
|
||||
var device = await dbContext.Devices
|
||||
.AsQueryable()
|
||||
Device? device;
|
||||
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
device = await dbContext.Devices
|
||||
.Where(d => d.DeviceId == id)
|
||||
.OrderByDescending(d => d.DateLastActivity)
|
||||
.Include(d => d.User)
|
||||
.FirstOrDefaultAsync()
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var deviceInfo = device == null ? null : ToDeviceInfo(device);
|
||||
|
||||
|
@ -114,8 +129,9 @@ namespace Jellyfin.Server.Implementations.Devices
|
|||
/// <inheritdoc />
|
||||
public async Task<QueryResult<Device>> GetDevices(DeviceQuery query)
|
||||
{
|
||||
await using var dbContext = _dbProvider.CreateContext();
|
||||
|
||||
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
var devices = dbContext.Devices.AsQueryable();
|
||||
|
||||
if (query.UserId.HasValue)
|
||||
|
@ -145,10 +161,8 @@ namespace Jellyfin.Server.Implementations.Devices
|
|||
devices = devices.Take(query.Limit.Value);
|
||||
}
|
||||
|
||||
return new QueryResult<Device>(
|
||||
query.Skip,
|
||||
count,
|
||||
await devices.ToListAsync().ConfigureAwait(false));
|
||||
return new QueryResult<Device>(query.Skip, count, await devices.ToListAsync().ConfigureAwait(false));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -165,10 +179,12 @@ namespace Jellyfin.Server.Implementations.Devices
|
|||
/// <inheritdoc />
|
||||
public async Task<QueryResult<DeviceInfo>> GetDevicesForUser(Guid? userId, bool? supportsSync)
|
||||
{
|
||||
await using var dbContext = _dbProvider.CreateContext();
|
||||
var sessions = dbContext.Devices
|
||||
IAsyncEnumerable<Device> sessions;
|
||||
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
sessions = dbContext.Devices
|
||||
.Include(d => d.User)
|
||||
.AsQueryable()
|
||||
.OrderByDescending(d => d.DateLastActivity)
|
||||
.ThenBy(d => d.DeviceId)
|
||||
.AsAsyncEnumerable();
|
||||
|
@ -189,14 +205,18 @@ namespace Jellyfin.Server.Implementations.Devices
|
|||
|
||||
return new QueryResult<DeviceInfo>(array);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task DeleteDevice(Device device)
|
||||
{
|
||||
await using var dbContext = _dbProvider.CreateContext();
|
||||
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
dbContext.Devices.Remove(device);
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanAccessDevice(User user, string deviceId)
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using EFCoreSecondLevelCacheInterceptor;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Extensions for the <see cref="IServiceCollection"/> interface.
|
||||
/// </summary>
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the <see cref="IDbContextFactory{TContext}"/> interface to the service collection with second level caching enabled.
|
||||
/// </summary>
|
||||
/// <param name="serviceCollection">An instance of the <see cref="IServiceCollection"/> interface.</param>
|
||||
/// <returns>The updated service collection.</returns>
|
||||
public static IServiceCollection AddJellyfinDbContext(this IServiceCollection serviceCollection)
|
||||
{
|
||||
serviceCollection.AddEFSecondLevelCache(options =>
|
||||
options.UseMemoryCacheProvider()
|
||||
.CacheAllQueries(CacheExpirationMode.Sliding, TimeSpan.FromMinutes(10))
|
||||
.DisableLogging(true)
|
||||
.UseCacheKeyPrefix("EF_")
|
||||
.SkipCachingCommands(commandText =>
|
||||
commandText.Contains("NEWID()", StringComparison.InvariantCultureIgnoreCase))
|
||||
// Don't cache null values. Remove this optional setting if it's not necessary.
|
||||
.SkipCachingResults(result =>
|
||||
result.Value == null || (result.Value is EFTableRows rows && rows.RowsCount == 0)));
|
||||
|
||||
serviceCollection.AddPooledDbContextFactory<JellyfinDb>((serviceProvider, opt) =>
|
||||
{
|
||||
var applicationPaths = serviceProvider.GetRequiredService<IApplicationPaths>();
|
||||
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
|
||||
opt.UseSqlite($"Filename={Path.Combine(applicationPaths.DataPath, "jellyfin.db")}")
|
||||
.AddInterceptors(serviceProvider.GetRequiredService<SecondLevelCacheInterceptor>())
|
||||
.UseLoggerFactory(loggerFactory);
|
||||
});
|
||||
|
||||
return serviceCollection;
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EFCoreSecondLevelCacheInterceptor" Version="3.7.3" />
|
||||
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.9" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.9" />
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Jellyfin.Server.Implementations
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory class for generating new <see cref="JellyfinDb"/> instances.
|
||||
/// </summary>
|
||||
public class JellyfinDbProvider
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
private readonly ILogger<JellyfinDbProvider> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JellyfinDbProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="serviceProvider">The application's service provider.</param>
|
||||
/// <param name="appPaths">The application paths.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
public JellyfinDbProvider(IServiceProvider serviceProvider, IApplicationPaths appPaths, ILogger<JellyfinDbProvider> logger)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_appPaths = appPaths;
|
||||
_logger = logger;
|
||||
|
||||
using var jellyfinDb = CreateContext();
|
||||
if (jellyfinDb.Database.GetPendingMigrations().Any())
|
||||
{
|
||||
_logger.LogInformation("There are pending EFCore migrations in the database. Applying... (This may take a while, do not stop Jellyfin)");
|
||||
jellyfinDb.Database.Migrate();
|
||||
_logger.LogInformation("EFCore migrations applied successfully");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="JellyfinDb"/> context.
|
||||
/// </summary>
|
||||
/// <returns>The newly created context.</returns>
|
||||
public JellyfinDb CreateContext()
|
||||
{
|
||||
var contextOptions = new DbContextOptionsBuilder<JellyfinDb>().UseSqlite($"Filename={Path.Combine(_appPaths.DataPath, "jellyfin.db")}");
|
||||
return ActivatorUtilities.CreateInstance<JellyfinDb>(_serviceProvider, contextOptions.Options);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,13 +10,13 @@ namespace Jellyfin.Server.Implementations.Security
|
|||
/// <inheritdoc />
|
||||
public class AuthenticationManager : IAuthenticationManager
|
||||
{
|
||||
private readonly JellyfinDbProvider _dbProvider;
|
||||
private readonly IDbContextFactory<JellyfinDb> _dbProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AuthenticationManager"/> class.
|
||||
/// </summary>
|
||||
/// <param name="dbProvider">The database provider.</param>
|
||||
public AuthenticationManager(JellyfinDbProvider dbProvider)
|
||||
public AuthenticationManager(IDbContextFactory<JellyfinDb> dbProvider)
|
||||
{
|
||||
_dbProvider = dbProvider;
|
||||
}
|
||||
|
@ -24,18 +24,21 @@ namespace Jellyfin.Server.Implementations.Security
|
|||
/// <inheritdoc />
|
||||
public async Task CreateApiKey(string name)
|
||||
{
|
||||
await using var dbContext = _dbProvider.CreateContext();
|
||||
|
||||
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
dbContext.ApiKeys.Add(new ApiKey(name));
|
||||
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<AuthenticationInfo>> GetApiKeys()
|
||||
{
|
||||
await using var dbContext = _dbProvider.CreateContext();
|
||||
|
||||
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
return await dbContext.ApiKeys
|
||||
.AsAsyncEnumerable()
|
||||
.Select(key => new AuthenticationInfo
|
||||
|
@ -48,12 +51,14 @@ namespace Jellyfin.Server.Implementations.Security
|
|||
AppVersion = string.Empty
|
||||
}).ToListAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task DeleteApiKey(string accessToken)
|
||||
{
|
||||
await using var dbContext = _dbProvider.CreateContext();
|
||||
|
||||
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
var key = await dbContext.ApiKeys
|
||||
.AsQueryable()
|
||||
.Where(apiKey => apiKey.AccessToken == accessToken)
|
||||
|
@ -70,4 +75,5 @@ namespace Jellyfin.Server.Implementations.Security
|
|||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using EFCoreSecondLevelCacheInterceptor;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Net;
|
||||
|
@ -15,12 +16,12 @@ namespace Jellyfin.Server.Implementations.Security
|
|||
{
|
||||
public class AuthorizationContext : IAuthorizationContext
|
||||
{
|
||||
private readonly JellyfinDbProvider _jellyfinDbProvider;
|
||||
private readonly IDbContextFactory<JellyfinDb> _jellyfinDbProvider;
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly IServerApplicationHost _serverApplicationHost;
|
||||
|
||||
public AuthorizationContext(
|
||||
JellyfinDbProvider jellyfinDb,
|
||||
IDbContextFactory<JellyfinDb> jellyfinDb,
|
||||
IUserManager userManager,
|
||||
IServerApplicationHost serverApplicationHost)
|
||||
{
|
||||
|
@ -121,7 +122,9 @@ namespace Jellyfin.Server.Implementations.Security
|
|||
#pragma warning restore CA1508
|
||||
|
||||
authInfo.HasToken = true;
|
||||
await using var dbContext = _jellyfinDbProvider.CreateContext();
|
||||
var dbContext = await _jellyfinDbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
var device = await dbContext.Devices.FirstOrDefaultAsync(d => d.AccessToken == token).ConfigureAwait(false);
|
||||
|
||||
if (device != null)
|
||||
|
@ -212,6 +215,7 @@ namespace Jellyfin.Server.Implementations.Security
|
|||
|
||||
return authInfo;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the auth.
|
||||
|
|
|
@ -20,10 +20,10 @@ namespace Jellyfin.Server.Implementations.Users
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DisplayPreferencesManager"/> class.
|
||||
/// </summary>
|
||||
/// <param name="dbContext">The database context.</param>
|
||||
public DisplayPreferencesManager(JellyfinDb dbContext)
|
||||
/// <param name="dbContextFactory">The database context factory.</param>
|
||||
public DisplayPreferencesManager(IDbContextFactory<JellyfinDb> dbContextFactory)
|
||||
{
|
||||
_dbContext = dbContext;
|
||||
_dbContext = dbContextFactory.CreateDbContext();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace Jellyfin.Server.Implementations.Users
|
|||
/// </summary>
|
||||
public class UserManager : IUserManager
|
||||
{
|
||||
private readonly JellyfinDbProvider _dbProvider;
|
||||
private readonly IDbContextFactory<JellyfinDb> _dbProvider;
|
||||
private readonly IEventManager _eventManager;
|
||||
private readonly ICryptoProvider _cryptoProvider;
|
||||
private readonly INetworkManager _networkManager;
|
||||
|
@ -59,7 +59,7 @@ namespace Jellyfin.Server.Implementations.Users
|
|||
/// <param name="imageProcessor">The image processor.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
public UserManager(
|
||||
JellyfinDbProvider dbProvider,
|
||||
IDbContextFactory<JellyfinDb> dbProvider,
|
||||
IEventManager eventManager,
|
||||
ICryptoProvider cryptoProvider,
|
||||
INetworkManager networkManager,
|
||||
|
@ -83,7 +83,7 @@ namespace Jellyfin.Server.Implementations.Users
|
|||
_defaultPasswordResetProvider = _passwordResetProviders.OfType<DefaultPasswordResetProvider>().First();
|
||||
|
||||
_users = new ConcurrentDictionary<Guid, User>();
|
||||
using var dbContext = _dbProvider.CreateContext();
|
||||
using var dbContext = _dbProvider.CreateDbContext();
|
||||
foreach (var user in dbContext.Users
|
||||
.Include(user => user.Permissions)
|
||||
.Include(user => user.Preferences)
|
||||
|
@ -139,8 +139,9 @@ namespace Jellyfin.Server.Implementations.Users
|
|||
throw new ArgumentException("The new and old names must be different.");
|
||||
}
|
||||
|
||||
await using var dbContext = _dbProvider.CreateContext();
|
||||
|
||||
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
if (await dbContext.Users
|
||||
.AsQueryable()
|
||||
.AnyAsync(u => u.Username == newName && !u.Id.Equals(user.Id))
|
||||
|
@ -153,17 +154,20 @@ namespace Jellyfin.Server.Implementations.Users
|
|||
}
|
||||
|
||||
user.Username = newName;
|
||||
await UpdateUserAsync(user).ConfigureAwait(false);
|
||||
await UpdateUserInternalAsync(dbContext, user).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
OnUserUpdated?.Invoke(this, new GenericEventArgs<User>(user));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task UpdateUserAsync(User user)
|
||||
{
|
||||
await using var dbContext = _dbProvider.CreateContext();
|
||||
dbContext.Users.Update(user);
|
||||
_users[user.Id] = user;
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
await UpdateUserInternalAsync(dbContext, user).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<User> CreateUserInternalAsync(string name, JellyfinDb dbContext)
|
||||
|
@ -202,12 +206,15 @@ namespace Jellyfin.Server.Implementations.Users
|
|||
name));
|
||||
}
|
||||
|
||||
await using var dbContext = _dbProvider.CreateContext();
|
||||
|
||||
var newUser = await CreateUserInternalAsync(name, dbContext).ConfigureAwait(false);
|
||||
User newUser;
|
||||
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
newUser = await CreateUserInternalAsync(name, dbContext).ConfigureAwait(false);
|
||||
|
||||
dbContext.Users.Add(newUser);
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await _eventManager.PublishAsync(new UserCreatedEventArgs(newUser)).ConfigureAwait(false);
|
||||
|
||||
|
@ -241,9 +248,13 @@ namespace Jellyfin.Server.Implementations.Users
|
|||
nameof(userId));
|
||||
}
|
||||
|
||||
await using var dbContext = _dbProvider.CreateContext();
|
||||
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
dbContext.Users.Remove(user);
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
_users.Remove(userId);
|
||||
|
||||
await _eventManager.PublishAsync(new UserDeletedEventArgs(user)).ConfigureAwait(false);
|
||||
|
@ -288,7 +299,7 @@ namespace Jellyfin.Server.Implementations.Users
|
|||
user.EasyPassword = newPasswordSha1;
|
||||
await UpdateUserAsync(user).ConfigureAwait(false);
|
||||
|
||||
_eventManager.Publish(new UserPasswordChangedEventArgs(user));
|
||||
await _eventManager.PublishAsync(new UserPasswordChangedEventArgs(user)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
@ -541,7 +552,9 @@ namespace Jellyfin.Server.Implementations.Users
|
|||
|
||||
_logger.LogWarning("No users, creating one with username {UserName}", defaultName);
|
||||
|
||||
await using var dbContext = _dbProvider.CreateContext();
|
||||
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
var newUser = await CreateUserInternalAsync(defaultName, dbContext).ConfigureAwait(false);
|
||||
newUser.SetPermission(PermissionKind.IsAdministrator, true);
|
||||
newUser.SetPermission(PermissionKind.EnableContentDeletion, true);
|
||||
|
@ -550,6 +563,7 @@ namespace Jellyfin.Server.Implementations.Users
|
|||
dbContext.Users.Add(newUser);
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public NameIdPair[] GetAuthenticationProviders()
|
||||
|
@ -584,7 +598,9 @@ namespace Jellyfin.Server.Implementations.Users
|
|||
/// <inheritdoc/>
|
||||
public async Task UpdateConfigurationAsync(Guid userId, UserConfiguration config)
|
||||
{
|
||||
await using var dbContext = _dbProvider.CreateContext();
|
||||
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
var user = dbContext.Users
|
||||
.Include(u => u.Permissions)
|
||||
.Include(u => u.Preferences)
|
||||
|
@ -614,11 +630,14 @@ namespace Jellyfin.Server.Implementations.Users
|
|||
_users[user.Id] = user;
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task UpdatePolicyAsync(Guid userId, UserPolicy policy)
|
||||
{
|
||||
await using var dbContext = _dbProvider.CreateContext();
|
||||
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
var user = dbContext.Users
|
||||
.Include(u => u.Permissions)
|
||||
.Include(u => u.Preferences)
|
||||
|
@ -684,6 +703,7 @@ namespace Jellyfin.Server.Implementations.Users
|
|||
_users[user.Id] = user;
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task ClearProfileImageAsync(User user)
|
||||
|
@ -693,9 +713,13 @@ namespace Jellyfin.Server.Implementations.Users
|
|||
return;
|
||||
}
|
||||
|
||||
await using var dbContext = _dbProvider.CreateContext();
|
||||
var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (dbContext.ConfigureAwait(false))
|
||||
{
|
||||
dbContext.Remove(user.ProfileImage);
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
user.ProfileImage = null;
|
||||
_users[user.Id] = user;
|
||||
}
|
||||
|
@ -859,5 +883,12 @@ namespace Jellyfin.Server.Implementations.Users
|
|||
|
||||
await UpdateUserAsync(user).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task UpdateUserInternalAsync(JellyfinDb dbContext, User user)
|
||||
{
|
||||
dbContext.Users.Update(user);
|
||||
_users[user.Id] = user;
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Emby.Drawing;
|
||||
using Emby.Server.Implementations;
|
||||
|
@ -71,19 +70,13 @@ namespace Jellyfin.Server
|
|||
Logger.LogWarning("Skia not available. Will fallback to {ImageEncoder}.", nameof(NullImageEncoder));
|
||||
}
|
||||
|
||||
serviceCollection.AddDbContextPool<JellyfinDb>(
|
||||
options => options
|
||||
.UseLoggerFactory(LoggerFactory)
|
||||
.UseSqlite($"Filename={Path.Combine(ApplicationPaths.DataPath, "jellyfin.db")}"));
|
||||
|
||||
serviceCollection.AddEventServices();
|
||||
serviceCollection.AddSingleton<IBaseItemManager, BaseItemManager>();
|
||||
serviceCollection.AddSingleton<IEventManager, EventManager>();
|
||||
serviceCollection.AddSingleton<JellyfinDbProvider>();
|
||||
|
||||
serviceCollection.AddSingleton<IActivityManager, ActivityManager>();
|
||||
serviceCollection.AddSingleton<IUserManager, UserManager>();
|
||||
serviceCollection.AddSingleton<IDisplayPreferencesManager, DisplayPreferencesManager>();
|
||||
serviceCollection.AddScoped<IDisplayPreferencesManager, DisplayPreferencesManager>();
|
||||
serviceCollection.AddSingleton<IDeviceManager, DeviceManager>();
|
||||
|
||||
// TODO search the assemblies instead of adding them manually?
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Jellyfin.Server.Migrations.Routines
|
|||
private const string DbFilename = "activitylog.db";
|
||||
|
||||
private readonly ILogger<MigrateActivityLogDb> _logger;
|
||||
private readonly JellyfinDbProvider _provider;
|
||||
private readonly IDbContextFactory<JellyfinDb> _provider;
|
||||
private readonly IServerApplicationPaths _paths;
|
||||
|
||||
/// <summary>
|
||||
|
@ -28,7 +28,7 @@ namespace Jellyfin.Server.Migrations.Routines
|
|||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="paths">The server application paths.</param>
|
||||
/// <param name="provider">The database provider.</param>
|
||||
public MigrateActivityLogDb(ILogger<MigrateActivityLogDb> logger, IServerApplicationPaths paths, JellyfinDbProvider provider)
|
||||
public MigrateActivityLogDb(ILogger<MigrateActivityLogDb> logger, IServerApplicationPaths paths, IDbContextFactory<JellyfinDb> provider)
|
||||
{
|
||||
_logger = logger;
|
||||
_provider = provider;
|
||||
|
@ -68,7 +68,7 @@ namespace Jellyfin.Server.Migrations.Routines
|
|||
{
|
||||
using var userDbConnection = SQLite3.Open(Path.Combine(dataPath, "users.db"), ConnectionFlags.ReadOnly, null);
|
||||
_logger.LogWarning("Migrating the activity database may take a while, do not stop Jellyfin.");
|
||||
using var dbContext = _provider.CreateContext();
|
||||
using var dbContext = _provider.CreateDbContext();
|
||||
|
||||
var queryResult = connection.Query("SELECT * FROM ActivityLog ORDER BY Id");
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ using Jellyfin.Data.Entities.Security;
|
|||
using Jellyfin.Server.Implementations;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SQLitePCL.pretty;
|
||||
|
||||
|
@ -19,7 +20,7 @@ namespace Jellyfin.Server.Migrations.Routines
|
|||
private const string DbFilename = "authentication.db";
|
||||
|
||||
private readonly ILogger<MigrateAuthenticationDb> _logger;
|
||||
private readonly JellyfinDbProvider _dbProvider;
|
||||
private readonly IDbContextFactory<JellyfinDb> _dbProvider;
|
||||
private readonly IServerApplicationPaths _appPaths;
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
|
@ -32,7 +33,7 @@ namespace Jellyfin.Server.Migrations.Routines
|
|||
/// <param name="userManager">The user manager.</param>
|
||||
public MigrateAuthenticationDb(
|
||||
ILogger<MigrateAuthenticationDb> logger,
|
||||
JellyfinDbProvider dbProvider,
|
||||
IDbContextFactory<JellyfinDb> dbProvider,
|
||||
IServerApplicationPaths appPaths,
|
||||
IUserManager userManager)
|
||||
{
|
||||
|
@ -60,7 +61,7 @@ namespace Jellyfin.Server.Migrations.Routines
|
|||
ConnectionFlags.ReadOnly,
|
||||
null))
|
||||
{
|
||||
using var dbContext = _dbProvider.CreateContext();
|
||||
using var dbContext = _dbProvider.CreateDbContext();
|
||||
|
||||
var authenticatedDevices = connection.Query("SELECT * FROM Tokens");
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ using Jellyfin.Server.Implementations;
|
|||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SQLitePCL.pretty;
|
||||
|
||||
|
@ -24,7 +25,7 @@ namespace Jellyfin.Server.Migrations.Routines
|
|||
|
||||
private readonly ILogger<MigrateDisplayPreferencesDb> _logger;
|
||||
private readonly IServerApplicationPaths _paths;
|
||||
private readonly JellyfinDbProvider _provider;
|
||||
private readonly IDbContextFactory<JellyfinDb> _provider;
|
||||
private readonly JsonSerializerOptions _jsonOptions;
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
|
@ -38,7 +39,7 @@ namespace Jellyfin.Server.Migrations.Routines
|
|||
public MigrateDisplayPreferencesDb(
|
||||
ILogger<MigrateDisplayPreferencesDb> logger,
|
||||
IServerApplicationPaths paths,
|
||||
JellyfinDbProvider provider,
|
||||
IDbContextFactory<JellyfinDb> provider,
|
||||
IUserManager userManager)
|
||||
{
|
||||
_logger = logger;
|
||||
|
@ -84,7 +85,7 @@ namespace Jellyfin.Server.Migrations.Routines
|
|||
var dbFilePath = Path.Combine(_paths.DataPath, DbFilename);
|
||||
using (var connection = SQLite3.Open(dbFilePath, ConnectionFlags.ReadOnly, null))
|
||||
{
|
||||
using var dbContext = _provider.CreateContext();
|
||||
using var dbContext = _provider.CreateDbContext();
|
||||
|
||||
var results = connection.Query("SELECT * FROM userdisplaypreferences");
|
||||
foreach (var result in results)
|
||||
|
|
|
@ -11,6 +11,7 @@ using MediaBrowser.Controller.Entities;
|
|||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Model.Users;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SQLitePCL.pretty;
|
||||
using JsonSerializer = System.Text.Json.JsonSerializer;
|
||||
|
@ -26,7 +27,7 @@ namespace Jellyfin.Server.Migrations.Routines
|
|||
|
||||
private readonly ILogger<MigrateUserDb> _logger;
|
||||
private readonly IServerApplicationPaths _paths;
|
||||
private readonly JellyfinDbProvider _provider;
|
||||
private readonly IDbContextFactory<JellyfinDb> _provider;
|
||||
private readonly IXmlSerializer _xmlSerializer;
|
||||
|
||||
/// <summary>
|
||||
|
@ -39,7 +40,7 @@ namespace Jellyfin.Server.Migrations.Routines
|
|||
public MigrateUserDb(
|
||||
ILogger<MigrateUserDb> logger,
|
||||
IServerApplicationPaths paths,
|
||||
JellyfinDbProvider provider,
|
||||
IDbContextFactory<JellyfinDb> provider,
|
||||
IXmlSerializer xmlSerializer)
|
||||
{
|
||||
_logger = logger;
|
||||
|
@ -65,7 +66,7 @@ namespace Jellyfin.Server.Migrations.Routines
|
|||
|
||||
using (var connection = SQLite3.Open(Path.Combine(dataPath, DbFilename), ConnectionFlags.ReadOnly, null))
|
||||
{
|
||||
var dbContext = _provider.CreateContext();
|
||||
var dbContext = _provider.CreateDbContext();
|
||||
|
||||
var queryResult = connection.Query("SELECT * FROM LocalUsersv2");
|
||||
|
||||
|
|
|
@ -192,6 +192,17 @@ namespace Jellyfin.Server
|
|||
|
||||
// Re-use the web host service provider in the app host since ASP.NET doesn't allow a custom service collection.
|
||||
appHost.ServiceProvider = webHost.Services;
|
||||
var jellyfinDb = await appHost.ServiceProvider.GetRequiredService<IDbContextFactory<JellyfinDb>>().CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (jellyfinDb.ConfigureAwait(false))
|
||||
{
|
||||
if ((await jellyfinDb.Database.GetPendingMigrationsAsync().ConfigureAwait(false)).Any())
|
||||
{
|
||||
_logger.LogInformation("There are pending EFCore migrations in the database. Applying... (This may take a while, do not stop Jellyfin)");
|
||||
await jellyfinDb.Database.MigrateAsync().ConfigureAwait(false);
|
||||
_logger.LogInformation("EFCore migrations applied successfully");
|
||||
}
|
||||
}
|
||||
|
||||
await appHost.InitializeServices().ConfigureAwait(false);
|
||||
Migrations.MigrationRunner.Run(appHost, _loggerFactory);
|
||||
|
||||
|
@ -236,10 +247,13 @@ namespace Jellyfin.Server
|
|||
{
|
||||
_logger.LogInformation("Running query planner optimizations in the database... This might take a while");
|
||||
// Run before disposing the application
|
||||
using var context = appHost.Resolve<JellyfinDbProvider>().CreateContext();
|
||||
var context = await appHost.ServiceProvider.GetRequiredService<IDbContextFactory<JellyfinDb>>().CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (context.ConfigureAwait(false))
|
||||
{
|
||||
if (context.Database.IsSqlite())
|
||||
{
|
||||
context.Database.ExecuteSqlRaw("PRAGMA optimize");
|
||||
await context.Database.ExecuteSqlRawAsync("PRAGMA optimize").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ using Jellyfin.MediaEncoding.Hls.Extensions;
|
|||
using Jellyfin.Networking.Configuration;
|
||||
using Jellyfin.Server.Extensions;
|
||||
using Jellyfin.Server.Implementations;
|
||||
using Jellyfin.Server.Implementations.Extensions;
|
||||
using Jellyfin.Server.Infrastructure;
|
||||
using Jellyfin.Server.Middleware;
|
||||
using MediaBrowser.Common.Net;
|
||||
|
@ -65,7 +66,7 @@ namespace Jellyfin.Server
|
|||
// TODO remove once this is fixed upstream https://github.com/dotnet/aspnetcore/issues/34371
|
||||
services.AddSingleton<IActionResultExecutor<PhysicalFileResult>, SymlinkFollowingPhysicalFileResultExecutor>();
|
||||
services.AddJellyfinApi(_serverApplicationHost.GetApiPluginAssemblies(), _serverConfigurationManager.GetNetworkConfiguration());
|
||||
|
||||
services.AddJellyfinDbContext();
|
||||
services.AddJellyfinApiSwagger();
|
||||
|
||||
// configure custom legacy authentication
|
||||
|
|
Loading…
Reference in New Issue
Block a user