@mengine/medeo-client 1.2.1-alpha.1 → 1.2.1-alpha.2
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.
|
@@ -322,6 +322,71 @@ function syncAggregatedClipsTimePosition(doc) {
|
|
|
322
322
|
}
|
|
323
323
|
}
|
|
324
324
|
/**
|
|
325
|
+
* Resolve authoritative `time_position` edges without inventing parents from
|
|
326
|
+
* geometric overlap. Roots stay absolute/sequential; every anchored descendant
|
|
327
|
+
* follows its declared parent, regardless of whether that parent is video, BGM,
|
|
328
|
+
* or speech.
|
|
329
|
+
*/
|
|
330
|
+
function resolveAnchoredClipsTimePosition(doc) {
|
|
331
|
+
const items = [
|
|
332
|
+
...doc.main_track,
|
|
333
|
+
...doc.bgm_track,
|
|
334
|
+
...doc.speech_track,
|
|
335
|
+
...doc.caption_track
|
|
336
|
+
];
|
|
337
|
+
const byPart = indexByPart(items);
|
|
338
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
339
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
340
|
+
const resolve = (item) => {
|
|
341
|
+
if (resolved.has(item.part_id)) return item.abs_time_position;
|
|
342
|
+
if (visiting.has(item.part_id)) throw new Error(`anchored position cycle at ${item.part_id}`);
|
|
343
|
+
visiting.add(item.part_id);
|
|
344
|
+
const position = item.time_position;
|
|
345
|
+
if (position.mode === "absolute") item.abs_time_position = Math.round(position.offsetMs);
|
|
346
|
+
else if (position.mode === "anchored") {
|
|
347
|
+
const parent = byPart.get(position.anchorPartId);
|
|
348
|
+
if (parent != null) item.abs_time_position = resolve(parent) + Math.round(position.offsetMs);
|
|
349
|
+
}
|
|
350
|
+
visiting.delete(item.part_id);
|
|
351
|
+
resolved.add(item.part_id);
|
|
352
|
+
return item.abs_time_position;
|
|
353
|
+
};
|
|
354
|
+
for (const item of items) resolve(item);
|
|
355
|
+
}
|
|
356
|
+
/** Preserve the historical orphan fallback without re-parenting valid roots. */
|
|
357
|
+
function recoverOrphanedSpeechAnchorsByTime(doc) {
|
|
358
|
+
const placedPartIds = new Set([
|
|
359
|
+
...doc.main_track,
|
|
360
|
+
...doc.bgm_track,
|
|
361
|
+
...doc.speech_track,
|
|
362
|
+
...doc.caption_track
|
|
363
|
+
].map((item) => item.part_id));
|
|
364
|
+
const ranges = doc.main_track.map((item) => ({
|
|
365
|
+
part_id: item.part_id,
|
|
366
|
+
start: item.abs_time_position,
|
|
367
|
+
end: item.abs_time_position + partDurationMs(doc, item.part_id)
|
|
368
|
+
}));
|
|
369
|
+
if (ranges.length === 0) return;
|
|
370
|
+
for (const speech of doc.speech_track) {
|
|
371
|
+
const position = speech.time_position;
|
|
372
|
+
if (position.mode !== "anchored" || placedPartIds.has(position.anchorPartId)) continue;
|
|
373
|
+
const range = ranges.find((candidate) => candidate.start <= speech.abs_time_position && speech.abs_time_position < candidate.end) ?? (speech.abs_time_position < ranges[0].start ? ranges[0] : ranges[ranges.length - 1]);
|
|
374
|
+
const relative = Math.max(0, Math.round(speech.abs_time_position - range.start));
|
|
375
|
+
let aggregation = doc.aggregations.find((candidate) => candidate.body_part_id === range.part_id);
|
|
376
|
+
if (aggregation == null) {
|
|
377
|
+
aggregation = {
|
|
378
|
+
body_part_id: range.part_id,
|
|
379
|
+
attachments: []
|
|
380
|
+
};
|
|
381
|
+
doc.aggregations.push(aggregation);
|
|
382
|
+
}
|
|
383
|
+
aggregation.attachments.push({
|
|
384
|
+
part_id: speech.part_id,
|
|
385
|
+
relative_time_position: relative
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
325
390
|
* Reassign each speech to the video clip whose time range contains its start,
|
|
326
391
|
* rebuilding `aggregations`. A speech before the first clip or after the last
|
|
327
392
|
* falls back to the first / last clip respectively (FE: see §4 note — FE falls
|
|
@@ -524,14 +589,14 @@ function hostVideoOf(doc, speechPartId) {
|
|
|
524
589
|
//#endregion
|
|
525
590
|
//#region src/timeline-core/entrypoints.ts
|
|
526
591
|
/**
|
|
527
|
-
* The full solve pipeline: arrange →
|
|
528
|
-
* fill-gaps → recalc. The single cascade the read-side projection runs; ops
|
|
592
|
+
* The full solve pipeline: arrange → resolve declared parent edges →
|
|
593
|
+
* resolve-overlap → fill-gaps → recalc. The single cascade the read-side projection runs; ops
|
|
529
594
|
* never call it (they write only facts — RFC 02 §7/§10).
|
|
530
595
|
*/
|
|
531
596
|
function cascadeAfterVideoClipChanges(doc, makeEmptyPart) {
|
|
532
597
|
arrangeMainTrackSeamlessly(doc);
|
|
533
|
-
|
|
534
|
-
|
|
598
|
+
resolveAnchoredClipsTimePosition(doc);
|
|
599
|
+
recoverOrphanedSpeechAnchorsByTime(doc);
|
|
535
600
|
resolveAllSpeechOverlaps(doc);
|
|
536
601
|
fillMainTrackTimeGaps(doc, makeEmptyPart);
|
|
537
602
|
recalculateTimelineDuration(doc);
|
|
@@ -605,7 +670,7 @@ function videoDocumentToTimelineDoc(document) {
|
|
|
605
670
|
caption_track: captionItems,
|
|
606
671
|
bgm_track: bgmItems,
|
|
607
672
|
part_library: partLibrary,
|
|
608
|
-
aggregations: seedAggregations(mainItems, speechItems),
|
|
673
|
+
aggregations: seedAggregations([...mainItems, ...bgmItems], speechItems),
|
|
609
674
|
timeline: {
|
|
610
675
|
duration_ms: 0,
|
|
611
676
|
unit_time_ms: document.timeline?.unit_time_ms ?? 0
|
|
@@ -629,19 +694,18 @@ function seedAbs(position, fallbackAbsMs) {
|
|
|
629
694
|
}
|
|
630
695
|
}
|
|
631
696
|
/**
|
|
632
|
-
* Build the
|
|
633
|
-
*
|
|
634
|
-
*
|
|
635
|
-
* caption→speech relations are recomputed by the cascade from `caption.start_ms`.
|
|
697
|
+
* Build the read-view `aggregations` parent-map from anchored speech items.
|
|
698
|
+
* Video and BGM roots are both valid hosts; absolute speech roots have no entry.
|
|
699
|
+
* Caption→speech ownership remains in `speech_part_id` / `caption_ids`.
|
|
636
700
|
*/
|
|
637
|
-
function seedAggregations(
|
|
638
|
-
const
|
|
701
|
+
function seedAggregations(hostItems, speechItems) {
|
|
702
|
+
const hostPartIds = new Set(hostItems.map((item) => item.part_id));
|
|
639
703
|
const byHost = /* @__PURE__ */ new Map();
|
|
640
704
|
const order = [];
|
|
641
705
|
for (const speech of speechItems) {
|
|
642
706
|
if (speech.time_position.mode !== "anchored") continue;
|
|
643
707
|
const host = speech.time_position.anchorPartId;
|
|
644
|
-
if (!
|
|
708
|
+
if (!hostPartIds.has(host)) continue;
|
|
645
709
|
let aggregation = byHost.get(host);
|
|
646
710
|
if (aggregation == null) {
|
|
647
711
|
aggregation = {
|
|
@@ -1663,4 +1727,4 @@ function toTrackRow(track) {
|
|
|
1663
1727
|
};
|
|
1664
1728
|
}
|
|
1665
1729
|
//#endregion
|
|
1666
|
-
export {
|
|
1730
|
+
export { TIMELINE_SKELETON_DURATION_MS as A, base64ToBytes as B, reassignSpeechesToVideoClipsByTime as C, resolveAnchoredClipsTimePosition as D, resolveAllSpeechOverlaps as E, VIDEO_DOCUMENT_SCHEMA_VERSION as F, effectiveVideoClipDurationMs as I, partUnionToDraft as L, partDurationMs as M, safeDurationMs as N, resolveSpeechOverlapByShiftingVideos as O, DEFAULT_UNIT_TIME_MS as P, recordEntries as R, fillMainTrackTimeGaps as S, recoverOrphanedSpeechAnchorsByTime as T, bytesToBase64 as V, findLaneTrack as _, buildInitialVideoDocument as a, cascadeAfterVideoClipChanges as b, fromVideoDocument as c, assertValidVideoDocument as d, validateVideoDocument as f, ensureLaneTrack as g, LANE_KINDS_IN_STACK_ORDER as h, readVideoDocumentFromDraft as i, isEmptyVideoClip as j, syncAggregatedClipsTimePosition as k, toVideoDocument as l, videoDocumentSchema as m, createMirrorVideoDocument as n, buildSpeechHostMap as o, partUnionSchema as p, createMirrorVideoDocumentAdapter as r, derivePositionFromAbs as s, MirrorVideoDocumentAdapter as t, VideoDocumentValidationError as u, laneTrackId as v, recalculateTimelineDuration as w, arrangeMainTrackSeamlessly as x, solveVideoDocument as y, videoDocumentMirrorSchema as z };
|
|
@@ -948,7 +948,7 @@ type TrackItemDraft = NonNullable<NonNullable<TrackDraft['items']>[number]>;
|
|
|
948
948
|
declare const addSpeechesInputSchema: z.ZodObject<{
|
|
949
949
|
speeches: z.ZodArray<z.ZodObject<{
|
|
950
950
|
speech_id: z.ZodString;
|
|
951
|
-
anchor_part_id: z.ZodString
|
|
951
|
+
anchor_part_id: z.ZodNullable<z.ZodString>;
|
|
952
952
|
offset_ms: z.ZodNumber;
|
|
953
953
|
audio_storage_key: z.ZodString;
|
|
954
954
|
duration_ms: z.ZodNumber;
|
|
@@ -1053,7 +1053,7 @@ type AdjustVideoClipVolumeInput = z.infer<typeof adjustVideoClipVolumeInputSchem
|
|
|
1053
1053
|
declare const changeSpeechScriptInputSchema: z.ZodObject<{
|
|
1054
1054
|
speeches: z.ZodArray<z.ZodObject<{
|
|
1055
1055
|
speech_id: z.ZodString;
|
|
1056
|
-
anchor_part_id: z.ZodString
|
|
1056
|
+
anchor_part_id: z.ZodNullable<z.ZodString>;
|
|
1057
1057
|
offset_ms: z.ZodNumber;
|
|
1058
1058
|
audio_storage_key: z.ZodString;
|
|
1059
1059
|
duration_ms: z.ZodNumber;
|
|
@@ -1077,7 +1077,7 @@ declare const changeSpeechScriptInputSchema: z.ZodObject<{
|
|
|
1077
1077
|
declare const changeSpeechVoiceInputSchema: z.ZodObject<{
|
|
1078
1078
|
speeches: z.ZodArray<z.ZodObject<{
|
|
1079
1079
|
speech_id: z.ZodString;
|
|
1080
|
-
anchor_part_id: z.ZodString
|
|
1080
|
+
anchor_part_id: z.ZodNullable<z.ZodString>;
|
|
1081
1081
|
offset_ms: z.ZodNumber;
|
|
1082
1082
|
audio_storage_key: z.ZodString;
|
|
1083
1083
|
duration_ms: z.ZodNumber;
|
|
@@ -1156,6 +1156,12 @@ declare const moveSpeechesInputSchema: z.ZodObject<{
|
|
|
1156
1156
|
}, z.core.$strip>;
|
|
1157
1157
|
type MoveSpeechesInput = z.infer<typeof moveSpeechesInputSchema>;
|
|
1158
1158
|
//#endregion
|
|
1159
|
+
//#region src/editor/schemas/move-bgm.d.ts
|
|
1160
|
+
declare const moveBgmInputSchema: z.ZodObject<{
|
|
1161
|
+
new_start_ms: z.ZodNumber;
|
|
1162
|
+
}, z.core.$strip>;
|
|
1163
|
+
type MoveBgmInput = z.infer<typeof moveBgmInputSchema>;
|
|
1164
|
+
//#endregion
|
|
1159
1165
|
//#region src/editor/schemas/move-video-clips-by-anchor.d.ts
|
|
1160
1166
|
/**
|
|
1161
1167
|
* Where the moved block lands on the main track.
|
|
@@ -1490,16 +1496,14 @@ declare const voiceSchema: z.ZodObject<{
|
|
|
1490
1496
|
* stable speech + caption parts and writes them as authoritative facts. No
|
|
1491
1497
|
* cascade runs on write — the projection derives absolute positions on read.
|
|
1492
1498
|
*
|
|
1493
|
-
*
|
|
1494
|
-
*
|
|
1495
|
-
*
|
|
1496
|
-
*
|
|
1497
|
-
* host-picking. Captions anchor to their speech via the caption part's
|
|
1498
|
-
* `start_ms` (offset within the speech).
|
|
1499
|
+
* `anchor_part_id` states the root relationship explicitly: a video/BGM id
|
|
1500
|
+
* creates an anchored speech; `null` creates an absolute audio root.
|
|
1501
|
+
* `offset_ms` is relative to the anchor or absolute for a root. Captions always
|
|
1502
|
+
* anchor to their speech via `start_ms`.
|
|
1499
1503
|
*/
|
|
1500
1504
|
declare const speechAssetSchema: z.ZodObject<{
|
|
1501
1505
|
speech_id: z.ZodString;
|
|
1502
|
-
anchor_part_id: z.ZodString
|
|
1506
|
+
anchor_part_id: z.ZodNullable<z.ZodString>;
|
|
1503
1507
|
offset_ms: z.ZodNumber;
|
|
1504
1508
|
audio_storage_key: z.ZodString;
|
|
1505
1509
|
duration_ms: z.ZodNumber;
|
|
@@ -1523,7 +1527,7 @@ declare const captionAssetSchema: z.ZodObject<{
|
|
|
1523
1527
|
declare const speechAssetsSchema: z.ZodObject<{
|
|
1524
1528
|
speeches: z.ZodArray<z.ZodObject<{
|
|
1525
1529
|
speech_id: z.ZodString;
|
|
1526
|
-
anchor_part_id: z.ZodString
|
|
1530
|
+
anchor_part_id: z.ZodNullable<z.ZodString>;
|
|
1527
1531
|
offset_ms: z.ZodNumber;
|
|
1528
1532
|
audio_storage_key: z.ZodString;
|
|
1529
1533
|
duration_ms: z.ZodNumber;
|
|
@@ -1548,7 +1552,7 @@ type SpeechAsset = z.infer<typeof speechAssetSchema>;
|
|
|
1548
1552
|
type CaptionAsset = z.infer<typeof captionAssetSchema>;
|
|
1549
1553
|
type SpeechAssets = z.infer<typeof speechAssetsSchema>;
|
|
1550
1554
|
declare namespace index_d_exports {
|
|
1551
|
-
export { AddSpeechesInput, AddVideoClipsInput, AdjustBgmVolumeInput, AdjustSpeechVolumeInput, AdjustVideoClipDurationInput, AdjustVideoClipVolumeInput, AnchoredDeletePolicy, AnchoredReplacePolicy, CaptionAsset, ChangeSpeechScriptInput, ChangeSpeechVoiceInput, DeleteBgmInput, DeleteSpeechesInput, DeleteVideoClipsInput, MoveAnchor, MoveSpeechesInput, MoveVideoClipsByAnchorInput, MoveVideoClipsInput, MovedClipAnchoredPolicy, ReplaceVideoClipContentInput, ReplaceVideoClipSequenceInput, ResizeCaptionInput, SetBgmInput, SetCaptionStyleInput, SetCaptionVisibilityInput, SetVideoClipSpeedShiftInput, SpeechAsset, SpeechAssets, addSpeechesInputSchema, addVideoClipsInputSchema, adjustBgmVolumeInputSchema, adjustSpeechVolumeInputSchema, adjustVideoClipDurationInputSchema, adjustVideoClipVolumeInputSchema, anchoredDeletePolicySchema, anchoredReplacePolicySchema, changeSpeechScriptInputSchema, changeSpeechVoiceInputSchema, clipIdSchema, clipIdsSchema, deleteBgmInputSchema, deleteSpeechesInputSchema, deleteVideoClipsInputSchema, mediaIdSchema, moveAnchorSchema, moveSpeechesInputSchema, moveVideoClipsByAnchorInputSchema, moveVideoClipsInputSchema, movedClipAnchoredPolicySchema, positiveMsSchema, replaceVideoClipContentInputSchema, replaceVideoClipSequenceInputSchema, resizeCaptionInputSchema, setBgmInputSchema, setCaptionStyleInputSchema, setCaptionVisibilityInputSchema, setVideoClipSpeedShiftInputSchema, speechAssetsSchema, speechIdSchema, speechIdsSchema, speedShiftSchema, timelineMsSchema, voiceSchema, volumeSchema };
|
|
1555
|
+
export { AddSpeechesInput, AddVideoClipsInput, AdjustBgmVolumeInput, AdjustSpeechVolumeInput, AdjustVideoClipDurationInput, AdjustVideoClipVolumeInput, AnchoredDeletePolicy, AnchoredReplacePolicy, CaptionAsset, ChangeSpeechScriptInput, ChangeSpeechVoiceInput, DeleteBgmInput, DeleteSpeechesInput, DeleteVideoClipsInput, MoveAnchor, MoveBgmInput, MoveSpeechesInput, MoveVideoClipsByAnchorInput, MoveVideoClipsInput, MovedClipAnchoredPolicy, ReplaceVideoClipContentInput, ReplaceVideoClipSequenceInput, ResizeCaptionInput, SetBgmInput, SetCaptionStyleInput, SetCaptionVisibilityInput, SetVideoClipSpeedShiftInput, SpeechAsset, SpeechAssets, addSpeechesInputSchema, addVideoClipsInputSchema, adjustBgmVolumeInputSchema, adjustSpeechVolumeInputSchema, adjustVideoClipDurationInputSchema, adjustVideoClipVolumeInputSchema, anchoredDeletePolicySchema, anchoredReplacePolicySchema, changeSpeechScriptInputSchema, changeSpeechVoiceInputSchema, clipIdSchema, clipIdsSchema, deleteBgmInputSchema, deleteSpeechesInputSchema, deleteVideoClipsInputSchema, mediaIdSchema, moveAnchorSchema, moveBgmInputSchema, moveSpeechesInputSchema, moveVideoClipsByAnchorInputSchema, moveVideoClipsInputSchema, movedClipAnchoredPolicySchema, positiveMsSchema, replaceVideoClipContentInputSchema, replaceVideoClipSequenceInputSchema, resizeCaptionInputSchema, setBgmInputSchema, setCaptionStyleInputSchema, setCaptionVisibilityInputSchema, setVideoClipSpeedShiftInputSchema, speechAssetsSchema, speechIdSchema, speechIdsSchema, speedShiftSchema, timelineMsSchema, voiceSchema, volumeSchema };
|
|
1552
1556
|
}
|
|
1553
1557
|
//#endregion
|
|
1554
1558
|
//#region src/editor/schema-validator.d.ts
|
|
@@ -1597,6 +1601,7 @@ declare class SchemaValidator {
|
|
|
1597
1601
|
validateChangeSpeechVoice(input: ChangeSpeechVoiceInput, doc: SnapshotReadable): void;
|
|
1598
1602
|
validateDeleteSpeeches(input: DeleteSpeechesInput, doc: SnapshotReadable): void;
|
|
1599
1603
|
validateMoveSpeeches(input: MoveSpeechesInput, doc: SnapshotReadable): void;
|
|
1604
|
+
validateMoveBgm(input: MoveBgmInput, doc: SnapshotReadable): void;
|
|
1600
1605
|
validateSetBgm(input: SetBgmInput, _doc: SnapshotReadable): void;
|
|
1601
1606
|
validateDeleteBgm(input: DeleteBgmInput, _doc: SnapshotReadable): void;
|
|
1602
1607
|
validateSetCaptionVisibility(input: SetCaptionVisibilityInput, _doc: SnapshotReadable): void;
|
|
@@ -1609,12 +1614,8 @@ declare class SchemaValidator {
|
|
|
1609
1614
|
/** Each regenerated speech (re-TTS) must already exist in the document. */
|
|
1610
1615
|
private assertSpeechesExist;
|
|
1611
1616
|
/**
|
|
1612
|
-
*
|
|
1613
|
-
*
|
|
1614
|
-
* by the projection and has no host to recover to (RFC 02 §4/§11.1), so the
|
|
1615
|
-
* write must be rejected rather than landing a dangling reference. (Restores
|
|
1616
|
-
* the dangling-reference guard the old `validateMaterializedPatch` carried;
|
|
1617
|
-
* ADR 0009.)
|
|
1617
|
+
* Anchored speeches may follow a video or BGM root. A null anchor is an
|
|
1618
|
+
* explicit absolute speech root. Missing and wrong-kind parents are rejected.
|
|
1618
1619
|
*/
|
|
1619
1620
|
private assertAnchorsExist;
|
|
1620
1621
|
private assertPartKind;
|
|
@@ -1649,7 +1650,7 @@ declare class SchemaValidator {
|
|
|
1649
1650
|
* side-effect payload contract in
|
|
1650
1651
|
* `docs/projects/medeo-integration/results/phase-4-side-effect-payload-contract.md`.
|
|
1651
1652
|
*/
|
|
1652
|
-
type ImplementedSemanticOpKind = 'MoveVideoClips' | 'MoveVideoClipsByAnchor' | 'DeleteVideoClips' | 'AddVideoClips' | 'AdjustVideoClipVolume' | 'SetVideoClipSpeedShift' | 'ReplaceVideoClipContent' | 'ReplaceVideoClipSequence' | 'AdjustVideoClipDuration' | 'AddSpeeches' | 'DeleteSpeeches' | 'MoveSpeeches' | 'ChangeSpeechScript' | 'ChangeSpeechVoice' | 'AdjustSpeechVolume' | 'ResizeCaption' | 'SetCaptionVisibility' | 'SetCaptionStyle' | 'SetBgm' | 'DeleteBgm' | 'AdjustBgmVolume';
|
|
1653
|
+
type ImplementedSemanticOpKind = 'MoveVideoClips' | 'MoveVideoClipsByAnchor' | 'DeleteVideoClips' | 'AddVideoClips' | 'AdjustVideoClipVolume' | 'SetVideoClipSpeedShift' | 'ReplaceVideoClipContent' | 'ReplaceVideoClipSequence' | 'AdjustVideoClipDuration' | 'AddSpeeches' | 'DeleteSpeeches' | 'MoveSpeeches' | 'MoveBgm' | 'ChangeSpeechScript' | 'ChangeSpeechVoice' | 'AdjustSpeechVolume' | 'ResizeCaption' | 'SetCaptionVisibility' | 'SetCaptionStyle' | 'SetBgm' | 'DeleteBgm' | 'AdjustBgmVolume';
|
|
1653
1654
|
/**
|
|
1654
1655
|
* Operations on the roadmap but NOT yet implemented by `SemanticEditor`. Empty:
|
|
1655
1656
|
* Phase 4 covers every op with a real entry point. Kept as a named type so the
|
|
@@ -1711,7 +1712,7 @@ interface CommitOptions {
|
|
|
1711
1712
|
/** Author of this op; omitted for writes with no acting user (bootstrap, repair). */
|
|
1712
1713
|
actor?: OpActor;
|
|
1713
1714
|
}
|
|
1714
|
-
type SemanticOpInput = MoveVideoClipsInput | MoveVideoClipsByAnchorInput | DeleteVideoClipsInput | AddVideoClipsInput | AdjustVideoClipVolumeInput | AdjustSpeechVolumeInput | AdjustBgmVolumeInput | SetVideoClipSpeedShiftInput | ReplaceVideoClipContentInput | ReplaceVideoClipSequenceInput | AdjustVideoClipDurationInput | AddSpeechesInput | DeleteSpeechesInput | MoveSpeechesInput | ResizeCaptionInput | SetCaptionVisibilityInput | SetCaptionStyleInput | SetBgmInput | DeleteBgmInput;
|
|
1715
|
+
type SemanticOpInput = MoveVideoClipsInput | MoveVideoClipsByAnchorInput | DeleteVideoClipsInput | AddVideoClipsInput | AdjustVideoClipVolumeInput | AdjustSpeechVolumeInput | AdjustBgmVolumeInput | SetVideoClipSpeedShiftInput | ReplaceVideoClipContentInput | ReplaceVideoClipSequenceInput | AdjustVideoClipDurationInput | AddSpeechesInput | DeleteSpeechesInput | MoveSpeechesInput | MoveBgmInput | ResizeCaptionInput | SetCaptionVisibilityInput | SetCaptionStyleInput | SetBgmInput | DeleteBgmInput;
|
|
1715
1716
|
type SemanticOpName = ImplementedSemanticOpKind;
|
|
1716
1717
|
/** Audit metadata committed alongside an op's writes. */
|
|
1717
1718
|
interface TransactAudit {
|
|
@@ -1827,6 +1828,8 @@ declare class SemanticEditor {
|
|
|
1827
1828
|
* derives absolute positions on read.
|
|
1828
1829
|
*/
|
|
1829
1830
|
moveSpeeches(input: MoveSpeechesInput, options?: CommitOptions): Promise<void>;
|
|
1831
|
+
/** Move the BGM root; any speech/caption descendants follow through their anchors. */
|
|
1832
|
+
moveBgm(input: MoveBgmInput, options?: CommitOptions): Promise<void>;
|
|
1830
1833
|
/** Change a speech's script. Re-TTS runs upstream; the regenerated parts arrive materialized (no cascade). */
|
|
1831
1834
|
changeSpeechScript(input: ChangeSpeechScriptInput, options?: CommitOptions): Promise<void>;
|
|
1832
1835
|
/** Change a speech's voice. Re-TTS runs upstream; the regenerated parts arrive materialized (no cascade). */
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as VideoDocumentSchemaVersion, A as assertValidVideoDocument, B as BgmPart, C as index_d_exports, D as VideoDocumentMirrorSchema, E as VideoDocumentDraft, F as SpeechHostMap, G as SpeechPart, H as DEFAULT_UNIT_TIME_MS, I as buildSpeechHostMap, J as TrackItem, K as Timeline, L as derivePositionFromAbs, M as InitialDocumentFacts, N as buildInitialVideoDocument, O as videoDocumentMirrorSchema, P as DerivedItemPosition, Q as VideoDocument, R as fromVideoDocument, S as ValidationError, T as TrackItemDraft, U as PartKind, V as CaptionPart, W as PartUnion, X as VIDEO_DOCUMENT_SCHEMA_VERSION, Y as TrackItemTimePosition, Z as VideoClipPart, _ as PlannedSemanticOpKind, a as MirrorVideoDocumentOptions, at as CaptionPart$1, b as SchemaValidator, c as CommitOptions, ct as SpeedShift, d as SemanticEditor, dt as TrackItem$1, et as VideoDocumentValidationIssue, f as SemanticOpInput, g as ImplementedSemanticOpKind, h as IMPLEMENTED_SEMANTIC_OP_KINDS, i as MirrorVideoDocumentAdapter, it as Attachment, j as validateVideoDocument, k as VideoDocumentValidationError, l as OpActor, lt as Timeline$1, m as TransactAudit, n as videoDocumentSchema, nt as VideoDraft, o as createMirrorVideoDocument, ot as CaptionStyle, p as SemanticOpName, q as Track, r as readVideoDocumentFromDraft, rt as VideoDraftPartUnion, s as createMirrorVideoDocumentAdapter, st as PartAggregation, t as partUnionSchema, tt as VideoDocumentValidationIssueCode, u as SemanticDocumentAdapter, ut as Track$1, v as SemanticOpKind, w as TrackDraft, x as SnapshotReadable, y as isImplementedSemanticOpKind, z as toVideoDocument } from "./index-
|
|
1
|
+
import { $ as VideoDocumentSchemaVersion, A as assertValidVideoDocument, B as BgmPart, C as index_d_exports, D as VideoDocumentMirrorSchema, E as VideoDocumentDraft, F as SpeechHostMap, G as SpeechPart, H as DEFAULT_UNIT_TIME_MS, I as buildSpeechHostMap, J as TrackItem, K as Timeline, L as derivePositionFromAbs, M as InitialDocumentFacts, N as buildInitialVideoDocument, O as videoDocumentMirrorSchema, P as DerivedItemPosition, Q as VideoDocument, R as fromVideoDocument, S as ValidationError, T as TrackItemDraft, U as PartKind, V as CaptionPart, W as PartUnion, X as VIDEO_DOCUMENT_SCHEMA_VERSION, Y as TrackItemTimePosition, Z as VideoClipPart, _ as PlannedSemanticOpKind, a as MirrorVideoDocumentOptions, at as CaptionPart$1, b as SchemaValidator, c as CommitOptions, ct as SpeedShift, d as SemanticEditor, dt as TrackItem$1, et as VideoDocumentValidationIssue, f as SemanticOpInput, g as ImplementedSemanticOpKind, h as IMPLEMENTED_SEMANTIC_OP_KINDS, i as MirrorVideoDocumentAdapter, it as Attachment, j as validateVideoDocument, k as VideoDocumentValidationError, l as OpActor, lt as Timeline$1, m as TransactAudit, n as videoDocumentSchema, nt as VideoDraft, o as createMirrorVideoDocument, ot as CaptionStyle, p as SemanticOpName, q as Track, r as readVideoDocumentFromDraft, rt as VideoDraftPartUnion, s as createMirrorVideoDocumentAdapter, st as PartAggregation, t as partUnionSchema, tt as VideoDocumentValidationIssueCode, u as SemanticDocumentAdapter, ut as Track$1, v as SemanticOpKind, w as TrackDraft, x as SnapshotReadable, y as isImplementedSemanticOpKind, z as toVideoDocument } from "./index-DrEHTguy.js";
|
|
2
2
|
import { PeerID } from "loro-crdt";
|
|
3
3
|
import { DocState } from "@mengine/sync";
|
|
4
4
|
import { BaseDocStorage, Connection, DocDiff, DocPushReceipt, DocSnapshotRecord, DocStorage, DocStorageOptions, DocUpdate, DocUpdateRecord } from "@mengine/storage";
|
|
@@ -853,6 +853,15 @@ declare function arrangeMainTrackSeamlessly(doc: TimelineDoc): void;
|
|
|
853
853
|
* caption.abs = speech.abs + caption.start_ms
|
|
854
854
|
*/
|
|
855
855
|
declare function syncAggregatedClipsTimePosition(doc: TimelineDoc): void;
|
|
856
|
+
/**
|
|
857
|
+
* Resolve authoritative `time_position` edges without inventing parents from
|
|
858
|
+
* geometric overlap. Roots stay absolute/sequential; every anchored descendant
|
|
859
|
+
* follows its declared parent, regardless of whether that parent is video, BGM,
|
|
860
|
+
* or speech.
|
|
861
|
+
*/
|
|
862
|
+
declare function resolveAnchoredClipsTimePosition(doc: TimelineDoc): void;
|
|
863
|
+
/** Preserve the historical orphan fallback without re-parenting valid roots. */
|
|
864
|
+
declare function recoverOrphanedSpeechAnchorsByTime(doc: TimelineDoc): void;
|
|
856
865
|
/**
|
|
857
866
|
* Reassign each speech to the video clip whose time range contains its start,
|
|
858
867
|
* rebuilding `aggregations`. A speech before the first clip or after the last
|
|
@@ -918,8 +927,8 @@ type MakeEmptyPart = (durationMs: number) => {
|
|
|
918
927
|
partId: string;
|
|
919
928
|
};
|
|
920
929
|
/**
|
|
921
|
-
* The full solve pipeline: arrange →
|
|
922
|
-
* fill-gaps → recalc. The single cascade the read-side projection runs; ops
|
|
930
|
+
* The full solve pipeline: arrange → resolve declared parent edges →
|
|
931
|
+
* resolve-overlap → fill-gaps → recalc. The single cascade the read-side projection runs; ops
|
|
923
932
|
* never call it (they write only facts — RFC 02 §7/§10).
|
|
924
933
|
*/
|
|
925
934
|
declare function cascadeAfterVideoClipChanges(doc: TimelineDoc, makeEmptyPart: MakeEmptyPart): void;
|
|
@@ -1064,4 +1073,4 @@ declare function hostForAbsMs(ranges: MainClipRange[], absMs: number): MainClipR
|
|
|
1064
1073
|
*/
|
|
1065
1074
|
declare function relativePositionForAbs(ranges: MainClipRange[], absMs: number): TrackItemTimePosition;
|
|
1066
1075
|
//#endregion
|
|
1067
|
-
export { type Aggregation, type Attachment, type BgmPart, type CaptionPart, type CaptionStyle, type CommitOptions, DEFAULT_UNIT_TIME_MS, type DerivedItemPosition, type DocStorageLike, type DocVersionMark, IMPLEMENTED_SEMANTIC_OP_KINDS, type ImplementedSemanticOpKind, type InitialDocumentFacts, LANE_KINDS_IN_STACK_ORDER, type LaneKind, type MainClipRange, type MakeEmptyPart, ManualSyncDoc, type ManualSyncDocOptions, MedeoHttpDocStorage, type MedeoHttpDocStorageOptions, MemoryDocStorage, MengineAckFailedError, type MengineAuditEntry, type MengineAuditResponse, MengineDocSession, type MengineDocSessionOptions, type MengineDocSessionUpdateEvent, type MengineDocumentVersion, type MengineEventStreamOptions, MengineHttpClient, type MengineHttpClientOptions, MengineHttpRequestError, MenginePushRejectedError, type MenginePushResponse, type MenginePushUpdateResponse, type MengineRejectedResponse, type MengineSnapshotResponse, type MengineSseUpdateEvent, type MengineSyncResponse, type MengineUpdateMeta, MirrorVideoDocumentAdapter, type MirrorVideoDocumentOptions, type OpActor, type PartAggregation, type PartKind, type PartUnion, type PlannedSemanticOpKind, type PullFailureReason, type PullResult, type PushOutcome, type PushOutcomeKind, type PushResult, type PushResultKind, SchemaValidator, type SemanticDocumentAdapter, SemanticEditor, type SemanticOpInput, type SemanticOpKind, type SemanticOpName, type SnapshotReadable, type SolvedVideoDocument, type SpeechHostMap, type SpeechPart, type SpeedShift, TIMELINE_SKELETON_DURATION_MS, type Timeline, type TimelineDoc, type TimelineItem, type Track, type TrackDraft, type TrackItem, type TrackItemDraft, type TrackItemTimePosition, type TransactAudit, VIDEO_DOCUMENT_SCHEMA_VERSION, ValidationError, type VideoClipPart, type VideoDocument, type VideoDocumentDraft, type VideoDocumentMirrorSchema, type VideoDocumentSchemaVersion, VideoDocumentValidationError, type VideoDocumentValidationIssue, type VideoDocumentValidationIssueCode, type VideoDraft, type CaptionPart$1 as VideoDraftCaptionPart, type VideoDraftPartUnion, type Timeline$1 as VideoDraftTimeline, type Track$1 as VideoDraftTrack, type TrackItem$1 as VideoDraftTrackItem, type WaitForServerAckOptions, arrangeMainTrackSeamlessly, assertValidVideoDocument, base64ToBytes, buildInitialVideoDocument, buildSpeechHostMap, bytesToBase64, cascadeAfterVideoClipChanges, createMirrorVideoDocument, createMirrorVideoDocumentAdapter, decodeDocVersionMark, derivePositionFromAbs, encodeDocVersionMark, ensureLaneTrack, fillMainTrackTimeGaps, findLaneTrack, fromVideoDocument, generatePartId, getAt, hostForAbsMs, isEmptyVideoClip, isImplementedSemanticOpKind, isMap, laneTrackId, mainTrackRanges, partDurationMs, partUnionSchema, readMainTrackItems, readMengineEventStream, readPart, readPartDurationMs, readVideoDocumentFromDraft, reassignSpeechesToVideoClipsByTime, recalculateTimelineDuration, relativePositionForAbs, resolveAllSpeechOverlaps, resolveSpeechOverlapByShiftingVideos, safeDurationMs, index_d_exports as schemas, snapshotToPlain, solveVideoDocument, syncAggregatedClipsTimePosition, toVideoDocument, validateVideoDocument, videoDocumentMirrorSchema, videoDocumentSchema };
|
|
1076
|
+
export { type Aggregation, type Attachment, type BgmPart, type CaptionPart, type CaptionStyle, type CommitOptions, DEFAULT_UNIT_TIME_MS, type DerivedItemPosition, type DocStorageLike, type DocVersionMark, IMPLEMENTED_SEMANTIC_OP_KINDS, type ImplementedSemanticOpKind, type InitialDocumentFacts, LANE_KINDS_IN_STACK_ORDER, type LaneKind, type MainClipRange, type MakeEmptyPart, ManualSyncDoc, type ManualSyncDocOptions, MedeoHttpDocStorage, type MedeoHttpDocStorageOptions, MemoryDocStorage, MengineAckFailedError, type MengineAuditEntry, type MengineAuditResponse, MengineDocSession, type MengineDocSessionOptions, type MengineDocSessionUpdateEvent, type MengineDocumentVersion, type MengineEventStreamOptions, MengineHttpClient, type MengineHttpClientOptions, MengineHttpRequestError, MenginePushRejectedError, type MenginePushResponse, type MenginePushUpdateResponse, type MengineRejectedResponse, type MengineSnapshotResponse, type MengineSseUpdateEvent, type MengineSyncResponse, type MengineUpdateMeta, MirrorVideoDocumentAdapter, type MirrorVideoDocumentOptions, type OpActor, type PartAggregation, type PartKind, type PartUnion, type PlannedSemanticOpKind, type PullFailureReason, type PullResult, type PushOutcome, type PushOutcomeKind, type PushResult, type PushResultKind, SchemaValidator, type SemanticDocumentAdapter, SemanticEditor, type SemanticOpInput, type SemanticOpKind, type SemanticOpName, type SnapshotReadable, type SolvedVideoDocument, type SpeechHostMap, type SpeechPart, type SpeedShift, TIMELINE_SKELETON_DURATION_MS, type Timeline, type TimelineDoc, type TimelineItem, type Track, type TrackDraft, type TrackItem, type TrackItemDraft, type TrackItemTimePosition, type TransactAudit, VIDEO_DOCUMENT_SCHEMA_VERSION, ValidationError, type VideoClipPart, type VideoDocument, type VideoDocumentDraft, type VideoDocumentMirrorSchema, type VideoDocumentSchemaVersion, VideoDocumentValidationError, type VideoDocumentValidationIssue, type VideoDocumentValidationIssueCode, type VideoDraft, type CaptionPart$1 as VideoDraftCaptionPart, type VideoDraftPartUnion, type Timeline$1 as VideoDraftTimeline, type Track$1 as VideoDraftTrack, type TrackItem$1 as VideoDraftTrackItem, type WaitForServerAckOptions, arrangeMainTrackSeamlessly, assertValidVideoDocument, base64ToBytes, buildInitialVideoDocument, buildSpeechHostMap, bytesToBase64, cascadeAfterVideoClipChanges, createMirrorVideoDocument, createMirrorVideoDocumentAdapter, decodeDocVersionMark, derivePositionFromAbs, encodeDocVersionMark, ensureLaneTrack, fillMainTrackTimeGaps, findLaneTrack, fromVideoDocument, generatePartId, getAt, hostForAbsMs, isEmptyVideoClip, isImplementedSemanticOpKind, isMap, laneTrackId, mainTrackRanges, partDurationMs, partUnionSchema, readMainTrackItems, readMengineEventStream, readPart, readPartDurationMs, readVideoDocumentFromDraft, reassignSpeechesToVideoClipsByTime, recalculateTimelineDuration, recoverOrphanedSpeechAnchorsByTime, relativePositionForAbs, resolveAllSpeechOverlaps, resolveAnchoredClipsTimePosition, resolveSpeechOverlapByShiftingVideos, safeDurationMs, index_d_exports as schemas, snapshotToPlain, solveVideoDocument, syncAggregatedClipsTimePosition, toVideoDocument, validateVideoDocument, videoDocumentMirrorSchema, videoDocumentSchema };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-D7D4PA-g.js";
|
|
2
|
-
import { A as
|
|
2
|
+
import { A as TIMELINE_SKELETON_DURATION_MS, B as base64ToBytes, C as reassignSpeechesToVideoClipsByTime, D as resolveAnchoredClipsTimePosition, E as resolveAllSpeechOverlaps, F as VIDEO_DOCUMENT_SCHEMA_VERSION, I as effectiveVideoClipDurationMs, L as partUnionToDraft, M as partDurationMs, N as safeDurationMs, O as resolveSpeechOverlapByShiftingVideos, P as DEFAULT_UNIT_TIME_MS, R as recordEntries, S as fillMainTrackTimeGaps, T as recoverOrphanedSpeechAnchorsByTime, V as bytesToBase64, _ as findLaneTrack, a as buildInitialVideoDocument, b as cascadeAfterVideoClipChanges, c as fromVideoDocument, d as assertValidVideoDocument, f as validateVideoDocument, g as ensureLaneTrack, h as LANE_KINDS_IN_STACK_ORDER, i as readVideoDocumentFromDraft, j as isEmptyVideoClip, k as syncAggregatedClipsTimePosition, l as toVideoDocument, m as videoDocumentSchema, n as createMirrorVideoDocument, o as buildSpeechHostMap, p as partUnionSchema, r as createMirrorVideoDocumentAdapter, s as derivePositionFromAbs, t as MirrorVideoDocumentAdapter, u as VideoDocumentValidationError, v as laneTrackId, w as recalculateTimelineDuration, x as arrangeMainTrackSeamlessly, y as solveVideoDocument, z as videoDocumentMirrorSchema } from "./document-C5KDvMMR.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { LoroDoc, VersionVector } from "loro-crdt";
|
|
5
5
|
import { ClientServerSynchronizer, DocManager } from "@mengine/sync";
|
|
@@ -308,17 +308,15 @@ const voiceSchema = z.object({
|
|
|
308
308
|
* stable speech + caption parts and writes them as authoritative facts. No
|
|
309
309
|
* cascade runs on write — the projection derives absolute positions on read.
|
|
310
310
|
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
* host-picking. Captions anchor to their speech via the caption part's
|
|
316
|
-
* `start_ms` (offset within the speech).
|
|
311
|
+
* `anchor_part_id` states the root relationship explicitly: a video/BGM id
|
|
312
|
+
* creates an anchored speech; `null` creates an absolute audio root.
|
|
313
|
+
* `offset_ms` is relative to the anchor or absolute for a root. Captions always
|
|
314
|
+
* anchor to their speech via `start_ms`.
|
|
317
315
|
*/
|
|
318
316
|
const speechAssetSchema = z.object({
|
|
319
317
|
speech_id: speechIdSchema.describe("The speech part ID (= side-effect speech_parts[].id)"),
|
|
320
|
-
anchor_part_id:
|
|
321
|
-
offset_ms: timelineMsSchema.describe("
|
|
318
|
+
anchor_part_id: z.string().min(1).nullable().describe("Video/BGM parent part ID, or null when the speech itself is the root"),
|
|
319
|
+
offset_ms: timelineMsSchema.describe("Relative offset from the parent, or absolute start for a root speech"),
|
|
322
320
|
audio_storage_key: z.string().min(1),
|
|
323
321
|
duration_ms: positiveMsSchema,
|
|
324
322
|
audio_script: z.string(),
|
|
@@ -509,6 +507,9 @@ const moveSpeechesInputSchema = z.object({ speeches: z.array(z.object({
|
|
|
509
507
|
new_start_ms: timelineMsSchema.describe("New absolute start time on the timeline")
|
|
510
508
|
})).min(1).describe("Speeches to move to new positions") });
|
|
511
509
|
//#endregion
|
|
510
|
+
//#region src/editor/schemas/move-bgm.ts
|
|
511
|
+
const moveBgmInputSchema = z.object({ new_start_ms: timelineMsSchema.describe("New absolute BGM start on the timeline") });
|
|
512
|
+
//#endregion
|
|
512
513
|
//#region src/editor/schemas/move-video-clips-by-anchor.ts
|
|
513
514
|
/**
|
|
514
515
|
* Where the moved block lands on the main track.
|
|
@@ -765,6 +766,7 @@ var schemas_exports = /* @__PURE__ */ __exportAll({
|
|
|
765
766
|
deleteVideoClipsInputSchema: () => deleteVideoClipsInputSchema,
|
|
766
767
|
mediaIdSchema: () => mediaIdSchema,
|
|
767
768
|
moveAnchorSchema: () => moveAnchorSchema,
|
|
769
|
+
moveBgmInputSchema: () => moveBgmInputSchema,
|
|
768
770
|
moveSpeechesInputSchema: () => moveSpeechesInputSchema,
|
|
769
771
|
moveVideoClipsByAnchorInputSchema: () => moveVideoClipsByAnchorInputSchema,
|
|
770
772
|
moveVideoClipsInputSchema: () => moveVideoClipsInputSchema,
|
|
@@ -1014,6 +1016,10 @@ var SchemaValidator = class {
|
|
|
1014
1016
|
const snapshot = doc.snapshot();
|
|
1015
1017
|
for (const s of parsed.speeches) this.assertPartKind(snapshot, s.speech_id, "speech", "move_speeches");
|
|
1016
1018
|
}
|
|
1019
|
+
validateMoveBgm(input, doc) {
|
|
1020
|
+
this.parse(moveBgmInputSchema, input, "move_bgm_invalid_input");
|
|
1021
|
+
if (doc.snapshot().tracks?.find((track) => track.parts_kind === "bgm")?.items?.[0] == null) throw new ValidationError("move_bgm_not_found", "document has no BGM item");
|
|
1022
|
+
}
|
|
1017
1023
|
validateSetBgm(input, _doc) {
|
|
1018
1024
|
this.parse(setBgmInputSchema, input, "set_bgm_invalid_input");
|
|
1019
1025
|
}
|
|
@@ -1061,16 +1067,20 @@ var SchemaValidator = class {
|
|
|
1061
1067
|
for (const speech of assets.speeches) this.assertPartKind(snapshot, speech.speech_id, "speech", opName);
|
|
1062
1068
|
}
|
|
1063
1069
|
/**
|
|
1064
|
-
*
|
|
1065
|
-
*
|
|
1066
|
-
* by the projection and has no host to recover to (RFC 02 §4/§11.1), so the
|
|
1067
|
-
* write must be rejected rather than landing a dangling reference. (Restores
|
|
1068
|
-
* the dangling-reference guard the old `validateMaterializedPatch` carried;
|
|
1069
|
-
* ADR 0009.)
|
|
1070
|
+
* Anchored speeches may follow a video or BGM root. A null anchor is an
|
|
1071
|
+
* explicit absolute speech root. Missing and wrong-kind parents are rejected.
|
|
1070
1072
|
*/
|
|
1071
1073
|
assertAnchorsExist(assets, doc, opName) {
|
|
1072
1074
|
const snapshot = doc.snapshot();
|
|
1073
|
-
for (const speech of assets.speeches)
|
|
1075
|
+
for (const speech of assets.speeches) {
|
|
1076
|
+
if (speech.anchor_part_id == null) continue;
|
|
1077
|
+
const part = readPart(snapshot, speech.anchor_part_id);
|
|
1078
|
+
if (part == null) throw new ValidationError(`${opName}_anchor_part_not_found`, `part_library has no parent`, { part_id: speech.anchor_part_id });
|
|
1079
|
+
if (part.part_kind !== "video_clip" && part.part_kind !== "bgm") throw new ValidationError(`${opName}_anchor_wrong_part_kind`, `speech parent must be video_clip or bgm`, {
|
|
1080
|
+
part_id: speech.anchor_part_id,
|
|
1081
|
+
part_kind: part.part_kind
|
|
1082
|
+
});
|
|
1083
|
+
}
|
|
1074
1084
|
}
|
|
1075
1085
|
assertPartKind(snapshot, partId, kind, opName) {
|
|
1076
1086
|
const part = readPart(snapshot, partId);
|
|
@@ -1419,8 +1429,9 @@ var SemanticEditor = class {
|
|
|
1419
1429
|
*/
|
|
1420
1430
|
async addSpeeches(input, options) {
|
|
1421
1431
|
this.validator.validateAddSpeeches(input, this.doc);
|
|
1432
|
+
const absByPartId = solveVideoDocument(this.doc.snapshot()).absByPartId;
|
|
1422
1433
|
this.doc.transact((draft) => {
|
|
1423
|
-
writeSpeechAssets(draft, input);
|
|
1434
|
+
writeSpeechAssets(draft, input, absByPartId);
|
|
1424
1435
|
}, audit("AddSpeeches", input, options));
|
|
1425
1436
|
}
|
|
1426
1437
|
/** Delete speeches with their captions; the subtree is removed (§9.2). Surviving lanes keep their facts. */
|
|
@@ -1438,29 +1449,60 @@ var SemanticEditor = class {
|
|
|
1438
1449
|
*/
|
|
1439
1450
|
async moveSpeeches(input, options) {
|
|
1440
1451
|
this.validator.validateMoveSpeeches(input, this.doc);
|
|
1452
|
+
const snapshot = this.doc.snapshot();
|
|
1453
|
+
const absByPartId = solveVideoDocument(snapshot).absByPartId;
|
|
1441
1454
|
this.doc.transact((draft) => {
|
|
1442
|
-
const ranges = mainTrackRanges(draft);
|
|
1443
1455
|
const track = findLaneTrack(draft, "speech");
|
|
1456
|
+
const ranges = mainTrackRanges(draft);
|
|
1444
1457
|
for (const move of input.speeches) {
|
|
1445
1458
|
const item = (track?.items ?? []).find((it) => it?.part_id === move.speech_id);
|
|
1446
1459
|
if (item == null) continue;
|
|
1447
|
-
|
|
1460
|
+
const current = item.time_position;
|
|
1461
|
+
if (current?.mode === "anchored") {
|
|
1462
|
+
const parentStartMs = absByPartId.get(current.anchorPartId);
|
|
1463
|
+
item.time_position = readPart(snapshot, current.anchorPartId)?.part_kind === "video_clip" ? relativePositionForAbs(ranges, move.new_start_ms) : parentStartMs == null ? {
|
|
1464
|
+
mode: "absolute",
|
|
1465
|
+
offsetMs: move.new_start_ms
|
|
1466
|
+
} : {
|
|
1467
|
+
mode: "anchored",
|
|
1468
|
+
anchorPartId: current.anchorPartId,
|
|
1469
|
+
offsetMs: Math.max(0, move.new_start_ms - parentStartMs)
|
|
1470
|
+
};
|
|
1471
|
+
} else item.time_position = {
|
|
1472
|
+
mode: "absolute",
|
|
1473
|
+
offsetMs: move.new_start_ms
|
|
1474
|
+
};
|
|
1448
1475
|
item.fallback_abs_ms = move.new_start_ms;
|
|
1449
1476
|
}
|
|
1450
1477
|
}, audit("MoveSpeeches", input, options));
|
|
1451
1478
|
}
|
|
1479
|
+
/** Move the BGM root; any speech/caption descendants follow through their anchors. */
|
|
1480
|
+
async moveBgm(input, options) {
|
|
1481
|
+
this.validator.validateMoveBgm(input, this.doc);
|
|
1482
|
+
this.doc.transact((draft) => {
|
|
1483
|
+
const item = findLaneTrack(draft, "bgm")?.items?.[0];
|
|
1484
|
+
if (item == null) return;
|
|
1485
|
+
item.time_position = {
|
|
1486
|
+
mode: "absolute",
|
|
1487
|
+
offsetMs: input.new_start_ms
|
|
1488
|
+
};
|
|
1489
|
+
item.fallback_abs_ms = void 0;
|
|
1490
|
+
}, audit("MoveBgm", input, options));
|
|
1491
|
+
}
|
|
1452
1492
|
/** Change a speech's script. Re-TTS runs upstream; the regenerated parts arrive materialized (no cascade). */
|
|
1453
1493
|
async changeSpeechScript(input, options) {
|
|
1454
1494
|
this.validator.validateChangeSpeechScript(input, this.doc);
|
|
1495
|
+
const absByPartId = solveVideoDocument(this.doc.snapshot()).absByPartId;
|
|
1455
1496
|
this.doc.transact((draft) => {
|
|
1456
|
-
writeSpeechAssets(draft, input);
|
|
1497
|
+
writeSpeechAssets(draft, input, absByPartId);
|
|
1457
1498
|
}, audit("ChangeSpeechScript", input, options));
|
|
1458
1499
|
}
|
|
1459
1500
|
/** Change a speech's voice. Re-TTS runs upstream; the regenerated parts arrive materialized (no cascade). */
|
|
1460
1501
|
async changeSpeechVoice(input, options) {
|
|
1461
1502
|
this.validator.validateChangeSpeechVoice(input, this.doc);
|
|
1503
|
+
const absByPartId = solveVideoDocument(this.doc.snapshot()).absByPartId;
|
|
1462
1504
|
this.doc.transact((draft) => {
|
|
1463
|
-
writeSpeechAssets(draft, input);
|
|
1505
|
+
writeSpeechAssets(draft, input, absByPartId);
|
|
1464
1506
|
}, audit("ChangeSpeechVoice", input, options));
|
|
1465
1507
|
}
|
|
1466
1508
|
/**
|
|
@@ -1699,7 +1741,7 @@ function normalizeSpeedShift(input) {
|
|
|
1699
1741
|
* projection derives it on read. On re-TTS the speech id is preserved, so an
|
|
1700
1742
|
* existing item is re-positioned rather than duplicated.
|
|
1701
1743
|
*/
|
|
1702
|
-
function writeSpeechAssets(draft, assets) {
|
|
1744
|
+
function writeSpeechAssets(draft, assets, absByPartId) {
|
|
1703
1745
|
const captionById = new Map(assets.captions.map((c) => [c.caption_id, c]));
|
|
1704
1746
|
const speechTrack = ensureLaneTrack(draft, "speech");
|
|
1705
1747
|
speechTrack.items ??= [];
|
|
@@ -1712,8 +1754,6 @@ function writeSpeechAssets(draft, assets) {
|
|
|
1712
1754
|
}
|
|
1713
1755
|
return captionItems;
|
|
1714
1756
|
};
|
|
1715
|
-
const ranges = mainTrackRanges(draft);
|
|
1716
|
-
const hostStartMs = (anchorPartId) => ranges.find((r) => r.partId === anchorPartId)?.startMs;
|
|
1717
1757
|
for (const speech of assets.speeches) {
|
|
1718
1758
|
const priorCaptionIds = draft.part_library?.[speech.speech_id]?.speech?.caption_ids ?? [];
|
|
1719
1759
|
const nextCaptionIds = new Set(speech.caption_ids);
|
|
@@ -1737,9 +1777,10 @@ function writeSpeechAssets(draft, assets) {
|
|
|
1737
1777
|
voice: speech.voice,
|
|
1738
1778
|
caption_ids: speech.caption_ids
|
|
1739
1779
|
} });
|
|
1740
|
-
const hostStart =
|
|
1741
|
-
const speechAbs = hostStart == null ? void 0 : hostStart + speech.offset_ms;
|
|
1742
|
-
|
|
1780
|
+
const hostStart = speech.anchor_part_id == null ? void 0 : absByPartId.get(speech.anchor_part_id);
|
|
1781
|
+
const speechAbs = speech.anchor_part_id == null ? speech.offset_ms : hostStart == null ? void 0 : hostStart + speech.offset_ms;
|
|
1782
|
+
if (speech.anchor_part_id == null) placeAbsolute(speechTrack.items, speech.speech_id, speech.offset_ms);
|
|
1783
|
+
else placeRelative(speechTrack.items, speech.speech_id, speech.anchor_part_id, speech.offset_ms, speechAbs);
|
|
1743
1784
|
for (const captionId of speech.caption_ids) {
|
|
1744
1785
|
const caption = captionById.get(captionId);
|
|
1745
1786
|
if (caption == null) continue;
|
|
@@ -1785,6 +1826,24 @@ function placeRelative(items, partId, anchorPartId, offsetMs, fallbackAbsMs) {
|
|
|
1785
1826
|
duration_override_ms: void 0
|
|
1786
1827
|
});
|
|
1787
1828
|
}
|
|
1829
|
+
function placeAbsolute(items, partId, startMs) {
|
|
1830
|
+
const timePosition = {
|
|
1831
|
+
mode: "absolute",
|
|
1832
|
+
offsetMs: Math.max(0, Math.round(startMs))
|
|
1833
|
+
};
|
|
1834
|
+
const existing = items.find((item) => item?.part_id === partId);
|
|
1835
|
+
if (existing != null) {
|
|
1836
|
+
existing.time_position = timePosition;
|
|
1837
|
+
existing.fallback_abs_ms = void 0;
|
|
1838
|
+
return;
|
|
1839
|
+
}
|
|
1840
|
+
items.push({
|
|
1841
|
+
part_id: partId,
|
|
1842
|
+
time_position: timePosition,
|
|
1843
|
+
fallback_abs_ms: void 0,
|
|
1844
|
+
duration_override_ms: void 0
|
|
1845
|
+
});
|
|
1846
|
+
}
|
|
1788
1847
|
/**
|
|
1789
1848
|
* After the main-track flow order changes (move / reorder), re-parent each
|
|
1790
1849
|
* anchored speech to the clip its *preserved* absolute landing now falls in
|
|
@@ -1923,6 +1982,7 @@ const IMPLEMENTED_SEMANTIC_OP_KINDS = [
|
|
|
1923
1982
|
"AddSpeeches",
|
|
1924
1983
|
"DeleteSpeeches",
|
|
1925
1984
|
"MoveSpeeches",
|
|
1985
|
+
"MoveBgm",
|
|
1926
1986
|
"ChangeSpeechScript",
|
|
1927
1987
|
"ChangeSpeechVoice",
|
|
1928
1988
|
"AdjustSpeechVolume",
|
|
@@ -2812,4 +2872,4 @@ function serverCovers(serverVV, target) {
|
|
|
2812
2872
|
return covers(server, target);
|
|
2813
2873
|
}
|
|
2814
2874
|
//#endregion
|
|
2815
|
-
export { DEFAULT_UNIT_TIME_MS, IMPLEMENTED_SEMANTIC_OP_KINDS, LANE_KINDS_IN_STACK_ORDER, ManualSyncDoc, MedeoHttpDocStorage, MemoryDocStorage, MengineAckFailedError, MengineDocSession, MengineHttpClient, MengineHttpRequestError, MenginePushRejectedError, MirrorVideoDocumentAdapter, SchemaValidator, SemanticEditor, TIMELINE_SKELETON_DURATION_MS, VIDEO_DOCUMENT_SCHEMA_VERSION, ValidationError, VideoDocumentValidationError, arrangeMainTrackSeamlessly, assertValidVideoDocument, base64ToBytes, buildInitialVideoDocument, buildSpeechHostMap, bytesToBase64, cascadeAfterVideoClipChanges, createMirrorVideoDocument, createMirrorVideoDocumentAdapter, decodeDocVersionMark, derivePositionFromAbs, encodeDocVersionMark, ensureLaneTrack, fillMainTrackTimeGaps, findLaneTrack, fromVideoDocument, generatePartId, getAt, hostForAbsMs, isEmptyVideoClip, isImplementedSemanticOpKind, isMap, laneTrackId, mainTrackRanges, partDurationMs, partUnionSchema, readMainTrackItems, readMengineEventStream, readPart, readPartDurationMs, readVideoDocumentFromDraft, reassignSpeechesToVideoClipsByTime, recalculateTimelineDuration, relativePositionForAbs, resolveAllSpeechOverlaps, resolveSpeechOverlapByShiftingVideos, safeDurationMs, schemas_exports as schemas, snapshotToPlain, solveVideoDocument, syncAggregatedClipsTimePosition, toVideoDocument, validateVideoDocument, videoDocumentMirrorSchema, videoDocumentSchema };
|
|
2875
|
+
export { DEFAULT_UNIT_TIME_MS, IMPLEMENTED_SEMANTIC_OP_KINDS, LANE_KINDS_IN_STACK_ORDER, ManualSyncDoc, MedeoHttpDocStorage, MemoryDocStorage, MengineAckFailedError, MengineDocSession, MengineHttpClient, MengineHttpRequestError, MenginePushRejectedError, MirrorVideoDocumentAdapter, SchemaValidator, SemanticEditor, TIMELINE_SKELETON_DURATION_MS, VIDEO_DOCUMENT_SCHEMA_VERSION, ValidationError, VideoDocumentValidationError, arrangeMainTrackSeamlessly, assertValidVideoDocument, base64ToBytes, buildInitialVideoDocument, buildSpeechHostMap, bytesToBase64, cascadeAfterVideoClipChanges, createMirrorVideoDocument, createMirrorVideoDocumentAdapter, decodeDocVersionMark, derivePositionFromAbs, encodeDocVersionMark, ensureLaneTrack, fillMainTrackTimeGaps, findLaneTrack, fromVideoDocument, generatePartId, getAt, hostForAbsMs, isEmptyVideoClip, isImplementedSemanticOpKind, isMap, laneTrackId, mainTrackRanges, partDurationMs, partUnionSchema, readMainTrackItems, readMengineEventStream, readPart, readPartDurationMs, readVideoDocumentFromDraft, reassignSpeechesToVideoClipsByTime, recalculateTimelineDuration, recoverOrphanedSpeechAnchorsByTime, relativePositionForAbs, resolveAllSpeechOverlaps, resolveAnchoredClipsTimePosition, resolveSpeechOverlapByShiftingVideos, safeDurationMs, schemas_exports as schemas, snapshotToPlain, solveVideoDocument, syncAggregatedClipsTimePosition, toVideoDocument, validateVideoDocument, videoDocumentMirrorSchema, videoDocumentSchema };
|
package/dist/testing.d.ts
CHANGED
package/dist/testing.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { B as base64ToBytes, F as VIDEO_DOCUMENT_SCHEMA_VERSION, V as bytesToBase64, r as createMirrorVideoDocumentAdapter } from "./document-C5KDvMMR.js";
|
|
2
2
|
import { t as classifyUpdate } from "./loro-relay-doc-ssdYpuef.js";
|
|
3
3
|
import { LoroDoc, VersionVector, encodeFrontiers } from "loro-crdt";
|
|
4
4
|
//#region src/testing/in-memory-mengine-server.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mengine/medeo-client",
|
|
3
|
-
"version": "1.2.1-alpha.
|
|
3
|
+
"version": "1.2.1-alpha.2",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"loro-mirror": "^2.2.0",
|
|
27
27
|
"zod": "^4.4.3",
|
|
28
|
-
"@mengine/storage": "1.2.1-alpha.
|
|
29
|
-
"@mengine/sync": "1.2.1-alpha.
|
|
30
|
-
"@mengine/utils": "1.2.1-alpha.
|
|
28
|
+
"@mengine/storage": "1.2.1-alpha.2",
|
|
29
|
+
"@mengine/sync": "1.2.1-alpha.2",
|
|
30
|
+
"@mengine/utils": "1.2.1-alpha.2"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^25.9.1",
|