using MediaBrowser.Common.Kernel; using MediaBrowser.Common.Logging; using MediaBrowser.Common.Serialization; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Plugins; using System; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Threading; namespace MediaBrowser.Common.Plugins { /// /// Provides a common base class for all plugins /// /// The type of the T configuration type. public abstract class BasePlugin : IDisposable, IPlugin where TConfigurationType : BasePluginConfiguration { /// /// Gets the kernel. /// /// The kernel. protected IKernel Kernel { get; private set; } /// /// Gets or sets the plugin's current context /// /// The context. protected KernelContext Context { get { return Kernel.KernelContext; } } /// /// Gets the name of the plugin /// /// The name. public abstract string Name { get; } /// /// Gets the description. /// /// The description. public virtual string Description { get { return string.Empty; } } /// /// Gets a value indicating whether this instance is a core plugin. /// /// true if this instance is a core plugin; otherwise, false. public virtual bool IsCorePlugin { get { return false; } } /// /// Gets the type of configuration this plugin uses /// /// The type of the configuration. public Type ConfigurationType { get { return typeof(TConfigurationType); } } /// /// The _assembly name /// private AssemblyName _assemblyName; /// /// Gets the name of the assembly. /// /// The name of the assembly. protected AssemblyName AssemblyName { get { return _assemblyName ?? (_assemblyName = GetType().Assembly.GetName()); } } /// /// The _unique id /// private Guid? _uniqueId; /// /// Gets the unique id. /// /// The unique id. public Guid UniqueId { get { if (!_uniqueId.HasValue) { _uniqueId = Marshal.GetTypeLibGuidForAssembly(GetType().Assembly); } return _uniqueId.Value; } } /// /// Gets the plugin version /// /// The version. public Version Version { get { return AssemblyName.Version; } } /// /// Gets the name the assembly file /// /// The name of the assembly file. public string AssemblyFileName { get { return AssemblyName.Name + ".dll"; } } /// /// Gets the last date modified of the configuration /// /// The configuration date last modified. public DateTime ConfigurationDateLastModified { get { // Ensure it's been lazy loaded var config = Configuration; return File.GetLastWriteTimeUtc(ConfigurationFilePath); } } /// /// Gets the last date modified of the plugin /// /// The assembly date last modified. public DateTime AssemblyDateLastModified { get { return File.GetLastWriteTimeUtc(AssemblyFilePath); } } /// /// Gets the path to the assembly file /// /// The assembly file path. public string AssemblyFilePath { get { return Path.Combine(Kernel.ApplicationPaths.PluginsPath, AssemblyFileName); } } /// /// The _configuration sync lock /// private object _configurationSyncLock = new object(); /// /// The _configuration initialized /// private bool _configurationInitialized; /// /// The _configuration /// private TConfigurationType _configuration; /// /// Gets the plugin's configuration /// /// The configuration. public TConfigurationType Configuration { get { // Lazy load LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationInitialized, ref _configurationSyncLock, () => XmlSerializer.GetXmlConfiguration(ConfigurationType, ConfigurationFilePath, Logger) as TConfigurationType); return _configuration; } protected set { _configuration = value; if (value == null) { _configurationInitialized = false; } } } /// /// Gets the name of the configuration file. Subclasses should override /// /// The name of the configuration file. public virtual string ConfigurationFileName { get { return Path.ChangeExtension(AssemblyFileName, ".xml"); } } /// /// Gets the full path to the configuration file /// /// The configuration file path. public string ConfigurationFilePath { get { return Path.Combine(Kernel.ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName); } } /// /// The _data folder path /// private string _dataFolderPath; /// /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed /// /// The data folder path. public string DataFolderPath { get { if (_dataFolderPath == null) { // Give the folder name the same name as the config file name // We can always make this configurable if/when needed _dataFolderPath = Path.Combine(Kernel.ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(ConfigurationFileName)); if (!Directory.Exists(_dataFolderPath)) { Directory.CreateDirectory(_dataFolderPath); } } return _dataFolderPath; } } /// /// Returns true or false indicating if the plugin should be downloaded and run within the Ui. /// /// true if [download to UI]; otherwise, false. public virtual bool DownloadToUi { get { return false; } } /// /// Gets the logger. /// /// The logger. public ILogger Logger { get; private set; } /// /// Starts the plugin. /// /// The kernel. /// The logger. /// kernel public void Initialize(IKernel kernel, ILogger logger) { if (kernel == null) { throw new ArgumentNullException("kernel"); } if (logger == null) { throw new ArgumentNullException("logger"); } Logger = logger; Kernel = kernel; if (kernel.KernelContext == KernelContext.Server) { InitializeOnServer(!File.Exists(ConfigurationFilePath)); } else if (kernel.KernelContext == KernelContext.Ui) { InitializeInUi(); } } /// /// Starts the plugin on the server /// /// if set to true [is first run]. protected virtual void InitializeOnServer(bool isFirstRun) { } /// /// Starts the plugin in the Ui /// protected virtual void InitializeInUi() { } /// /// Disposes the plugins. Undos all actions performed during Init. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected void Dispose(bool dispose) { if (Kernel.KernelContext == KernelContext.Server) { DisposeOnServer(dispose); } else if (Kernel.KernelContext == KernelContext.Ui) { DisposeInUI(dispose); } } /// /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void DisposeOnServer(bool dispose) { } /// /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void DisposeInUI(bool dispose) { } /// /// The _save lock /// private readonly object _configurationSaveLock = new object(); /// /// Saves the current configuration to the file system /// /// Cannot call Plugin.SaveConfiguration from the UI. public virtual void SaveConfiguration() { if (Kernel.KernelContext != KernelContext.Server) { throw new InvalidOperationException("Cannot call Plugin.SaveConfiguration from the UI."); } Logger.Info("Saving configuration"); lock (_configurationSaveLock) { XmlSerializer.SerializeToFile(Configuration, ConfigurationFilePath); } // Notify connected UI's Kernel.TcpManager.SendWebSocketMessage("PluginConfigurationUpdated-" + Name, Configuration); } /// /// Completely overwrites the current configuration with a new copy /// Returns true or false indicating success or failure /// /// The configuration. /// configuration public virtual void UpdateConfiguration(BasePluginConfiguration configuration) { if (configuration == null) { throw new ArgumentNullException("configuration"); } Configuration = (TConfigurationType)configuration; SaveConfiguration(); } /// /// Gets the plugin info. /// /// PluginInfo. public PluginInfo GetPluginInfo() { var info = new PluginInfo { Name = Name, DownloadToUI = DownloadToUi, Version = Version.ToString(), AssemblyFileName = AssemblyFileName, ConfigurationDateLastModified = ConfigurationDateLastModified, Description = Description, IsCorePlugin = IsCorePlugin, UniqueId = UniqueId, EnableAutoUpdate = Configuration.EnableAutoUpdate, UpdateClass = Configuration.UpdateClass, ConfigurationFileName = ConfigurationFileName }; var uiPlugin = this as IUIPlugin; if (uiPlugin != null) { info.MinimumRequiredUIVersion = uiPlugin.MinimumRequiredUIVersion.ToString(); } return info; } /// /// Called when just before the plugin is uninstalled from the server. /// public virtual void OnUninstalling() { } /// /// Gets the plugin's configuration /// /// The configuration. BasePluginConfiguration IPlugin.Configuration { get { return Configuration; } } } }