@mengine/medeo-client 1.2.1-alpha.9 → 1.3.1-alpha.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 +43 -11
- package/dist/{document-Df6awJDH.js → document-Caidonf8.js} +214 -66
- package/dist/{index-CbuWTReu.d.ts → index-WxMNUbOa.d.ts} +9 -5
- package/dist/index.d.ts +90 -19
- package/dist/index.js +271 -86
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -10,15 +10,43 @@ asset facts, not an entity store implementation.
|
|
|
10
10
|
For entity-authoritative documents:
|
|
11
11
|
|
|
12
12
|
1. `EntityGraphHttpClient.fetchState()` obtains the complete graph and revision.
|
|
13
|
-
2. `
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
`
|
|
13
|
+
2. `ensureEditorFoundation` gets or creates one Timeline and four role Tracks,
|
|
14
|
+
retaining all existing identities and settings. `importMediaAsset` groups and
|
|
15
|
+
deduplicates only by direct media variants carrying the same external asset
|
|
16
|
+
id `(system, key)`. Imports mint exactly one media variant Entity
|
|
17
|
+
(Video/Image/Audio/Voice) that carries the Asset locator itself; no separate
|
|
18
|
+
`asset` Entity and no `physical-asset` Relation are created, and the returned
|
|
19
|
+
identity is that single Entity. Video duration must come from asset
|
|
20
|
+
metadata, never a clip trim window. Dual-entity media graphs (`asset` row
|
|
21
|
+
plus a `physical-asset` binding) are not reused and fail explicitly instead
|
|
22
|
+
of minting a second identity. This policy is local to the MEngine editor, not
|
|
23
|
+
the general-purpose DSL.
|
|
17
24
|
3. `EntityTimelineEditor` edits Clip/Marker/Track relations and payloads.
|
|
18
25
|
4. `EntityGraphHttpClient.commit(baseState, nextRows)` performs revision CAS,
|
|
19
26
|
including explicit net deletions. The server writes entities and the derived
|
|
20
27
|
Loro read view in the same transaction.
|
|
21
28
|
|
|
29
|
+
Repeated placement reuses the same single media identity but creates independent
|
|
30
|
+
Clips and SequenceMarkers. Asset generation alone still does not create editor
|
|
31
|
+
Entities. Two commit-level guards protect the single-identity policy:
|
|
32
|
+
`assertCanonicalEditorResources(rows)` checks one complete state — every media
|
|
33
|
+
variant carries its own external locator, one external asset id has exactly one
|
|
34
|
+
claimant, and no media variant is bound through a `physical-asset` Relation.
|
|
35
|
+
The exported `assertMediaAssetWritePolicy(before, after)` checks one CAS
|
|
36
|
+
transition and is reusable by the host tool and the entity stores: it applies
|
|
37
|
+
the same rules to the complete after state, and a self-carried identity cannot
|
|
38
|
+
be rewritten to another asset id. The editor applies it to every transaction
|
|
39
|
+
and `EntityGraphHttpClient.commit` applies it to `(baseState.rows, nextRows)`
|
|
40
|
+
before any request. There is no historical exemption: dual-entity media graphs
|
|
41
|
+
are rejected rather than kept editable — the shared `decodeEntityRelationRows`
|
|
42
|
+
boundary already fails rows whose media variants lack `external` or carry a
|
|
43
|
+
`physical-asset` binding, so editors and commits never observe them — deleting
|
|
44
|
+
media is an explicit caller decision, and no implicit historical merge or
|
|
45
|
+
identity rewriting occurs. The
|
|
46
|
+
compatibility reader reads physical facts directly from the media variant;
|
|
47
|
+
Caption inline text still resolves its inline Asset through
|
|
48
|
+
`physical-asset`.
|
|
49
|
+
|
|
22
50
|
`SequenceMarker` owns source/target ranges, duration, and time remapping. `Clip`
|
|
23
51
|
owns clip properties such as volume; `Track` owns track properties. References
|
|
24
52
|
between entities belong in Relations, not in payload IDs. There is no separate
|
|
@@ -39,19 +67,23 @@ cold-start reconciliation must not POST legacy `/updates`. Entity writes use
|
|
|
39
67
|
the independent graph CAS endpoint.
|
|
40
68
|
|
|
41
69
|
Anchored placement is a `clip-anchor` Relation from child Clip to host Clip
|
|
42
|
-
plus `SequenceMarker.anchorOffset`. Voice
|
|
43
|
-
|
|
70
|
+
plus `SequenceMarker.anchorOffset`. Voice and Caption can be independently
|
|
71
|
+
placed or explicitly anchored; generation relations do not choose a host. `resolveEntityTimelineLayout` resolves this graph
|
|
44
72
|
without reparenting or synthesizing content. Move/delete operations must write
|
|
45
73
|
their follow, keep-absolute, cascade, or detach decisions into the same entity
|
|
46
74
|
plan. A speech-overlap shift is explicit placement on the real visual Clips;
|
|
47
75
|
an uncovered time interval does not require an invented empty-media entity.
|
|
48
76
|
|
|
49
77
|
Voice links to its AudioScript through `audio-script-render`; Caption links to
|
|
50
|
-
|
|
51
|
-
speech identity and physical storage key belong to
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
78
|
+
its source script and optional alignment Voice through separate provenance/alignment relations. External
|
|
79
|
+
speech identity and physical storage key belong to the Voice Entity itself.
|
|
80
|
+
`insertCaptionClip` creates a complete independent Caption placement;
|
|
81
|
+
`upsertVoiceoverTake` accepts explicit `placement` for independent voice.
|
|
82
|
+
AudioScript carries only text. `audio-script-marker` attaches directly assigned
|
|
83
|
+
`segmentRanges` timing; those annotation Markers cannot be Clip display Markers.
|
|
84
|
+
Caption styles belong to Caption; text can have a real inline text Asset rather
|
|
85
|
+
than a fabricated Memota ID. Background Audio's Marker declares
|
|
86
|
+
`durationPolicy: 'timeline'`: its source duration stays factual,
|
|
55
87
|
while the view fills the non-BGM timeline duration.
|
|
56
88
|
|
|
57
89
|
`migrateLegacyTimelineToEntities` is a one-time migration utility, **not** an
|
|
@@ -130,7 +130,7 @@ const captionStyle$1 = schema.LoroMap({
|
|
|
130
130
|
const captionPart = schema.LoroMap({
|
|
131
131
|
id: schema.String(),
|
|
132
132
|
initial_duration_ms: schema.Number(),
|
|
133
|
-
speech_part_id: schema.String(),
|
|
133
|
+
speech_part_id: schema.String({ required: false }),
|
|
134
134
|
text: schema.String(),
|
|
135
135
|
start_ms: schema.Number(),
|
|
136
136
|
style: captionStyle$1
|
|
@@ -819,7 +819,7 @@ const captionPartSchema = z.object({
|
|
|
819
819
|
id: z.string().optional(),
|
|
820
820
|
kind: z.literal("caption"),
|
|
821
821
|
initial_duration_ms: finiteNumber,
|
|
822
|
-
speech_part_id: z.string(),
|
|
822
|
+
speech_part_id: z.string().optional(),
|
|
823
823
|
text: z.string(),
|
|
824
824
|
start_ms: finiteNumber,
|
|
825
825
|
style: z.object({
|
|
@@ -1489,45 +1489,6 @@ function emptyLaneTracks() {
|
|
|
1489
1489
|
}));
|
|
1490
1490
|
}
|
|
1491
1491
|
//#endregion
|
|
1492
|
-
//#region src/document/entity-projection-values.ts
|
|
1493
|
-
/** A graph cannot be represented faithfully by the compatibility reader. */
|
|
1494
|
-
var EntityTimelineProjectionError = class extends Error {
|
|
1495
|
-
constructor(message) {
|
|
1496
|
-
super(message);
|
|
1497
|
-
this.name = "EntityTimelineProjectionError";
|
|
1498
|
-
}
|
|
1499
|
-
};
|
|
1500
|
-
function fail(message) {
|
|
1501
|
-
throw new EntityTimelineProjectionError(message);
|
|
1502
|
-
}
|
|
1503
|
-
function object(value) {
|
|
1504
|
-
return value !== void 0 && value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1505
|
-
}
|
|
1506
|
-
function finite(value, label) {
|
|
1507
|
-
if (typeof value !== "number" || !Number.isFinite(value)) fail(`${label} must be finite`);
|
|
1508
|
-
return value;
|
|
1509
|
-
}
|
|
1510
|
-
function positive(value, label) {
|
|
1511
|
-
const result = finite(value, label);
|
|
1512
|
-
if (result <= 0) fail(`${label} must be positive`);
|
|
1513
|
-
return result;
|
|
1514
|
-
}
|
|
1515
|
-
function milliseconds(value, label) {
|
|
1516
|
-
const result = finite(value, label);
|
|
1517
|
-
if (!Number.isSafeInteger(result) || result < 0) fail(`${label} must be non-negative whole milliseconds`);
|
|
1518
|
-
return result;
|
|
1519
|
-
}
|
|
1520
|
-
function range(value, label) {
|
|
1521
|
-
if (!object(value)) fail(`${label} must be a numeric range`);
|
|
1522
|
-
const start = finite(value.start, `${label}.start`);
|
|
1523
|
-
const end = finite(value.end, `${label}.end`);
|
|
1524
|
-
if (start >= end) fail(`${label} must be non-empty`);
|
|
1525
|
-
return {
|
|
1526
|
-
start,
|
|
1527
|
-
end
|
|
1528
|
-
};
|
|
1529
|
-
}
|
|
1530
|
-
//#endregion
|
|
1531
1492
|
//#region ../medeo-dsl/src/entities.ts
|
|
1532
1493
|
function hasSequence(entity) {
|
|
1533
1494
|
if (isKnownNonSequenceKind(entity.entityKind) || isReservedEntityKind(entity.entityKind)) return false;
|
|
@@ -1544,7 +1505,10 @@ function isSequenceFields(value) {
|
|
|
1544
1505
|
return "coordinateSpace" in value && value.coordinateSpace !== void 0;
|
|
1545
1506
|
}
|
|
1546
1507
|
function isKnownSequenceKind(kind) {
|
|
1547
|
-
return kind
|
|
1508
|
+
return isMediaAssetVariantKind(kind) || kind === "caption" || kind === "axvideo";
|
|
1509
|
+
}
|
|
1510
|
+
function isMediaAssetVariantKind(kind) {
|
|
1511
|
+
return kind === "video" || kind === "image" || kind === "audio" || kind === "voice";
|
|
1548
1512
|
}
|
|
1549
1513
|
function isKnownEntityKind(kind) {
|
|
1550
1514
|
return isKnownSequenceKind(kind) || isKnownNonSequenceKind(kind);
|
|
@@ -1573,6 +1537,18 @@ function createId(value, label) {
|
|
|
1573
1537
|
}
|
|
1574
1538
|
//#endregion
|
|
1575
1539
|
//#region ../medeo-dsl/src/invariants.ts
|
|
1540
|
+
/**
|
|
1541
|
+
* Media variants own their Asset identity directly: a `physical-asset`
|
|
1542
|
+
* Relation may not bind them. Caption inline text (and other non-media
|
|
1543
|
+
* Sequence entities) keep their Asset binding.
|
|
1544
|
+
*/
|
|
1545
|
+
function validateMediaAssetIdentity(entity, index) {
|
|
1546
|
+
return [...index.relationsOf(entity)].filter((relation) => relation.kind === "physical-asset").map(() => ({
|
|
1547
|
+
code: "media_physical_asset_forbidden",
|
|
1548
|
+
entityId: entity.entityId,
|
|
1549
|
+
message: `Media entity "${entity.entityId}" owns its Asset identity; remove its physical-asset Relation`
|
|
1550
|
+
}));
|
|
1551
|
+
}
|
|
1576
1552
|
/** Validates one complete set of entities and its authoritative Relation rows. */
|
|
1577
1553
|
function validateEntityRelationSet(entityRefs, index, options) {
|
|
1578
1554
|
const { entities, issues } = collectRelatedEntities(entityRefs, index);
|
|
@@ -1581,6 +1557,7 @@ function validateEntityRelationSet(entityRefs, index, options) {
|
|
|
1581
1557
|
const current = entity.current();
|
|
1582
1558
|
const entityIssues = validateEntity(entity, entityIds);
|
|
1583
1559
|
issues.push(...entityIssues);
|
|
1560
|
+
if (isMediaAssetVariantKind(current.entityKind)) issues.push(...validateMediaAssetIdentity(entity, index));
|
|
1584
1561
|
if (entityIssues.some((issue) => issue.code === "invalid_entity_payload")) continue;
|
|
1585
1562
|
if (current.entityKind === "sequence-marker") {
|
|
1586
1563
|
const marker = entity;
|
|
@@ -1608,7 +1585,39 @@ function validateMarkerUse(marker, index) {
|
|
|
1608
1585
|
const axVideoEdges = ofKind(relations, "axvideo-marker");
|
|
1609
1586
|
const contentEdges = ofKind(relations, "marker-content");
|
|
1610
1587
|
const timelineEdges = ofKind(relations, "marker-timeline");
|
|
1588
|
+
const scriptEdges = ofKind(relations, "audio-script-marker");
|
|
1611
1589
|
const issues = [];
|
|
1590
|
+
if (scriptEdges.length > 0) {
|
|
1591
|
+
if (clipEdges.length + axVideoEdges.length + contentEdges.length + timelineEdges.length > 0) issues.push({
|
|
1592
|
+
code: "marker_container_xor",
|
|
1593
|
+
entityId: marker.entityId,
|
|
1594
|
+
message: "An AudioScript annotation Marker must not carry Clip, AXVideo, content, or Timeline relations"
|
|
1595
|
+
});
|
|
1596
|
+
const ranges = marker.current().segmentRanges;
|
|
1597
|
+
if (!ranges?.length) issues.push({
|
|
1598
|
+
code: "invalid_entity_payload",
|
|
1599
|
+
entityId: marker.entityId,
|
|
1600
|
+
message: "An AudioScript annotation Marker requires segmentRanges"
|
|
1601
|
+
});
|
|
1602
|
+
for (const relation of scriptEdges) {
|
|
1603
|
+
const script = relation.other(marker)?.deref()?.current();
|
|
1604
|
+
if (script?.entityKind !== "audio-script") continue;
|
|
1605
|
+
const segments = script.segments;
|
|
1606
|
+
if (!Array.isArray(segments)) continue;
|
|
1607
|
+
const segmentIds = new Set(segments.map((segment) => segment.segmentId));
|
|
1608
|
+
if (ranges?.some((range) => !segmentIds.has(range.segmentId))) issues.push({
|
|
1609
|
+
code: "invalid_entity_payload",
|
|
1610
|
+
entityId: marker.entityId,
|
|
1611
|
+
message: "Annotation segmentRanges must name segments of the related AudioScript"
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
return issues;
|
|
1615
|
+
}
|
|
1616
|
+
if (marker.current().segmentRanges !== void 0) issues.push({
|
|
1617
|
+
code: "invalid_entity_payload",
|
|
1618
|
+
entityId: marker.entityId,
|
|
1619
|
+
message: "segmentRanges belongs to an AudioScript annotation Marker, not a display Marker"
|
|
1620
|
+
});
|
|
1612
1621
|
if (clipEdges.length + axVideoEdges.length !== 1) issues.push({
|
|
1613
1622
|
code: "marker_container_xor",
|
|
1614
1623
|
entityId: marker.entityId,
|
|
@@ -1711,10 +1720,10 @@ function validateClipPlacement(clip, index) {
|
|
|
1711
1720
|
const content = ofKind([...index.relationsOf(marker)], "marker-content")[0]?.other(marker)?.deref()?.current();
|
|
1712
1721
|
const track = ofKind([...index.relationsOf(clip)], "track-clip")[0]?.other(clip)?.deref()?.current();
|
|
1713
1722
|
const trackRole = track?.entityKind === "track" ? track.role : void 0;
|
|
1714
|
-
if (content?.entityKind !== "audio" || trackRole !== "bgm" || !hasOrder) issues.push({
|
|
1723
|
+
if (content?.entityKind !== "audio" || trackRole !== "bgm" || !hasOrder && !hasTarget) issues.push({
|
|
1715
1724
|
code: "timeline_duration_policy_invalid",
|
|
1716
1725
|
entityId: marker.entityId,
|
|
1717
|
-
message: "Marker durationPolicy \"timeline\" is only valid for ordered Audio Clips on the bgm Track"
|
|
1726
|
+
message: "Marker durationPolicy \"timeline\" is only valid for ordered or absolutely placed Audio Clips on the bgm Track"
|
|
1718
1727
|
});
|
|
1719
1728
|
}
|
|
1720
1729
|
return issues;
|
|
@@ -1824,11 +1833,13 @@ function validateKnownEntityPayload(entity) {
|
|
|
1824
1833
|
case "voice":
|
|
1825
1834
|
case "caption":
|
|
1826
1835
|
validateSequencePayload(value, "bounded", "native", problems);
|
|
1836
|
+
if (entity.entityKind !== "caption") validateMediaAssetFields(value, problems);
|
|
1827
1837
|
if (entity.entityKind === "voice") validateVoicePayload(value, problems);
|
|
1828
1838
|
if (entity.entityKind === "caption") validateCaptionPayload(value, problems);
|
|
1829
1839
|
break;
|
|
1830
1840
|
case "image":
|
|
1831
1841
|
validateSequencePayload(value, "unbounded", "constant", problems);
|
|
1842
|
+
validateMediaAssetFields(value, problems);
|
|
1832
1843
|
break;
|
|
1833
1844
|
case "axvideo":
|
|
1834
1845
|
validateSequencePayload(value, "bounded", "derived", problems);
|
|
@@ -1876,22 +1887,64 @@ function validateMarkerPayload(value, problems) {
|
|
|
1876
1887
|
} else if (duration.mode !== "from-source") problems.push("duration.mode must be \"from-source\" or \"fixed\"");
|
|
1877
1888
|
if (value.anchorOffset === void 0 && Object.hasOwn(value, "anchorOffset")) problems.push("anchorOffset cannot be undefined when present");
|
|
1878
1889
|
if (value.durationPolicy !== void 0 && value.durationPolicy !== "timeline") problems.push("durationPolicy must be \"timeline\" when present");
|
|
1890
|
+
if (value.segmentRanges !== void 0) validateSegmentRanges(value.segmentRanges, problems);
|
|
1879
1891
|
}
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
if (
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
if (typeof external.key !== "string" || external.key.trim() === "") problems.push("external.key must be a non-empty string");
|
|
1892
|
+
/** Annotation Marker time values are directly assigned facts; no cross-references are allowed. */
|
|
1893
|
+
function validateSegmentRanges(value, problems) {
|
|
1894
|
+
if (!Array.isArray(value)) {
|
|
1895
|
+
problems.push("segmentRanges must be an array when present");
|
|
1896
|
+
return;
|
|
1886
1897
|
}
|
|
1887
|
-
|
|
1898
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1899
|
+
for (const [index, entry] of value.entries()) {
|
|
1900
|
+
if (!isRecord(entry)) {
|
|
1901
|
+
problems.push(`segmentRanges[${index}] must be an object`);
|
|
1902
|
+
continue;
|
|
1903
|
+
}
|
|
1904
|
+
if (typeof entry.segmentId !== "string") problems.push(`segmentRanges[${index}].segmentId must be a string`);
|
|
1905
|
+
else if (seen.has(entry.segmentId)) problems.push(`segmentRanges[${index}].segmentId must be unique within the Marker`);
|
|
1906
|
+
else seen.add(entry.segmentId);
|
|
1907
|
+
for (const key of ["startMs", "endMs"]) {
|
|
1908
|
+
const point = entry[key];
|
|
1909
|
+
if (typeof point !== "number" || !Number.isFinite(point)) problems.push(`segmentRanges[${index}].${key} must be a finite number`);
|
|
1910
|
+
}
|
|
1911
|
+
if (typeof entry.startMs === "number" && Number.isFinite(entry.startMs) && typeof entry.endMs === "number" && Number.isFinite(entry.endMs) && entry.startMs > entry.endMs) problems.push(`segmentRanges[${index}].startMs must not exceed segmentRanges[${index}].endMs`);
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
function validateAssetPayload(value, problems) {
|
|
1915
|
+
validateExternalLocator(value, problems);
|
|
1916
|
+
validateStorageKey(value, problems);
|
|
1888
1917
|
const inline = value.inline;
|
|
1889
1918
|
if (inline !== void 0) if (!isRecord(inline)) problems.push("inline must be an object when present");
|
|
1890
1919
|
else {
|
|
1891
1920
|
if (inline.mediaType !== "text/plain") problems.push("inline.mediaType must be \"text/plain\"");
|
|
1892
1921
|
if (typeof inline.text !== "string") problems.push("inline.text must be a string");
|
|
1893
1922
|
}
|
|
1894
|
-
if (external !== void 0 && inline !== void 0) problems.push("Asset must not combine external and inline physical locations");
|
|
1923
|
+
if (value.external !== void 0 && value.inline !== void 0) problems.push("Asset must not combine external and inline physical locations");
|
|
1924
|
+
}
|
|
1925
|
+
/**
|
|
1926
|
+
* Media variants own their Asset locator directly: `external` is required and
|
|
1927
|
+
* inline text stays an Asset-kind-only value so the two physical-location
|
|
1928
|
+
* vocabularies cannot mix.
|
|
1929
|
+
*/
|
|
1930
|
+
function validateMediaAssetFields(value, problems) {
|
|
1931
|
+
if (value.external === void 0) problems.push("external is required; media variants own their Asset identity");
|
|
1932
|
+
else validateExternalLocator(value, problems);
|
|
1933
|
+
validateStorageKey(value, problems);
|
|
1934
|
+
if (value.inline !== void 0) problems.push("inline is an Asset-kind field; media variants use external");
|
|
1935
|
+
}
|
|
1936
|
+
function validateExternalLocator(value, problems) {
|
|
1937
|
+
const external = value.external;
|
|
1938
|
+
if (external === void 0) return;
|
|
1939
|
+
if (!isRecord(external)) {
|
|
1940
|
+
problems.push("external must be an object when present");
|
|
1941
|
+
return;
|
|
1942
|
+
}
|
|
1943
|
+
if (external.system !== "memota" && external.system !== "memota-speech") problems.push("external.system must be \"memota\" or \"memota-speech\"");
|
|
1944
|
+
if (typeof external.key !== "string" || external.key.trim() === "") problems.push("external.key must be a non-empty string");
|
|
1945
|
+
}
|
|
1946
|
+
function validateStorageKey(value, problems) {
|
|
1947
|
+
if (value.storageKey !== void 0 && (typeof value.storageKey !== "string" || value.storageKey.trim() === "")) problems.push("storageKey must be a non-empty string when present");
|
|
1895
1948
|
}
|
|
1896
1949
|
function validateVoicePayload(value, problems) {
|
|
1897
1950
|
const voice = value.voice;
|
|
@@ -1950,6 +2003,15 @@ function validateScriptPayload(value, problems) {
|
|
|
1950
2003
|
problems.push(`segments[${index}] must be an object`);
|
|
1951
2004
|
continue;
|
|
1952
2005
|
}
|
|
2006
|
+
for (const key of [
|
|
2007
|
+
"startMs",
|
|
2008
|
+
"endMs",
|
|
2009
|
+
"start_ms",
|
|
2010
|
+
"end_ms",
|
|
2011
|
+
"sourceRange",
|
|
2012
|
+
"targetRange",
|
|
2013
|
+
"duration"
|
|
2014
|
+
]) if (Object.hasOwn(segment, key)) problems.push(`segments[${index}].${key} is timing; attach an AudioScript annotation Marker instead`);
|
|
1953
2015
|
if (typeof segment.segmentId !== "string") problems.push(`segments[${index}].segmentId must be a string`);
|
|
1954
2016
|
if (typeof segment.text !== "string") problems.push(`segments[${index}].text must be a string`);
|
|
1955
2017
|
if (segment.language !== void 0 && typeof segment.language !== "string") problems.push(`segments[${index}].language must be a string when present`);
|
|
@@ -2008,7 +2070,9 @@ function isOwnedLocalEntityValuePath(entityKind, path) {
|
|
|
2008
2070
|
"axvideo"
|
|
2009
2071
|
].includes(entityKind) && /^(?:sampling|coordinateSpace|coordinateSpace\.unit|extent\.kind)$/.test(path)) return true;
|
|
2010
2072
|
if (entityKind === "sequence-marker" && /^(?:durationPolicy|duration\.mode|timeRemapping\.(?:kind|mode))$/.test(path)) return true;
|
|
2073
|
+
if (entityKind === "sequence-marker" && /^segmentRanges\[\d+\]\.segmentId$/.test(path)) return true;
|
|
2011
2074
|
if (entityKind === "asset" && /^(?:external\.(?:system|key)|storageKey|inline\.(?:mediaType|text))$/.test(path)) return true;
|
|
2075
|
+
if (isMediaAssetVariantKind(entityKind) && /^(?:external\.(?:system|key)|storageKey)$/.test(path)) return true;
|
|
2012
2076
|
if (entityKind === "voice" && /^voice\.(?:system|key|name)$/.test(path)) return true;
|
|
2013
2077
|
if (entityKind === "caption" && (path === "text" || path.startsWith("style."))) return true;
|
|
2014
2078
|
if ((entityKind === "audio-script" || entityKind === "phonetic-script") && /^segments\[\d+\]\.(?:segmentId|text|language)$/.test(path)) return true;
|
|
@@ -2017,6 +2081,7 @@ function isOwnedLocalEntityValuePath(entityKind, path) {
|
|
|
2017
2081
|
function isOwnedLocalIdPath(entityKind, path) {
|
|
2018
2082
|
if (path === "lifecycle.actorId") return true;
|
|
2019
2083
|
if ((entityKind === "audio-script" || entityKind === "phonetic-script") && /^segments\[\d+\]\.segmentId$/.test(path)) return true;
|
|
2084
|
+
if (entityKind === "sequence-marker" && /^segmentRanges\[\d+\]\.segmentId$/.test(path)) return true;
|
|
2020
2085
|
if (entityKind === "asset" && /^(?:tracks\[\d+\]\.trackId|renditions\[\d+\]\.renditionId)$/.test(path)) return true;
|
|
2021
2086
|
return false;
|
|
2022
2087
|
}
|
|
@@ -2144,6 +2209,23 @@ const audioScriptRenderRelationSpec = Object.freeze({
|
|
|
2144
2209
|
},
|
|
2145
2210
|
validateMetadata: isEmptyMetadata
|
|
2146
2211
|
});
|
|
2212
|
+
/** `audio-script-source(script, source)` means endpoint 0 was transcribed from endpoint 1. */
|
|
2213
|
+
const audioScriptSourceRelationSpec = Object.freeze({
|
|
2214
|
+
kind: "audio-script-source",
|
|
2215
|
+
validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["audio-script"]), new Set(["audio", "video"])),
|
|
2216
|
+
validateMetadata: isEmptyMetadata
|
|
2217
|
+
});
|
|
2218
|
+
/**
|
|
2219
|
+
* `audio-script-marker(script, marker)` attaches an annotation Sequence Marker
|
|
2220
|
+
* whose `segmentRanges` hold directly assigned per-Segment time values. The
|
|
2221
|
+
* script keeps no Sequence and no Clip admission; annotation Markers cannot
|
|
2222
|
+
* enter Clip/AXVideo use chains (enforced by the marker-use invariant).
|
|
2223
|
+
*/
|
|
2224
|
+
const audioScriptMarkerRelationSpec = Object.freeze({
|
|
2225
|
+
kind: "audio-script-marker",
|
|
2226
|
+
validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["audio-script"]), new Set(["sequence-marker"])),
|
|
2227
|
+
validateMetadata: isEmptyMetadata
|
|
2228
|
+
});
|
|
2147
2229
|
/** Built-in kinds are reserved; callers may add specs only under new names. */
|
|
2148
2230
|
const builtInRelationSpecs = Object.freeze([
|
|
2149
2231
|
timelineTrackRelationSpec,
|
|
@@ -2158,7 +2240,9 @@ const builtInRelationSpecs = Object.freeze([
|
|
|
2158
2240
|
captionProvenanceRelationSpec,
|
|
2159
2241
|
captionAlignmentRelationSpec,
|
|
2160
2242
|
clipAnchorRelationSpec,
|
|
2161
|
-
audioScriptRenderRelationSpec
|
|
2243
|
+
audioScriptRenderRelationSpec,
|
|
2244
|
+
audioScriptSourceRelationSpec,
|
|
2245
|
+
audioScriptMarkerRelationSpec
|
|
2162
2246
|
]);
|
|
2163
2247
|
function emptySpec(kind, a, b) {
|
|
2164
2248
|
return metadataSpec(kind, a, b, isEmptyMetadata);
|
|
@@ -2255,7 +2339,7 @@ var BiRelationIndex = class {
|
|
|
2255
2339
|
byRelationId = /* @__PURE__ */ new Map();
|
|
2256
2340
|
link(input) {
|
|
2257
2341
|
if (input.spec.kind === "generated") throw new Error("Author generated Relations with linkGenerated({ output, input })");
|
|
2258
|
-
if (input.spec.kind === "clip-anchor" || input.spec.kind === "audio-script-render") throw new Error(`Author ordered ${input.spec.kind} Relations with the dedicated role-named method`);
|
|
2342
|
+
if (input.spec.kind === "clip-anchor" || input.spec.kind === "audio-script-render" || input.spec.kind === "audio-script-source") throw new Error(`Author ordered ${input.spec.kind} Relations with the dedicated role-named method`);
|
|
2259
2343
|
return this.linkValidated(input);
|
|
2260
2344
|
}
|
|
2261
2345
|
/** Author `generated(output, input)` without exposing positional arguments. */
|
|
@@ -2288,6 +2372,16 @@ var BiRelationIndex = class {
|
|
|
2288
2372
|
trace: input.trace
|
|
2289
2373
|
});
|
|
2290
2374
|
}
|
|
2375
|
+
/** Author `audio-script-source(script, source)` without exposing positional arguments. */
|
|
2376
|
+
linkAudioScriptSource(input) {
|
|
2377
|
+
return this.linkValidated({
|
|
2378
|
+
relationId: input.relationId,
|
|
2379
|
+
spec: audioScriptSourceRelationSpec,
|
|
2380
|
+
endpoints: [input.script, input.source],
|
|
2381
|
+
metadata: {},
|
|
2382
|
+
trace: input.trace
|
|
2383
|
+
});
|
|
2384
|
+
}
|
|
2291
2385
|
/**
|
|
2292
2386
|
* Rehydrate a persisted row after resolving its spec and endpoint refs.
|
|
2293
2387
|
*
|
|
@@ -2507,6 +2601,45 @@ function errorMessage(error) {
|
|
|
2507
2601
|
return error instanceof Error ? error.message : String(error);
|
|
2508
2602
|
}
|
|
2509
2603
|
//#endregion
|
|
2604
|
+
//#region src/document/entity-projection-values.ts
|
|
2605
|
+
/** A graph cannot be represented faithfully by the compatibility reader. */
|
|
2606
|
+
var EntityTimelineProjectionError = class extends Error {
|
|
2607
|
+
constructor(message) {
|
|
2608
|
+
super(message);
|
|
2609
|
+
this.name = "EntityTimelineProjectionError";
|
|
2610
|
+
}
|
|
2611
|
+
};
|
|
2612
|
+
function fail(message) {
|
|
2613
|
+
throw new EntityTimelineProjectionError(message);
|
|
2614
|
+
}
|
|
2615
|
+
function object(value) {
|
|
2616
|
+
return value !== void 0 && value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2617
|
+
}
|
|
2618
|
+
function finite(value, label) {
|
|
2619
|
+
if (typeof value !== "number" || !Number.isFinite(value)) fail(`${label} must be finite`);
|
|
2620
|
+
return value;
|
|
2621
|
+
}
|
|
2622
|
+
function positive(value, label) {
|
|
2623
|
+
const result = finite(value, label);
|
|
2624
|
+
if (result <= 0) fail(`${label} must be positive`);
|
|
2625
|
+
return result;
|
|
2626
|
+
}
|
|
2627
|
+
function milliseconds(value, label) {
|
|
2628
|
+
const result = finite(value, label);
|
|
2629
|
+
if (!Number.isSafeInteger(result) || result < 0) fail(`${label} must be non-negative whole milliseconds`);
|
|
2630
|
+
return result;
|
|
2631
|
+
}
|
|
2632
|
+
function range(value, label) {
|
|
2633
|
+
if (!object(value)) fail(`${label} must be a numeric range`);
|
|
2634
|
+
const start = finite(value.start, `${label}.start`);
|
|
2635
|
+
const end = finite(value.end, `${label}.end`);
|
|
2636
|
+
if (start >= end) fail(`${label} must be non-empty`);
|
|
2637
|
+
return {
|
|
2638
|
+
start,
|
|
2639
|
+
end
|
|
2640
|
+
};
|
|
2641
|
+
}
|
|
2642
|
+
//#endregion
|
|
2510
2643
|
//#region src/document/entity-timeline-layout.ts
|
|
2511
2644
|
/** Validated graph lookup shared by the compatibility layout and projection. */
|
|
2512
2645
|
var EntityProjectionGraph = class {
|
|
@@ -2643,7 +2776,7 @@ function resolveGraphLayout(graph) {
|
|
|
2643
2776
|
let start = fact.absoluteStart ?? 0;
|
|
2644
2777
|
if (fact.mode === "anchored") {
|
|
2645
2778
|
const host = resolve(fact.anchorId);
|
|
2646
|
-
if (fact.role === "speech" && facts.get(host.clipEntityId).role
|
|
2779
|
+
if (fact.role === "speech" && !["video_clip", "bgm"].includes(facts.get(host.clipEntityId).role)) fail("An anchored Voice must use a visual or BGM Clip as its host");
|
|
2647
2780
|
start = host.start + fact.anchorOffset;
|
|
2648
2781
|
} else if (fact.predecessor !== void 0) {
|
|
2649
2782
|
const prior = resolve(fact.predecessor);
|
|
@@ -2671,7 +2804,6 @@ function resolveGraphLayout(graph) {
|
|
|
2671
2804
|
const durationMs = Math.max(0, ...[...resolved.values()].filter((clip) => facts.get(clip.clipEntityId).role !== "bgm").map((clip) => clip.start + clip.duration));
|
|
2672
2805
|
for (const [id, fact] of facts) if (fact.fillTimeline) {
|
|
2673
2806
|
const clip = resolved.get(id);
|
|
2674
|
-
if (clip.start !== 0) fail("Timeline-filling BGM must start at zero");
|
|
2675
2807
|
resolved.set(id, {
|
|
2676
2808
|
...clip,
|
|
2677
2809
|
duration: durationMs
|
|
@@ -2710,8 +2842,8 @@ function projectEntityTimeline(rows, meta) {
|
|
|
2710
2842
|
const content = graph.entity(placement.contentEntityId);
|
|
2711
2843
|
const volume = volumeSchema.safeParse(clip.payload.volume ?? 0);
|
|
2712
2844
|
if (!volume.success) fail("Clip volume must be decibels between -60 and 20");
|
|
2713
|
-
const asset = graph.one(content.entityId, "physical-asset", "asset");
|
|
2714
2845
|
if (track.role === "video_clip") {
|
|
2846
|
+
const asset = mediaAssetFacts(graph, content);
|
|
2715
2847
|
const source = range(marker.payload.sourceRange, "sourceRange");
|
|
2716
2848
|
const external = externalLocator(asset, "memota");
|
|
2717
2849
|
const speed = videoSpeed(content, marker, placement);
|
|
@@ -2725,6 +2857,7 @@ function projectEntityTimeline(rows, meta) {
|
|
|
2725
2857
|
...speed === void 0 ? {} : { speed_shift: speed }
|
|
2726
2858
|
} };
|
|
2727
2859
|
} else if (track.role === "speech") {
|
|
2860
|
+
const asset = mediaAssetFacts(graph, content);
|
|
2728
2861
|
requireUntrimmedAudio(content, marker, placement);
|
|
2729
2862
|
const external = externalLocator(asset, "memota-speech");
|
|
2730
2863
|
const script = graph.one(content.entityId, "audio-script-render", "audio-script");
|
|
@@ -2748,27 +2881,25 @@ function projectEntityTimeline(rows, meta) {
|
|
|
2748
2881
|
} else if (track.role === "caption") {
|
|
2749
2882
|
const text = content.payload.text;
|
|
2750
2883
|
if (typeof text !== "string") fail("Caption " + content.entityId + " requires its text");
|
|
2884
|
+
const asset = graph.one(content.entityId, "physical-asset", "asset");
|
|
2751
2885
|
const inline = asset.payload.inline;
|
|
2752
2886
|
if (inline !== void 0 && (!object(inline) || inline.mediaType !== "text/plain" || inline.text !== text)) fail("Inline Caption Asset and Caption text must agree");
|
|
2753
2887
|
if (inline === void 0 && !object(asset.payload.external)) fail("Caption Asset must identify physical text or contain actual inline text");
|
|
2754
|
-
if (placement.anchorClipEntityId === void 0) fail("Caption placement requires an explicit voice Clip anchor");
|
|
2755
|
-
const anchor = layout.clips.get(placement.anchorClipEntityId);
|
|
2756
|
-
if (anchor === void 0 || graph.entity(anchor.contentEntityId).entityKind !== "voice") fail("Caption anchor must resolve a placed Voice");
|
|
2757
|
-
if (graph.one(content.entityId, "caption-alignment").entityId !== anchor.contentEntityId) fail("Caption alignment must agree with its placed Voice");
|
|
2758
|
-
const script = graph.one(content.entityId, "caption-provenance", "audio-script");
|
|
2759
|
-
if (graph.one(anchor.contentEntityId, "audio-script-render", "audio-script").entityId !== script.entityId) fail("Caption provenance must agree with the Voice AudioScript");
|
|
2760
2888
|
if (marker.payload.timeRemapping !== void 0) fail("Caption timeRemapping is not supported by this reader");
|
|
2761
2889
|
const style = captionStyle(content.payload.style);
|
|
2890
|
+
const anchor = placement.anchorClipEntityId === void 0 ? void 0 : layout.clips.get(placement.anchorClipEntityId);
|
|
2891
|
+
const anchoredToSpeech = anchor !== void 0 && trackRoleOf(layout, anchor.trackEntityId) === "speech";
|
|
2762
2892
|
parts[clip.entityId] = { caption: {
|
|
2763
2893
|
id: clip.entityId,
|
|
2764
2894
|
kind: "caption",
|
|
2765
2895
|
text,
|
|
2766
|
-
speech_part_id: placement.anchorClipEntityId,
|
|
2767
|
-
start_ms: placement.anchorOffset,
|
|
2896
|
+
speech_part_id: anchoredToSpeech ? placement.anchorClipEntityId : void 0,
|
|
2897
|
+
start_ms: anchoredToSpeech ? placement.anchorOffset : 0,
|
|
2768
2898
|
initial_duration_ms: placement.duration,
|
|
2769
2899
|
...style === void 0 ? {} : { style }
|
|
2770
2900
|
} };
|
|
2771
2901
|
} else {
|
|
2902
|
+
const asset = mediaAssetFacts(graph, content);
|
|
2772
2903
|
const external = externalLocator(asset, "memota");
|
|
2773
2904
|
if (marker.payload.durationPolicy !== "timeline") fail("Background Audio requires an explicit timeline-fill durationPolicy");
|
|
2774
2905
|
if (marker.payload.timeRemapping !== void 0) fail("BGM timeRemapping is not supported by this reader");
|
|
@@ -2815,6 +2946,23 @@ function projectEntityTimeline(rows, meta) {
|
|
|
2815
2946
|
assertValidVideoDocument(document);
|
|
2816
2947
|
return document;
|
|
2817
2948
|
}
|
|
2949
|
+
function trackRoleOf(layout, trackEntityId) {
|
|
2950
|
+
return layout.tracks.find((track) => track.entityId === trackEntityId)?.role;
|
|
2951
|
+
}
|
|
2952
|
+
/**
|
|
2953
|
+
* Media facts are read from the variant itself: a media variant owns its
|
|
2954
|
+
* Asset identity and carries `external`/`storageKey` directly. Dual-entity
|
|
2955
|
+
* media graphs are rejected instead of being resolved through a legacy
|
|
2956
|
+
* `physical-asset` binding.
|
|
2957
|
+
*/
|
|
2958
|
+
function mediaAssetFacts(graph, content) {
|
|
2959
|
+
if (graph.adjacent(content.entityId, "physical-asset").length > 0) fail("Media " + content.entityId + " owns its Asset identity; remove its physical-asset Relation");
|
|
2960
|
+
if (!object(content.payload.external)) fail("Media " + content.entityId + " must carry its own Asset external identity");
|
|
2961
|
+
return {
|
|
2962
|
+
entityId: String(content.entityId),
|
|
2963
|
+
payload: content.payload
|
|
2964
|
+
};
|
|
2965
|
+
}
|
|
2818
2966
|
function externalLocator(asset, system) {
|
|
2819
2967
|
const value = asset.payload.external;
|
|
2820
2968
|
if (!object(value) || value.system !== system || typeof value.key !== "string" || value.key.trim() === "") fail("Asset " + asset.entityId + " requires an explicit " + system + " external key");
|
|
@@ -3203,4 +3351,4 @@ function createPlainMemoryAdapter(document, options) {
|
|
|
3203
3351
|
return new PlainMemoryAdapter(document, options);
|
|
3204
3352
|
}
|
|
3205
3353
|
//#endregion
|
|
3206
|
-
export {
|
|
3354
|
+
export { videoDocumentMirrorSchema as $, ensureLaneTrack as A, resolveSpeechOverlapByShiftingVideos as B, toVideoDocument as C, partUnionSchema as D, validateVideoDocument as E, arrangeMainTrackSeamlessly as F, safeDurationMs as G, TIMELINE_SKELETON_DURATION_MS as H, fillMainTrackTimeGaps as I, VIDEO_DOCUMENT_SCHEMA_VERSION as J, DEFAULT_UNIT_TIME_MS as K, reassignSpeechesToVideoClipsByTime as L, laneTrackId as M, solveVideoDocument as N, videoDocumentSchema as O, cascadeAfterVideoClipChanges as P, recordEntries as Q, recalculateTimelineDuration as R, fromVideoDocument as S, assertValidVideoDocument as T, isEmptyVideoClip as U, syncAggregatedClipsTimePosition as V, partDurationMs as W, speedOf as X, effectiveVideoClipDurationMs as Y, partUnionToDraft as Z, hasSequence as _, applyEntityTimelineProjection as a, buildSpeechHostMap as b, writeVideoDocumentToDraft as c, resolveEntityTimelineLayout as d, base64ToBytes as et, EntityTimelineProjectionError as f, createRelationId as g, createEntityId as h, MirrorVideoDocumentAdapter as i, findLaneTrack as j, LANE_KINDS_IN_STACK_ORDER as k, readVideoDocumentFromDraft as l, isJsonObject as m, createPlainMemoryAdapter as n, createMirrorVideoDocument as o, decodeEntityRelationRows as p, ENTITY_TIMELINE_PROJECTION_SCHEMA_VERSION as q, generatePartId as r, createMirrorVideoDocumentAdapter as s, PlainMemoryAdapter as t, bytesToBase64 as tt, projectEntityTimeline as u, isMediaAssetVariantKind as v, VideoDocumentValidationError as w, derivePositionFromAbs as x, buildInitialVideoDocument as y, resolveAllSpeechOverlaps as z };
|
|
@@ -487,7 +487,11 @@ type SpeechPart = Omit<SpeechPart$1, 'duration_ms'> & {
|
|
|
487
487
|
*
|
|
488
488
|
* `speech_part_id` / `start_ms` are kept as resource-intrinsic facts (which
|
|
489
489
|
* speech it was split from, and its offset in that source) — reference/17
|
|
490
|
-
* principle 4.
|
|
490
|
+
* principle 4. An independently placed Caption (its own Clip and Sequence
|
|
491
|
+
* Marker, no Voice take) has no owning speech: both fields stay unset and its
|
|
492
|
+
* position is expressed entirely by the Clip's placement. Generation source
|
|
493
|
+
* (AudioScript provenance) and Voice alignment are entity Relations, decoupled
|
|
494
|
+
* from this display-side legacy view.
|
|
491
495
|
*/
|
|
492
496
|
type CaptionPart = Omit<CaptionPart$1, 'duration_ms'> & {
|
|
493
497
|
initial_duration_ms?: number | undefined;
|
|
@@ -893,7 +897,7 @@ type JsonObject = {
|
|
|
893
897
|
};
|
|
894
898
|
//#endregion
|
|
895
899
|
//#region ../medeo-dsl/src/relations.d.ts
|
|
896
|
-
type KnownRelationKind = 'timeline-track' | 'track-clip' | 'clip-marker' | 'marker-content' | 'axvideo-marker' | 'marker-timeline' | 'physical-asset' | 'generated' | 'phonetic-script-provenance' | 'caption-provenance' | 'caption-alignment' | 'clip-anchor' | 'audio-script-render';
|
|
900
|
+
type KnownRelationKind = 'timeline-track' | 'track-clip' | 'clip-marker' | 'marker-content' | 'axvideo-marker' | 'marker-timeline' | 'physical-asset' | 'generated' | 'phonetic-script-provenance' | 'caption-provenance' | 'caption-alignment' | 'clip-anchor' | 'audio-script-render' | 'audio-script-source' | 'audio-script-marker';
|
|
897
901
|
type RelationKind = KnownRelationKind | (string & {});
|
|
898
902
|
type RelationTrace = JsonObject;
|
|
899
903
|
/**
|
|
@@ -1606,7 +1610,7 @@ declare const partUnionSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
1606
1610
|
id: z.ZodOptional<z.ZodString>;
|
|
1607
1611
|
kind: z.ZodLiteral<"caption">;
|
|
1608
1612
|
initial_duration_ms: z.ZodNumber;
|
|
1609
|
-
speech_part_id: z.ZodString
|
|
1613
|
+
speech_part_id: z.ZodOptional<z.ZodString>;
|
|
1610
1614
|
text: z.ZodString;
|
|
1611
1615
|
start_ms: z.ZodNumber;
|
|
1612
1616
|
style: z.ZodOptional<z.ZodObject<{
|
|
@@ -1709,7 +1713,7 @@ declare const videoDocumentSchema: z.ZodObject<{
|
|
|
1709
1713
|
id: z.ZodOptional<z.ZodString>;
|
|
1710
1714
|
kind: z.ZodLiteral<"caption">;
|
|
1711
1715
|
initial_duration_ms: z.ZodNumber;
|
|
1712
|
-
speech_part_id: z.ZodString
|
|
1716
|
+
speech_part_id: z.ZodOptional<z.ZodString>;
|
|
1713
1717
|
text: z.ZodString;
|
|
1714
1718
|
start_ms: z.ZodNumber;
|
|
1715
1719
|
style: z.ZodOptional<z.ZodObject<{
|
|
@@ -1736,4 +1740,4 @@ declare const videoDocumentSchema: z.ZodObject<{
|
|
|
1736
1740
|
}, z.core.$loose>]>>>;
|
|
1737
1741
|
}, z.core.$loose>;
|
|
1738
1742
|
//#endregion
|
|
1739
|
-
export {
|
|
1743
|
+
export { VoiceDescriptor as $, generatePartId as A, Attachment as At, ResolvedEntityClipPlacement as B, PlannedSemanticOpKind as C, VideoDocumentSchemaVersion as Ct, SnapshotReadable as D, VideoDraftPartUnion as Dt, SchemaValidator as E, VideoDraft as Et, videoDocumentMirrorSchema as F, Timeline$1 as Ft, EntityTimelineProjectionError as G, ResolvedEntityTrack as H, VideoDocumentValidationError as I, Track$1 as It, JsonObject as J, EntityRelationRows as K, assertValidVideoDocument as L, TrackItem$1 as Lt, TrackItemDraft as M, CaptionStyle as Mt, VideoDocumentDraft as N, PartAggregation as Nt, ValidationError as O, effectiveVideoClipDurationMs as Ot, VideoDocumentMirrorSchema as P, SpeedShift as Pt, SequenceRange as Q, validateVideoDocument as R, ImplementedSemanticOpKind as S, VideoDocument as St, isImplementedSemanticOpKind as T, VideoDocumentValidationIssueCode as Tt, resolveEntityTimelineLayout as U, ResolvedEntityTimelineLayout as V, projectEntityTimeline as W, CaptionStyleFields as X, JsonValue as Y, SequenceDuration as Z, SemanticEditor as _, Track as _t, PlainMemoryAdapter as a, buildSpeechHostMap as at, TransactAudit as b, VIDEO_DOCUMENT_SCHEMA_VERSION as bt, MirrorVideoDocumentAdapter as c, toVideoDocument as ct, createMirrorVideoDocument as d, DEFAULT_UNIT_TIME_MS as dt, EntityId as et, createMirrorVideoDocumentAdapter as f, ENTITY_TIMELINE_PROJECTION_SCHEMA_VERSION as ft, SemanticDocumentAdapter as g, Timeline as gt, OpActor as h, SpeechPart as ht, JournalEntry as i, SpeechHostMap as it, TrackDraft as j, CaptionPart$1 as jt, PartIdFactory as k, speedOf as kt, MirrorVideoDocumentOptions as l, BgmPart as lt, CommitOptions as m, PartUnion as mt, videoDocumentSchema as n, buildInitialVideoDocument as nt, PlainMemoryAdapterOptions as o, derivePositionFromAbs as ot, writeVideoDocumentToDraft as p, PartKind as pt, EntityRow as q, readVideoDocumentFromDraft as r, DerivedItemPosition as rt, createPlainMemoryAdapter as s, fromVideoDocument as st, partUnionSchema as t, InitialDocumentFacts as tt, applyEntityTimelineProjection as u, CaptionPart as ut, SemanticOpInput as v, TrackItem as vt, SemanticOpKind as w, VideoDocumentValidationIssue as wt, IMPLEMENTED_SEMANTIC_OP_KINDS as x, VideoClipPart as xt, SemanticOpName as y, TrackItemTimePosition as yt, EntityTimelineTrackRole as z };
|