Introduction to Writing Plugins
Writing Plugins for Unmanic should be easy.
All that is required to get started is a basic knowledge of writing Python scripts.
Directory Structure
The basic directory structure of a Plugin should be:
my_new_plugin
├── changelog.md
├── description.md
├── info.json
├── lib
├── package.json
├── plugin.py
├── requirements.post-install.txt
├── requirements.txt
└── static
File: changelog.md
This file should be used to record changes made to the Plugin.
File: description.json (optional)
This file can be used to extend the description provided within the info.json file.
File: info.json
A JSON file containing the metadata of your Plugin. This JSON file should contain the following data:
author- Your name.compatibility- A list of Unmanic Plugin Handler versions that this Plugin is compatible with (see table below).description- A long description of your Plugin detailing what it does. To include line-breaks, insert a\n.icon- A URI to an image. If no icon is provided, the default Plugin icon will be used in the WebUI.id- A string identifier for your Plugin. Should only contain lowercasea-zor_characters.name- A very short name for your Plugin.platform- Platform compatibility of this plugin. Can be any combination of ["all", "linux", "windows", "mac"].priorities- A dictionary of the initial priority that this Plugin's runners within the Plugin flow.tags- A comma separated list able to be used as keywords when searching for Plugins.defer_dependency_install- Optional boolean. Whentrue, Unmanic will install this plugin'srequirements.txtandpackage.jsondependencies during plugin installation instead of assuming they were already bundled with the plugin package.version- The version of your Plugin.
Example:
{
"author": "Josh.5",
"compatibility": [
1,
2
],
"description": "Specify a language tag for Unmanic to try and put as 1st subtitle track.",
"defer_dependency_install": false,
"icon": "https://raw.githubusercontent.com/Josh5/unmanic.plugin.reorder_subtitle_streams_by_language/master/icon.png",
"id": "reorder_subtitle_streams_by_language",
"name": "Re-order subtitle streams by language",
"platform": [
"all"
],
"priorities": {
"on_library_management_file_test": 99,
"on_worker_process": 2
},
"tags": "subtitle,ffmpeg,library file test",
"version": "1.0.4"
}
Plugin handler compatibility:
| Plugin handler | Unmanic versions |
|---|---|
| v1 | v0.1.0 - v0.1.4 |
| v2 | v0.2.0 - current |
Directory: lib (optional)
Location of any additional Python modules imported by this plugin. Use this directory if you are including files from another project on GitHub, etc.
File: package.json (optional)
This file will be read during the plugin build in GitHub. Any nodeJS packages listed will be installed and packaged with the plugin.
Python modules will be installed to {plugin root}/node_modules.
File: plugin.py
The main Pugin Python module.
This file will be imported and it's functions called by the main Unmanic process.
File: requirements.txt (optional)
This file will be read during the plugin build in GitHub. Any Python modules listed will be installed and packaged with the plugin.
Python modules will be installed to {plugin root}/site-packages.
File: requirements.post-install.txt (optional)
This file may be used when a packaged plugin needs to install Python dependencies at plugin install time.
When a plugin zip is installed, Unmanic checks for requirements.post-install.txt and, if present, installs the listed Python modules into {plugin root}/site-packages.
Use this file for dependencies that should be resolved on the target Unmanic system during installation rather than being pre-bundled into the plugin package.
This file is processed during packaged plugin installation whether or not defer_dependency_install is set.
Dependency Installation Modes
Unmanic supports two dependency installation patterns for plugin authors.
1. Bundled dependencies
This is the default plugin packaging model.
- Add Python dependencies to
requirements.txt. - Add frontend dependencies to
package.jsonif needed. - Build/package the plugin so those dependencies are bundled with the plugin.
- Do not set
defer_dependency_installunless you explicitly want the target Unmanic instance to install dependencies during plugin installation.
2. Deferred dependency installation
Set "defer_dependency_install": true in info.json when you want Unmanic to install dependencies during plugin installation.
This option exists primarily for plugins whose packaged dependencies would be too large to reasonably ship inside the plugin repository.
In practice, this matters most for plugins intended for the official plugin repository hosted on GitHub. If bundling the built plugin with all of its dependencies would create artifacts that are too large for GitHub's storage limits or generally too large to manage well in the repository, you can defer dependency installation and let the target Unmanic instance install them after the plugin itself has been installed.
This allows the plugin source and package metadata to remain small while still making large Python or frontend dependency sets available on the destination system at install time.
When this flag is enabled during packaged plugin installation:
requirements.post-install.txtis installed first, if present.requirements.txtis then installed into the plugin'ssite-packagesdirectory.package.jsondependencies are then installed and built.- The
requirements.txtinstall recreatessite-packages, so any Python packages installed only byrequirements.post-install.txtwill be replaced unless they are also present inrequirements.txt.
Example:
{
"id": "my_plugin",
"name": "My Plugin",
"author": "Example Author",
"version": "0.0.1",
"description": "Example plugin using deferred dependency installation.",
"defer_dependency_install": true,
"compatibility": [2]
}
When To Use defer_dependency_install
Use defer_dependency_install when:
- the plugin's bundled Python or Node dependencies would make the packaged plugin too large to store comfortably in a Git-based plugin repository
- the plugin is intended for the official repository and packaging everything into the published plugin would exceed practical GitHub size limits
- installing dependencies on the destination Unmanic system is a better tradeoff than committing large built dependency payloads to the repository
Do not use defer_dependency_install when:
- your dependency set is reasonably small and can be bundled normally
- you do not need to work around repository size constraints
- you want the most predictable install behavior with dependencies already packaged with the plugin
For most plugins, it is better to leave defer_dependency_install unset or false and ship the plugin with its dependencies bundled normally.
Choosing Between requirements.txt and requirements.post-install.txt
- Use
requirements.txtfor the main Python dependencies of your plugin. - Use
requirements.post-install.txtfor Python dependencies that should be installed during packaged plugin installation even whendefer_dependency_installis not enabled. - If both files exist and the plugin is installed from a package,
requirements.post-install.txtis processed first. - If
defer_dependency_installis also enabled, ensure packages required after installation are present inrequirements.txt, because that later install rebuildssite-packages.
Development Caveat
At the time of writing, the local plugin reload workflow used during development installs requirements.txt directly when you reload plugins from disk. The requirements.post-install.txt behavior described above is part of the packaged plugin install path.
Because of this:
- Keep development dependencies in
requirements.txtif you rely on--reload-pluginsduring local plugin development. - Treat
requirements.post-install.txtas an install-time feature for packaged plugin installs. - If you use
defer_dependency_install, test both local development reloads and packaged plugin installation so you verify the behavior you expect in both paths.
Directory: static (optional)
Location of any static assets to be served via the webserver.
Assets located in this directory will be available via the webserver at /unmanic/panel/{plugin ID}/static/(.*).
Plugin Module
The plugin.py file is a stand-alone Python module. From this module you may import other
modules as you see fit. There is no limitation on what may be executed within the
runner
of your Plugin.
Runners
The Plugin module is made up of defined runner functions. A Plugin may include multiple runner functions such that it is executed at multiple stages of the library optimisation process.
A data parameter is provided to the runner functions. This data parameter is a dictionary object of information pertaining to that stage of the library optimisation process. This data object's schema that is provided to a plugin runner should still exist once the plugin runner function has completed execution. During the function, that data may be manipulated as you see fit. A plugin runner may fail if data is removed from that data dictionary.
Plugins should be tested with the Plugin Manager CLI before publishing changes to ensure that the returned data matches the required schema for all included runner functions.
Example:
def on_worker_process(data):
...
return
| Runner Type | Documentation |
|---|---|
| on_library_management_file_test | LINK |
| on_worker_process | LINK |
| on_postprocessor_file_movement | LINK |
| on_postprocessor_task_results | LINK |
| render_frontend_panel | LINK |
| render_plugin_api | LINK |