@mengine/medeo-tool 2.0.1-alpha.6 → 2.0.1-alpha.8

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.
@@ -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' | 'voice';
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
- voice: BoundedNativeSequencePayload & MediaAssetPayload;
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
- /** Stored own fields; a variant may obtain required content fields from its declared bases. */
119
- type StoredEntityPayload<K extends KnownEntityKind> = EntityPayloadByKind[K] | (K extends 'caption' | 'voice' ? JsonObject & Pick<MediaAssetPayload, 'external'> : never) | (JsonObject & Partial<EntityPayloadByKind[K]> & {
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-G4HxwNXl.d.mts.map
258
+ //# sourceMappingURL=entity-contract-Bpe6NRIQ.d.mts.map
@@ -1,7 +1,62 @@
1
1
  import { applyFieldChanges, assertCanonicalEditorResources, assertFieldChanges, ensureEditorFoundation, importMediaAsset, readDocumentAudioScript } from "@mengine/medeo-client";
2
2
  //#region ../medeo-dsl/src/entities.ts
3
+ /**
4
+ * The declared trait set of every known kind.
5
+ *
6
+ * `caption` carries `sequence` on purpose: a Caption owns the display timing of
7
+ * the one AudioScript segment it shows, which is what admits it into a Clip.
8
+ * `voice` carries no media or sequence trait — it is a timbre identity, not
9
+ * playable content; a rendered voiceover is an `audio` entity.
10
+ */
11
+ const ENTITY_TRAIT_KINDS = Object.freeze({
12
+ axvideo: Object.freeze([
13
+ "container",
14
+ "sequence",
15
+ "visual",
16
+ "audible"
17
+ ]),
18
+ timeline: Object.freeze(["container"]),
19
+ track: Object.freeze(["container", "sequence"]),
20
+ clip: Object.freeze(["container"]),
21
+ asset: Object.freeze([]),
22
+ video: Object.freeze([
23
+ "media-file",
24
+ "sequence",
25
+ "visual",
26
+ "audible"
27
+ ]),
28
+ audio: Object.freeze([
29
+ "media-file",
30
+ "sequence",
31
+ "audible"
32
+ ]),
33
+ image: Object.freeze([
34
+ "media-file",
35
+ "sequence",
36
+ "visual"
37
+ ]),
38
+ voice: Object.freeze(["container", "ip-bound"]),
39
+ "sequence-marker": Object.freeze([]),
40
+ viewport: Object.freeze([]),
41
+ "audio-script": Object.freeze(["segment-text"]),
42
+ "phonetic-script": Object.freeze(["segment-text"]),
43
+ caption: Object.freeze(["segment-text", "sequence"])
44
+ });
45
+ /** Declared traits of a known kind; extension kinds declare none through this table. */
46
+ function traitKindsOf(entityKind) {
47
+ return isKnownEntityKind(entityKind) ? ENTITY_TRAIT_KINDS[entityKind] : [];
48
+ }
49
+ function hasTraitKind(entityKind, trait) {
50
+ return traitKindsOf(entityKind).includes(trait);
51
+ }
52
+ /**
53
+ * Sequence admission asks the declared traits first, so a kind that does not
54
+ * declare `sequence` can never buy its way in by carrying the fields. Extension
55
+ * kinds declare no traits and stay structurally detected.
56
+ */
3
57
  function hasSequence(entity) {
4
- if (isKnownNonSequenceKind(entity.entityKind) || isReservedEntityKind(entity.entityKind)) return false;
58
+ if (isReservedEntityKind(entity.entityKind)) return false;
59
+ if (isKnownEntityKind(entity.entityKind) && !hasTraitKind(entity.entityKind, "sequence")) return false;
5
60
  return isSequenceFields(entity);
6
61
  }
7
62
  function isSequenceFields(value) {
@@ -14,22 +69,16 @@ function isSequenceFields(value) {
14
69
  if (value.sampling !== "native" && value.sampling !== "constant" && value.sampling !== "derived") return false;
15
70
  return "coordinateSpace" in value && value.coordinateSpace !== void 0;
16
71
  }
17
- function isKnownSequenceKind(kind) {
18
- return isMediaAssetVariantKind(kind) || kind === "caption" || kind === "axvideo";
19
- }
20
72
  function isMediaAssetVariantKind(kind) {
21
- return kind === "video" || kind === "image" || kind === "audio" || kind === "voice";
73
+ return kind === "video" || kind === "image" || kind === "audio";
22
74
  }
23
75
  function isKnownEntityKind(kind) {
24
- return isKnownSequenceKind(kind) || isKnownNonSequenceKind(kind);
76
+ return Object.hasOwn(ENTITY_TRAIT_KINDS, kind);
25
77
  }
26
78
  function isReservedEntityKind(kind) {
27
79
  const normalized = kind.toLowerCase().replaceAll("-", "").replaceAll("_", "");
28
80
  return normalized === "speech" || normalized === "videodocument";
29
81
  }
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
82
  function isRecord$1(value) {
34
83
  return typeof value === "object" && value != null && !Array.isArray(value);
35
84
  }
@@ -608,12 +657,15 @@ function validateKnownEntityPayload(entity) {
608
657
  break;
609
658
  case "video":
610
659
  case "audio":
611
- case "voice":
660
+ validateSequencePayload(value, "bounded", "native", problems);
661
+ validateMediaAssetFields(value, problems);
662
+ break;
612
663
  case "caption":
613
664
  validateSequencePayload(value, "bounded", "native", problems);
614
- if (entity.entityKind !== "caption") validateMediaAssetFields(value, problems);
615
- if (entity.entityKind === "voice") validateVoicePayload(value, problems);
616
- if (entity.entityKind === "caption") validateCaptionPayload(value, problems);
665
+ validateCaptionPayload(value, problems);
666
+ break;
667
+ case "voice":
668
+ validateVoicePayload(value, problems);
617
669
  break;
618
670
  case "image":
619
671
  validateSequencePayload(value, "unbounded", "constant", problems);
@@ -720,11 +772,26 @@ function validateExternalLocator(value, problems) {
720
772
  function validateStorageKey(value, problems) {
721
773
  if (value.storageKey !== void 0 && (typeof value.storageKey !== "string" || value.storageKey.trim() === "")) problems.push("storageKey must be a non-empty string when present");
722
774
  }
775
+ /**
776
+ * Voice is a timbre identity, not playable content: it owns the voice-library
777
+ * descriptor and nothing that would make it look like media or a Sequence. The
778
+ * rendered voiceover is an `audio` entity bound by a `voice-timbre` Relation.
779
+ */
723
780
  function validateVoicePayload(value, problems) {
781
+ for (const key of [
782
+ "extent",
783
+ "sampling",
784
+ "coordinateSpace",
785
+ "external",
786
+ "storageKey"
787
+ ]) if (Object.hasOwn(value, key)) problems.push(`${key} belongs to the rendered Audio, not to the Voice identity`);
724
788
  const voice = value.voice;
725
- if (voice === void 0) return;
789
+ if (voice === void 0) {
790
+ problems.push("voice is required; a Voice entity is its voice-library identity");
791
+ return;
792
+ }
726
793
  if (!isRecord(voice)) {
727
- problems.push("voice must be an object when present");
794
+ problems.push("voice must be an object");
728
795
  return;
729
796
  }
730
797
  if (voice.system !== "voice-library") problems.push("voice.system must be \"voice-library\"");
@@ -856,7 +923,6 @@ function isOwnedLocalEntityValuePath(entityKind, path) {
856
923
  if ([
857
924
  "video",
858
925
  "audio",
859
- "voice",
860
926
  "image",
861
927
  "caption",
862
928
  "axvideo"
@@ -920,7 +986,7 @@ function collectRelatedEntities(entityRefs, index) {
920
986
  };
921
987
  }
922
988
  function expectedSequenceShape(kind) {
923
- if (kind === "video" || kind === "audio" || kind === "voice" || kind === "caption") return {
989
+ if (kind === "video" || kind === "audio" || kind === "caption") return {
924
990
  extent: "bounded",
925
991
  sampling: "native"
926
992
  };
@@ -961,7 +1027,7 @@ const generatedRelationSpec = Object.freeze({
961
1027
  });
962
1028
  const captionAlignmentRelationSpec = Object.freeze({
963
1029
  kind: "caption-alignment",
964
- validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["caption"]), new Set(["audio", "voice"])),
1030
+ validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["caption"]), new Set(["audio"])),
965
1031
  validateMetadata: isCaptionAlignmentMetadata
966
1032
  });
967
1033
  /** `clip-anchor(child, host)` means endpoint 0 follows endpoint 1. */
@@ -970,20 +1036,29 @@ const clipAnchorRelationSpec = Object.freeze({
970
1036
  validateEndpoints: (endpoints) => endpoints[0].current().entityKind === "clip" && endpoints[1].current().entityKind === "clip",
971
1037
  validateMetadata: isEmptyMetadata
972
1038
  });
973
- /** Voice is generated from PhoneticScript; kinds determine roles regardless of endpoint positions. */
1039
+ /**
1040
+ * The rendered voiceover Audio and the PhoneticScript it was synthesized from;
1041
+ * kinds determine roles regardless of endpoint positions.
1042
+ */
974
1043
  const phoneticScriptRenderRelationSpec = Object.freeze({
975
1044
  kind: "phonetic-script-render",
976
- validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["voice"]), new Set(["phonetic-script"])),
1045
+ validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["audio"]), new Set(["phonetic-script"])),
1046
+ validateMetadata: isEmptyMetadata
1047
+ });
1048
+ /**
1049
+ * The timbre identity a rendered voiceover Audio was synthesized with. Voice
1050
+ * stays an identity: it never carries the audio itself, so the link between the
1051
+ * two is a Relation rather than one shared entity.
1052
+ */
1053
+ const voiceTimbreRelationSpec = Object.freeze({
1054
+ kind: "voice-timbre",
1055
+ validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["audio"]), new Set(["voice"])),
977
1056
  validateMetadata: isEmptyMetadata
978
1057
  });
979
1058
  /** AudioScript was transcribed from Audio, Video, or recorded Voice; kinds determine roles. */
980
1059
  const audioScriptSourceRelationSpec = Object.freeze({
981
1060
  kind: "audio-script-source",
982
- validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["audio-script"]), new Set([
983
- "audio",
984
- "video",
985
- "voice"
986
- ])),
1061
+ validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["audio-script"]), new Set(["audio", "video"])),
987
1062
  validateMetadata: isEmptyMetadata
988
1063
  });
989
1064
  /**
@@ -1010,6 +1085,7 @@ const builtInRelationSpecs = Object.freeze([
1010
1085
  captionAlignmentRelationSpec,
1011
1086
  clipAnchorRelationSpec,
1012
1087
  phoneticScriptRenderRelationSpec,
1088
+ voiceTimbreRelationSpec,
1013
1089
  audioScriptSourceRelationSpec,
1014
1090
  audioScriptMarkerRelationSpec
1015
1091
  ]);
@@ -1039,7 +1115,7 @@ function hasAssetAndSequence(endpoints) {
1039
1115
  return first.entityKind === "asset" && hasSequence(second) || second.entityKind === "asset" && hasSequence(first);
1040
1116
  }
1041
1117
  function isGeneratedMedia(entity) {
1042
- return entity.entityKind === "video" || entity.entityKind === "image" || entity.entityKind === "audio" || entity.entityKind === "voice";
1118
+ return entity.entityKind === "video" || entity.entityKind === "image" || entity.entityKind === "audio";
1043
1119
  }
1044
1120
  function isEmptyMetadata(value) {
1045
1121
  return isJsonObject(value) && Object.keys(value).length === 0;
@@ -1788,7 +1864,7 @@ var EntitySandbox = class {
1788
1864
  }
1789
1865
  for (const row of rows.relations) {
1790
1866
  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}`);
1867
+ if (row.relationKind !== "timeline-track" && row.relationKind !== "voice-timbre") throw new Error(`Unexpected resource Relation ${row.relationKind}`);
1792
1868
  this.link({
1793
1869
  relation_id: row.relationId,
1794
1870
  relation_kind: row.relationKind,
@@ -2095,4 +2171,4 @@ const numericMarkerComparators = { compareMarkerPoints: (_marker, _range, left,
2095
2171
  //#endregion
2096
2172
  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
2173
 
2098
- //# sourceMappingURL=entity-sandbox-DSFbfybl.mjs.map
2174
+ //# sourceMappingURL=entity-sandbox-CzaUmSsS.mjs.map