onair

package
v0.3.5 Latest Latest
Warning

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

Go to latest
Published: Nov 7, 2026 License: GPL-3.0 Imports: 54 Imported by: 0

README

On Air rendering

scene owns document v1, strict decoding, binding/keyframe evaluation and cost validation. families.Get(family, moment) loads an embedded document; families.Defaults(family) supplies example bindings. raster.Render is a pure function. Supply immutable, already decoded images through raster.Bindings.Assets; resolve slot values to their asset keys. There is no network access, disk access or per-frame concurrency in the rasterizer.

encode.Encode writes the default 1080p30 master. encode.WithOptions accepts synthesized audio, output size and a total bitrate ceiling for Signal loops. Use encode.Hash with the complete profile/options to address a cached clip. The queue, transcode slot acquisition, asset loading and cache lifecycle belong to the integrating caller. Text uses kerning/tracking and static bundled faces; complex shaping and CJK download are not implemented.

Reproduce the gates:

nice -n 10 go test -count=1 -p 2 ./internal/onair/...
nice -n 10 go test ./internal/onair/raster -run '^$' -bench BenchmarkRender -benchtime=1x
nice -n 10 go run ./cmd/onair-bench -out /tmp/onair-master.mp4
nice -n 10 go run ./cmd/onair-bench -family signal -moment screen.static -width 1280 -maxrate 4M -out /tmp/static.mp4
GOOS=js GOARCH=wasm go build -o /tmp/onair.wasm ./cmd/onair-wasm
GOOS=js GOARCH=wasm go test -exec "$(go env GOROOT)/lib/wasm/go_js_wasm_exec" ./internal/onair/raster -run TestParity -count=1

Update the PNG goldens intentionally with UPDATE_GOLDEN=1 go test ./internal/onair/raster. Fonts are reproducibly sourced from the revision in fonts/data/revision.txt via tools/subset-onair-fonts.py (a Python venv with fonttools is required). Special Elite is Apache 2.0; the other faces are OFL.

Phase 0, Apple M4 Pro: final eight-second Late night ident encoded in 12.749 s with four workers (1.594×; Mac target 1.5× was missed). Late night ident WASM rendered at 4.43 fps at 960×540 in host Chrome, well below the 45 fps gate. Decision 4 uses the TypeScript Canvas2D fallback with Go golden parity in the console (web/src/pages/livetv/onair/scene-renderer.ts). Reproduce its browser gates with tools/onair/README.md. Cropped scratch buffers, per-job prepared fonts/masks and a measured TinyGo spike are possible improvements; none has been demonstrated to close the gap. The production server and Windows were not tested.

Documentation

Overview

Package onair owns owner looks, assets, reels and background master clips.

Index

Constants

View Source
const StyleDraftGuide = `` /* 1794-byte string literal not displayed */

StyleDraftGuide is the renderer contract shared by generation and live agent drafts.

Variables

View Source
var ErrNotFound = errors.New("onair: not found")
View Source
var ErrUsed = errors.New("onair: asset is in use")

Functions

func BoundaryDeadline

func BoundaryDeadline(ctx context.Context) (time.Time, bool)

func ServeFont

func ServeFont(w http.ResponseWriter, r *http.Request)

MCP exclusion: these owner-authenticated routes supply immutable rendering resources to the console. They do not expose owner records or mutations; fetching binary fonts and bundled scene data has no useful agent tool analogue.

func ServeSystemScreens

func ServeSystemScreens(w http.ResponseWriter, r *http.Request)

func WithBoundaryDeadline

func WithBoundaryDeadline(ctx context.Context, at time.Time) context.Context

WithBoundaryDeadline identifies an imminent master without changing the job's cancellation deadline or its content-addressed cache key.

Types

type Asset

type Asset struct {
	Source      string   `json:"source,omitempty"`
	Attribution string   `json:"attribution,omitempty"`
	Status      string   `json:"status,omitempty"`
	Progress    float64  `json:"progress,omitempty"`
	Error       string   `json:"error,omitempty"`
	Digest      string   `json:"digest"`
	Name        string   `json:"name"`
	Kind        string   `json:"kind"`
	Size        int64    `json:"size"`
	License     string   `json:"license,omitempty"`
	UsedBy      []string `json:"usedBy"`
	URL         string   `json:"url,omitempty"`
	MIME        string   `json:"-"`
	DurationMS  int64    `json:"durationMs,omitempty"`
	Width       int      `json:"width,omitempty"`
	Height      int      `json:"height,omitempty"`
}

type Brand

type Brand struct {
	Palette struct {
		Background string `json:"background"`
		Accent     string `json:"accent"`
		Text       string `json:"text"`
		Glow       string `json:"glow"`
	} `json:"palette"`
	Type struct {
		Display string `json:"display"`
		Body    string `json:"body"`
	} `json:"type"`
	Energy   float64  `json:"energy"`
	Textures []string `json:"textures"`
	Tagline  string   `json:"tagline"`
	Language string   `json:"language"`
}

type Chatter

type Chatter interface {
	Enabled() bool
	Chat(context.Context, []llm.Message, bool) (string, error)
}

Chatter is the owner's configured model. Proposals never save records.

type ClipUploadMetadata

type ClipUploadMetadata struct {
	Source      string
	Attribution string
}

ClipUploadMetadata preserves provenance when a local file is imported into a reel.

type ImportJob

type ImportJob struct {
	UseSourceTitle bool    `json:"useSourceTitle,omitempty"`
	ID             string  `json:"id"`
	Source         string  `json:"source"`
	Attribution    string  `json:"attribution,omitempty"`
	Name           string  `json:"name"`
	Kind           string  `json:"kind"`
	Status         string  `json:"status"`
	Progress       float64 `json:"progress"`
	Error          string  `json:"error,omitempty"`
}

type ImportRequest

type ImportRequest struct {
	URL  string   `json:"url"`
	URLs []string `json:"urls"`
	Name string   `json:"name"`
	Kind string   `json:"kind"`
}

Import jobs share the bounded background queue with look preparation. No network request or media probe runs on the console request goroutine.

type LibraryAsset

type LibraryAsset struct {
	Asset
	MIME      string       `json:"mime"`
	UsedBy    []LibraryUse `json:"usedBy"`
	CreatedAt int64        `json:"createdAt"`
}

type LibraryUse

type LibraryUse struct {
	Kind  string `json:"kind"`
	ID    string `json:"id"`
	Name  string `json:"name"`
	Scene string `json:"scene,omitempty"`
}

type Look

type Look struct {
	ID           string                `json:"id"`
	Name         string                `json:"name"`
	Template     string                `json:"template"`
	Brand        Brand                 `json:"brand"`
	Roles        map[string]RoleConfig `json:"roles"`
	Scenes       []StyleScene          `json:"scenes"`
	Controls     []StyleControl        `json:"controls"`
	ChannelIDs   []string              `json:"channelIds"`
	ChannelCount int                   `json:"channelCount"`
	Bug          *core.BugSettings     `json:"bug,omitempty"`
	Screens      *config.LiveTVScreens `json:"screens,omitempty"`
	Pack         *Pack                 `json:"pack,omitempty"`
	Render       *Progress             `json:"render,omitempty"`
}

type Options

type Options struct {
	// Changed runs after an owner style save has committed and locks are released.
	Changed        func(context.Context) error
	StyleChannels  func(context.Context) (map[string][]string, error)
	ReelChannels   func(context.Context) (map[string][]string, error)
	PluginSegment  func(string) (PluginSegment, bool)
	StaticRequests func(context.Context, string) []core.ClipRequest
	DB             *sql.DB
	Chat           Chatter
	Dir            string
	FFmpeg         func() string
	Probe          func(context.Context, string) (*core.MediaInfo, error)
	Acquire        func(context.Context) (context.Context, func(), error)
	Bindings       func(context.Context, string, string, time.Time) (any, error)
	// Upcoming supplies requests for the next three hours of watched channels.
	Upcoming    func(context.Context) []core.ClipRequest
	Logger      *slog.Logger
	CacheBytes  int64
	WorkingSize func() (int, int)
	Artwork     func(context.Context, string) (image.Image, error)
	// Render is a seam for verifying that Ensure never executes work inline.
	Render func(context.Context, scene.Document, raster.Bindings, string) error
}

type Pack

type Pack struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

type PluginContentCache

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

PluginContentCache connects the host's background publisher to render readers. Values are immutable after publication; the cache owns the map itself.

func NewPluginContentCache

func NewPluginContentCache() *PluginContentCache

func (*PluginContentCache) EnablePlugin

func (c *PluginContentCache) EnablePlugin(plugin string)

func (*PluginContentCache) Lookup

func (c *PluginContentCache) Lookup(id string) (PluginSegment, bool)

func (*PluginContentCache) Publish

func (c *PluginContentCache) Publish(values map[string]PluginSegment)

func (*PluginContentCache) RemovePlugin

func (c *PluginContentCache) RemovePlugin(plugin string)

type PluginSegment

type PluginSegment struct {
	Document scene.Document
	Slots    map[string]string
	Images   map[string]image.Image
	Version  string
	Expires  time.Time
	Rating   string
	Name     string
}

PluginSegment is an immutable background-loaded snapshot. Lookup must do no I/O. Expiry gates playback and rendering, never the declared schedule duration.

type Progress

type Progress struct {
	Done  int `json:"done"`
	Total int `json:"total"`
}

type Reel

type Reel struct {
	Anchor        time.Time  `json:"-"`
	ID            string     `json:"id"`
	Name          string     `json:"name"`
	Order         string     `json:"order"`
	RepeatHours   float64    `json:"repeatHours"`
	LoudnessMatch bool       `json:"loudnessMatch"`
	TrimSilence   bool       `json:"trimSilence"`
	Clips         []ReelClip `json:"clips"`
	ChannelIDs    []string   `json:"channelIds"`
}

type ReelClip

type ReelClip struct {
	Source       string  `json:"source,omitempty"`
	Attribution  string  `json:"attribution,omitempty"`
	ID           string  `json:"id"`
	Name         string  `json:"name"`
	DurationMS   int64   `json:"durationMs"`
	SourceDigest string  `json:"sourceDigest,omitempty"`
	Status       string  `json:"status"`
	Progress     float64 `json:"progress,omitempty"`
	Size         int64   `json:"size,omitempty"`
	Preview      string  `json:"preview,omitempty"`
	Error        string  `json:"error,omitempty"`
}

type RoleConfig

type RoleConfig struct {
	Duration float64 `json:"duration"`
}

type SceneSound

type SceneSound struct {
	ID          string   `json:"id"`
	AssetDigest string   `json:"assetDigest"`
	At          float64  `json:"at"`
	Start       float64  `json:"start"`
	End         *float64 `json:"end,omitempty"`
	Volume      float64  `json:"volume"`
	FadeIn      float64  `json:"fadeIn"`
	FadeOut     float64  `json:"fadeOut"`
}

type SceneVideo

type SceneVideo struct {
	AssetDigest string  `json:"assetDigest"`
	Mode        string  `json:"mode"`
	Fit         string  `json:"fit"`
	Start       float64 `json:"start"`
	Crossfade   bool    `json:"crossfade,omitempty"`
}

type Service

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

func New

func New(o Options) (*Service, error)

func (*Service) AssetMetadata

func (s *Service) AssetMetadata(ctx context.Context, digest string) (Asset, error)

AssetMetadata reads one asset's stored facts by digest: a single indexed row, with no usage scan, no content read and no probing. Duration is 0 until the asset has been probed.

func (*Service) Assets

func (s *Service) Assets(ctx context.Context) ([]Asset, error)

func (*Service) Clip

func (*Service) Close

func (s *Service) Close()

func (*Service) Crossfade

func (s *Service) Crossfade(look, moment string) bool

Crossfade is read from saved look metadata by the background input planner.

func (*Service) DeleteAsset

func (s *Service) DeleteAsset(ctx context.Context, d string) error

func (*Service) DeleteClip

func (s *Service) DeleteClip(ctx context.Context, reel, id string) error

func (*Service) DeleteLook

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

func (*Service) DeleteReel

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

func (*Service) Draft

func (s *Service) Draft(ctx context.Context, l Look) (Look, error)

func (*Service) Duration

func (s *Service) Duration(id, m string) (time.Duration, bool)

func (*Service) Ensure

func (s *Service) Ensure(_ context.Context, r core.ClipRequest)

func (*Service) ExportStylePack

func (s *Service) ExportStylePack(ctx context.Context, id string) ([]byte, []string, error)

ExportStylePack uses the plugin package digest and data runtime; no executable files, credentials, viewers or channel bindings leave this server.

func (*Service) Generate

func (s *Service) Generate(ctx context.Context, prompt, summary string, base Look) (Look, error)

func (*Service) GenerateScene

func (s *Service) GenerateScene(ctx context.Context, prompt, summary string, base Look, sceneID string) (Look, error)

func (*Service) Handler

func (s *Service) Handler(resources ...func(chi.Router)) http.Handler

Handler must be mounted behind the application's owner authentication and CSRF guard, like livetv.Handler. MCP exclusions: these routes are console designer transport. Binary assets, multipart clips and previews cannot be represented by JSON MCP tools; look/reel transport accompanies the visual stage and explicit Save. Agent look drafts/saves use the separate proposal-backed builtin tools. Bindings is an ephemeral stage preview; it does not mutate server state. ResourceRoutes mounts W6's font and family resource handlers on this router.

func (*Service) Import

func (s *Service) Import(ctx context.Context, reel string, in ImportRequest) ([]ImportJob, error)

func (*Service) ImportStyleFiles

func (s *Service) ImportStyleFiles(ctx context.Context, packID string, files map[string][]byte) (Look, error)

ImportStyleFiles is the plugin install bridge for upload, URL and Marketplace.

func (*Service) ImportStylePack

func (s *Service) ImportStylePack(ctx context.Context, raw []byte) (Look, error)

func (*Service) Library

func (s *Service) Library(ctx context.Context) ([]LibraryAsset, error)

func (*Service) LibraryRoutes

func (s *Service) LibraryRoutes(r chi.Router)

func (*Service) LiveStatic

func (s *Service) LiveStatic(ctx context.Context, r core.ClipRequest) bool

LiveStatic reports readiness without requiring a rendered clip for a scene whose analogue background is supplied continuously by the Signal engine.

func (*Service) Look

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

func (*Service) Looks

func (s *Service) Looks(ctx context.Context) ([]Look, error)

func (*Service) Overlay

Overlay prepares an immutable scene and decoded resources for the playout background planner. It never renders; frame ticks evaluate the pure renderer.

func (*Service) Reel

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

func (*Service) ReelClipPath

func (s *Service) ReelClipPath(ctx context.Context, reelID, clipID string) (string, bool)

ReelClipPath reads clip metadata and the local master index. A miss is materialized by the background queue, and the caller skips it until a later planning pass.

func (*Service) ReelSnapshot

func (s *Service) ReelSnapshot(ctx context.Context, id string) (Reel, error)

ReelSnapshot reads the durable owner clip ordering and conversion states.

func (*Service) Reels

func (s *Service) Reels(ctx context.Context) ([]Reel, error)

func (*Service) RenderLead

func (s *Service) RenderLead(look, moment string) time.Duration

RenderLead reflects measured 1080p master work, including slot contention. Every channel decodes these same masters; lower-resolution outputs inherit the conservative native-resolution estimate. Cached repeats need no work.

func (*Service) SaveLook

func (s *Service) SaveLook(ctx context.Context, l Look) (Look, error)

func (*Service) SaveReel

func (s *Service) SaveReel(ctx context.Context, r Reel) (Reel, error)

func (*Service) SegmentInfo

func (s *Service) SegmentInfo(moment string) (name, rating string, ok bool)

SegmentInfo supplies immutable policy metadata without accessing the database.

func (*Service) Start

func (s *Service) Start()

Start begins workers after application dependencies have been constructed.

func (*Service) StudioRoutes

func (s *Service) StudioRoutes(r chi.Router)

func (*Service) StyleScreens

func (s *Service) StyleScreens(id string) *config.LiveTVScreens

StyleScreens reads the cached owner style; it performs no rendering or database work.

func (*Service) Template

func (s *Service) Template(ctx context.Context, prompt string, base Look) (Look, error)

func (*Service) Templates

func (s *Service) Templates(_ context.Context) ([]TemplateRecord, error)

func (*Service) UploadAsset

func (s *Service) UploadAsset(ctx context.Context, name, kind, license string, r io.Reader) (Asset, error)

func (*Service) UploadClip

func (s *Service) UploadClip(ctx context.Context, reel, name string, r io.Reader, metadata ...ClipUploadMetadata) (ReelClip, error)

type StyleControl

type StyleControl struct {
	ID    string   `json:"id"`
	Label string   `json:"label"`
	Kind  string   `json:"kind"`
	Value any      `json:"value"`
	Min   *float64 `json:"min,omitempty"`
	Max   *float64 `json:"max,omitempty"`
	Step  *float64 `json:"step,omitempty"`
	Group string   `json:"group,omitempty"`
}

type StyleScene

type StyleScene struct {
	ID       string         `json:"id"`
	Role     string         `json:"role"`
	Name     string         `json:"name"`
	Enabled  bool           `json:"enabled"`
	Weight   int            `json:"weight"`
	Document scene.Document `json:"document"`
	Sounds   []SceneSound   `json:"sounds"`
	Video    *SceneVideo    `json:"video,omitempty"`
}

func (*StyleScene) UnmarshalJSON

func (s *StyleScene) UnmarshalJSON(b []byte) error

Every owned document goes through the strict, cost-limited scene decoder.

type TemplateRecord

type TemplateRecord struct {
	ID       string   `json:"id"`
	Name     string   `json:"name"`
	Blurb    string   `json:"blurb"`
	BlurbKey string   `json:"blurbKey"`
	Tags     []string `json:"tags"`
	Style    Look     `json:"style"`
}

Directories

Path Synopsis
Package encode writes content-addressed On Air master clips off request paths.
Package encode writes content-addressed On Air master clips off request paths.
Package families embeds the built-in scene documents; families are data.
Package families embeds the built-in scene documents; families are data.
Package fonts provides embedded OFL faces.
Package fonts provides embedded OFL faces.
Package raster renders scene v1 with no I/O, global mutable state or frame goroutines.
Package raster renders scene v1 with no I/O, global mutable state or frame goroutines.
Package scene defines the portable, executable-code-free On Air document v1.
Package scene defines the portable, executable-code-free On Air document v1.
Package signal produces live, deterministic signal screens without clip loops or per-tick allocations.
Package signal produces live, deterministic signal screens without clip loops or per-tick allocations.

Jump to

Keyboard shortcuts

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