@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/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { c as isMediaAssetVariantKind, i as businessState, o as createEntityId, s as createRelationId, t as EntitySandbox } from "./entity-sandbox-DSFbfybl.mjs";
1
+ import { c as isMediaAssetVariantKind, i as businessState, o as createEntityId, s as createRelationId, t as EntitySandbox } from "./entity-sandbox-CJeLcNcx.mjs";
2
2
  import { ManualSyncDoc, MengineHttpClient, ValidationError, base64ToBytes, bytesToBase64, compileEntityRows, createPlainMemoryAdapter, decodeDocVersionMark, effectiveVideoClipDurationMs, encodeDocVersionMark, replayJournal, solveVideoDocument, speedOf } from "@mengine/medeo-client";
3
3
  import { Worker } from "node:worker_threads";
4
4
  import { createHash, randomUUID } from "node:crypto";
@@ -442,6 +442,7 @@ const KNOWN_RELATION_KINDS = [
442
442
  "caption-alignment",
443
443
  "clip-anchor",
444
444
  "phonetic-script-render",
445
+ "voice-timbre",
445
446
  "audio-script-source",
446
447
  "audio-script-marker"
447
448
  ];
@@ -821,8 +822,10 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
821
822
  " };",
822
823
  " clip: JsonObject;",
823
824
  " video: BoundedNativeSequencePayload & MediaAssetPayload;",
825
+ " /** Every playable sound, including video original audio and synthesized voiceovers. */",
824
826
  " audio: BoundedNativeSequencePayload & MediaAssetPayload;",
825
- " voice: BoundedNativeSequencePayload & MediaAssetPayload;",
827
+ " /** A timbre identity, never playable content; the rendered take is an `audio` entity. */",
828
+ " voice: VoiceIdentityPayload;",
826
829
  " image: UnboundedConstantSequencePayload & MediaAssetPayload;",
827
830
  " 'sequence-marker': JsonObject & {",
828
831
  " sourceRange: {",
@@ -950,6 +953,7 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
950
953
  " | 'caption-alignment'",
951
954
  " | 'clip-anchor'",
952
955
  " | 'phonetic-script-render'",
956
+ " | 'voice-timbre'",
953
957
  " | 'audio-script-source'",
954
958
  " | 'audio-script-marker';",
955
959
  "interface LinkRelationBase {",
@@ -990,10 +994,17 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
990
994
  " text: string;",
991
995
  " language?: string;",
992
996
  "};",
993
- "/** Stored own fields; a variant may obtain required content fields from its declared bases. */",
997
+ "/**",
998
+ " * Stored own fields; a variant may obtain required content fields from its",
999
+ " * declared bases.",
1000
+ " *",
1001
+ " * Caption and Audio may also be declared resource-only: the ASR transcript and",
1002
+ " * the synthesized voiceover are attached by the host, which then fills in the",
1003
+ " * remaining factual fields.",
1004
+ " */",
994
1005
  "export type StoredEntityPayload<K extends KnownEntityKind> =",
995
1006
  " | EntityPayloadByKind[K]",
996
- " | (K extends 'caption' | 'voice' ? JsonObject & Pick<MediaAssetPayload, 'external'> : never)",
1007
+ " | (K extends 'caption' | 'audio' ? JsonObject & Pick<MediaAssetPayload, 'external'> : never)",
997
1008
  " | (JsonObject &",
998
1009
  " Partial<EntityPayloadByKind[K]> & {",
999
1010
  " baseEntityIds: string[];",
@@ -1014,6 +1025,14 @@ const ENTITY_EDIT_SANDBOX_API_DTS = [
1014
1025
  " entity_id: string;",
1015
1026
  " payload: JsonObject;",
1016
1027
  "}",
1028
+ "/** The voice-library timbre a synthesized Audio was rendered with. */",
1029
+ "export type VoiceIdentityPayload = JsonObject & {",
1030
+ " voice: {",
1031
+ " system: 'voice-library';",
1032
+ " key: string;",
1033
+ " name?: string;",
1034
+ " };",
1035
+ "};",
1017
1036
  "export declare const entities: BusinessEntityFacade;",
1018
1037
  "export declare const relations: BusinessRelationFacade;",
1019
1038
  "/** Resolve this Entity's attached resource through the host; await before using an uninitialized Caption or Voice. */",
@@ -1054,13 +1073,13 @@ Variants directly hold baseEntityIds. Assembly validates every base before apply
1054
1073
 
1055
1074
  Generation tools return resource references. An asset id may be passed in an entity's external:{system:'memota',key:assetId}; it is never an entity id, baseEntityId or relation endpoint. Physical resources are host infrastructure. The sandbox has no direct resource lookup/CRUD by asset id. rgetAssetFromEntity takes only an existing business entity id and returns {assetId,content} for its attached resource. For output_caption, create a Caption with payload:{external:{system:'memota',key:assetId}}, then await rgetAssetFromEntity(captionId). The host initializes the complete AudioScript and one Caption per resource segment in the same causal plan when selection is omitted. The requested Caption represents the first segment; additional Caption entities share that same script and external reference. After awaiting, discover ALL resource Captions with entities.list filtered by entity_kind and payload.external.key, inspect each singular selection/extent, and place each in its own Clip/SequenceMarker. An explicit selection initializes only that selected Caption. Never place the first Caption over the whole transcript; Without explicit bases, the host creates an independent AudioScript for that resource. With bases, it resolves their unique AudioScript text owner and preserves other nonconflicting bases. It never routes text through the panel selection. Read the editable fields with entities.get afterwards. Repeated reads preserve edited text and return the original immutable resource content. Unawaited initialization is completed before validation; failure prevents publication. Resource-free Caption composition from AudioScript is also valid.
1056
1075
 
1057
- Project initialization creates one current Timeline, the four standard Tracks and one attached AudioScript with segments:[]. A project may contain multiple independent complete AudioScripts (for example a generated script and an uploaded video transcript). The project AudioScript attachment selects only the panel view; it is not a singleton or membership constraint. Locate scripts with entities.list and choose the appropriate source. AudioScript owns independent multi-segment text; each can have multiple Caption variants. Caption and PhoneticScript compose it through baseEntityIds. Caption owns exactly one selection object ({segmentId,textRange?}), never an array or the legacy selections field. It selects one script segment or a contiguous substring, and owns only that selection's intrinsic timing and style; segmentId is a stable script-local identity, not an entity reference or transient array index; selection textRange is a half-open code-point range and can split display text without rewriting the script. PhoneticScript owns pronunciation/prosody; Voice is generated from it and related by phonetic-script-render(voice,phoneticScript). Speech and caption occupy separate Clips. Subtitles normally accompany sound; use source media or generate Voice from PhoneticScript when the request requires narration. Do not fabricate a sound resource.
1076
+ Project initialization creates one current Timeline, the four standard Tracks and one attached AudioScript with segments:[]. A project may contain multiple independent complete AudioScripts (for example a generated script and an uploaded video transcript). The project AudioScript attachment selects only the panel view; it is not a singleton or membership constraint. Locate scripts with entities.list and choose the appropriate source. AudioScript owns independent multi-segment text; each can have multiple Caption variants. Caption and PhoneticScript compose it through baseEntityIds. Caption locates its text as AudioScript plus index: it owns exactly one selection object ({segmentId,textRange?}), never an array or the legacy selections field. segmentId is that segment's stable identity rather than its ordinal, so a concurrent insert or re-segmentation cannot slide a Caption onto other text. It selects one script segment or a contiguous substring, and owns only that selection's intrinsic timing and style; selection textRange is a half-open code-point range and can split display text without rewriting the script. PhoneticScript owns pronunciation/prosody; the voiceover is an Audio rendered from it and related by phonetic-script-render(audio,phoneticScript). Voice is only a timbre identity ({voice:{system:'voice-library',key}}) and is never playable; link the rendered Audio to it with voice-timbre(audio,voice). Speech and caption occupy separate Clips. Caption carries Sequence: it owns its display timing and can be Clip content directly. Subtitles normally accompany sound; use source media or render a voiceover Audio from PhoneticScript when the request requires narration. Do not fabricate a sound resource.
1058
1077
 
1059
- For Speech resources, create a Voice with external:{system:'memota-speech',key:speechId}, then await rgetAssetFromEntity(voiceId) before linking its placement. The host supplies full extent and optional voice-library identity, and assembles physical storage facts internally. Read the visible fields through entities.get; never invent them or use a Speech ID as a Memota media ID. TTS Voice still requires its real PhoneticScript relation; resource loading does not invent script content.
1078
+ For Speech resources, create an Audio with external:{system:'memota-speech',key:speechId}, then await rgetAssetFromEntity(audioId) before linking its placement. The host supplies full extent and assembles physical storage facts internally; the voice-library identity stays on its own Voice entity. Read the visible fields through entities.get; never invent them or use a Speech ID as a Memota media ID. A TTS voiceover still requires its real PhoneticScript relation; resource loading does not invent script content.
1060
1079
 
1061
- Direct AudioScript creation saves its segments as a resource before publication. Its assembled segments remain readable and editable; committed text changes preserve native Loro operations and save a new immutable resource, automatically advancing the external reference. AudioScripts initialized through Caption resources keep inline segments without an additional script resource. Do not manage these resources yourself. AudioScript has no intrinsic time and cannot be Clip content. ASR input Audio/Video/Voice associates via audio-script-source(script,source). Its external annotation SequenceMarker associates via audio-script-marker(script,marker) and receives assigned segmentRanges, not references to another marker. Caption intrinsic timing and its Clip's display SequenceMarker are distinct. Move/stretch display by editing only the placement marker.
1080
+ Direct AudioScript creation saves its segments as a resource before publication. Its assembled segments remain readable and editable; committed text changes preserve native Loro operations and save a new immutable resource, automatically advancing the external reference. AudioScripts initialized through Caption resources keep inline segments without an additional script resource. Do not manage these resources yourself. AudioScript has no intrinsic time and cannot be Clip content. ASR input Audio/Video associates via audio-script-source(script,source). Its external annotation SequenceMarker associates via audio-script-marker(script,marker) and receives assigned segmentRanges, not references to another marker. Caption intrinsic timing and its Clip's display SequenceMarker are distinct. Move/stretch display by editing only the placement marker.
1062
1081
 
1063
- To place content, create a Clip and a SequenceMarker, then link track-clip(track,clip), clip-marker(clip,marker) and marker-content(marker,content). Existing track membership uses timeline-track(timeline,track). The editor has one track of each role: video_clip for Image/Video, speech for Voice, caption for Caption, bgm for Audio. Inspect factual media extents; never invent duration. Each placement is exactly one of Clip.order (sequential), Marker.targetRange (absolute) or clip-anchor(childClip,hostClip) plus Marker.anchorOffset. Marker.sourceRange selects source, duration describes playback/display. Whole milliseconds are required by this editor projection. Clip.volume is decibels (-60..20, 0 original). Image is unbounded/constant. Linear timeRemapping is {kind:'linear',rate:2,mode:'constant'}, with duration matching the rounded source span/rate. BGM can use durationPolicy:'timeline'. Nonlinear speed and multiple overlay tracks are unsupported. Caption visibility is the caption Track's hidden field. Caption style is its style map. Handle structural links explicitly when moving/deleting; there is no implicit cascade.
1082
+ To place content, create a Clip and a SequenceMarker, then link track-clip(track,clip), clip-marker(clip,marker) and marker-content(marker,content). Existing track membership uses timeline-track(timeline,track). The editor has one track of each role: video_clip for Image/Video, speech for the voiceover Audio, caption for Caption, bgm for Audio. Inspect factual media extents; never invent duration. Each placement is exactly one of Clip.order (sequential), Marker.targetRange (absolute) or clip-anchor(childClip,hostClip) plus Marker.anchorOffset. Marker.sourceRange selects source, duration describes playback/display. Whole milliseconds are required by this editor projection. Clip.volume is decibels (-60..20, 0 original). Image is unbounded/constant. Linear timeRemapping is {kind:'linear',rate:2,mode:'constant'}, with duration matching the rounded source span/rate. BGM can use durationPolicy:'timeline'. Nonlinear speed and multiple overlay tracks are unsupported. Caption visibility is the caption Track's hidden field. Caption style is its style map. Handle structural links explicitly when moving/deleting; there is no implicit cascade.
1064
1083
 
1065
1084
  checkpoint returns an opaque token valid only in this execution. rollbackTo restores that point and invalidates later checkpoints. Returned read snapshots and inputs are not writable document authorities. Use only the declarations below.
1066
1085
  `.trim();
@@ -1781,13 +1800,15 @@ async function materializeResources(options, resources) {
1781
1800
  const client = new EntityHttpClient(options);
1782
1801
  const state = await client.fetchState();
1783
1802
  let resourceKey = "";
1803
+ let mintOrdinal = 0;
1784
1804
  const sandbox = new EntitySandbox({
1785
1805
  state,
1786
- idFactory: (prefix) => stableId(prefix, options.docId, resourceKey)
1806
+ idFactory: (prefix) => stableId(prefix, options.docId, resourceKey, String(mintOrdinal++))
1787
1807
  });
1788
1808
  const ids = [];
1789
1809
  for (const resource of resources) {
1790
1810
  resourceKey = `${resource.kind}:${resource.assetId}`;
1811
+ mintOrdinal = 0;
1791
1812
  if (resource.kind !== "caption") {
1792
1813
  ids.push(sandbox.entities.ensureMedia(resource).contentEntityId);
1793
1814
  continue;