repo creation with initial code after cloning public repo
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
namespace MediaBrowser.Common.Api;
|
||||
|
||||
/// <summary>
|
||||
/// Policies for the API authorization.
|
||||
/// </summary>
|
||||
public static class Policies
|
||||
{
|
||||
/// <summary>
|
||||
/// Policy name for requiring first time setup or elevated privileges.
|
||||
/// </summary>
|
||||
public const string FirstTimeSetupOrElevated = "FirstTimeSetupOrElevated";
|
||||
|
||||
/// <summary>
|
||||
/// Policy name for requiring elevated privileges.
|
||||
/// </summary>
|
||||
public const string RequiresElevation = "RequiresElevation";
|
||||
|
||||
/// <summary>
|
||||
/// Policy name for allowing local access only.
|
||||
/// </summary>
|
||||
public const string LocalAccessOnly = "LocalAccessOnly";
|
||||
|
||||
/// <summary>
|
||||
/// Policy name for escaping schedule controls.
|
||||
/// </summary>
|
||||
public const string IgnoreParentalControl = "IgnoreParentalControl";
|
||||
|
||||
/// <summary>
|
||||
/// Policy name for requiring download permission.
|
||||
/// </summary>
|
||||
public const string Download = "Download";
|
||||
|
||||
/// <summary>
|
||||
/// Policy name for requiring first time setup or default permissions.
|
||||
/// </summary>
|
||||
public const string FirstTimeSetupOrDefault = "FirstTimeSetupOrDefault";
|
||||
|
||||
/// <summary>
|
||||
/// Policy name for requiring local access or elevated privileges.
|
||||
/// </summary>
|
||||
public const string LocalAccessOrRequiresElevation = "LocalAccessOrRequiresElevation";
|
||||
|
||||
/// <summary>
|
||||
/// Policy name for requiring (anonymous) LAN access.
|
||||
/// </summary>
|
||||
public const string AnonymousLanAccessPolicy = "AnonymousLanAccessPolicy";
|
||||
|
||||
/// <summary>
|
||||
/// Policy name for escaping schedule controls or requiring first time setup.
|
||||
/// </summary>
|
||||
public const string FirstTimeSetupOrIgnoreParentalControl = "FirstTimeSetupOrIgnoreParentalControl";
|
||||
|
||||
/// <summary>
|
||||
/// Policy name for accessing SyncPlay.
|
||||
/// </summary>
|
||||
public const string SyncPlayHasAccess = "SyncPlayHasAccess";
|
||||
|
||||
/// <summary>
|
||||
/// Policy name for creating a SyncPlay group.
|
||||
/// </summary>
|
||||
public const string SyncPlayCreateGroup = "SyncPlayCreateGroup";
|
||||
|
||||
/// <summary>
|
||||
/// Policy name for joining a SyncPlay group.
|
||||
/// </summary>
|
||||
public const string SyncPlayJoinGroup = "SyncPlayJoinGroup";
|
||||
|
||||
/// <summary>
|
||||
/// Policy name for accessing a SyncPlay group.
|
||||
/// </summary>
|
||||
public const string SyncPlayIsInGroup = "SyncPlayIsInGroup";
|
||||
|
||||
/// <summary>
|
||||
/// Policy name for accessing collection management.
|
||||
/// </summary>
|
||||
public const string CollectionManagement = "CollectionManagement";
|
||||
|
||||
/// <summary>
|
||||
/// Policy name for accessing LiveTV.
|
||||
/// </summary>
|
||||
public const string LiveTvAccess = "LiveTvAccess";
|
||||
|
||||
/// <summary>
|
||||
/// Policy name for managing LiveTV.
|
||||
/// </summary>
|
||||
public const string LiveTvManagement = "LiveTvManagement";
|
||||
|
||||
/// <summary>
|
||||
/// Policy name for accessing subtitles management.
|
||||
/// </summary>
|
||||
public const string SubtitleManagement = "SubtitleManagement";
|
||||
|
||||
/// <summary>
|
||||
/// Policy name for accessing lyric management.
|
||||
/// </summary>
|
||||
public const string LyricManagement = "LyricManagement";
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Common.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes a single entry in the application configuration.
|
||||
/// </summary>
|
||||
public class ConfigurationStore
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the unique identifier for the configuration.
|
||||
/// </summary>
|
||||
public string Key { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type used to store the data for this configuration entry.
|
||||
/// </summary>
|
||||
public Type ConfigurationType { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Common.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="EventArgs" /> for the ConfigurationUpdated event.
|
||||
/// </summary>
|
||||
public class ConfigurationUpdateEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConfigurationUpdateEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="key">The configuration key.</param>
|
||||
/// <param name="newConfiguration">The new configuration.</param>
|
||||
public ConfigurationUpdateEventArgs(string key, object newConfiguration)
|
||||
{
|
||||
Key = key;
|
||||
NewConfiguration = newConfiguration;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the key.
|
||||
/// </summary>
|
||||
/// <value>The key.</value>
|
||||
public string Key { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the new configuration.
|
||||
/// </summary>
|
||||
/// <value>The new configuration.</value>
|
||||
public object NewConfiguration { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
|
||||
namespace MediaBrowser.Common.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Class containing extension methods for working with the encoding configuration.
|
||||
/// </summary>
|
||||
public static class EncodingConfigurationExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the encoding options.
|
||||
/// </summary>
|
||||
/// <param name="configurationManager">The configuration manager.</param>
|
||||
/// <returns>The encoding options.</returns>
|
||||
public static EncodingOptions GetEncodingOptions(this IConfigurationManager configurationManager)
|
||||
=> configurationManager.GetConfiguration<EncodingOptions>("encoding");
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the transcoding temp path from the encoding configuration, falling back to a default if no path
|
||||
/// is specified in configuration. If the directory does not exist, it will be created.
|
||||
/// </summary>
|
||||
/// <param name="configurationManager">The configuration manager.</param>
|
||||
/// <returns>The transcoding temp path.</returns>
|
||||
/// <exception cref="UnauthorizedAccessException">If the directory does not exist, and the caller does not have the required permission to create it.</exception>
|
||||
/// <exception cref="NotSupportedException">If there is a custom path transcoding path specified, but it is invalid.</exception>
|
||||
/// <exception cref="IOException">If the directory does not exist, and it also could not be created.</exception>
|
||||
public static string GetTranscodePath(this IConfigurationManager configurationManager)
|
||||
{
|
||||
// Get the configured path and fall back to a default
|
||||
var transcodingTempPath = configurationManager.GetEncodingOptions().TranscodingTempPath;
|
||||
if (string.IsNullOrEmpty(transcodingTempPath))
|
||||
{
|
||||
transcodingTempPath = Path.Combine(configurationManager.CommonApplicationPaths.CachePath, "transcodes");
|
||||
}
|
||||
|
||||
configurationManager.CommonApplicationPaths.CreateAndCheckMarker(transcodingTempPath, "transcode", true);
|
||||
return transcodingTempPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
namespace MediaBrowser.Common.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface IApplicationPaths.
|
||||
/// </summary>
|
||||
public interface IApplicationPaths
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the path to the program data folder.
|
||||
/// </summary>
|
||||
/// <value>The program data path.</value>
|
||||
string ProgramDataPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
string WebPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the program system folder.
|
||||
/// </summary>
|
||||
/// <value>The program data path.</value>
|
||||
string ProgramSystemPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the folder path to the data directory.
|
||||
/// </summary>
|
||||
/// <value>The data directory.</value>
|
||||
string DataPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the image cache path.
|
||||
/// </summary>
|
||||
/// <value>The image cache path.</value>
|
||||
string ImageCachePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the plugin directory.
|
||||
/// </summary>
|
||||
/// <value>The plugins path.</value>
|
||||
string PluginsPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the plugin configurations directory.
|
||||
/// </summary>
|
||||
/// <value>The plugin configurations path.</value>
|
||||
string PluginConfigurationsPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the log directory.
|
||||
/// </summary>
|
||||
/// <value>The log directory path.</value>
|
||||
string LogDirectoryPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the application configuration root directory.
|
||||
/// </summary>
|
||||
/// <value>The configuration directory path.</value>
|
||||
string ConfigurationDirectoryPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the system configuration file.
|
||||
/// </summary>
|
||||
/// <value>The system configuration file path.</value>
|
||||
string SystemConfigurationFilePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the folder path to the cache directory.
|
||||
/// </summary>
|
||||
/// <value>The cache directory.</value>
|
||||
string CachePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the folder path to the temp directory within the cache folder.
|
||||
/// </summary>
|
||||
/// <value>The temp directory.</value>
|
||||
string TempDirectory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the magic string used for virtual path manipulation.
|
||||
/// </summary>
|
||||
/// <value>The magic string used for virtual path manipulation.</value>
|
||||
string VirtualDataPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path used for storing trickplay files.
|
||||
/// </summary>
|
||||
/// <value>The trickplay path.</value>
|
||||
string TrickplayPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path used for storing backup archives.
|
||||
/// </summary>
|
||||
/// <value>The backup path.</value>
|
||||
string BackupPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Checks and creates all known base paths.
|
||||
/// </summary>
|
||||
void MakeSanityCheckOrThrow();
|
||||
|
||||
/// <summary>
|
||||
/// Checks and creates the given path and adds it with a marker file if non existent.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to check.</param>
|
||||
/// <param name="markerName">The common marker file name.</param>
|
||||
/// <param name="recursive">Check for other settings paths recursively.</param>
|
||||
void CreateAndCheckMarker(string path, string markerName, bool recursive = false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Common.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides an interface to retrieve a configuration store. Classes with this interface are scanned for at
|
||||
/// application start to dynamically register configuration for various modules/plugins.
|
||||
/// </summary>
|
||||
public interface IConfigurationFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the configuration store for this module.
|
||||
/// </summary>
|
||||
/// <returns>The configuration store.</returns>
|
||||
IEnumerable<ConfigurationStore> GetConfigurations();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
|
||||
namespace MediaBrowser.Common.Configuration
|
||||
{
|
||||
public interface IConfigurationManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Occurs when [configuration updating].
|
||||
/// </summary>
|
||||
event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdating;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [configuration updated].
|
||||
/// </summary>
|
||||
event EventHandler<EventArgs> ConfigurationUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [named configuration updated].
|
||||
/// </summary>
|
||||
event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the application paths.
|
||||
/// </summary>
|
||||
/// <value>The application paths.</value>
|
||||
IApplicationPaths CommonApplicationPaths { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configuration.
|
||||
/// </summary>
|
||||
/// <value>The configuration.</value>
|
||||
BaseApplicationConfiguration CommonConfiguration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Saves the configuration.
|
||||
/// </summary>
|
||||
void SaveConfiguration();
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the configuration.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
object GetConfiguration(string key);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the array of configuration stores.
|
||||
/// </summary>
|
||||
/// <returns>Array of ConfigurationStore.</returns>
|
||||
ConfigurationStore[] GetConfigurationStores();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the configuration.
|
||||
/// </summary>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <returns>Type.</returns>
|
||||
Type GetConfigurationType(string key);
|
||||
|
||||
/// <summary>
|
||||
/// Saves the configuration.
|
||||
/// </summary>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <param name="configuration">The configuration.</param>
|
||||
void SaveConfiguration(string key, object configuration);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the parts.
|
||||
/// </summary>
|
||||
/// <param name="factories">The factories.</param>
|
||||
void AddParts(IEnumerable<IConfigurationFactory> factories);
|
||||
}
|
||||
|
||||
public static class ConfigurationManagerExtensions
|
||||
{
|
||||
public static T GetConfiguration<T>(this IConfigurationManager manager, string key)
|
||||
{
|
||||
return (T)manager.GetConfiguration(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace MediaBrowser.Common.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// A configuration store that can be validated.
|
||||
/// </summary>
|
||||
public interface IValidatingConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Validation method to be invoked before saving the configuration.
|
||||
/// </summary>
|
||||
/// <param name="oldConfig">The old configuration.</param>
|
||||
/// <param name="newConfig">The new configuration.</param>
|
||||
void Validate(object oldConfig, object newConfig);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Common
|
||||
{
|
||||
public static class Crc32
|
||||
{
|
||||
private static readonly uint[] _crcTable =
|
||||
{
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
|
||||
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
||||
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
|
||||
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
|
||||
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
|
||||
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
|
||||
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
|
||||
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
|
||||
0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
|
||||
0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
|
||||
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
|
||||
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
|
||||
0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
|
||||
0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
|
||||
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
|
||||
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
|
||||
0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
|
||||
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
|
||||
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
|
||||
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
|
||||
0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
|
||||
0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
|
||||
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
|
||||
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
|
||||
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
|
||||
0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
|
||||
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
|
||||
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
|
||||
0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
|
||||
0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
|
||||
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
|
||||
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
|
||||
0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
|
||||
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
|
||||
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
|
||||
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
|
||||
0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
|
||||
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
|
||||
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
|
||||
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
|
||||
0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
|
||||
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
|
||||
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||
};
|
||||
|
||||
public static uint Compute(ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
var crc = 0xffffffff;
|
||||
var len = bytes.Length;
|
||||
for (var i = 0; i < len; i++)
|
||||
{
|
||||
crc = (crc >> 8) ^ _crcTable[(bytes[i] ^ crc) & 0xff];
|
||||
}
|
||||
|
||||
return ~crc;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Common.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Class EventHelper.
|
||||
/// </summary>
|
||||
// TODO: @bond Remove
|
||||
public static class EventHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Fires the event.
|
||||
/// </summary>
|
||||
/// <param name="handler">The handler.</param>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="args">The <see cref="EventArgs" /> instance containing the event data.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
public static void QueueEventIfNotNull(EventHandler? handler, object sender, EventArgs args, ILogger logger)
|
||||
{
|
||||
if (handler is not null)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
handler(sender, args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Error in event handler");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queues the event.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Argument type for the <c>handler</c>.</typeparam>
|
||||
/// <param name="handler">The handler.</param>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="args">The args.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
public static void QueueEventIfNotNull<T>(EventHandler<T>? handler, object sender, T args, ILogger logger)
|
||||
{
|
||||
if (handler is not null)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
handler(sender, args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Error in event handler");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace MediaBrowser.Common.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Class BaseExtensions.
|
||||
/// </summary>
|
||||
public static partial class BaseExtensions
|
||||
{
|
||||
// http://stackoverflow.com/questions/1349023/how-can-i-strip-html-from-text-in-net
|
||||
[GeneratedRegex(@"<(.|\n)*?>")]
|
||||
private static partial Regex StripHtmlRegex();
|
||||
|
||||
/// <summary>
|
||||
/// Strips the HTML.
|
||||
/// </summary>
|
||||
/// <param name="htmlString">The HTML string.</param>
|
||||
/// <returns><see cref="string" />.</returns>
|
||||
public static string StripHtml(this string htmlString)
|
||||
=> StripHtmlRegex().Replace(htmlString, string.Empty).Trim();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Md5.
|
||||
/// </summary>
|
||||
/// <param name="str">The string.</param>
|
||||
/// <returns><see cref="Guid" />.</returns>
|
||||
public static Guid GetMD5(this string str)
|
||||
{
|
||||
#pragma warning disable CA5351
|
||||
return new Guid(MD5.HashData(Encoding.Unicode.GetBytes(str)));
|
||||
#pragma warning restore CA5351
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace MediaBrowser.Common.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Static class containing extension methods for <see cref="HttpContext"/>.
|
||||
/// </summary>
|
||||
public static class HttpContextExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks the origin of the HTTP context.
|
||||
/// </summary>
|
||||
/// <param name="context">The incoming HTTP context.</param>
|
||||
/// <returns><c>true</c> if the request is coming from the same machine as is running the server, <c>false</c> otherwise.</returns>
|
||||
public static bool IsLocal(this HttpContext context)
|
||||
{
|
||||
return (context.Connection.LocalIpAddress is null
|
||||
&& context.Connection.RemoteIpAddress is null)
|
||||
|| Equals(context.Connection.LocalIpAddress, context.Connection.RemoteIpAddress);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the remote IP address of the caller of the HTTP context.
|
||||
/// </summary>
|
||||
/// <param name="context">The HTTP context.</param>
|
||||
/// <returns>The remote caller IP address.</returns>
|
||||
public static IPAddress GetNormalizedRemoteIP(this HttpContext context)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Common.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Class MethodNotAllowedException.
|
||||
/// </summary>
|
||||
public class MethodNotAllowedException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MethodNotAllowedException" /> class.
|
||||
/// </summary>
|
||||
public MethodNotAllowedException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MethodNotAllowedException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
public MethodNotAllowedException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="Process"/>.
|
||||
/// </summary>
|
||||
public static class ProcessExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously wait for the process to exit.
|
||||
/// </summary>
|
||||
/// <param name="process">The process to wait for.</param>
|
||||
/// <param name="timeout">The duration to wait before cancelling waiting for the task.</param>
|
||||
/// <returns>A task that will complete when the process has exited, cancellation has been requested, or an error occurs.</returns>
|
||||
/// <exception cref="OperationCanceledException">The timeout ended.</exception>
|
||||
public static async Task WaitForExitAsync(this Process process, TimeSpan timeout)
|
||||
{
|
||||
using (var cancelTokenSource = new CancellationTokenSource(timeout))
|
||||
{
|
||||
await process.WaitForExitAsync(cancelTokenSource.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Common.Extensions
|
||||
{
|
||||
public class RateLimitExceededException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RateLimitExceededException" /> class.
|
||||
/// </summary>
|
||||
public RateLimitExceededException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RateLimitExceededException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
public RateLimitExceededException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Common.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Class ResourceNotFoundException.
|
||||
/// </summary>
|
||||
public class ResourceNotFoundException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ResourceNotFoundException" /> class.
|
||||
/// </summary>
|
||||
public ResourceNotFoundException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ResourceNotFoundException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
public ResourceNotFoundException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents errors that occur during interaction with FFmpeg.
|
||||
/// </summary>
|
||||
public class FfmpegException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FfmpegException"/> class.
|
||||
/// </summary>
|
||||
public FfmpegException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FfmpegException"/> class with a specified error message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public FfmpegException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FfmpegException"/> class with a specified error message and a
|
||||
/// reference to the inner exception that is the cause of this exception.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">
|
||||
/// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if
|
||||
/// no inner exception is specified.
|
||||
/// </param>
|
||||
public FfmpegException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace MediaBrowser.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate used with GetExports{T}.
|
||||
/// </summary>
|
||||
/// <param name="type">Type to create.</param>
|
||||
/// <returns>New instance of type <param>type</param>.</returns>
|
||||
public delegate object? CreationDelegateFactory(Type type);
|
||||
|
||||
/// <summary>
|
||||
/// An interface to be implemented by the applications hosting a kernel.
|
||||
/// </summary>
|
||||
public interface IApplicationHost
|
||||
{
|
||||
/// <summary>
|
||||
/// Occurs when [has pending restart changed].
|
||||
/// </summary>
|
||||
event EventHandler? HasPendingRestartChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device identifier.
|
||||
/// </summary>
|
||||
/// <value>The device identifier.</value>
|
||||
string SystemId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance has pending changes requiring a restart.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance has a pending restart; otherwise, <c>false</c>.</value>
|
||||
bool HasPendingRestart { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the application should restart.
|
||||
/// </summary>
|
||||
bool ShouldRestart { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the application version.
|
||||
/// </summary>
|
||||
/// <value>The application version.</value>
|
||||
Version ApplicationVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the service provider.
|
||||
/// </summary>
|
||||
IServiceProvider? ServiceProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the application version.
|
||||
/// </summary>
|
||||
/// <value>The application version.</value>
|
||||
string ApplicationVersionString { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the application user agent.
|
||||
/// </summary>
|
||||
/// <value>The application user agent.</value>
|
||||
string ApplicationUserAgent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the email address for use within a comment section of a user agent field.
|
||||
/// Presently used to provide contact information to MusicBrainz service.
|
||||
/// </summary>
|
||||
string ApplicationUserAgentAddress { 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();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the exports.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type.</typeparam>
|
||||
/// <param name="manageLifetime">If set to <c>true</c> [manage lifetime].</param>
|
||||
/// <returns><see cref="IReadOnlyCollection{T}" />.</returns>
|
||||
IReadOnlyCollection<T> GetExports<T>(bool manageLifetime = true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the exports.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type.</typeparam>
|
||||
/// <param name="defaultFunc">Delegate function that gets called to create the object.</param>
|
||||
/// <param name="manageLifetime">If set to <c>true</c> [manage lifetime].</param>
|
||||
/// <returns><see cref="IReadOnlyCollection{T}" />.</returns>
|
||||
IReadOnlyCollection<T> GetExports<T>(CreationDelegateFactory defaultFunc, bool manageLifetime = true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the export types.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type.</typeparam>
|
||||
/// <returns>IEnumerable{Type}.</returns>
|
||||
IEnumerable<Type> GetExportTypes<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Resolves this instance.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The <c>Type</c>.</typeparam>
|
||||
/// <returns>``0.</returns>
|
||||
T Resolve<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this instance.
|
||||
/// </summary>
|
||||
/// <param name="serviceCollection">Instance of the <see cref="IServiceCollection"/> interface.</param>
|
||||
void Init(IServiceCollection serviceCollection);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Authors>Jellyfin Contributors</Authors>
|
||||
<PackageId>Jellyfin.Common</PackageId>
|
||||
<VersionPrefix>10.12.0</VersionPrefix>
|
||||
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
|
||||
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\SharedVersion.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<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 -->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="IDisposableAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>Jellyfin.Common.Tests</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using MediaBrowser.Model.Net;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace MediaBrowser.Common.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for the NetworkManager class.
|
||||
/// </summary>
|
||||
public interface INetworkManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Event triggered on network changes.
|
||||
/// </summary>
|
||||
event EventHandler NetworkChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether IPv4 is enabled.
|
||||
/// </summary>
|
||||
bool IsIPv4Enabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether IPv6 is enabled.
|
||||
/// </summary>
|
||||
bool IsIPv6Enabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the list of interfaces to use for Kestrel.
|
||||
/// </summary>
|
||||
/// <returns>A IReadOnlyList{IPData} object containing all the interfaces to bind.
|
||||
/// If all the interfaces are specified, and none are excluded, it returns zero items
|
||||
/// to represent any address.</returns>
|
||||
/// <param name="individualInterfaces">When false, return <see cref="IPAddress.Any"/> or <see cref="IPAddress.IPv6Any"/> for all interfaces.</param>
|
||||
IReadOnlyList<IPData> GetAllBindInterfaces(bool individualInterfaces = false);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list containing the loopback interfaces.
|
||||
/// </summary>
|
||||
/// <returns>IReadOnlyList{IPData}.</returns>
|
||||
IReadOnlyList<IPData> GetLoopbacks();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the bind address to use in system URLs. (Server Discovery, PlayTo, LiveTV, SystemInfo)
|
||||
/// If no bind addresses are specified, an internal interface address is selected.
|
||||
/// The priority of selection is as follows:-
|
||||
///
|
||||
/// The value contained in the startup parameter --published-server-url.
|
||||
///
|
||||
/// If the user specified custom subnet overrides, the correct subnet for the source address.
|
||||
///
|
||||
/// If the user specified bind interfaces to use:-
|
||||
/// The bind interface that contains the source subnet.
|
||||
/// The first bind interface specified that suits best first the source's endpoint. eg. external or internal.
|
||||
///
|
||||
/// If the source is from a public subnet address range and the user hasn't specified any bind addresses:-
|
||||
/// The first public interface that isn't a loopback and contains the source subnet.
|
||||
/// The first public interface that isn't a loopback.
|
||||
/// The first internal interface that isn't a loopback.
|
||||
///
|
||||
/// If the source is from a private subnet address range and the user hasn't specified any bind addresses:-
|
||||
/// The first private interface that contains the source subnet.
|
||||
/// The first private interface that isn't a loopback.
|
||||
///
|
||||
/// If no interfaces meet any of these criteria, then a loopback address is returned.
|
||||
///
|
||||
/// Interfaces that have been specifically excluded from binding are not used in any of the calculations.
|
||||
/// </summary>
|
||||
/// <param name="source">Source of the request.</param>
|
||||
/// <param name="port">Optional port returned, if it's part of an override.</param>
|
||||
/// <returns>IP address to use, or loopback address if all else fails.</returns>
|
||||
string GetBindAddress(HttpRequest source, out int? port);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the bind address to use in system URLs. (Server Discovery, PlayTo, LiveTV, SystemInfo)
|
||||
/// If no bind addresses are specified, an internal interface address is selected.
|
||||
/// </summary>
|
||||
/// <param name="source">IP address of the request.</param>
|
||||
/// <param name="port">Optional port returned, if it's part of an override.</param>
|
||||
/// <param name="skipOverrides">Optional boolean denoting if published server overrides should be ignored. Defaults to false.</param>
|
||||
/// <returns>IP address to use, or loopback address if all else fails.</returns>
|
||||
string GetBindAddress(IPAddress? source, out int? port, bool skipOverrides = false);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the bind address to use in system URLs. (Server Discovery, PlayTo, LiveTV, SystemInfo)
|
||||
/// If no bind addresses are specified, an internal interface address is selected.
|
||||
/// (See <see cref="GetBindAddress(IPAddress, out int?, bool)"/>.
|
||||
/// </summary>
|
||||
/// <param name="source">Source of the request.</param>
|
||||
/// <param name="port">Optional port returned, if it's part of an override.</param>
|
||||
/// <returns>IP address to use, or loopback address if all else fails.</returns>
|
||||
string GetBindAddress(string source, out int? port);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the address is part of the user defined LAN.
|
||||
/// </summary>
|
||||
/// <param name="address">IP to check.</param>
|
||||
/// <returns>True if endpoint is within the LAN range.</returns>
|
||||
bool IsInLocalNetwork(string address);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the address is part of the user defined LAN.
|
||||
/// </summary>
|
||||
/// <param name="address">IP to check.</param>
|
||||
/// <returns>True if endpoint is within the LAN range.</returns>
|
||||
bool IsInLocalNetwork(IPAddress address);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to convert the interface name to an IP address.
|
||||
/// eg. "eth1", or "enp3s5".
|
||||
/// </summary>
|
||||
/// <param name="intf">Interface name.</param>
|
||||
/// <param name="result">Resulting object's IP addresses, if successful.</param>
|
||||
/// <returns>Success of the operation.</returns>
|
||||
bool TryParseInterface(string intf, [NotNullWhen(true)] out IReadOnlyList<IPData>? result);
|
||||
|
||||
/// <summary>
|
||||
/// Returns all internal (LAN) bind interface addresses.
|
||||
/// </summary>
|
||||
/// <returns>An list of internal (LAN) interfaces addresses.</returns>
|
||||
IReadOnlyList<IPData> GetInternalBindAddresses();
|
||||
|
||||
/// <summary>
|
||||
/// Checks if <paramref name="remoteIP"/> has access to the server.
|
||||
/// </summary>
|
||||
/// <param name="remoteIP">IP address of the client.</param>
|
||||
/// <returns>The result of evaluating the access policy, <c>Allow</c> if it should be allowed.</returns>
|
||||
RemoteAccessPolicyResult ShouldAllowServerAccess(IPAddress remoteIP);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace MediaBrowser.Common.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Registered http client names.
|
||||
/// </summary>
|
||||
public static class NamedClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the value for the default named http client which implements happy eyeballs.
|
||||
/// </summary>
|
||||
public const string Default = nameof(Default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value for the MusicBrainz named http client.
|
||||
/// </summary>
|
||||
public const string MusicBrainz = nameof(MusicBrainz);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value for the DLNA named http client.
|
||||
/// </summary>
|
||||
public const string Dlna = nameof(Dlna);
|
||||
|
||||
/// <summary>
|
||||
/// Non happy eyeballs implementation.
|
||||
/// </summary>
|
||||
public const string DirectIp = nameof(DirectIp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
#pragma warning disable CA1819 // Properties should not return arrays
|
||||
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Common.Net;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="NetworkConfiguration" />.
|
||||
/// </summary>
|
||||
public class NetworkConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// The default value for <see cref="InternalHttpPort"/>.
|
||||
/// </summary>
|
||||
public const int DefaultHttpPort = 8096;
|
||||
|
||||
/// <summary>
|
||||
/// The default value for <see cref="PublicHttpsPort"/> and <see cref="InternalHttpsPort"/>.
|
||||
/// </summary>
|
||||
public const int DefaultHttpsPort = 8920;
|
||||
|
||||
private string _baseUrl = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value used to specify the URL prefix that your Jellyfin instance can be accessed at.
|
||||
/// </summary>
|
||||
public string BaseUrl
|
||||
{
|
||||
get => _baseUrl;
|
||||
set
|
||||
{
|
||||
// Normalize the start of the string
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
// If baseUrl is empty, set an empty prefix string
|
||||
_baseUrl = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
if (value[0] != '/')
|
||||
{
|
||||
// If baseUrl was not configured with a leading slash, append one for consistency
|
||||
value = "/" + value;
|
||||
}
|
||||
|
||||
// Normalize the end of the string
|
||||
if (value[^1] == '/')
|
||||
{
|
||||
// If baseUrl was configured with a trailing slash, remove it for consistency
|
||||
value = value.Remove(value.Length - 1);
|
||||
}
|
||||
|
||||
_baseUrl = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to use HTTPS.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In order for HTTPS to be used, in addition to setting this to true, valid values must also be
|
||||
/// provided for <see cref="CertificatePath"/> and <see cref="CertificatePassword"/>.
|
||||
/// </remarks>
|
||||
public bool EnableHttps { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the server should force connections over HTTPS.
|
||||
/// </summary>
|
||||
public bool RequireHttps { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the filesystem path of an X.509 certificate to use for SSL.
|
||||
/// </summary>
|
||||
public string CertificatePath { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password required to access the X.509 certificate data in the file specified by <see cref="CertificatePath"/>.
|
||||
/// </summary>
|
||||
public string CertificatePassword { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the internal HTTP server port.
|
||||
/// </summary>
|
||||
/// <value>The HTTP server port.</value>
|
||||
public int InternalHttpPort { get; set; } = DefaultHttpPort;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the internal HTTPS server port.
|
||||
/// </summary>
|
||||
/// <value>The HTTPS server port.</value>
|
||||
public int InternalHttpsPort { get; set; } = DefaultHttpsPort;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the public HTTP port.
|
||||
/// </summary>
|
||||
/// <value>The public HTTP port.</value>
|
||||
public int PublicHttpPort { get; set; } = DefaultHttpPort;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the public HTTPS port.
|
||||
/// </summary>
|
||||
/// <value>The public HTTPS port.</value>
|
||||
public int PublicHttpsPort { get; set; } = DefaultHttpsPort;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether Autodiscovery is enabled.
|
||||
/// </summary>
|
||||
public bool AutoDiscovery { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to enable automatic port forwarding.
|
||||
/// </summary>
|
||||
[Obsolete("No longer supported")]
|
||||
public bool EnableUPnP { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether IPv6 is enabled.
|
||||
/// </summary>
|
||||
public bool EnableIPv4 { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether IPv6 is enabled.
|
||||
/// </summary>
|
||||
public bool EnableIPv6 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether access from outside of the LAN is permitted.
|
||||
/// </summary>
|
||||
public bool EnableRemoteAccess { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the subnets that are deemed to make up the LAN.
|
||||
/// </summary>
|
||||
public string[] LocalNetworkSubnets { get; set; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the interface addresses which Jellyfin will bind to. If empty, all interfaces will be used.
|
||||
/// </summary>
|
||||
public string[] LocalNetworkAddresses { get; set; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the known proxies.
|
||||
/// </summary>
|
||||
public string[] KnownProxies { get; set; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether address names that match <see cref="VirtualInterfaceNames"/> should be ignored for the purposes of binding.
|
||||
/// </summary>
|
||||
public bool IgnoreVirtualInterfaces { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating the interface name prefixes that should be ignored. The list can be comma separated and values are case-insensitive. <seealso cref="IgnoreVirtualInterfaces"/>.
|
||||
/// </summary>
|
||||
public string[] VirtualInterfaceNames { get; set; } = new string[] { "veth" };
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the published server uri is based on information in HTTP requests.
|
||||
/// </summary>
|
||||
public bool EnablePublishedServerUriByRequest { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the PublishedServerUriBySubnet
|
||||
/// Gets or sets PublishedServerUri to advertise for specific subnets.
|
||||
/// </summary>
|
||||
public string[] PublishedServerUriBySubnet { get; set; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the filter for remote IP connectivity. Used in conjunction with <seealso cref="IsRemoteIPFilterBlacklist"/>.
|
||||
/// </summary>
|
||||
public string[] RemoteIPFilter { get; set; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether <seealso cref="RemoteIPFilter"/> contains a blacklist or a whitelist. Default is a whitelist.
|
||||
/// </summary>
|
||||
public bool IsRemoteIPFilterBlacklist { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
|
||||
namespace MediaBrowser.Common.Net;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="NetworkConfigurationExtensions" />.
|
||||
/// </summary>
|
||||
public static class NetworkConfigurationExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the network configuration.
|
||||
/// </summary>
|
||||
/// <param name="config">The <see cref="IConfigurationManager"/>.</param>
|
||||
/// <returns>The <see cref="NetworkConfiguration"/>.</returns>
|
||||
public static NetworkConfiguration GetNetworkConfiguration(this IConfigurationManager config)
|
||||
{
|
||||
return config.GetConfiguration<NetworkConfiguration>(NetworkConfigurationStore.StoreKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
|
||||
namespace MediaBrowser.Common.Net;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="NetworkConfigurationFactory" />.
|
||||
/// </summary>
|
||||
public class NetworkConfigurationFactory : IConfigurationFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// The GetConfigurations.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="IEnumerable{ConfigurationStore}"/>.</returns>
|
||||
public IEnumerable<ConfigurationStore> GetConfigurations()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new NetworkConfigurationStore()
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
|
||||
namespace MediaBrowser.Common.Net;
|
||||
|
||||
/// <summary>
|
||||
/// A configuration that stores network related settings.
|
||||
/// </summary>
|
||||
public class NetworkConfigurationStore : ConfigurationStore
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the configuration in the storage.
|
||||
/// </summary>
|
||||
public const string StoreKey = "network";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NetworkConfigurationStore"/> class.
|
||||
/// </summary>
|
||||
public NetworkConfigurationStore()
|
||||
{
|
||||
ConfigurationType = typeof(NetworkConfiguration);
|
||||
Key = StoreKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using System.Net;
|
||||
|
||||
namespace MediaBrowser.Common.Net;
|
||||
|
||||
/// <summary>
|
||||
/// Networking constants.
|
||||
/// </summary>
|
||||
public static class NetworkConstants
|
||||
{
|
||||
/// <summary>
|
||||
/// IPv4 mask bytes.
|
||||
/// </summary>
|
||||
public const int IPv4MaskBytes = 4;
|
||||
|
||||
/// <summary>
|
||||
/// IPv6 mask bytes.
|
||||
/// </summary>
|
||||
public const int IPv6MaskBytes = 16;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum IPv4 prefix size.
|
||||
/// </summary>
|
||||
public const int MinimumIPv4PrefixSize = 32;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum IPv6 prefix size.
|
||||
/// </summary>
|
||||
public const int MinimumIPv6PrefixSize = 128;
|
||||
|
||||
/// <summary>
|
||||
/// Whole IPv4 address space.
|
||||
/// </summary>
|
||||
public static readonly IPNetwork IPv4Any = new IPNetwork(IPAddress.Any, 0);
|
||||
|
||||
/// <summary>
|
||||
/// Whole IPv6 address space.
|
||||
/// </summary>
|
||||
public static readonly IPNetwork IPv6Any = new IPNetwork(IPAddress.IPv6Any, 0);
|
||||
|
||||
/// <summary>
|
||||
/// IPv4 Loopback as defined in RFC 5735.
|
||||
/// </summary>
|
||||
public static readonly IPNetwork IPv4RFC5735Loopback = new IPNetwork(IPAddress.Loopback, 8);
|
||||
|
||||
/// <summary>
|
||||
/// IPv4 private class A as defined in RFC 1918.
|
||||
/// </summary>
|
||||
public static readonly IPNetwork IPv4RFC1918PrivateClassA = new IPNetwork(IPAddress.Parse("10.0.0.0"), 8);
|
||||
|
||||
/// <summary>
|
||||
/// IPv4 private class B as defined in RFC 1918.
|
||||
/// </summary>
|
||||
public static readonly IPNetwork IPv4RFC1918PrivateClassB = new IPNetwork(IPAddress.Parse("172.16.0.0"), 12);
|
||||
|
||||
/// <summary>
|
||||
/// IPv4 private class C as defined in RFC 1918.
|
||||
/// </summary>
|
||||
public static readonly IPNetwork IPv4RFC1918PrivateClassC = new IPNetwork(IPAddress.Parse("192.168.0.0"), 16);
|
||||
|
||||
/// <summary>
|
||||
/// IPv4 Link-Local as defined in RFC 3927.
|
||||
/// </summary>
|
||||
public static readonly IPNetwork IPv4RFC3927LinkLocal = new IPNetwork(IPAddress.Parse("169.254.0.0"), 16);
|
||||
|
||||
/// <summary>
|
||||
/// IPv6 loopback as defined in RFC 4291.
|
||||
/// </summary>
|
||||
public static readonly IPNetwork IPv6RFC4291Loopback = new IPNetwork(IPAddress.IPv6Loopback, 128);
|
||||
|
||||
/// <summary>
|
||||
/// IPv6 site local as defined in RFC 4291.
|
||||
/// </summary>
|
||||
public static readonly IPNetwork IPv6RFC4291SiteLocal = new IPNetwork(IPAddress.Parse("fe80::"), 10);
|
||||
|
||||
/// <summary>
|
||||
/// IPv6 unique local as defined in RFC 4193.
|
||||
/// </summary>
|
||||
public static readonly IPNetwork IPv6RFC4193UniqueLocal = new IPNetwork(IPAddress.Parse("fc00::"), 7);
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text.RegularExpressions;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Model.Net;
|
||||
|
||||
namespace MediaBrowser.Common.Net;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="NetworkUtils" />.
|
||||
/// </summary>
|
||||
public static partial class NetworkUtils
|
||||
{
|
||||
// Use regular expression as CheckHostName isn't RFC5892 compliant.
|
||||
// Modified from gSkinner's expression at https://stackoverflow.com/questions/11809631/fully-qualified-domain-name-validation
|
||||
[GeneratedRegex(@"(?im)^(?!:\/\/)(?=.{1,255}$)((.{1,63}\.){0,127}(?![0-9]*$)[a-z0-9-]+\.?)(:(\d){1,5}){0,1}$", RegexOptions.IgnoreCase, "en-US")]
|
||||
private static partial Regex FqdnGeneratedRegex();
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the IPAddress contains an IP6 Local link address.
|
||||
/// </summary>
|
||||
/// <param name="address">IPAddress object to check.</param>
|
||||
/// <returns>True if it is a local link address.</returns>
|
||||
/// <remarks>
|
||||
/// See https://stackoverflow.com/questions/6459928/explain-the-instance-properties-of-system-net-ipaddress
|
||||
/// it appears that the IPAddress.IsIPv6LinkLocal is out of date.
|
||||
/// </remarks>
|
||||
public static bool IsIPv6LinkLocal(IPAddress address)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(address);
|
||||
|
||||
if (address.IsIPv4MappedToIPv6)
|
||||
{
|
||||
address = address.MapToIPv4();
|
||||
}
|
||||
|
||||
if (address.AddressFamily != AddressFamily.InterNetworkV6)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// GetAddressBytes
|
||||
Span<byte> octet = stackalloc byte[16];
|
||||
address.TryWriteBytes(octet, out _);
|
||||
uint word = (uint)(octet[0] << 8) + octet[1];
|
||||
|
||||
return word >= 0xfe80 && word <= 0xfebf; // fe80::/10 :Local link.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a subnet mask in CIDR notation to a dotted decimal string value. IPv4 only.
|
||||
/// </summary>
|
||||
/// <param name="cidr">Subnet mask in CIDR notation.</param>
|
||||
/// <param name="family">IPv4 or IPv6 family.</param>
|
||||
/// <returns>String value of the subnet mask in dotted decimal notation.</returns>
|
||||
public static IPAddress CidrToMask(byte cidr, AddressFamily family)
|
||||
{
|
||||
uint addr = 0xFFFFFFFF << ((family == AddressFamily.InterNetwork ? NetworkConstants.MinimumIPv4PrefixSize : NetworkConstants.MinimumIPv6PrefixSize) - cidr);
|
||||
addr = ((addr & 0xff000000) >> 24)
|
||||
| ((addr & 0x00ff0000) >> 8)
|
||||
| ((addr & 0x0000ff00) << 8)
|
||||
| ((addr & 0x000000ff) << 24);
|
||||
return new IPAddress(addr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a subnet mask in CIDR notation to a dotted decimal string value. IPv4 only.
|
||||
/// </summary>
|
||||
/// <param name="cidr">Subnet mask in CIDR notation.</param>
|
||||
/// <param name="family">IPv4 or IPv6 family.</param>
|
||||
/// <returns>String value of the subnet mask in dotted decimal notation.</returns>
|
||||
public static IPAddress CidrToMask(int cidr, AddressFamily family)
|
||||
{
|
||||
uint addr = 0xFFFFFFFF << ((family == AddressFamily.InterNetwork ? NetworkConstants.MinimumIPv4PrefixSize : NetworkConstants.MinimumIPv6PrefixSize) - cidr);
|
||||
addr = ((addr & 0xff000000) >> 24)
|
||||
| ((addr & 0x00ff0000) >> 8)
|
||||
| ((addr & 0x0000ff00) << 8)
|
||||
| ((addr & 0x000000ff) << 24);
|
||||
return new IPAddress(addr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a subnet mask to a CIDR. IPv4 only.
|
||||
/// https://stackoverflow.com/questions/36954345/get-cidr-from-netmask.
|
||||
/// </summary>
|
||||
/// <param name="mask">Subnet mask.</param>
|
||||
/// <returns>Byte CIDR representing the mask.</returns>
|
||||
public static byte MaskToCidr(IPAddress mask)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(mask);
|
||||
|
||||
byte cidrnet = 0;
|
||||
if (mask.Equals(IPAddress.Any))
|
||||
{
|
||||
return cidrnet;
|
||||
}
|
||||
|
||||
// GetAddressBytes
|
||||
Span<byte> bytes = stackalloc byte[mask.AddressFamily == AddressFamily.InterNetwork ? NetworkConstants.IPv4MaskBytes : NetworkConstants.IPv6MaskBytes];
|
||||
if (!mask.TryWriteBytes(bytes, out var bytesWritten))
|
||||
{
|
||||
Console.WriteLine("Unable to write address bytes, only {0} bytes written.", bytesWritten.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
var zeroed = false;
|
||||
for (var i = 0; i < bytes.Length; i++)
|
||||
{
|
||||
for (int v = bytes[i]; (v & 0xFF) != 0; v <<= 1)
|
||||
{
|
||||
if (zeroed)
|
||||
{
|
||||
// Invalid netmask.
|
||||
return (byte)~cidrnet;
|
||||
}
|
||||
|
||||
if ((v & 0x80) == 0)
|
||||
{
|
||||
zeroed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
cidrnet++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cidrnet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an IPAddress into a string.
|
||||
/// IPv6 addresses are returned in [ ], with their scope removed.
|
||||
/// </summary>
|
||||
/// <param name="address">Address to convert.</param>
|
||||
/// <returns>URI safe conversion of the address.</returns>
|
||||
public static string FormatIPString(IPAddress? address)
|
||||
{
|
||||
if (address is null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var str = address.ToString();
|
||||
if (address.AddressFamily == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
int i = str.IndexOf('%', StringComparison.Ordinal);
|
||||
if (i != -1)
|
||||
{
|
||||
str = str.Substring(0, i);
|
||||
}
|
||||
|
||||
return $"[{str}]";
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try parsing an array of strings into <see cref="IPNetwork"/> objects, respecting exclusions.
|
||||
/// Elements without a subnet mask will be represented as <see cref="IPNetwork"/> with a single IP.
|
||||
/// </summary>
|
||||
/// <param name="values">Input string array to be parsed.</param>
|
||||
/// <param name="result">Collection of <see cref="IPNetwork"/>.</param>
|
||||
/// <param name="negated">Boolean signaling if negated or not negated values should be parsed.</param>
|
||||
/// <returns><c>True</c> if parsing was successful.</returns>
|
||||
public static bool TryParseToSubnets(string[] values, [NotNullWhen(true)] out IReadOnlyList<IPData>? result, bool negated = false)
|
||||
{
|
||||
if (values is null || values.Length == 0)
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
List<IPData>? tmpResult = null;
|
||||
for (int a = 0; a < values.Length; a++)
|
||||
{
|
||||
if (TryParseToSubnet(values[a], out var innerResult, negated))
|
||||
{
|
||||
(tmpResult ??= new()).Add(innerResult);
|
||||
}
|
||||
}
|
||||
|
||||
result = tmpResult;
|
||||
return result is not null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try parsing a string into an <see cref="IPData"/>, respecting exclusions.
|
||||
/// Inputs without a subnet mask will be represented as <see cref="IPData"/> with a single IP.
|
||||
/// </summary>
|
||||
/// <param name="value">Input string to be parsed.</param>
|
||||
/// <param name="result">An <see cref="IPData"/>.</param>
|
||||
/// <param name="negated">Boolean signaling if negated or not negated values should be parsed.</param>
|
||||
/// <returns><c>True</c> if parsing was successful.</returns>
|
||||
public static bool TryParseToSubnet(ReadOnlySpan<char> value, [NotNullWhen(true)] out IPData? result, bool negated = false)
|
||||
{
|
||||
// If multiple IP addresses are in a comma-separated string, the individual addresses may contain leading and/or trailing whitespace
|
||||
value = value.Trim();
|
||||
|
||||
bool isAddressNegated = false;
|
||||
if (value.StartsWith('!'))
|
||||
{
|
||||
isAddressNegated = true;
|
||||
value = value[1..]; // Remove leading '!' character
|
||||
}
|
||||
|
||||
if (isAddressNegated != negated)
|
||||
{
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var index = value.IndexOf('/');
|
||||
if (index != -1)
|
||||
{
|
||||
if (IPAddress.TryParse(value[..index], out var address) && IPNetwork.TryParse(value, out var subnet))
|
||||
{
|
||||
result = new IPData(address, subnet);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (IPAddress.TryParse(value, out var address))
|
||||
{
|
||||
if (address.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
result = address.Equals(IPAddress.Any) ? new IPData(IPAddress.Any, NetworkConstants.IPv4Any) : new IPData(address, new IPNetwork(address, NetworkConstants.MinimumIPv4PrefixSize));
|
||||
return true;
|
||||
}
|
||||
else if (address.AddressFamily == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
result = address.Equals(IPAddress.IPv6Any) ? new IPData(IPAddress.IPv6Any, NetworkConstants.IPv6Any) : new IPData(address, new IPNetwork(address, NetworkConstants.MinimumIPv6PrefixSize));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to parse a host span.
|
||||
/// </summary>
|
||||
/// <param name="host">Host name to parse.</param>
|
||||
/// <param name="addresses">Object representing the span, if it has successfully been parsed.</param>
|
||||
/// <param name="isIPv4Enabled"><c>true</c> if IPv4 is enabled.</param>
|
||||
/// <param name="isIPv6Enabled"><c>true</c> if IPv6 is enabled.</param>
|
||||
/// <returns><c>true</c> if the parsing is successful, <c>false</c> if not.</returns>
|
||||
public static bool TryParseHost(ReadOnlySpan<char> host, [NotNullWhen(true)] out IPAddress[]? addresses, bool isIPv4Enabled = true, bool isIPv6Enabled = false)
|
||||
{
|
||||
host = host.Trim();
|
||||
if (host.IsEmpty)
|
||||
{
|
||||
addresses = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// See if it's an IPv6 with port address e.g. [::1] or [::1]:120.
|
||||
if (host[0] == '[')
|
||||
{
|
||||
int i = host.IndexOf(']');
|
||||
if (i != -1)
|
||||
{
|
||||
return TryParseHost(host[1..(i - 1)], out addresses);
|
||||
}
|
||||
|
||||
addresses = Array.Empty<IPAddress>();
|
||||
return false;
|
||||
}
|
||||
|
||||
var hosts = new List<string>();
|
||||
foreach (var splitSpan in host.Split(':'))
|
||||
{
|
||||
hosts.Add(splitSpan.ToString());
|
||||
}
|
||||
|
||||
if (hosts.Count <= 2)
|
||||
{
|
||||
var firstPart = hosts[0];
|
||||
|
||||
// Is hostname or hostname:port
|
||||
if (FqdnGeneratedRegex().IsMatch(firstPart))
|
||||
{
|
||||
try
|
||||
{
|
||||
// .NET automatically filters only supported returned addresses based on OS support.
|
||||
addresses = Dns.GetHostAddresses(firstPart);
|
||||
return true;
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
// Ignore socket errors, as the result value will just be an empty array.
|
||||
}
|
||||
}
|
||||
|
||||
// Is an IPv4 or IPv4:port
|
||||
if (IPAddress.TryParse(firstPart.AsSpan().LeftPart('/'), out var address))
|
||||
{
|
||||
if (((address.AddressFamily == AddressFamily.InterNetwork) && (!isIPv4Enabled && isIPv6Enabled))
|
||||
|| ((address.AddressFamily == AddressFamily.InterNetworkV6) && (isIPv4Enabled && !isIPv6Enabled)))
|
||||
{
|
||||
addresses = Array.Empty<IPAddress>();
|
||||
return false;
|
||||
}
|
||||
|
||||
addresses = new[] { address };
|
||||
|
||||
// Host name is an IPv4 address, so fake resolve.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (hosts.Count > 0 && hosts.Count <= 9) // 8 octets + port
|
||||
{
|
||||
if (IPAddress.TryParse(host.LeftPart('/'), out var address))
|
||||
{
|
||||
addresses = new[] { address };
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
addresses = Array.Empty<IPAddress>();
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the broadcast address for a <see cref="IPNetwork"/>.
|
||||
/// </summary>
|
||||
/// <param name="network">The <see cref="IPNetwork"/>.</param>
|
||||
/// <returns>The broadcast address.</returns>
|
||||
public static IPAddress GetBroadcastAddress(IPNetwork network)
|
||||
{
|
||||
var addressBytes = network.BaseAddress.GetAddressBytes();
|
||||
uint ipAddress = BitConverter.ToUInt32(addressBytes, 0);
|
||||
uint ipMaskV4 = BitConverter.ToUInt32(CidrToMask(network.PrefixLength, AddressFamily.InterNetwork).GetAddressBytes(), 0);
|
||||
uint broadCastIPAddress = ipAddress | ~ipMaskV4;
|
||||
|
||||
return new IPAddress(BitConverter.GetBytes(broadCastIPAddress));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a subnet contains an address. This method also handles IPv4 mapped to IPv6 addresses.
|
||||
/// </summary>
|
||||
/// <param name="network">The <see cref="IPNetwork"/>.</param>
|
||||
/// <param name="address">The <see cref="IPAddress"/>.</param>
|
||||
/// <returns>Whether the supplied IP is in the supplied network.</returns>
|
||||
public static bool SubnetContainsAddress(IPNetwork network, IPAddress address)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(address);
|
||||
|
||||
if (address.IsIPv4MappedToIPv6)
|
||||
{
|
||||
address = address.MapToIPv4();
|
||||
}
|
||||
|
||||
return network.Contains(address);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Common.Net;
|
||||
|
||||
/// <summary>
|
||||
/// Result of <see cref="INetworkManager.ShouldAllowServerAccess" />.
|
||||
/// </summary>
|
||||
public enum RemoteAccessPolicyResult
|
||||
{
|
||||
/// <summary>
|
||||
/// The connection should be allowed.
|
||||
/// </summary>
|
||||
Allow,
|
||||
|
||||
/// <summary>
|
||||
/// The connection should be rejected since it is not from a local IP and remote access is disabled.
|
||||
/// </summary>
|
||||
RejectDueToRemoteAccessDisabled,
|
||||
|
||||
/// <summary>
|
||||
/// The connection should be rejected since it is from a blocklisted IP.
|
||||
/// </summary>
|
||||
RejectDueToIPBlocklist,
|
||||
|
||||
/// <summary>
|
||||
/// The connection should be rejected since it is from a remote IP that is not in the allowlist.
|
||||
/// </summary>
|
||||
RejectDueToNotAllowlistedRemoteIP,
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using MediaBrowser.Model.Plugins;
|
||||
|
||||
namespace MediaBrowser.Common.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a common base class for all plugins.
|
||||
/// </summary>
|
||||
public abstract class BasePlugin : IPlugin, IPluginAssembly
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the plugin.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public abstract string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description.
|
||||
/// </summary>
|
||||
/// <value>The description.</value>
|
||||
public virtual string Description => string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique id.
|
||||
/// </summary>
|
||||
/// <value>The unique id.</value>
|
||||
public virtual Guid Id { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin version.
|
||||
/// </summary>
|
||||
/// <value>The version.</value>
|
||||
public Version Version { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the assembly file.
|
||||
/// </summary>
|
||||
/// <value>The assembly file path.</value>
|
||||
public string AssemblyFilePath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed.
|
||||
/// </summary>
|
||||
/// <value>The data folder path.</value>
|
||||
public string DataFolderPath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the plugin can be uninstalled.
|
||||
/// </summary>
|
||||
public bool CanUninstall => !Path.GetDirectoryName(AssemblyFilePath)
|
||||
.Equals(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), StringComparison.Ordinal);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin info.
|
||||
/// </summary>
|
||||
/// <returns>PluginInfo.</returns>
|
||||
public virtual PluginInfo GetPluginInfo()
|
||||
{
|
||||
var info = new PluginInfo(
|
||||
Name,
|
||||
Version,
|
||||
Description,
|
||||
Id,
|
||||
CanUninstall);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called just before the plugin is uninstalled from the server.
|
||||
/// </summary>
|
||||
public virtual void OnUninstalling()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion)
|
||||
{
|
||||
AssemblyFilePath = assemblyFilePath;
|
||||
DataFolderPath = dataFolderPath;
|
||||
Version = assemblyVersion;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetId(Guid assemblyId)
|
||||
{
|
||||
Id = assemblyId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
#nullable disable
|
||||
#pragma warning disable SA1649 // File name should match first type name
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Model.Plugins;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a common base class for all plugins.
|
||||
/// </summary>
|
||||
/// <typeparam name="TConfigurationType">The type of the T configuration type.</typeparam>
|
||||
public abstract class BasePlugin<TConfigurationType> : BasePlugin, IHasPluginConfiguration
|
||||
where TConfigurationType : BasePluginConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// The configuration sync lock.
|
||||
/// </summary>
|
||||
private readonly Lock _configurationSyncLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// The configuration save lock.
|
||||
/// </summary>
|
||||
private readonly Lock _configurationSaveLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// The configuration.
|
||||
/// </summary>
|
||||
private TConfigurationType _configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BasePlugin{TConfigurationType}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="applicationPaths">The application paths.</param>
|
||||
/// <param name="xmlSerializer">The XML serializer.</param>
|
||||
protected BasePlugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
|
||||
{
|
||||
ApplicationPaths = applicationPaths;
|
||||
XmlSerializer = xmlSerializer;
|
||||
|
||||
var assembly = GetType().Assembly;
|
||||
var assemblyName = assembly.GetName();
|
||||
var assemblyFilePath = assembly.Location;
|
||||
|
||||
var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath));
|
||||
if (Version is not null && !Directory.Exists(dataFolderPath))
|
||||
{
|
||||
// Try again with the version number appended to the folder name.
|
||||
dataFolderPath += "_" + Version;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
SetId(assemblyId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the application paths.
|
||||
/// </summary>
|
||||
/// <value>The application paths.</value>
|
||||
protected IApplicationPaths ApplicationPaths { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the XML serializer.
|
||||
/// </summary>
|
||||
/// <value>The XML serializer.</value>
|
||||
protected IXmlSerializer XmlSerializer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of configuration this plugin uses.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
/// <value>The name of the assembly file.</value>
|
||||
protected string AssemblyFileName => Path.GetFileName(AssemblyFilePath);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the plugin configuration.
|
||||
/// </summary>
|
||||
/// <value>The configuration.</value>
|
||||
public TConfigurationType Configuration
|
||||
{
|
||||
get
|
||||
{
|
||||
// Lazy load
|
||||
if (_configuration is null)
|
||||
{
|
||||
lock (_configurationSyncLock)
|
||||
{
|
||||
_configuration ??= LoadConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
return _configuration;
|
||||
}
|
||||
|
||||
protected set => _configuration = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the configuration file. Subclasses should override.
|
||||
/// </summary>
|
||||
/// <value>The name of the configuration file.</value>
|
||||
public virtual string ConfigurationFileName => Path.ChangeExtension(AssemblyFileName, ".xml");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path to the configuration file.
|
||||
/// </summary>
|
||||
/// <value>The configuration file path.</value>
|
||||
public string ConfigurationFilePath => Path.Combine(ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin configuration.
|
||||
/// </summary>
|
||||
/// <value>The configuration.</value>
|
||||
BasePluginConfiguration IHasPluginConfiguration.Configuration => Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Saves the current configuration to the file system.
|
||||
/// </summary>
|
||||
/// <param name="config">Configuration to save.</param>
|
||||
public virtual void SaveConfiguration(TConfigurationType config)
|
||||
{
|
||||
lock (_configurationSaveLock)
|
||||
{
|
||||
var folder = Path.GetDirectoryName(ConfigurationFilePath);
|
||||
Directory.CreateDirectory(folder);
|
||||
|
||||
XmlSerializer.SerializeToFile(config, ConfigurationFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the current configuration to the file system.
|
||||
/// </summary>
|
||||
public virtual void SaveConfiguration()
|
||||
{
|
||||
SaveConfiguration(Configuration);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void UpdateConfiguration(BasePluginConfiguration configuration)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(configuration);
|
||||
|
||||
Configuration = (TConfigurationType)configuration;
|
||||
|
||||
SaveConfiguration(Configuration);
|
||||
|
||||
ConfigurationChanged?.Invoke(this, configuration);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override PluginInfo GetPluginInfo()
|
||||
{
|
||||
var info = base.GetPluginInfo();
|
||||
|
||||
info.ConfigurationFileName = ConfigurationFileName;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
private TConfigurationType LoadConfiguration()
|
||||
{
|
||||
var path = ConfigurationFilePath;
|
||||
|
||||
try
|
||||
{
|
||||
return (TConfigurationType)XmlSerializer.DeserializeFromFile(typeof(TConfigurationType), path);
|
||||
}
|
||||
catch
|
||||
{
|
||||
var config = Activator.CreateInstance<TConfigurationType>();
|
||||
SaveConfiguration(config);
|
||||
return config;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using MediaBrowser.Model.Plugins;
|
||||
|
||||
namespace MediaBrowser.Common.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the <see cref="IHasPluginConfiguration" />.
|
||||
/// </summary>
|
||||
public interface IHasPluginConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the type of configuration this plugin uses.
|
||||
/// </summary>
|
||||
Type ConfigurationType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin's configuration.
|
||||
/// </summary>
|
||||
BasePluginConfiguration Configuration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Completely overwrites the current configuration with a new copy.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The configuration.</param>
|
||||
void UpdateConfiguration(BasePluginConfiguration configuration);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using MediaBrowser.Model.Plugins;
|
||||
|
||||
namespace MediaBrowser.Common.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the <see cref="IPlugin" />.
|
||||
/// </summary>
|
||||
public interface IPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the plugin.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Description.
|
||||
/// </summary>
|
||||
string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique id.
|
||||
/// </summary>
|
||||
Guid Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin version.
|
||||
/// </summary>
|
||||
Version Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the assembly file.
|
||||
/// </summary>
|
||||
string AssemblyFilePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the plugin can be uninstalled.
|
||||
/// </summary>
|
||||
bool CanUninstall { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed.
|
||||
/// </summary>
|
||||
string DataFolderPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="PluginInfo"/>.
|
||||
/// </summary>
|
||||
/// <returns>PluginInfo.</returns>
|
||||
PluginInfo GetPluginInfo();
|
||||
|
||||
/// <summary>
|
||||
/// Called when just before the plugin is uninstalled from the server.
|
||||
/// </summary>
|
||||
void OnUninstalling();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Common.Plugins
|
||||
{
|
||||
public interface IPluginAssembly
|
||||
{
|
||||
void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion);
|
||||
|
||||
void SetId(Guid assemblyId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Plugins;
|
||||
using MediaBrowser.Model.Updates;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace MediaBrowser.Common.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the <see cref="IPluginManager" />.
|
||||
/// </summary>
|
||||
public interface IPluginManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the Plugins.
|
||||
/// </summary>
|
||||
IReadOnlyList<LocalPlugin> Plugins { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates the plugins.
|
||||
/// </summary>
|
||||
void CreatePlugins();
|
||||
|
||||
/// <summary>
|
||||
/// Returns all the assemblies.
|
||||
/// </summary>
|
||||
/// <returns>An IEnumerable{Assembly}.</returns>
|
||||
IEnumerable<Assembly> LoadAssemblies();
|
||||
|
||||
/// <summary>
|
||||
/// Registers the plugin's services with the DI.
|
||||
/// Note: DI is not yet instantiated yet.
|
||||
/// </summary>
|
||||
/// <param name="serviceCollection">A <see cref="ServiceCollection"/> instance.</param>
|
||||
void RegisterServices(IServiceCollection serviceCollection);
|
||||
|
||||
/// <summary>
|
||||
/// Saves the manifest back to disk.
|
||||
/// </summary>
|
||||
/// <param name="manifest">The <see cref="PluginManifest"/> to save.</param>
|
||||
/// <param name="path">The path where to save the manifest.</param>
|
||||
/// <returns>True if successful.</returns>
|
||||
bool SaveManifest(PluginManifest manifest, string path);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a manifest from repository data.
|
||||
/// </summary>
|
||||
/// <param name="packageInfo">The <see cref="PackageInfo"/> used to generate a manifest.</param>
|
||||
/// <param name="version">Version to be installed.</param>
|
||||
/// <param name="path">The path where to save the manifest.</param>
|
||||
/// <param name="status">Initial status of the plugin.</param>
|
||||
/// <returns>True if successful.</returns>
|
||||
Task<bool> PopulateManifest(PackageInfo packageInfo, Version version, string path, PluginStatus status);
|
||||
|
||||
/// <summary>
|
||||
/// Imports plugin details from a folder.
|
||||
/// </summary>
|
||||
/// <param name="folder">Folder of the plugin.</param>
|
||||
void ImportPluginFrom(string folder);
|
||||
|
||||
/// <summary>
|
||||
/// Disable the plugin.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The <see cref="Assembly"/> of the plug to disable.</param>
|
||||
void FailPlugin(Assembly assembly);
|
||||
|
||||
/// <summary>
|
||||
/// Disable the plugin.
|
||||
/// </summary>
|
||||
/// <param name="plugin">The <see cref="LocalPlugin"/> of the plug to disable.</param>
|
||||
void DisablePlugin(LocalPlugin plugin);
|
||||
|
||||
/// <summary>
|
||||
/// Enables the plugin, disabling all other versions.
|
||||
/// </summary>
|
||||
/// <param name="plugin">The <see cref="LocalPlugin"/> of the plug to disable.</param>
|
||||
void EnablePlugin(LocalPlugin plugin);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to find the plugin with and id of <paramref name="id"/>.
|
||||
/// </summary>
|
||||
/// <param name="id">Id of plugin.</param>
|
||||
/// <param name="version">The version of the plugin to locate.</param>
|
||||
/// <returns>A <see cref="LocalPlugin"/> if located, or null if not.</returns>
|
||||
LocalPlugin? GetPlugin(Guid id, Version? version = null);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the plugin.
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin.</param>
|
||||
/// <returns>Outcome of the operation.</returns>
|
||||
bool RemovePlugin(LocalPlugin plugin);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Model.Plugins;
|
||||
|
||||
namespace MediaBrowser.Common.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// Local plugin class.
|
||||
/// </summary>
|
||||
public class LocalPlugin : IEquatable<LocalPlugin>
|
||||
{
|
||||
private readonly bool _supported;
|
||||
private Version? _version;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalPlugin"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The plugin path.</param>
|
||||
/// <param name="isSupported"><b>True</b> if Jellyfin supports this version of the plugin.</param>
|
||||
/// <param name="manifest">The manifest record for this plugin, or null if one does not exist.</param>
|
||||
public LocalPlugin(string path, bool isSupported, PluginManifest manifest)
|
||||
{
|
||||
Path = path;
|
||||
DllFiles = Array.Empty<string>();
|
||||
_supported = isSupported;
|
||||
Manifest = manifest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin id.
|
||||
/// </summary>
|
||||
public Guid Id => Manifest.Id;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin name.
|
||||
/// </summary>
|
||||
public string Name => Manifest.Name;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin version.
|
||||
/// </summary>
|
||||
public Version Version
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_version is null)
|
||||
{
|
||||
_version = Version.Parse(Manifest.Version);
|
||||
}
|
||||
|
||||
return _version;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin path.
|
||||
/// </summary>
|
||||
public string Path { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of dll files for this plugin.
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> DllFiles { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the instance of this plugin.
|
||||
/// </summary>
|
||||
public IPlugin? Instance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether Jellyfin supports this version of the plugin, and it's enabled.
|
||||
/// </summary>
|
||||
public bool IsEnabledAndSupported => _supported && Manifest.Status >= PluginStatus.Active;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the plugin has a manifest.
|
||||
/// </summary>
|
||||
public PluginManifest Manifest { get; }
|
||||
|
||||
/// <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)
|
||||
{
|
||||
if (a is null || b is null)
|
||||
{
|
||||
throw new ArgumentNullException(a is null ? nameof(a) : nameof(b));
|
||||
}
|
||||
|
||||
var compare = string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
// Id is not equal but name is.
|
||||
if (!a.Id.Equals(b.Id) && compare == 0)
|
||||
{
|
||||
compare = a.Id.CompareTo(b.Id);
|
||||
}
|
||||
|
||||
return compare == 0 ? a.Version.CompareTo(b.Version) : compare;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the plugin information.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="PluginInfo"/> instance containing the information.</returns>
|
||||
public PluginInfo GetPluginInfo()
|
||||
{
|
||||
var inst = Instance?.GetPluginInfo() ?? new PluginInfo(Manifest.Name, Version, Manifest.Description, Manifest.Id, true);
|
||||
inst.Status = Manifest.Status;
|
||||
inst.HasImage = !string.IsNullOrEmpty(Manifest.ImagePath);
|
||||
return inst;
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
if (other is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Name.Equals(other.Name, StringComparison.OrdinalIgnoreCase) && Id.Equals(other.Id) && Version.Equals(other.Version);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using MediaBrowser.Model.Plugins;
|
||||
|
||||
namespace MediaBrowser.Common.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a Plugin manifest file.
|
||||
/// </summary>
|
||||
public class PluginManifest
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PluginManifest"/> class.
|
||||
/// </summary>
|
||||
public PluginManifest()
|
||||
{
|
||||
Category = string.Empty;
|
||||
Changelog = string.Empty;
|
||||
Description = string.Empty;
|
||||
Id = Guid.Empty;
|
||||
Name = string.Empty;
|
||||
Owner = string.Empty;
|
||||
Overview = string.Empty;
|
||||
TargetAbi = string.Empty;
|
||||
Version = string.Empty;
|
||||
Assemblies = Array.Empty<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the category of the plugin.
|
||||
/// </summary>
|
||||
[JsonPropertyName("category")]
|
||||
public string Category { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the changelog information.
|
||||
/// </summary>
|
||||
[JsonPropertyName("changelog")]
|
||||
public string Changelog { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the description of the plugin.
|
||||
/// </summary>
|
||||
[JsonPropertyName("description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Global Unique Identifier for the plugin.
|
||||
/// </summary>
|
||||
[JsonPropertyName("guid")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Name of the plugin.
|
||||
/// </summary>
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an overview of the plugin.
|
||||
/// </summary>
|
||||
[JsonPropertyName("overview")]
|
||||
public string Overview { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the owner of the plugin.
|
||||
/// </summary>
|
||||
[JsonPropertyName("owner")]
|
||||
public string Owner { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the compatibility version for the plugin.
|
||||
/// </summary>
|
||||
[JsonPropertyName("targetAbi")]
|
||||
public string TargetAbi { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp of the plugin.
|
||||
/// </summary>
|
||||
[JsonPropertyName("timestamp")]
|
||||
public DateTime Timestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Version number of the plugin.
|
||||
/// </summary>
|
||||
[JsonPropertyName("version")]
|
||||
public string Version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating the operational status of this plugin.
|
||||
/// </summary>
|
||||
[JsonPropertyName("status")]
|
||||
public PluginStatus Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this plugin should automatically update.
|
||||
/// </summary>
|
||||
[JsonPropertyName("autoUpdate")]
|
||||
public bool AutoUpdate { get; set; } = true; // DO NOT MOVE THIS INTO THE CONSTRUCTOR.
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ImagePath
|
||||
/// Gets or sets a value indicating whether this plugin has an image.
|
||||
/// Image must be located in the local plugin folder.
|
||||
/// </summary>
|
||||
[JsonPropertyName("imagePath")]
|
||||
public string? ImagePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the collection of assemblies that should be loaded.
|
||||
/// Paths are considered relative to the plugin folder.
|
||||
/// </summary>
|
||||
[JsonPropertyName("assemblies")]
|
||||
public IReadOnlyList<string> Assemblies { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("MediaBrowser.Common")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Jellyfin Project")]
|
||||
[assembly: AssemblyProduct("Jellyfin Server")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: NeutralResourcesLanguage("en")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
@@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace MediaBrowser.Common.Providers
|
||||
{
|
||||
/// <summary>
|
||||
/// Parsers for provider ids.
|
||||
/// </summary>
|
||||
public static class ProviderIdParsers
|
||||
{
|
||||
private const int ImdbMinNumbers = 7;
|
||||
private const int ImdbMaxNumbers = 8;
|
||||
private const string ImdbPrefix = "tt";
|
||||
|
||||
/// <summary>
|
||||
/// Parses an IMDb id from a string.
|
||||
/// </summary>
|
||||
/// <param name="text">The text to parse.</param>
|
||||
/// <param name="imdbId">The parsed IMDb id.</param>
|
||||
/// <returns>True if parsing was successful, false otherwise.</returns>
|
||||
public static bool TryFindImdbId(ReadOnlySpan<char> text, out ReadOnlySpan<char> imdbId)
|
||||
{
|
||||
// IMDb id is at least 9 chars (tt + 7 numbers)
|
||||
while (text.Length >= 2 + ImdbMinNumbers)
|
||||
{
|
||||
var ttPos = text.IndexOf(ImdbPrefix);
|
||||
if (ttPos == -1)
|
||||
{
|
||||
imdbId = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
text = text.Slice(ttPos);
|
||||
var i = 2;
|
||||
var limit = Math.Min(text.Length, ImdbMaxNumbers + 2);
|
||||
for (; i < limit; i++)
|
||||
{
|
||||
var c = text[i];
|
||||
if (!IsDigit(c))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip if more than 8 digits + 2 chars for tt
|
||||
if (i <= ImdbMaxNumbers + 2 && i >= ImdbMinNumbers + 2)
|
||||
{
|
||||
imdbId = text.Slice(0, i);
|
||||
return true;
|
||||
}
|
||||
|
||||
text = text.Slice(i);
|
||||
}
|
||||
|
||||
imdbId = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses an TMDb id from a movie url.
|
||||
/// </summary>
|
||||
/// <param name="text">The text with the url to parse.</param>
|
||||
/// <param name="tmdbId">The parsed TMDb id.</param>
|
||||
/// <returns>True if parsing was successful, false otherwise.</returns>
|
||||
public static bool TryFindTmdbMovieId(ReadOnlySpan<char> text, out ReadOnlySpan<char> tmdbId)
|
||||
=> TryFindProviderId(text, "themoviedb.org/movie/", out tmdbId);
|
||||
|
||||
/// <summary>
|
||||
/// Parses an TMDb id from a series url.
|
||||
/// </summary>
|
||||
/// <param name="text">The text with the url to parse.</param>
|
||||
/// <param name="tmdbId">The parsed TMDb id.</param>
|
||||
/// <returns>True if parsing was successful, false otherwise.</returns>
|
||||
public static bool TryFindTmdbSeriesId(ReadOnlySpan<char> text, out ReadOnlySpan<char> tmdbId)
|
||||
=> TryFindProviderId(text, "themoviedb.org/tv/", out tmdbId);
|
||||
|
||||
/// <summary>
|
||||
/// Parses an TVDb id from a url.
|
||||
/// </summary>
|
||||
/// <param name="text">The text with the url to parse.</param>
|
||||
/// <param name="tvdbId">The parsed TVDb id.</param>
|
||||
/// <returns>True if parsing was successful, false otherwise.</returns>
|
||||
public static bool TryFindTvdbId(ReadOnlySpan<char> text, out ReadOnlySpan<char> tvdbId)
|
||||
=> TryFindProviderId(text, "thetvdb.com/?tab=series&id=", out tvdbId);
|
||||
|
||||
private static bool TryFindProviderId(ReadOnlySpan<char> text, ReadOnlySpan<char> searchString, [NotNullWhen(true)] out ReadOnlySpan<char> providerId)
|
||||
{
|
||||
var searchPos = text.IndexOf(searchString);
|
||||
if (searchPos == -1)
|
||||
{
|
||||
providerId = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
text = text.Slice(searchPos + searchString.Length);
|
||||
|
||||
int i = 0;
|
||||
for (; i < text.Length; i++)
|
||||
{
|
||||
var c = text[i];
|
||||
|
||||
if (!IsDigit(c))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= 1)
|
||||
{
|
||||
providerId = text.Slice(0, i);
|
||||
return true;
|
||||
}
|
||||
|
||||
providerId = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsDigit(char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Model.Providers;
|
||||
|
||||
namespace MediaBrowser.Common.Providers
|
||||
{
|
||||
public class SubtitleConfigurationFactory : IConfigurationFactory
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<ConfigurationStore> GetConfigurations()
|
||||
{
|
||||
yield return new ConfigurationStore()
|
||||
{
|
||||
Key = "subtitles",
|
||||
ConfigurationType = typeof(SubtitleOptions)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Marks a BaseItem as needing custom serialisation from the Data field of the db.
|
||||
/// </summary>
|
||||
[System.AttributeUsage(System.AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
|
||||
public sealed class RequiresSourceSerialisationAttribute : System.Attribute
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Model.Updates;
|
||||
|
||||
namespace MediaBrowser.Common.Updates
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the <see cref="IInstallationManager" />.
|
||||
/// </summary>
|
||||
public interface IInstallationManager : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the completed installations.
|
||||
/// </summary>
|
||||
IEnumerable<InstallationInfo> CompletedInstallations { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Parses a plugin manifest at the supplied URL.
|
||||
/// </summary>
|
||||
/// <param name="manifestName">Name of the repository.</param>
|
||||
/// <param name="manifest">The URL to query.</param>
|
||||
/// <param name="filterIncompatible">Filter out incompatible plugins.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{IReadOnlyList{PackageInfo}}.</returns>
|
||||
Task<PackageInfo[]> GetPackages(string manifestName, string manifest, bool filterIncompatible, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all available packages that are supported by this version.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{IReadOnlyList{PackageInfo}}.</returns>
|
||||
Task<IReadOnlyList<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Returns all plugins matching the requirements.
|
||||
/// </summary>
|
||||
/// <param name="availablePackages">The available packages.</param>
|
||||
/// <param name="name">The name of the plugin.</param>
|
||||
/// <param name="id">The id of the plugin.</param>
|
||||
/// <param name="specificVersion">The version of the plugin.</param>
|
||||
/// <returns>All plugins matching the requirements.</returns>
|
||||
IEnumerable<PackageInfo> FilterPackages(
|
||||
IEnumerable<PackageInfo> availablePackages,
|
||||
string? name = null,
|
||||
Guid id = default,
|
||||
Version? specificVersion = null);
|
||||
|
||||
/// <summary>
|
||||
/// Returns all compatible versions ordered from newest to oldest.
|
||||
/// </summary>
|
||||
/// <param name="availablePackages">The available packages.</param>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="id">The id 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 id = default,
|
||||
Version? minVersion = null,
|
||||
Version? specificVersion = null);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the available compatible plugin updates.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The available plugin updates.</returns>
|
||||
Task<IEnumerable<InstallationInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Installs the package.
|
||||
/// </summary>
|
||||
/// <param name="package">The package.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns><see cref="Task" />.</returns>
|
||||
Task InstallPackage(InstallationInfo package, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Uninstalls a plugin.
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin.</param>
|
||||
void UninstallPlugin(LocalPlugin plugin);
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the installation.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the package that is being installed.</param>
|
||||
/// <returns>Returns true if the install was cancelled.</returns>
|
||||
bool CancelInstallation(Guid id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using MediaBrowser.Model.Updates;
|
||||
|
||||
namespace MediaBrowser.Common.Updates
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the <see cref="InstallationEventArgs" />.
|
||||
/// </summary>
|
||||
public class InstallationEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="InstallationInfo"/>.
|
||||
/// </summary>
|
||||
public InstallationInfo InstallationInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="VersionInfo"/>.
|
||||
/// </summary>
|
||||
public VersionInfo VersionInfo { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#nullable disable
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Common.Updates
|
||||
{
|
||||
public class InstallationFailedEventArgs : InstallationEventArgs
|
||||
{
|
||||
public Exception Exception { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// <autogenerated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v10.0", FrameworkDisplayName = ".NET 10.0")]
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
is_global = true
|
||||
build_property.TargetFramework = net10.0
|
||||
build_property.TargetFramework = net10.0
|
||||
build_property.TargetFrameworkIdentifier = .NETCoreApp
|
||||
build_property.TargetFrameworkIdentifier = .NETCoreApp
|
||||
build_property.TargetFrameworkVersion = v10.0
|
||||
build_property.TargetFrameworkVersion = v10.0
|
||||
build_property.TargetPlatformMinVersion =
|
||||
build_property.TargetPlatformMinVersion =
|
||||
build_property.UsingMicrosoftNETSdkWeb =
|
||||
build_property.UsingMicrosoftNETSdkWeb =
|
||||
build_property.ProjectTypeGuids =
|
||||
build_property.ProjectTypeGuids =
|
||||
build_property.InvariantGlobalization =
|
||||
build_property.InvariantGlobalization =
|
||||
build_property.PlatformNeutralAssembly =
|
||||
build_property.PlatformNeutralAssembly =
|
||||
build_property.EnforceExtendedAnalyzerRules =
|
||||
build_property.EnforceExtendedAnalyzerRules =
|
||||
build_property._SupportedPlatformList = Linux,macOS,Windows
|
||||
build_property._SupportedPlatformList = Linux,macOS,Windows
|
||||
build_property.RootNamespace = MediaBrowser.Common
|
||||
build_property.ProjectDir = /srv/common_drive/Projects/pgsql-jellyfin/MediaBrowser.Common/
|
||||
build_property.EnableComHosting =
|
||||
build_property.EnableGeneratedComInterfaceComImportInterop =
|
||||
build_property.EffectiveAnalysisLevelStyle = 10.0
|
||||
build_property.EnableCodeStyleSeverity =
|
||||
Binary file not shown.
BIN
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
|
||||
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
|
||||
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
|
||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">/home/wjones/.nuget/packages/</NuGetPackageRoot>
|
||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">/home/wjones/.nuget/packages/</NuGetPackageFolders>
|
||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">7.0.0</NuGetToolVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<SourceRoot Include="/home/wjones/.nuget/packages/" />
|
||||
</ItemGroup>
|
||||
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.entityframeworkcore/10.0.3/buildTransitive/net10.0/Microsoft.EntityFrameworkCore.props" Condition="Exists('$(NuGetPackageRoot)microsoft.entityframeworkcore/10.0.3/buildTransitive/net10.0/Microsoft.EntityFrameworkCore.props')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.codeanalysis.bannedapianalyzers/4.14.0/buildTransitive/Microsoft.CodeAnalysis.BannedApiAnalyzers.props" Condition="Exists('$(NuGetPackageRoot)microsoft.codeanalysis.bannedapianalyzers/4.14.0/buildTransitive/Microsoft.CodeAnalysis.BannedApiAnalyzers.props')" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<PkgStyleCop_Analyzers_Unstable Condition=" '$(PkgStyleCop_Analyzers_Unstable)' == '' ">/home/wjones/.nuget/packages/stylecop.analyzers.unstable/1.2.0.556</PkgStyleCop_Analyzers_Unstable>
|
||||
<PkgSmartAnalyzers_MultithreadingAnalyzer Condition=" '$(PkgSmartAnalyzers_MultithreadingAnalyzer)' == '' ">/home/wjones/.nuget/packages/smartanalyzers.multithreadinganalyzer/1.1.31</PkgSmartAnalyzers_MultithreadingAnalyzer>
|
||||
<PkgSerilogAnalyzer Condition=" '$(PkgSerilogAnalyzer)' == '' ">/home/wjones/.nuget/packages/seriloganalyzer/0.15.0</PkgSerilogAnalyzer>
|
||||
<PkgMicrosoft_CodeAnalysis_BannedApiAnalyzers Condition=" '$(PkgMicrosoft_CodeAnalysis_BannedApiAnalyzers)' == '' ">/home/wjones/.nuget/packages/microsoft.codeanalysis.bannedapianalyzers/4.14.0</PkgMicrosoft_CodeAnalysis_BannedApiAnalyzers>
|
||||
<PkgIDisposableAnalyzers Condition=" '$(PkgIDisposableAnalyzers)' == '' ">/home/wjones/.nuget/packages/idisposableanalyzers/4.0.8</PkgIDisposableAnalyzers>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.codeanalysis.bannedapianalyzers/4.14.0/buildTransitive/Microsoft.CodeAnalysis.BannedApiAnalyzers.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.codeanalysis.bannedapianalyzers/4.14.0/buildTransitive/Microsoft.CodeAnalysis.BannedApiAnalyzers.targets')" />
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"version": 2,
|
||||
"dgSpecHash": "SvTROcOUqzw=",
|
||||
"success": true,
|
||||
"projectFilePath": "/srv/common_drive/Projects/pgsql-jellyfin/MediaBrowser.Common/MediaBrowser.Common.csproj",
|
||||
"expectedPackageFiles": [
|
||||
"/home/wjones/.nuget/packages/diacritics/4.1.4/diacritics.4.1.4.nupkg.sha512",
|
||||
"/home/wjones/.nuget/packages/icu4n/60.1.0-alpha.356/icu4n.60.1.0-alpha.356.nupkg.sha512",
|
||||
"/home/wjones/.nuget/packages/icu4n.transliterator/60.1.0-alpha.356/icu4n.transliterator.60.1.0-alpha.356.nupkg.sha512",
|
||||
"/home/wjones/.nuget/packages/idisposableanalyzers/4.0.8/idisposableanalyzers.4.0.8.nupkg.sha512",
|
||||
"/home/wjones/.nuget/packages/j2n/2.0.0/j2n.2.0.0.nupkg.sha512",
|
||||
"/home/wjones/.nuget/packages/microsoft.codeanalysis.bannedapianalyzers/4.14.0/microsoft.codeanalysis.bannedapianalyzers.4.14.0.nupkg.sha512",
|
||||
"/home/wjones/.nuget/packages/microsoft.entityframeworkcore/10.0.3/microsoft.entityframeworkcore.10.0.3.nupkg.sha512",
|
||||
"/home/wjones/.nuget/packages/microsoft.entityframeworkcore.abstractions/10.0.3/microsoft.entityframeworkcore.abstractions.10.0.3.nupkg.sha512",
|
||||
"/home/wjones/.nuget/packages/microsoft.entityframeworkcore.analyzers/10.0.3/microsoft.entityframeworkcore.analyzers.10.0.3.nupkg.sha512",
|
||||
"/home/wjones/.nuget/packages/microsoft.entityframeworkcore.relational/10.0.3/microsoft.entityframeworkcore.relational.10.0.3.nupkg.sha512",
|
||||
"/home/wjones/.nuget/packages/polly/8.6.5/polly.8.6.5.nupkg.sha512",
|
||||
"/home/wjones/.nuget/packages/polly.core/8.6.5/polly.core.8.6.5.nupkg.sha512",
|
||||
"/home/wjones/.nuget/packages/seriloganalyzer/0.15.0/seriloganalyzer.0.15.0.nupkg.sha512",
|
||||
"/home/wjones/.nuget/packages/smartanalyzers.multithreadinganalyzer/1.1.31/smartanalyzers.multithreadinganalyzer.1.1.31.nupkg.sha512",
|
||||
"/home/wjones/.nuget/packages/stylecop.analyzers/1.2.0-beta.556/stylecop.analyzers.1.2.0-beta.556.nupkg.sha512",
|
||||
"/home/wjones/.nuget/packages/stylecop.analyzers.unstable/1.2.0.556/stylecop.analyzers.unstable.1.2.0.556.nupkg.sha512"
|
||||
],
|
||||
"logs": []
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
17714532047800000
|
||||
@@ -0,0 +1 @@
|
||||
17715044198300000
|
||||
Reference in New Issue
Block a user