Documentation
¶
Overview ¶
Package kindstream is Ether's unified byte-acquisition and streaming layer for plugin-provided content kinds (spec §4, §5, §15.1). Every plugin media item — comic page, ebook file, audiobook chapter, podcast episode, or video part — resolves through kindstream into a random-access ByteSource.
Index ¶
- Constants
- Variables
- func ExpandHeaders(ctx context.Context, headers map[string]string, ...) (map[string]string, error)
- func Handler(src ByteSource) http.Handler
- func MediaSource(src ByteSource, releaseID string) core.MediaSource
- func Seeker(src ByteSource) io.ReadSeekCloser
- func ServeLoopback(src ByteSource) (urlStr string, closeFn func() error, err error)
- type ByteSource
- func ExtractMember(ctx context.Context, c core.ContainerRef, format archive.Format, ...) (ByteSource, error)
- func Open(ctx context.Context, src core.PlayableSource, deps Deps) (ByteSource, error)
- func OpenContainer(ctx context.Context, c core.ContainerRef, declaredMIME string, maxBytes int64, ...) (ByteSource, error)
- func OpenSources(ctx context.Context, sources []core.PlayableSource, deps Deps) (ByteSource, core.PlayableSource, error)
- type Deps
- type DirectSource
- func (d *DirectSource) Close() error
- func (d *DirectSource) DirectHeaders() map[string]string
- func (d *DirectSource) DirectURL() string
- func (d *DirectSource) MIME() string
- func (d *DirectSource) Name() string
- func (d *DirectSource) ReadAt(p []byte, off int64) (int, error)
- func (d *DirectSource) Size() int64
- type HTTPReaderAt
- type HostBackoff
- type Leaf
- type Sequence
- func (s *Sequence) Close() error
- func (s *Sequence) IsLeafOpen(i int) bool
- func (s *Sequence) Leaf(i int) (ByteSource, error)
- func (s *Sequence) LeafContext(ctx context.Context, i int) (ByteSource, error)
- func (s *Sequence) Len() int
- func (s *Sequence) MIME() string
- func (s *Sequence) Name() string
- func (s *Sequence) OpenLeafCount() int
- func (s *Sequence) ReadAt(p []byte, off int64) (int, error)
- func (s *Sequence) Size() int64
- type TemplateVars
- type URLTemplate
Constants ¶
const ( PatternRange = "range" PatternDownload = "download" PatternDirect = "direct" PatternContainer = "container" PatternRelease = "release" )
Retrieval pattern constants (spec §4.1, §4.3).
Variables ¶
var ( // ErrMemberUndecodable is returned when an archive member is compressed or // solid in a format/method that internal/archive cannot decode sequentially. ErrMemberUndecodable = errors.New("kindstream: archive member not sequentially decodable") // ErrHostNotAllowed is returned when a source URL's host is rejected by the // plugin's declared network allowlist. ErrHostNotAllowed = errors.New("kindstream: host not in plugin allowlist") // ErrSizeLimitExceeded is returned when a download or range-demoted source // exceeds its configured maxBytes or library cache quota. ErrSizeLimitExceeded = errors.New("kindstream: source exceeds download size limit") // ErrMIMEMismatch is returned when sniffed magic bytes disagree with the // plugin-declared MIME at the family level (e.g. text/html error page for an // image/* source). ErrMIMEMismatch = errors.New("kindstream: sniffed MIME family disagrees with declared MIME") )
var ( // ErrInvalidTemplate is returned when a URL template or secret placeholder // is malformed or expands to a disallowed URL. ErrInvalidTemplate = errors.New("kindstream: invalid URL template") )
Functions ¶
func ExpandHeaders ¶
func ExpandHeaders(ctx context.Context, headers map[string]string, secretFn func(ctx context.Context, name string) (string, error)) (map[string]string, error)
ExpandHeaders resolves {secret:name} placeholders in header values via secretFn. Resolved values must never be logged by callers.
func Handler ¶
func Handler(src ByteSource) http.Handler
Handler returns an http.Handler that serves a ByteSource with full HTTP Range support (206 Partial Content, Accept-Ranges: bytes, Content-Type, and Content-Length).
func MediaSource ¶
func MediaSource(src ByteSource, releaseID string) core.MediaSource
MediaSource adapts a ByteSource into a core.MediaSource so any pipeline accepting core.MediaSource can consume a kindstream ByteSource directly.
func Seeker ¶
func Seeker(src ByteSource) io.ReadSeekCloser
Seeker wraps any ByteSource as an io.ReadSeekCloser so it can be passed to http.ServeContent, standard library readers, or transcode pipelines.
func ServeLoopback ¶
func ServeLoopback(src ByteSource) (urlStr string, closeFn func() error, err error)
ServeLoopback starts a loopback HTTP server (http://127.0.0.1:<port>/<name>) backed by src so local ffmpeg / ffprobe sessions in internal/transcode can seek and stream any ByteSource over HTTP Range (spec §15.1). Calling closeFn shuts down the loopback server without closing src.
Types ¶
type ByteSource ¶
type ByteSource interface {
io.ReaderAt
Size() int64 // total byte length; always > 0 once Open returns
Name() string // filename for Content-Disposition and container sniffing
MIME() string // MIME type (plugin-declared, verified/refined by sniffing first 512 B)
Close() error
}
ByteSource is the uniform random-access stream interface produced by kindstream for all plugin content kinds (spec §5.1).
func ExtractMember ¶
func ExtractMember( ctx context.Context, c core.ContainerRef, format archive.Format, selected archive.Member, nonDirIdx int, declaredMIME string, maxBytes int64, deps Deps, ) (ByteSource, error)
ExtractMember downloads the container's volume(s) into deps.Cache (if not already local) and sequentially decodes the target member into a cached file in deps.Cache, returning a random-access ByteSource over that file.
If the archive format or compression method is not sequentially decodable by internal/archive, ExtractMember returns ErrMemberUndecodable.
func Open ¶
func Open(ctx context.Context, src core.PlayableSource, deps Deps) (ByteSource, error)
Open resolves a single core.PlayableSource into a random-access ByteSource according to its retrieval pattern (spec §4.3, §5.1).
func OpenContainer ¶
func OpenContainer(ctx context.Context, c core.ContainerRef, declaredMIME string, maxBytes int64, deps Deps) (ByteSource, error)
OpenContainer opens an archive container (RAR4/RAR5/7z/ZIP) over one or more VolumeRefs and returns a ByteSource for the requested inner member (spec §5.3).
Store-mode members (including encrypted store-mode RAR/7z when PasswordSecret is supplied) are served directly via archive.NewMemberReader without extracting the archive. Compressed ZIP members are sequentially decoded into deps.Cache via ExtractMember; undecodable compressed archives (such as compressed RAR) fail with ErrMemberUndecodable so the caller can fall back to the next core.PlayableSource.
func OpenSources ¶
func OpenSources(ctx context.Context, sources []core.PlayableSource, deps Deps) (ByteSource, core.PlayableSource, error)
OpenSources tries each candidate core.PlayableSource in order, returning the first ByteSource that opens and validates cleanly. When all candidates fail (or sources is empty), OpenSources returns an error wrapping core.ErrSourceUnavailable (spec §4.5).
type Deps ¶
type Deps struct {
HTTP *http.Client
Cache *blobcache.Namespace
OpenRelease func(ctx context.Context, r core.ReleaseHint) (core.MediaSource, error)
Secret func(ctx context.Context, name string) (string, error)
AllowHost func(host string) bool
MaxDownloadBytes int64
ItemIndex int
Backoff *HostBackoff
}
Deps supplies the runtime dependencies needed by Open and OpenSources (spec §5.1).
type DirectSource ¶
type DirectSource struct {
// contains filtered or unexported fields
}
DirectSource represents a validated "direct" core.PlayableSource. Callers that support client redirects (spec §4.3) can extract the target URL via DirectURL() and issue a 307 Temporary Redirect without proxying bytes; callers that require a ByteSource stream (such as tests or local transcode bridges) can call ReadAt, which lazily opens the underlying HTTP source.
func AsDirectSource ¶
func AsDirectSource(bs ByteSource) (*DirectSource, bool)
AsDirectSource extracts the *DirectSource if bs was opened from a "direct" core.PlayableSource.
func (*DirectSource) Close ¶
func (d *DirectSource) Close() error
func (*DirectSource) DirectHeaders ¶
func (d *DirectSource) DirectHeaders() map[string]string
DirectHeaders returns the resolved request headers for the direct URL.
func (*DirectSource) DirectURL ¶
func (d *DirectSource) DirectURL() string
DirectURL returns the validated, template-expanded target URL for a 307 redirect.
func (*DirectSource) MIME ¶
func (d *DirectSource) MIME() string
func (*DirectSource) Name ¶
func (d *DirectSource) Name() string
func (*DirectSource) Size ¶
func (d *DirectSource) Size() int64
type HTTPReaderAt ¶
type HTTPReaderAt struct {
// contains filtered or unexported fields
}
HTTPReaderAt implements ByteSource (and io.ReaderAt) over an HTTP Range or range-templated URL source.
func (*HTTPReaderAt) Close ¶
func (r *HTTPReaderAt) Close() error
Close is a no-op for stateless HTTP range readers.
func (*HTTPReaderAt) MIME ¶
func (r *HTTPReaderAt) MIME() string
MIME returns the resource's MIME type.
func (*HTTPReaderAt) Name ¶
func (r *HTTPReaderAt) Name() string
Name returns the resource's filename.
func (*HTTPReaderAt) ReadAt ¶
func (r *HTTPReaderAt) ReadAt(p []byte, off int64) (int, error)
ReadAt reads len(p) bytes starting at byte offset off.
func (*HTTPReaderAt) Size ¶
func (r *HTTPReaderAt) Size() int64
Size returns the total byte size of the resource.
func (*HTTPReaderAt) SniffBytes ¶
func (r *HTTPReaderAt) SniffBytes() []byte
SniffBytes returns up to the first 512 bytes captured during the initial probe.
type HostBackoff ¶
type HostBackoff struct {
// contains filtered or unexported fields
}
HostBackoff tracks per-host exponential backoff and Retry-After windows on 429 and 503 responses.
func NewHostBackoff ¶
func NewHostBackoff() *HostBackoff
NewHostBackoff creates a per-host backoff tracker.
type Leaf ¶
type Leaf struct {
Name string
MIME string
Size int64
Open func(ctx context.Context) (ByteSource, error)
}
Leaf describes one part/page/chapter in a multi-part Sequence (spec §4.4, §5.1).
type Sequence ¶
type Sequence struct {
// contains filtered or unexported fields
}
Sequence presents an ordered list of Leaf sources both as discrete addressable leaves (Leaf(i)) and as a single concatenated ByteSource (ReadAt) when every leaf has a known positive Size (spec §4.4, §5.1).
Sequence opens leaves lazily on demand, holds at most 2 leaves open at a time (current + next), and prefetches the next leaf when a read crosses 90% of the current leaf.
func NewSequence ¶
NewSequence constructs a Sequence over leaves.
func (*Sequence) IsLeafOpen ¶
IsLeafOpen reports whether leaf i is currently open in the sequence pool.
func (*Sequence) Leaf ¶
func (s *Sequence) Leaf(i int) (ByteSource, error)
Leaf opens (or returns the already-open) ByteSource for leaf i (0-based). The returned ByteSource shares the sequence's open-leaf pool; calling Close on the returned leaf handle is a no-op (Sequence.Close closes all open leaves).
func (*Sequence) LeafContext ¶
LeafContext is Leaf with an explicit context for opening the leaf on cache miss.
func (*Sequence) OpenLeafCount ¶
OpenLeafCount returns the number of currently open leaves (at most 2).
type TemplateVars ¶
TemplateVars holds the integer values allowed in a URL template (spec §4.2). Values are strictly non-negative integers; arbitrary string interpolation is never performed.
type URLTemplate ¶
type URLTemplate struct {
// contains filtered or unexported fields
}
URLTemplate is a parsed and validated URL template.
func ParseURLTemplate ¶
func ParseURLTemplate(raw string, allowHost func(host string) bool) (*URLTemplate, error)
ParseURLTemplate validates that raw contains only known placeholders ({itemIndex}, {pageNumber}, {byteStart}, {byteEnd}, {byteRange}) and that a trial expansion with zero values yields a valid http/https URL with a host.
func (*URLTemplate) Expand ¶
func (t *URLTemplate) Expand(v TemplateVars, allowHost func(host string) bool) (string, error)
Expand substitutes integer placeholders and validates the resulting URL's scheme (http/https), non-empty host, and optional host allowlist.
func (*URLTemplate) HasByteRange ¶
func (t *URLTemplate) HasByteRange() bool
HasByteRange reports whether the template embeds a bounded byte range ({byteRange} or both {byteStart} and {byteEnd}) in the URL itself.
func (*URLTemplate) HasByteStartOnly ¶
func (t *URLTemplate) HasByteStartOnly() bool
HasByteStartOnly reports whether the template embeds {byteStart} without an end byte, indicating an upstream that seeks from {byteStart} without a Range request header (spec §4.2).
func (*URLTemplate) UsesRangePlaceholders ¶
func (t *URLTemplate) UsesRangePlaceholders() bool
UsesRangePlaceholders reports whether any byte-offset placeholder is present.