@alpha2-sh/editor 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -1
- package/dist/browser/bridge.d.ts +44 -0
- package/dist/browser/bridge.js +2176 -0
- package/dist/browser/extensions-worker-entry.js +3 -0
- package/dist/browser/extensions-worker.d.ts +46 -0
- package/dist/browser/extensions-worker.js +149 -0
- package/dist/browser/hash-worker-entry.d.ts +1 -0
- package/dist/browser/hash-worker-entry.js +3 -0
- package/dist/browser/hash-worker.d.ts +24 -0
- package/dist/browser/hash-worker.js +87 -0
- package/dist/browser/index.d.ts +11 -0
- package/dist/browser/index.js +9 -0
- package/dist/browser/jobs.d.ts +82 -0
- package/dist/browser/jobs.js +291 -0
- package/dist/browser/resources.d.ts +40 -0
- package/dist/browser/resources.js +83 -0
- package/dist/browser/runtime/assets.d.ts +233 -0
- package/dist/browser/runtime/assets.js +633 -0
- package/dist/browser/runtime/audio.d.ts +60 -0
- package/dist/browser/runtime/audio.js +549 -0
- package/dist/browser/runtime/budget.d.ts +11 -0
- package/dist/browser/runtime/budget.js +26 -0
- package/dist/browser/runtime/caption-fonts.d.ts +34 -0
- package/dist/browser/runtime/caption-fonts.js +69 -0
- package/dist/browser/runtime/color.d.ts +7 -0
- package/dist/browser/runtime/color.js +12 -0
- package/dist/browser/runtime/engine.d.ts +91 -0
- package/dist/browser/runtime/engine.js +352 -0
- package/dist/browser/runtime/export-audio.d.ts +23 -0
- package/dist/browser/runtime/export-audio.js +75 -0
- package/dist/browser/runtime/exporter.d.ts +140 -0
- package/dist/browser/runtime/exporter.js +751 -0
- package/dist/browser/runtime/extension-import.d.ts +196 -0
- package/dist/browser/runtime/extension-import.js +521 -0
- package/dist/browser/runtime/fingerprint.d.ts +57 -0
- package/dist/browser/runtime/fingerprint.js +157 -0
- package/dist/browser/runtime/generators.d.ts +188 -0
- package/dist/browser/runtime/generators.js +173 -0
- package/dist/browser/runtime/locks.d.ts +32 -0
- package/dist/browser/runtime/locks.js +86 -0
- package/dist/browser/runtime/output.d.ts +87 -0
- package/dist/browser/runtime/output.js +230 -0
- package/dist/browser/runtime/preview-utils.d.ts +3 -0
- package/dist/browser/runtime/preview-utils.js +49 -0
- package/dist/browser/runtime/proxies.d.ts +72 -0
- package/dist/browser/runtime/proxies.js +369 -0
- package/dist/browser/runtime/proxy-storage.d.ts +59 -0
- package/dist/browser/runtime/proxy-storage.js +119 -0
- package/dist/browser/runtime/sources.d.ts +82 -0
- package/dist/browser/runtime/sources.js +819 -0
- package/dist/browser/runtime/staging.d.ts +10 -0
- package/dist/browser/runtime/staging.js +32 -0
- package/dist/browser/runtime/still.d.ts +21 -0
- package/dist/browser/runtime/still.js +52 -0
- package/dist/browser/runtime/wasm.d.ts +9 -0
- package/dist/browser/runtime/wasm.js +20 -0
- package/dist/browser/types.d.ts +549 -0
- package/dist/browser/types.js +1 -0
- package/dist/browser/worker-entry.d.ts +1 -0
- package/dist/browser/worker-entry.js +2 -0
- package/dist/browser/worker.d.ts +24 -0
- package/dist/browser/worker.js +239 -0
- package/dist/core/captions.d.ts +34 -0
- package/dist/core/captions.js +108 -0
- package/dist/core/clip-time.d.ts +31 -0
- package/dist/core/clip-time.js +125 -0
- package/dist/core/commands.d.ts +413 -0
- package/dist/core/commands.js +1118 -0
- package/dist/core/compositions.d.ts +234 -0
- package/dist/core/compositions.js +851 -0
- package/dist/core/document.d.ts +245 -0
- package/dist/core/document.js +683 -0
- package/dist/core/keyframes.d.ts +82 -0
- package/dist/core/keyframes.js +153 -0
- package/dist/core/presets.d.ts +64 -0
- package/dist/core/presets.js +241 -0
- package/dist/core/session.d.ts +150 -0
- package/dist/core/session.js +541 -0
- package/dist/core/text.d.ts +17 -0
- package/dist/core/text.js +17 -0
- package/dist/core/time.d.ts +19 -0
- package/dist/core/time.js +39 -0
- package/dist/core/timeline-index.d.ts +44 -0
- package/dist/core/timeline-index.js +59 -0
- package/dist/core/transitions.d.ts +20 -0
- package/dist/core/transitions.js +118 -0
- package/dist/core/validated.d.ts +23 -0
- package/dist/core/validated.js +52 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +11 -0
- package/dist/native-client/index.d.ts +131 -0
- package/dist/native-client/index.js +354 -0
- package/dist/ports/index.d.ts +146 -0
- package/dist/ports/index.js +1 -0
- package/dist/react/index.d.ts +37 -0
- package/dist/react/index.js +94 -0
- package/package.json +69 -8
- /package/{index.js → dist/browser/extensions-worker-entry.d.ts} +0 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const indexes = new WeakMap();
|
|
2
|
+
/**
|
|
3
|
+
* Every clip, transition, caption cue, and marker of a document, in timeline order (then by kind,
|
|
4
|
+
* then id). Derived from the document alone and kept per document, so a playing viewer never
|
|
5
|
+
* rebuilds it: only an edit does.
|
|
6
|
+
*/
|
|
7
|
+
export function timelineIndex(document) {
|
|
8
|
+
const cached = indexes.get(document);
|
|
9
|
+
if (cached)
|
|
10
|
+
return cached;
|
|
11
|
+
const names = new Map(Object.values(document.assets).map((asset) => [asset.id, asset.name]));
|
|
12
|
+
const entries = [];
|
|
13
|
+
for (const track of document.project.timeline.tracks) {
|
|
14
|
+
for (const clip of track.clips ?? [])
|
|
15
|
+
entries.push({
|
|
16
|
+
kind: "clip",
|
|
17
|
+
id: clip.id,
|
|
18
|
+
name: document.metadata.clips[clip.id]?.name ??
|
|
19
|
+
names.get(clip.source_id) ??
|
|
20
|
+
clip.source_id,
|
|
21
|
+
trackId: track.id,
|
|
22
|
+
start: clip.timeline_range.start,
|
|
23
|
+
end: clip.timeline_range.end,
|
|
24
|
+
});
|
|
25
|
+
for (const transition of track.transitions ?? [])
|
|
26
|
+
entries.push({
|
|
27
|
+
kind: "transition",
|
|
28
|
+
id: `${transition.from_clip}->${transition.to_clip}`,
|
|
29
|
+
name: transition.effect.id,
|
|
30
|
+
trackId: track.id,
|
|
31
|
+
start: transition.timeline_range.start,
|
|
32
|
+
end: transition.timeline_range.end,
|
|
33
|
+
});
|
|
34
|
+
(track.captions?.cues ?? []).forEach((cue, index) => entries.push({
|
|
35
|
+
kind: "caption",
|
|
36
|
+
id: `${track.id}#${index}`,
|
|
37
|
+
name: cue.text,
|
|
38
|
+
trackId: track.id,
|
|
39
|
+
start: cue.timeline_range.start,
|
|
40
|
+
end: cue.timeline_range.end,
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
for (const marker of document.metadata.markers)
|
|
44
|
+
entries.push({
|
|
45
|
+
kind: "marker",
|
|
46
|
+
id: marker.id,
|
|
47
|
+
name: marker.name,
|
|
48
|
+
trackId: null,
|
|
49
|
+
start: marker.time,
|
|
50
|
+
end: marker.end ?? marker.time,
|
|
51
|
+
marker,
|
|
52
|
+
});
|
|
53
|
+
const order = { marker: 0, clip: 1, transition: 2, caption: 3 };
|
|
54
|
+
entries.sort((a, b) => a.start - b.start ||
|
|
55
|
+
order[a.kind] - order[b.kind] ||
|
|
56
|
+
(a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
57
|
+
indexes.set(document, entries);
|
|
58
|
+
return entries;
|
|
59
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ClipEffect, Track } from "alpha2/project";
|
|
2
|
+
/**
|
|
3
|
+
* `track` with a transition of `duration` ticks from clip `fromId` into `toId`, ending where
|
|
4
|
+
* `fromId` ends. `toId` and the clips chained after it move earlier so it overlaps `fromId` by
|
|
5
|
+
* `duration`.
|
|
6
|
+
*
|
|
7
|
+
* Throws an `EditorError` (`invalid_transition`) when the clips are missing or the same, either
|
|
8
|
+
* edge already has a transition, or `duration` is not positive and shorter than both clips.
|
|
9
|
+
*/
|
|
10
|
+
export declare function addTransition(track: Track, fromId: string, toId: string, duration: number, effect: ClipEffect): Track;
|
|
11
|
+
/**
|
|
12
|
+
* `track` with transition `index` given `duration` ticks, keeping its end at the outgoing clip's
|
|
13
|
+
* end and moving the incoming clip's chain to match. Throws like {@link addTransition}.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resizeTransition(track: Track, index: number, duration: number): Track;
|
|
16
|
+
/**
|
|
17
|
+
* `track` without transition `index`. The incoming clip and its chain move later so it starts
|
|
18
|
+
* where the outgoing clip ends. Throws an `EditorError` for an unknown index.
|
|
19
|
+
*/
|
|
20
|
+
export declare function removeTransition(track: Track, index: number): Track;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { EditorError } from "./document.js";
|
|
2
|
+
const fail = (message) => {
|
|
3
|
+
throw new EditorError("invalid_transition", message, "transition");
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* `value` with every keyframe time moved `delta` ticks. Every `Animated<T>` in a clip, masks
|
|
7
|
+
* included, uses timeline ticks; its composition keeps its own time.
|
|
8
|
+
*/
|
|
9
|
+
function shifted(value, delta) {
|
|
10
|
+
if (!value || typeof value !== "object")
|
|
11
|
+
return value;
|
|
12
|
+
if (Array.isArray(value))
|
|
13
|
+
return value.map((v) => shifted(v, delta));
|
|
14
|
+
const object = value;
|
|
15
|
+
if (object.mode === "keyframes" && Array.isArray(object.keyframes))
|
|
16
|
+
return {
|
|
17
|
+
...object,
|
|
18
|
+
keyframes: object.keyframes.map((key) => ({
|
|
19
|
+
...key,
|
|
20
|
+
time: key.time + delta,
|
|
21
|
+
})),
|
|
22
|
+
};
|
|
23
|
+
return Object.fromEntries(Object.entries(object).map(([key, v]) => [key, shifted(v, delta)]));
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* `track` with clip `id` starting at `start`, and the clips that follow it through transitions (its
|
|
27
|
+
* outgoing transition's clip, that clip's, and so on) moved as far with their transitions, so each
|
|
28
|
+
* overlap the chain holds stays exact. Collisions with other clips are for validation to refuse.
|
|
29
|
+
*/
|
|
30
|
+
function moveChain(track, id, start) {
|
|
31
|
+
const clip = track.clips.find((c) => c.id === id);
|
|
32
|
+
const delta = start - clip.timeline_range.start;
|
|
33
|
+
if (delta === 0)
|
|
34
|
+
return track;
|
|
35
|
+
const chain = new Set();
|
|
36
|
+
for (let at = id; at !== undefined && !chain.has(at);) {
|
|
37
|
+
chain.add(at);
|
|
38
|
+
const from = at;
|
|
39
|
+
at = track.transitions?.find((t) => t.from_clip === from)?.to_clip;
|
|
40
|
+
}
|
|
41
|
+
const range = (r) => ({
|
|
42
|
+
start: r.start + delta,
|
|
43
|
+
end: r.end + delta,
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
...track,
|
|
47
|
+
clips: track.clips.map((c) => chain.has(c.id)
|
|
48
|
+
? { ...shifted(c, delta), timeline_range: range(c.timeline_range) }
|
|
49
|
+
: c),
|
|
50
|
+
transitions: track.transitions?.map((t) => chain.has(t.from_clip)
|
|
51
|
+
? { ...shifted(t, delta), timeline_range: range(t.timeline_range) }
|
|
52
|
+
: t),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* `track` with a transition of `duration` ticks from clip `fromId` into `toId`, ending where
|
|
57
|
+
* `fromId` ends. `toId` and the clips chained after it move earlier so it overlaps `fromId` by
|
|
58
|
+
* `duration`.
|
|
59
|
+
*
|
|
60
|
+
* Throws an `EditorError` (`invalid_transition`) when the clips are missing or the same, either
|
|
61
|
+
* edge already has a transition, or `duration` is not positive and shorter than both clips.
|
|
62
|
+
*/
|
|
63
|
+
export function addTransition(track, fromId, toId, duration, effect) {
|
|
64
|
+
const from = track.clips?.find((c) => c.id === fromId), to = track.clips?.find((c) => c.id === toId);
|
|
65
|
+
if (!from || !to || fromId === toId)
|
|
66
|
+
fail("Select two different clips on the same track");
|
|
67
|
+
if ((track.transitions ?? []).some((t) => t.from_clip === fromId || t.to_clip === toId))
|
|
68
|
+
fail("This clip edge already has a transition");
|
|
69
|
+
const transition = {
|
|
70
|
+
from_clip: fromId,
|
|
71
|
+
to_clip: toId,
|
|
72
|
+
timeline_range: {
|
|
73
|
+
start: from.timeline_range.end - duration,
|
|
74
|
+
end: from.timeline_range.end,
|
|
75
|
+
},
|
|
76
|
+
effect,
|
|
77
|
+
easing: { type: "linear" },
|
|
78
|
+
};
|
|
79
|
+
return resizeTransition({ ...track, transitions: [...(track.transitions ?? []), transition] }, (track.transitions ?? []).length, duration);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* `track` with transition `index` given `duration` ticks, keeping its end at the outgoing clip's
|
|
83
|
+
* end and moving the incoming clip's chain to match. Throws like {@link addTransition}.
|
|
84
|
+
*/
|
|
85
|
+
export function resizeTransition(track, index, duration) {
|
|
86
|
+
const transition = track.transitions?.[index];
|
|
87
|
+
if (!transition)
|
|
88
|
+
fail("Unknown transition");
|
|
89
|
+
const from = track.clips.find((c) => c.id === transition.from_clip), to = track.clips.find((c) => c.id === transition.to_clip);
|
|
90
|
+
if (!Number.isSafeInteger(duration) ||
|
|
91
|
+
duration <= 0 ||
|
|
92
|
+
duration >=
|
|
93
|
+
Math.min(from.timeline_range.end - from.timeline_range.start, to.timeline_range.end - to.timeline_range.start))
|
|
94
|
+
fail("Transition duration must be positive and shorter than both clips");
|
|
95
|
+
const start = from.timeline_range.end - duration;
|
|
96
|
+
const moved = moveChain(track, to.id, start);
|
|
97
|
+
return {
|
|
98
|
+
...moved,
|
|
99
|
+
transitions: moved.transitions.map((t, i) => i === index
|
|
100
|
+
? { ...t, timeline_range: { start, end: from.timeline_range.end } }
|
|
101
|
+
: t),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* `track` without transition `index`. The incoming clip and its chain move later so it starts
|
|
106
|
+
* where the outgoing clip ends. Throws an `EditorError` for an unknown index.
|
|
107
|
+
*/
|
|
108
|
+
export function removeTransition(track, index) {
|
|
109
|
+
const transition = track.transitions?.[index];
|
|
110
|
+
if (!transition)
|
|
111
|
+
fail("Unknown transition");
|
|
112
|
+
const from = track.clips.find((c) => c.id === transition.from_clip);
|
|
113
|
+
const moved = moveChain(track, transition.to_clip, from.timeline_range.end);
|
|
114
|
+
return {
|
|
115
|
+
...moved,
|
|
116
|
+
transitions: moved.transitions.filter((_, i) => i !== index),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Project } from "alpha2/project";
|
|
2
|
+
/**
|
|
3
|
+
* Records that `project` passed `validateProject`. A deep-frozen project is remembered for good;
|
|
4
|
+
* another only until the `withValidated` call running now returns, since its owner may change it
|
|
5
|
+
* later.
|
|
6
|
+
*/
|
|
7
|
+
export declare function rememberValid(project: Project): void;
|
|
8
|
+
/** Whether `project` is known to pass `validateProject` (see `rememberValid`). */
|
|
9
|
+
export declare function knownValid(project: Project): boolean;
|
|
10
|
+
/** Runs `run`, forgetting when it returns the unfrozen projects it called valid. */
|
|
11
|
+
export declare function withValidated<T>(run: () => T): T;
|
|
12
|
+
/**
|
|
13
|
+
* Deep-freezes `document`, which validated, and remembers its project as valid, so compiling it or
|
|
14
|
+
* checking it again does not walk the project again.
|
|
15
|
+
*/
|
|
16
|
+
export declare function frozenValid<T extends {
|
|
17
|
+
readonly project: Project;
|
|
18
|
+
}>(document: T): T;
|
|
19
|
+
/**
|
|
20
|
+
* `document` itself when {@link frozenValid} froze it, which nothing can change, or else a copy
|
|
21
|
+
* for a session to own. Keeping the frozen document keeps what validating its parts found.
|
|
22
|
+
*/
|
|
23
|
+
export declare function owned<T extends object>(document: T): T;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { freeze } from "./document.js";
|
|
2
|
+
/** Deep-frozen projects that validated: they cannot change, so they stay valid. */
|
|
3
|
+
const frozen = new WeakSet();
|
|
4
|
+
/** Projects an edit validated during the `executeCommand` running now (see `withValidated`). */
|
|
5
|
+
let current = null;
|
|
6
|
+
/**
|
|
7
|
+
* Records that `project` passed `validateProject`. A deep-frozen project is remembered for good;
|
|
8
|
+
* another only until the `withValidated` call running now returns, since its owner may change it
|
|
9
|
+
* later.
|
|
10
|
+
*/
|
|
11
|
+
export function rememberValid(project) {
|
|
12
|
+
if (Object.isFrozen(project))
|
|
13
|
+
frozen.add(project);
|
|
14
|
+
else
|
|
15
|
+
current?.add(project);
|
|
16
|
+
}
|
|
17
|
+
/** Whether `project` is known to pass `validateProject` (see `rememberValid`). */
|
|
18
|
+
export function knownValid(project) {
|
|
19
|
+
return frozen.has(project) || (current?.has(project) ?? false);
|
|
20
|
+
}
|
|
21
|
+
/** Runs `run`, forgetting when it returns the unfrozen projects it called valid. */
|
|
22
|
+
export function withValidated(run) {
|
|
23
|
+
const previous = current;
|
|
24
|
+
current = new WeakSet();
|
|
25
|
+
try {
|
|
26
|
+
return run();
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
current = previous;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Documents {@link frozenValid} froze: plain data the editor made or copied, frozen all through.
|
|
34
|
+
*/
|
|
35
|
+
const sealedDocuments = new WeakSet();
|
|
36
|
+
/**
|
|
37
|
+
* Deep-freezes `document`, which validated, and remembers its project as valid, so compiling it or
|
|
38
|
+
* checking it again does not walk the project again.
|
|
39
|
+
*/
|
|
40
|
+
export function frozenValid(document) {
|
|
41
|
+
freeze(document);
|
|
42
|
+
rememberValid(document.project);
|
|
43
|
+
sealedDocuments.add(document);
|
|
44
|
+
return document;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* `document` itself when {@link frozenValid} froze it, which nothing can change, or else a copy
|
|
48
|
+
* for a session to own. Keeping the frozen document keeps what validating its parts found.
|
|
49
|
+
*/
|
|
50
|
+
export function owned(document) {
|
|
51
|
+
return sealedDocuments.has(document) ? document : structuredClone(document);
|
|
52
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from "./core/document.js";
|
|
2
|
+
export * from "./core/time.js";
|
|
3
|
+
export * from "./core/commands.js";
|
|
4
|
+
export * from "./core/compositions.js";
|
|
5
|
+
export * from "./core/clip-time.js";
|
|
6
|
+
export * from "./core/session.js";
|
|
7
|
+
export * from "./core/timeline-index.js";
|
|
8
|
+
export * from "./core/keyframes.js";
|
|
9
|
+
export * from "./core/presets.js";
|
|
10
|
+
export * from "./core/captions.js";
|
|
11
|
+
export * from "./core/text.js";
|
|
12
|
+
export type * from "./ports/index.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from "./core/document.js";
|
|
2
|
+
export * from "./core/time.js";
|
|
3
|
+
export * from "./core/commands.js";
|
|
4
|
+
export * from "./core/compositions.js";
|
|
5
|
+
export * from "./core/clip-time.js";
|
|
6
|
+
export * from "./core/session.js";
|
|
7
|
+
export * from "./core/timeline-index.js";
|
|
8
|
+
export * from "./core/keyframes.js";
|
|
9
|
+
export * from "./core/presets.js";
|
|
10
|
+
export * from "./core/captions.js";
|
|
11
|
+
export * from "./core/text.js";
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type { MediaFingerprint } from "alpha2/project";
|
|
2
|
+
import type { EditorDocument } from "../core/document.js";
|
|
3
|
+
import type { EditorSnapshot } from "../core/session.js";
|
|
4
|
+
import { type ExportFailure } from "alpha2/export-failure";
|
|
5
|
+
import { ExportJob } from "../browser/jobs.js";
|
|
6
|
+
import type { BrowserExportOptions } from "../browser/types.js";
|
|
7
|
+
/**
|
|
8
|
+
* A file uploaded for a native export, usually produced by the browser bridge's `nativeAssets`.
|
|
9
|
+
* The host reads media only from these uploads, never from a filesystem path the document names.
|
|
10
|
+
*/
|
|
11
|
+
export interface NativeAsset {
|
|
12
|
+
/** The source, resource or font id the file belongs to (for `frames`, the upload's own id). */
|
|
13
|
+
readonly id: string;
|
|
14
|
+
/**
|
|
15
|
+
* `proxies`: a proxy standing in for source `id`, made from `madeFrom`. `frames`: one frame of an
|
|
16
|
+
* image sequence, the file at `locator`.
|
|
17
|
+
*/
|
|
18
|
+
readonly kind: "assets" | "effect-assets" | "caption-fonts" | "proxies" | "frames";
|
|
19
|
+
readonly blob: Blob;
|
|
20
|
+
/** The original file name, sent to the host percent-encoded. */
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly madeFrom?: MediaFingerprint;
|
|
23
|
+
readonly locator?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Export settings for a native job: the browser export's options, plus the MPEG-4 Part 2 codec the
|
|
27
|
+
* host can encode.
|
|
28
|
+
*/
|
|
29
|
+
export interface NativeExportOptions extends Omit<BrowserExportOptions, "videoCodec"> {
|
|
30
|
+
readonly videoCodec?: BrowserExportOptions["videoCodec"] | "mpeg4-part2";
|
|
31
|
+
}
|
|
32
|
+
/** A finished native export's file, as the service keeps it. */
|
|
33
|
+
export interface NativeArtifactDescriptor {
|
|
34
|
+
readonly name: string;
|
|
35
|
+
readonly bytes: number;
|
|
36
|
+
readonly sha256: string;
|
|
37
|
+
readonly createdAt: number;
|
|
38
|
+
/** When the service deletes it, in milliseconds since the epoch. */
|
|
39
|
+
readonly expiresAt: number;
|
|
40
|
+
}
|
|
41
|
+
/** Whether a kept file can still be downloaded, and if not, why. */
|
|
42
|
+
export type NativeArtifactStatus = {
|
|
43
|
+
readonly status: "available";
|
|
44
|
+
readonly expiresAt: number | null;
|
|
45
|
+
} | {
|
|
46
|
+
readonly status: "gone";
|
|
47
|
+
/** `expired`, `deleted`, or `evicted`, as the service says. */
|
|
48
|
+
readonly reason: string;
|
|
49
|
+
} | {
|
|
50
|
+
readonly status: "missing" | "unreachable";
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Asks the service whether the file at `url` (an artifact's) can still be downloaded, without
|
|
54
|
+
* downloading it. Never rejects: a network error is `unreachable`, a 404 is `missing`, and a 410
|
|
55
|
+
* is `gone` with the reason the service gives (`expired` when it gives none).
|
|
56
|
+
*/
|
|
57
|
+
export declare function nativeArtifactStatus(url: string, request?: typeof fetch): Promise<NativeArtifactStatus>;
|
|
58
|
+
/** A native job's state as the service reports it. */
|
|
59
|
+
export interface NativeJobState {
|
|
60
|
+
readonly phase: "preparing" | "rendering" | "finalizing" | "publishing" | "completed" | "failed" | "cancelled";
|
|
61
|
+
/** The finished file, once `completed`. */
|
|
62
|
+
readonly artifact?: NativeArtifactDescriptor;
|
|
63
|
+
/** The share done, from 0 to 1. */
|
|
64
|
+
readonly fraction: number;
|
|
65
|
+
readonly frames?: {
|
|
66
|
+
readonly completed: number;
|
|
67
|
+
readonly total: number;
|
|
68
|
+
};
|
|
69
|
+
/** The failure message, when `failed` or `cancelled`. */
|
|
70
|
+
readonly error?: string;
|
|
71
|
+
/** The host's classified failure, when `failed`. */
|
|
72
|
+
readonly failure?: ExportFailure;
|
|
73
|
+
/**
|
|
74
|
+
* The host's report on the render cache of the last attempt to finish rendering: `used` with its
|
|
75
|
+
* counts, or `unavailable` with why (the GPU-resident path bakes nothing); `null` for an attempt
|
|
76
|
+
* rendered without one (a first attempt, a short export). Absent until an attempt reports.
|
|
77
|
+
*/
|
|
78
|
+
readonly resume?: {
|
|
79
|
+
readonly status: "used";
|
|
80
|
+
readonly [count: string]: unknown;
|
|
81
|
+
} | {
|
|
82
|
+
readonly status: "unavailable";
|
|
83
|
+
readonly reason: string;
|
|
84
|
+
} | null;
|
|
85
|
+
}
|
|
86
|
+
export interface NativeClientOptions {
|
|
87
|
+
/** The export service's root URL; a trailing slash is ignored. */
|
|
88
|
+
readonly baseUrl: string;
|
|
89
|
+
/** The `fetch` used for every request; the global one by default. */
|
|
90
|
+
readonly fetch?: typeof fetch;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* A client for the native export service (`@alpha2-sh/node`'s HTTP host). Exports run on the service
|
|
94
|
+
* with the editor's retry policy; each job uploads its assets once and reuses them across attempts.
|
|
95
|
+
*/
|
|
96
|
+
export declare function createNativeClient(options: NativeClientOptions): {
|
|
97
|
+
/** The service's `/health` report. Rejects when the service answers with an error status. */
|
|
98
|
+
capabilities(): Promise<Record<string, unknown>>;
|
|
99
|
+
/**
|
|
100
|
+
* Starts an export of `editor.document` on the service and returns its job. The document,
|
|
101
|
+
* `assets` and `settings` are captured now; later edits do not reach the job. `assets` may be
|
|
102
|
+
* a function, called once per upload with the document and the attempt's signal. The range is
|
|
103
|
+
* the document's export range, else the whole timeline. Throws when the client is disposed,
|
|
104
|
+
* and an `EditorError` `invalid_frame_rate` for a `frameRate` the engine does not read.
|
|
105
|
+
*/
|
|
106
|
+
startExport(editor: Pick<EditorSnapshot, "document" | "revision">, assets: readonly NativeAsset[] | ((document: EditorDocument, signal: AbortSignal) => Promise<readonly NativeAsset[]>), settings?: NativeExportOptions): ExportJob;
|
|
107
|
+
/**
|
|
108
|
+
* What the service knows of job `id`, for a page that lost the job it started (it was
|
|
109
|
+
* reloaded): its state, and where its file can be downloaded once it is complete. Null when the
|
|
110
|
+
* service no longer has the job. Rejects on any other error status.
|
|
111
|
+
*/
|
|
112
|
+
jobState(id: string): Promise<(NativeJobState & {
|
|
113
|
+
readonly url?: string;
|
|
114
|
+
}) | null>;
|
|
115
|
+
/**
|
|
116
|
+
* Removes job `id` from the service: a render it runs stops, and its kept file is deleted. For
|
|
117
|
+
* a job this client did not start, such as one a page that is gone left (see
|
|
118
|
+
* {@link jobState}). Rejects when the service answers with an error status.
|
|
119
|
+
*/
|
|
120
|
+
releaseJob(id: string): Promise<void>;
|
|
121
|
+
/** Cancels every running job and refuses new ones. Resolves once the cancellations settle. */
|
|
122
|
+
dispose(): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Lets go of every running job and refuses new ones, for a page that is leaving, without
|
|
125
|
+
* cancelling what the service already renders: a job whose render was asked for goes on there
|
|
126
|
+
* and keeps its file, for a later page to find with {@link jobState}. A job still uploading is
|
|
127
|
+
* removed, since only this client could send the rest. The jobs here settle as cancelled.
|
|
128
|
+
* Resolves once they settle; a removal that fails is left to the service's lease.
|
|
129
|
+
*/
|
|
130
|
+
detach(): Promise<void>;
|
|
131
|
+
};
|