+ return result
+ }
+
+ // ###################### Registration ######################
+
+ async registerPluginsAndThemes () {
+ await this.resetCSSGlobalFile()
+
+ const plugins = await PluginModel.listEnabledPluginsAndThemes()
+
+ for (const plugin of plugins) {
+ try {
+ await this.registerPluginOrTheme(plugin)
+ } catch (err) {
+ // Try to unregister the plugin
+ try {
+ await this.unregister(PluginModel.buildNpmName(plugin.name, plugin.type))
+ } catch {
+ // we don't care if we cannot unregister it
+ }
+
+ logger.error('Cannot register plugin %s, skipping.', plugin.name, { err })
+ }
+ }
+
+ this.sortHooksByPriority()
+ }
+
+ // Don't need the plugin type since themes cannot register server code
+ async unregister (npmName: string) {
+ logger.info('Unregister plugin %s.', npmName)
+
+ const plugin = this.getRegisteredPluginOrTheme(npmName)
+
+ if (!plugin) {
+ throw new Error(`Unknown plugin ${npmName} to unregister`)
+ }
+
+ delete this.registeredPlugins[plugin.npmName]
+
+ this.deleteTranslations(plugin.npmName)
+
+ if (plugin.type === PluginType.PLUGIN) {
+ await plugin.unregister()
+
+ // Remove hooks of this plugin
+ for (const key of Object.keys(this.hooks)) {
+ this.hooks[key] = this.hooks[key].filter(h => h.npmName !== npmName)
+ }
+
+ const store = plugin.registerHelpers
+ store.reinitVideoConstants(plugin.npmName)
+ store.reinitTranscodingProfilesAndEncoders(plugin.npmName)
+
+ logger.info('Regenerating registered plugin CSS to global file.')
+ await this.regeneratePluginGlobalCSS()
+ }