feat(plugins): runtime-installable plugin system (loader + manager)#713
Open
shuff57 wants to merge 3 commits into
Open
feat(plugins): runtime-installable plugin system (loader + manager)#713shuff57 wants to merge 3 commits into
shuff57 wants to merge 3 commits into
Conversation
A generic, plugin-agnostic ecosystem so Listenarr can load and manage plugins at runtime. No-op on a stock install (no plugins/ folder → unchanged behaviour). Host: - IListenarrPlugin contract + PluginLoader: scan plugins/*.dll, register each as an MVC application part, call ConfigureServices (a plugin owns its own DbContext and migration runner with an isolated history table). - PluginAssets: serve GET /plugins/manifest + static /plugins/<id>/ui/* so plugin frontends load without a rebuild. - PluginManager + PluginsController (/api/v1/plugins, admin-gated): add/remove plugin repositories (a listenarr-plugins.json index), list installed ⊎ available, install/update (download package zip → zip-slip-safe extract → atomic swap) and uninstall. Applies via a restart (IHostApplicationLifetime.StopApplication; the container restart policy relaunches and the loader picks up the change). - docker-entrypoint: chown /app/plugins to the runtime user so runtime install works. Frontend: - window.LISTENARR SDK (shares the host's vue/router/pinia + curated services) and a runtime loader (fetch manifest → inject each plugin's bundle, version-stamped so updates bust the browser cache). Plugin registry + route/root-component injection. - Settings → Plugins tab: Repositories / Available / Installed with install/update/ remove and a "restarting…" reconnect overlay. Trust model: installing from a repository URL runs code with full server access, identical to *arr custom repositories — admin/local-network only. Includes a zip-slip safe-extract unit test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Collaborator
|
@shuff57 I think a plugin system is a better way to handle this. Thank you for taking the initiative. This offloads the maintenance of the plugin to the plugin owner and gives Listenarr extensibility without that maintenance overhead I spoke of. I'll need to do a thorough security review of this to make sure we're implementing this sort of system as securely as possible though. |
Plugins doing writes call window.LISTENARR.api.ensureAntiforgery() before a mutation so the CSRF token is fresh (stale token → 500 not covered by the core auto-retry). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Documents the runtime plugin system introduced in this PR: repositories are a catalog you install from selectively (not a bundle), how install/update/uninstall work, the plugin package layout, and how to run your own repository. Includes a getting-started pointer to an example plugin catalog. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
A generic, plugin-agnostic system so Listenarr can load and manage plugins at runtime — without a rebuild. It's a foundation, not a specific feature: no
plugins/folder → behaviour is byte-for-byte unchanged, so a stock install is unaffected.Host (backend)
IListenarrPlugin— a one-method contract (ConfigureServices). A plugin owns its own state: it registers its ownDbContextand a migration runner with an isolated migrations-history table, so it never collides with the core schema.PluginLoader— scansplugins/*.dll, registers each as an MVC application part (so its controllers route) and callsConfigureServices. Safe no-op when the folder is absent.PluginAssets— servesGET /plugins/manifestand static/plugins/<id>/ui/*, so a plugin's prebuilt frontend loads without rebuilding the host.PluginManager+PluginsController(/api/v1/plugins, admin-gated) — manage plugin repositories (alistenarr-plugins.jsonindex), list installed ⊎ available, install / update (download package zip → zip-slip-safe extract → atomic swap) and uninstall. Changes apply via a restart (IHostApplicationLifetime.StopApplication; the container restart policy relaunches and the loader picks up the change)./app/pluginsto the runtime user so runtime install works underPUID/PGID.Frontend
window.LISTENARRSDK (shares the host's Vue/Router/Pinia + a few curated services, so a plugin never bundles a second framework) and a runtime loader (fetch manifest → inject each plugin's bundle, version-stamped so updates bust the browser cache). Plugin registry + route / root-component injection.How a plugin uses it
A plugin ships as a folder —
plugin.json+ a.dll+ a prebuiltui/bundle — dropped intoplugins/<id>/(or installed at runtime from a repository). The.dllimplementsIListenarrPlugin; the UI self-registers viawindow.LISTENARR.registerPlugin.Live demo
A working plugin using this system: shuff57/listenarr-player — an in-app audiobook player (chapters, bookmarks, resume, a Listen page). Add its repo in Settings → Plugins and Install to see the whole flow end-to-end.
Trust model
Installing from a repository URL downloads and runs code with full server access — the *same trust model as arr custom repositories. The endpoints are admin-gated; intended for admin / local-network use, and documented as such.
Testing
Scope
This PR is only the generic ecosystem — no bundled plugin, no packaging opinions. The player and any specific plugins live in their own repos and install on top.