aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Common
diff options
context:
space:
mode:
authornyanmisaka <nst799610810@gmail.com>2020-11-08 19:11:54 +0800
committernyanmisaka <nst799610810@gmail.com>2020-11-08 19:11:54 +0800
commit737cb727f9f943f8bb55fca8a5c5023a98aca2d7 (patch)
tree8858a423545a03d0f7dfe5c1d1b0ae1157d62c5e /MediaBrowser.Common
parent05e78ee78c56364971956507f6239ded61f0af87 (diff)
parent96dcd9c87e2eb4b14004368856949e9fde2db261 (diff)
Merge remote-tracking branch 'upstream/master' into fonts
Diffstat (limited to 'MediaBrowser.Common')
-rw-r--r--MediaBrowser.Common/Configuration/IApplicationPaths.cs5
-rw-r--r--MediaBrowser.Common/Extensions/HttpContextExtensions.cs34
-rw-r--r--MediaBrowser.Common/IApplicationHost.cs10
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs53
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs28
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonDoubleConverter.cs56
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs40
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonInt64Converter.cs56
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverter.cs82
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverterFactory.cs59
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonNullableStructConverter.cs45
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonNullableStructConverterFactory.cs27
-rw-r--r--MediaBrowser.Common/Json/JsonDefaults.cs19
-rw-r--r--MediaBrowser.Common/MediaBrowser.Common.csproj17
-rw-r--r--MediaBrowser.Common/Net/CacheMode.cs11
-rw-r--r--MediaBrowser.Common/Net/CompressionMethods.cs15
-rw-r--r--MediaBrowser.Common/Net/DefaultHttpClientHandler.cs20
-rw-r--r--MediaBrowser.Common/Net/HttpRequestOptions.cs105
-rw-r--r--MediaBrowser.Common/Net/HttpResponseInfo.cs80
-rw-r--r--MediaBrowser.Common/Net/IHttpClient.cs53
-rw-r--r--MediaBrowser.Common/Net/NamedClient.cs18
-rw-r--r--MediaBrowser.Common/Plugins/BasePlugin.cs36
-rw-r--r--MediaBrowser.Common/Plugins/IPlugin.cs13
-rw-r--r--MediaBrowser.Common/Updates/IInstallationManager.cs27
-rw-r--r--MediaBrowser.Common/Updates/InstallationEventArgs.cs3
25 files changed, 303 insertions, 609 deletions
diff --git a/MediaBrowser.Common/Configuration/IApplicationPaths.cs b/MediaBrowser.Common/Configuration/IApplicationPaths.cs
index 4cea61682..57c654667 100644
--- a/MediaBrowser.Common/Configuration/IApplicationPaths.cs
+++ b/MediaBrowser.Common/Configuration/IApplicationPaths.cs
@@ -1,5 +1,3 @@
-using MediaBrowser.Model.Configuration;
-
namespace MediaBrowser.Common.Configuration
{
/// <summary>
@@ -17,8 +15,7 @@ namespace MediaBrowser.Common.Configuration
/// Gets the path to the web UI resources folder.
/// </summary>
/// <remarks>
- /// This value is not relevant if the server is configured to not host any static web content. Additionally,
- /// the value for <see cref="ServerConfiguration.DashboardSourcePath"/> takes precedence over this one.
+ /// This value is not relevant if the server is configured to not host any static web content.
/// </remarks>
string WebPath { get; }
diff --git a/MediaBrowser.Common/Extensions/HttpContextExtensions.cs b/MediaBrowser.Common/Extensions/HttpContextExtensions.cs
index d746207c7..19fa95480 100644
--- a/MediaBrowser.Common/Extensions/HttpContextExtensions.cs
+++ b/MediaBrowser.Common/Extensions/HttpContextExtensions.cs
@@ -1,4 +1,4 @@
-using MediaBrowser.Model.Services;
+using System.Net;
using Microsoft.AspNetCore.Http;
namespace MediaBrowser.Common.Extensions
@@ -8,26 +8,34 @@ namespace MediaBrowser.Common.Extensions
/// </summary>
public static class HttpContextExtensions
{
- private const string ServiceStackRequest = "ServiceStackRequest";
-
/// <summary>
- /// Set the ServiceStack request.
+ /// Checks the origin of the HTTP context.
/// </summary>
- /// <param name="httpContext">The HttpContext instance.</param>
- /// <param name="request">The service stack request instance.</param>
- public static void SetServiceStackRequest(this HttpContext httpContext, IRequest request)
+ /// <param name="context">The incoming HTTP context.</param>
+ /// <returns><c>true</c> if the request is coming from LAN, <c>false</c> otherwise.</returns>
+ public static bool IsLocal(this HttpContext context)
{
- httpContext.Items[ServiceStackRequest] = request;
+ return (context.Connection.LocalIpAddress == null
+ && context.Connection.RemoteIpAddress == null)
+ || context.Connection.LocalIpAddress.Equals(context.Connection.RemoteIpAddress);
}
/// <summary>
- /// Get the ServiceStack request.
+ /// Extracts the remote IP address of the caller of the HTTP context.
/// </summary>
- /// <param name="httpContext">The HttpContext instance.</param>
- /// <returns>The service stack request instance.</returns>
- public static IRequest GetServiceStackRequest(this HttpContext httpContext)
+ /// <param name="context">The HTTP context.</param>
+ /// <returns>The remote caller IP address.</returns>
+ public static string GetNormalizedRemoteIp(this HttpContext context)
{
- return (IRequest)httpContext.Items[ServiceStackRequest];
+ // Default to the loopback address if no RemoteIpAddress is specified (i.e. during integration tests)
+ var ip = context.Connection.RemoteIpAddress ?? IPAddress.Loopback;
+
+ if (ip.IsIPv4MappedToIPv6)
+ {
+ ip = ip.MapToIPv4();
+ }
+
+ return ip.ToString();
}
}
}
diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs
index e8d9282e4..849037ac4 100644
--- a/MediaBrowser.Common/IApplicationHost.cs
+++ b/MediaBrowser.Common/IApplicationHost.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Reflection;
using System.Threading.Tasks;
using MediaBrowser.Common.Plugins;
using Microsoft.Extensions.DependencyInjection;
@@ -77,6 +78,12 @@ namespace MediaBrowser.Common
IReadOnlyList<IPlugin> Plugins { get; }
/// <summary>
+ /// Gets all plugin assemblies which implement a custom rest api.
+ /// </summary>
+ /// <returns>An <see cref="IEnumerable{Assembly}"/> containing the plugin assemblies.</returns>
+ IEnumerable<Assembly> GetApiPluginAssemblies();
+
+ /// <summary>
/// Notifies the pending restart.
/// </summary>
void NotifyPendingRestart();
@@ -116,8 +123,7 @@ namespace MediaBrowser.Common
/// <summary>
/// Initializes this instance.
/// </summary>
- /// <param name="serviceCollection">The service collection.</param>
- void Init(IServiceCollection serviceCollection);
+ void Init();
/// <summary>
/// Creates the instance.
diff --git a/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs b/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs
new file mode 100644
index 000000000..bf7048c37
--- /dev/null
+++ b/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs
@@ -0,0 +1,53 @@
+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(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+ if (stringEntries == null || stringEntries.Length == 0)
+ {
+ return Array.Empty<T>();
+ }
+
+ var entries = new T[stringEntries.Length];
+ for (var i = 0; i < stringEntries.Length; i++)
+ {
+ entries[i] = (T)_typeConverter.ConvertFrom(stringEntries[i].Trim());
+ }
+
+ return entries;
+ }
+
+ return JsonSerializer.Deserialize<T[]>(ref reader, options);
+ }
+
+ /// <inheritdoc />
+ public override void Write(Utf8JsonWriter writer, T[] value, JsonSerializerOptions options)
+ {
+ JsonSerializer.Serialize(writer, value, options);
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs b/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs
new file mode 100644
index 000000000..24ed3ea19
--- /dev/null
+++ b/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs
@@ -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));
+ }
+ }
+}
diff --git a/MediaBrowser.Common/Json/Converters/JsonDoubleConverter.cs b/MediaBrowser.Common/Json/Converters/JsonDoubleConverter.cs
deleted file mode 100644
index 56c0ecbe9..000000000
--- a/MediaBrowser.Common/Json/Converters/JsonDoubleConverter.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using System;
-using System.Buffers;
-using System.Buffers.Text;
-using System.Globalization;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace MediaBrowser.Common.Json.Converters
-{
- /// <summary>
- /// Double to String JSON converter.
- /// Web client send quoted doubles.
- /// </summary>
- public class JsonDoubleConverter : JsonConverter<double>
- {
- /// <summary>
- /// Read JSON string as double.
- /// </summary>
- /// <param name="reader"><see cref="Utf8JsonReader"/>.</param>
- /// <param name="typeToConvert">Type.</param>
- /// <param name="options">Options.</param>
- /// <returns>Parsed value.</returns>
- public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- {
- if (reader.TokenType == JsonTokenType.String)
- {
- // try to parse number directly from bytes
- var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
- if (Utf8Parser.TryParse(span, out double number, out var bytesConsumed) && span.Length == bytesConsumed)
- {
- return number;
- }
-
- // try to parse from a string if the above failed, this covers cases with other escaped/UTF characters
- if (double.TryParse(reader.GetString(), out number))
- {
- return number;
- }
- }
-
- // fallback to default handling
- return reader.GetDouble();
- }
-
- /// <summary>
- /// Write double to JSON string.
- /// </summary>
- /// <param name="writer"><see cref="Utf8JsonWriter"/>.</param>
- /// <param name="value">Value to write.</param>
- /// <param name="options">Options.</param>
- public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options)
- {
- writer.WriteNumberValue(value);
- }
- }
-}
diff --git a/MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs b/MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs
deleted file mode 100644
index 70c375b8c..000000000
--- a/MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System;
-using System.Buffers;
-using System.Buffers.Text;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace MediaBrowser.Common.Json.Converters
-{
- /// <summary>
- /// Converts a GUID object or value to/from JSON.
- /// </summary>
- public class JsonInt32Converter : JsonConverter<int>
- {
- /// <inheritdoc />
- public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- {
- if (reader.TokenType == JsonTokenType.String)
- {
- ReadOnlySpan<byte> span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
- if (Utf8Parser.TryParse(span, out int number, out int bytesConsumed) && span.Length == bytesConsumed)
- {
- return number;
- }
-
- if (int.TryParse(reader.GetString(), out number))
- {
- return number;
- }
- }
-
- return reader.GetInt32();
- }
-
- /// <inheritdoc />
- public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
- {
- writer.WriteNumberValue(value);
- }
- }
-}
diff --git a/MediaBrowser.Common/Json/Converters/JsonInt64Converter.cs b/MediaBrowser.Common/Json/Converters/JsonInt64Converter.cs
deleted file mode 100644
index 427f1fa7e..000000000
--- a/MediaBrowser.Common/Json/Converters/JsonInt64Converter.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using System;
-using System.Buffers;
-using System.Buffers.Text;
-using System.Globalization;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace MediaBrowser.Common.Json.Converters
-{
- /// <summary>
- /// Parse JSON string as long.
- /// Javascript does not support 64-bit integers.
- /// </summary>
- public class JsonInt64Converter : JsonConverter<long>
- {
- /// <summary>
- /// Read JSON string as int64.
- /// </summary>
- /// <param name="reader"><see cref="Utf8JsonReader"/>.</param>
- /// <param name="type">Type.</param>
- /// <param name="options">Options.</param>
- /// <returns>Parsed value.</returns>
- public override long Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options)
- {
- if (reader.TokenType == JsonTokenType.String)
- {
- // try to parse number directly from bytes
- var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
- if (Utf8Parser.TryParse(span, out long number, out var bytesConsumed) && span.Length == bytesConsumed)
- {
- return number;
- }
-
- // try to parse from a string if the above failed, this covers cases with other escaped/UTF characters
- if (long.TryParse(reader.GetString(), out number))
- {
- return number;
- }
- }
-
- // fallback to default handling
- return reader.GetInt64();
- }
-
- /// <summary>
- /// Write long to JSON long.
- /// </summary>
- /// <param name="writer"><see cref="Utf8JsonWriter"/>.</param>
- /// <param name="value">Value to write.</param>
- /// <param name="options">Options.</param>
- public override void Write(Utf8JsonWriter writer, long value, JsonSerializerOptions options)
- {
- writer.WriteNumberValue(value);
- }
- }
-}
diff --git a/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverter.cs b/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverter.cs
deleted file mode 100644
index 8053461f0..000000000
--- a/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverter.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-#nullable enable
-
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Reflection;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace MediaBrowser.Common.Json.Converters
-{
- /// <summary>
- /// Converter for Dictionaries without string key.
- /// TODO This can be removed when System.Text.Json supports Dictionaries with non-string keys.
- /// </summary>
- /// <typeparam name="TKey">Type of key.</typeparam>
- /// <typeparam name="TValue">Type of value.</typeparam>
- internal sealed class JsonNonStringKeyDictionaryConverter<TKey, TValue> : JsonConverter<IDictionary<TKey, TValue>>
- {
- /// <summary>
- /// Read JSON.
- /// </summary>
- /// <param name="reader">The Utf8JsonReader.</param>
- /// <param name="typeToConvert">The type to convert.</param>
- /// <param name="options">The json serializer options.</param>
- /// <returns>Typed dictionary.</returns>
- /// <exception cref="NotSupportedException">Dictionary key type not supported.</exception>
- public override IDictionary<TKey, TValue> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- {
- var convertedType = typeof(Dictionary<,>).MakeGenericType(typeof(string), typeToConvert.GenericTypeArguments[1]);
- var value = JsonSerializer.Deserialize(ref reader, convertedType, options);
- var instance = (Dictionary<TKey, TValue>)Activator.CreateInstance(
- typeToConvert,
- BindingFlags.Instance | BindingFlags.Public,
- null,
- null,
- CultureInfo.CurrentCulture);
- var enumerator = (IEnumerator)convertedType.GetMethod("GetEnumerator")!.Invoke(value, null);
- var parse = typeof(TKey).GetMethod(
- "Parse",
- 0,
- BindingFlags.Public | BindingFlags.Static,
- null,
- CallingConventions.Any,
- new[] { typeof(string) },
- null);
- if (parse == null)
- {
- throw new NotSupportedException($"{typeof(TKey)} as TKey in IDictionary<TKey, TValue> is not supported.");
- }
-
- while (enumerator.MoveNext())
- {
- var element = (KeyValuePair<string?, TValue>)enumerator.Current;
- instance.Add((TKey)parse.Invoke(null, new[] { (object?)element.Key }), element.Value);
- }
-
- return instance;
- }
-
- /// <summary>
- /// Write dictionary as Json.
- /// </summary>
- /// <param name="writer">The Utf8JsonWriter.</param>
- /// <param name="value">The dictionary value.</param>
- /// <param name="options">The Json serializer options.</param>
- public override void Write(Utf8JsonWriter writer, IDictionary<TKey, TValue> value, JsonSerializerOptions options)
- {
- var convertedDictionary = new Dictionary<string?, TValue>(value.Count);
- foreach (var (k, v) in value)
- {
- if (k != null)
- {
- convertedDictionary[k.ToString()] = v;
- }
- }
-
- JsonSerializer.Serialize(writer, convertedDictionary, options);
- }
- }
-}
diff --git a/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverterFactory.cs b/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverterFactory.cs
deleted file mode 100644
index 52f360740..000000000
--- a/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverterFactory.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-#nullable enable
-
-using System;
-using System.Collections;
-using System.Globalization;
-using System.Reflection;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace MediaBrowser.Common.Json.Converters
-{
- /// <summary>
- /// https://github.com/dotnet/runtime/issues/30524#issuecomment-524619972.
- /// TODO This can be removed when System.Text.Json supports Dictionaries with non-string keys.
- /// </summary>
- internal sealed class JsonNonStringKeyDictionaryConverterFactory : JsonConverterFactory
- {
- /// <summary>
- /// Only convert objects that implement IDictionary and do not have string keys.
- /// </summary>
- /// <param name="typeToConvert">Type convert.</param>
- /// <returns>Conversion ability.</returns>
- public override bool CanConvert(Type typeToConvert)
- {
- if (!typeToConvert.IsGenericType)
- {
- return false;
- }
-
- // Let built in converter handle string keys
- if (typeToConvert.GenericTypeArguments[0] == typeof(string))
- {
- return false;
- }
-
- // Only support objects that implement IDictionary
- return typeToConvert.GetInterface(nameof(IDictionary)) != null;
- }
-
- /// <summary>
- /// Create converter for generic dictionary type.
- /// </summary>
- /// <param name="typeToConvert">Type to convert.</param>
- /// <param name="options">Json serializer options.</param>
- /// <returns>JsonConverter for given type.</returns>
- public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
- {
- var converterType = typeof(JsonNonStringKeyDictionaryConverter<,>)
- .MakeGenericType(typeToConvert.GenericTypeArguments[0], typeToConvert.GenericTypeArguments[1]);
- var converter = (JsonConverter)Activator.CreateInstance(
- converterType,
- BindingFlags.Instance | BindingFlags.Public,
- null,
- null,
- CultureInfo.CurrentCulture);
- return converter;
- }
- }
-}
diff --git a/MediaBrowser.Common/Json/Converters/JsonNullableStructConverter.cs b/MediaBrowser.Common/Json/Converters/JsonNullableStructConverter.cs
new file mode 100644
index 000000000..0501f7b2a
--- /dev/null
+++ b/MediaBrowser.Common/Json/Converters/JsonNullableStructConverter.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace MediaBrowser.Common.Json.Converters
+{
+ /// <summary>
+ /// Converts a nullable struct or value to/from JSON.
+ /// Required - some clients send an empty string.
+ /// </summary>
+ /// <typeparam name="TStruct">The struct type.</typeparam>
+ public class JsonNullableStructConverter<TStruct> : JsonConverter<TStruct?>
+ where TStruct : struct
+ {
+ /// <inheritdoc />
+ public override TStruct? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ 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 JsonSerializer.Deserialize<TStruct>(ref reader, options);
+ }
+
+ /// <inheritdoc />
+ public override void Write(Utf8JsonWriter writer, TStruct? value, JsonSerializerOptions options)
+ {
+ if (value.HasValue)
+ {
+ JsonSerializer.Serialize(writer, value.Value, options);
+ }
+ else
+ {
+ writer.WriteNullValue();
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Common/Json/Converters/JsonNullableStructConverterFactory.cs b/MediaBrowser.Common/Json/Converters/JsonNullableStructConverterFactory.cs
new file mode 100644
index 000000000..d5b54e3ca
--- /dev/null
+++ b/MediaBrowser.Common/Json/Converters/JsonNullableStructConverterFactory.cs
@@ -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));
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs
index 36ab6d900..6605ae962 100644
--- a/MediaBrowser.Common/Json/JsonDefaults.cs
+++ b/MediaBrowser.Common/Json/JsonDefaults.cs
@@ -10,6 +10,16 @@ namespace MediaBrowser.Common.Json
public static class JsonDefaults
{
/// <summary>
+ /// Pascal case json profile media type.
+ /// </summary>
+ public const string PascalCaseMediaType = "application/json; profile=\"PascalCase\"";
+
+ /// <summary>
+ /// Camel case json profile media type.
+ /// </summary>
+ public const string CamelCaseMediaType = "application/json; profile=\"CamelCase\"";
+
+ /// <summary>
/// Gets the default <see cref="JsonSerializerOptions" /> options.
/// </summary>
/// <remarks>
@@ -24,15 +34,14 @@ namespace MediaBrowser.Common.Json
var options = new JsonSerializerOptions
{
ReadCommentHandling = JsonCommentHandling.Disallow,
- WriteIndented = false
+ WriteIndented = false,
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+ NumberHandling = JsonNumberHandling.AllowReadingFromString
};
options.Converters.Add(new JsonGuidConverter());
- options.Converters.Add(new JsonInt32Converter());
options.Converters.Add(new JsonStringEnumConverter());
- options.Converters.Add(new JsonNonStringKeyDictionaryConverterFactory());
- options.Converters.Add(new JsonInt64Converter());
- options.Converters.Add(new JsonDoubleConverter());
+ options.Converters.Add(new JsonNullableStructConverterFactory());
return options;
}
diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj
index 7380f39fd..e716a6610 100644
--- a/MediaBrowser.Common/MediaBrowser.Common.csproj
+++ b/MediaBrowser.Common/MediaBrowser.Common.csproj
@@ -8,8 +8,9 @@
<PropertyGroup>
<Authors>Jellyfin Contributors</Authors>
<PackageId>Jellyfin.Common</PackageId>
- <PackageLicenseUrl>https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt</PackageLicenseUrl>
+ <VersionPrefix>10.7.0</VersionPrefix>
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
+ <PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
</PropertyGroup>
<ItemGroup>
@@ -17,8 +18,9 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.6" />
- <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.6" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.9" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.9" />
+ <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
<PackageReference Include="Microsoft.Net.Http.Headers" Version="2.2.8" />
</ItemGroup>
@@ -31,6 +33,15 @@
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <PublishRepositoryUrl>true</PublishRepositoryUrl>
+ <EmbedUntrackedSources>true</EmbedUntrackedSources>
+ <IncludeSymbols>true</IncludeSymbols>
+ <SymbolPackageFormat>snupkg</SymbolPackageFormat>
+ </PropertyGroup>
+
+ <PropertyGroup Condition=" '$(Stability)'=='Unstable'">
+ <!-- Include all symbols in the main nupkg until Azure Artifact Feed starts supporting ingesting NuGet symbol packages. -->
+ <AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
</PropertyGroup>
<!-- Code analyzers-->
diff --git a/MediaBrowser.Common/Net/CacheMode.cs b/MediaBrowser.Common/Net/CacheMode.cs
deleted file mode 100644
index 78fa3bf9b..000000000
--- a/MediaBrowser.Common/Net/CacheMode.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1602
-
-namespace MediaBrowser.Common.Net
-{
- public enum CacheMode
- {
- None = 0,
- Unconditional = 1
- }
-}
diff --git a/MediaBrowser.Common/Net/CompressionMethods.cs b/MediaBrowser.Common/Net/CompressionMethods.cs
deleted file mode 100644
index 39b72609f..000000000
--- a/MediaBrowser.Common/Net/CompressionMethods.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1602
-
-using System;
-
-namespace MediaBrowser.Common.Net
-{
- [Flags]
- public enum CompressionMethods
- {
- None = 0b00000001,
- Deflate = 0b00000010,
- Gzip = 0b00000100
- }
-}
diff --git a/MediaBrowser.Common/Net/DefaultHttpClientHandler.cs b/MediaBrowser.Common/Net/DefaultHttpClientHandler.cs
new file mode 100644
index 000000000..e189d6e70
--- /dev/null
+++ b/MediaBrowser.Common/Net/DefaultHttpClientHandler.cs
@@ -0,0 +1,20 @@
+using System.Net;
+using System.Net.Http;
+
+namespace MediaBrowser.Common.Net
+{
+ /// <summary>
+ /// Default http client handler.
+ /// </summary>
+ public class DefaultHttpClientHandler : HttpClientHandler
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DefaultHttpClientHandler"/> class.
+ /// </summary>
+ public DefaultHttpClientHandler()
+ {
+ // TODO change to DecompressionMethods.All with .NET5
+ AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
+ }
+ }
+}
diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs
deleted file mode 100644
index 347fc9833..000000000
--- a/MediaBrowser.Common/Net/HttpRequestOptions.cs
+++ /dev/null
@@ -1,105 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using Microsoft.Net.Http.Headers;
-
-namespace MediaBrowser.Common.Net
-{
- /// <summary>
- /// Class HttpRequestOptions.
- /// </summary>
- public class HttpRequestOptions
- {
- /// <summary>
- /// Initializes a new instance of the <see cref="HttpRequestOptions"/> class.
- /// </summary>
- public HttpRequestOptions()
- {
- RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
- CacheMode = CacheMode.None;
- DecompressionMethod = CompressionMethods.Deflate;
- }
-
- /// <summary>
- /// Gets or sets the URL.
- /// </summary>
- /// <value>The URL.</value>
- public string Url { get; set; }
-
- public CompressionMethods DecompressionMethod { get; set; }
-
- /// <summary>
- /// Gets or sets the accept header.
- /// </summary>
- /// <value>The accept header.</value>
- public string AcceptHeader
- {
- get => GetHeaderValue(HeaderNames.Accept);
- set => RequestHeaders[HeaderNames.Accept] = value;
- }
-
- /// <summary>
- /// Gets or sets the cancellation token.
- /// </summary>
- /// <value>The cancellation token.</value>
- public CancellationToken CancellationToken { get; set; }
-
- /// <summary>
- /// Gets or sets the user agent.
- /// </summary>
- /// <value>The user agent.</value>
- public string UserAgent
- {
- get => GetHeaderValue(HeaderNames.UserAgent);
- set => RequestHeaders[HeaderNames.UserAgent] = value;
- }
-
- /// <summary>
- /// Gets or sets the referrer.
- /// </summary>
- /// <value>The referrer.</value>
- public string Referer
- {
- get => GetHeaderValue(HeaderNames.Referer);
- set => RequestHeaders[HeaderNames.Referer] = value;
- }
-
- /// <summary>
- /// Gets or sets the host.
- /// </summary>
- /// <value>The host.</value>
- public string Host
- {
- get => GetHeaderValue(HeaderNames.Host);
- set => RequestHeaders[HeaderNames.Host] = value;
- }
-
- public Dictionary<string, string> RequestHeaders { get; private set; }
-
- public string RequestContentType { get; set; }
-
- public string RequestContent { get; set; }
-
- public bool BufferContent { get; set; }
-
- public bool LogErrorResponseBody { get; set; }
-
- public bool EnableKeepAlive { get; set; }
-
- public CacheMode CacheMode { get; set; }
-
- public TimeSpan CacheLength { get; set; }
-
- public bool EnableDefaultUserAgent { get; set; }
-
- private string GetHeaderValue(string name)
- {
- RequestHeaders.TryGetValue(name, out var value);
-
- return value;
- }
- }
-}
diff --git a/MediaBrowser.Common/Net/HttpResponseInfo.cs b/MediaBrowser.Common/Net/HttpResponseInfo.cs
deleted file mode 100644
index d4fee6c78..000000000
--- a/MediaBrowser.Common/Net/HttpResponseInfo.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-using System;
-using System.IO;
-using System.Net;
-using System.Net.Http.Headers;
-
-namespace MediaBrowser.Common.Net
-{
- /// <summary>
- /// Class HttpResponseInfo.
- /// </summary>
- public sealed class HttpResponseInfo : IDisposable
- {
-#pragma warning disable CS1591
- public HttpResponseInfo()
- {
- }
-
- public HttpResponseInfo(HttpResponseHeaders headers, HttpContentHeaders contentHeader)
- {
- Headers = headers;
- ContentHeaders = contentHeader;
- }
-
-#pragma warning restore CS1591
-
- /// <summary>
- /// Gets or sets the type of the content.
- /// </summary>
- /// <value>The type of the content.</value>
- public string ContentType { get; set; }
-
- /// <summary>
- /// Gets or sets the response URL.
- /// </summary>
- /// <value>The response URL.</value>
- public string ResponseUrl { get; set; }
-
- /// <summary>
- /// Gets or sets the content.
- /// </summary>
- /// <value>The content.</value>
- public Stream Content { get; set; }
-
- /// <summary>
- /// Gets or sets the status code.
- /// </summary>
- /// <value>The status code.</value>
- public HttpStatusCode StatusCode { get; set; }
-
- /// <summary>
- /// Gets or sets the temp file path.
- /// </summary>
- /// <value>The temp file path.</value>
- public string TempFilePath { get; set; }
-
- /// <summary>
- /// Gets or sets the length of the content.
- /// </summary>
- /// <value>The length of the content.</value>
- public long? ContentLength { get; set; }
-
- /// <summary>
- /// Gets or sets the headers.
- /// </summary>
- /// <value>The headers.</value>
- public HttpResponseHeaders Headers { get; set; }
-
- /// <summary>
- /// Gets or sets the content headers.
- /// </summary>
- /// <value>The content headers.</value>
- public HttpContentHeaders ContentHeaders { get; set; }
-
- /// <inheritdoc />
- public void Dispose()
- {
- // backwards compatibility
- }
- }
-}
diff --git a/MediaBrowser.Common/Net/IHttpClient.cs b/MediaBrowser.Common/Net/IHttpClient.cs
deleted file mode 100644
index 534e22edd..000000000
--- a/MediaBrowser.Common/Net/IHttpClient.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using System;
-using System.IO;
-using System.Net.Http;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Common.Net
-{
- /// <summary>
- /// Interface IHttpClient.
- /// </summary>
- public interface IHttpClient
- {
- /// <summary>
- /// Gets the response.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>Task{HttpResponseInfo}.</returns>
- Task<HttpResponseInfo> GetResponse(HttpRequestOptions options);
-
- /// <summary>
- /// Gets the specified options.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>Task{Stream}.</returns>
- Task<Stream> Get(HttpRequestOptions options);
-
- /// <summary>
- /// Warning: Deprecated function,
- /// use 'Task{HttpResponseInfo} SendAsync(HttpRequestOptions options, HttpMethod httpMethod);' instead
- /// Sends the asynchronous.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <param name="httpMethod">The HTTP method.</param>
- /// <returns>Task{HttpResponseInfo}.</returns>
- [Obsolete("Use 'Task{HttpResponseInfo} SendAsync(HttpRequestOptions options, HttpMethod httpMethod);' instead")]
- Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod);
-
- /// <summary>
- /// Sends the asynchronous.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <param name="httpMethod">The HTTP method.</param>
- /// <returns>Task{HttpResponseInfo}.</returns>
- Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, HttpMethod httpMethod);
-
- /// <summary>
- /// Posts the specified options.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>Task{HttpResponseInfo}.</returns>
- Task<HttpResponseInfo> Post(HttpRequestOptions options);
- }
-}
diff --git a/MediaBrowser.Common/Net/NamedClient.cs b/MediaBrowser.Common/Net/NamedClient.cs
new file mode 100644
index 000000000..0f6161c32
--- /dev/null
+++ b/MediaBrowser.Common/Net/NamedClient.cs
@@ -0,0 +1,18 @@
+namespace MediaBrowser.Common.Net
+{
+ /// <summary>
+ /// Registered http client names.
+ /// </summary>
+ public static class NamedClient
+ {
+ /// <summary>
+ /// Gets the value for the default named http client.
+ /// </summary>
+ public const string Default = nameof(Default);
+
+ /// <summary>
+ /// Gets the value for the MusicBrainz named http client.
+ /// </summary>
+ public const string MusicBrainz = nameof(MusicBrainz);
+ }
+}
diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs
index f10a1918f..8545fd5dc 100644
--- a/MediaBrowser.Common/Plugins/BasePlugin.cs
+++ b/MediaBrowser.Common/Plugins/BasePlugin.cs
@@ -3,9 +3,11 @@
using System;
using System.IO;
using System.Reflection;
+using System.Runtime.InteropServices;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Serialization;
+using Microsoft.Extensions.DependencyInjection;
namespace MediaBrowser.Common.Plugins
{
@@ -82,6 +84,16 @@ 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)
{
AssemblyFilePath = assemblyFilePath;
@@ -129,6 +141,30 @@ namespace MediaBrowser.Common.Plugins
{
ApplicationPaths = applicationPaths;
XmlSerializer = xmlSerializer;
+ if (this is IPluginAssembly assemblyPlugin)
+ {
+ var assembly = GetType().Assembly;
+ var assemblyName = assembly.GetName();
+ var assemblyFilePath = assembly.Location;
+
+ var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath));
+
+ assemblyPlugin.SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version);
+
+ var idAttributes = assembly.GetCustomAttributes(typeof(GuidAttribute), true);
+ if (idAttributes.Length > 0)
+ {
+ var attribute = (GuidAttribute)idAttributes[0];
+ var assemblyId = new Guid(attribute.Value);
+
+ assemblyPlugin.SetId(assemblyId);
+ }
+ }
+
+ if (this is IHasPluginConfiguration hasPluginConfiguration)
+ {
+ hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s));
+ }
}
/// <summary>
diff --git a/MediaBrowser.Common/Plugins/IPlugin.cs b/MediaBrowser.Common/Plugins/IPlugin.cs
index 7bd37d210..1844eb124 100644
--- a/MediaBrowser.Common/Plugins/IPlugin.cs
+++ b/MediaBrowser.Common/Plugins/IPlugin.cs
@@ -2,6 +2,7 @@
using System;
using MediaBrowser.Model.Plugins;
+using Microsoft.Extensions.DependencyInjection;
namespace MediaBrowser.Common.Plugins
{
@@ -61,6 +62,18 @@ 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
diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs
index 4b4030bc2..6aa16fea7 100644
--- a/MediaBrowser.Common/Updates/IInstallationManager.cs
+++ b/MediaBrowser.Common/Updates/IInstallationManager.cs
@@ -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>
@@ -73,12 +50,14 @@ namespace MediaBrowser.Common.Updates
/// <param name="name">The name.</param>
/// <param name="guid">The guid of the plugin.</param>
/// <param name="minVersion">The minimum required version of the plugin.</param>
+ /// <param name="specificVersion">The specific version of the plugin to install.</param>
/// <returns>All compatible versions ordered from newest to oldest.</returns>
IEnumerable<InstallationInfo> GetCompatibleVersions(
IEnumerable<PackageInfo> availablePackages,
string name = null,
Guid guid = default,
- Version minVersion = null);
+ Version minVersion = null,
+ Version specificVersion = null);
/// <summary>
/// Returns the available plugin updates.
diff --git a/MediaBrowser.Common/Updates/InstallationEventArgs.cs b/MediaBrowser.Common/Updates/InstallationEventArgs.cs
index 11eb2ad34..61178f631 100644
--- a/MediaBrowser.Common/Updates/InstallationEventArgs.cs
+++ b/MediaBrowser.Common/Updates/InstallationEventArgs.cs
@@ -1,10 +1,11 @@
#pragma warning disable CS1591
+using System;
using MediaBrowser.Model.Updates;
namespace MediaBrowser.Common.Updates
{
- public class InstallationEventArgs
+ public class InstallationEventArgs : EventArgs
{
public InstallationInfo InstallationInfo { get; set; }