BasePlugin.cs 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. #pragma warning disable SA1402
  2. using System;
  3. using System.IO;
  4. using System.Reflection;
  5. using MediaBrowser.Common.Configuration;
  6. using MediaBrowser.Model.Plugins;
  7. using MediaBrowser.Model.Serialization;
  8. using Microsoft.Extensions.DependencyInjection;
  9. namespace MediaBrowser.Common.Plugins
  10. {
  11. /// <summary>
  12. /// Provides a common base class for all plugins.
  13. /// </summary>
  14. public abstract class BasePlugin : IPlugin, IPluginAssembly
  15. {
  16. /// <summary>
  17. /// Gets the name of the plugin.
  18. /// </summary>
  19. /// <value>The name.</value>
  20. public abstract string Name { get; }
  21. /// <summary>
  22. /// Gets the description.
  23. /// </summary>
  24. /// <value>The description.</value>
  25. public virtual string Description => string.Empty;
  26. /// <summary>
  27. /// Gets the unique id.
  28. /// </summary>
  29. /// <value>The unique id.</value>
  30. public virtual Guid Id { get; private set; }
  31. /// <summary>
  32. /// Gets the plugin version.
  33. /// </summary>
  34. /// <value>The version.</value>
  35. public Version Version { get; private set; }
  36. /// <summary>
  37. /// Gets the path to the assembly file.
  38. /// </summary>
  39. /// <value>The assembly file path.</value>
  40. public string AssemblyFilePath { get; private set; }
  41. /// <summary>
  42. /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed.
  43. /// </summary>
  44. /// <value>The data folder path.</value>
  45. public string DataFolderPath { get; private set; }
  46. /// <summary>
  47. /// Gets a value indicating whether the plugin can be uninstalled.
  48. /// </summary>
  49. public bool CanUninstall => !Path.GetDirectoryName(AssemblyFilePath)
  50. .Equals(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), StringComparison.InvariantCulture);
  51. /// <summary>
  52. /// Gets the plugin info.
  53. /// </summary>
  54. /// <returns>PluginInfo.</returns>
  55. public virtual PluginInfo GetPluginInfo()
  56. {
  57. var info = new PluginInfo
  58. {
  59. Name = Name,
  60. Version = Version.ToString(),
  61. Description = Description,
  62. Id = Id.ToString(),
  63. CanUninstall = CanUninstall
  64. };
  65. return info;
  66. }
  67. /// <summary>
  68. /// Called just before the plugin is uninstalled from the server.
  69. /// </summary>
  70. public virtual void OnUninstalling()
  71. {
  72. }
  73. /// <inheritdoc />
  74. public virtual void RegisterServices(IServiceCollection serviceCollection)
  75. {
  76. }
  77. /// <inheritdoc />
  78. public virtual void UnregisterServices(IServiceCollection serviceCollection)
  79. {
  80. }
  81. /// <inheritdoc />
  82. public void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion)
  83. {
  84. AssemblyFilePath = assemblyFilePath;
  85. DataFolderPath = dataFolderPath;
  86. Version = assemblyVersion;
  87. }
  88. /// <inheritdoc />
  89. public void SetId(Guid assemblyId)
  90. {
  91. Id = assemblyId;
  92. }
  93. }
  94. /// <summary>
  95. /// Provides a common base class for all plugins.
  96. /// </summary>
  97. /// <typeparam name="TConfigurationType">The type of the T configuration type.</typeparam>
  98. public abstract class BasePlugin<TConfigurationType> : BasePlugin, IHasPluginConfiguration
  99. where TConfigurationType : BasePluginConfiguration
  100. {
  101. /// <summary>
  102. /// The configuration sync lock.
  103. /// </summary>
  104. private readonly object _configurationSyncLock = new object();
  105. /// <summary>
  106. /// The configuration save lock.
  107. /// </summary>
  108. private readonly object _configurationSaveLock = new object();
  109. private Action<string> _directoryCreateFn;
  110. /// <summary>
  111. /// The configuration.
  112. /// </summary>
  113. private TConfigurationType _configuration;
  114. /// <summary>
  115. /// Initializes a new instance of the <see cref="BasePlugin{TConfigurationType}" /> class.
  116. /// </summary>
  117. /// <param name="applicationPaths">The application paths.</param>
  118. /// <param name="xmlSerializer">The XML serializer.</param>
  119. protected BasePlugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
  120. {
  121. ApplicationPaths = applicationPaths;
  122. XmlSerializer = xmlSerializer;
  123. if (this is IPluginAssembly assemblyPlugin)
  124. {
  125. var assembly = GetType().Assembly;
  126. var assemblyName = assembly.GetName();
  127. var assemblyFilePath = assembly.Location;
  128. var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath));
  129. assemblyPlugin.SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version);
  130. try
  131. {
  132. var idAttributes = assembly.GetCustomAttributes(typeof(GuidAttribute), true);
  133. if (idAttributes.Length > 0)
  134. {
  135. var attribute = (GuidAttribute)idAttributes[0];
  136. var assemblyId = new Guid(attribute.Value);
  137. assemblyPlugin.SetId(assemblyId);
  138. }
  139. }
  140. catch (Exception ex)
  141. {
  142. Logger.LogError(ex, "Error getting plugin Id from {PluginName}.", plugin.GetType().FullName);
  143. }
  144. }
  145. if (this is IHasPluginConfiguration hasPluginConfiguration)
  146. {
  147. hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s));
  148. }
  149. }
  150. /// <summary>
  151. /// Gets the application paths.
  152. /// </summary>
  153. /// <value>The application paths.</value>
  154. protected IApplicationPaths ApplicationPaths { get; private set; }
  155. /// <summary>
  156. /// Gets the XML serializer.
  157. /// </summary>
  158. /// <value>The XML serializer.</value>
  159. protected IXmlSerializer XmlSerializer { get; private set; }
  160. /// <summary>
  161. /// Gets the type of configuration this plugin uses.
  162. /// </summary>
  163. /// <value>The type of the configuration.</value>
  164. public Type ConfigurationType => typeof(TConfigurationType);
  165. /// <summary>
  166. /// Gets the name the assembly file.
  167. /// </summary>
  168. /// <value>The name of the assembly file.</value>
  169. protected string AssemblyFileName => Path.GetFileName(AssemblyFilePath);
  170. /// <summary>
  171. /// Gets or sets the plugin configuration.
  172. /// </summary>
  173. /// <value>The configuration.</value>
  174. public TConfigurationType Configuration
  175. {
  176. get
  177. {
  178. // Lazy load
  179. if (_configuration == null)
  180. {
  181. lock (_configurationSyncLock)
  182. {
  183. if (_configuration == null)
  184. {
  185. _configuration = LoadConfiguration();
  186. }
  187. }
  188. }
  189. return _configuration;
  190. }
  191. protected set => _configuration = value;
  192. }
  193. /// <summary>
  194. /// Gets the name of the configuration file. Subclasses should override.
  195. /// </summary>
  196. /// <value>The name of the configuration file.</value>
  197. public virtual string ConfigurationFileName => Path.ChangeExtension(AssemblyFileName, ".xml");
  198. /// <summary>
  199. /// Gets the full path to the configuration file.
  200. /// </summary>
  201. /// <value>The configuration file path.</value>
  202. public string ConfigurationFilePath => Path.Combine(ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName);
  203. /// <summary>
  204. /// Gets the plugin configuration.
  205. /// </summary>
  206. /// <value>The configuration.</value>
  207. BasePluginConfiguration IHasPluginConfiguration.Configuration => Configuration;
  208. /// <inheritdoc />
  209. public void SetStartupInfo(Action<string> directoryCreateFn)
  210. {
  211. // hack alert, until the .net core transition is complete
  212. _directoryCreateFn = directoryCreateFn;
  213. }
  214. private TConfigurationType LoadConfiguration()
  215. {
  216. var path = ConfigurationFilePath;
  217. try
  218. {
  219. return (TConfigurationType)XmlSerializer.DeserializeFromFile(typeof(TConfigurationType), path);
  220. }
  221. catch
  222. {
  223. return (TConfigurationType)Activator.CreateInstance(typeof(TConfigurationType));
  224. }
  225. }
  226. /// <summary>
  227. /// Saves the current configuration to the file system.
  228. /// </summary>
  229. public virtual void SaveConfiguration()
  230. {
  231. lock (_configurationSaveLock)
  232. {
  233. _directoryCreateFn(Path.GetDirectoryName(ConfigurationFilePath));
  234. XmlSerializer.SerializeToFile(Configuration, ConfigurationFilePath);
  235. }
  236. }
  237. /// <inheritdoc />
  238. public virtual void UpdateConfiguration(BasePluginConfiguration configuration)
  239. {
  240. if (configuration == null)
  241. {
  242. throw new ArgumentNullException(nameof(configuration));
  243. }
  244. Configuration = (TConfigurationType)configuration;
  245. SaveConfiguration();
  246. }
  247. /// <inheritdoc />
  248. public override PluginInfo GetPluginInfo()
  249. {
  250. var info = base.GetPluginInfo();
  251. info.ConfigurationFileName = ConfigurationFileName;
  252. return info;
  253. }
  254. }
  255. }