@mengine/medeo-tool 2.0.1-alpha.7 → 2.0.1-alpha.9
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/dist/{entity-contract-G4HxwNXl.d.mts → entity-contract-Bpe6NRIQ.d.mts} +23 -6
- package/dist/{entity-sandbox-DSFbfybl.mjs → entity-sandbox-CJeLcNcx.mjs} +132 -30
- package/dist/entity-sandbox-CJeLcNcx.mjs.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +30 -9
- package/dist/index.mjs.map +1 -1
- package/dist/sandbox-api.d.mts +22 -5
- package/dist/worker-entry.d.mts +1 -1
- package/dist/worker-entry.mjs +13 -15
- package/dist/worker-entry.mjs.map +1 -1
- package/package.json +2 -2
- package/dist/entity-sandbox-DSFbfybl.mjs.map +0 -1
|
@@ -8,8 +8,8 @@ interface JsonObject {
|
|
|
8
8
|
}
|
|
9
9
|
type KnownEntityKind = 'axvideo' | 'timeline' | 'track' | 'clip' | 'asset' | 'video' | 'audio' | 'voice' | 'image' | 'sequence-marker' | 'viewport' | 'audio-script' | 'phonetic-script' | 'caption';
|
|
10
10
|
/** Asset identity, either an old physical-only row or a directly composed media variant. */
|
|
11
|
-
type ResourceEntityKind = 'image' | 'video' | 'audio'
|
|
12
|
-
type KnownRelationKind = 'timeline-track' | 'track-clip' | 'clip-marker' | 'marker-content' | 'axvideo-marker' | 'marker-timeline' | 'physical-asset' | 'generated' | 'caption-alignment' | 'clip-anchor' | 'phonetic-script-render' | 'audio-script-source' | 'audio-script-marker';
|
|
11
|
+
type ResourceEntityKind = 'image' | 'video' | 'audio';
|
|
12
|
+
type KnownRelationKind = 'timeline-track' | 'track-clip' | 'clip-marker' | 'marker-content' | 'axvideo-marker' | 'marker-timeline' | 'physical-asset' | 'generated' | 'caption-alignment' | 'clip-anchor' | 'phonetic-script-render' | 'voice-timbre' | 'audio-script-source' | 'audio-script-marker';
|
|
13
13
|
type AuthorableRelationKind = KnownRelationKind;
|
|
14
14
|
interface BoundedNativeSequencePayload extends JsonObject {
|
|
15
15
|
/** Factual coordinates from recalled media metadata; never invent an end/duration. */
|
|
@@ -50,6 +50,14 @@ type MediaAssetPayload = JsonObject & {
|
|
|
50
50
|
};
|
|
51
51
|
storageKey?: string;
|
|
52
52
|
};
|
|
53
|
+
/** The voice-library timbre a synthesized Audio was rendered with. */
|
|
54
|
+
type VoiceIdentityPayload = JsonObject & {
|
|
55
|
+
voice: {
|
|
56
|
+
system: 'voice-library';
|
|
57
|
+
key: string;
|
|
58
|
+
name?: string;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
53
61
|
type CaptionTextSelection = JsonObject & {
|
|
54
62
|
segmentId: string; /** Half-open Unicode code-point range within the selected source segment. */
|
|
55
63
|
textRange?: {
|
|
@@ -68,8 +76,10 @@ interface EntityPayloadByKind {
|
|
|
68
76
|
/** Physical resource fields; never a copy of Caption content. */
|
|
69
77
|
asset: JsonObject;
|
|
70
78
|
video: BoundedNativeSequencePayload & MediaAssetPayload;
|
|
79
|
+
/** Every playable sound, including video original audio and synthesized voiceovers. */
|
|
71
80
|
audio: BoundedNativeSequencePayload & MediaAssetPayload;
|
|
72
|
-
|
|
81
|
+
/** A timbre identity, never playable content; the rendered take is an `audio` entity. */
|
|
82
|
+
voice: VoiceIdentityPayload;
|
|
73
83
|
image: UnboundedConstantSequencePayload & MediaAssetPayload;
|
|
74
84
|
'sequence-marker': JsonObject & {
|
|
75
85
|
sourceRange: {
|
|
@@ -115,8 +125,15 @@ interface EntityPayloadByKind {
|
|
|
115
125
|
}[];
|
|
116
126
|
};
|
|
117
127
|
}
|
|
118
|
-
/**
|
|
119
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Stored own fields; a variant may obtain required content fields from its
|
|
130
|
+
* declared bases.
|
|
131
|
+
*
|
|
132
|
+
* Caption and Audio may also be declared resource-only: the ASR transcript and
|
|
133
|
+
* the synthesized voiceover are attached by the host, which then fills in the
|
|
134
|
+
* remaining factual fields.
|
|
135
|
+
*/
|
|
136
|
+
type StoredEntityPayload<K extends KnownEntityKind> = EntityPayloadByKind[K] | (K extends 'caption' | 'audio' ? JsonObject & Pick<MediaAssetPayload, 'external'> : never) | (JsonObject & Partial<EntityPayloadByKind[K]> & {
|
|
120
137
|
baseEntityIds: string[];
|
|
121
138
|
});
|
|
122
139
|
interface SandboxEntity<K extends KnownEntityKind = KnownEntityKind> {
|
|
@@ -238,4 +255,4 @@ interface BusinessRelationFacade {
|
|
|
238
255
|
}
|
|
239
256
|
//#endregion
|
|
240
257
|
export { UpdateEntityInput as C, UnlinkRelationInput as S, LinkRelationInput as _, DeleteEntityInput as a, SandboxEntity as b, EntitySandboxCheckpoint as c, JsonObject as d, JsonPrimitive as f, LinkGeneratedRelationInput as g, KnownRelationKind as h, CreateEntityInput as i, EntityStoreSnapshot as l, KnownEntityKind as m, BusinessEntityFacade as n, EntityCommand as o, JsonValue as p, BusinessRelationFacade as r, EntityPlanState as s, AuthorableRelationKind as t, EntityUpdateInput as u, RelationUpdateInput as v, SandboxRelation as x, ResourceEntityKind as y };
|
|
241
|
-
//# sourceMappingURL=entity-contract-
|
|
258
|
+
//# sourceMappingURL=entity-contract-Bpe6NRIQ.d.mts.map
|
|
@@ -1,7 +1,84 @@
|
|
|
1
1
|
import { applyFieldChanges, assertCanonicalEditorResources, assertFieldChanges, ensureEditorFoundation, importMediaAsset, readDocumentAudioScript } from "@mengine/medeo-client";
|
|
2
2
|
//#region ../medeo-dsl/src/entities.ts
|
|
3
|
+
/**
|
|
4
|
+
* What every known entity kind declares.
|
|
5
|
+
*
|
|
6
|
+
* `kinds` is a multi-declaration: every entry holds at the same time, the first
|
|
7
|
+
* naming the object and the rest the properties it shares with others. The
|
|
8
|
+
* order carries no priority, endpoint role, or edit sequence — an operation
|
|
9
|
+
* asks whether the entity declares the kind it needs, never what its "main
|
|
10
|
+
* type" is.
|
|
11
|
+
*
|
|
12
|
+
* `Caption` declares `Sequence` on purpose: it owns the display timing of the
|
|
13
|
+
* one AudioScript segment it shows, which is what admits it into a Clip.
|
|
14
|
+
* `Voice` declares neither `MediaFile` nor `Sequence` — it is a timbre
|
|
15
|
+
* identity, not playable content; a rendered voiceover is an `audio` entity.
|
|
16
|
+
*/
|
|
17
|
+
const ENTITY_KINDS = Object.freeze({
|
|
18
|
+
axvideo: Object.freeze([
|
|
19
|
+
"AXVideo",
|
|
20
|
+
"Container",
|
|
21
|
+
"Sequence",
|
|
22
|
+
"Visual",
|
|
23
|
+
"Audible"
|
|
24
|
+
]),
|
|
25
|
+
timeline: Object.freeze(["Timeline", "Container"]),
|
|
26
|
+
track: Object.freeze([
|
|
27
|
+
"Track",
|
|
28
|
+
"Container",
|
|
29
|
+
"Sequence"
|
|
30
|
+
]),
|
|
31
|
+
clip: Object.freeze(["Clip", "Container"]),
|
|
32
|
+
asset: Object.freeze(["Asset"]),
|
|
33
|
+
video: Object.freeze([
|
|
34
|
+
"Video",
|
|
35
|
+
"MediaFile",
|
|
36
|
+
"Sequence",
|
|
37
|
+
"Visual",
|
|
38
|
+
"Audible"
|
|
39
|
+
]),
|
|
40
|
+
audio: Object.freeze([
|
|
41
|
+
"Audio",
|
|
42
|
+
"MediaFile",
|
|
43
|
+
"Sequence",
|
|
44
|
+
"Audible"
|
|
45
|
+
]),
|
|
46
|
+
image: Object.freeze([
|
|
47
|
+
"Image",
|
|
48
|
+
"MediaFile",
|
|
49
|
+
"Sequence",
|
|
50
|
+
"Visual"
|
|
51
|
+
]),
|
|
52
|
+
voice: Object.freeze([
|
|
53
|
+
"Voice",
|
|
54
|
+
"Container",
|
|
55
|
+
"IPBound"
|
|
56
|
+
]),
|
|
57
|
+
"sequence-marker": Object.freeze(["SequenceMarker"]),
|
|
58
|
+
viewport: Object.freeze(["Viewport"]),
|
|
59
|
+
"audio-script": Object.freeze(["AudioScript", "SegmentText"]),
|
|
60
|
+
"phonetic-script": Object.freeze(["PhoneticScript", "SegmentText"]),
|
|
61
|
+
caption: Object.freeze([
|
|
62
|
+
"Caption",
|
|
63
|
+
"SegmentText",
|
|
64
|
+
"Sequence"
|
|
65
|
+
])
|
|
66
|
+
});
|
|
67
|
+
/** What a known kind declares; extension kinds declare nothing through this table. */
|
|
68
|
+
function kindsOf(entityKind) {
|
|
69
|
+
return isKnownEntityKind(entityKind) ? ENTITY_KINDS[entityKind] : [];
|
|
70
|
+
}
|
|
71
|
+
function declaresKind(entityKind, kind) {
|
|
72
|
+
return kindsOf(entityKind).includes(kind);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Sequence admission asks `kinds` first, so an entity that does not declare
|
|
76
|
+
* `Sequence` can never buy its way in by carrying the fields. Extension kinds
|
|
77
|
+
* declare nothing and stay structurally detected.
|
|
78
|
+
*/
|
|
3
79
|
function hasSequence(entity) {
|
|
4
|
-
if (
|
|
80
|
+
if (isReservedEntityKind(entity.entityKind)) return false;
|
|
81
|
+
if (isKnownEntityKind(entity.entityKind) && !declaresKind(entity.entityKind, "Sequence")) return false;
|
|
5
82
|
return isSequenceFields(entity);
|
|
6
83
|
}
|
|
7
84
|
function isSequenceFields(value) {
|
|
@@ -14,22 +91,16 @@ function isSequenceFields(value) {
|
|
|
14
91
|
if (value.sampling !== "native" && value.sampling !== "constant" && value.sampling !== "derived") return false;
|
|
15
92
|
return "coordinateSpace" in value && value.coordinateSpace !== void 0;
|
|
16
93
|
}
|
|
17
|
-
function isKnownSequenceKind(kind) {
|
|
18
|
-
return isMediaAssetVariantKind(kind) || kind === "caption" || kind === "axvideo";
|
|
19
|
-
}
|
|
20
94
|
function isMediaAssetVariantKind(kind) {
|
|
21
|
-
return kind === "video" || kind === "image" || kind === "audio"
|
|
95
|
+
return kind === "video" || kind === "image" || kind === "audio";
|
|
22
96
|
}
|
|
23
97
|
function isKnownEntityKind(kind) {
|
|
24
|
-
return
|
|
98
|
+
return Object.hasOwn(ENTITY_KINDS, kind);
|
|
25
99
|
}
|
|
26
100
|
function isReservedEntityKind(kind) {
|
|
27
101
|
const normalized = kind.toLowerCase().replaceAll("-", "").replaceAll("_", "");
|
|
28
102
|
return normalized === "speech" || normalized === "videodocument";
|
|
29
103
|
}
|
|
30
|
-
function isKnownNonSequenceKind(kind) {
|
|
31
|
-
return kind === "timeline" || kind === "track" || kind === "clip" || kind === "asset" || kind === "sequence-marker" || kind === "viewport" || kind === "audio-script" || kind === "phonetic-script";
|
|
32
|
-
}
|
|
33
104
|
function isRecord$1(value) {
|
|
34
105
|
return typeof value === "object" && value != null && !Array.isArray(value);
|
|
35
106
|
}
|
|
@@ -209,14 +280,17 @@ function assembleCaptionContent(rows, captionEntityId) {
|
|
|
209
280
|
requireEntityKind(rows, captionEntityId, "caption");
|
|
210
281
|
const caption = assembleEntityContent(rows, captionEntityId);
|
|
211
282
|
const script = findComposedAudioScript(rows, captionEntityId, "caption");
|
|
212
|
-
const
|
|
283
|
+
const selection = captionSelection(caption);
|
|
284
|
+
const composed = {
|
|
213
285
|
...script,
|
|
214
286
|
payload: caption.payload
|
|
215
|
-
}
|
|
287
|
+
};
|
|
288
|
+
const segment = selectAudioScriptSegment(composed, selection);
|
|
216
289
|
return {
|
|
217
290
|
caption,
|
|
218
291
|
audioScript: script,
|
|
219
292
|
segments: [segment],
|
|
293
|
+
segmentIndex: scriptSegments(composed).findIndex((entry) => entry.segmentId === selection.segmentId),
|
|
220
294
|
text: segment.text
|
|
221
295
|
};
|
|
222
296
|
}
|
|
@@ -608,12 +682,15 @@ function validateKnownEntityPayload(entity) {
|
|
|
608
682
|
break;
|
|
609
683
|
case "video":
|
|
610
684
|
case "audio":
|
|
611
|
-
|
|
685
|
+
validateSequencePayload(value, "bounded", "native", problems);
|
|
686
|
+
validateMediaAssetFields(value, problems);
|
|
687
|
+
break;
|
|
612
688
|
case "caption":
|
|
613
689
|
validateSequencePayload(value, "bounded", "native", problems);
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
690
|
+
validateCaptionPayload(value, problems);
|
|
691
|
+
break;
|
|
692
|
+
case "voice":
|
|
693
|
+
validateVoicePayload(value, problems);
|
|
617
694
|
break;
|
|
618
695
|
case "image":
|
|
619
696
|
validateSequencePayload(value, "unbounded", "constant", problems);
|
|
@@ -720,11 +797,26 @@ function validateExternalLocator(value, problems) {
|
|
|
720
797
|
function validateStorageKey(value, problems) {
|
|
721
798
|
if (value.storageKey !== void 0 && (typeof value.storageKey !== "string" || value.storageKey.trim() === "")) problems.push("storageKey must be a non-empty string when present");
|
|
722
799
|
}
|
|
800
|
+
/**
|
|
801
|
+
* Voice is a timbre identity, not playable content: it owns the voice-library
|
|
802
|
+
* descriptor and nothing that would make it look like media or a Sequence. The
|
|
803
|
+
* rendered voiceover is an `audio` entity bound by a `voice-timbre` Relation.
|
|
804
|
+
*/
|
|
723
805
|
function validateVoicePayload(value, problems) {
|
|
806
|
+
for (const key of [
|
|
807
|
+
"extent",
|
|
808
|
+
"sampling",
|
|
809
|
+
"coordinateSpace",
|
|
810
|
+
"external",
|
|
811
|
+
"storageKey"
|
|
812
|
+
]) if (Object.hasOwn(value, key)) problems.push(`${key} belongs to the rendered Audio, not to the Voice identity`);
|
|
724
813
|
const voice = value.voice;
|
|
725
|
-
if (voice === void 0)
|
|
814
|
+
if (voice === void 0) {
|
|
815
|
+
problems.push("voice is required; a Voice entity is its voice-library identity");
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
726
818
|
if (!isRecord(voice)) {
|
|
727
|
-
problems.push("voice must be an object
|
|
819
|
+
problems.push("voice must be an object");
|
|
728
820
|
return;
|
|
729
821
|
}
|
|
730
822
|
if (voice.system !== "voice-library") problems.push("voice.system must be \"voice-library\"");
|
|
@@ -856,7 +948,6 @@ function isOwnedLocalEntityValuePath(entityKind, path) {
|
|
|
856
948
|
if ([
|
|
857
949
|
"video",
|
|
858
950
|
"audio",
|
|
859
|
-
"voice",
|
|
860
951
|
"image",
|
|
861
952
|
"caption",
|
|
862
953
|
"axvideo"
|
|
@@ -920,7 +1011,7 @@ function collectRelatedEntities(entityRefs, index) {
|
|
|
920
1011
|
};
|
|
921
1012
|
}
|
|
922
1013
|
function expectedSequenceShape(kind) {
|
|
923
|
-
if (kind === "video" || kind === "audio" || kind === "
|
|
1014
|
+
if (kind === "video" || kind === "audio" || kind === "caption") return {
|
|
924
1015
|
extent: "bounded",
|
|
925
1016
|
sampling: "native"
|
|
926
1017
|
};
|
|
@@ -961,7 +1052,7 @@ const generatedRelationSpec = Object.freeze({
|
|
|
961
1052
|
});
|
|
962
1053
|
const captionAlignmentRelationSpec = Object.freeze({
|
|
963
1054
|
kind: "caption-alignment",
|
|
964
|
-
validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["caption"]), new Set(["audio"
|
|
1055
|
+
validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["caption"]), new Set(["audio"])),
|
|
965
1056
|
validateMetadata: isCaptionAlignmentMetadata
|
|
966
1057
|
});
|
|
967
1058
|
/** `clip-anchor(child, host)` means endpoint 0 follows endpoint 1. */
|
|
@@ -970,20 +1061,29 @@ const clipAnchorRelationSpec = Object.freeze({
|
|
|
970
1061
|
validateEndpoints: (endpoints) => endpoints[0].current().entityKind === "clip" && endpoints[1].current().entityKind === "clip",
|
|
971
1062
|
validateMetadata: isEmptyMetadata
|
|
972
1063
|
});
|
|
973
|
-
/**
|
|
1064
|
+
/**
|
|
1065
|
+
* The rendered voiceover Audio and the PhoneticScript it was synthesized from;
|
|
1066
|
+
* kinds determine roles regardless of endpoint positions.
|
|
1067
|
+
*/
|
|
974
1068
|
const phoneticScriptRenderRelationSpec = Object.freeze({
|
|
975
1069
|
kind: "phonetic-script-render",
|
|
976
|
-
validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["
|
|
1070
|
+
validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["audio"]), new Set(["phonetic-script"])),
|
|
1071
|
+
validateMetadata: isEmptyMetadata
|
|
1072
|
+
});
|
|
1073
|
+
/**
|
|
1074
|
+
* The timbre identity a rendered voiceover Audio was synthesized with. Voice
|
|
1075
|
+
* stays an identity: it never carries the audio itself, so the link between the
|
|
1076
|
+
* two is a Relation rather than one shared entity.
|
|
1077
|
+
*/
|
|
1078
|
+
const voiceTimbreRelationSpec = Object.freeze({
|
|
1079
|
+
kind: "voice-timbre",
|
|
1080
|
+
validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["audio"]), new Set(["voice"])),
|
|
977
1081
|
validateMetadata: isEmptyMetadata
|
|
978
1082
|
});
|
|
979
1083
|
/** AudioScript was transcribed from Audio, Video, or recorded Voice; kinds determine roles. */
|
|
980
1084
|
const audioScriptSourceRelationSpec = Object.freeze({
|
|
981
1085
|
kind: "audio-script-source",
|
|
982
|
-
validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["audio-script"]), new Set([
|
|
983
|
-
"audio",
|
|
984
|
-
"video",
|
|
985
|
-
"voice"
|
|
986
|
-
])),
|
|
1086
|
+
validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["audio-script"]), new Set(["audio", "video"])),
|
|
987
1087
|
validateMetadata: isEmptyMetadata
|
|
988
1088
|
});
|
|
989
1089
|
/**
|
|
@@ -1010,6 +1110,7 @@ const builtInRelationSpecs = Object.freeze([
|
|
|
1010
1110
|
captionAlignmentRelationSpec,
|
|
1011
1111
|
clipAnchorRelationSpec,
|
|
1012
1112
|
phoneticScriptRenderRelationSpec,
|
|
1113
|
+
voiceTimbreRelationSpec,
|
|
1013
1114
|
audioScriptSourceRelationSpec,
|
|
1014
1115
|
audioScriptMarkerRelationSpec
|
|
1015
1116
|
]);
|
|
@@ -1039,7 +1140,7 @@ function hasAssetAndSequence(endpoints) {
|
|
|
1039
1140
|
return first.entityKind === "asset" && hasSequence(second) || second.entityKind === "asset" && hasSequence(first);
|
|
1040
1141
|
}
|
|
1041
1142
|
function isGeneratedMedia(entity) {
|
|
1042
|
-
return entity.entityKind === "video" || entity.entityKind === "image" || entity.entityKind === "audio"
|
|
1143
|
+
return entity.entityKind === "video" || entity.entityKind === "image" || entity.entityKind === "audio";
|
|
1043
1144
|
}
|
|
1044
1145
|
function isEmptyMetadata(value) {
|
|
1045
1146
|
return isJsonObject(value) && Object.keys(value).length === 0;
|
|
@@ -1599,6 +1700,7 @@ var EntitySandbox = class {
|
|
|
1599
1700
|
const assembled = assembleCaptionContent(toDslRows(this.state), createEntityId(entityId));
|
|
1600
1701
|
return clone({
|
|
1601
1702
|
audio_script_entity_id: assembled.audioScript.entityId,
|
|
1703
|
+
segment_index: assembled.segmentIndex,
|
|
1602
1704
|
text: assembled.text,
|
|
1603
1705
|
segments: assembled.segments.map((segment) => ({ ...segment }))
|
|
1604
1706
|
});
|
|
@@ -1788,7 +1890,7 @@ var EntitySandbox = class {
|
|
|
1788
1890
|
}
|
|
1789
1891
|
for (const row of rows.relations) {
|
|
1790
1892
|
if (this.state.relations.some((relation) => relation.relation_id === row.relationId)) continue;
|
|
1791
|
-
if (row.relationKind !== "timeline-track") throw new Error(`Unexpected resource Relation ${row.relationKind}`);
|
|
1893
|
+
if (row.relationKind !== "timeline-track" && row.relationKind !== "voice-timbre") throw new Error(`Unexpected resource Relation ${row.relationKind}`);
|
|
1792
1894
|
this.link({
|
|
1793
1895
|
relation_id: row.relationId,
|
|
1794
1896
|
relation_kind: row.relationKind,
|
|
@@ -2095,4 +2197,4 @@ const numericMarkerComparators = { compareMarkerPoints: (_marker, _range, left,
|
|
|
2095
2197
|
//#endregion
|
|
2096
2198
|
export { isJsonObject as a, isMediaAssetVariantKind as c, businessState as i, toDslRows as n, createEntityId as o, businessFacades as r, createRelationId as s, EntitySandbox as t };
|
|
2097
2199
|
|
|
2098
|
-
//# sourceMappingURL=entity-sandbox-
|
|
2200
|
+
//# sourceMappingURL=entity-sandbox-CJeLcNcx.mjs.map
|