AviatoAviato
DeveloperPlugins

Library Capability

How a library plugin declares mediaTypes, item schemas, entities, and renderer slots.

A library plugin defines what a library type is. It owns:

  • The set of mediaTypes the library represents (movies, tv, audiobooks, photos, etc.) and per-type config flags.
  • The bundling strategy that determines how scanned files are grouped into items.
  • The path rules that classify discovered files (primary, trailer, companion, and so on).
  • The item schema: typed fields stored on each library item.
  • The entity definitions for show, season, person, genre, and any other records that items link to.
  • The renderer config that decides which fields paint which UI slots, and what extra sections appear below them.

If a media type renders or behaves differently in Aviato, that difference lives in a library plugin, not in the web app. The web app is intentionally generic: it consumes whatever schema the library plugin returns and renders it via the slot vocabulary.

Manifest declaration

Two parts of the manifest matter for library plugins.

mediaTypes lists the supported types. Use the array form for the simple case; switch to the object form when you need per-type flags:

"capabilities": ["library"],
"mediaTypes": ["movies"]
"capabilities": ["library"],
"mediaTypes": {
  "tv": { "episodic": true }
}

Per-type flags currently understood:

FlagDefaultEffect
episodicfalseEnables the player's "End of current episode" sleep timer (and future auto-advance).
seekabletrueWhen false, hides player seek controls (e.g. books).
idleProtectiontrueWhen false, suppresses the "Are you still there?" prompt (e.g. books).

Add new flags when the host UI needs to behave differently for a media type. Never branch on the mediaTypes string itself in the web; branch on a declared flag.

capabilityConfig.library declares bundling and path rules:

"capabilityConfig": {
  "library": {
    "bundling": { "strategy": "per-folder" },
    "paths": [
      { "extensions": ["system:video"] },
      { "type": "trailer", "rules": ["*trailer*", "trailers/**/*"], "extensions": ["system:video"] },
      { "type": "behind-the-scenes", "rules": ["*behind?the?scenes*", "*bts*"], "extensions": ["system:video"] },
      { "type": "deleted-scene", "rules": ["*deleted?scene*"], "extensions": ["system:video"] },
      { "type": "extra", "rules": ["extras/**/*", "featurettes/**/*"], "extensions": ["system:video"] },
      { "type": "companion", "extensions": ["system:text", ".nfo", ".jpg", ".srt"] }
    ]
  }
}

Bundling strategies

strategy: "per-folder": every primary file in a folder bundles into one item. Used by aviato-library-movies.

strategy: "per-file": each primary file is its own item. Pair with an identity block for grouping under a parent entity:

"bundling": {
  "strategy": "per-file",
  "identity": { "pattern": "S(?<season>\\d+)E(?<episode>\\d+)", "scope": "directory" }
}

Used by aviato-library-tv: every episode file is its own item but shares a parent show entity at the directory level.

Path rules

Each entry classifies files by glob rules and/or extensions. The first matching entry wins. Type vocabulary (non-exhaustive), defined by MediaFileTypeEnum in @aviato/plugin-sdk:

primary (default), trailer, behind-the-scenes, deleted-scene, extra, companion. Companion files are sidecars (artwork, subtitles, .nfo) that ride along with a primary item but do not become items themselves.

extensions accepts:

  • Raw extensions, like ".mkv" or ".srt".
  • Aliases, including:
    • system:video: every common video container.
    • system:audio: every common audio container.
    • system:image: every common image format.
    • system:text: .pdf, .epub, .txt.

The full alias map is exported from @aviato/plugin-sdk as SYSTEM_EXTENSION_ALIASES, with resolveExtensions() available as a helper.

The library schema (returned from getSchema)

When Aviato starts a library plugin it calls library.getSchema() once and caches the result. The returned LibrarySchema drives every UI surface Aviato renders for that library:

{
  name: 'Movies',
  icon: 'FilmStrip',          // Phosphor icon
  description: '...',
  mediaTypes: ['movies'],     // optional override of manifest

  itemSchema: { ... },        // typed property map for library items
  entitySchemas: { ... },     // typed property maps per entity type

  searchableFields:  ['title', 'year'],
  filterableFields:  ['genres', 'year', 'rating'],

  libraryViewBy: ['entity:show', 'items'],   // browse defaults

  display: {                  // library-wide visual hints
    poster: { aspect: 'portrait' },
    heroBadges: { field: 'year' },
  },

  allowItemRematch: true,

  entityRenderers: {          // optional: paints entity detail pages
    show:   { view: 'list', viewOptions: { ... }, slots: { ... }, extras: [...] },
    season: { view: 'horizontal-scroll', slots: { ... } },
  },

  itemRenderer: {             // optional: paints item detail pages
    slots: { ... },
    extras: [...],
  },
}

Item and entity schemas

itemSchema and entitySchemas[type] follow the same shape: a property map naming the fields stored against an item or entity, their type (string, number, string[], entity-ref, artwork-ref, etc.), and optional metadata like editable, order, and label. Aviato uses this schema to:

  • Render the item-detail editor (Fix Match → "Edit item").
  • Drive the per-entity detail page (/library/<id>/<entityType>/<entityId>).
  • Type the bundle delta payloads that hooks return.

Library plugins don't store fields directly. They declare the shape, and indexer plugins or hook plugins populate values during ingestion. Aviato persists everything as JSON keyed by the schema.

Entities

Entities are first-class records owned by a library plugin: a show, a season, a person, an album, a genre. They exist independently of items so they can be referenced from many items at once and so they get their own URL and detail page.

To declare an entity:

  1. Add an entry to entitySchemas describing its property map.
  2. Optionally add entityRenderers[type] to control how the entity's detail page renders.
  3. Optionally include entity:<type> in libraryViewBy so the library browse view shows entities of that type by default (for example, shows instead of episodes).

Items reference entities via entity-ref fields in their item schema. Indexer plugins emit entity payloads alongside item payloads during indexing, and Aviato reconciles them into the entity graph.

The aviato-tmdb indexer, for example, emits show, season, and person entities when it indexes a TV episode. The aviato-library-tv plugin declares those types in its schema and renders them.

Renderer slots

Both entityRenderers[type] and itemRenderer use the same slot vocabulary: the same key means the same thing on entity pages and item detail pages. Slots bind a region of the rendered page to a metadata source.

slots: {
  title:       { field: 'title' },
  subtitle:    { fields: ['seasonNumber', 'episodeNumber'], format: 'template', template: 'S{0}E{1}' },
  description: { field: 'overview' },
  rating:      { field: 'voteAverage', fallbackField: 'imdbRating' },
  poster:      { prefer: ['poster', 'thumb'] },     // asset slot
  backdrop:    { prefer: ['fanart', 'backdrop'] },
  links:       { include: ['canonicalIds', 'externalLinks'] },
}

extras declares ordered sections rendered below the slot region:

extras: [
  { type: 'kv-grid', title: 'Details', fields: ['runtime', 'releaseDate', 'genres'] },
  { type: 'chips',   title: 'Cast',    field: 'cast', limit: 10 },
  { type: 'entity-cards', title: 'Seasons', source: 'children', childType: 'season' },
]

When itemRenderer is absent, Aviato falls back to a generic layout. When entityRenderers[type] is absent, the entity detail page is hidden: the entity exists in the graph but has no dedicated UI.

Library-wide display hints

display lives at the top of LibrarySchema and controls visuals that apply across every page in the library, not specific to the detail layout:

  • display.poster.aspect is 'square' | 'portrait' | 'landscape'. Browse cards and home rows render at this aspect; mismatched artwork is letterboxed.
  • display.heroBadges is a SlotValue that resolves to a comma-joined string and renders as badge chips on library hero banners.

Library plugin RPC contract

Methods Aviato calls on a library plugin (library.* namespace):

MethodCalled whenReturns
getSchemaPlugin starts (cached)LibrarySchema
getSortOptionsUser opens the sort dropdownSortOption[]
getFilterOptionsUser opens the filter dropdownFilterOption[]
getGroupingOptionsUser toggles groupingGroupingOption[]
getItemSummaryBuilding list/grid rowsItemSummary (compact projection)
getItemDetailOpening the item detail pageItemDetail (full projection)

The summary/detail split exists so list views stay fast: list rows only need a poster URL, title, year, runtime, and rating, while the detail page needs cast, crew, related entities, and so on.

Reference

The following types are exported from @aviato/plugin-sdk:

  • LibrarySchema, LibraryDisplaySchema, EntityRendererConfig, ItemRendererConfig.
  • The slot value vocabulary, RendererSlotsSchema, and RendererExtraSchema.
  • The entity schema definitions used by entitySchemas and entity-ref fields.
  • SYSTEM_EXTENSION_ALIASES and resolveExtensions() for the system:* aliases.

Reference plugins to read: aviato-library-movies, aviato-library-tv, aviato-library-audiobooks, aviato-library-photos.

See also

On this page