stash/pkg/plugin/plugins.go

148 lines
3.6 KiB
Go
Raw Normal View History

2020-08-08 02:05:35 +00:00
// Package plugin implements functions and types for maintaining and running
// stash plugins.
//
// Stash plugins are configured using yml files in the configured plugins
// directory. These yml files must follow the Config structure format.
//
// The main entry into the plugin sub-system is via the Cache type.
package plugin
import (
"fmt"
2021-05-26 04:17:53 +00:00
"net/http"
2020-08-08 02:05:35 +00:00
"os"
"path/filepath"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/plugin/common"
)
// Cache stores plugin details.
type Cache struct {
2021-05-26 04:17:53 +00:00
path string
plugins []Config
gqlHandler http.HandlerFunc
2020-08-08 02:05:35 +00:00
}
// NewCache returns a new Cache loading plugin configurations
// from the provided plugin path. It returns an new instance and an error
// if the plugin directory could not be loaded.
//
// Plugins configurations are loaded from yml files in the provided plugin
// directory and any subdirectories.
func NewCache(pluginPath string) (*Cache, error) {
plugins, err := loadPlugins(pluginPath)
if err != nil {
return nil, err
}
return &Cache{
path: pluginPath,
plugins: plugins,
}, nil
}
2021-05-26 04:17:53 +00:00
func (c *Cache) RegisterGQLHandler(handler http.HandlerFunc) {
c.gqlHandler = handler
}
2020-08-08 02:05:35 +00:00
// ReloadPlugins clears the plugin cache and reloads from the plugin path.
// In the event of an error during loading, the cache will be left empty.
func (c *Cache) ReloadPlugins() error {
c.plugins = nil
plugins, err := loadPlugins(c.path)
if err != nil {
return err
}
c.plugins = plugins
return nil
}
func loadPlugins(path string) ([]Config, error) {
plugins := make([]Config, 0)
logger.Debugf("Reading plugin configs from %s", path)
pluginFiles := []string{}
err := filepath.Walk(path, func(fp string, f os.FileInfo, err error) error {
if filepath.Ext(fp) == ".yml" {
pluginFiles = append(pluginFiles, fp)
}
return nil
})
if err != nil {
return nil, err
}
for _, file := range pluginFiles {
plugin, err := loadPluginFromYAMLFile(file)
if err != nil {
logger.Errorf("Error loading plugin %s: %s", file, err.Error())
} else {
plugins = append(plugins, *plugin)
}
}
return plugins, nil
}
// ListPlugins returns plugin details for all of the loaded plugins.
func (c Cache) ListPlugins() []*models.Plugin {
var ret []*models.Plugin
for _, s := range c.plugins {
ret = append(ret, s.toPlugin())
}
return ret
}
// ListPluginTasks returns all runnable plugin tasks in all loaded plugins.
func (c Cache) ListPluginTasks() []*models.PluginTask {
var ret []*models.PluginTask
for _, s := range c.plugins {
ret = append(ret, s.getPluginTasks(true)...)
}
return ret
}
// CreateTask runs the plugin operation for the pluginID and operation
// name provided. Returns an error if the plugin or the operation could not be
// resolved.
func (c Cache) CreateTask(pluginID string, operationName string, serverConnection common.StashServerConnection, args []*models.PluginArgInput, progress chan float64) (Task, error) {
// find the plugin and operation
plugin := c.getPlugin(pluginID)
if plugin == nil {
return nil, fmt.Errorf("no plugin with ID %s", pluginID)
}
operation := plugin.getTask(operationName)
if operation == nil {
return nil, fmt.Errorf("no task with name %s in plugin %s", operationName, plugin.getName())
}
task := pluginTask{
plugin: plugin,
operation: operation,
serverConnection: serverConnection,
args: args,
progress: progress,
2021-05-26 04:17:53 +00:00
gqlHandler: c.gqlHandler,
2020-08-08 02:05:35 +00:00
}
return task.createTask(), nil
}
func (c Cache) getPlugin(pluginID string) *Config {
for _, s := range c.plugins {
if s.id == pluginID {
return &s
}
}
return nil
}