-
Notifications
You must be signed in to change notification settings - Fork 19
Plugins
Watcher supplies a simple method to run external scripts, or plugins
, when triggered by various events.
Plugins can be found and submitted in the watcherplugins repo.
Watcher is able to execute a python script when triggered by certain events. The script will be passed several arguments which will vary depending on the calling event. Plugins will be executed using the same python binary that executed Watcher. All arguments are passed as strings.
When a movie is added via user-entry, automatic watchlist, or api call.
The script will receive the following command line arguments:
Position | Argument | Example |
---|---|---|
0 | Script | /opt/watcher/plugins/added/added_movie.py |
1 | Title | Night of the Living Dead |
2 | Year | 1968 |
3 | IMDB ID | tt0063350 |
4 | Quality Profile | Default |
5 | Config | {"key1": "value1", "key2": "value2"} |
When a release is snatched and sent to a download client.
The script will receive the following command line arguments:
Position | Argument | Example |
---|---|---|
0 | Script | /opt/watcher/plugins/added/snatched_movie.py |
1 | Title | Night of the Living Dead |
2 | Year | 1968 |
3 | IMDB ID | tt0063350 |
4 | Resolution | 1080P |
5 | Type | nzb |
6 | Download client | NZBGet |
7 | Download ID | 12 |
8 | Indexer | www.indexer.com |
9 | Info Link | www.indexer.com/details/123456789 |
10 | Config | {"key1": "value1", "key2": "value2"} |
Note that info_link will be url encoded.
www.indexer.com%2Fdetails%2F123456789
After all postprocessing steps have completed.
The script will receive the following command line arguments:
Position | Argument | Example |
---|---|---|
0 | Script | /opt/watcher/plugins/added/finished_movie.py |
1 | Title | Night of the Living Dead |
2 | IMDB ID | tt0063350 |
3 | Resolution | 1080P |
4 | Rated | PG-13 |
5 | Original File | /home/user/downloads/movie.mkv |
6 | New File | /home/user/movies/movie.mkv |
7 | Download ID | 123456789 |
8 | Finished Date | 2017-01-01 |
9 | Quality Profile | Bluray-1080P only |
10 | Config | {"key1": "value1", "key2": "value2"} |
Note that Original File and New File may be None
when moving or renaming is disabled.
When processing downloads that were not snatched by Watcher, several other arguments may also be None
.
Each script must be placed in the appropriate directory for its calling event.
Event | Directory |
---|---|
Added Movie | watcher/plugins/added/ |
Snatched Release | watcher/plugins/snatched/ |
Postprocessing Finished | watcher/plugins/finished/ |
Plugins may then be enabled in Settings > Plugins. Enable the plugin by checking the box next to its name. Set the order of plugin execution by dragging the handle next to the checkbox.
Since the plugins are executed using the same python binary you used to start Watcher, plugins must be compatible with that python version as well. There are minimal differences between python 2.7.9 and newer, so this should be of little concern.
Plugins are executed sequentially in a separate thread as to not block the main application.
Watcher.py____<Watcher.py continues as normal>__
\__myplugin.py__myplugin2.py__
Always follow good practices and close all open file handlers and avoid any potential infinite loops.
Since the plugin will be executed in a separate thread, logging lines my be interrupted by other logging events.
Logging will begin with a line indicating execution. All lines printed by a plugin will be logged to the Watcher log file. Logging will finish with a line indicating the exit status.
INFO 2017-01-28 15:43:31,710 core.plugins.added: Executing plugin my_plugin.py.
INFO 2017-01-28 15:43:31,789 core.plugins.execute: writelog.py - This line was printed in the plugin.
INFO 2017-01-28 15:43:31,789 core.plugins.execute: writelog.py - Execution finished. Exit code 0.
A plugin's exit status will have no effect on subsequent plugin executions and should be used as means of troubleshooting and logging errors.
Any non-zero exit code will be logged.
For example:
import sys
sys.exit('Something when horribly wrong')
Will result in the following log entries:
INFO 2017-01-28 15:49:04,082 core.plugins.execute: my_broken_plugin.py - Something went horribly wrong
INFO 2017-01-28 15:49:04,082 core.plugins.execute: my_broken_plugin.py - Execution failed. Exit code 1
User-editable configs can be provided with any plugin. Place a .conf
file in the plugin directory with the same name as your plugin file. So my_plugin.py
would use the config my_plugin.conf
.
Config files must include only a plain-text JSON object.
{
"host": "localhost",
"port": "9090",
"name": "Watcher"
}
The user will be able to modify any of these values in Settings > Plugins.
The config will then be passed to the plugin as the final argument. All key:value pairs in the config will be represented as string, regardless of content. It is the responsibility of the plugin author to parse integers or split lists according to their needs.
Since the JSON object must be passed to the script as a string you will need to load it in your script. Once loaded you can treat it as a normal python dictionary. Check the example section for clarification.
A basic example is as follows:
# Added movie script template
import sys
script, title, year, imdbid, quality, config_json = sys.argv
print 'Executing plugin {} for {}'.format(script, title)
sys.exit(0)
This example shows how to load a config file.
# Added movie script template
import json
import sys
script, title, year, imdbid, quality, config_json = sys.argv
config = json.loads(config_json)
# type(config) is now <'dict'>
sys.exit(0)