plugins

package
v0.0.49 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 25, 2026 License: MIT Imports: 39 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var AllowedDownloadHosts = []string{"https://github.com/"}

AllowedDownloadHosts lists the allowed host prefixes for plugin download URLs. Tests can override this to allow test servers.

View Source
var AllowedFetchHosts = []string{"https://raw.githubusercontent.com/"}

AllowedFetchHosts lists the allowed host prefixes for repository manifest URLs. Tests can override this to allow test servers.

View Source
var SupportedManifestVersions = []int{1}

SupportedManifestVersions lists manifest versions this Shisho release supports.

View Source
var ValidMetadataFields = []string{
	"title", "subtitle", "authors", "narrators",
	"series", "seriesNumber", "genres", "tags",
	"description", "publisher", "url",
	"releaseDate", "cover", "identifiers",
	"language", "abridged",
}

ValidMetadataFields lists all valid field names for enricher declarations. These map to fields in mediafile.ParsedMetadata.

Functions

func BuildBookContext

func BuildBookContext(book *models.Book) map[string]interface{}

BuildBookContext converts a Book model to a map for plugin context.

func BuildFileContext

func BuildFileContext(file *models.File) map[string]interface{}

BuildFileContext converts a File model to a map for plugin context.

func DownloadCoverFromURL added in v0.0.21

func DownloadCoverFromURL(ctx context.Context, md *mediafile.ParsedMetadata, allowedDomains []string, log logger.Logger) bool

DownloadCoverFromURL fetches a cover image from a URL and populates md.CoverData and md.CoverMimeType. Returns true if the download succeeded, false otherwise. Skips if CoverData is already set (precedence rule). The URL's domain (and any redirect domains) must be in the plugin's httpAccess.domains allowlist.

func InjectHostAPIs

func InjectHostAPIs(rt *Runtime, configGetter ConfigGetter) error

InjectHostAPIs sets up the shisho.* APIs in a plugin's goja runtime. The logPrefix is derived from the runtime's scope and pluginID. The configGetter provides config values for the plugin.

func IsValidMetadataField added in v0.0.10

func IsValidMetadataField(field string) bool

IsValidMetadataField returns true if the field name is a valid enricher field.

func NewHandler

func NewHandler(service *Service, manager *Manager, installer *Installer) *handler

NewHandler creates a handler for testing and external route registration.

func RegisterIdentifyRoutes added in v0.0.21

func RegisterIdentifyRoutes(g *echo.Group, service *Service, manager *Manager, ed *EnrichDeps)

RegisterIdentifyRoutes registers plugin search/apply routes that require books:write permission. These are separate from the admin plugin management routes which require config:write.

func RegisterLibraryRoutes

func RegisterLibraryRoutes(g *echo.Group, service *Service, manager *Manager, authMiddleware *auth.Middleware)

RegisterLibraryRoutes registers per-library plugin order routes on a libraries group.

func RegisterRoutesWithGroup

func RegisterRoutesWithGroup(g *echo.Group, service *Service, manager *Manager, installer *Installer, db *bun.DB, ed *EnrichDeps)

RegisterRoutesWithGroup registers plugin management API routes.

Types

type AddRepositoryPayload added in v0.0.47

type AddRepositoryPayload struct {
	URL   string `json:"url" validate:"required,url"`
	Scope string `json:"scope" validate:"required"`
}

AddRepositoryPayload is the body of POST /plugins/repositories.

type AnnotatedPluginVersion added in v0.0.26

type AnnotatedPluginVersion struct {
	PluginVersion `tstype:",extends"`
	Compatible    bool `json:"compatible"`
}

AnnotatedPluginVersion extends PluginVersion with a compatibility flag.

func AnnotateVersionCompatibility added in v0.0.26

func AnnotateVersionCompatibility(versions []PluginVersion) []AnnotatedPluginVersion

AnnotateVersionCompatibility annotates each version with whether it is compatible with the running Shisho version based on minShishoVersion.

type ApplyOverrides added in v0.0.47

type ApplyOverrides struct {
	// FileName is the value to write to file.Name. Nil = no change.
	// Empty string is treated as nil (treat absent or "" as no-op so
	// callers don't need to special-case empty inputs).
	FileName *string
	// FileNameSource is the value to write to file.NameSource. Nil
	// means "default to the plugin source for this apply call".
	FileNameSource *string
	// SeriesEntries, when non-nil, replaces the book's series associations
	// with the provided list. An empty slice clears all series. Nil means
	// "don't touch series" (the identify form's series checkbox was off).
	SeriesEntries *[]SeriesEntry
}

ApplyOverrides carries apply-path-only signals that don't belong on mediafile.ParsedMetadata (which is part of the public plugin SDK contract). These come exclusively from the identify apply payload — plugins do not model them.

type AvailablePlugin

type AvailablePlugin struct {
	ID          string          `json:"id"`
	Name        string          `json:"name"`
	Overview    string          `json:"overview"`
	Description string          `json:"description"`
	Homepage    string          `json:"homepage"`
	ImageURL    string          `json:"imageUrl"`
	Versions    []PluginVersion `json:"versions"`
}

AvailablePlugin describes a plugin available for installation from a repository.

type AvailablePluginResponse added in v0.0.47

type AvailablePluginResponse struct {
	Scope       string                   `json:"scope"`
	ID          string                   `json:"id"`
	Name        string                   `json:"name"`
	Overview    string                   `json:"overview"`
	Description string                   `json:"description"`
	Homepage    string                   `json:"homepage"`
	ImageURL    string                   `json:"imageUrl"`
	IsOfficial  bool                     `json:"is_official"`
	Versions    []AnnotatedPluginVersion `json:"versions"`
	Compatible  bool                     `json:"compatible"`
}

AvailablePluginResponse is the per-plugin shape returned by GET /plugins/available (as a bare array) and GET /plugins/available/:scope/:id. imageUrl is repository-index passthrough (camelCase exemption); is_official and compatible are server-added.

type Capabilities

type Capabilities struct {
	InputConverter   *InputConverterCap   `json:"inputConverter"`
	FileParser       *FileParserCap       `json:"fileParser"`
	OutputGenerator  *OutputGeneratorCap  `json:"outputGenerator"`
	MetadataEnricher *MetadataEnricherCap `json:"metadataEnricher"`
	IdentifierTypes  []IdentifierTypeCap  `json:"identifierTypes"`
	HTTPAccess       *HTTPAccessCap       `json:"httpAccess"`
	FileAccess       *FileAccessCap       `json:"fileAccess"`
	FFmpegAccess     *FFmpegAccessCap     `json:"ffmpegAccess"`
	ShellAccess      *ShellAccessCap      `json:"shellAccess"`
}

type ConfigField

type ConfigField struct {
	Type        string         `json:"type" tstype:"ConfigFieldType"`
	Label       string         `json:"label"`
	Description string         `json:"description"`
	Required    bool           `json:"required"`
	Secret      bool           `json:"secret"`
	Default     interface{}    `json:"default"`
	Min         *float64       `json:"min"`
	Max         *float64       `json:"max"`
	Options     []SelectOption `json:"options"`
}

ConfigField describes one entry of a plugin's configSchema as parsed from the manifest. Keys stay camelCase on the wire (manifest passthrough exemption, ADR 0004).

type ConfigGetter

type ConfigGetter interface {
	GetConfigRaw(ctx context.Context, scope, pluginID, key string) (*string, error)
	GetAllConfigRaw(ctx context.Context, scope, pluginID string) (map[string]*string, error)
}

ConfigGetter provides config values for a plugin at runtime.

type ConfigSchema

type ConfigSchema map[string]ConfigField

type ConvertResult

type ConvertResult struct {
	Success    bool
	TargetPath string
}

ConvertResult is the result of an input converter hook.

type EnrichDeps added in v0.0.18

type EnrichDeps struct {
	BookStore       bookStore
	RelStore        relationStore
	IdentStore      identifierStore
	PersonFinder    personFinder
	GenreFinder     genreFinder
	TagFinder       tagFinder
	PublisherFinder publisherFinder
	SearchIndexer   searchIndexer
	PageExtractor   pageExtractor
}

EnrichDeps holds optional dependencies for metadata persistence (apply/enrich). If nil, metadata persistence operations are unavailable.

type EnrichSearchResult added in v0.0.22

type EnrichSearchResult struct {
	mediafile.ParsedMetadata `tstype:",extends"`
	PluginScope              string   `json:"plugin_scope"`
	PluginID                 string   `json:"plugin_id"`
	DisabledFields           []string `json:"disabled_fields,omitempty"`
}

EnrichSearchResult wraps ParsedMetadata with server-added fields for the search HTTP response (sent to the frontend, not used by plugins).

type ErrVersionIncompatible added in v0.0.18

type ErrVersionIncompatible struct {
	PluginMinVersion string
	CurrentVersion   string
}

ErrVersionIncompatible is returned when a plugin requires a newer Shisho version. The Manager uses this to distinguish version incompatibility from other load errors and set the appropriate PluginStatusNotSupported status.

func (*ErrVersionIncompatible) Error added in v0.0.18

func (e *ErrVersionIncompatible) Error() string

type FFmpegAccessCap

type FFmpegAccessCap struct {
	Description string `json:"description"`
}

type FSContext

type FSContext struct {
	// contains filtered or unexported fields
}

FSContext tracks allowed paths and temp dir for a single hook invocation. It controls what filesystem operations a plugin can perform.

allowedPaths grants read+write on hook-provided paths (e.g. a file parser's sourcePath, an output generator's destPath). Callers pass a file path for file-only scope or a directory for subtree scope — there is no third option, because isPathWithin does prefix matching against a trailing separator.

allowedReadOnlyPaths is a strict subset of allowedPaths' semantics but only grants reads. Used by the metadata enricher to expose the enrichment target without letting the plugin overwrite the user's book file.

func NewFSContext

func NewFSContext(pluginDir, dataDir string, allowedPaths []string, fileAccessCap *FileAccessCap) *FSContext

NewFSContext creates a new FSContext for a hook invocation.

func (*FSContext) Cleanup

func (ctx *FSContext) Cleanup() error

Cleanup removes the temp directory if it was created.

func (*FSContext) SetReadOnlyAllowedPaths added in v0.0.35

func (ctx *FSContext) SetReadOnlyAllowedPaths(paths []string)

SetReadOnlyAllowedPaths replaces the set of paths the plugin may read but not write. Pass a file path for file-only scope, or a directory for subtree scope (same semantics as allowedPaths).

type FieldSettingsResponse added in v0.0.47

type FieldSettingsResponse struct {
	Fields map[string]bool `json:"fields"`
}

FieldSettingsResponse is the body of GET /plugins/installed/:scope/:id/fields.

type FileAccessCap

type FileAccessCap struct {
	Level       string `json:"level"` // "read" or "readwrite"
	Description string `json:"description"`
}

type FileParserCap

type FileParserCap struct {
	Description string   `json:"description"`
	Types       []string `json:"types"`
	MIMETypes   []string `json:"mimeTypes"`
}

type HTTPAccessCap

type HTTPAccessCap struct {
	Description string   `json:"description"`
	Domains     []string `json:"domains"`
}

type IdentifierTypeCap

type IdentifierTypeCap struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	URLTemplate string `json:"urlTemplate"`
	Pattern     string `json:"pattern"`
}

type InputConverterCap

type InputConverterCap struct {
	Description string   `json:"description"`
	SourceTypes []string `json:"sourceTypes"`
	MIMETypes   []string `json:"mimeTypes"`
	TargetType  string   `json:"targetType"`
}

type InstallPluginPayload added in v0.0.47

type InstallPluginPayload struct {
	Scope       string `json:"scope" validate:"required"`
	ID          string `json:"id" validate:"required"`
	Name        string `json:"name,omitempty"`
	Version     string `json:"version,omitempty"`
	DownloadURL string `json:"download_url,omitempty"`
	SHA256      string `json:"sha256,omitempty"`
}

InstallPluginPayload is the body of POST /plugins/installed. Either both download_url and sha256 are provided, or neither (install from repository).

type Installer

type Installer struct {
	// contains filtered or unexported fields
}

Installer handles downloading and extracting plugins.

func NewInstaller

func NewInstaller(pluginDir string) *Installer

NewInstaller creates a new Installer.

func (*Installer) DownloadPluginImage added in v0.0.18

func (inst *Installer) DownloadPluginImage(ctx context.Context, scope, pluginID, imageURL string) error

DownloadPluginImage downloads an image from the given URL and saves it as icon.png in the plugin's directory. Errors are non-fatal and logged by the caller.

func (*Installer) InstallPlugin

func (inst *Installer) InstallPlugin(ctx context.Context, scope, pluginID, downloadURL, expectedSHA256 string) (*Manifest, error)

InstallPlugin downloads a plugin ZIP, verifies its SHA256, and extracts it. Returns the parsed manifest from the extracted plugin.

func (*Installer) PluginDir

func (inst *Installer) PluginDir() string

PluginDir returns the base directory for installed plugins.

func (*Installer) UninstallPlugin

func (inst *Installer) UninstallPlugin(scope, pluginID string) error

UninstallPlugin removes a plugin's files from disk.

func (*Installer) UpdatePlugin

func (inst *Installer) UpdatePlugin(ctx context.Context, scope, pluginID, downloadURL, expectedSHA256 string) (*Manifest, error)

UpdatePlugin replaces an existing plugin with a new version.

type LibraryFieldSettingsResponse added in v0.0.47

type LibraryFieldSettingsResponse struct {
	Fields     map[string]bool `json:"fields"`
	Customized bool            `json:"customized"`
}

LibraryFieldSettingsResponse is the body of GET /libraries/:id/plugins/:scope/:pluginId/fields.

type LibraryOrderEntry added in v0.0.47

type LibraryOrderEntry struct {
	Scope string `json:"scope" validate:"required"`
	ID    string `json:"id" validate:"required"`
	Mode  string `json:"mode" tstype:"PluginMode"`
}

LibraryOrderEntry is one entry in SetLibraryOrderPayload. An empty mode defaults to "enabled" server-side.

type LibraryPluginOrderPlugin added in v0.0.47

type LibraryPluginOrderPlugin struct {
	Scope string `json:"scope"`
	ID    string `json:"id"`
	Name  string `json:"name"`
	Mode  string `json:"mode" tstype:"PluginMode"`
}

LibraryPluginOrderPlugin is one entry in LibraryPluginOrderResponse, resolved to the plugin's display name.

type LibraryPluginOrderResponse added in v0.0.47

type LibraryPluginOrderResponse struct {
	Customized bool                       `json:"customized"`
	Plugins    []LibraryPluginOrderPlugin `json:"plugins"`
}

LibraryPluginOrderResponse is the body of GET /libraries/:id/plugins/order/:hookType.

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager holds loaded Runtime instances indexed by "scope/id". It coordinates loading at startup, unloading, and hot-reloading on install/update/enable.

func NewManager

func NewManager(service *Service, pluginDir, pluginDataDir string) *Manager

NewManager creates a new Manager.

func (*Manager) CheckForUpdates

func (m *Manager) CheckForUpdates(ctx context.Context) error

CheckForUpdates checks all installed plugins against enabled repositories for available updates. It sets or clears UpdateAvailableVersion on each plugin.

func (*Manager) CheckForUpdatesForRepo added in v0.0.34

func (m *Manager) CheckForUpdatesForRepo(ctx context.Context, scope string, manifest *RepositoryManifest) error

CheckForUpdatesForRepo re-evaluates UpdateAvailableVersion for installed plugins belonging to the given scope using an already-fetched manifest. Used by the sync-repo handler to avoid re-fetching every enabled repository when a single repo is synced. Plugins in other scopes are left untouched — they can't have changed since we only synced one repo.

func (*Manager) DeletePluginData added in v0.0.18

func (m *Manager) DeletePluginData(scope, id string)

DeletePluginData removes the persistent data directory for a plugin. Errors are logged but not returned since this is a best-effort cleanup.

func (*Manager) GetManualRuntimes added in v0.0.26

func (m *Manager) GetManualRuntimes(ctx context.Context, hookType string, libraryID int) ([]*Runtime, error)

GetManualRuntimes returns runtimes for a hook type that are available for manual invocation. Both "enabled" and "manual_only" plugins are returned; only "disabled" are excluded. If libraryID > 0 and the library has customized the order for this hook type, uses the per-library order. Otherwise falls back to global order.

func (*Manager) GetOrderedRuntimes

func (m *Manager) GetOrderedRuntimes(ctx context.Context, hookType string, libraryID int) ([]*Runtime, error)

GetOrderedRuntimes returns runtimes for a hook type in user-defined order. If libraryID > 0 and the library has customized the order for this hook type, uses the per-library order (only enabled entries). Otherwise falls back to global order. In both paths, only runtimes that are actually loaded (globally enabled) are returned.

func (*Manager) GetOutputGenerator

func (m *Manager) GetOutputGenerator(formatID string) *PluginGenerator

GetOutputGenerator returns the PluginGenerator for a given format ID. Returns nil if no plugin provides that format.

func (*Manager) GetParserForType

func (m *Manager) GetParserForType(fileType string) *Runtime

GetParserForType returns the first loaded runtime that has a fileParser for the given type.

func (*Manager) GetRuntime

func (m *Manager) GetRuntime(scope, id string) *Runtime

GetRuntime returns the runtime for a plugin (nil if not loaded).

func (*Manager) LoadAll

func (m *Manager) LoadAll(ctx context.Context) error

LoadAll loads all enabled plugins from the database at startup. Errors are stored in plugins.LoadError and don't prevent other plugins from loading.

func (*Manager) LoadPlugin

func (m *Manager) LoadPlugin(ctx context.Context, scope, id string) error

LoadPlugin loads a single plugin (called during install/enable).

func (*Manager) RegisteredConverterExtensions

func (m *Manager) RegisteredConverterExtensions() map[string]struct{}

RegisteredConverterExtensions returns source extensions that have input converters.

func (*Manager) RegisteredFileExtensions

func (m *Manager) RegisteredFileExtensions() map[string]struct{}

RegisteredFileExtensions returns all file extensions registered by plugin fileParsers (excluding reserved built-in extensions: epub, cbz, m4b, pdf).

func (*Manager) RegisteredOutputFormats

func (m *Manager) RegisteredOutputFormats() []OutputFormatInfo

RegisteredOutputFormats returns all format IDs registered by plugin output generators, along with their source type restrictions.

func (*Manager) ReloadPlugin

func (m *Manager) ReloadPlugin(ctx context.Context, scope, id string) error

ReloadPlugin performs a hot-reload (called during update). Acquires write lock on old runtime (waits for in-progress hooks), then swaps.

func (*Manager) RunFileParser

func (m *Manager) RunFileParser(ctx context.Context, rt *Runtime, filePath, fileType string) (*mediafile.ParsedMetadata, error)

RunFileParser invokes a plugin's fileParser.parse() hook.

func (*Manager) RunFingerprint

func (m *Manager) RunFingerprint(rt *Runtime, bookCtx, fileCtx map[string]interface{}) (string, error)

RunFingerprint invokes a plugin's outputGenerator.fingerprint() hook.

func (*Manager) RunInputConverter

func (m *Manager) RunInputConverter(ctx context.Context, rt *Runtime, sourcePath, targetDir string) (*ConvertResult, error)

RunInputConverter invokes a plugin's inputConverter.convert() hook.

func (*Manager) RunMetadataSearch added in v0.0.18

func (m *Manager) RunMetadataSearch(ctx context.Context, rt *Runtime, searchCtx map[string]interface{}, targetFilePath string) (*SearchResponse, error)

RunMetadataSearch invokes a plugin's metadataEnricher.search() hook.

targetFilePath, if non-empty, grants the enricher read-only access to exactly that one file (the enrichment target) via FSContext's read-only allowedPaths — without it, enrichers get no filesystem access beyond the plugin's own directory unless they declare the broader fileAccess capability. Writes to the target path are NOT granted; a plugin that needs to modify files must declare fileAccess: readwrite explicitly.

func (*Manager) RunOnUninstalling added in v0.0.18

func (m *Manager) RunOnUninstalling(rt *Runtime)

RunOnUninstalling invokes a plugin's optional onUninstalling() lifecycle hook. This is called before uninstall to give the plugin a chance to clean up. Errors in the hook do not prevent uninstall.

func (*Manager) RunOutputGenerator

func (m *Manager) RunOutputGenerator(ctx context.Context, rt *Runtime, sourcePath, destPath string, bookCtx, fileCtx map[string]interface{}) error

RunOutputGenerator invokes a plugin's outputGenerator.generate() hook.

func (*Manager) SetEventCallback added in v0.0.18

func (m *Manager) SetEventCallback(cb PluginEventCallback)

SetEventCallback registers a callback for plugin lifecycle events. Only one callback is supported; subsequent calls replace the previous one.

func (*Manager) UnloadPlugin

func (m *Manager) UnloadPlugin(scope, id string)

UnloadPlugin removes a plugin from memory (called during uninstall/disable).

type Manifest

type Manifest struct {
	ManifestVersion  int          `json:"manifestVersion"`
	ID               string       `json:"id"`
	Name             string       `json:"name"`
	Version          string       `json:"version"`
	Overview         string       `json:"overview"`
	Description      string       `json:"description"`
	Homepage         string       `json:"homepage"`
	License          string       `json:"license"`
	MinShishoVersion string       `json:"minShishoVersion"`
	Capabilities     Capabilities `json:"capabilities"`
	ConfigSchema     ConfigSchema `json:"configSchema"`
}

func ParseManifest

func ParseManifest(data []byte) (*Manifest, error)

ParseManifest parses and validates a manifest.json byte slice.

type MetadataEnricherCap

type MetadataEnricherCap struct {
	Description string   `json:"description"`
	FileTypes   []string `json:"fileTypes"`
	Fields      []string `json:"fields"`
}

type OrderEntry added in v0.0.47

type OrderEntry struct {
	Scope string `json:"scope" validate:"required"`
	ID    string `json:"id" validate:"required"`
	Mode  string `json:"mode" tstype:"PluginMode"`
}

OrderEntry is one entry in SetOrderPayload. An empty mode defaults to "enabled" server-side.

type OutputFormatInfo

type OutputFormatInfo struct {
	ID          string
	Name        string
	SourceTypes []string
	Scope       string
	PluginID    string
}

OutputFormatInfo describes a plugin-provided output format.

type OutputGeneratorCap

type OutputGeneratorCap struct {
	Description string   `json:"description"`
	ID          string   `json:"id"`
	Name        string   `json:"name"`
	SourceTypes []string `json:"sourceTypes"`
}

type PluginApplyPayload added in v0.0.47

type PluginApplyPayload struct {
	BookID int            `json:"book_id" validate:"required"`
	FileID *int           `json:"file_id"`
	Fields map[string]any `json:"fields" validate:"required"`
	// FileName is an optional override for file.Name. The backend treats an
	// empty string as absent; only set when the user explicitly opts the
	// Name field into the apply payload.
	FileName *string `json:"file_name"`
	// FileNameSource is the source attribution for file.Name: "plugin" when
	// the saved value matches the plugin's proposal, "user" when edited.
	FileNameSource *string `json:"file_name_source" tstype:"'plugin' | 'user'"`
	PluginScope    string  `json:"plugin_scope" validate:"required"`
	PluginID       string  `json:"plugin_id" validate:"required"`
}

PluginApplyPayload is the body of POST /plugins/apply. Fields carries the user-selected metadata as an untyped map (converted server-side by convertFieldsToMetadata / extractSeriesEntries).

type PluginConfigResponse added in v0.0.47

type PluginConfigResponse struct {
	Schema              ConfigSchema           `json:"schema"`
	Values              map[string]interface{} `json:"values"`
	DeclaredFields      []string               `json:"declaredFields"`
	FieldSettings       map[string]bool        `json:"fieldSettings"`
	ConfidenceThreshold *float64               `json:"confidence_threshold"`
}

PluginConfigResponse is the body of GET /plugins/installed/:scope/:id/config. Schema/declaredFields come from the parsed manifest (camelCase exemption); values holds the stored config with secrets masked.

type PluginEvent added in v0.0.18

type PluginEvent struct {
	Type  PluginEventType
	Scope string
	ID    string
	// Hooks lists the hook types this plugin provides (e.g., "fileParser", "metadataEnricher").
	// Empty for uninstall/disable events.
	Hooks []string
}

PluginEvent contains information about a plugin lifecycle change.

type PluginEventCallback added in v0.0.18

type PluginEventCallback func(event PluginEvent)

PluginEventCallback is the signature for event listeners.

type PluginEventType added in v0.0.18

type PluginEventType int

PluginEventType describes a plugin lifecycle event.

const (
	// PluginEventInstalled fires after a plugin is installed and loaded.
	PluginEventInstalled PluginEventType = iota
	// PluginEventUninstalled fires after a plugin is uninstalled.
	PluginEventUninstalled
	// PluginEventUpdated fires after a plugin version is updated (hot-reloaded).
	PluginEventUpdated
	// PluginEventEnabled fires after a disabled plugin is re-enabled and loaded.
	PluginEventEnabled
	// PluginEventDisabled fires after an active plugin is disabled and unloaded.
	PluginEventDisabled
	// PluginEventMalfunctioned fires when a plugin fails to load.
	PluginEventMalfunctioned
)

type PluginGenerator

type PluginGenerator struct {
	// contains filtered or unexported fields
}

PluginGenerator adapts a plugin's outputGenerator hook to the filegen.Generator interface.

func NewPluginGenerator

func NewPluginGenerator(manager *Manager, scope, pluginID, formatID string) *PluginGenerator

NewPluginGenerator creates a new PluginGenerator.

func (*PluginGenerator) Fingerprint

func (g *PluginGenerator) Fingerprint(book *models.Book, file *models.File) (string, error)

Fingerprint returns the plugin's fingerprint string for cache invalidation.

func (*PluginGenerator) Generate

func (g *PluginGenerator) Generate(ctx context.Context, srcPath, destPath string, book *models.Book, file *models.File) error

Generate implements filegen.Generator by delegating to the plugin's outputGenerator.generate() hook.

func (*PluginGenerator) SupportedType

func (g *PluginGenerator) SupportedType() string

SupportedType implements filegen.Generator.

type PluginSearchError added in v0.0.31

type PluginSearchError struct {
	PluginScope string `json:"plugin_scope"`
	PluginID    string `json:"plugin_id"`
	PluginName  string `json:"plugin_name"`
	Message     string `json:"message"`
}

PluginSearchError reports a plugin whose search() hook failed so the frontend can surface it to the user instead of silently dropping it.

type PluginSearchPayload added in v0.0.47

type PluginSearchPayload struct {
	Query       string                       `json:"query" validate:"required"`
	BookID      int                          `json:"book_id" validate:"required"`
	FileID      *int                         `json:"file_id"`
	Author      string                       `json:"author,omitempty"`
	Identifiers []mediafile.ParsedIdentifier `json:"identifiers,omitempty" tstype:"ParsedIdentifier[]"`
}

PluginSearchPayload is the body of POST /plugins/search.

type PluginSearchResponse added in v0.0.32

type PluginSearchResponse struct {
	Results        []EnrichSearchResult  `json:"results"`
	Errors         []PluginSearchError   `json:"errors,omitempty"`
	SkippedPlugins []PluginSearchSkipped `json:"skipped_plugins,omitempty"`
	TotalPlugins   int                   `json:"total_plugins"`
}

PluginSearchResponse is the HTTP response body for POST /plugins/search. TotalPlugins is the number of candidate enricher runtimes considered for this search (after library + mode filtering but before file-type skipping), which lets the frontend distinguish "every enricher was skipped" from "some enrichers ran and returned nothing".

type PluginSearchSkipped added in v0.0.31

type PluginSearchSkipped struct {
	PluginScope string `json:"plugin_scope"`
	PluginID    string `json:"plugin_id"`
	PluginName  string `json:"plugin_name"`
}

PluginSearchSkipped reports an enricher that was skipped because it does not declare support for the target file type. The frontend uses this to distinguish "no plugins handle this file type" from "plugins ran and returned nothing".

type PluginVersion

type PluginVersion struct {
	Version          string `json:"version"`
	MinShishoVersion string `json:"minShishoVersion"`
	ManifestVersion  int    `json:"manifestVersion"`
	ReleaseDate      string `json:"releaseDate"`
	Changelog        string `json:"changelog"`
	DownloadURL      string `json:"downloadUrl"`
	SHA256           string `json:"sha256"`
	// ReleaseURL is an optional explicit URL for this version's release page
	// (e.g. a GitHub release, a GitLab tag page). When present, the UI renders
	// a "View release" link on the version card. When absent, no link is shown.
	// Not validated — any string URL is accepted.
	ReleaseURL   string        `json:"releaseUrl,omitempty"`
	Capabilities *Capabilities `json:"capabilities,omitempty"`
}

PluginVersion describes a specific version of an available plugin.

func FilterCompatibleVersions

func FilterCompatibleVersions(versions []PluginVersion) []PluginVersion

FilterCompatibleVersions returns only versions with a manifestVersion supported by this Shisho release.

func FilterVersionCompatibleVersions added in v0.0.26

func FilterVersionCompatibleVersions(versions []PluginVersion) []PluginVersion

FilterVersionCompatibleVersions returns only versions whose minShishoVersion is satisfied by the running Shisho version.

type RepositoryManifest

type RepositoryManifest struct {
	RepositoryVersion int               `json:"repositoryVersion"`
	Scope             string            `json:"scope"`
	Name              string            `json:"name"`
	Plugins           []AvailablePlugin `json:"plugins"`
}

RepositoryManifest is the parsed JSON from a repository URL.

func FetchRepository

func FetchRepository(rawURL string) (*RepositoryManifest, error)

FetchRepository downloads and parses a repository manifest from the given URL. Only HTTPS URLs to allowed hosts (raw.githubusercontent.com by default) are permitted.

type Runtime

type Runtime struct {
	// contains filtered or unexported fields
}

Runtime wraps a goja VM for a single plugin. It loads manifest.json, executes main.js (which defines a `plugin` global via IIFE), and extracts hook function references.

func LoadPlugin

func LoadPlugin(dir, scope, pluginID string) (*Runtime, error)

LoadPlugin creates a new Runtime by reading manifest.json and executing main.js from the given plugin directory.

func (*Runtime) HookTypes

func (rt *Runtime) HookTypes() []string

HookTypes returns the list of hook type strings this plugin provides, sorted alphabetically for deterministic output.

func (*Runtime) LoadWarning added in v0.0.10

func (rt *Runtime) LoadWarning() string

LoadWarning returns any non-fatal warning from plugin load. Empty string means no warning.

func (*Runtime) Manifest

func (rt *Runtime) Manifest() *Manifest

Manifest returns the parsed manifest.

func (*Runtime) PluginID

func (rt *Runtime) PluginID() string

PluginID returns the plugin's ID (e.g., "goodreads-metadata").

func (*Runtime) Scope

func (rt *Runtime) Scope() string

Scope returns the plugin's scope (e.g., "shisho").

func (*Runtime) SetFSContext

func (rt *Runtime) SetFSContext(ctx *FSContext)

SetFSContext sets the filesystem context for the current hook invocation. Called by hook execution code before invoking hooks. Pass nil to clear.

type SearchResponse added in v0.0.18

type SearchResponse struct {
	Results []mediafile.ParsedMetadata
}

SearchResponse is the result of a metadata enricher's search() hook. Results are ParsedMetadata directly — no intermediate SearchResult type.

type SelectOption

type SelectOption struct {
	Value string `json:"value"`
	Label string `json:"label"`
}

type SeriesEntry added in v0.0.42

type SeriesEntry struct {
	Name             string
	Number           *float64
	SeriesNumberUnit *string
}

SeriesEntry represents a single series association in the multi-series apply payload. Used by the identify form which supports multiple series per book, unlike the plugin SDK which models a single series.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service provides database operations for plugin management.

func NewService

func NewService(db *bun.DB) *Service

NewService creates a new plugin service.

func (*Service) AddRepository

func (s *Service) AddRepository(ctx context.Context, repo *models.PluginRepository) error

AddRepository inserts a new plugin repository.

func (*Service) AppendToOrder

func (s *Service) AppendToOrder(ctx context.Context, hookType, scope, pluginID string) error

AppendToOrder appends a plugin to the end of the hook config for a hook type.

func (*Service) GetAllConfigRaw

func (s *Service) GetAllConfigRaw(ctx context.Context, scope, pluginID string) (map[string]*string, error)

GetAllConfigRaw returns all raw config key-value pairs for a plugin.

func (*Service) GetConfig

func (s *Service) GetConfig(ctx context.Context, scope, pluginID string, schema ConfigSchema, raw bool) (map[string]interface{}, error)

GetConfig returns the configuration values for a plugin. If raw is false, secret values are masked with "***". The schema is used to determine which fields are secrets.

func (*Service) GetConfigRaw

func (s *Service) GetConfigRaw(ctx context.Context, scope, pluginID, key string) (*string, error)

GetConfigRaw returns the raw value for a single config key.

func (*Service) GetEffectiveFieldSettings added in v0.0.10

func (s *Service) GetEffectiveFieldSettings(ctx context.Context, libraryID int, scope, pluginID string, declaredFields []string) (map[string]bool, error)

GetEffectiveFieldSettings returns the effective field settings for a library, merging global settings with library-specific overrides. Returns a map of field name -> enabled status for the specified declared fields. Priority: library override > global setting > default (enabled).

func (*Service) GetFieldSettings added in v0.0.10

func (s *Service) GetFieldSettings(ctx context.Context, scope, pluginID string) (map[string]bool, error)

GetFieldSettings returns global field settings for a plugin. Returns a map of field name -> enabled status for fields that are explicitly disabled. Absence from the map means the field is enabled (default).

func (*Service) GetLibraryFieldSettings added in v0.0.10

func (s *Service) GetLibraryFieldSettings(ctx context.Context, libraryID int, scope, pluginID string) (map[string]bool, error)

GetLibraryFieldSettings returns per-library field overrides for a plugin. Returns a map of field name -> enabled status for fields with explicit overrides. Unlike global settings, both enabled and disabled overrides are stored.

func (*Service) GetLibraryOrder

func (s *Service) GetLibraryOrder(ctx context.Context, libraryID int, hookType string) ([]*models.LibraryPluginHookConfig, error)

GetLibraryOrder returns the per-library plugin hook config for a hook type, sorted by position.

func (*Service) GetOrder

func (s *Service) GetOrder(ctx context.Context, hookType string) ([]*models.PluginHookConfig, error)

GetOrder returns the plugin hook config entries for a hook type, sorted by position.

func (*Service) GetPlugin

func (s *Service) GetPlugin(ctx context.Context, scope, id string) (*models.Plugin, error)

GetPlugin returns a single plugin by scope and ID, or nil if not found.

func (*Service) GetRepository

func (s *Service) GetRepository(ctx context.Context, scope string) (*models.PluginRepository, error)

GetRepository returns a single plugin repository by scope.

func (*Service) InstallPlugin

func (s *Service) InstallPlugin(ctx context.Context, plugin *models.Plugin) error

InstallPlugin inserts a new plugin record.

func (*Service) IsLibraryCustomized

func (s *Service) IsLibraryCustomized(ctx context.Context, libraryID int, hookType string) (bool, error)

IsLibraryCustomized checks if a library has customized plugin order for a hook type.

func (*Service) ListIdentifierTypes

func (s *Service) ListIdentifierTypes(ctx context.Context) ([]*models.PluginIdentifierType, error)

ListIdentifierTypes returns plugin-registered identifier types, deduplicated by ID. Multiple plugins can register the same type (e.g., local and published versions); only the first by scope/plugin ordering is returned.

func (*Service) ListPlugins

func (s *Service) ListPlugins(ctx context.Context) ([]*models.Plugin, error)

ListPlugins returns all installed plugins.

func (*Service) ListRepositories

func (s *Service) ListRepositories(ctx context.Context) ([]*models.PluginRepository, error)

ListRepositories returns all plugin repositories, ordered by official first then by scope.

func (*Service) RemoveRepository

func (s *Service) RemoveRepository(ctx context.Context, scope string) error

RemoveRepository removes a non-official plugin repository by scope.

func (*Service) ResetAllLibraryOrders

func (s *Service) ResetAllLibraryOrders(ctx context.Context, libraryID int) error

ResetAllLibraryOrders removes all per-library plugin customizations for a library.

func (*Service) ResetLibraryFieldSettings added in v0.0.10

func (s *Service) ResetLibraryFieldSettings(ctx context.Context, libraryID int, scope, pluginID string) error

ResetLibraryFieldSettings removes all per-library field overrides for a plugin.

func (*Service) ResetLibraryOrder

func (s *Service) ResetLibraryOrder(ctx context.Context, libraryID int, hookType string) error

ResetLibraryOrder removes per-library customization for a specific hook type.

func (*Service) RetrievePlugin

func (s *Service) RetrievePlugin(ctx context.Context, scope, id string) (*models.Plugin, error)

RetrievePlugin returns a single plugin by scope and id.

func (*Service) SetConfig

func (s *Service) SetConfig(ctx context.Context, scope, pluginID, key, value string) error

SetConfig upserts a single config key-value pair for a plugin.

func (*Service) SetFieldSetting added in v0.0.10

func (s *Service) SetFieldSetting(ctx context.Context, scope, pluginID, field string, enabled bool) error

SetFieldSetting upserts a single global field setting. If enabled=true, the row is deleted (enabled is the default). If enabled=false, the row is upserted.

func (*Service) SetLibraryFieldSetting added in v0.0.10

func (s *Service) SetLibraryFieldSetting(ctx context.Context, libraryID int, scope, pluginID, field string, enabled bool) error

SetLibraryFieldSetting upserts a per-library field override. Both enabled and disabled overrides are stored to allow library-specific overrides in either direction.

func (*Service) SetLibraryOrder

func (s *Service) SetLibraryOrder(ctx context.Context, libraryID int, hookType string, entries []models.LibraryPluginHookConfig) error

SetLibraryOrder replaces all per-library plugin hook config entries for a hook type. Also creates the customization record if it doesn't exist.

func (*Service) SetOrder

func (s *Service) SetOrder(ctx context.Context, hookType string, entries []models.PluginHookConfig) error

SetOrder replaces all hook config entries for a hook type in a transaction.

func (*Service) UninstallPlugin

func (s *Service) UninstallPlugin(ctx context.Context, scope, id string) error

UninstallPlugin removes a plugin and its related data (configs, orders cascade via FK).

func (*Service) UpdateConfidenceThreshold added in v0.0.22

func (s *Service) UpdateConfidenceThreshold(ctx context.Context, scope, pluginID string, threshold *float64) error

UpdateConfidenceThreshold sets the per-plugin confidence threshold.

func (*Service) UpdatePlugin

func (s *Service) UpdatePlugin(ctx context.Context, plugin *models.Plugin) error

UpdatePlugin updates an existing plugin record.

func (*Service) UpdateRepository

func (s *Service) UpdateRepository(ctx context.Context, repo *models.PluginRepository) error

UpdateRepository updates an existing plugin repository.

func (*Service) UpsertIdentifierTypes

func (s *Service) UpsertIdentifierTypes(ctx context.Context, scope, pluginID string, types []IdentifierTypeCap) error

UpsertIdentifierTypes replaces all identifier types for a plugin in a transaction.

type SetFieldSettingsPayload added in v0.0.47

type SetFieldSettingsPayload struct {
	Fields map[string]bool `json:"fields" validate:"required"`
}

SetFieldSettingsPayload is the body of PUT /plugins/installed/:scope/:id/fields and PUT /libraries/:id/plugins/:scope/:pluginId/fields.

type SetLibraryOrderPayload added in v0.0.47

type SetLibraryOrderPayload struct {
	Plugins []LibraryOrderEntry `json:"plugins" validate:"required"`
}

SetLibraryOrderPayload is the body of PUT /libraries/:id/plugins/order/:hookType.

type SetOrderPayload added in v0.0.47

type SetOrderPayload struct {
	Order []OrderEntry `json:"order" validate:"required"`
}

SetOrderPayload is the body of PUT /plugins/order/:hookType.

type ShellAccessCap added in v0.0.9

type ShellAccessCap struct {
	Description string   `json:"description"`
	Commands    []string `json:"commands"`
}

type SyncRepositoryResponse added in v0.0.47

type SyncRepositoryResponse struct {
	models.PluginRepository `tstype:",extends"`
	UpdateRefreshError      *string `json:"update_refresh_error,omitempty"`
}

SyncRepositoryResponse is the body of POST /plugins/repositories/:scope/sync. It embeds the repository by value (so tygo emits `extends PluginRepository` and the fields stay at the top level for backwards-compat with clients reading them) and adds an optional update_refresh_error populated when the post-sync update refresh failed.

type UpdatePluginPayload added in v0.0.47

type UpdatePluginPayload struct {
	Enabled                  *bool             `json:"enabled"`
	AutoUpdate               *bool             `json:"auto_update"`
	Config                   map[string]string `json:"config,omitempty"`
	ConfidenceThreshold      *float64          `json:"confidence_threshold"`
	ClearConfidenceThreshold *bool             `json:"clear_confidence_threshold"`
}

UpdatePluginPayload is the body of PATCH /plugins/installed/:scope/:id.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL