mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-07 18:24:19 -04:00
Allow custom plugin provided database providers to be loaded (#14171)
This commit is contained in:
parent
d5672ce407
commit
916e897ed2
@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Jellyfin.Database.Implementations;
|
using Jellyfin.Database.Implementations;
|
||||||
using Jellyfin.Database.Implementations.DbConfiguration;
|
using Jellyfin.Database.Implementations.DbConfiguration;
|
||||||
@ -42,6 +44,28 @@ public static class ServiceCollectionExtensions
|
|||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static JellyfinDbProviderFactory? LoadDatabasePlugin(CustomDatabaseOptions customProviderOptions, IApplicationPaths applicationPaths)
|
||||||
|
{
|
||||||
|
var plugin = Directory.EnumerateDirectories(applicationPaths.PluginsPath)
|
||||||
|
.Where(e => Path.GetFileName(e)!.StartsWith(customProviderOptions.PluginName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
.Order()
|
||||||
|
.FirstOrDefault()
|
||||||
|
?? throw new InvalidOperationException($"The requested custom database plugin with the name '{customProviderOptions.PluginName}' could not been found in '{applicationPaths.PluginsPath}'");
|
||||||
|
|
||||||
|
var dbProviderAssembly = Path.Combine(plugin, Path.ChangeExtension(customProviderOptions.PluginAssembly, "dll"));
|
||||||
|
if (!File.Exists(dbProviderAssembly))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Could not find the requested assembly at '{dbProviderAssembly}'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have to load the assembly without proxy to ensure maximum performance for this.
|
||||||
|
var assembly = Assembly.LoadFrom(dbProviderAssembly);
|
||||||
|
var dbProviderType = assembly.GetExportedTypes().FirstOrDefault(f => f.IsAssignableTo(typeof(IJellyfinDatabaseProvider)))
|
||||||
|
?? throw new InvalidOperationException($"Could not find any type implementing the '{nameof(IJellyfinDatabaseProvider)}' interface.");
|
||||||
|
|
||||||
|
return (services) => (IJellyfinDatabaseProvider)ActivatorUtilities.CreateInstance(services, dbProviderType);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the <see cref="IDbContextFactory{TContext}"/> interface to the service collection with second level caching enabled.
|
/// Adds the <see cref="IDbContextFactory{TContext}"/> interface to the service collection with second level caching enabled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -55,7 +79,6 @@ public static class ServiceCollectionExtensions
|
|||||||
IConfiguration configuration)
|
IConfiguration configuration)
|
||||||
{
|
{
|
||||||
var efCoreConfiguration = configurationManager.GetConfiguration<DatabaseConfigurationOptions>("database");
|
var efCoreConfiguration = configurationManager.GetConfiguration<DatabaseConfigurationOptions>("database");
|
||||||
var providers = GetSupportedDbProviders();
|
|
||||||
JellyfinDbProviderFactory? providerFactory = null;
|
JellyfinDbProviderFactory? providerFactory = null;
|
||||||
|
|
||||||
if (efCoreConfiguration?.DatabaseType is null)
|
if (efCoreConfiguration?.DatabaseType is null)
|
||||||
@ -80,9 +103,22 @@ public static class ServiceCollectionExtensions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!providers.TryGetValue(efCoreConfiguration.DatabaseType.ToUpperInvariant(), out providerFactory!))
|
if (efCoreConfiguration.DatabaseType.Equals("PLUGIN_PROVIDER", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"Jellyfin cannot find the database provider of type '{efCoreConfiguration.DatabaseType}'. Supported types are {string.Join(", ", providers.Keys)}");
|
if (efCoreConfiguration.CustomProviderOptions is null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The custom database provider must declare the custom provider options to work");
|
||||||
|
}
|
||||||
|
|
||||||
|
providerFactory = LoadDatabasePlugin(efCoreConfiguration.CustomProviderOptions, configurationManager.ApplicationPaths);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var providers = GetSupportedDbProviders();
|
||||||
|
if (!providers.TryGetValue(efCoreConfiguration.DatabaseType.ToUpperInvariant(), out providerFactory!))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Jellyfin cannot find the database provider of type '{efCoreConfiguration.DatabaseType}'. Supported types are {string.Join(", ", providers.Keys)}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceCollection.AddSingleton<IJellyfinDatabaseProvider>(providerFactory!);
|
serviceCollection.AddSingleton<IJellyfinDatabaseProvider>(providerFactory!);
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Jellyfin.Database.Implementations.DbConfiguration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The custom value option for custom database providers.
|
||||||
|
/// </summary>
|
||||||
|
public class CustomDatabaseOption
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the key of the value.
|
||||||
|
/// </summary>
|
||||||
|
public required string Key { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the value.
|
||||||
|
/// </summary>
|
||||||
|
public required string Value { get; set; }
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
|
namespace Jellyfin.Database.Implementations.DbConfiguration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the options for a custom database connector.
|
||||||
|
/// </summary>
|
||||||
|
public class CustomDatabaseOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the Plugin name to search for database providers.
|
||||||
|
/// </summary>
|
||||||
|
public required string PluginName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the plugin assembly to search for providers.
|
||||||
|
/// </summary>
|
||||||
|
public required string PluginAssembly { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the connection string for the custom database provider.
|
||||||
|
/// </summary>
|
||||||
|
public required string ConnectionString { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of extra options for the custom provider.
|
||||||
|
/// </summary>
|
||||||
|
#pragma warning disable CA2227 // Collection properties should be read only
|
||||||
|
public Collection<CustomDatabaseOption> Options { get; set; } = [];
|
||||||
|
#pragma warning restore CA2227 // Collection properties should be read only
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Jellyfin.Database.Implementations.DbConfiguration;
|
namespace Jellyfin.Database.Implementations.DbConfiguration;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -10,6 +12,11 @@ public class DatabaseConfigurationOptions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public required string DatabaseType { get; set; }
|
public required string DatabaseType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the options required to use a custom database provider.
|
||||||
|
/// </summary>
|
||||||
|
public CustomDatabaseOptions? CustomProviderOptions { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or Sets the kind of locking behavior jellyfin should perform. Possible options are "NoLock", "Pessimistic", "Optimistic".
|
/// Gets or Sets the kind of locking behavior jellyfin should perform. Possible options are "NoLock", "Pessimistic", "Optimistic".
|
||||||
/// Defaults to "NoLock".
|
/// Defaults to "NoLock".
|
||||||
|
Loading…
x
Reference in New Issue
Block a user