mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-06-17 05:00:24 +01:00
Merge branch 'master' into PluginConfigSave
This commit is contained in:
@@ -46,6 +46,13 @@ namespace MediaBrowser.Common.Configuration
|
||||
/// <param name="newConfiguration">The new configuration.</param>
|
||||
void ReplaceConfiguration(BaseApplicationConfiguration newConfiguration);
|
||||
|
||||
/// <summary>
|
||||
/// Manually pre-loads a factory so that it is available pre system initialisation.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Class to register.</typeparam>
|
||||
void RegisterConfiguration<T>()
|
||||
where T : IConfigurationFactory;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configuration.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Json.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert comma delimited string to array of type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type to convert to.</typeparam>
|
||||
public class JsonCommaDelimitedArrayConverter<T> : JsonConverter<T[]>
|
||||
{
|
||||
private readonly TypeConverter _typeConverter;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JsonCommaDelimitedArrayConverter{T}"/> class.
|
||||
/// </summary>
|
||||
public JsonCommaDelimitedArrayConverter()
|
||||
{
|
||||
_typeConverter = TypeDescriptor.GetConverter(typeof(T));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override T[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.String)
|
||||
{
|
||||
var stringEntries = reader.GetString()?.Split(',', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (stringEntries == null || stringEntries.Length == 0)
|
||||
{
|
||||
return Array.Empty<T>();
|
||||
}
|
||||
|
||||
var parsedValues = new object[stringEntries.Length];
|
||||
var convertedCount = 0;
|
||||
for (var i = 0; i < stringEntries.Length; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
parsedValues[i] = _typeConverter.ConvertFrom(stringEntries[i].Trim());
|
||||
convertedCount++;
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
// TODO log when upgraded to .Net5
|
||||
// _logger.LogWarning(e, "Error converting value.");
|
||||
}
|
||||
}
|
||||
|
||||
var typedValues = new T[convertedCount];
|
||||
var typedValueIndex = 0;
|
||||
for (var i = 0; i < stringEntries.Length; i++)
|
||||
{
|
||||
if (parsedValues[i] != null)
|
||||
{
|
||||
typedValues.SetValue(parsedValues[i], typedValueIndex);
|
||||
typedValueIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
return typedValues;
|
||||
}
|
||||
|
||||
return JsonSerializer.Deserialize<T[]>(ref reader, options);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Write(Utf8JsonWriter writer, T[] value, JsonSerializerOptions options)
|
||||
{
|
||||
JsonSerializer.Serialize(writer, value, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Json.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Json comma delimited array converter factory.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This must be applied as an attribute, adding to the JsonConverter list causes stack overflow.
|
||||
/// </remarks>
|
||||
public class JsonCommaDelimitedArrayConverterFactory : JsonConverterFactory
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvert(Type typeToConvert)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
var structType = typeToConvert.GetElementType() ?? typeToConvert.GenericTypeArguments[0];
|
||||
return (JsonConverter)Activator.CreateInstance(typeof(JsonCommaDelimitedArrayConverter<>).MakeGenericType(structType));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,37 +8,38 @@ namespace MediaBrowser.Common.Json.Converters
|
||||
/// Converts a nullable struct or value to/from JSON.
|
||||
/// Required - some clients send an empty string.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The struct type.</typeparam>
|
||||
public class JsonNullableStructConverter<T> : JsonConverter<T?>
|
||||
where T : struct
|
||||
/// <typeparam name="TStruct">The struct type.</typeparam>
|
||||
public class JsonNullableStructConverter<TStruct> : JsonConverter<TStruct?>
|
||||
where TStruct : struct
|
||||
{
|
||||
private readonly JsonConverter<T?> _baseJsonConverter;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JsonNullableStructConverter{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="baseJsonConverter">The base json converter.</param>
|
||||
public JsonNullableStructConverter(JsonConverter<T?> baseJsonConverter)
|
||||
{
|
||||
_baseJsonConverter = baseJsonConverter;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
public override TStruct? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
// Handle empty string.
|
||||
if (reader.TokenType == JsonTokenType.Null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Token is empty string.
|
||||
if (reader.TokenType == JsonTokenType.String && ((reader.HasValueSequence && reader.ValueSequence.IsEmpty) || reader.ValueSpan.IsEmpty))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _baseJsonConverter.Read(ref reader, typeToConvert, options);
|
||||
return JsonSerializer.Deserialize<TStruct>(ref reader, options);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
|
||||
public override void Write(Utf8JsonWriter writer, TStruct? value, JsonSerializerOptions options)
|
||||
{
|
||||
_baseJsonConverter.Write(writer, value, options);
|
||||
if (value.HasValue)
|
||||
{
|
||||
JsonSerializer.Serialize(writer, value.Value, options);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteNullValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Json.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Json nullable struct converter factory.
|
||||
/// </summary>
|
||||
public class JsonNullableStructConverterFactory : JsonConverterFactory
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvert(Type typeToConvert)
|
||||
{
|
||||
return typeToConvert.IsGenericType
|
||||
&& typeToConvert.GetGenericTypeDefinition() == typeof(Nullable<>)
|
||||
&& typeToConvert.GenericTypeArguments[0].IsValueType;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
var structType = typeToConvert.GenericTypeArguments[0];
|
||||
return (JsonConverter)Activator.CreateInstance(typeof(JsonNullableStructConverter<>).MakeGenericType(structType));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,14 +39,9 @@ namespace MediaBrowser.Common.Json
|
||||
NumberHandling = JsonNumberHandling.AllowReadingFromString
|
||||
};
|
||||
|
||||
// Get built-in converters for fallback converting.
|
||||
var baseNullableInt32Converter = (JsonConverter<int?>)options.GetConverter(typeof(int?));
|
||||
var baseNullableInt64Converter = (JsonConverter<long?>)options.GetConverter(typeof(long?));
|
||||
|
||||
options.Converters.Add(new JsonGuidConverter());
|
||||
options.Converters.Add(new JsonStringEnumConverter());
|
||||
options.Converters.Add(new JsonNullableStructConverter<int>(baseNullableInt32Converter));
|
||||
options.Converters.Add(new JsonNullableStructConverter<long>(baseNullableInt64Converter));
|
||||
options.Converters.Add(new JsonNullableStructConverterFactory());
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
|
||||
<PropertyGroup>
|
||||
@@ -18,8 +18,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
|
||||
<PackageReference Include="Microsoft.Net.Http.Headers" Version="2.2.8" />
|
||||
</ItemGroup>
|
||||
@@ -29,7 +29,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
|
||||
@@ -13,8 +13,7 @@ namespace MediaBrowser.Common.Net
|
||||
/// </summary>
|
||||
public DefaultHttpClientHandler()
|
||||
{
|
||||
// TODO change to DecompressionMethods.All with .NET5
|
||||
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
|
||||
AutomaticDecompression = DecompressionMethods.All;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,16 +83,6 @@ namespace MediaBrowser.Common.Plugins
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void RegisterServices(IServiceCollection serviceCollection)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void UnregisterServices(IServiceCollection serviceCollection)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion)
|
||||
{
|
||||
@@ -185,6 +175,11 @@ namespace MediaBrowser.Common.Plugins
|
||||
/// <value>The type of the configuration.</value>
|
||||
public Type ConfigurationType => typeof(TConfigurationType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the event handler that is triggered when this configuration changes.
|
||||
/// </summary>
|
||||
public EventHandler<BasePluginConfiguration> ConfigurationChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name the assembly file.
|
||||
/// </summary>
|
||||
@@ -296,6 +291,8 @@ namespace MediaBrowser.Common.Plugins
|
||||
Configuration = (TConfigurationType)configuration;
|
||||
|
||||
SaveConfiguration(Configuration);
|
||||
|
||||
ConfigurationChanged?.Invoke(this, configuration);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -62,18 +62,6 @@ namespace MediaBrowser.Common.Plugins
|
||||
/// Called when just before the plugin is uninstalled from the server.
|
||||
/// </summary>
|
||||
void OnUninstalling();
|
||||
|
||||
/// <summary>
|
||||
/// Registers the plugin's services to the service collection.
|
||||
/// </summary>
|
||||
/// <param name="serviceCollection">The service collection.</param>
|
||||
void RegisterServices(IServiceCollection serviceCollection);
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters the plugin's services from the service collection.
|
||||
/// </summary>
|
||||
/// <param name="serviceCollection">The service collection.</param>
|
||||
void UnregisterServices(IServiceCollection serviceCollection);
|
||||
}
|
||||
|
||||
public interface IHasPluginConfiguration
|
||||
|
||||
19
MediaBrowser.Common/Plugins/IPluginServiceRegistrator.cs
Normal file
19
MediaBrowser.Common/Plugins/IPluginServiceRegistrator.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
namespace MediaBrowser.Common.Plugins
|
||||
{
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="IPluginServiceRegistrator" />.
|
||||
/// </summary>
|
||||
public interface IPluginServiceRegistrator
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers the plugin's services with the service collection.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This interface is only used for service registration and requires a parameterless constructor.
|
||||
/// </remarks>
|
||||
/// <param name="serviceCollection">The service collection.</param>
|
||||
void RegisterServices(IServiceCollection serviceCollection);
|
||||
}
|
||||
}
|
||||
113
MediaBrowser.Common/Plugins/LocalPlugin.cs
Normal file
113
MediaBrowser.Common/Plugins/LocalPlugin.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MediaBrowser.Common.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// Local plugin struct.
|
||||
/// </summary>
|
||||
public class LocalPlugin : IEquatable<LocalPlugin>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalPlugin"/> class.
|
||||
/// </summary>
|
||||
/// <param name="id">The plugin id.</param>
|
||||
/// <param name="name">The plugin name.</param>
|
||||
/// <param name="version">The plugin version.</param>
|
||||
/// <param name="path">The plugin path.</param>
|
||||
public LocalPlugin(Guid id, string name, Version version, string path)
|
||||
{
|
||||
Id = id;
|
||||
Name = name;
|
||||
Version = version;
|
||||
Path = path;
|
||||
DllFiles = new List<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin id.
|
||||
/// </summary>
|
||||
public Guid Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin name.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin version.
|
||||
/// </summary>
|
||||
public Version Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin path.
|
||||
/// </summary>
|
||||
public string Path { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of dll files for this plugin.
|
||||
/// </summary>
|
||||
public List<string> DllFiles { get; }
|
||||
|
||||
/// <summary>
|
||||
/// == operator.
|
||||
/// </summary>
|
||||
/// <param name="left">Left item.</param>
|
||||
/// <param name="right">Right item.</param>
|
||||
/// <returns>Comparison result.</returns>
|
||||
public static bool operator ==(LocalPlugin left, LocalPlugin right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// != operator.
|
||||
/// </summary>
|
||||
/// <param name="left">Left item.</param>
|
||||
/// <param name="right">Right item.</param>
|
||||
/// <returns>Comparison result.</returns>
|
||||
public static bool operator !=(LocalPlugin left, LocalPlugin right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two <see cref="LocalPlugin"/>.
|
||||
/// </summary>
|
||||
/// <param name="a">The first item.</param>
|
||||
/// <param name="b">The second item.</param>
|
||||
/// <returns>Comparison result.</returns>
|
||||
public static int Compare(LocalPlugin a, LocalPlugin b)
|
||||
{
|
||||
var compare = string.Compare(a.Name, b.Name, true, CultureInfo.InvariantCulture);
|
||||
|
||||
// Id is not equal but name is.
|
||||
if (a.Id != b.Id && compare == 0)
|
||||
{
|
||||
compare = a.Id.CompareTo(b.Id);
|
||||
}
|
||||
|
||||
return compare == 0 ? a.Version.CompareTo(b.Version) : compare;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is LocalPlugin other && this.Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Name.GetHashCode(StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(LocalPlugin other)
|
||||
{
|
||||
return Name.Equals(other.Name, StringComparison.OrdinalIgnoreCase)
|
||||
&& Id.Equals(other.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,29 +11,6 @@ namespace MediaBrowser.Common.Updates
|
||||
{
|
||||
public interface IInstallationManager : IDisposable
|
||||
{
|
||||
event EventHandler<InstallationInfo> PackageInstalling;
|
||||
|
||||
event EventHandler<InstallationInfo> PackageInstallationCompleted;
|
||||
|
||||
event EventHandler<InstallationFailedEventArgs> PackageInstallationFailed;
|
||||
|
||||
event EventHandler<InstallationInfo> PackageInstallationCancelled;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a plugin is uninstalled.
|
||||
/// </summary>
|
||||
event EventHandler<IPlugin> PluginUninstalled;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a plugin is updated.
|
||||
/// </summary>
|
||||
event EventHandler<InstallationInfo> PluginUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a plugin is installed.
|
||||
/// </summary>
|
||||
event EventHandler<InstallationInfo> PluginInstalled;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the completed installations.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user