Documentation
¶
Overview ¶
Package cardigann runs owner-supplied indexer definitions written in the Cardigann YAML format (the format Prowlarr and Jackett use for their built-in sites). Ether ships the engine only: no site definitions are bundled. Definitions are installed by the owner, either uploaded or fetched from a definitions source the owner configures, and stored in the database.
A configured indexer of kind "cardigann" is searched through newznab.Client's pluggable backend, so built-in sites share Ether's daily budgets, result cache, reputation events and manager fan-out with Newznab and Torznab indexers.
Index ¶
- Variables
- type Caps
- type CatalogEntry
- type CategoryMapping
- type Definition
- type Download
- type ErrorBlock
- type Field
- type Filter
- type HandlerDeps
- type HeaderKV
- type InfoHashBlock
- type KV
- type Login
- type LoginLock
- type LoginTest
- type NamedSelector
- type Option
- type Options
- type Request
- type Reserver
- type Response
- type Rows
- type Runner
- func (r *Runner) Definition() *Definition
- func (r *Runner) FetchNZB(ctx context.Context, rel core.Release, reserve Reserver) ([]byte, error)
- func (r *Runner) ResolveTorrent(ctx context.Context, rel core.Release, reserve Reserver) (core.Release, error)
- func (r *Runner) Search(ctx context.Context, req core.SearchRequest, movieCats, tvCats []int, ...) ([]core.Release, error)
- func (r *Runner) Test(ctx context.Context) (TestResult, error)
- type Search
- type SearchPath
- type Selector
- type Service
- func (s *Service) ApplyConfig(cfg config.Config)
- func (s *Service) Backend(cfg config.Indexer) (newznab.Backend, error)
- func (s *Service) Catalog(ctx context.Context, refresh bool) ([]CatalogEntry, error)
- func (s *Service) Definition(ctx context.Context, id string) (Summary, error)
- func (s *Service) Definitions(ctx context.Context) ([]Summary, error)
- func (s *Service) Handler(deps HandlerDeps) http.Handler
- func (s *Service) Install(ctx context.Context, data []byte, origin, sha string) (Summary, error)
- func (s *Service) InstallFromSource(ctx context.Context, id string) (Summary, error)
- func (s *Service) MigrateSecrets(ctx context.Context, indexers []config.Indexer) (int, error)
- func (s *Service) Remove(ctx context.Context, id string) error
- func (s *Service) Statuses(ctx context.Context) ([]TestStatus, error)
- func (s *Service) Test(ctx context.Context, cfg config.Indexer, record bool) TestResult
- func (s *Service) Update(ctx context.Context) (UpdateReport, error)
- type Setting
- type Summary
- type TestResult
- type TestStatus
- type UpdateFailed
- type UpdateReport
- type ValidationError
Constants ¶
This section is empty.
Variables ¶
var ErrCaptcha = errors.New("the site's sign-in requires a captcha, which Ether cannot solve")
ErrCaptcha reports a login that needs a captcha.
var ErrCloudflare = errors.New("site is protected by a browser challenge")
ErrCloudflare reports a site behind a Cloudflare (or similar) browser challenge that could not be passed.
var ErrLogin = errors.New("sign-in failed")
ErrLogin reports credentials or a session the site rejected.
var ErrNoSource = errors.New("no definitions source is set")
ErrNoSource reports that no definitions source is configured.
var ErrUnknownDefinition = errors.New("definition is not installed")
ErrUnknownDefinition reports a definition id that is not installed.
Functions ¶
This section is empty.
Types ¶
type Caps ¶
type Caps struct {
// CategoryMappings maps a site category id to a Newznab category.
CategoryMappings []CategoryMapping
Modes map[string][]string
AllowRawSearch bool
}
Caps is the definition's capabilities block.
type CatalogEntry ¶
type CatalogEntry struct {
ID string `json:"id"`
File string `json:"file"`
Installed bool `json:"installed"`
// UpdateAvailable is set when the installed copy came from this source
// at a different revision.
UpdateAvailable bool `json:"updateAvailable,omitempty"`
URL string `json:"-"`
SHA string `json:"-"`
}
CatalogEntry is one definition a source offers.
type CategoryMapping ¶
type CategoryMapping struct {
ID string
Cat string // Newznab category name, e.g. "Movies/HD"
Desc string
Default bool
Newznab int // resolved Newznab category id; 0 when Cat is unknown
}
CategoryMapping is one caps.categorymappings (or legacy caps.categories) entry.
type Definition ¶
type Definition struct {
ID string
Name string
Description string
Language string
Type string // public, semi-private, private
Encoding string
Links []string
LegacyLinks []string
FollowRedirect bool
TestLinkTorrent bool
RequestDelay float64 // seconds between requests
// Protocol is "torrent" (the Cardigann default) or "usenet" for a site
// whose downloads are NZB documents. Definitions may declare
// `protocol: usenet`; anything else is a torrent site.
Protocol string
Caps Caps
Settings []Setting
Login *Login
Search Search
Download *Download
}
Definition is a parsed Cardigann indexer definition.
func Parse ¶
func Parse(data []byte) (*Definition, []string, error)
Parse decodes and validates a definition. Problems that make the definition unusable are returned as an error; unsupported optional features are returned as warnings.
func (*Definition) SiteLink ¶
func (d *Definition) SiteLink(override string) string
SiteLink returns the base URL requests resolve against: the owner's choice when set, else the definition's first link. Always ends in "/".
func (*Definition) SupportsMovies ¶
func (d *Definition) SupportsMovies() bool
SupportsMovies reports whether the definition maps any movie category (or maps no categories at all).
func (*Definition) SupportsTV ¶
func (d *Definition) SupportsTV() bool
SupportsTV reports whether the definition maps any TV category (or maps no categories at all).
type Download ¶
type Download struct {
Selectors []Selector
Method string
Before *Request
InfoHash *InfoHashBlock
}
Download is the definition's download block.
type ErrorBlock ¶
ErrorBlock detects an error page: when Selector matches, the request failed with Message (or the matched element's text).
type HandlerDeps ¶
type HandlerDeps struct {
// Indexers returns the configured indexers (to refuse removing a
// definition that is in use).
Indexers func() []config.Indexer
// Unredact restores secrets the UI sent back as placeholders.
Unredact func(config.Indexer) config.Indexer
}
HandlerDeps are the owner API's links to configuration.
type InfoHashBlock ¶
InfoHashBlock builds a magnet from a page's hash and title.
type Login ¶
type Login struct {
Path string
SubmitPath string
Method string // post, form, cookie, get, oneurl
Form string
Inputs []KV
SelectorInputs []NamedSelector
GetSelectorInputs []NamedSelector
Cookies []string
Headers []HeaderKV
Error []ErrorBlock
Test *LoginTest
Captcha bool
}
Login describes how to establish a session with the site.
type LoginLock ¶
LoginLock serialises logins for one key across every Ether node sharing the database. unlock must be called once the login state is saved.
type NamedSelector ¶
NamedSelector is a selector bound to an input name.
type Options ¶
type Options struct {
DB *sql.DB // nil keeps definitions and sessions in memory
// HTTPClient supplies the transport and timeout for site, definitions
// source and FlareSolverr requests (tests inject fakes).
HTTPClient *http.Client
Logger *slog.Logger
Now func() time.Time
// LoginLock serialises logins per indexer across nodes; nil uses an
// in-process lock.
LoginLock LoginLock
Sites config.IndexerSites
// Sealer protects stored login cookies; nil stores them as given.
Sealer config.FieldSealer
}
Options configures a Service.
type Reserver ¶
Reserver reserves one unit of an indexer's daily budget ("hit" or "grab") before a request that counts against it.
type Rows ¶
type Rows struct {
Selector string
After int
Remove string
DateHeaders *Selector
Count *Selector
Multiple bool
MissingAttributeEqualsNoResults bool
Attribute string
Filters []Filter
}
Rows selects result rows from a response.
type Runner ¶
type Runner struct {
// contains filtered or unexported fields
}
Runner executes one configured indexer's definition. It is safe for concurrent use.
func (*Runner) Definition ¶
func (r *Runner) Definition() *Definition
Definition returns the runner's definition.
func (*Runner) ResolveTorrent ¶
func (r *Runner) ResolveTorrent(ctx context.Context, rel core.Release, reserve Reserver) (core.Release, error)
ResolveTorrent fills a torrent release's info hash and magnet, downloading its .torrent (one grab) when the result had only a link.
type Search ¶
type Search struct {
Paths []SearchPath
Inputs []KV
Headers []HeaderKV
KeywordsFilters []Filter
PreprocessingFilters []Filter
Rows Rows
Fields []Field
Error []ErrorBlock
AllowEmptyInputs bool
}
Search is the definition's search block.
type SearchPath ¶
type SearchPath struct {
Path string
Method string
Inputs []KV
InheritInputs bool
Categories []string
FollowRedirect bool
Response Response
}
SearchPath is one request issued per search.
type Selector ¶
type Selector struct {
Selector string
Attribute string
Remove string
Filters []Filter
Case []KV
Optional bool
Text string
HasText bool
Default string
HasDefault bool
UseBeforeResponse bool
}
Selector is a Cardigann selector block.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service manages installed definitions and the runners of configured built-in indexers.
func (*Service) ApplyConfig ¶
ApplyConfig takes the definitions source and FlareSolverr settings from a config snapshot; subscribe it to config changes.
func (*Service) Catalog ¶
Catalog lists the definitions the configured source offers, marking installed ones. refresh bypasses the hour-long cache.
func (*Service) Definition ¶
Definition returns one installed definition.
func (*Service) Definitions ¶
Definitions lists installed definitions by name.
func (*Service) Handler ¶
func (s *Service) Handler(deps HandlerDeps) http.Handler
Handler serves the owner API for built-in indexers:
GET /definitions installed definitions
POST /definitions install an uploaded definition {"yaml": "..."}
GET /definitions/{id} one installed definition
DELETE /definitions/{id} remove a definition no indexer uses
GET /catalog[?refresh=1] the definitions source listing
POST /catalog/install install {"id": "..."} from the source
POST /update refresh definitions installed from the source
POST /test test an indexer config (saved or not)
GET /status last test result per indexer
func (*Service) Install ¶
Install validates and stores a definition (replacing one with the same id). origin is the URL it came from ("" for an upload).
func (*Service) InstallFromSource ¶
InstallFromSource downloads one definition from the source and installs it.
func (*Service) MigrateSecrets ¶ added in v0.2.4
MigrateSecrets seals login cookies stored in plaintext, bound to each configured built-in indexer's site origin, drops plaintext cookies of indexers no longer configured, and returns how many rows it changed. Indexers whose definition cannot be loaded keep their row for a later run (reported in the error).
func (*Service) Statuses ¶
func (s *Service) Statuses(ctx context.Context) ([]TestStatus, error)
Statuses returns the last test result of every tested indexer.
type Setting ¶
type Setting struct {
Name string `json:"name"`
Type string `json:"type"`
Label string `json:"label,omitempty"`
Default string `json:"default,omitempty"`
Defaults []string `json:"defaults,omitempty"`
Options []Option `json:"options,omitempty"`
}
Setting is one owner-editable setting a definition declares.
type Summary ¶
type Summary struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Language string `json:"language,omitempty"`
Type string `json:"type,omitempty"`
Protocol string `json:"protocol"`
Links []string `json:"links"`
Settings []Setting `json:"settings"`
Movies bool `json:"movies"`
TV bool `json:"tv"`
Login string `json:"login,omitempty"`
Source string `json:"source"` // "upload" or "source"
Warnings []string `json:"warnings,omitempty"`
InstalledAt time.Time `json:"installedAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
Summary describes an installed definition for the owner UI.
type TestResult ¶
type TestResult struct {
OK bool `json:"ok"`
Results int `json:"results"`
Titles []string `json:"titles,omitempty"`
Error string `json:"error,omitempty"`
}
TestResult is the outcome of an indexer test.
type TestStatus ¶
type TestStatus struct {
IndexerID string `json:"indexerId"`
OK bool `json:"ok"`
Message string `json:"message,omitempty"`
Results int `json:"results"`
TestedAt time.Time `json:"testedAt"`
}
TestStatus is the outcome of an indexer's most recent test.
type UpdateFailed ¶
UpdateFailed is a definition that could not be refreshed.
type UpdateReport ¶
type UpdateReport struct {
Updated []string `json:"updated"`
Unchanged int `json:"unchanged"`
Failed []UpdateFailed `json:"failed"`
}
UpdateReport summarises "Update definitions".
type ValidationError ¶
type ValidationError struct{ Problems []string }
ValidationError lists the reasons a definition cannot be used.
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string