Documentation
¶
Overview ¶
Package config is Ether's owner-editable settings, stored as JSON in the data directory. The web UI edits it through internal/admin; components get a snapshot at construction and subscribe for changes.
Index ¶
- Constants
- Variables
- func ContainsSealed(b []byte) bool
- func DataDirWarning(dataDir string) string
- func DefaultDataDir() string
- func DropFrontendServerNames(c *Config)
- func EnsureDataDir(dataDir string) error
- func FieldContext(id int64) string
- func InstallCredentialKey(dir string, key []byte) error
- func IsSealed(stored string) bool
- func KeyFingerprint(key []byte) string
- func LocalJSON(c Config) []byte
- func NormalizeProwlarrTags(ids []int) []int
- func OpenWith(s FieldSealer, stored string, context ...string) (string, bool, error)
- func ProtectedValues(cfg Config) []string
- func ReadCredentialKey(dir string) ([]byte, error)
- func RedactDatabaseURL(raw string) string
- func SealWith(s FieldSealer, plain string, context ...string) (string, error)
- func SharedJSON(c Config) []byte
- type Availability
- type Cache
- type Catalog
- type ClientCompatibility
- type Config
- type Database
- type Debrid
- type DebridProvider
- type Downloads
- type Duration
- type Extras
- type FieldSealer
- type Frontend
- type Indexer
- type IndexerCap
- type IndexerSites
- type LLM
- type LLMProvider
- type Markers
- type Notifier
- type OpenSubtitles
- type Outbound
- type OutboundProblem
- type Personalize
- type Plex
- type PolicySnapshot
- type Posters
- type Prowlarr
- type Quality
- type QualityRule
- type RTXVideo
- type Repair
- type Resolver
- type Scope
- type Server
- type SharedBackend
- type SharedDoc
- type SharedOptions
- type Simkl
- type Spike
- type Store
- func (s *Store) AcquirePolicy(ctx context.Context) (func(), error)
- func (s *Store) AcquirePolicySnapshot(ctx context.Context) (*PolicySnapshot, error)
- func (s *Store) AttachShared(ctx context.Context, b SharedBackend, o SharedOptions) error
- func (s *Store) Close()
- func (s *Store) Clustered() bool
- func (s *Store) CredentialKey() []byte
- func (s *Store) CredentialsUpgraded() bool
- func (s *Store) DerivedKey(label string) []byte
- func (s *Store) Generation() uint64
- func (s *Store) Get() Config
- func (s *Store) KeyCreated() bool
- func (s *Store) KeyFingerprint() string
- func (s *Store) OpenField(stored string, context ...string) (string, bool, error)
- func (s *Store) Path() string
- func (s *Store) Read() Config
- func (s *Store) RefreshShared(ctx context.Context) error
- func (s *Store) Seal(name string, plain []byte) (string, error)
- func (s *Store) SealField(plain string, context ...string) (string, error)
- func (s *Store) SealsFields() bool
- func (s *Store) SetNotifier(n Notifier)
- func (s *Store) SetPolicyEpoch(bump func() error)
- func (s *Store) SharedVersion() int64
- func (s *Store) Subscribe(fn func(Config))
- func (s *Store) TrustedProxyCIDRs() []string
- func (s *Store) Unseal(name, sealed string) ([]byte, error)
- func (s *Store) Update(fn func(*Config) error) error
- func (s *Store) UpgradeCredentials(ctx context.Context) error
- type Trakt
- type Transcode
- type Updates
- type Usenet
- type UsenetProvider
Constants ¶
const ( ModePlayable = "playable" ModeDigital = "digital" ModeImmediate = "immediate" )
const ( IndexerKindNewznab = "newznab" IndexerKindCardigann = "cardigann" IndexerKindStremio = "stremio" )
Indexer kinds (Indexer.Kind).
const ( DatabaseSQLite = "sqlite" DatabasePostgres = "postgres" )
const ( DebridRealDebrid = "realdebrid" DebridTorBox = "torbox" DebridAllDebrid = "alldebrid" DebridPremiumize = "premiumize" DebridDebridLink = "debridlink" DebridOffcloud = "offcloud" )
Debrid kinds (DebridProvider.Kind).
const ( LLMAPIOpenAI = "openai" // POST {base}/chat/completions, GET {base}/models LLMAPIAnthropic = "anthropic" // POST {base}/messages, GET {base}/models // LLMAPIGoogle chats through Gemini's OpenAI-compatible surface // ({base}/openai/chat/completions) and lists models natively. LLMAPIGoogle = "google" )
LLM wire protocols.
const ( // node changes the shared settings. SharedTopic = "config:changed" // ResyncTopic is the event bus's synthetic "you may have missed // events" notice after a reconnect; the store re-reads its version. ResyncTopic = "bus:resync" DefaultSharedPoll = 30 * time.Second )
const CredentialKeyFile = ".credentials-key"
const DatabaseURLEnv = "ETHER_DATABASE_URL"
DatabaseURLEnv overrides Database for this process: a postgres:// (or postgresql://) URL selects PostgreSQL with that URL. It lets container and service deployments keep credentials out of config.json.
const DefaultPort = 32400
DefaultPort is the HTTP port of a fresh install: Plex Media Server's. When it is taken, the server moves to a fallback port and saves it.
const LLMProviderCustom = "custom"
LLMProviderCustom is any OpenAI-compatible endpoint the owner supplies.
Variables ¶
var ( // stored version is not the expected one. ErrSharedConflict = errors.New("config: shared settings were changed by another node") // ErrKeyMismatch means this node's credential key is not the cluster's. ErrKeyMismatch = errors.New("config: this node's credential key does not match the cluster's") ErrSharedMissing = errors.New("config: the cluster database holds no shared settings") )
var DebridKinds = []string{DebridRealDebrid, DebridTorBox, DebridAllDebrid, DebridPremiumize, DebridDebridLink, DebridOffcloud}
DebridKinds lists the supported debrid services.
var ErrSealedWithoutKey = errors.New("config: sealed value but no credential key")
ErrSealedWithoutKey is returned when a sealed field is read without a sealer.
var LLMProviders = []LLMProvider{ {ID: "anthropic", Label: "Anthropic — Claude", API: LLMAPIAnthropic, BaseURL: "https://api.anthropic.com/v1", NeedsKey: true, KeyHint: "sk-ant-…"}, {ID: "openai", Label: "OpenAI — GPT", API: LLMAPIOpenAI, BaseURL: "https://api.openai.com/v1", NeedsKey: true, KeyHint: "sk-…"}, {ID: "google", Label: "Google — Gemini", API: LLMAPIGoogle, BaseURL: "https://generativelanguage.googleapis.com/v1beta", NeedsKey: true, KeyHint: "AIza…"}, {ID: "openrouter", Label: "OpenRouter — any model, one key", API: LLMAPIOpenAI, BaseURL: "https://openrouter.ai/api/v1", NeedsKey: true, KeyHint: "sk-or-…", ModelHint: "Models are vendor/model slugs, e.g. openai/gpt-4o."}, {ID: "groq", Label: "Groq — fast inference", API: LLMAPIOpenAI, BaseURL: "https://api.groq.com/openai/v1", NeedsKey: true, KeyHint: "gsk_…"}, {ID: "xai", Label: "xAI — Grok", API: LLMAPIOpenAI, BaseURL: "https://api.x.ai/v1", NeedsKey: true, KeyHint: "xai-…"}, {ID: "deepseek", Label: "DeepSeek", API: LLMAPIOpenAI, BaseURL: "https://api.deepseek.com", NeedsKey: true, KeyHint: "sk-…"}, {ID: "mistral", Label: "Mistral", API: LLMAPIOpenAI, BaseURL: "https://api.mistral.ai/v1", NeedsKey: true, KeyHint: "Your Mistral API key"}, {ID: "ollama", Label: "Ollama — on your network", API: LLMAPIOpenAI, BaseURL: "http://localhost:11434/v1", Local: true, KeyHint: "Not needed"}, {ID: "lmstudio", Label: "LM Studio — on your network", API: LLMAPIOpenAI, BaseURL: "http://localhost:1234/v1", Local: true, KeyHint: "Not needed"}, {ID: LLMProviderCustom, Label: "Custom (OpenAI-compatible)", API: LLMAPIOpenAI, Local: true, RequiresBaseURL: true, KeyHint: "Your provider API key", ModelHint: "Any OpenAI-compatible endpoint, such as Together, Fireworks, vLLM or llama.cpp."}, }
LLMProviders lists the supported services, hosted first.
Functions ¶
func ContainsSealed ¶ added in v0.2.4
ContainsSealed reports whether b holds a sealed value of any format, so that it needs the data directory's credential key to be read.
func DataDirWarning ¶ added in v0.2.4
DataDirWarning returns a warning when the data directory or the credential key can be read by other local accounts, or "" (always on Windows, where the installer and service install manage ACLs). It never changes permissions: an existing directory keeps the mode its owner or a package postinstall chose. The message names modes, not paths.
func DefaultDataDir ¶
func DefaultDataDir() string
DefaultDataDir is where Ether keeps config, database and cache. On Windows that is %ProgramData%\Ether: the server runs as a LocalSystem service, so a per-user directory would depend on who installed it.
func DropFrontendServerNames ¶ added in v0.3.0
func DropFrontendServerNames(c *Config)
DropFrontendServerNames removes a stored per-app server name so a save cannot keep a second name beside the general one.
func EnsureDataDir ¶ added in v0.2.4
EnsureDataDir creates dataDir owner-only (0700) when it does not exist: it holds config.json, the credential key and ether.db. Parents are made as usual, and a directory that already exists keeps the mode its owner or a package postinstall gave it.
func FieldContext ¶ added in v0.2.4
FieldContext joins an int64 row identity for a field context.
func InstallCredentialKey ¶
InstallCredentialKey writes key as dir's credential key. It refuses to replace an existing key file: a node joining a cluster moves its old configuration aside first.
func KeyFingerprint ¶
KeyFingerprint identifies a credential key without revealing it. Cluster nodes compare fingerprints to prove they hold the same key.
func NormalizeProwlarrTags ¶ added in v0.3.0
NormalizeProwlarrTags returns the positive tag ids in ids, deduped and sorted. Non-positive ids are dropped. An empty result is nil so it matches the "no tag filter" config.
func OpenWith ¶ added in v0.2.4
OpenWith is s.OpenField; with a nil s, a sealed value is ErrSealedWithoutKey and anything else is returned as is.
func ProtectedValues ¶ added in v0.2.49
ProtectedValues returns the plaintext config strings the credential walker treats as secrets, including usernames, tokens, passwords, API keys, URL templates, secret-settings values, and the database URL. The result is for redaction. Callers must not write it into an archive.
func ReadCredentialKey ¶
ReadCredentialKey returns dir's credential key, or os.ErrNotExist.
func RedactDatabaseURL ¶
RedactDatabaseURL masks the password in a connection URL.
func SealWith ¶ added in v0.2.4
func SealWith(s FieldSealer, plain string, context ...string) (string, error)
SealWith is s.SealField, or plain unchanged when s is nil (components built without a credential key, such as in tests).
func SharedJSON ¶
SharedJSON is the cluster-shared part of c as JSON (unsealed).
Types ¶
type Availability ¶ added in v0.2.8
type Availability struct {
DisableMovieEvidenceGate bool `json:"disableMovieEvidenceGate"`
MovieEvidenceDays int `json:"movieEvidenceDays"` // zero preserves the 60-day default
Monitor bool `json:"monitor"`
Verify bool `json:"verify"`
PreferVerified bool `json:"preferVerified"`
NewlyAvailable bool `json:"newlyAvailable"`
IndexerIDs []string `json:"indexerIds"` // empty = all supported enabled indexers
Debrid bool `json:"debrid"`
PollMinutes int `json:"pollMinutes"`
MaxPages int `json:"maxPages"`
FeedRequests int `json:"feedRequests"` // total per UTC day, across indexers
CheckRequests int `json:"checkRequests"` // indexer + provider API calls per day
Grabs int `json:"grabs"`
Checks int `json:"checks"`
CheckBytes int64 `json:"checkBytes"` // conservative wire reservations for validation
ReservePercent int `json:"reservePercent"` // preserve this share of configured indexer caps
// Effort is gentle, normal, unlimited, or custom. Empty means the
// v0.2.57 default budgets still apply and should scale; any other
// stored budget is a custom choice.
Effort string `json:"effort,omitempty"`
// CatchUp walks older feed pages. It is optional and never required
// before a title can appear.
CatchUp bool `json:"catchUp,omitempty"`
// DigitalDateEvidence lets a passed digital or physical release date
// show a movie before a playable file has been seen. Theatrical dates
// never count.
DigitalDateEvidence bool `json:"digitalDateEvidence,omitempty"`
// Mode is playable, digital, or immediate. Empty is derived from the
// gate and date flags so existing configs keep working.
Mode string `json:"mode,omitempty"`
}
Availability bounds automatic recent-release monitoring and optional checks. Zero budgets disable the corresponding background work.
func DefaultAvailability ¶ added in v0.2.8
func DefaultAvailability() Availability
func (*Availability) ApplyMode ¶ added in v0.3.0
func (a *Availability) ApplyMode()
ApplyMode writes the engine flags for EffectiveMode. Gentle/Unlimited presets become the self-tuned normal budget unless the owner set a ceiling.
func (Availability) CustomBudgets ¶ added in v0.3.0
func (a Availability) CustomBudgets() bool
CustomBudgets reports an owner-chosen allowance. Presets recompute.
func (Availability) EffectiveMode ¶ added in v0.3.0
func (a Availability) EffectiveMode() string
EffectiveMode maps stored flags onto the three owner modes. The shipped default (gate on, no digital-date shortcut) is "When they can play".
func (Availability) EffectiveMovieEvidenceDays ¶ added in v0.2.35
func (a Availability) EffectiveMovieEvidenceDays() int
EffectiveMovieEvidenceDays preserves legacy configurations.
func (Availability) LegacyDefaultBudgets ¶ added in v0.3.0
func (a Availability) LegacyDefaultBudgets() bool
LegacyDefaultBudgets reports the v0.2.57 shipped feed allowance.
func (Availability) Resolved ¶ added in v0.3.0
func (a Availability) Resolved(indexers []IndexerCap) Availability
Resolved applies Gentle / Normal / Unlimited. Custom and non-default saved numbers are kept. Unlimited indexers get a generous share; a configured daily cap contributes only the background portion.
type Cache ¶
type Cache struct {
Storage string `json:"storage,omitempty"` // local (default) or shared; restart required
PreloadEnabled bool `json:"preloadEnabled,omitempty"`
PreloadBudgetBytes int64 `json:"preloadBudgetBytes,omitempty"`
PreloadMaxTitles int `json:"preloadMaxTitles,omitempty"`
PreloadRateMiB int `json:"preloadRateMiB,omitempty"`
PreloadDuringPlayback bool `json:"preloadDuringPlayback,omitempty"`
PreloadMinFreeBytes int64 `json:"preloadMinFreeBytes,omitempty"`
PreloadSource string `json:"preloadSource,omitempty"`
PreloadContent string `json:"preloadContent,omitempty"` // mixed (default), movies, tv
PreloadHouseholdWeight *int `json:"preloadHouseholdWeight,omitempty"` // 0–100; nil = 65
PreloadHouseholdScope string `json:"preloadHouseholdScope,omitempty"` // home (default), all
PreloadEpisodesAhead int `json:"preloadEpisodesAhead,omitempty"` // per-show shelf window; 0 = 3
PreloadHistoryDays int `json:"preloadHistoryDays,omitempty"` // 0 = 90
// Mode: empty/demand retains fetched bytes; adaptive favours replayed titles;
// off bypasses persistent media writes. Zero budget remains unlimited.
Mode string `json:"mode,omitempty"`
RetentionDays int `json:"retentionDays,omitempty"`
ReplayBoostHours int `json:"replayBoostHours,omitempty"`
DisableSpeculation bool `json:"disableSpeculation,omitempty"`
DetailWarmup string `json:"detailWarmup,omitempty"` // empty/on or off
DetailBytes int64 `json:"detailBytes,omitempty"` // zero warms 40s of source media, capped at 256 MiB
Dir string `json:"dir"`
BudgetBytes int64 `json:"budgetBytes"`
// Prefetch
PrefetchNextEpisode bool `json:"prefetchNextEpisode"`
PrefetchBytes int64 `json:"prefetchBytes"` // opening bytes pulled ahead
KeepInProgressWarm bool `json:"keepInProgressWarm"`
NextEpisodeAt float64 `json:"nextEpisodeAt"` // fraction of current episode, e.g. 0.8
// InProgressPinMaxAge: an in-progress title nobody has watched for this
// long stops pinning its release in the cache (0 = default 30 days).
InProgressPinMaxAge Duration `json:"inProgressPinMaxAge,omitempty"`
// InProgressPinPercent caps the share of BudgetBytes that in-progress
// pins may hold; the least recently watched titles lose their pin first
// (0 = default 50, max 100).
InProgressPinPercent int `json:"inProgressPinPercent,omitempty"`
}
type Catalog ¶
type Catalog struct {
// ShowUpcomingEpisodes adds metadata-only scheduled episodes to airing seasons.
ShowUpcomingEpisodes bool `json:"showUpcomingEpisodes"`
// GateMoviesByHomeRelease waits for a worldwide digital or physical release.
GateMoviesByHomeRelease bool `json:"gateMoviesByHomeRelease"`
// HideFranchiseCollections hides automatic TMDB groups from Plex's
// Collections tab. The zero value preserves existing libraries.
HideFranchiseCollections bool `json:"hideFranchiseCollections"`
// VisibilityDefault is what a title does when no exclusion rule matches:
// "show" or "hide". Empty means show, which keeps saved exclusion lists
// (hide rules, first match) identical to the previous OR of groups.
VisibilityDefault string `json:"visibilityDefault,omitempty"`
Exclusions []core.LibraryExclusion `json:"exclusions,omitempty"`
TMDBKey string `json:"tmdbKey"` // owner override; empty uses the bundled key
Language string `json:"language"` // TMDB language, e.g. "en-US"
Region string `json:"region"`
MinPopularity float64 `json:"minPopularity"`
MinVotes int `json:"minVotes"`
IncludeAdult bool `json:"includeAdult"`
RefreshInterval Duration `json:"refreshInterval"`
}
type ClientCompatibility ¶ added in v0.3.0
type ClientCompatibility struct {
Overrides []core.ClientCapabilityOverride `json:"overrides"`
}
ClientCompatibility stores owner edits of the capability registry. An empty list means the embedded defaults. A client declaration still wins.
type Config ¶
type Config struct {
// Frontends selects protocol listeners by their registered ID.
Frontends map[string]Frontend `json:"frontends,omitempty"`
// FrontendPorts are node-local listener port overrides.
FrontendPorts map[string]int `json:"frontendPorts,omitempty"`
Posters Posters `json:"posters"`
Availability Availability `json:"availability"`
// SharedCache is the cluster-wide NAS policy. Mount paths stay in Cache.
Server Server `json:"server"`
Plex Plex `json:"plex"`
Usenet Usenet `json:"usenet"`
Indexers []Indexer `json:"indexers"`
// Prowlarr is the saved connection for indexer sync. Empty means the
// owner has not saved one. The API key is a secret, redacted like indexer keys.
Prowlarr Prowlarr `json:"prowlarr,omitempty"`
Cache Cache `json:"cache"`
Catalog Catalog `json:"catalog"`
Resolver Resolver `json:"resolver"`
Quality Quality `json:"quality"`
Transcode Transcode `json:"transcode"`
Trakt Trakt `json:"trakt"`
Simkl Simkl `json:"simkl"`
OpenSubtitles OpenSubtitles `json:"openSubtitles"`
LLM LLM `json:"llm"`
Updates Updates `json:"updates"`
Spike Spike `json:"spike"`
Downloads Downloads `json:"downloads"`
Markers Markers `json:"markers"`
Personalize Personalize `json:"personalize"`
Extras Extras `json:"extras"`
Debrid Debrid `json:"debrid"`
// Clients holds owner overrides of the capability registry.
Clients ClientCompatibility `json:"clients"`
// Database is node-local storage selection (SQLite or PostgreSQL).
Database Database `json:"database"`
IndexerSites IndexerSites `json:"indexerSites"`
// Outbound is this computer's optional proxy for source traffic.
Outbound Outbound `json:"outbound"`
SetupComplete bool `json:"setupComplete"`
}
func MergeShared ¶
MergeShared returns local with every shared setting taken from the shared JSON document (as produced by SharedJSON). Shared settings the document lacks (written by an older version) take their defaults; node-local keys in the document are ignored.
func Read ¶ added in v0.2.4
Read loads dataDir's settings without creating or rewriting anything: no directory, no credential key and no re-sealed config.json, unlike Open. It is for other processes (the tray) that only look at the server's settings and must not race its writes. Sealed credentials need the existing credential key.
func (Config) EffectiveCache ¶ added in v0.2.4
EffectiveCache combines the shared pool policy with this node's mount path.
func (*Config) SetCachePolicy ¶ added in v0.2.4
SetCachePolicy updates the shared policy only when this node joins the NAS pool.
type Database ¶
type Database struct {
// Driver is "sqlite" (default) or "postgres".
Driver string `json:"driver,omitempty"`
// URL is the PostgreSQL connection URL, e.g.
// postgres://ether:secret@db.lan:5432/ether?sslmode=verify-full
URL string `json:"url,omitempty"`
// DeferMigrations makes this node wait, at startup, instead of applying
// new schema migrations to the shared database while another live node
// runs a version of Ether that does not know them. Off by default:
// migrations are additive, so older nodes keep working on a newer
// schema and a rolling update never stalls on it.
DeferMigrations bool `json:"deferMigrations,omitempty"`
}
Database is this node's storage backend. It is node-local: a standalone server keeps SQLite in its data directory; a cluster node points at the owner-supplied PostgreSQL database. The URL carries credentials, so it is sealed in config.json like other secrets and must only ever be logged through Redacted.
func (Database) Effective ¶
Effective applies the ETHER_DATABASE_URL override (read through getenv) and normalises the driver name.
func (Database) IsPostgres ¶
IsPostgres reports whether these (effective) settings select PostgreSQL.
type Debrid ¶
type Debrid struct {
Providers []DebridProvider `json:"providers"`
// CachedOnly accepts a torrent only when a provider can serve it
// instantly. When false, an uncached torrent is left on the account to
// download and the release is skipped for now; playback never waits.
CachedOnly bool `json:"cachedOnly"`
// RemoveUncached deletes torrents Ether added that turned out not to be
// cached, keeping the owner's account clean (CachedOnly only).
RemoveUncached bool `json:"removeUncached"`
// ChunkBytes caps one HTTP range request (0 = 8 MiB). Requests start
// small for a fast first byte and grow towards this size.
ChunkBytes int64 `json:"chunkBytes,omitempty"`
// ReadAheadChunks fixes the sequential read-ahead window in chunks
// (0 = adaptive to measured throughput; negative disables read-ahead).
ReadAheadChunks int `json:"readAheadChunks,omitempty"`
// MaxConnections caps concurrent range requests per open file (0 = 4).
MaxConnections int `json:"maxConnections,omitempty"`
}
Debrid configures owner-supplied debrid accounts. Torrent releases from Torznab indexers are added to a debrid service that already holds them and streamed over HTTPS range requests; nothing is downloaded locally.
func (Debrid) EnabledProviders ¶
EnabledProviders reports whether any provider is enabled with a key.
type DebridProvider ¶
type DebridProvider struct {
ID string `json:"id"`
Name string `json:"name"`
Kind string `json:"kind"` // realdebrid, torbox, alldebrid, premiumize, debridlink, offcloud
APIKey string `json:"apiKey"`
Priority int `json:"priority"` // lower is tried first
Enabled bool `json:"enabled"`
}
DebridProvider is one debrid account.
type Downloads ¶
type Downloads struct {
Enabled bool `json:"enabled"`
// AllowFriends lets shared (non-owner) users download. Off by
// default, matching PMS sharing where sync is a separate grant; an
// AccessPolicy AllowDownloads decision overrides this per user.
AllowFriends bool `json:"allowFriends"`
// MaxConcurrent bounds simultaneous remuxes server-wide (default 2),
// MaxConcurrentPerUser per viewer (default 1).
MaxConcurrent int `json:"maxConcurrent"`
MaxConcurrentPerUser int `json:"maxConcurrentPerUser"`
// DiskBudgetBytes bounds the temporary remuxed files on disk at any
// time (default 20 GiB). 0 uses the default.
DiskBudgetBytes int64 `json:"diskBudgetBytes"`
// Expiry is how long a finished download stays fetchable before its
// temp file is deleted (default 24h).
Expiry Duration `json:"expiry"`
// MaxHeight caps the download profile's resolution (default 1080).
MaxHeight int `json:"maxHeight"`
}
Downloads controls Plex offline downloads (allowSync + the Download Queue API). A download resolves a compact mobile-native release and remuxes it (stream copy) to a temporary MP4; nothing is stored permanently. Enabled by default for the owner: it is bounded by the disk budget, the concurrency caps and the normal stream limits, and the owner can turn it off entirely here.
type Duration ¶
Duration marshals as a Go duration string ("6h").
func (Duration) MarshalJSON ¶
func (*Duration) UnmarshalJSON ¶
type Extras ¶
type Extras struct {
// Enabled turns extras (and Cinema Trailers) on.
Enabled bool `json:"enabled"`
// ResolverCommand is the path (or PATH name) of a yt-dlp-compatible
// tool that prints a direct media URL for a video page. Empty disables
// the command source. Ether ships no resolver binary.
ResolverCommand string `json:"resolverCommand,omitempty"`
// ResolverArgs is the fixed argument template passed to
// ResolverCommand. The placeholders {url}, {site}, {key} and {id} are
// substituted; the command always runs without a shell. Empty uses a
// yt-dlp-compatible default ("-g -f b[ext=mp4]/b --no-playlist {url}").
ResolverArgs []string `json:"resolverArgs,omitempty"`
// ResolverTimeout bounds one resolver invocation (default 20s).
ResolverTimeout Duration `json:"resolverTimeout,omitempty"`
// PrewarmBytes is how much of the feature to pre-buffer while a
// trailer plays (clamped to 32–64 MiB, default 48 MiB).
PrewarmBytes int64 `json:"prewarmBytes,omitempty"`
}
Extras controls trailers and other clips attached to titles (Plex "Extras" and Cinema Trailers). TMDB only provides YouTube/Vimeo keys, which Plex apps cannot play, so a playable source must be configured by the owner as a yt-dlp-compatible resolver command. Off by default; with no working command Ether advertises no extras at all.
func (Extras) PrewarmBytesOr ¶
PrewarmBytesOr returns the trailer pre-buffer size, clamped to 32–64 MiB.
type FieldSealer ¶ added in v0.2.4
type FieldSealer interface {
SealField(plain string, context ...string) (string, error)
OpenField(stored string, context ...string) (plain string, legacy bool, err error)
}
FieldSealer protects secrets stored outside config.json (*Store implements it).
type Frontend ¶ added in v0.3.0
type Frontend struct {
Enabled bool `json:"enabled"`
Settings map[string]any `json:"settings,omitempty"`
}
Frontend holds the enabled state of a protocol front end.
type Indexer ¶
type Indexer struct {
ID string `json:"id"`
Name string `json:"name"`
URL string `json:"url"` // base URL; /api is appended
APIKey string `json:"apiKey"`
DailyLimit int `json:"dailyLimit"` // optional personal API cap per UTC day; 0 = automatic service limits
DailyGrabs int `json:"dailyGrabs"` // optional personal NZB cap per UTC day; 0 = automatic service limits
Priority int `json:"priority"`
Enabled bool `json:"enabled"`
// Categories override; empty uses Newznab defaults (2000 movies, 5000 TV).
MovieCategories []int `json:"movieCategories"`
TVCategories []int `json:"tvCategories"`
// Protocol is "usenet" (Newznab, the default when empty) or "torrent"
// (Torznab; results stream through a debrid provider).
Protocol string `json:"protocol,omitempty"`
// Kind selects how the indexer is searched: "" or IndexerKindNewznab
// for a Newznab/Torznab API at URL, IndexerKindCardigann for a built-in
// site run from an owner-installed definition, IndexerKindStremio for a
// Stremio stream addon. For a built-in site URL is the site address and
// Protocol follows the definition; for an addon URL is its install URL
// and results are torrents.
Kind string `json:"kind,omitempty"`
// Definition is the installed definition id of a built-in site.
Definition string `json:"definition,omitempty"`
// Settings are the definition's setting values by name.
Settings map[string]string `json:"settings,omitempty"`
// SecretSettings are the definition's password-type settings. They are
// sealed at rest and redacted in API responses like API keys.
SecretSettings map[string]string `json:"secretSettings,omitempty"`
}
func (Indexer) IsStremio ¶ added in v0.2.17
IsStremio reports whether the indexer is a Stremio stream addon.
func (Indexer) IsTorrent ¶
IsTorrent reports whether the indexer's results are torrents (Torznab or a Stremio addon).
func (Indexer) UsesBackend ¶ added in v0.2.17
UsesBackend reports whether the indexer is searched through a backend rather than the Newznab API: a built-in site or a Stremio addon.
type IndexerCap ¶ added in v0.3.0
IndexerCap is one indexer's own limit. DailyLimit 0 means the indexer reports no daily cap ("unlimited"). PageMax is the caps document's page size; 0 means it was not reported.
type IndexerSites ¶
type IndexerSites struct {
// DefinitionsURL is an optional source of definitions the owner trusts
// and has the right to use: a GitHub contents API URL, a GitHub folder
// or raw folder URL, or a plain list of definition URLs. Empty = none;
// definitions are then added only by upload.
DefinitionsURL string `json:"definitionsUrl,omitempty"`
// FlareSolverrURL is an optional FlareSolverr service used to pass
// Cloudflare browser challenges, e.g. http://localhost:8191.
FlareSolverrURL string `json:"flareSolverrUrl,omitempty"`
}
IndexerSites configures built-in indexer sites: owner-installed definitions in the Cardigann YAML format, run by internal/cardigann. Ether bundles no definitions and no definitions source.
type LLM ¶
type LLM struct {
Enabled bool `json:"enabled"` // off by default
// Provider is an LLMProviders ID; empty means LLMProviderCustom.
Provider string `json:"provider"`
// BaseURL overrides the provider's endpoint for local servers and is
// required for a custom OpenAI-compatible endpoint, e.g.
// http://localhost:11434/v1. Hosted providers ignore it.
BaseURL string `json:"baseUrl"`
APIKey string `json:"apiKey"`
Model string `json:"model"`
// Speech is an OpenAI-compatible transcription endpoint (Whisper).
// It is independent of Enabled: subtitle-from-audio uses it only when
// an owner turns that feature on and a viewer asks for the track.
SpeechBaseURL string `json:"speechBaseUrl,omitempty"`
SpeechAPIKey string `json:"speechApiKey,omitempty"`
SpeechModel string `json:"speechModel,omitempty"`
}
func (LLM) Endpoint ¶ added in v0.2.2
func (l LLM) Endpoint() (LLMProvider, string)
Endpoint resolves the provider and the base URL requests go to: the owner's BaseURL for local and custom servers when set, otherwise the provider default. An unknown provider resolves to an empty base URL.
type LLMProvider ¶ added in v0.2.2
type LLMProvider struct {
ID string `json:"id"`
Label string `json:"label"`
// API is the wire protocol (LLMAPIOpenAI, LLMAPIAnthropic, LLMAPIGoogle).
API string `json:"api"`
// BaseURL is the default endpoint; empty for LLMProviderCustom.
BaseURL string `json:"baseUrl"`
// NeedsKey is set for hosted services that reject anonymous calls.
NeedsKey bool `json:"needsKey"`
// Local servers run on the owner's network, so their endpoint is
// editable (BaseURL is only the usual default).
Local bool `json:"local"`
// RequiresBaseURL is set when the owner must supply the endpoint.
RequiresBaseURL bool `json:"requiresBaseUrl"`
// KeyHint is the API-key placeholder; ModelHint explains model IDs.
KeyHint string `json:"keyHint,omitempty"`
ModelHint string `json:"modelHint,omitempty"`
}
LLMProvider is one language-model service the owner can pick. The hosted entries mirror @braedonsaunders/appkit-ai's AI_PROVIDER_SPECS (same IDs, labels and endpoints) so Ether and AppKit apps configure providers the same way; Ollama and LM Studio are Ether's local additions.
func LookupLLMProvider ¶ added in v0.2.2
func LookupLLMProvider(id string) (LLMProvider, bool)
LookupLLMProvider finds a provider by ID; empty means LLMProviderCustom.
type Markers ¶
type Markers struct {
TheIntroDB bool `json:"theIntroDb"`
APIKey string `json:"apiKey"`
// SkipIntro and SkipCredits turn the skip buttons off.
// A missing value keeps the button, which is the existing behaviour.
SkipIntro *bool `json:"skipIntro,omitempty"`
SkipCredits *bool `json:"skipCredits,omitempty"`
}
func (Markers) OfferCredits ¶ added in v0.3.0
OfferCredits reports whether the credits skip button is offered.
func (Markers) OfferIntro ¶ added in v0.3.0
OfferIntro reports whether intro and recap skip buttons are offered.
type Notifier ¶
type Notifier interface {
Publish(ctx context.Context, topic string, payload []byte) error
Subscribe(topic string, fn func([]byte)) (unsubscribe func())
}
Notifier is the cluster event bus as the config store uses it. A nil Notifier means no cross-node push; polling still picks changes up.
type OpenSubtitles ¶
type Outbound ¶ added in v0.3.0
type Outbound struct {
Enabled bool `json:"enabled"`
// Preset is "gluetun", "custom", or empty. It only affects the owner
// console; routing uses URL.
Preset string `json:"preset,omitempty"`
URL string `json:"url"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
// Source routes default on. Metadata stays off until the owner opts in.
// Plex, updates, UPnP and remote access are never routed here.
Indexers bool `json:"indexers"`
Stremio bool `json:"stremio"`
Debrid bool `json:"debrid"`
Usenet bool `json:"usenet"`
Metadata bool `json:"metadata"`
// Bypass are extra hostnames that stay on the normal route.
// Loopback and literal private addresses always do.
Bypass []string `json:"bypass,omitempty"`
}
Outbound is this computer's optional proxy for source traffic. It is node-local and off by default. While it is off, Ether dials the way it always has. Passwords are sealed with the other secrets; the URL must not carry them.
func (Outbound) Problems ¶ added in v0.3.0
func (o Outbound) Problems() []OutboundProblem
Problems reports validation errors. A disabled proxy may keep a blank address. A non-empty address is checked either way, and an enabled proxy requires one. Messages never include the address or the password.
type OutboundProblem ¶ added in v0.3.0
OutboundProblem is one owner-fixable problem with an outbound proxy.
type Personalize ¶
type Personalize struct {
// PostPlay ranks the post-play "hook queue" per viewer instead of
// returning generic related rows (#30). The ranked endpoint itself is
// cheap; the switch exists for owners who prefer plain PMS behaviour.
PostPlay bool `json:"postPlay"`
// PostPlayPrebuffer pre-warms and pre-buffers the #1 post-play pick in
// the last minutes of a movie or finale (#30).
PostPlayPrebuffer bool `json:"postPlayPrebuffer"`
// MicroReasons decorates single-item detail pages with the viewer's
// decision header ("96% Match · … · Ends at 10:45 PM") (#31).
MicroReasons bool `json:"microReasons"`
// SessionIntent adapts /hubs to what the viewer is browsing tonight
// (detail opens that did not turn into playback) (#32).
SessionIntent bool `json:"sessionIntent"`
// ClockFormat is the finish-time style of the decision header:
// "12h", "24h", or "" to derive it from Catalog.Language.
ClockFormat string `json:"clockFormat"`
}
Personalize holds the owner's switches for the Netflix-style per-viewer hooks (roadmap items #30–#32). All are on by default (see Defaults); a config written before this section existed keeps those defaults because loading unmarshals over them.
type Plex ¶
type Plex struct {
CompatibilityVersion string `json:"compatibilityVersion,omitempty"`
LatestCompatibilityVersion string `json:"latestCompatibilityVersion,omitempty"`
// TrackLatestVersion advertises the newest public PMS release instead of
// the version Ether's responses were verified against. Plex apps enable
// features by server version, so tracking can reach endpoints Ether lacks.
TrackLatestVersion bool `json:"trackLatestVersion,omitempty"`
// Deprecated: retained for config compatibility; latest tracking is now opt-in.
DisableVersionTracking bool `json:"disableVersionTracking,omitempty"`
// Token is the owner's plex.tv token from sign-in; the server token.
Token string `json:"token"`
ClientIdentifier string `json:"clientIdentifier"` // X-Plex-Client-Identifier Ether uses towards plex.tv
OwnerPlexID int64 `json:"ownerPlexId"`
OwnerUsername string `json:"ownerUsername"`
Claimed bool `json:"claimed"`
// UsePlexMetadata serves cached Plex metadata-provider fields to Plex
// apps when a title has a verified plex:// guid. Off by default.
// Other frontends and the owner console keep TMDB. Viewer limits use
// the safer (more mature) of the TMDB and Plex content ratings; Plex
// apps still display Plex's own rating. Date-added stays the catalogue
// release/air date.
UsePlexMetadata bool `json:"usePlexMetadata,omitempty"`
// Remote access
RemoteAccess bool `json:"remoteAccess"`
UseUPnP bool `json:"useUpnp"`
PublicPort int `json:"publicPort"` // manual port forward; 0 = same as Server.Port
PublicHost string `json:"publicHost"` // override detected WAN IP
CustomURLs []string `json:"customUrls"`
// TLS certificate for *.plex.direct (or a self-signed fallback).
CertFile string `json:"certFile"`
KeyFile string `json:"keyFile"`
CertHash string `json:"certHash"` // plex.direct subdomain hash
SecureConnections string `json:"secureConnections"` // "required", "preferred", "disabled"
}
type PolicySnapshot ¶ added in v0.3.0
type PolicySnapshot struct {
// contains filtered or unexported fields
}
PolicySnapshot is an immutable configuration copy plus the clustered version observed when it was taken. It does not hold the policy lock: a browse path must not stall a writer. SharedVersion may already be stale after a remote write. A durable authorization must recheck that version inside its epoch-fenced transaction before publishing. Close is idempotent and does not authorize any operation.
func (*PolicySnapshot) Close ¶ added in v0.3.0
func (p *PolicySnapshot) Close()
func (*PolicySnapshot) Config ¶ added in v0.3.0
func (p *PolicySnapshot) Config() Config
Config returns a detached copy. It is meaningful for authorization only until Close and while the durable shared-version and epoch checks succeed.
func (*PolicySnapshot) SharedVersion ¶ added in v0.3.0
func (p *PolicySnapshot) SharedVersion() (int64, bool)
type Posters ¶ added in v0.2.34
type Posters struct {
Enabled bool `json:"enabled"`
Source string `json:"source"`
BaseURL string `json:"baseUrl"`
APIKey string `json:"apiKey"`
URLTemplate string `json:"urlTemplate"`
Profile string `json:"profile"`
}
Posters configures optional owner-supplied artwork. Templates are secrets: exported provider URLs can embed account configuration and credentials.
type Prowlarr ¶
type Prowlarr struct {
URL string `json:"url"`
APIKey string `json:"apiKey"`
DailyLimit int `json:"dailyLimit"`
DailyGrabs int `json:"dailyGrabs"`
Priority int `json:"priority"`
// Tags are Prowlarr tag ids from /api/v1/tag. Empty syncs every enabled
// Usenet and torrent indexer (the same import as before tags existed).
// With ids set, only indexers carrying one of them are synced.
Tags []int `json:"tags,omitempty"`
}
Prowlarr is an owner-managed source of Newznab indexers. Discovery is explicit and does not search or download from the indexers.
type Quality ¶
type Quality struct {
// Profiles by name. Rules pick a profile per client.
Profiles []core.QualityProfile `json:"profiles"`
Rules []QualityRule `json:"rules"`
Default string `json:"default"`
Remote string `json:"remote"` // profile for remote clients, "" = same as default
}
func DefaultQuality ¶
func DefaultQuality() Quality
type QualityRule ¶
type QualityRule struct {
Protocol core.Protocol `json:"protocol,omitempty"`
Product string `json:"product,omitempty"` // substring of X-Plex-Product
Platform string `json:"platform,omitempty"` // substring of X-Plex-Platform
UserID int64 `json:"userId,omitempty"`
Profile string `json:"profile"`
MaxHeight int `json:"maxHeight,omitempty"` // optional max-height override (2160, 1080, 720, 480; 0 = profile ceiling)
ConvertSurroundAudio bool `json:"convertSurroundAudio,omitempty"` // convert Dolby/DTS/TrueHD audio to AAC for players that do not decode surround formats
}
QualityRule selects a profile when all non-empty matchers match.
type RTXVideo ¶ added in v0.2.4
type RTXVideo struct {
Mode string `json:"mode"` // off, auto (LAN), on (also remote)
ExecutablePath string `json:"executablePath"` // optional NVEncC64.exe, otherwise managed runtime or PATH
SourceMaxHeight int `json:"sourceMaxHeight"` // default 720
TargetMaxHeight int `json:"targetMaxHeight"` // default 1080, maximum 2160
Quality int `json:"quality"` // default 2, NGX quality 1..4 or streaming 21/23 (Ampere+); 0 uses default
MaxConcurrent int `json:"maxConcurrent"` // default 1, maximum 8
}
RTXVideo controls the optional NVEncC RTX Video backend. Empty mode is off. Enabling installs the pinned runtime on supported Windows nodes when no executable is configured. Paths remain node-local. Numeric zero values use the documented defaults; invalid values disable enhancement rather than silently substituting a non-AI scaler.
type Repair ¶ added in v0.2.8
type Repair struct {
AdaptiveSummaries bool `json:"adaptiveSummaries"`
Enabled bool `json:"enabled"`
WholeSlices bool `json:"wholeSlices,omitempty"`
SummaryRows int `json:"summaryRows"`
MaxDownloadBytes int64 `json:"maxDownloadBytes"`
LiveWait Duration `json:"liveWait"`
AheadWait Duration `json:"aheadWait"`
}
Repair bounds opportunistic PAR2 reconstruction. Disabled until the owner opts in; the live benchmark enables it explicitly without editing config.
type Resolver ¶
type Resolver struct {
HealthRace int `json:"healthRace"` // 0 = ranked checks; >0 = first healthy of bounded concurrent candidates
OptimisticStart bool `json:"optimisticStart"` // return top NZB before background health check
CustomFormats []core.CustomFormat `json:"customFormats,omitempty"`
CustomFormatScores map[string]int `json:"customFormatScores,omitempty"`
MinCustomFormatScore *int `json:"minCustomFormatScore,omitempty"`
HealthSamples int `json:"healthSamples"`
SearchCacheTTL Duration `json:"searchCacheTtl"`
MaxCandidates int `json:"maxCandidates"`
// SameQualityAlternatives is how many other releases of the same cut,
// resolution and HDR type a detail page lists after Auto as Play
// Versions (0 = none, at most 3; default 1).
SameQualityAlternatives int `json:"sameQualityAlternatives"`
// OfferHigherVersions lets Play Version list releases taller than the
// profile ceiling (a 4K copy on a 1080p profile). Off, the default,
// keeps that ceiling: height, HDR policy and size limits.
OfferHigherVersions bool `json:"offerHigherVersions"`
BlockedGroups []string `json:"blockedGroups"`
BlockedTerms []string `json:"blockedTerms"`
// SourcePreference orders releases of equivalent quality by source:
// "auto" (or empty) puts the source that measurably starts faster
// first, and cached debrid first for popular titles until both
// sources have measurements; "usenet" or "debrid" always prefer one.
// Quality is never traded for speed (see resolver/routing.go).
SourcePreference string `json:"sourcePreference,omitempty"`
// SourceScoreMargin is how far below the leader's score a release of
// the same quality tier may be and still be moved first for its
// source (0 = 25).
SourceScoreMargin float64 `json:"sourceScoreMargin,omitempty"`
}
type Scope ¶
type Scope int
Scope says where a setting lives when nodes form a cluster.
A standalone server keeps every setting in config.json. In a cluster the shared part lives in the database (one document every node reads) and the node-local part stays in each node's config.json. The classification is expressed over the JSON keys of Config so that it can split and merge the documents without reflection; TestScopeClassifiesEveryField fails when a field is added without being classified here.
type Server ¶
type Server struct {
// TrustedProxyCIDRs are proxy peer networks whose forwarding headers are trusted.
TrustedProxyCIDRs []string `json:"trustedProxyCIDRs,omitempty"`
// Port for the Plex API and web UI. PMS uses 32400.
Port int `json:"port"`
BindAddress string `json:"bindAddress"`
FriendlyName string `json:"friendlyName"`
// MoviesLibraryName is the Movies section title shown in Plex apps.
// Empty falls back to "Movies".
MoviesLibraryName string `json:"moviesLibraryName"`
// TVShowsLibraryName is the TV Shows section title shown in Plex apps.
// Empty falls back to "TV Shows".
TVShowsLibraryName string `json:"tvShowsLibraryName"`
// MachineIdentifier is generated on first run and never changes.
MachineIdentifier string `json:"machineIdentifier"`
LogLevel string `json:"logLevel"`
// DefaultStreamLimit applies to users without their own limit.
DefaultStreamLimit int `json:"defaultStreamLimit"`
MaxStreams int `json:"maxStreams"` // server-wide cap, 0 = derived from Usenet connections
// ClusterWeight is this node's weight when a cluster divides
// account-wide budgets (Usenet provider connections) among live nodes;
// a node with weight 2 gets twice the share of a node with weight 1.
// 0 means 1. Node-local; ignored on a standalone server.
ClusterWeight int `json:"clusterWeight,omitempty"`
// WebFrameOrigins permits embedding the owner UI in these exact HTTP(S)
// origins (for example a dashboard). Empty denies framing everywhere.
WebFrameOrigins []string `json:"webFrameOrigins,omitempty"`
}
type SharedBackend ¶
type SharedBackend interface {
// has been stored yet.
LoadShared(ctx context.Context) (doc SharedDoc, found bool, err error)
// nothing stored yet) and returns the new version, or
// ErrSharedConflict.
SaveShared(ctx context.Context, doc SharedDoc, expectVersion int64) (int64, error)
SharedVersion(ctx context.Context) (int64, error)
}
SharedBackend stores the shared settings document (implemented over the database by internal/clusterconfig).
type SharedOptions ¶
type SharedOptions struct {
// SetNotifier. Nil: polling only.
Notifier Notifier
// DefaultSharedPoll; negative disables polling and the watcher).
PollInterval time.Duration
Timeout time.Duration
// NoSeed makes AttachShared fail with ErrSharedMissing instead of
// seeding an empty database from this node's settings.
}
SharedOptions configure AttachShared.
type Spike ¶
Spike serves local files as a library so the Plex front end can be verified against official apps without Usenet.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store loads, saves and broadcasts the config.
func (*Store) AcquirePolicy ¶ added in v0.3.0
AcquirePolicy keeps local policy stable and refuses a cluster cache whose shared document has not caught up with the authoritative database version.
func (*Store) AcquirePolicySnapshot ¶ added in v0.3.0
func (s *Store) AcquirePolicySnapshot(ctx context.Context) (*PolicySnapshot, error)
func (*Store) AttachShared ¶
func (s *Store) AttachShared(ctx context.Context, b SharedBackend, o SharedOptions) error
AttachShared switches the store to cluster mode: it loads the shared settings from b (seeding b from this node's settings when it holds none, unless NoSeed) and overlays them on the node-local ones, refusing with ErrKeyMismatch when the cluster was sealed with another credential key. Until ctx ends it then follows changes made by other nodes.
func (*Store) Close ¶
func (s *Store) Close()
Close stops following other nodes' changes (the watcher started by AttachShared and the notifier subscriptions) and waits for an in-flight refresh to finish. The store keeps working; it no longer refreshes.
func (*Store) CredentialKey ¶
CredentialKey returns a copy of this node's credential key. Only the cluster join-token issuer needs it; never log or store it.
func (*Store) CredentialsUpgraded ¶ added in v0.2.4
CredentialsUpgraded reports whether this node completed UpgradeCredentials (config.json is written in v2).
func (*Store) DerivedKey ¶ added in v0.3.0
DerivedKey is an HMAC of the unexported credential key. Callers use it as a node-shared secret without receiving the credential key itself.
func (*Store) Generation ¶ added in v0.3.0
Generation changes when the live config is replaced. It does not change when a save is rolled back, so a snapshot taken at this value stays valid.
func (*Store) KeyCreated ¶ added in v0.2.4
KeyCreated reports whether Open generated this data directory's credential key, i.e. nothing stored before this start was sealed with it. Database rows sealed under another key mean the key file was lost.
func (*Store) KeyFingerprint ¶
KeyFingerprint identifies this node's credential key.
func (*Store) OpenField ¶ added in v0.2.4
OpenField reverses SealField for the same context. A value stored unsealed (written before the upgrade) is returned as is with legacy set, so the caller can re-seal it. A sealed value that does not open — wrong key, damaged, or moved to another context — is an error.
func (*Store) Read ¶ added in v0.3.0
Read returns the live config without cloning it. Callers must not mutate the result or anything it points at. Browse paths use it so a page miss does not marshal the whole config.
func (*Store) RefreshShared ¶
RefreshShared applies the stored shared settings if another node changed them, notifying subscribers. It is a no-op on a standalone server.
func (*Store) SealField ¶ added in v0.2.4
SealField protects plain for storage, bound to context. Empty values stay empty. Until the store seals fields (SealsFields) it returns plain unchanged.
func (*Store) SealsFields ¶ added in v0.2.4
SealsFields reports whether SealField seals (database fields and shared settings are written in v2), which can precede CredentialsUpgraded on a cluster node that loaded settings another node upgraded.
func (*Store) SetNotifier ¶
SetNotifier connects (or, with nil, disconnects) the cluster event bus. It may be called before or after AttachShared.
func (*Store) SetPolicyEpoch ¶ added in v0.3.0
SetPolicyEpoch binds durable invalidation after a visibility configuration transition. The lease excludes local publication through receipt commit.
func (*Store) SharedVersion ¶
SharedVersion is the version of the shared settings this node holds (0 on a standalone server).
func (*Store) TrustedProxyCIDRs ¶ added in v0.3.0
TrustedProxyCIDRs is the live trusted-proxy list. The returned slice is a copy, so a caller cannot change the stored configuration.
func (*Store) Update ¶
Update applies fn to a copy of the config, saves it and notifies subscribers.
On a cluster node the shared part is written to the database with an optimistic version check; when another node changed it first, fn is applied again to the fresh settings, so fn must only mutate its argument.
func (*Store) UpgradeCredentials ¶ added in v0.2.4
UpgradeCredentials switches the store to v2 credentials (see above): config.json is rewritten in v2 and, on a cluster node, the shared settings are re-sealed in v2 unless another node already did. It is idempotent and resumable: config.json is rewritten atomically when the store switches (a configuration opened in v2 already has), and the shared settings are checked on every call, so a call interrupted between the two steps completes on the next.
type Transcode ¶
type Transcode struct {
RTXVideo RTXVideo `json:"rtxVideo"`
FFmpegPath string `json:"ffmpegPath"` // empty = bundled, then PATH
FFprobePath string `json:"ffprobePath"`
HWAccel string `json:"hwAccel"` // "auto", "none", "videotoolbox", "nvenc", "qsv", "vaapi"
VAAPIDevice string `json:"vaapiDevice"`
TempDir string `json:"tempDir"`
MaxTranscodes int `json:"maxTranscodes"`
RemoteMaxBitrateKbps int `json:"remoteMaxBitrateKbps"`
// Adaptive HLS engine (additive). Numeric zero values select the
// transcode package defaults; see Defaults for the shipped values.
FastStart bool `json:"fastStart"` // short, quick opening segments
FastStartSegments int `json:"fastStartSegments"` // opening segments in the ramp (default 2)
FastStartSegment Duration `json:"fastStartSegment"` // ramp segment length (default 2s)
FastStartMaxHeight int `json:"fastStartMaxHeight"` // ramp encode height cap (default 720)
ABRLadder bool `json:"abrLadder"` // multi-rung master playlist / MPD
ABRLadderUnderCopy bool `json:"abrLadderUnderCopy"` // soft encode rungs under Direct Stream / video-copy (default on; set false to disable)
VBVReservoir bool `json:"vbvReservoir"` // constant-quality + generous VBV for capped transcodes
VBVBufferSeconds int `json:"vbvBufferSeconds"` // VBV buffer depth in seconds of maxrate (default 8)
SpikeGovernor bool `json:"spikeGovernor"` // clamp VBR spikes in copy streams to measured goodput
SpikeMarginPercent int `json:"spikeMarginPercent"` // spike threshold over goodput, percent (default 125)
}
type Updates ¶
type Updates struct {
Channel string `json:"channel"` // "stable", "beta"
AutoUpdate bool `json:"autoUpdate"`
FeedURL string `json:"feedUrl"`
// MaintenanceStart/End bound the local-time window ("HH:MM") in which
// a downloaded update may be installed automatically. The window may
// wrap midnight; equal values mean "any time".
MaintenanceStart string `json:"maintenanceStart"`
MaintenanceEnd string `json:"maintenanceEnd"`
// MaxDrain bounds how long a clustered node about to install an update
// waits for its own playback sessions to end (it admits no new ones
// meanwhile) before installing anyway inside the window. 0 means 30
// minutes. Ignored on a standalone server, which waits for idle.
MaxDrain Duration `json:"maxDrain,omitempty"`
}
type Usenet ¶
type Usenet struct {
Repair Repair `json:"repair"`
Providers []UsenetProvider `json:"providers"`
// MinIdle is how many connections per primary (non-backfill) provider
// are kept warm (dialled, authenticated, DATE-pinged) at all times so
// Play never pays a cold handshake. 0 = default (2); negative = off
// (connections are dialled lazily and expire when idle). A provider's
// own MinIdle overrides this.
MinIdle int `json:"minIdle"`
}
type UsenetProvider ¶
type UsenetProvider struct {
ID string `json:"id"`
Name string `json:"name"`
Host string `json:"host"`
Port int `json:"port"`
TLS bool `json:"tls"`
Username string `json:"username"`
Password string `json:"password"`
Connections int `json:"connections"`
// Priority: lower is tried first. Block accounts set Backfill.
Priority int `json:"priority"`
Backfill bool `json:"backfill"` // only used for articles missing elsewhere
Enabled bool `json:"enabled"`
// MinIdle overrides Usenet.MinIdle for this provider: 0 = inherit the
// global setting, positive = that many warm connections, negative =
// no warm floor for this provider. Backfill providers are never kept
// warm regardless.
MinIdle int `json:"minIdle,omitempty"`
}