feat: publish av-tools 0.1.0
This commit is contained in:
141
docs/ARCHITECTURE.md
Normal file
141
docs/ARCHITECTURE.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# Architecture
|
||||
|
||||
av-tools is a static, local-first React application. The browser receives code
|
||||
and the two self-hosted FFmpeg cores; media bytes do not leave the origin
|
||||
through an application backend. Toolbox Portal assembles the already-built
|
||||
artifact and never compiles its source.
|
||||
|
||||
## Layers and boundaries
|
||||
|
||||
```text
|
||||
Toolbox AppShell / standalone context
|
||||
└─ React application
|
||||
├─ Quick Convert and sequential Edit workflows
|
||||
├─ project + typed preset state
|
||||
├─ media inspection / preview / result views
|
||||
├─ bounded application queue + resource policy
|
||||
└─ typed command plans
|
||||
└─ EngineManager
|
||||
├─ @ffmpeg/ffmpeg wrapper worker
|
||||
├─ ST or MT same-origin core
|
||||
└─ per-job virtual filesystem
|
||||
|
||||
Browser storage boundary
|
||||
├─ object URLs (ephemeral previews)
|
||||
├─ IndexedDB (validated user presets)
|
||||
└─ OPFS cache (optional derivatives; never source in the current app)
|
||||
```
|
||||
|
||||
- `src/toolbox/` owns the one typed manifest and suite integration.
|
||||
- `src/media/` normalizes ffprobe JSON, duration/timecode, stream selection,
|
||||
playback MIME decisions and safe names.
|
||||
- `src/project/` owns the versioned, non-destructive project document and
|
||||
migrations. A source is represented by identity/metadata and must be
|
||||
reattached after load; the current application does not persist source bytes.
|
||||
- `src/presets/` defines typed built-ins and validates user presets against
|
||||
detected capabilities.
|
||||
- `src/commands/` converts validated operations to a `CommandPlan`. A plan has
|
||||
generated input/output paths, argument arrays, expected outputs,
|
||||
capabilities and cleanup intent; it is not a shell string.
|
||||
- `src/ffmpeg/` selects/loads the engine, discovers capabilities, mounts
|
||||
inputs, executes one job, reads outputs, sanitizes diagnostics and cleans.
|
||||
- `src/jobs/` provides typed queue, progress, cancellation, recovery and
|
||||
durable-metadata infrastructure. The mounted application scheduler is a
|
||||
bounded in-memory promise chain around `EngineManager`.
|
||||
- `src/waveform/`, `src/storage/` and `src/export/` keep bounded analysis,
|
||||
persistence and browser-download responsibilities outside the UI.
|
||||
|
||||
The DOM receives normalized values as text/attributes. It never renders media
|
||||
metadata as HTML. Command builders never accept arbitrary virtual paths or a
|
||||
raw command field.
|
||||
|
||||
## FFmpeg lifecycle
|
||||
|
||||
1. Inspect secure-context, worker, `crossOriginIsolated` and
|
||||
`SharedArrayBuffer` availability.
|
||||
2. Select MT only when all MT prerequisites are true (or ST when explicitly
|
||||
selected). A failed MT load is terminated and retried once with ST.
|
||||
3. Load wrapper/core assets via base-relative, same-origin URLs.
|
||||
4. Query version/build configuration/formats/codecs/encoders/filters
|
||||
sequentially and publish immutable capability state.
|
||||
5. For one job, validate resource estimates and structured settings, allocate
|
||||
a generated directory, mount/write inputs, run a generated argument array,
|
||||
read only declared outputs, and re-probe applicable results.
|
||||
6. Return result bytes and an export report, then unmount/delete virtual files.
|
||||
7. Retain the idle engine for the next sequential job; terminate it on mode
|
||||
changes, unrecoverable errors or cancellation.
|
||||
|
||||
The FFmpeg instance is a single process. `maxConcurrentFFmpegJobs` is one and
|
||||
no probe/export operations overlap on it. The application-facing scheduler is
|
||||
also bounded: it retains at most eight scheduled operations in memory,
|
||||
including the active operation, and advances its promise chain after either
|
||||
success or failure. Queue state is deliberately not persisted across a page
|
||||
reload.
|
||||
|
||||
## Project and edit model
|
||||
|
||||
A project is versioned JSON containing source descriptors, a sequential clip
|
||||
timeline, non-destructive clip in/out and transform/audio settings, stream
|
||||
selection, metadata/chapter/subtitle edits and export preset references. Clips
|
||||
do not overlap. There is no arbitrary compositing graph, collaborative state or
|
||||
server-side document.
|
||||
|
||||
The structural-export panel maps the selected clip's current range to either a
|
||||
stream-copy or preset-backed trim plan. It maps the visible timeline order to
|
||||
either a compatibility-checked stream-copy concat plan or a normalized
|
||||
re-encode plan. Normalized concat requires the user to choose what happens when
|
||||
some clips lack audio; unchecked fast compatibility fails closed.
|
||||
|
||||
Migrations are explicit version-to-version functions. Unknown future versions,
|
||||
invalid shapes and unsafe source references fail with actionable validation
|
||||
errors. Source bytes are not embedded in normal project JSON. Reattachment
|
||||
matches stable descriptors and asks the user before associating a local file.
|
||||
|
||||
## Command plans and filesystem
|
||||
|
||||
Plans use arrays such as `['-i', generatedInput, ...]`; no shell evaluates
|
||||
them. Filtergraph builders escape values for their exact FFmpeg grammar.
|
||||
Generated paths contain a job id plus bounded safe basename and never reuse a
|
||||
user-controlled directory. Expected output paths are declared before
|
||||
execution, so the result reader cannot enumerate arbitrary output.
|
||||
|
||||
Small inputs may be copied into FFmpeg's in-memory filesystem. Where supported,
|
||||
WORKERFS mounts `File`/`Blob` inputs to avoid an initial JavaScript copy; it
|
||||
does not remove WASM address-space/output constraints. OPFS is optional for
|
||||
regenerable derivatives and uses asynchronous origin-private file handles with
|
||||
an in-memory fallback. See `docs/STORAGE.md`.
|
||||
|
||||
## Result flow
|
||||
|
||||
Output bytes become a Blob with an operation-derived filename and MIME type.
|
||||
The UI creates a short-lived object URL only when the browser can preview that
|
||||
container/codec, exposes an explicit save/download action, and includes probe
|
||||
data plus warnings in the export report. Revoking a preview does not invalidate
|
||||
the retained Blob until the result is cleared. Batch derivatives may be
|
||||
assembled into a bounded ZIP.
|
||||
|
||||
For unsupported direct playback, a preview proxy is a disposable H.264/AAC MP4
|
||||
derivative rather than a source mutation. Duration and maximum width are
|
||||
bounded by validated user controls. Its cache identity includes the source
|
||||
fingerprint and proxy settings, so only an exact matching derivative is reused;
|
||||
cache failure falls back to normal generation.
|
||||
|
||||
## Cancellation and recovery
|
||||
|
||||
ffmpeg.wasm cancellation terminates the active wrapper/core worker rather than
|
||||
pretending a native FFmpeg process can receive a signal. The job becomes
|
||||
cancelled, outstanding promises are rejected, object URLs/mounts are released
|
||||
where possible, and that engine generation is discarded. The next job creates
|
||||
a fresh engine and re-runs capability discovery. A cancellation is complete
|
||||
only after the stale worker cannot publish a result.
|
||||
|
||||
## Integration boundaries
|
||||
|
||||
`AppShell` owns suite chrome, theme/personalization, help and app switching.
|
||||
av-tools passes `manifestUrl="./toolbox-app.json"` and works when context is
|
||||
missing, malformed or unavailable. It does not embed another switcher.
|
||||
|
||||
The manifest advertises secure context, workers, IndexedDB and top-level
|
||||
navigation. It does not require cross-origin isolation because ST is a valid
|
||||
fallback. Portal receives only the deterministic ZIP/checksum and places it
|
||||
under a locked target such as `apps/av/`.
|
||||
Reference in New Issue
Block a user