@mengine/medeo-client 1.1.0 → 1.2.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{document-C98vSu7J.js → document-49OdvviW.js} +146 -6
- package/dist/{index-BUKA3L7o.d.ts → index-BO2fSUKi.d.ts} +127 -608
- package/dist/index-InEWX9rk.d.ts +609 -0
- package/dist/index.d.ts +42 -34
- package/dist/index.js +148 -575
- package/dist/schemas.d.ts +2 -0
- package/dist/schemas.js +518 -0
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +40 -1
- package/package.json +7 -4
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Mirror, schema } from "loro-mirror";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { LoroDoc } from "loro-crdt";
|
|
4
|
+
import { produce } from "immer";
|
|
4
5
|
//#region src/client/base64.ts
|
|
5
6
|
function bytesToBase64(bytes) {
|
|
6
7
|
let binary = "";
|
|
@@ -230,6 +231,16 @@ function effectiveVideoClipDurationMs(clip) {
|
|
|
230
231
|
const effective = Math.round(sourceMs / speed);
|
|
231
232
|
return Number.isFinite(effective) && effective > 0 ? effective : 0;
|
|
232
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* The `timeline.unit_time_ms` the read-view states when the document states none.
|
|
236
|
+
*
|
|
237
|
+
* One frame at 30fps. A document legitimately has no such fact (display
|
|
238
|
+
* granularity is the editor's, not the timeline's), but the read-view IDL marks
|
|
239
|
+
* the field `@required`, so the projection has to name a value. This one is a
|
|
240
|
+
* usable granularity; `0` — which a downstream consumer had been defaulting to —
|
|
241
|
+
* is not, and passes any `>= 0` guard on its way to an editor that cannot use it.
|
|
242
|
+
*/
|
|
243
|
+
const DEFAULT_UNIT_TIME_MS = 33.333;
|
|
233
244
|
//#endregion
|
|
234
245
|
//#region src/timeline-core/types.ts
|
|
235
246
|
/** Total document duration when there is no real content (matches FE bgm fallback). */
|
|
@@ -1279,6 +1290,20 @@ function deriveItem(item, isMain, speechHost, partLibrary) {
|
|
|
1279
1290
|
* Project the authoritative `VideoDocument` back into the legacy `VideoDraft`
|
|
1280
1291
|
* read-view, solving each item's absolute position, the `part_aggregations`, and
|
|
1281
1292
|
* the total duration via the timeline-core cascade.
|
|
1293
|
+
*
|
|
1294
|
+
* The read-view is a compatibility contract, and the IDL declares
|
|
1295
|
+
* `Timeline.unit_time_ms` and `Track.is_hidden` `@required`
|
|
1296
|
+
* (`video_draft_comp.smithy`) — so a projection that omitted them handed every
|
|
1297
|
+
* consumer a payload that violated the shape it claims to satisfy, leaving each
|
|
1298
|
+
* one to invent its own default. Two already had, and they disagreed: the agent
|
|
1299
|
+
* harness used `unit_time_ms: 0`, director used a frame at 30fps. Defaulting here
|
|
1300
|
+
* makes the contract true at the one place that produces it.
|
|
1301
|
+
*
|
|
1302
|
+
* These two are defaultable because the projection knows their values on its own:
|
|
1303
|
+
* absent `unit_time_ms` means "no display granularity was ever stated" and absent
|
|
1304
|
+
* `is_hidden` means "this track was never hidden". `version` is NOT defaultable
|
|
1305
|
+
* here — its only honest value is server state (`update_seq`) that a
|
|
1306
|
+
* `VideoDocument` cannot see, so it stays absent and the HTTP layer fills it.
|
|
1282
1307
|
*/
|
|
1283
1308
|
function fromVideoDocument(document) {
|
|
1284
1309
|
assertValidVideoDocument(document);
|
|
@@ -1294,7 +1319,7 @@ function fromVideoDocument(document) {
|
|
|
1294
1319
|
thumbnail_storage_key: document.meta.thumbnail_storage_key,
|
|
1295
1320
|
timeline: {
|
|
1296
1321
|
duration_ms: view.durationMs,
|
|
1297
|
-
unit_time_ms: document.timeline?.unit_time_ms
|
|
1322
|
+
unit_time_ms: document.timeline?.unit_time_ms ?? 33.333
|
|
1298
1323
|
},
|
|
1299
1324
|
video_creation_settings: clone(document.meta.video_creation_settings),
|
|
1300
1325
|
chat_session_id: document.meta.chat_session_id,
|
|
@@ -1390,7 +1415,7 @@ function draftTrack(track, absByPartId) {
|
|
|
1390
1415
|
return {
|
|
1391
1416
|
id: track.id,
|
|
1392
1417
|
parts_kind: track.parts_kind,
|
|
1393
|
-
is_hidden: track.is_hidden,
|
|
1418
|
+
is_hidden: track.is_hidden ?? false,
|
|
1394
1419
|
items: (track.items ?? []).map((item) => ({
|
|
1395
1420
|
part_id: item.part_id,
|
|
1396
1421
|
abs_time_position: absByPartId.get(item.part_id) ?? 0
|
|
@@ -1588,7 +1613,7 @@ function createMirrorVideoDocument(document, options = {}) {
|
|
|
1588
1613
|
doc,
|
|
1589
1614
|
schema: videoDocumentMirrorSchema
|
|
1590
1615
|
}).setState((draft) => {
|
|
1591
|
-
|
|
1616
|
+
writeVideoDocumentToDraft(draft, document);
|
|
1592
1617
|
}, { origin: options.origin ?? "mengine.bootstrap" });
|
|
1593
1618
|
return doc;
|
|
1594
1619
|
}
|
|
@@ -1596,8 +1621,8 @@ function createMirrorVideoDocument(document, options = {}) {
|
|
|
1596
1621
|
function createMirrorVideoDocumentAdapter(document, options) {
|
|
1597
1622
|
return new MirrorVideoDocumentAdapter(createMirrorVideoDocument(document, options));
|
|
1598
1623
|
}
|
|
1599
|
-
/** Write a whole `VideoDocument` into
|
|
1600
|
-
function
|
|
1624
|
+
/** Write a whole `VideoDocument` into a draft (seed a fresh doc / plain-memory state). */
|
|
1625
|
+
function writeVideoDocumentToDraft(draft, document) {
|
|
1601
1626
|
draft.meta = {
|
|
1602
1627
|
schema_version: document.meta.schema_version,
|
|
1603
1628
|
draft_id: document.meta.draft_id ?? void 0,
|
|
@@ -1627,4 +1652,119 @@ function toTrackRow(track) {
|
|
|
1627
1652
|
};
|
|
1628
1653
|
}
|
|
1629
1654
|
//#endregion
|
|
1630
|
-
|
|
1655
|
+
//#region src/editor/id-gen.ts
|
|
1656
|
+
/**
|
|
1657
|
+
* Part-id generation, aligned with the online ecosystem.
|
|
1658
|
+
*
|
|
1659
|
+
* The authoritative online producers — agent-harness (`@harness/shared`
|
|
1660
|
+
* `genObjId`) and director.v2 (`common/obj_id.py` `gen_obj_id`) — both mint part
|
|
1661
|
+
* ids as `` `${prefix}_${ulid()}` ``, and real captured drafts use exactly that
|
|
1662
|
+
* shape (`clip_…` / `spe_…` / `cap_…` / `bgm_…`, each a 26-char ULID). The engine
|
|
1663
|
+
* previously emitted `vc_<base36 timestamp><6 random>`, a different prefix AND a
|
|
1664
|
+
* different encoding — the sole cross-repo id divergence. This module removes it
|
|
1665
|
+
* by emitting the same `<prefix>_<ULID>` bytes.
|
|
1666
|
+
*
|
|
1667
|
+
* The ULID is generated inline (Crockford Base32, 48-bit time + 80-bit random)
|
|
1668
|
+
* rather than pulling the `ulid` npm package: the randomness class matches the
|
|
1669
|
+
* old generator (both `Math.random`-based) and it keeps `@mengine/medeo-client`
|
|
1670
|
+
* dependency-free for a purely mechanical id string. Part ids only need to be
|
|
1671
|
+
* unique and lexicographically time-sortable, which this satisfies.
|
|
1672
|
+
*/
|
|
1673
|
+
/** Crockford Base32 alphabet (no I, L, O, U), per the ULID spec. */
|
|
1674
|
+
const CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
1675
|
+
const TIME_LEN = 10;
|
|
1676
|
+
const RANDOM_LEN = 16;
|
|
1677
|
+
function encodeTime(now) {
|
|
1678
|
+
let out = "";
|
|
1679
|
+
let ms = now;
|
|
1680
|
+
for (let i = TIME_LEN - 1; i >= 0; i--) {
|
|
1681
|
+
const mod = ms % 32;
|
|
1682
|
+
out = CROCKFORD[mod] + out;
|
|
1683
|
+
ms = (ms - mod) / 32;
|
|
1684
|
+
}
|
|
1685
|
+
return out;
|
|
1686
|
+
}
|
|
1687
|
+
function encodeRandom() {
|
|
1688
|
+
let out = "";
|
|
1689
|
+
for (let i = 0; i < RANDOM_LEN; i++) out += CROCKFORD[Math.floor(Math.random() * 32)];
|
|
1690
|
+
return out;
|
|
1691
|
+
}
|
|
1692
|
+
/** A 26-char Crockford Base32 ULID (10-char time + 16-char random). */
|
|
1693
|
+
function ulid() {
|
|
1694
|
+
return encodeTime(Date.now()) + encodeRandom();
|
|
1695
|
+
}
|
|
1696
|
+
function generatePartId(prefix) {
|
|
1697
|
+
return `${prefix}_${ulid()}`;
|
|
1698
|
+
}
|
|
1699
|
+
//#endregion
|
|
1700
|
+
//#region src/document/plain-memory-adapter.ts
|
|
1701
|
+
/**
|
|
1702
|
+
* Pure in-memory `SemanticDocumentAdapter` — no Loro/WASM. Holds a
|
|
1703
|
+
* `VideoDocumentDraft` object and applies each `transact` via immer `produce`,
|
|
1704
|
+
* journaling every audit that actually mutated state (plus any ids minted
|
|
1705
|
+
* during that transact).
|
|
1706
|
+
*
|
|
1707
|
+
* Known benign difference vs `MirrorVideoDocumentAdapter`: the editor's
|
|
1708
|
+
* `setPart` assigns a fresh part object on every call, so a same-value rewrite
|
|
1709
|
+
* produces a new immer state and IS journaled here, while the mirror's deep
|
|
1710
|
+
* diff emits no commit. Terminal `snapshot()` stays equal; replay is idempotent.
|
|
1711
|
+
*/
|
|
1712
|
+
var PlainMemoryAdapter = class {
|
|
1713
|
+
state;
|
|
1714
|
+
_journal = [];
|
|
1715
|
+
baseIdFactory;
|
|
1716
|
+
/** Non-null only while a `transact` edit callback is running. */
|
|
1717
|
+
pendingIds = null;
|
|
1718
|
+
/**
|
|
1719
|
+
* Recording wrapper around the underlying factory. Callers (sandbox editor)
|
|
1720
|
+
* use this so every minted id is appended to the current transact's list.
|
|
1721
|
+
*/
|
|
1722
|
+
idFactory;
|
|
1723
|
+
constructor(document, options) {
|
|
1724
|
+
assertValidVideoDocument(document);
|
|
1725
|
+
const draft = {};
|
|
1726
|
+
writeVideoDocumentToDraft(draft, document);
|
|
1727
|
+
this.state = draft;
|
|
1728
|
+
this.baseIdFactory = options?.idFactory ?? generatePartId;
|
|
1729
|
+
this.idFactory = (prefix) => {
|
|
1730
|
+
const id = this.baseIdFactory(prefix);
|
|
1731
|
+
if (this.pendingIds != null) this.pendingIds.push(id);
|
|
1732
|
+
return id;
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
get journal() {
|
|
1736
|
+
return this._journal;
|
|
1737
|
+
}
|
|
1738
|
+
hasContent() {
|
|
1739
|
+
return (this.state.meta?.schema_version ?? "") !== "";
|
|
1740
|
+
}
|
|
1741
|
+
snapshot() {
|
|
1742
|
+
return readVideoDocumentFromDraft(this.state);
|
|
1743
|
+
}
|
|
1744
|
+
/**
|
|
1745
|
+
* Apply one op via immer. A throw in `edit` discards the draft (state and
|
|
1746
|
+
* journal unchanged). When `produce` returns the same reference, there was
|
|
1747
|
+
* no structural change — skip journal, matching mirror "no change, no commit".
|
|
1748
|
+
*/
|
|
1749
|
+
transact(edit, audit) {
|
|
1750
|
+
this.pendingIds = [];
|
|
1751
|
+
try {
|
|
1752
|
+
const next = produce(this.state, edit);
|
|
1753
|
+
if (next !== this.state) {
|
|
1754
|
+
this.state = next;
|
|
1755
|
+
this._journal.push({
|
|
1756
|
+
...audit,
|
|
1757
|
+
generated_ids: this.pendingIds
|
|
1758
|
+
});
|
|
1759
|
+
}
|
|
1760
|
+
} finally {
|
|
1761
|
+
this.pendingIds = null;
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
};
|
|
1765
|
+
/** Build a `PlainMemoryAdapter` seeded with `document`. */
|
|
1766
|
+
function createPlainMemoryAdapter(document, options) {
|
|
1767
|
+
return new PlainMemoryAdapter(document, options);
|
|
1768
|
+
}
|
|
1769
|
+
//#endregion
|
|
1770
|
+
export { resolveSpeechOverlapByShiftingVideos as A, partUnionToDraft as B, solveVideoDocument as C, reassignSpeechesToVideoClipsByTime as D, fillMainTrackTimeGaps as E, safeDurationMs as F, videoDocumentMirrorSchema as H, DEFAULT_UNIT_TIME_MS as I, VIDEO_DOCUMENT_SCHEMA_VERSION as L, TIMELINE_SKELETON_DURATION_MS as M, isEmptyVideoClip as N, recalculateTimelineDuration as O, partDurationMs as P, effectiveVideoClipDurationMs as R, laneTrackId as S, arrangeMainTrackSeamlessly as T, base64ToBytes as U, recordEntries as V, bytesToBase64 as W, partUnionSchema as _, createMirrorVideoDocument as a, ensureLaneTrack as b, readVideoDocumentFromDraft as c, derivePositionFromAbs as d, fromVideoDocument as f, validateVideoDocument as g, assertValidVideoDocument as h, MirrorVideoDocumentAdapter as i, syncAggregatedClipsTimePosition as j, resolveAllSpeechOverlaps as k, buildInitialVideoDocument as l, VideoDocumentValidationError as m, createPlainMemoryAdapter as n, createMirrorVideoDocumentAdapter as o, toVideoDocument as p, generatePartId as r, writeVideoDocumentToDraft as s, PlainMemoryAdapter as t, buildSpeechHostMap as u, videoDocumentSchema as v, cascadeAfterVideoClipChanges as w, findLaneTrack as x, LANE_KINDS_IN_STACK_ORDER as y, speedOf as z };
|