The importer
The durable Import workflow — how an uploaded lineup is deduped, numbered, and rebuilt against your own media server, with an end-to-end dry-run that skips every write.
The importer is the execution half of Import / Export. Once you upload a lineup file and pick which packages and channels to bring in, the staging Import button dispatches a durable Workflow SDK run that recreates them on your instance — resolving each channel's filter against your media server as it goes. It uses the same durable machinery as the AI lineup builder, minus the AI: no library analysis, no planner call, no per-channel agent loop.
Entry point is importLineupWorkflow(args) in apps/server/workflows/import.ts.
The steps
planStep— a deterministic plan (dedupe + number assignment + library remap), reads only. It traces each duplicate it will skip.createPackagesStep— create or reuse (by key) every needed package.buildChannelStep×N — create the channel plus itsPREDICATEfilter definitions and lay a windowed schedule so it's watchable immediately. Fanned out in waves ofBUILD_CONCURRENCY = 4— lower than the AI builder's, because schedule generation (~30s/channel) is the slow step here.reportStep— summarize the outcome into anImportReport(the run's return value).
Because the plan step already deduplicates and each build's create is idempotent against existing rows, the importer gets the fan-out safety of the AI builder's reserve-don't-check guard for free — there is no expensive agent loop to duplicate.
Dry-run
The importer's distinctive feature is dry-run (args.dryRun). The whole workflow runs
end-to-end for real — it validates, resolves every channel's filter against Plex to report true
pool sizes, and writes trace rows so the run page shows live progress — but skips every persisting
write. Nothing is created; you get an honest preview of exactly what a real import would do.
This is how a deployed build (on TrueNAS, say) gets validated before you commit an import. The run
page banners the dryRun flag so a dry run is never mistaken for the real thing.
What travels, and what doesn't
The importer only rebuilds the authoring — packages, channels, and their filters. Everything an instance regenerates locally is deliberately left behind and rebuilt on your box: the media-source binding (the target picks its own), the materialized schedule (regenerated from the filters), the media-metadata cache, and the per-instance scheduling cursor. The full file format and the round-trip rules are documented in Import / Export.
Source map
| Concern | File |
|---|---|
| Workflow orchestrator + steps | apps/server/workflows/import.ts |
Reusable import brains (planImport, dedupe/remap) | packages/api/src/services/transfer/import.ts |
| Runner registry (dispatch + poll) | packages/api/src/services/transfer/import-runner.ts |
| Trace + run read model (observability) | packages/api/src/services/transfer/import-trace.ts, import-runs.ts |
| Export / import file format | Import / Export |
See also: Import / Export (the file format and the round-trip) · Observability (watching an import run) · Channels (what a rebuilt channel is).
The AI lineup builder
The durable, multi-agent 'Build Lineup with AI' workflow — how the planner authors filters, the workers verify-and-commit, and why a full lineup costs about a dollar.
Observability
Watching a workflow run — the Workflow SDK's inspector UI (pnpm workflow:ui) and Airwave's own in-app run pages backed by trace tables.
