Project files
Kinema projects are .fcproj files: UTF-8 JSON with an
explicit schema version. Reading and writing them is
crates/format (kinema-format); the engine's
file.* commands use it.
Format
{
"format": "kinema.project",
"schema_version": 13,
"generator": "Kinema 0.1.0",
"project": { "name": "…", "settings": { … }, "root": { … }, "items": { … }, "next_id": 48 },
"view": { "open_sequences": [21, 50], "active_sequence": 50, "sequences": { "21": { "pps": 40.0, "scroll": 0.0, … } } }
}
schema_versionis the version of the whole document. This build writeskinema_format::SCHEMA_VERSIONand reads every version from 1 up to it.projectis the serializedkinema_project::Project(bins, media clips, sequences, tracks, clips, effects, keyframes, markers). Media is referenced by path, never embedded.- Files are written compact (no indentation). A 2,000-clip project is about 1.4 MB.
view(optional) is what was open when the project was saved: the Timeline's sequence tabs in order, the active one, each sequence's zoom, scroll and track heights, and each sequence's playhead (kinema_project::ProjectView). It is beside the project, not in it: it is not part of the edit, never an undo step, and changing it does not mark the project as changed. Opening a project restores it when the Timeline preference "Restore open sequences when opening projects" is on. It is outside the schema version: older builds ignore it, ids that are not sequences and numbers out of range are dropped or clamped (a playhead also snaps to a frame of its sequence), and aviewthat cannot be read is ignored (the project opens on its first sequence, as it does without one or with no open sequence in it).- A coordinate of an effect's point parameter may be
null. Point parameters use NaN for "auto" (the frame centre, or the source centre foranchor), and JSON writes NaN asnull; it reads back as "auto". Placing a clip resolves these points at once. An interchange import resolves them too, except theanchorof a clip whose media file was not found. That one stays "auto", and the renderer centres it, until Link Media (media.relink,media.autoRelink) reads the file and with it the real picture size; a file that merely comes back at its old path does not change it.nullanywhere else is a damaged file.
Schema history
| Version | Since | Shape |
|---|---|---|
| 1 | M0 | the bare Project object with a
"version": 1 field |
| 2 | M11.1 | the envelope above; the project's own version field is
gone |
| 3 | M10.2 | graphic clips (ItemKind::Graphic with text/shape
layers); a no-op step for older data |
| 4 | M7.4 | Essential Sound: clip audio types and settings; no-op step |
| 5 | M8.8 | colour management: working space, Interpret Footage colour space, LUT library; no-op step |
| 6 | M11.7/M11.9 | media identity fingerprints, ingest settings; no-op step |
| 7 | M5.5 | effect masks: masks on effect instances with
keyframable Bézier Path values; no-op step |
| 8 | M12 | multi-camera source sequences and clips, merged clips; no-op step |
| 9 | M10.5 | transcripts of media items (project.transcripts); no-op
step |
| 10 | M3.10 | clip time interpolation, Hold Filters, Field Options, audio source channels, Modify ▸ Audio Channels map, subclip Restrict Trims; no-op step |
| 11 | M3.11 | search bins (project.search_bins), Flash Cue markers,
Project Settings safe areas, capture format and scratch disks; no-op
step |
| 12 | M10.7 | graphics design data: TrackItem::graphic (roll / crawl,
responsive time, template link), EffectInstance::layer
(layer uid, per-character styles, responsive pins),
project.source_graphics; no-op step |
| 13 | Multi-stream audio | MediaInfo::audio_streams and
TrackItem::audio_stream; older audio objects
load as stream zero, missing clip indices default to zero; no-op
step |
Migrations
Older files are upgraded on load, in memory, by a chain of
single-step functions (kinema_format::MIGRATIONS, where
entry i upgrades vi+1 to vi+2). Each step
rewrites the JSON document; after the last one the result is
deserialized into the current model. To change the schema:
- add a function
vN_to_vN1(doc: Value) -> Result<Value, String>and append it toMIGRATIONS(SCHEMA_VERSIONfollows automatically); - add a small hand-made fixture of the old shape to
crates/format/tests/fixtures/and a test that it loads; - never edit a migration that has shipped.
Fields added with #[serde(default)] don't need a
migration: missing values take their defaults. But when new data would
make the file unreadable to an older build (a new enum variant such as
ItemKind::Graphic, a new required shape), bump the schema
with a no-op step so older builds refuse the file with the message below
instead of failing with a parse error. Do the same when an older build
would load the file but silently drop new fields on its next save.
When you open an older file Kinema says so ("Upgraded project from
schema v1 to v2"). The file on disk is unchanged until you save. The
first save over it keeps the original next to it as
<name> (schema v1 backup).fcproj, so an older Kinema
can still open your work.
Newer files
A file with a schema_version newer than the build
supports is refused, not half-read:
this project was saved by a newer version of Kinema (project schema v12); this build reads up to v11. Update Kinema to open it.
Reading it partially and saving it back would silently drop whatever the newer version added.
Saving is atomic
Every write of a project file, auto-save, recovery snapshot or
preferences file goes through
kinema_format::atomic_write:
- write the bytes to a hidden temp file in the same
directory
(
.<name>.<pid>-<n>.tmp); fsyncthe temp file;- rename it over the target (an atomic replace on every supported OS);
fsyncthe directory so the rename itself is durable.
A crash, kill -9 or power cut at any point leaves either
the complete old file or the complete new one. If a step fails, the temp
file is removed and the old file is untouched.
Commands
| Id | Menu | |
|---|---|---|
file.save {path?} |
File ▸ Save (⌘S) | write to the project's path (or path) |
file.saveAs {path} |
File ▸ Save As… (⇧⌘S) | write and adopt the new path |
file.saveCopy {path} |
File ▸ Save a Copy… (⌥⌘S) | write a copy; the project keeps its path and unsaved state |
file.revert |
File ▸ Revert | reload the last saved version, discarding changes |
file.open {path} |
File ▸ Open Project… (⌘O) | returns {schemaVersion, migrated} |
In the UI, Save / Save As / Save a Copy without a path
show a file dialog.
Auto Save
Preferences ▸ Auto Save (Edit ▸ Preferences ▸ Auto Save…, ⌘,; Kinema ▸ Settings… on macOS) works like Premiere's:
| Setting | Key | Default |
|---|---|---|
| Automatically save projects | autoSave.enabled |
on |
| Automatically Save Every N minute(s) | autoSave.intervalMinutes |
5 (1–1440) |
| Maximum Project Versions | autoSave.maxVersions |
20 (1–1000) |
| Auto Save also saves the current project(s) | autoSave.saveCurrentProject |
off |
| Keep a recovery copy of unsaved changes | autoSave.recoveryJournal |
on |
| Update it at least every N second(s) | autoSave.recoveryIntervalSeconds |
5 (1–600) |
At each interval, if the project changed since the last auto-save, a
copy is written to the Auto-Save folder next to the project
as
<name>-YYYY-MM-DD_HH-MM-SS-<unix seconds>.fcproj
(local time, then the real time that orders the copies, so a clock
change at the end of daylight saving time cannot reorder them). An
unsaved project uses <data dir>/Auto-Save/. After
each write the oldest copies beyond the limit are deleted. Only files
named exactly <name>-<timestamp>.fcproj or
<name>-<timestamp>-<unix seconds>.fcproj
are ever deleted. An auto-save is an ordinary project file: open it with
File ▸ Open Project. file.listAutoSaves lists them, newest
first.
Project Settings ▸ Scratch Disks can send auto-saves (Project Auto
Save), render previews (Video Previews), captured and generated media
(Captured Audio and Video, also the default for proxies) and audio
previews to another folder. A folder named in a project file is used
only when it is inside the project's own folder or was chosen in Scratch
Disks on this computer (remembered in
autoSave.scratchFolders). Otherwise Kinema uses the default
location and says so when the project opens. Folders on another computer
or a device are never used.
Preferences are stored in
<data dir>/preferences.json. The prefs.*
commands read and change them.
Crash recovery
Premiere's auto-save can lose up to one interval of work. Kinema also keeps a recovery journal:
- Whenever the project has unsaved changes, a snapshot is written to
<data dir>/Recovery/<started>-<pid>[-<n>]/snapshot.fcproj, plussession.json(time, revision, project name and path). It is written about 1 s after the last edit, or after at mostrecoveryIntervalSecondswhile edits keep coming. Saving the project deletes the snapshot. - Each running Kinema holds an OS file lock on
<session>/lock. When the process dies (crash,kill -9, power loss) the OS releases the lock. - At startup, every other session directory whose lock can be taken and that has a snapshot is a recovery candidate. Kinema asks: "Kinema quit unexpectedly while 'X' had unsaved changes. Recover unsaved changes from 2026-10-01 00:02:09?", with the buttons Recover, Not Now and Discard. Stale directories without a snapshot are removed. Journals of running sessions are never touched.
- If discarding a recovery session fails, Kinema reports the error and keeps that session and the remaining candidates in the recovery list for retry. Sessions already deleted stay removed.
- Recovering loads the snapshot as an unsaved project with its original path, so Save writes over the original file. The recovered state goes into the new session's own journal before the old journal is deleted.
- A normal quit with nothing unsaved removes the session directory.
Quitting with unsaved changes (Quit, Cmd+Q or the window's close button)
first asks "Save changes to 'X' before closing?" with
Cancel, Don't Save and
Save (Save on a never-saved project opens the Save
dialog); Don't Save discards the changes and their snapshot. A quit that
does not ask (the control channel's
app.quit, the OS ending the session) keeps the snapshot, markedcleanExit, and Kinema offers it on the next launch ("Kinema was closed while … had unsaved changes").
Desktop flags: --recover recovers the newest candidate
without asking. --no-recover starts without asking; the
changes stay available through File ▸ Recover Unsaved Changes….
`--data-dir
Cost
The UI thread only gives the worker thread an
Arc<Project>: the immutable snapshot the engine
already keeps for undo, passed through a channel. JSON encoding and all
file I/O run on the kinema-autosave thread. Measured on the
development Mac (release build) with a 2,000-clip project
(cargo test --release -p kinema-format --test fixtures -- --nocapture):
| Step | Time | Size |
|---|---|---|
| encode, compact (what is written) | 2.5 ms | 1.4 MB |
| encode, pretty (not used) | 7.1 ms | 5.6 MB |
| decode | 4.6 ms | |
| atomic write + fsync (APFS SSD) | 30 ms |
In the running app with the demo project, a recovery write measured 0.1 ms to encode and 27 ms to write (two fsynced files).
Commands
| Id | |
|---|---|
file.recoveryList |
candidates: id, projectName,
projectPath, savedAt, revision,
cleanExit… |
file.recover {id?} |
recover (newest when no id). From the UI,
{} opens the recovery dialog |
file.discardRecovery {id?, all?} |
delete a candidate's journal |
file.autoSaveNow |
write an auto-save now (if anything is unsaved) |
file.autoSaveStatus |
prefs, folders, last auto-save / journal times, last encode/write ms and bytes |
file.listAutoSaves |
auto-save files of the current project, newest first |
prefs.get {key?} / prefs.set {key, value}
or {values:{…}} / prefs.reset {category?} |
settings (below) |
UI automation ids: recovery.recover,
recovery.later, recovery.discard,
recovery.item.<n>; revert.yes,
revert.no (File ▸ Revert asks first when invoked without
params).
Settings
Premiere's Settings dialog (app menu ▸ Settings ▸ preferences.json in the per-user data
directory (written atomically on every change) and never touch the
.fcproj schema. The file carries a version
(currently 2); older files are migrated on load, unknown dropdown
values, bad colours and out-of-range numbers are repaired to
defaults/limits, and an unreadable file falls back to the defaults.
Every value is a dotted key whose first segment is its category:
timeline.stillImageDuration,
labels.colors.rose.name. prefs.get /
prefs.set / prefs.reset {category?} reach them
from the CLI, the control channel and MCP; prefs.set
rejects values that are not one of a dropdown's choices.
prefs.schema {category?} lists every category with its
fields (label, kind, choices, range, unit, current value, and
wired: whether Kinema acts on it yet).
app.settings.<category> opens the dialog on a
page.
| Category | Takes effect |
|---|---|
| General | At Startup (Show Home = demo project, Open Most Recent, empty project; recent projects are remembered on open/save), Show Tool Tips |
| Appearance | Appearance Mode appearanceMode (auto =
Sync with system / dark (default) / light; the
header button and View ▸ Appearance ▸ Next Appearance Mode cycle it),
Dark Theme darkTheme (darkest (default) /
dark), Light Theme lightTheme
(light), highlight colour, accessible contrast.
colorTheme (Darkest / Dark / Light) is legacy input:
setting it (older files and clients, View ▸ Appearance ▸ a theme)
selects that theme and fixes the mode to its family; a file without
appearanceMode migrates to its theme's fixed mode. It is
kept in line with the choices: the theme a fixed mode shows, the dark
theme in Auto |
| Audio | Automatch Time, Large Volume Adjustment, automation keyframe thinning (linear, minimum time) |
| Audio Hardware | device class (cpal host), output device, I/O buffer size, sample rate, force document rate, Output Mapping (programme L/R → device channels) |
| Auto Save | the auto-save ring and the crash-recovery journal |
| Color | stored only (display colour management, EDR monitoring, HDR graphics white) |
| Graphics | new text layers: smart quotes, ligatures, default font |
| Labels | the 16 label names and colours (timeline, Project panel, Edit ▸
Label), label defaults for imported movies / video / audio / stills and
new sequences, Label Color Presets: JSON files
({"name", "labels": [16 × {"name", "color": "#rrggbb"}]})
in <data dir>/Label Presets, built in: "Default" (the
labels Kinema ships with), "Bright" (16 saturated hues) and "Roles"
(B-Roll, Footage, Dialogue… named so the label defaults land on matching
roles) (labels.presets,
labels.importPreset {path},
labels.exportPreset {path},
labels.deletePreset {name}; labels.preset is
the selected one) |
| Media | Indeterminate Media Timebase (frame rate of stills), Default Media Scaling (Scale to / Set to frame size when a clip of another size is edited in), Enable proxies |
| Media Analysis & Transcription | auto-transcribe imported clips (scope "all imported"), speech model,
default language / auto-detect, speaker labelling (defaults of
transcript.generate) |
| Media Cache | location (<data dir>/Media Cache by default;
unsaved projects' render previews live there), automatic deletion (older
than N days / beyond N GB), Delete…
(mediaCache.clean {all?},
mediaCache.info) |
| Memory | frame cache budget of the monitors and thumbnails |
| Playback | preroll / postroll (Play Around, trim loop), Step forward/back many |
| Plugins | stub (no plugins) |
| Timeline | video / audio transition and still image default durations (frames or seconds), playback auto-scrolling (none / page / smooth), snap playhead, return to beginning at playback end, play after rendering previews |
| Trim | Large Trim Offset, Selection tool picks roll (on the cut) / ripple (on the edge) without a modifier, playhead determines trim loop |
The remaining fields are kept for parity and shown, but have no
effect yet (wired: false).
UI automation ids: settings.category.<id>;
settings.<key> for every control (for example
settings.timeline.autoScroll,
settings.labels.colors.rose.color);
settings.<key>.<value> for the items of an open
dropdown; settings.<key>.browse,
settings.<key>.hex;
settings.mediaCache.clean; settings.help,
settings.reset (this page back to its defaults),
settings.cancel, settings.ok. The open
dialog's page and draft are ui.settings in
ui.inspect;
ui.set {"settings": {"page": id, "values": {key: value}}}
edits the draft (OK applies it).
Media: offline, relinking, proxies, ingest
Media is referenced by path. Each imported file also records its
identity (MediaClip::identity): the size
and a 64-bit fingerprint of the size, the first MiB and the last MiB.
Reading 2 MiB is cheap on any file, and it tells a moved original from a
different take with the same name. The field is optional
(skip_serializing_if); it and
ProjectSettings::ingest came with schema v6, so builds that
would drop them on save refuse the file instead. Projects from older
builds have no identity, so relinking them skips the fingerprint
check.
Offline media
- Opening a project checks every file
(
media.findMissing). Missing items don't stop the open. They render the offline slate (crates/render/src/offline.rs, our own design: a red striped field, a warning triangle, "MEDIA NOT FOUND", the file name and a hint), their audio is silent, and the app opens the Link Media dialog.file.openreturnsmissingMedia. - Make Offline
(
media.makeOffline {items?, deleteFiles?}) setsMediaClip::offline. The clip then shows "MEDIA SET OFFLINE" even though the file exists, until it is linked again. Files are deleted only withdeleteFiles: true. - The media pool caches sources per item and per reference (path, offline flag). So relinking, Make Offline and undoing either take effect on the next frame.
- Stored paths. Media and proxy paths read from a
document (the project file, a project template, an interchange file) are
opened only when they name a file on this computer or on the network
share the document itself is on
(
crates/engine/src/doc_path.rs, built oncraft-docpath); a relative path is taken from the document's folder. Other paths (another computer, a device) count as missing: the item shows the offline slate, the open says why, and the Link Media dialog lists it. A file imported or linked in this session is opened as given.
Link Media
| Command | |
|---|---|
media.findMissing |
{missing: [{item, name, fileName, path, status: missing|offline|proxyMissing, duration, startTimecode}]} |
media.status {item?} |
per item: online, missing,
offline, unreadable or generated,
plus identity and proxy |
media.linkMedia |
File ▸ Link Media… (rescans; the UI opens the dialog) |
media.relink {item, path, force?, relinkOthers?=true, alignTimecode?, match?} |
check, link, then the others |
media.autoRelink {from, to} /
{folder} |
batch relink: a folder-prefix remap, or a search of a folder tree |
media.search {folder, item?, exactName?=true} |
ranked candidates with ok, identityMatch,
problems |
media.replaceFootage {item, path} |
relink without checks (Replace Footage) |
media.offlineAll |
close the dialog and leave the rest offline |
match selects which properties must agree. Each defaults
as shown:
fileName(true): the file name stem;extension(true);clipId(true): the fingerprint; a mismatch is refused with "not the same file…";duration(true): within one frame;mediaStart(false): the start timecode;metadata(true): frame size, frame rate and audio channels.
force: true links regardless. Relink others
automatically works out the folder remap from the file just
linked, by dropping the common trailing path components
(/Volumes/A/shoot/a.mov →
/Users/me/shoot/a.mov gives /Volumes/A →
/Users/me). It applies the remap to the other missing files
and checks each one. It also looks in the new file's folder. Windows and
macOS separators both work. Align Timecode keeps clips
on the same timecode when the new file starts at a different timecode:
the clips' source in-points, keyframes, marks and markers move with it
(Project::shift_media_time). One relink, together with the
files it pulls along, is one undo step.
Link Media dialog automation ids:
linkMedia.row.<n>,
linkMedia.match.<fileName|extension|clipId|duration|mediaStart|metadata>,
linkMedia.alignTimecode,
linkMedia.relinkOthers, linkMedia.folder,
linkMedia.browse, linkMedia.exactName,
linkMedia.search,
linkMedia.candidate.<n>,
linkMedia.preview, linkMedia.link,
linkMedia.locate, linkMedia.offline,
linkMedia.offlineAll, linkMedia.cancel. Make
Offline: makeOffline.keep|delete|ok|cancel. In the Project
panel, offline items get a broken-link badge
(project.item.<id>.offline in Icon view), and items
with a proxy get a P badge
(project.item.<id>.proxy).
Proxies
| Command | Menu | |
|---|---|---|
media.createProxies {items?, preset?, destination?, attach?=true, wait?} |
Clip ▸ Proxy ▸ Create Proxies… | background job; attaches when done |
media.attachProxies {item, path} /
{items, paths} |
Attach Proxies… | duration within one frame and the same frame rate are required; size and aspect may differ |
media.detachProxies {items?} |
Detach Proxies | |
media.reconnectFullRes {item, path} |
Reconnect Full Resolution Media… | link the full-resolution file of a clip that has a proxy |
media.toggleProxies {enabled?} |
View ▸ Toggle Proxies, monitor button | Preferences ▸ Media ▸ media.enableProxies |
media.proxyPresets |
The presets use our own encoders:
prores_proxy_quarter(default),prores_proxy_half,prores_lt_half: ProRes 422 Proxy or LT in MOV with PCM audio;h264_quarter,h264_half: H.264 + AAC in MP4.
Proxies go to
<media folder>/Proxies/<name>_Proxy.<ext>,
or to the chosen folder. Job progress is in jobs.list.
Session::poll_persistence, which the app calls every frame,
attaches finished proxies.
With proxies enabled, monitors, thumbnails and playback read the
proxy. The proxy source reports the original's size. The compositor
derives its pixel scale from the frame it gets, so Motion and pixel-size
effect parameters (blur radii…) give the same picture at the proxy's
resolution. A 320×180 clip with Motion and Gaussian Blur and a ½-size
ProRes Proxy measures 44 dB PSNR against full resolution (48 dB at ½
playback resolution). Export always uses full
resolution (MediaPool::full_res_provider).
Toggling proxies bumps the revision so caches refresh, but it is not an
edit and doesn't mark the project as modified.
Cost. These numbers are from
proxy_playback_perf_4k, an ignored test:
RAYON_NUM_THREADS=1 cargo test --release -p kinema-engine proxy_playback_perf_4k -- --ignored --nocapture.
It plays a 2 s 3840×2160 H.264 clip (ffmpeg testsrc2,
libx264) on one core of the development Mac and reports milliseconds per
frame:
| decode (GPU path) | CPU composite at ½ | CPU composite at ¼ | proxy creation | |
|---|---|---|---|---|
| full resolution | 9.2 | 277 | 201 | |
| ProRes Proxy ¼ | 8.9 | 150 | 35 | 17.8 s |
| H.264 ¼ | 0.7 | 131 | 48 | 8.9 s |
The test pattern is unusually cheap for H.264 to decode, so real camera footage gains more from proxies. The CPU composite (YUV→linear conversion and resampling) is what proxies cut most: 5.7× at ¼ resolution, where the proxy needs no resampling.
Create Proxies dialog ids: proxies.preset.<id>,
proxies.destination, proxies.browse,
proxies.ok, proxies.cancel. Monitor button:
program.transport.media.toggleProxies (and
source.…). It is lit while proxies are on.
Ingest settings
project.ingestSettings {enabled, action: copy|transcode|createProxies|copyAndCreateProxies, destination?, preset?}
(Project Settings ▸ Ingest) acts on every file.import:
- copy copies the file to the destination (default
<media folder>/Ingested Media), checks the copy's fingerprint against the original, and then uses the copy; - transcode writes the preset's format (default ProRes 422 LT) in the background and switches the clip to the result when it is done;
- createProxies makes proxies in the background and attaches them;
- copyAndCreateProxies does both.
Copied files keep their names when available; collisions receive a
numbered suffix before the extension (A001 2.mov) and never
replace an existing file. Transcodes reserve distinct output names for
every clip in the import batch. Copies keep their source fingerprint,
and each proxy stays attached to its corresponding take.
file.import reports this under ingest.
Project Manager
file.projectManager (File ▸ Project Manager…) makes a
self-contained copy of a project:
{"destination": "/path/Folder", "mode": "collect|consolidate", "sequences": [id], "excludeUnused": true,
"handles": 30, "preset": "prores_lt", "includeProxies": true, "includePreviews": false,
"projectName": "…", "dryRun": false, "overwrite": false, "wait": false}
- collect copies every media file that the chosen sequences use (following nested sequences and subclips), plus their proxies and render previews if asked.
- consolidate writes only the used range of each
movie or audio file, plus
handlesframes on each side, with a transcode preset (prores_lt,prores_hq,h264). It then re-bases the clips' media time, so every edit, keyframe and marker stays in place. Stills are copied. - excludeUnused drops media items and sequences that the chosen sequences don't use.
- dryRun returns the plan and the disk-space estimate
(
originalBytes,resultBytes,files) without writing anything.
The copy runs as a job. The project file
(<destination>/<name>.fcproj) is written last,
with the new paths and fingerprints, so it opens with no missing
media.
Dialog ids: pm.seq.<id>,
pm.mode.<collect|consolidate>,
pm.preset.<id>, pm.excludeUnused,
pm.handles, pm.includeProxies,
pm.includePreviews, pm.destination,
pm.browse, pm.calculate,
pm.sizes, pm.ok, pm.cancel.
Effect presets
Effect presets (Effects panel ▸ Presets; Effect Controls ▸
right-click an effect ▸ Save Preset…) are not part of the project.
Built-in presets are defined in code
(kinema_engine::presets); user presets live in
<data dir>/effect-presets.json, and
presets.export / presets.import read and write
the same JSON format:
{"format": "kinema.effect-presets", "version": 1, "presets": [
{"name": "My Vignette", "description": "", "keyframes": "AnchorToIn",
"source_duration": 2540160000000, "source_size": [1920, 1080],
"effects": [ /* EffectInstance, exactly as in .fcproj: effect, enabled, params, masks */ ]}
]}
Keyframe times are relative to the in point of the clip the preset
was saved from (source_duration is that clip's media length
in ticks). Applying re-times them: Scale stretches them
over the target clip, AnchorToIn / AnchorToOut
keep their distance from the target's in / out point. Point parameters,
mask paths, feather and expansion are scaled by the target / source
frame-size ratio. Intrinsic effects (Motion, Opacity…) replace the
clip's own instance; other effects are added. Applying is one undo step;
saving, renaming, deleting and importing change the library, not the
project, and are not undoable. Files with another format, a
newer version or unknown effect ids are refused.