Documentation
¶
Overview ¶
Package etherplugin is the Go authoring SDK for Ether Plugin Protocol v1 (EPP/1).
It provides the same declarative router shape, settings decoder, localization helper, host service client, test harness, and capability templates as `@ether/plugin-sdk` (TypeScript) and `ether_plugin` (Python).
Execution modes ¶
Go plugins run as native HTTP or WebSocket tunnel sidecars. Hosted Go is not supported: the standard Go wasip1 toolchain emits WASI imports, which Ether's sandbox rejects. See README.md for details.
Server packages under `internal/` and `cmd/` must not import this package.
Index ¶
- Constants
- Variables
- func AssertValidViewSpec(doc ViewSpecDocument) error
- func DecodeSettings(schema *SettingsSchema, incoming map[string]any, defaults map[string]any) (map[string]any, map[string]string)
- func LintCopy(raw string, maxRunes int) string
- func StreamURL(itemID string) string
- func TRef(key string) string
- type ActionProposal
- type ActionRequest
- type ActionResponse
- type BehaviorHints
- type CandidateFacts
- type Capability
- type CatalogItem
- type CatalogProvider
- type CatalogRequest
- type CatalogResponse
- type CatalogTitleSummary
- type ChangesProvider
- type ChannelInput
- type ChannelItem
- type ChannelsMeta
- type ChannelsResponse
- type ClientCaps
- type CommandMeta
- type ConfigureResponse
- type ContainerRef
- type Context
- type Contributes
- type Definition
- type EtherEvent
- type EtherManifestBlock
- type EventResponse
- type EventsMeta
- type FieldOption
- type FieldProblem
- type FileRangeResponse
- type FileRequest
- type FolderEntry
- type FrontendAdapter
- type FrontendAdapterMeta
- type GuideRequest
- type GuideResponse
- type HealthDecl
- type HealthResponse
- type Host
- type HostFetchRequest
- type HostFetchResponse
- type I18n
- type IntegrationMeta
- type JobRequest
- type JobResponse
- type KindArt
- type KindArtRef
- type KindBrowseDecl
- type KindBrowseRequest
- type KindCacheDecl
- type KindChangePage
- type KindChangesRequest
- type KindField
- type KindItem
- type KindLevel
- type KindLibraryDecl
- type KindMatchDecl
- type KindMatchRequest
- type KindMatchResponse
- type KindMatchScore
- type KindMatcher
- type KindPage
- type KindResolveRequest
- type KindResolveResponse
- type KindResolver
- type KindSyncDecl
- type ListSourceMeta
- type Manifest
- type MarkerItem
- type MarkersMeta
- type MarkersRequest
- type MarkersResponse
- type MediaKindMeta
- type MetaItem
- type MetaRequest
- type MetaResponse
- type MetaVideo
- type MockHost
- func (m *MockHost) CacheOpen(_ context.Context, blobRef string) ([]byte, error)
- func (m *MockHost) CachePut(_ context.Context, _, _ string, data []byte) (string, error)
- func (m *MockHost) CatalogGet(_ context.Context, id string) (*CatalogTitleSummary, error)
- func (m *MockHost) CatalogSearch(_ context.Context, query string, limit, offset int) ([]CatalogTitleSummary, error)
- func (m *MockHost) Fetch(ctx context.Context, req HostFetchRequest) (HostFetchResponse, error)
- func (m *MockHost) FoldersList(_ context.Context, root, path string) ([]FolderEntry, error)
- func (m *MockHost) FoldersStat(_ context.Context, root, path string) (FolderEntry, error)
- func (m *MockHost) KVDelete(_ context.Context, key string) error
- func (m *MockHost) KVGet(_ context.Context, key string) (string, bool, error)
- func (m *MockHost) KVList(_ context.Context, prefix string) ([]string, error)
- func (m *MockHost) KVPut(_ context.Context, key, value string) error
- func (m *MockHost) KindDelete(_ context.Context, kindKey string, externalIDs []string) error
- func (m *MockHost) KindItems(_ context.Context, kindKey, cursor string, limit int) (KindPage, error)
- func (m *MockHost) KindUpsert(_ context.Context, kindKey string, items []KindItem) (int, error)
- func (m *MockHost) Log(_ context.Context, level, message string, attrs map[string]any) error
- func (m *MockHost) NotifyOwner(_ context.Context, text string) error
- func (m *MockHost) ResolveSources(_ context.Context, _ SourceQuery) ([]SourceCandidate, error)
- func (m *MockHost) SecretGet(_ context.Context, name string) (string, error)
- func (m *MockHost) SetWatchState(_ context.Context, viewerID int64, titleID string, watched bool) error
- func (m *MockHost) UpsertCollection(_ context.Context, collection map[string]any) error
- func (m *MockHost) ViewerHistory(_ context.Context, viewerID int64, limit, offset int) ([]ViewerWatchEntry, error)
- type NotificationMeta
- type NotifyRequest
- type NotifyResponse
- type OpenStreamRequest
- type OpenStreamResponse
- type PageMeta
- type Permissions
- type PlayableSource
- type Plugin
- func New(def Definition) *Plugin
- func NewCatalogTemplate(opts TemplateOptions) *Plugin
- func NewConditionTemplate(opts TemplateOptions) *Plugin
- func NewRankerTemplate(opts TemplateOptions) *Plugin
- func NewScrobblerTemplate(opts TemplateOptions) *Plugin
- func NewSubtitlesTemplate(opts TemplateOptions) *Plugin
- func NewViewTemplate(opts TemplateOptions) *Plugin
- func (p *Plugin) CondMatch(candidate CandidateFacts, operator string, value any) bool
- func (p *Plugin) Dispatch(ctx context.Context, req RawRequest) RawResponse
- func (p *Plugin) DispatchTunnelFrame(ctx context.Context, frame TunnelFrame, maxChunkBytes int) []TunnelFrame
- func (p *Plugin) GetSettings() map[string]any
- func (p *Plugin) Manifest() Manifest
- func (p *Plugin) OnCatalog(kind string, c CatalogProvider)
- func (p *Plugin) OnFrontend(key string, a FrontendAdapter)
- func (p *Plugin) OnMatch(kind string, m KindMatcher)
- func (p *Plugin) OnResolve(kind string, r KindResolver)
- func (p *Plugin) OnSetupTest(t SetupTester)
- func (p *Plugin) RankKey(candidate CandidateFacts, config map[string]any) int64
- func (p *Plugin) Serve(ctx context.Context, opts ServeOptions) error
- func (p *Plugin) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (p *Plugin) SetSettings(incoming map[string]any) (map[string]any, map[string]string)
- func (p *Plugin) Start(ctx context.Context, opts ServeOptions) (*ServeHandle, error)
- type PluginAuthor
- type PluginKind
- type Presentations
- type ProgrammeItem
- type RankingStepMeta
- type RawRequest
- type RawResponse
- type RecordedLog
- type ReleaseHint
- type RemoteHostClient
- func (c *RemoteHostClient) CacheOpen(ctx context.Context, blobRef string) ([]byte, error)
- func (c *RemoteHostClient) CachePut(ctx context.Context, libraryKey, name string, data []byte) (string, error)
- func (c *RemoteHostClient) CatalogGet(ctx context.Context, id string) (*CatalogTitleSummary, error)
- func (c *RemoteHostClient) CatalogSearch(ctx context.Context, query string, limit, offset int) ([]CatalogTitleSummary, error)
- func (c *RemoteHostClient) Fetch(ctx context.Context, r HostFetchRequest) (HostFetchResponse, error)
- func (c *RemoteHostClient) FoldersList(_ context.Context, _, _ string) ([]FolderEntry, error)
- func (c *RemoteHostClient) FoldersStat(_ context.Context, _, _ string) (FolderEntry, error)
- func (c *RemoteHostClient) KVDelete(ctx context.Context, key string) error
- func (c *RemoteHostClient) KVGet(ctx context.Context, key string) (string, bool, error)
- func (c *RemoteHostClient) KVList(ctx context.Context, prefix string) ([]string, error)
- func (c *RemoteHostClient) KVPut(ctx context.Context, key, value string) error
- func (c *RemoteHostClient) KindDelete(ctx context.Context, kindKey string, externalIDs []string) error
- func (c *RemoteHostClient) KindItems(ctx context.Context, kindKey, cursor string, limit int) (KindPage, error)
- func (c *RemoteHostClient) KindUpsert(ctx context.Context, kindKey string, items []KindItem) (int, error)
- func (c *RemoteHostClient) Log(ctx context.Context, level, message string, attrs map[string]any) error
- func (c *RemoteHostClient) NotifyOwner(ctx context.Context, text string) error
- func (c *RemoteHostClient) ResolveSources(ctx context.Context, q SourceQuery) ([]SourceCandidate, error)
- func (c *RemoteHostClient) SecretGet(_ context.Context, _ string) (string, error)
- func (c *RemoteHostClient) SetWatchState(ctx context.Context, viewerID int64, titleID string, watched bool) error
- func (c *RemoteHostClient) SettingsGet(ctx context.Context) (map[string]any, error)
- func (c *RemoteHostClient) UpsertCollection(ctx context.Context, collection map[string]any) error
- func (c *RemoteHostClient) ViewerHistory(ctx context.Context, viewerID int64, limit, offset int) ([]ViewerWatchEntry, error)
- type RemoteHostOptions
- type RouteError
- type Routes
- type RuleConditionMeta
- type RuntimeDecl
- type ScheduleEntry
- type SearchProvider
- type ServeHandle
- type ServeOptions
- type SettingField
- type SettingsGroup
- type SettingsSchema
- type SetupMeta
- type SetupStep
- type SetupTestRequest
- type SetupTestResult
- type SetupTester
- type SlotMeta
- type SourceCandidate
- type SourceMeta
- type SourceQuery
- type StreamEtherExt
- type StreamItem
- type StreamRequest
- type StreamResponse
- type StremioCatalogDecl
- type StremioExtraField
- type StringsCatalog
- type SubtitleDownloadRequest
- type SubtitleDownloadResponse
- type SubtitleItem
- type SubtitlesMeta
- type SubtitlesRequest
- type SubtitlesResponse
- type TemplateOptions
- type TestHarness
- func (h *TestHarness) Action(action string, input map[string]any) (int, ActionResponse, error)
- func (h *TestHarness) Catalog(typ, id string, extra map[string]string) (int, CatalogResponse, error)
- func (h *TestHarness) Configure(settings map[string]any) (int, ConfigureResponse, error)
- func (h *TestHarness) Event(events []EtherEvent) (int, EventResponse, error)
- func (h *TestHarness) File(path, rangeHeader string) RawResponse
- func (h *TestHarness) Health() (int, HealthResponse, error)
- func (h *TestHarness) Manifest() (Manifest, error)
- func (h *TestHarness) Markers(typ, id string) (int, MarkersResponse, error)
- func (h *TestHarness) Meta(typ, id string) (int, MetaResponse, error)
- func (h *TestHarness) Options(field string) (int, []FieldOption, error)
- func (h *TestHarness) Request(req RawRequest) RawResponse
- func (h *TestHarness) Stream(typ, id string, extra map[string]string) (int, StreamResponse, error)
- func (h *TestHarness) Subtitles(typ, id, pickToken string, extra map[string]string) (int, SubtitlesResponse, RawResponse)
- func (h *TestHarness) Test() (int, TestResponse, error)
- func (h *TestHarness) Tool(name string, input map[string]any) (int, ToolResponse, error)
- func (h *TestHarness) View(page string, query map[string]string) (int, ViewResponse, error)
- type TestResponse
- type ToolMeta
- type ToolRequest
- type ToolResponse
- type TunnelError
- type TunnelFrame
- type ViewRequest
- type ViewResponse
- type ViewSpecDocument
- type ViewerPermissions
- type ViewerWatchEntry
- type VolumeRef
Constants ¶
const ( // TunnelProtocol is the WebSocket subprotocol for Ether remote plugin tunnels. TunnelProtocol = "ether-tunnel-v1" // DefaultMaxChunkBytes is the default chunk size for streamed file responses. DefaultMaxChunkBytes = 64 * 1024 )
const ( KindSyncPull = "pull" KindSyncPush = "push" )
Kind sync modes: pull (Ether walks browse/changes) or push (KindUpsert).
Variables ¶
var ( ErrKindNotFound = &RouteError{Status: http.StatusNotFound, Code: "not_found", Message: "Item not found."} ErrKindNotAvailable = &RouteError{Status: http.StatusOK, Code: "not_available", Message: "No source is available right now."} )
Reserved plugin error codes Ether understands on kind routes.
var Fixtures = struct { PickToken func(itemID string) string Movie func() MetaItem CatalogItem func() CatalogItem PlaybackEvent func(name string) EtherEvent Candidate func() CandidateFacts }{ PickToken: func(itemID string) string { if itemID == "" { itemID = "ether:movie:1" } return "pick-v1:" + itemID + ":sig-test" }, Movie: func() MetaItem { return MetaItem{ CatalogItem: CatalogItem{ ID: "tt0133093", Type: "movie", Name: "The Matrix", ReleaseInfo: "1999", Description: "A computer hacker learns about the true nature of reality.", Genres: []string{"Action", "Sci-Fi"}, IMDBRating: "8.7", }, } }, CatalogItem: func() CatalogItem { return CatalogItem{ ID: "tt0133093", Type: "movie", Name: "The Matrix", ReleaseInfo: "1999", } }, PlaybackEvent: func(name string) EtherEvent { if name == "" { name = "playback.completed" } return EtherEvent{ ID: "evt_01", Name: name, At: "2026-11-02T12:00:00Z", ViewerID: 1, Payload: map[string]any{ "titleId": "tt0133093", "title": "The Matrix", "progress": 0.95, }, } }, Candidate: func() CandidateFacts { return CandidateFacts{ ReleaseID: "rel_01", Name: "The.Matrix.1999.2160p.UHD.BluRay.x265", Resolution: 2160, HDR: []string{"DV", "HDR10"}, VideoCodec: "hevc", Cached: true, Verified: true, } }, }
Fixtures provides sample EPP/1 records for plugin unit tests.
Functions ¶
func AssertValidViewSpec ¶
func AssertValidViewSpec(doc ViewSpecDocument) error
AssertValidViewSpec validates a ViewSpecDocument for structural rules and rejects any `*ClassName` fields.
func DecodeSettings ¶
func DecodeSettings( schema *SettingsSchema, incoming map[string]any, defaults map[string]any, ) (map[string]any, map[string]string)
DecodeSettings applies schema defaults and validates incoming settings, returning the decoded settings map and any JSON-pointer keyed errors.
func LintCopy ¶
LintCopy validates a user-visible string against Ether's copy rules. It returns an empty string when valid, or the plain reason when invalid.
Types ¶
type ActionProposal ¶
type ActionProposal struct {
Category string `json:"category"`
Description string `json:"description"`
Input map[string]any `json:"input,omitempty"`
}
ActionProposal describes a confirmation proposal returned by an Action handler.
type ActionRequest ¶
ActionRequest is passed to an Action route handler.
type ActionResponse ¶
type ActionResponse struct {
Toast string `json:"toast,omitempty"`
Refresh bool `json:"refresh,omitempty"`
Open string `json:"open,omitempty"`
Proposal *ActionProposal `json:"proposal,omitempty"`
}
ActionResponse is returned by an Action route handler.
type BehaviorHints ¶
type BehaviorHints struct {
Adult bool `json:"adult,omitempty"`
P2P bool `json:"p2p,omitempty"`
Configurable bool `json:"configurable,omitempty"`
ConfigurationRequired bool `json:"configurationRequired,omitempty"`
}
BehaviorHints carries Stremio-compatible behavior hints.
type CandidateFacts ¶
type CandidateFacts struct {
ReleaseID string `json:"releaseId"`
Name string `json:"name"`
Size int64 `json:"size,omitempty"`
Protocol string `json:"protocol,omitempty"`
SourceClass string `json:"sourceClass,omitempty"`
Indexer string `json:"indexer,omitempty"`
Resolution any `json:"resolution,omitempty"`
HDR []string `json:"hdr,omitempty"`
VideoCodec string `json:"videoCodec,omitempty"`
AudioCodecs []string `json:"audioCodecs,omitempty"`
AudioChannels string `json:"audioChannels,omitempty"`
Languages []string `json:"languages,omitempty"`
Subtitles []string `json:"subtitles,omitempty"`
ReleaseType string `json:"releaseType,omitempty"`
Group string `json:"group,omitempty"`
Cached bool `json:"cached,omitempty"`
Verified bool `json:"verified,omitempty"`
ClientCaps *ClientCaps `json:"clientCaps,omitempty"`
}
CandidateFacts is the release candidate structure passed to RankKey and CondMatch.
type Capability ¶
type Capability string
Capability names a capability declared in manifest.ether.capabilities.
const ( CapStreamSource Capability = "stream_source" CapDebridProvider Capability = "debrid_provider" CapListSource Capability = "list_source" CapLiveTV Capability = "livetv_channels" CapSubtitles Capability = "subtitles" CapMarkers Capability = "markers" CapNotify Capability = "notify" CapEvents Capability = "events" CapRankingStep Capability = "ranking_step" CapRuleCondition Capability = "rule_condition" CapTools Capability = "tools" CapUI Capability = "ui" CapCatalogProvider Capability = "catalog_provider" CapKindResolver Capability = "kind_resolver" CapFrontendAdapter Capability = "frontend_adapter" CapMatcher Capability = "matcher" )
type CatalogItem ¶
type CatalogItem struct {
ID string `json:"id"`
Type string `json:"type"`
Name string `json:"name"`
Poster string `json:"poster,omitempty"`
PosterShape string `json:"posterShape,omitempty"`
Background string `json:"background,omitempty"`
Logo string `json:"logo,omitempty"`
Description string `json:"description,omitempty"`
ReleaseInfo string `json:"releaseInfo,omitempty"`
IMDBRating string `json:"imdbRating,omitempty"`
Genres []string `json:"genres,omitempty"`
}
CatalogItem is one item in a catalog response.
type CatalogProvider ¶
type CatalogProvider interface {
Browse(ctx *Context, req KindBrowseRequest) (KindPage, error)
Detail(ctx *Context, kind, id string) (KindItem, error)
}
CatalogProvider serves browse and detail for one kind. Implement ChangesProvider too for delta pull sync; without it Ether re-walks.
type CatalogRequest ¶
type CatalogRequest struct {
Type string `json:"type"`
ID string `json:"id"`
Extra map[string]string `json:"extra"`
Skip int `json:"skip"`
Search string `json:"search,omitempty"`
Genre string `json:"genre,omitempty"`
}
CatalogRequest is passed to a Catalog route handler.
type CatalogResponse ¶
type CatalogResponse struct {
Metas []CatalogItem `json:"metas"`
}
CatalogResponse is returned by a Catalog route handler.
type CatalogTitleSummary ¶
type CatalogTitleSummary struct {
ID any `json:"id"`
Title string `json:"title"`
Type string `json:"type"`
Year int `json:"year,omitempty"`
IMDBID string `json:"imdbId,omitempty"`
TMDBID int `json:"tmdbId,omitempty"`
}
CatalogTitleSummary is a catalog record returned by Host.CatalogSearch or Host.CatalogGet.
type ChangesProvider ¶
type ChangesProvider interface {
Changes(ctx *Context, req KindChangesRequest) (KindChangePage, error)
}
ChangesProvider is the optional delta feed for pull sync.
type ChannelInput ¶
type ChannelInput struct {
URL string `json:"url"`
Name string `json:"name,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
Priority int `json:"priority,omitempty"`
}
ChannelInput is one stream input for a Live TV channel.
type ChannelItem ¶
type ChannelItem struct {
ID string `json:"id"`
Name string `json:"name"`
Number int `json:"number,omitempty"`
Group string `json:"group,omitempty"`
Logo string `json:"logo,omitempty"`
GuideID string `json:"guideId,omitempty"`
Inputs []ChannelInput `json:"inputs"`
}
ChannelItem is one Live TV channel.
type ChannelsMeta ¶
ChannelsMeta describes a Live TV channel contribution.
type ChannelsResponse ¶
type ChannelsResponse struct {
Channels []ChannelItem `json:"channels"`
}
ChannelsResponse is returned by a Channels route handler.
type ClientCaps ¶
type ClientCaps struct {
MaxHeight int `json:"maxHeight,omitempty"`
HDR []string `json:"hdr,omitempty"`
Codecs []string `json:"codecs,omitempty"`
}
ClientCaps describes playback client capabilities passed to hot-path functions.
type CommandMeta ¶
type CommandMeta struct {
Key string `json:"key"`
Label string `json:"label"`
Action string `json:"action"`
}
CommandMeta describes a command contributed by a plugin.
type ConfigureResponse ¶
ConfigureResponse is returned by a Configure route handler.
type ContainerRef ¶
type ContainerRef struct {
Format string `json:"format,omitempty"` // "" (sniff)|rar|7z|zip
Volumes []VolumeRef `json:"volumes"`
Member string `json:"member,omitempty"`
MemberIndex *int `json:"memberIndex,omitempty"`
PasswordSecret string `json:"passwordSecret,omitempty"`
}
ContainerRef describes an archive member inside ordered volumes.
type Context ¶
type Context struct {
context.Context
Manifest Manifest
Settings map[string]any
Locale string
PickToken string
RequestID string
Headers map[string]string
Query map[string]string
Ether Host
I18n *I18n
}
Context is passed to every plugin route handler.
type Contributes ¶
type Contributes struct {
Source *SourceMeta `json:"source,omitempty"`
Channels *ChannelsMeta `json:"channels,omitempty"`
Subtitles *SubtitlesMeta `json:"subtitles,omitempty"`
Markers *MarkersMeta `json:"markers,omitempty"`
Notification *NotificationMeta `json:"notification,omitempty"`
Events *EventsMeta `json:"events,omitempty"`
Integration *IntegrationMeta `json:"integration,omitempty"`
ListSources []ListSourceMeta `json:"listSources,omitempty"`
RankingSteps []RankingStepMeta `json:"rankingSteps,omitempty"`
RuleConditions []RuleConditionMeta `json:"ruleConditions,omitempty"`
Tools []ToolMeta `json:"tools,omitempty"`
Pages []PageMeta `json:"pages,omitempty"`
Slots []SlotMeta `json:"slots,omitempty"`
Commands []CommandMeta `json:"commands,omitempty"`
MediaKinds []MediaKindMeta `json:"mediaKinds,omitempty"`
FrontendAdapters []FrontendAdapterMeta `json:"frontendAdapters,omitempty"`
Setup *SetupMeta `json:"setup,omitempty"`
}
Contributes declares the descriptors a plugin contributes to Ether.
type Definition ¶
type Definition struct {
Manifest Manifest
Routes Routes
DefaultSettings map[string]any
Strings StringsCatalog
DefaultLocale string
Host Host
RemoteHostOptions RemoteHostOptions
}
Definition configures a new Plugin instance.
type EtherEvent ¶
type EtherEvent struct {
ID string `json:"id"`
Name string `json:"name"`
At string `json:"at"`
ViewerID int64 `json:"viewerId,omitempty"`
Payload map[string]any `json:"payload,omitempty"`
}
EtherEvent is one event delivered in a batch to POST /ether/event.
type EtherManifestBlock ¶
type EtherManifestBlock struct {
Protocol int `json:"protocol"`
Kind PluginKind `json:"kind"`
Capabilities []Capability `json:"capabilities,omitempty"`
Summary string `json:"summary"`
Author *PluginAuthor `json:"author,omitempty"`
License string `json:"license,omitempty"`
Repo string `json:"repo,omitempty"`
Icon string `json:"icon,omitempty"`
Runtime RuntimeDecl `json:"runtime"`
Cluster string `json:"cluster,omitempty"`
Health *HealthDecl `json:"health,omitempty"`
Permissions *Permissions `json:"permissions,omitempty"`
Settings *SettingsSchema `json:"settings,omitempty"`
Strings map[string]any `json:"strings,omitempty"`
Schedule []ScheduleEntry `json:"schedule,omitempty"`
Contributes *Contributes `json:"contributes,omitempty"`
}
EtherManifestBlock holds the EPP/1 `ether` manifest block.
type EventResponse ¶
type EventResponse struct {
Ack []string `json:"ack"`
}
EventResponse is returned by an Event route handler.
type EventsMeta ¶
type EventsMeta struct {
Subscribe []string `json:"subscribe"`
}
EventsMeta lists subscribed event names.
type FieldOption ¶
type FieldOption struct {
Value string `json:"value"`
Label string `json:"label"`
Hint string `json:"hint,omitempty"`
}
FieldOption is one selectable option for a select or multiSelect field.
type FieldProblem ¶
FieldProblem describes one schema validation or copy-rule problem.
func LintSettingsSchema ¶
func LintSettingsSchema(schema *SettingsSchema) []FieldProblem
LintSettingsSchema checks a SettingsSchema for forbidden kinds and copy-rule violations.
type FileRangeResponse ¶
type FileRangeResponse struct {
Status int `json:"status,omitempty"`
ContentType string `json:"contentType,omitempty"`
ContentRange string `json:"contentRange,omitempty"`
TotalSize *int64 `json:"totalSize,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
Body []byte `json:"-"`
}
FileRangeResponse is returned by a File route handler.
type FileRequest ¶
type FileRequest struct {
Path string `json:"path"`
Range string `json:"range,omitempty"`
HeadOnly bool `json:"headOnly,omitempty"`
}
FileRequest is passed to a File route handler.
type FolderEntry ¶
type FolderEntry struct {
Name string `json:"name"`
Path string `json:"path"`
Dir bool `json:"dir"`
Size int64 `json:"size,omitempty"`
Mtime int64 `json:"mtime,omitempty"`
}
FolderEntry describes one entry returned by Host.FoldersList or Host.FoldersStat.
type FrontendAdapter ¶
FrontendAdapter is a plugin-provided protocol surface (OPDS, Subsonic, custom). Ether authenticates the viewer first and forwards the request under /ether/v1/frontend/{key}; the handler sees X-Ether-Viewer headers and never a credential.
type FrontendAdapterMeta ¶
type FrontendAdapterMeta struct {
Key string `json:"key"`
Label string `json:"label"`
ProtocolHint string `json:"protocolHint"` // opds|subsonic|custom
PathPrefix string `json:"pathPrefix"`
DefaultPort int `json:"defaultPort,omitempty"`
Clients []string `json:"clients,omitempty"`
Auth string `json:"auth,omitempty"` // ether_token (default)|basic|none_lan
}
FrontendAdapterMeta declares one plugin-provided protocol surface.
type GuideRequest ¶
type GuideRequest struct {
ChannelID string `json:"channelId"`
From string `json:"from,omitempty"`
To string `json:"to,omitempty"`
}
GuideRequest is passed to a Guide route handler.
type GuideResponse ¶
type GuideResponse struct {
Programmes []ProgrammeItem `json:"programmes"`
}
GuideResponse is returned by a Guide route handler.
type HealthDecl ¶
type HealthDecl struct {
Interval string `json:"interval,omitempty"`
}
HealthDecl configures periodic health checks.
type HealthResponse ¶
type HealthResponse struct {
Status string `json:"status"`
Reason string `json:"reason,omitempty"`
}
HealthResponse is returned by a Health route handler.
type Host ¶
type Host interface {
KVGet(ctx context.Context, key string) (string, bool, error)
KVPut(ctx context.Context, key, value string) error
KVDelete(ctx context.Context, key string) error
KVList(ctx context.Context, prefix string) ([]string, error)
SecretGet(ctx context.Context, name string) (string, error)
Fetch(ctx context.Context, req HostFetchRequest) (HostFetchResponse, error)
CatalogSearch(ctx context.Context, query string, limit, offset int) ([]CatalogTitleSummary, error)
CatalogGet(ctx context.Context, id string) (*CatalogTitleSummary, error)
ViewerHistory(ctx context.Context, viewerID int64, limit, offset int) ([]ViewerWatchEntry, error)
SetWatchState(ctx context.Context, viewerID int64, titleID string, watched bool) error
UpsertCollection(ctx context.Context, collection map[string]any) error
FoldersList(ctx context.Context, root, path string) ([]FolderEntry, error)
FoldersStat(ctx context.Context, root, path string) (FolderEntry, error)
NotifyOwner(ctx context.Context, text string) error
Log(ctx context.Context, level, message string, attrs map[string]any) error
// Content-kind host services. KindUpsert/KindDelete need a push-mode
// kind; ResolveSources needs permissions.sources.
KindUpsert(ctx context.Context, kindKey string, items []KindItem) (accepted int, err error)
KindDelete(ctx context.Context, kindKey string, externalIDs []string) error
KindItems(ctx context.Context, kindKey, cursor string, limit int) (KindPage, error)
ResolveSources(ctx context.Context, q SourceQuery) ([]SourceCandidate, error)
CachePut(ctx context.Context, libraryKey, name string, data []byte) (blobRef string, err error)
CacheOpen(ctx context.Context, blobRef string) ([]byte, error)
}
Host provides access to Ether host services (KV, secrets, HTTP, catalog, viewers, collections, folders, owner notifications, and logging).
type HostFetchRequest ¶
type HostFetchRequest struct {
Method string `json:"method,omitempty"`
URL string `json:"url"`
Headers map[string]string `json:"headers,omitempty"`
Body string `json:"body,omitempty"`
}
HostFetchRequest describes an outbound HTTP call via the host service.
type HostFetchResponse ¶
type HostFetchResponse struct {
Status int `json:"status"`
Headers map[string]string `json:"headers"`
Body string `json:"body"`
}
HostFetchResponse is returned by Host.Fetch.
type I18n ¶
type I18n struct {
// contains filtered or unexported fields
}
I18n resolves localized strings with fallback to base language and "en".
func NewI18n ¶
func NewI18n(catalog StringsCatalog, defaultLocale string) *I18n
NewI18n creates a localization helper backed by a StringsCatalog.
func (*I18n) Catalog ¶
func (i *I18n) Catalog() StringsCatalog
Catalog returns the underlying locale catalog.
type IntegrationMeta ¶
type IntegrationMeta struct {
Category string `json:"category"`
Logo string `json:"logo,omitempty"`
Adds []string `json:"adds"`
Connect string `json:"connect"`
Test bool `json:"test,omitempty"`
}
IntegrationMeta describes an external service integration.
type JobRequest ¶
type JobRequest struct {
Name string `json:"name"`
}
JobRequest is passed to a Job route handler.
type KindArtRef ¶
type KindArtRef struct {
Role string `json:"role"`
URL string `json:"url"`
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
}
KindArtRef is one artwork reference on a KindItem.
type KindBrowseDecl ¶
type KindBrowseDecl struct {
Sorts []string `json:"sorts,omitempty"`
Facets []string `json:"facets,omitempty"`
Search bool `json:"search"`
SearchFields []string `json:"searchFields,omitempty"`
// LiveSearch: client searches also call the plugin's /search route
// with a short budget; results are upserted and merged (§15.4).
LiveSearch bool `json:"liveSearch,omitempty"`
}
KindBrowseDecl is how a kind library is browsed.
type KindBrowseRequest ¶
type KindBrowseRequest struct {
Kind string `json:"-"` // filled from the route, never on the wire
ParentID string `json:"parentId,omitempty"`
Level string `json:"level,omitempty"`
Cursor string `json:"cursor,omitempty"`
Limit int `json:"limit,omitempty"`
Sort string `json:"sort,omitempty"`
Facets map[string][]string `json:"facets,omitempty"`
Query string `json:"query,omitempty"`
}
KindBrowseRequest is Ether's browse/search request for one kind level.
type KindCacheDecl ¶
type KindCacheDecl struct {
MaxBytes int64 `json:"maxBytes,omitempty"`
TTL string `json:"ttl,omitempty"`
}
KindCacheDecl is the per-library blob cache budget; 0 means server default.
type KindChangePage ¶
type KindChangePage struct {
Upserts []KindItem `json:"upserts,omitempty"`
Deletes []string `json:"deletes,omitempty"`
NextCursor string `json:"nextCursor,omitempty"`
Complete bool `json:"complete"`
}
KindChangePage is one delta page for pull sync.
type KindChangesRequest ¶
type KindChangesRequest struct {
Kind string `json:"-"`
Cursor string `json:"cursor,omitempty"`
Limit int `json:"limit,omitempty"`
}
KindChangesRequest is Ether's delta-feed request.
type KindField ¶
type KindField struct {
Key string `json:"key"`
Type string `json:"type"` // string|int|float|bool|date|duration|enum|strings|people
Label string `json:"label"`
Indexed bool `json:"indexed,omitempty"`
Enum []string `json:"enum,omitempty"`
}
KindField is one declared item field.
type KindItem ¶
type KindItem struct {
ID string `json:"id"`
Level string `json:"level"`
ParentID string `json:"parentId,omitempty"`
Index int `json:"index,omitempty"`
Title string `json:"title"`
SortTitle string `json:"sortTitle,omitempty"`
Summary string `json:"summary,omitempty"`
Year int `json:"year,omitempty"`
Released int64 `json:"released,omitempty"` // unix seconds
Rating float64 `json:"rating,omitempty"`
Playable bool `json:"playable,omitempty"`
ChildCount int `json:"childCount,omitempty"`
LeafCount int `json:"leafCount,omitempty"`
Art []KindArtRef `json:"art,omitempty"`
Fields map[string]json.RawMessage `json:"fields,omitempty"`
Hints json.RawMessage `json:"hints,omitempty"` // opaque JSON object, echoed to the resolver
ETag string `json:"etag,omitempty"`
}
KindItem is the standardised plugin item envelope. Ether assigns the stored identity; the plugin only ever sees its own external ID.
type KindLevel ¶
type KindLevel struct {
Key string `json:"key"`
Label string `json:"label"`
Playable bool `json:"playable,omitempty"`
Container bool `json:"container,omitempty"`
}
KindLevel is one hierarchy level of a kind.
type KindLibraryDecl ¶
KindLibraryDecl is the library created for a kind on install.
type KindMatchDecl ¶
type KindMatchDecl struct {
ThresholdDefault float64 `json:"thresholdDefault,omitempty"`
}
KindMatchDecl holds matcher defaults for a kind.
type KindMatchRequest ¶
type KindMatchRequest struct {
Kind string `json:"-"`
Item KindItem `json:"item"`
Candidates []SourceCandidate `json:"candidates"`
Settings map[string]any `json:"settings,omitempty"`
}
KindMatchRequest asks a matcher to score candidates for one item.
type KindMatchResponse ¶
type KindMatchResponse struct {
Matches []KindMatchScore `json:"matches"`
}
KindMatchResponse is a matcher's answer, best first.
type KindMatchScore ¶
type KindMatchScore struct {
CandidateRef string `json:"candidateRef"`
Score float64 `json:"score"`
Reasons []string `json:"reasons,omitempty"`
}
KindMatchScore is one candidate score (0..1) with plain-language reasons.
type KindMatcher ¶
type KindMatcher interface {
Match(ctx *Context, req KindMatchRequest) (KindMatchResponse, error)
}
KindMatcher scores source candidates for one item.
type KindPage ¶
type KindPage struct {
Items []KindItem `json:"items"`
NextCursor string `json:"nextCursor,omitempty"`
Total *int `json:"total,omitempty"`
}
KindPage is one page of kind items; an empty NextCursor ends the walk.
type KindResolveRequest ¶
type KindResolveRequest struct {
Kind string `json:"-"`
LibraryKey string `json:"libraryKey"`
Item KindItem `json:"item"`
PartIndex *int `json:"partIndex,omitempty"`
Client map[string]any `json:"client,omitempty"`
}
KindResolveRequest asks a resolver for playable sources for one item.
type KindResolveResponse ¶
type KindResolveResponse struct {
Sources []PlayableSource `json:"sources"`
TTLSec int `json:"ttlSec,omitempty"`
}
KindResolveResponse is a resolver's ordered sources.
type KindResolver ¶
type KindResolver interface {
Resolve(ctx *Context, req KindResolveRequest) (KindResolveResponse, error)
}
KindResolver turns a playable item into ordered playable sources.
type KindSyncDecl ¶
type KindSyncDecl struct {
Mode string `json:"mode"` // pull|push
Refresh string `json:"refresh,omitempty"`
}
KindSyncDecl is how a kind's items reach Ether's store.
type ListSourceMeta ¶
type ListSourceMeta struct {
Key string `json:"key"`
Label string `json:"label"`
Hint string `json:"hint,omitempty"`
Catalog string `json:"catalog"`
Params any `json:"params,omitempty"`
MaxItems int `json:"maxItems,omitempty"`
Refresh string `json:"refresh,omitempty"`
}
ListSourceMeta describes a catalog list source.
type Manifest ¶
type Manifest struct {
ID string `json:"id"`
Version string `json:"version"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Logo string `json:"logo,omitempty"`
Background string `json:"background,omitempty"`
Types []string `json:"types,omitempty"`
Catalogs []StremioCatalogDecl `json:"catalogs,omitempty"`
Resources []any `json:"resources,omitempty"`
IDPrefixes []string `json:"idPrefixes,omitempty"`
BehaviorHints *BehaviorHints `json:"behaviorHints,omitempty"`
Ether *EtherManifestBlock `json:"ether,omitempty"`
}
Manifest is the EPP/1 manifest document served at GET /manifest.json.
type MarkerItem ¶
type MarkerItem struct {
Kind string `json:"kind"`
Start float64 `json:"start"`
End float64 `json:"end"`
Label string `json:"label,omitempty"`
}
MarkerItem is one intro/credits/recap/preview marker.
type MarkersMeta ¶
type MarkersMeta struct {
Kinds []string `json:"kinds"`
}
MarkersMeta describes a playback markers contribution.
type MarkersRequest ¶
MarkersRequest is passed to a Markers route handler.
type MarkersResponse ¶
type MarkersResponse struct {
Markers []MarkerItem `json:"markers"`
}
MarkersResponse is returned by a Markers route handler.
type MediaKindMeta ¶
type MediaKindMeta struct {
Key string `json:"key"`
Label string `json:"label"`
Library KindLibraryDecl `json:"library"`
Levels []KindLevel `json:"levels"`
Fields []KindField `json:"fields,omitempty"`
Art []KindArt `json:"art,omitempty"`
Present Presentations `json:"present"`
Browse KindBrowseDecl `json:"browse"`
Sync KindSyncDecl `json:"sync"`
Cache *KindCacheDecl `json:"cache,omitempty"`
Match *KindMatchDecl `json:"match,omitempty"`
}
MediaKindMeta declares one content kind; each kind becomes its own library.
type MetaItem ¶
type MetaItem struct {
CatalogItem
Runtime string `json:"runtime,omitempty"`
Cast []string `json:"cast,omitempty"`
Director []string `json:"director,omitempty"`
Videos []MetaVideo `json:"videos,omitempty"`
}
MetaItem is a full metadata record returned by a Meta route handler.
type MetaRequest ¶
MetaRequest is passed to a Meta route handler.
type MetaResponse ¶
type MetaResponse struct {
Meta MetaItem `json:"meta"`
}
MetaResponse is returned by a Meta route handler.
type MetaVideo ¶
type MetaVideo struct {
ID string `json:"id"`
Title string `json:"title"`
Released string `json:"released,omitempty"`
Season int `json:"season,omitempty"`
Episode int `json:"episode,omitempty"`
Overview string `json:"overview,omitempty"`
Thumbnail string `json:"thumbnail,omitempty"`
}
MetaVideo describes one episode/video inside a MetaItem.
type MockHost ¶
type MockHost struct {
KV map[string]string
Secrets map[string]string
Catalog map[string]CatalogTitleSummary
History map[int64][]ViewerWatchEntry
WatchState map[string]bool
Collections []map[string]any
Kinds map[string][]KindItem // kindKey -> pushed items, in upsert order
Sources []SourceCandidate // returned by ResolveSources
Blobs map[string][]byte // cache blob ref -> bytes
Folders map[string][]FolderEntry
Notifications []string
Logs []RecordedLog
FetchHandler func(ctx context.Context, req HostFetchRequest) (HostFetchResponse, error)
// contains filtered or unexported fields
}
MockHost is an in-memory Host implementation for unit tests.
func (*MockHost) CatalogGet ¶
CatalogGet returns a title by ID from the in-memory catalog.
func (*MockHost) CatalogSearch ¶
func (m *MockHost) CatalogSearch(_ context.Context, query string, limit, offset int) ([]CatalogTitleSummary, error)
CatalogSearch searches the in-memory catalog by case-insensitive substring.
func (*MockHost) Fetch ¶
func (m *MockHost) Fetch(ctx context.Context, req HostFetchRequest) (HostFetchResponse, error)
Fetch invokes FetchHandler if set, or returns a 200 "{}" response.
func (*MockHost) FoldersList ¶
FoldersList returns folder entries for root/path.
func (*MockHost) FoldersStat ¶
FoldersStat returns a FolderEntry for root and path.
func (*MockHost) KindDelete ¶
KindDelete drops recorded items by external id.
func (*MockHost) KindItems ¶
func (m *MockHost) KindItems(_ context.Context, kindKey, cursor string, limit int) (KindPage, error)
KindItems pages recorded items; the cursor is the last item id seen.
func (*MockHost) KindUpsert ¶
KindUpsert records pushed items under their kind, replacing same-id items.
func (*MockHost) NotifyOwner ¶
NotifyOwner appends text to Notifications.
func (*MockHost) ResolveSources ¶
func (m *MockHost) ResolveSources(_ context.Context, _ SourceQuery) ([]SourceCandidate, error)
ResolveSources returns the canned Sources.
func (*MockHost) SetWatchState ¶
func (m *MockHost) SetWatchState(_ context.Context, viewerID int64, titleID string, watched bool) error
SetWatchState records watch state for viewerID:titleID.
func (*MockHost) UpsertCollection ¶
UpsertCollection appends the collection to Collections.
func (*MockHost) ViewerHistory ¶
func (m *MockHost) ViewerHistory(_ context.Context, viewerID int64, limit, offset int) ([]ViewerWatchEntry, error)
ViewerHistory returns watch history for viewerID.
type NotificationMeta ¶
type NotificationMeta struct {
Fields any `json:"fields,omitempty"`
SendTest bool `json:"sendTest"`
Formats []string `json:"formats,omitempty"`
MaxLength int `json:"maxLength,omitempty"`
}
NotificationMeta describes a notification channel contribution.
type NotifyRequest ¶
type NotifyRequest struct {
Title string `json:"title,omitempty"`
Message string `json:"message"`
Event string `json:"event,omitempty"`
Settings map[string]any `json:"settings,omitempty"`
}
NotifyRequest is passed to a Notify route handler.
type NotifyResponse ¶
NotifyResponse is returned by a Notify route handler.
type OpenStreamRequest ¶
type OpenStreamRequest struct {
StreamID string `json:"streamId"`
}
OpenStreamRequest is passed to an OpenStream route handler.
type OpenStreamResponse ¶
type OpenStreamResponse struct {
URL string `json:"url,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
ExpiresAt string `json:"expiresAt,omitempty"`
File string `json:"file,omitempty"`
}
OpenStreamResponse is returned by an OpenStream route handler.
type PageMeta ¶
type PageMeta struct {
Key string `json:"key"`
Label string `json:"label"`
Route string `json:"route"`
Group string `json:"group,omitempty"`
Icon string `json:"icon,omitempty"`
}
PageMeta describes a ViewSpec page contributed by a plugin.
type Permissions ¶
type Permissions struct {
Network []string `json:"network,omitempty"`
Catalog string `json:"catalog,omitempty"`
Viewers *ViewerPermissions `json:"viewers,omitempty"`
Collections string `json:"collections,omitempty"`
Folders string `json:"folders,omitempty"`
Secrets []string `json:"secrets,omitempty"`
// Sources lets a kind resolver ask Ether to find bytes: ""|none|search|acquire.
Sources string `json:"sources,omitempty"`
}
Permissions declares the host access a plugin requests.
type PlayableSource ¶
type PlayableSource struct {
ID string `json:"id"`
Label string `json:"label,omitempty"`
Pattern string `json:"pattern"` // range|download|direct|container|release
MIME string `json:"mime"`
Size int64 `json:"size,omitempty"`
DurationSec float64 `json:"durationSec,omitempty"`
URL string `json:"url,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
BlobRef string `json:"blobRef,omitempty"`
SHA256 string `json:"sha256,omitempty"`
MaxBytes int64 `json:"maxBytes,omitempty"`
ExpiresAt int64 `json:"expiresAt,omitempty"`
Container *ContainerRef `json:"container,omitempty"`
Release *ReleaseHint `json:"release,omitempty"`
}
PlayableSource is one candidate way to acquire an item's bytes. URL may carry {itemIndex}, {pageNumber}, {byteStart}, {byteEnd} and {byteRange} placeholders that Ether substitutes.
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin is a runnable Ether plugin instance.
func NewCatalogTemplate ¶
func NewCatalogTemplate(opts TemplateOptions) *Plugin
NewCatalogTemplate builds a starter catalog and metadata plugin.
func NewConditionTemplate ¶
func NewConditionTemplate(opts TemplateOptions) *Plugin
NewConditionTemplate builds a starter rule-condition plugin.
func NewRankerTemplate ¶
func NewRankerTemplate(opts TemplateOptions) *Plugin
NewRankerTemplate builds a starter release-ranking step plugin.
func NewScrobblerTemplate ¶
func NewScrobblerTemplate(opts TemplateOptions) *Plugin
NewScrobblerTemplate builds a starter playback event subscriber plugin.
func NewSubtitlesTemplate ¶
func NewSubtitlesTemplate(opts TemplateOptions) *Plugin
NewSubtitlesTemplate builds a starter subtitles provider plugin.
func NewViewTemplate ¶
func NewViewTemplate(opts TemplateOptions) *Plugin
NewViewTemplate builds a starter ViewSpec UI and action plugin.
func (*Plugin) CondMatch ¶
func (p *Plugin) CondMatch(candidate CandidateFacts, operator string, value any) bool
CondMatch invokes the plugin's CondMatch hot-path function.
func (*Plugin) Dispatch ¶
func (p *Plugin) Dispatch(ctx context.Context, req RawRequest) RawResponse
Dispatch routes a RawRequest through the plugin and returns a RawResponse.
func (*Plugin) DispatchTunnelFrame ¶
func (p *Plugin) DispatchTunnelFrame(ctx context.Context, frame TunnelFrame, maxChunkBytes int) []TunnelFrame
DispatchTunnelFrame executes a `"request"` TunnelFrame against a Plugin and returns the outgoing response frames (either a single `"response"` frame or a `"response_start"` / `"response_chunk"` / `"response_end"` sequence for `/ether/file/*` binary responses).
func (*Plugin) GetSettings ¶
GetSettings returns a copy of the plugin's active settings.
func (*Plugin) OnCatalog ¶
func (p *Plugin) OnCatalog(kind string, c CatalogProvider)
OnCatalog registers the catalogue provider for one declared kind.
func (*Plugin) OnFrontend ¶
func (p *Plugin) OnFrontend(key string, a FrontendAdapter)
OnFrontend registers one declared frontend adapter.
func (*Plugin) OnMatch ¶
func (p *Plugin) OnMatch(kind string, m KindMatcher)
OnMatch registers the matcher for one declared kind.
func (*Plugin) OnResolve ¶
func (p *Plugin) OnResolve(kind string, r KindResolver)
OnResolve registers the resolver for one declared kind.
func (*Plugin) OnSetupTest ¶
func (p *Plugin) OnSetupTest(t SetupTester)
OnSetupTest registers the handler for /ether/v1/setup/{step}/test.
func (*Plugin) RankKey ¶
func (p *Plugin) RankKey(candidate CandidateFacts, config map[string]any) int64
RankKey invokes the plugin's RankKey hot-path function.
func (*Plugin) Serve ¶
func (p *Plugin) Serve(ctx context.Context, opts ServeOptions) error
Serve runs the plugin sidecar until ctx is cancelled.
func (*Plugin) ServeHTTP ¶
func (p *Plugin) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP implements net/http.Handler for running a Plugin as an HTTP sidecar.
func (*Plugin) SetSettings ¶
SetSettings validates and updates the plugin's settings.
func (*Plugin) Start ¶
func (p *Plugin) Start(ctx context.Context, opts ServeOptions) (*ServeHandle, error)
Start launches the plugin sidecar in the background (either HTTP or WebSocket tunnel) and returns a ServeHandle that stops it when Close is called or ctx is cancelled.
type PluginAuthor ¶
PluginAuthor identifies the plugin author.
type PluginKind ¶
type PluginKind string
PluginKind identifies the top-level plugin classification in manifest.ether.kind.
const ( KindSource PluginKind = "source" KindIntegration PluginKind = "integration" KindNotificationChannel PluginKind = "notification_channel" KindLiveTVSource PluginKind = "livetv_source" KindExtension PluginKind = "extension" KindContent PluginKind = "content" )
type Presentations ¶
type Presentations struct {
AdapterOnly bool `json:"adapterOnly,omitempty"`
Plex *struct {
Base string `json:"base"` // movie|show|artist|photo
} `json:"plex,omitempty"`
MediaBrowser *struct {
CollectionType string `json:"collectionType"`
ItemTypes map[string]string `json:"itemTypes,omitempty"`
} `json:"mediaBrowser,omitempty"`
Stremio *struct {
Type string `json:"type"`
} `json:"stremio,omitempty"`
Xtream *struct {
Category string `json:"category"`
} `json:"xtream,omitempty"`
Silo *struct {
Type string `json:"type"`
} `json:"silo,omitempty"`
}
Presentations declares how a library is published per protocol.
type ProgrammeItem ¶
type ProgrammeItem struct {
ID string `json:"id,omitempty"`
ChannelID string `json:"channelId,omitempty"`
Title string `json:"title"`
Subtitle string `json:"subtitle,omitempty"`
Description string `json:"description,omitempty"`
Start string `json:"start"`
Stop string `json:"stop"`
Icon string `json:"icon,omitempty"`
Categories []string `json:"categories,omitempty"`
}
ProgrammeItem is one EPG programme entry.
type RankingStepMeta ¶
type RankingStepMeta struct {
Key string `json:"key"`
Label string `json:"label"`
Hint string `json:"hint,omitempty"`
Editor any `json:"editor,omitempty"`
Budget any `json:"budget,omitempty"`
}
RankingStepMeta describes a custom ranking step.
type RawRequest ¶
type RawRequest struct {
PluginID string `json:"pluginId,omitempty"`
Method string `json:"method,omitempty"`
Path string `json:"path"`
Query map[string]string `json:"query,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
Body json.RawMessage `json:"body,omitempty"`
Locale string `json:"locale,omitempty"`
}
RawRequest is the low-level transport-agnostic request passed to Plugin.Dispatch.
type RawResponse ¶
type RawResponse struct {
Status int `json:"status"`
Headers map[string]string `json:"headers,omitempty"`
Body json.RawMessage `json:"body,omitempty"`
BinaryBody []byte `json:"-"`
}
RawResponse is the low-level transport-agnostic response returned by Plugin.Dispatch.
type RecordedLog ¶
RecordedLog is one log message recorded by MockHost.
type ReleaseHint ¶
type ReleaseHint struct {
NZBURL string `json:"nzbUrl,omitempty"`
Magnet string `json:"magnet,omitempty"`
InfoHash string `json:"infoHash,omitempty"`
Candidate *SourceCandidate `json:"candidate,omitempty"`
FileHint string `json:"fileHint,omitempty"`
PasswordSecret string `json:"passwordSecret,omitempty"`
}
ReleaseHint hands a release to Ether's Usenet/debrid/torrent pipeline.
type RemoteHostClient ¶
type RemoteHostClient struct {
// contains filtered or unexported fields
}
RemoteHostClient implements Host by calling `/ether/plugin-host/*` on the Ether server authenticated with `X-Ether-Plugin-Credential`.
func NewRemoteHostClient ¶
func NewRemoteHostClient(opts RemoteHostOptions) *RemoteHostClient
NewRemoteHostClient creates a RemoteHostClient.
func (*RemoteHostClient) CachePut ¶
func (c *RemoteHostClient) CachePut(ctx context.Context, libraryKey, name string, data []byte) (string, error)
CachePut stores one blob in a kind library's cache; the ref is content-addressed.
func (*RemoteHostClient) CatalogGet ¶
func (c *RemoteHostClient) CatalogGet(ctx context.Context, id string) (*CatalogTitleSummary, error)
CatalogGet fetches one title summary from the Ether catalog.
func (*RemoteHostClient) CatalogSearch ¶
func (c *RemoteHostClient) CatalogSearch( ctx context.Context, query string, limit, offset int, ) ([]CatalogTitleSummary, error)
CatalogSearch searches the Ether catalog.
func (*RemoteHostClient) Fetch ¶
func (c *RemoteHostClient) Fetch(ctx context.Context, r HostFetchRequest) (HostFetchResponse, error)
Fetch performs an outbound HTTP request.
func (*RemoteHostClient) FoldersList ¶
func (c *RemoteHostClient) FoldersList(_ context.Context, _, _ string) ([]FolderEntry, error)
FoldersList is only supported in hosted mode; sidecars access local folders directly.
func (*RemoteHostClient) FoldersStat ¶
func (c *RemoteHostClient) FoldersStat(_ context.Context, _, _ string) (FolderEntry, error)
FoldersStat is only supported in hosted mode; sidecars access local folders directly.
func (*RemoteHostClient) KVDelete ¶
func (c *RemoteHostClient) KVDelete(ctx context.Context, key string) error
KVDelete deletes a key from the plugin's KV namespace.
func (*RemoteHostClient) KVList ¶
KVList lists keys matching the given prefix in the plugin's KV namespace.
func (*RemoteHostClient) KVPut ¶
func (c *RemoteHostClient) KVPut(ctx context.Context, key, value string) error
KVPut writes a key-value pair to the plugin's KV namespace.
func (*RemoteHostClient) KindDelete ¶
func (c *RemoteHostClient) KindDelete(ctx context.Context, kindKey string, externalIDs []string) error
KindDelete removes items from a push-mode kind library.
func (*RemoteHostClient) KindItems ¶
func (c *RemoteHostClient) KindItems(ctx context.Context, kindKey, cursor string, limit int) (KindPage, error)
KindItems reads the plugin's own kind library back, keyset-paged.
func (*RemoteHostClient) KindUpsert ¶
func (c *RemoteHostClient) KindUpsert(ctx context.Context, kindKey string, items []KindItem) (int, error)
KindUpsert pushes items into a push-mode kind library.
func (*RemoteHostClient) Log ¶
func (c *RemoteHostClient) Log(ctx context.Context, level, message string, attrs map[string]any) error
Log sends a structured log entry to Ether.
func (*RemoteHostClient) NotifyOwner ¶
func (c *RemoteHostClient) NotifyOwner(ctx context.Context, text string) error
NotifyOwner sends an owner notification through Ether.
func (*RemoteHostClient) ResolveSources ¶
func (c *RemoteHostClient) ResolveSources(ctx context.Context, q SourceQuery) ([]SourceCandidate, error)
ResolveSources asks Ether to find sources through its indexers/debrid.
func (*RemoteHostClient) SecretGet ¶
SecretGet is only available in hosted mode; remote plugins receive secrets via /ether/configure.
func (*RemoteHostClient) SetWatchState ¶
func (c *RemoteHostClient) SetWatchState( ctx context.Context, viewerID int64, titleID string, watched bool, ) error
SetWatchState updates the watch state for a granted viewer.
func (*RemoteHostClient) SettingsGet ¶
SettingsGet reads the current non-secret settings from Ether.
func (*RemoteHostClient) UpsertCollection ¶
UpsertCollection creates or updates a plugin-managed collection.
func (*RemoteHostClient) ViewerHistory ¶
func (c *RemoteHostClient) ViewerHistory( ctx context.Context, viewerID int64, limit, offset int, ) ([]ViewerWatchEntry, error)
ViewerHistory reads watch history for a granted viewer.
type RemoteHostOptions ¶
RemoteHostOptions configures a RemoteHostClient for sidecar plugins.
type RouteError ¶
RouteError represents a structured HTTP error returned by a plugin handler.
func NewRouteError ¶
func NewRouteError(status int, code, message string) *RouteError
NewRouteError constructs a structured RouteError.
func (*RouteError) Error ¶
func (e *RouteError) Error() string
type Routes ¶
type Routes struct {
Catalog func(ctx *Context, req CatalogRequest) (CatalogResponse, error)
Meta func(ctx *Context, req MetaRequest) (MetaResponse, error)
Stream func(ctx *Context, req StreamRequest) (StreamResponse, error)
OpenStream func(ctx *Context, req OpenStreamRequest) (OpenStreamResponse, error)
Subtitles func(ctx *Context, req SubtitlesRequest) (SubtitlesResponse, error)
SubtitleDownload func(ctx *Context, req SubtitleDownloadRequest) (SubtitleDownloadResponse, error)
Markers func(ctx *Context, req MarkersRequest) (MarkersResponse, error)
Channels func(ctx *Context) (ChannelsResponse, error)
Guide func(ctx *Context, req GuideRequest) (GuideResponse, error)
Configure func(ctx *Context, settings map[string]any) (ConfigureResponse, error)
Test func(ctx *Context) (TestResponse, error)
Options func(ctx *Context, field string) ([]FieldOption, error)
Health func(ctx *Context) (HealthResponse, error)
Notify func(ctx *Context, req NotifyRequest) (NotifyResponse, error)
Event func(ctx *Context, events []EtherEvent) (EventResponse, error)
View func(ctx *Context, req ViewRequest) (ViewResponse, error)
Action func(ctx *Context, req ActionRequest) (ActionResponse, error)
Tool func(ctx *Context, req ToolRequest) (ToolResponse, error)
Job func(ctx *Context, req JobRequest) (JobResponse, error)
File func(ctx *Context, req FileRequest) (FileRangeResponse, error)
RankKey func(candidate CandidateFacts, config map[string]any) int64
CondMatch func(candidate CandidateFacts, operator string, value any) bool
}
Routes holds the capability handlers for a Plugin.
type RuleConditionMeta ¶
type RuleConditionMeta struct {
Key string `json:"key"`
Label string `json:"label"`
Operators []string `json:"operators,omitempty"`
Editor any `json:"editor,omitempty"`
}
RuleConditionMeta describes a custom rule condition.
type RuntimeDecl ¶
type RuntimeDecl struct {
Kind string `json:"kind"`
Entry string `json:"entry,omitempty"`
Module string `json:"module,omitempty"`
}
RuntimeDecl declares how the plugin is executed.
type ScheduleEntry ¶
ScheduleEntry declares a scheduled background job.
type SearchProvider ¶
type SearchProvider interface {
Search(ctx *Context, req KindBrowseRequest) (KindPage, error)
}
SearchProvider is the optional provider-side search route.
type ServeHandle ¶
type ServeHandle struct {
// Done closes when the tunnel supervisor stops (ctx cancellation or Close).
// Transient disconnects reconnect automatically. It is nil for HTTP servers.
Done <-chan struct{}
Mode string
URL string
Port int
Close func() error
}
ServeHandle represents a running HTTP or tunnel sidecar session.
type ServeOptions ¶
ServeOptions configures how Plugin.Serve runs in sidecar mode.
type SettingField ¶
type SettingField struct {
Key string `json:"key"`
Kind string `json:"kind"`
Label string `json:"label,omitempty"`
Hint string `json:"hint,omitempty"`
Required bool `json:"required,omitempty"`
Default any `json:"default,omitempty"`
Options []FieldOption `json:"options,omitempty"`
Fields []SettingField `json:"fields,omitempty"`
Min *float64 `json:"min,omitempty"`
Max *float64 `json:"max,omitempty"`
Placeholder string `json:"placeholder,omitempty"`
}
SettingField describes one leaf or group setting field.
func CollectSettingFields ¶
func CollectSettingFields(schema *SettingsSchema) []SettingField
CollectSettingFields returns all leaf SettingFields from a SettingsSchema.
type SettingsGroup ¶
type SettingsGroup struct {
Key string `json:"key,omitempty"`
Label string `json:"label,omitempty"`
Hint string `json:"hint,omitempty"`
Advanced bool `json:"advanced,omitempty"`
Fields []SettingField `json:"fields"`
}
SettingsGroup groups related settings fields.
type SettingsSchema ¶
type SettingsSchema struct {
Ref string `json:"$ref,omitempty"`
Fields []SettingField `json:"fields,omitempty"`
Groups []SettingsGroup `json:"groups,omitempty"`
}
SettingsSchema describes a plugin's configuration fields.
type SetupMeta ¶
type SetupMeta struct {
Steps []SetupStep `json:"steps"`
}
SetupMeta declares guided setup for any plugin kind.
type SetupStep ¶
type SetupStep struct {
Key string `json:"key"`
Label string `json:"label"`
Hint string `json:"hint,omitempty"`
Fields []string `json:"fields"`
Test string `json:"test"`
Required bool `json:"required"`
}
SetupStep is one setup wizard step; an empty Test means saving is the step.
type SetupTestRequest ¶
type SetupTestRequest struct {
Step string `json:"step"`
Fields map[string]any `json:"fields,omitempty"`
}
SetupTestRequest is Ether's setup-step test call: the step key plus the saved (non-secret) values of the step's fields.
type SetupTestResult ¶
type SetupTestResult struct {
OK bool `json:"ok"`
Detail string `json:"detail,omitempty"`
Fields map[string]string `json:"fields,omitempty"`
}
SetupTestResult is a setup test's answer.
type SetupTester ¶
type SetupTester interface {
TestSetup(ctx *Context, req SetupTestRequest) (SetupTestResult, error)
}
SetupTester answers setup-step test calls.
type SlotMeta ¶
type SlotMeta struct {
Slot string `json:"slot"`
Key string `json:"key"`
Label string `json:"label"`
Route string `json:"route"`
}
SlotMeta describes a UI slot contributed by a plugin.
type SourceCandidate ¶
type SourceCandidate struct {
InfoHash string `json:"infoHash,omitempty"`
Magnet string `json:"magnet,omitempty"`
NZBURL string `json:"nzbUrl,omitempty"`
FileIndex int `json:"fileIndex,omitempty"`
Title string `json:"title,omitempty"`
Size int64 `json:"size,omitempty"`
Seeders int `json:"seeders,omitempty"`
Cached bool `json:"cached,omitempty"`
// Ref names this candidate in match scores. Ether derives it from the
// infohash/NZB when empty; matchers echo it verbatim.
Ref string `json:"-"`
}
SourceCandidate is one Ether-found release for a resolver to rank.
func (SourceCandidate) CandidateRef ¶
func (c SourceCandidate) CandidateRef() string
CandidateRef returns the stable ref matchers report for a candidate.
type SourceMeta ¶
type SourceMeta struct {
Class string `json:"class"`
Abilities []string `json:"abilities,omitempty"`
Limits map[string]any `json:"limits,omitempty"`
}
SourceMeta describes a stream source contribution.
type SourceQuery ¶
type SourceQuery struct {
Kind string `json:"kind"` // indexer_search|debrid_check|debrid_acquire
Query string `json:"query,omitempty"`
Category string `json:"category,omitempty"`
InfoHashes []string `json:"infoHashes,omitempty"`
InfoHash string `json:"infoHash,omitempty"`
Magnet string `json:"magnet,omitempty"`
}
SourceQuery asks Ether to find sources (Host.ResolveSources).
type StreamEtherExt ¶
type StreamEtherExt struct {
ID string `json:"id,omitempty"`
ReleaseName string `json:"releaseName,omitempty"`
Size int64 `json:"size,omitempty"`
Languages []string `json:"languages,omitempty"`
Lazy bool `json:"lazy,omitempty"`
File string `json:"file,omitempty"`
}
StreamEtherExt holds Ether-specific extensions on a StreamItem.
type StreamItem ¶
type StreamItem struct {
Name string `json:"name,omitempty"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
URL string `json:"url,omitempty"`
InfoHash string `json:"infoHash,omitempty"`
FileIdx *int `json:"fileIdx,omitempty"`
YtID string `json:"ytId,omitempty"`
ExternalURL string `json:"externalUrl,omitempty"`
Ether *StreamEtherExt `json:"ether,omitempty"`
}
StreamItem is one playable candidate returned by a Stream route handler.
type StreamRequest ¶
type StreamRequest struct {
Type string `json:"type"`
ID string `json:"id"`
Extra map[string]string `json:"extra"`
}
StreamRequest is passed to a Stream route handler.
type StreamResponse ¶
type StreamResponse struct {
Streams []StreamItem `json:"streams"`
}
StreamResponse is returned by a Stream route handler.
type StremioCatalogDecl ¶
type StremioCatalogDecl struct {
Type string `json:"type"`
ID string `json:"id"`
Name string `json:"name,omitempty"`
Extra []StremioExtraField `json:"extra,omitempty"`
}
StremioCatalogDecl declares a Stremio catalog in the manifest.
type StremioExtraField ¶
type StremioExtraField struct {
Name string `json:"name"`
IsRequired bool `json:"isRequired,omitempty"`
Options []string `json:"options,omitempty"`
OptionsLimit int `json:"optionsLimit,omitempty"`
}
StremioExtraField declares a supported extra parameter on a catalog.
type StringsCatalog ¶
StringsCatalog maps locale codes (e.g. "en", "fr-CA") to key-value tables.
type SubtitleDownloadRequest ¶
type SubtitleDownloadRequest struct {
ID string `json:"id"`
Lang string `json:"lang"`
Format string `json:"format"`
}
SubtitleDownloadRequest identifies a viewer-picked subtitle.
type SubtitleDownloadResponse ¶
type SubtitleDownloadResponse struct {
Format string `json:"format"`
Encoding string `json:"encoding"`
Data string `json:"data"`
}
SubtitleDownloadResponse carries subtitle text or base64-encoded bytes.
type SubtitleItem ¶
type SubtitleItem struct {
ID string `json:"id"`
URL string `json:"url,omitempty"`
Name string `json:"name,omitempty"`
Lang string `json:"lang"`
Label string `json:"label,omitempty"`
Format string `json:"format,omitempty"`
HearingImpaired bool `json:"hearingImpaired,omitempty"`
}
SubtitleItem is one subtitle track returned by a Subtitles route handler.
type SubtitlesMeta ¶
type SubtitlesMeta struct {
Languages any `json:"languages,omitempty"`
Formats []string `json:"formats,omitempty"`
}
SubtitlesMeta describes a subtitle provider contribution.
type SubtitlesRequest ¶
type SubtitlesRequest struct {
Type string `json:"type"`
ID string `json:"id"`
Extra map[string]string `json:"extra"`
PickToken string `json:"pickToken"`
VideoHash string `json:"videoHash,omitempty"`
VideoSize int64 `json:"videoSize,omitempty"`
Filename string `json:"filename,omitempty"`
}
SubtitlesRequest is passed to a Subtitles route handler.
type SubtitlesResponse ¶
type SubtitlesResponse struct {
Subtitles []SubtitleItem `json:"subtitles"`
}
SubtitlesResponse is returned by a Subtitles route handler.
type TemplateOptions ¶
type TemplateOptions struct {
ID string
Name string
Version string
Summary string
Description string
}
TemplateOptions configures a capability starter template.
type TestHarness ¶
TestHarness wraps a Plugin and MockHost for ergonomic unit tests.
func NewTestHarness ¶
func NewTestHarness(p *Plugin, host *MockHost) *TestHarness
NewTestHarness constructs a TestHarness for p.
func (*TestHarness) Action ¶
func (h *TestHarness) Action(action string, input map[string]any) (int, ActionResponse, error)
Action calls POST /ether/action/{action}.
func (*TestHarness) Catalog ¶
func (h *TestHarness) Catalog(typ, id string, extra map[string]string) (int, CatalogResponse, error)
Catalog calls GET /catalog/{type}/{id}[/{extra}].json.
func (*TestHarness) Configure ¶
func (h *TestHarness) Configure(settings map[string]any) (int, ConfigureResponse, error)
Configure calls POST /ether/configure.
func (*TestHarness) Event ¶
func (h *TestHarness) Event(events []EtherEvent) (int, EventResponse, error)
Event calls POST /ether/event.
func (*TestHarness) File ¶
func (h *TestHarness) File(path, rangeHeader string) RawResponse
File calls GET /ether/file/{path}.
func (*TestHarness) Health ¶
func (h *TestHarness) Health() (int, HealthResponse, error)
Health calls GET /ether/health.
func (*TestHarness) Manifest ¶
func (h *TestHarness) Manifest() (Manifest, error)
Manifest fetches and decodes GET /manifest.json.
func (*TestHarness) Markers ¶
func (h *TestHarness) Markers(typ, id string) (int, MarkersResponse, error)
Markers calls GET /ether/markers/{type}/{id}.json.
func (*TestHarness) Meta ¶
func (h *TestHarness) Meta(typ, id string) (int, MetaResponse, error)
Meta calls GET /meta/{type}/{id}.json.
func (*TestHarness) Options ¶
func (h *TestHarness) Options(field string) (int, []FieldOption, error)
Options calls GET /ether/options/{field}.
func (*TestHarness) Request ¶
func (h *TestHarness) Request(req RawRequest) RawResponse
Request dispatches a RawRequest against the plugin.
func (*TestHarness) Stream ¶
func (h *TestHarness) Stream(typ, id string, extra map[string]string) (int, StreamResponse, error)
Stream calls GET /stream/{type}/{id}[/{extra}].json.
func (*TestHarness) Subtitles ¶
func (h *TestHarness) Subtitles(typ, id, pickToken string, extra map[string]string) (int, SubtitlesResponse, RawResponse)
Subtitles calls GET /subtitles/{type}/{id}[/{extra}].json with optional pickToken.
func (*TestHarness) Test ¶
func (h *TestHarness) Test() (int, TestResponse, error)
Test calls POST /ether/test.
func (*TestHarness) Tool ¶
func (h *TestHarness) Tool(name string, input map[string]any) (int, ToolResponse, error)
Tool calls POST /ether/tool/{name}.
func (*TestHarness) View ¶
func (h *TestHarness) View(page string, query map[string]string) (int, ViewResponse, error)
View calls GET /ether/view/{page} and validates the returned ViewSpecDocument.
type TestResponse ¶
type TestResponse struct {
OK bool `json:"ok"`
Message string `json:"message,omitempty"`
Details []string `json:"details,omitempty"`
}
TestResponse is returned by a Test route handler.
type ToolMeta ¶
type ToolMeta struct {
Name string `json:"name"`
Title string `json:"title"`
Description string `json:"description"`
InputSchema map[string]any `json:"inputSchema"`
ReadOnly bool `json:"readOnly,omitempty"`
Destructive bool `json:"destructive,omitempty"`
OpenWorld bool `json:"openWorld,omitempty"`
}
ToolMeta describes an agent tool contributed by a plugin.
type ToolRequest ¶
ToolRequest is passed to a Tool route handler.
type ToolResponse ¶
ToolResponse is returned by a Tool route handler.
type TunnelError ¶
TunnelError carries an error payload inside a TunnelFrame.
type TunnelFrame ¶
type TunnelFrame struct {
Type string `json:"type"`
ID string `json:"id,omitempty"`
RequestID string `json:"requestId,omitempty"`
Protocol string `json:"protocol,omitempty"`
Plugin string `json:"plugin,omitempty"`
PluginID string `json:"pluginId,omitempty"`
ConnectionID string `json:"connectionId,omitempty"`
Features []string `json:"features,omitempty"`
MaxChunkBytes int `json:"maxChunkBytes,omitempty"`
Method string `json:"method,omitempty"`
Path string `json:"path,omitempty"`
Query map[string][]string `json:"query,omitempty"`
Headers map[string][]string `json:"headers,omitempty"`
Body string `json:"body,omitempty"`
Data string `json:"data,omitempty"`
Status int `json:"status,omitempty"`
Deadline string `json:"deadline,omitempty"`
Error *TunnelError `json:"error,omitempty"`
}
TunnelFrame is one JSON message sent or received over the `ether-tunnel-v1` WebSocket.
type ViewRequest ¶
ViewRequest is passed to a View route handler.
type ViewResponse ¶
type ViewResponse struct {
Spec ViewSpecDocument `json:"spec"`
Data map[string]any `json:"data"`
}
ViewResponse is returned by a View route handler.
type ViewSpecDocument ¶
type ViewSpecDocument struct {
SpecVersion int `json:"specVersion"`
Layout string `json:"layout"`
Route string `json:"route,omitempty"`
Header []map[string]any `json:"header"`
Body []map[string]any `json:"body"`
}
ViewSpecDocument is a declarative ViewSpec v1 page specification.
type ViewerPermissions ¶
type ViewerPermissions struct {
Identity string `json:"identity,omitempty"`
WatchHistory string `json:"watch_history,omitempty"`
WriteWatchState string `json:"write_watch_state,omitempty"`
}
ViewerPermissions scopes viewer identity and watch-history access.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
kit
|
|
|
archivex
Package archivex lists and reads members of zip-shaped containers (zip, cbz, epub) with the standard library only, so hosted builds stay possible.
|
Package archivex lists and reads members of zip-shaped containers (zip, cbz, epub) with the standard library only, so hosted builds stay possible. |
|
feedparse
Package feedparse turns RSS, Atom and JSON Feed documents into kind items: one feed is usually one root item with entries as leaves.
|
Package feedparse turns RSS, Atom and JSON Feed documents into kind items: one feed is usually one root item with entries as leaves. |
|
hints
Package hints marshals typed resolver hints for KindItem.Hints.
|
Package hints marshals typed resolver hints for KindItem.Hints. |
|
metaparse
Package metaparse reads embedded metadata (EPUB OPF, ComicInfo.xml, ID3v2, FLAC Vorbis comments) into plain maps a provider can copy into KindItem fields.
|
Package metaparse reads embedded metadata (EPUB OPF, ComicInfo.xml, ID3v2, FLAC Vorbis comments) into plain maps a provider can copy into KindItem fields. |
|
scrape
Package scrape fetches pages through Ether's host HTTP service (so the manifest network allowlist applies) and pulls text and links out of simple HTML.
|
Package scrape fetches pages through Ether's host HTTP service (so the manifest network allowlist applies) and pulls text and links out of simple HTML. |