@mengine/medeo-client 1.2.1-alpha.2 → 1.2.1-alpha.4
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-C5KDvMMR.js → document-C-h5-rCG.js} +128 -24
- package/dist/index-CiDSfSMi.d.ts +613 -0
- package/dist/{index-DrEHTguy.d.ts → index-DkJ0JK2t.d.ts} +107 -630
- package/dist/index.d.ts +38 -25
- package/dist/index.js +151 -609
- package/dist/schemas.d.ts +2 -0
- package/dist/schemas.js +541 -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 = "";
|
|
@@ -87,8 +88,7 @@ function jsonTransform() {
|
|
|
87
88
|
const trackItem = schema.LoroMap({
|
|
88
89
|
part_id: schema.String(),
|
|
89
90
|
time_position: schema.String().transform(jsonTransform()),
|
|
90
|
-
fallback_abs_ms: schema.Number({ required: false })
|
|
91
|
-
duration_override_ms: schema.Number({ required: false })
|
|
91
|
+
fallback_abs_ms: schema.Number({ required: false })
|
|
92
92
|
});
|
|
93
93
|
const track = schema.LoroMap({
|
|
94
94
|
id: schema.String(),
|
|
@@ -111,7 +111,7 @@ const speechPart = schema.LoroMap({
|
|
|
111
111
|
volume: schema.Number(),
|
|
112
112
|
audio_storage_key: schema.String(),
|
|
113
113
|
origin_speech_id: schema.String(),
|
|
114
|
-
voice: schema.String().transform(jsonTransform()),
|
|
114
|
+
voice: schema.String({ required: false }).transform(jsonTransform()),
|
|
115
115
|
caption_ids: schema.String().transform(jsonTransform())
|
|
116
116
|
}, { required: false });
|
|
117
117
|
const captionStyle = schema.LoroMap({
|
|
@@ -838,8 +838,7 @@ const trackItemTimePositionSchema = z.discriminatedUnion("mode", [
|
|
|
838
838
|
const trackItemSchema = z.object({
|
|
839
839
|
part_id: z.string(),
|
|
840
840
|
time_position: trackItemTimePositionSchema,
|
|
841
|
-
fallback_abs_ms: finiteNumber.optional()
|
|
842
|
-
duration_override_ms: z.number().finite().positive().optional()
|
|
841
|
+
fallback_abs_ms: finiteNumber.optional()
|
|
843
842
|
}).passthrough();
|
|
844
843
|
const trackSchema = z.object({
|
|
845
844
|
id: z.string().optional(),
|
|
@@ -871,7 +870,7 @@ const speechPartSchema = z.object({
|
|
|
871
870
|
volume: finiteNumber,
|
|
872
871
|
audio_storage_key: z.string(),
|
|
873
872
|
origin_speech_id: z.string(),
|
|
874
|
-
voice: z.unknown(),
|
|
873
|
+
voice: z.unknown().optional(),
|
|
875
874
|
caption_ids: z.array(z.string())
|
|
876
875
|
}).passthrough();
|
|
877
876
|
const captionPartSchema = z.object({
|
|
@@ -1392,7 +1391,7 @@ function fromVideoDocument(document) {
|
|
|
1392
1391
|
above_main_tracks: aboveTracks.map((t) => draftTrack(t, view.absByPartId)),
|
|
1393
1392
|
below_main_tracks: belowTracks.map((t) => draftTrack(t, view.absByPartId)),
|
|
1394
1393
|
part_aggregations: view.aggregations,
|
|
1395
|
-
part_library: toReadViewPartLibrary(view.partLibrary, view.durationMs, view.derivedFillerPartIds
|
|
1394
|
+
part_library: toReadViewPartLibrary(view.partLibrary, view.durationMs, view.derivedFillerPartIds),
|
|
1396
1395
|
version: document.meta.version
|
|
1397
1396
|
};
|
|
1398
1397
|
}
|
|
@@ -1413,7 +1412,7 @@ function fromVideoDocument(document) {
|
|
|
1413
1412
|
* and they are not part of the document (see `fromVideoDocument`). An empty clip a
|
|
1414
1413
|
* writer placed deliberately is NOT in that set and is projected normally.
|
|
1415
1414
|
*/
|
|
1416
|
-
function toReadViewPartLibrary(partLibrary, durationMs, derivedFillerPartIds
|
|
1415
|
+
function toReadViewPartLibrary(partLibrary, durationMs, derivedFillerPartIds) {
|
|
1417
1416
|
const out = {};
|
|
1418
1417
|
for (const [partId, part] of Object.entries(partLibrary)) {
|
|
1419
1418
|
if (derivedFillerPartIds.has(partId)) continue;
|
|
@@ -1433,7 +1432,7 @@ function toReadViewPartLibrary(partLibrary, durationMs, derivedFillerPartIds, ca
|
|
|
1433
1432
|
const caption = clone(part.caption);
|
|
1434
1433
|
out[partId] = { caption: {
|
|
1435
1434
|
...caption,
|
|
1436
|
-
duration_ms:
|
|
1435
|
+
duration_ms: caption.initial_duration_ms
|
|
1437
1436
|
} };
|
|
1438
1437
|
} else if (part.bgm != null) out[partId] = { bgm: {
|
|
1439
1438
|
...clone(part.bgm),
|
|
@@ -1442,14 +1441,6 @@ function toReadViewPartLibrary(partLibrary, durationMs, derivedFillerPartIds, ca
|
|
|
1442
1441
|
}
|
|
1443
1442
|
return out;
|
|
1444
1443
|
}
|
|
1445
|
-
function captionDurationOverrides(document) {
|
|
1446
|
-
const result = /* @__PURE__ */ new Map();
|
|
1447
|
-
for (const track of document.tracks ?? []) {
|
|
1448
|
-
if (track.parts_kind !== "caption") continue;
|
|
1449
|
-
for (const item of track.items ?? []) if (item.duration_override_ms != null) result.set(item.part_id, item.duration_override_ms);
|
|
1450
|
-
}
|
|
1451
|
-
return result;
|
|
1452
|
-
}
|
|
1453
1444
|
/** Deep-clone a part payload, dropping the derived read-view `duration_ms`. */
|
|
1454
1445
|
function omitDurationMs(part) {
|
|
1455
1446
|
const { duration_ms: _drop, ...rest } = clone(part);
|
|
@@ -1612,7 +1603,6 @@ function rowToTrack(row) {
|
|
|
1612
1603
|
time_position: item.time_position
|
|
1613
1604
|
};
|
|
1614
1605
|
if (item.fallback_abs_ms != null) result.fallback_abs_ms = item.fallback_abs_ms;
|
|
1615
|
-
if (item.duration_override_ms != null) result.duration_override_ms = item.duration_override_ms;
|
|
1616
1606
|
return result;
|
|
1617
1607
|
})
|
|
1618
1608
|
};
|
|
@@ -1687,7 +1677,7 @@ function createMirrorVideoDocument(document, options = {}) {
|
|
|
1687
1677
|
doc,
|
|
1688
1678
|
schema: videoDocumentMirrorSchema
|
|
1689
1679
|
}).setState((draft) => {
|
|
1690
|
-
|
|
1680
|
+
writeVideoDocumentToDraft(draft, document);
|
|
1691
1681
|
}, { origin: options.origin ?? "mengine.bootstrap" });
|
|
1692
1682
|
return doc;
|
|
1693
1683
|
}
|
|
@@ -1695,8 +1685,8 @@ function createMirrorVideoDocument(document, options = {}) {
|
|
|
1695
1685
|
function createMirrorVideoDocumentAdapter(document, options) {
|
|
1696
1686
|
return new MirrorVideoDocumentAdapter(createMirrorVideoDocument(document, options));
|
|
1697
1687
|
}
|
|
1698
|
-
/** Write a whole `VideoDocument` into
|
|
1699
|
-
function
|
|
1688
|
+
/** Write a whole `VideoDocument` into a draft (seed a fresh doc / plain-memory state). */
|
|
1689
|
+
function writeVideoDocumentToDraft(draft, document) {
|
|
1700
1690
|
draft.meta = {
|
|
1701
1691
|
schema_version: document.meta.schema_version,
|
|
1702
1692
|
draft_id: document.meta.draft_id ?? void 0,
|
|
@@ -1721,10 +1711,124 @@ function toTrackRow(track) {
|
|
|
1721
1711
|
items: (track.items ?? []).map((item) => ({
|
|
1722
1712
|
part_id: item.part_id,
|
|
1723
1713
|
time_position: item.time_position,
|
|
1724
|
-
fallback_abs_ms: item.fallback_abs_ms
|
|
1725
|
-
duration_override_ms: item.duration_override_ms
|
|
1714
|
+
fallback_abs_ms: item.fallback_abs_ms
|
|
1726
1715
|
}))
|
|
1727
1716
|
};
|
|
1728
1717
|
}
|
|
1729
1718
|
//#endregion
|
|
1730
|
-
|
|
1719
|
+
//#region src/editor/id-gen.ts
|
|
1720
|
+
/**
|
|
1721
|
+
* Part-id generation, aligned with the online ecosystem.
|
|
1722
|
+
*
|
|
1723
|
+
* The authoritative online producers — agent-harness (`@harness/shared`
|
|
1724
|
+
* `genObjId`) and director.v2 (`common/obj_id.py` `gen_obj_id`) — both mint part
|
|
1725
|
+
* ids as `` `${prefix}_${ulid()}` ``, and real captured drafts use exactly that
|
|
1726
|
+
* shape (`clip_…` / `spe_…` / `cap_…` / `bgm_…`, each a 26-char ULID). The engine
|
|
1727
|
+
* previously emitted `vc_<base36 timestamp><6 random>`, a different prefix AND a
|
|
1728
|
+
* different encoding — the sole cross-repo id divergence. This module removes it
|
|
1729
|
+
* by emitting the same `<prefix>_<ULID>` bytes.
|
|
1730
|
+
*
|
|
1731
|
+
* The ULID is generated inline (Crockford Base32, 48-bit time + 80-bit random)
|
|
1732
|
+
* rather than pulling the `ulid` npm package: the randomness class matches the
|
|
1733
|
+
* old generator (both `Math.random`-based) and it keeps `@mengine/medeo-client`
|
|
1734
|
+
* dependency-free for a purely mechanical id string. Part ids only need to be
|
|
1735
|
+
* unique and lexicographically time-sortable, which this satisfies.
|
|
1736
|
+
*/
|
|
1737
|
+
/** Crockford Base32 alphabet (no I, L, O, U), per the ULID spec. */
|
|
1738
|
+
const CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
1739
|
+
const TIME_LEN = 10;
|
|
1740
|
+
const RANDOM_LEN = 16;
|
|
1741
|
+
function encodeTime(now) {
|
|
1742
|
+
let out = "";
|
|
1743
|
+
let ms = now;
|
|
1744
|
+
for (let i = TIME_LEN - 1; i >= 0; i--) {
|
|
1745
|
+
const mod = ms % 32;
|
|
1746
|
+
out = CROCKFORD[mod] + out;
|
|
1747
|
+
ms = (ms - mod) / 32;
|
|
1748
|
+
}
|
|
1749
|
+
return out;
|
|
1750
|
+
}
|
|
1751
|
+
function encodeRandom() {
|
|
1752
|
+
let out = "";
|
|
1753
|
+
for (let i = 0; i < RANDOM_LEN; i++) out += CROCKFORD[Math.floor(Math.random() * 32)];
|
|
1754
|
+
return out;
|
|
1755
|
+
}
|
|
1756
|
+
/** A 26-char Crockford Base32 ULID (10-char time + 16-char random). */
|
|
1757
|
+
function ulid() {
|
|
1758
|
+
return encodeTime(Date.now()) + encodeRandom();
|
|
1759
|
+
}
|
|
1760
|
+
function generatePartId(prefix) {
|
|
1761
|
+
return `${prefix}_${ulid()}`;
|
|
1762
|
+
}
|
|
1763
|
+
//#endregion
|
|
1764
|
+
//#region src/document/plain-memory-adapter.ts
|
|
1765
|
+
/**
|
|
1766
|
+
* Pure in-memory `SemanticDocumentAdapter` — no Loro/WASM. Holds a
|
|
1767
|
+
* `VideoDocumentDraft` object and applies each `transact` via immer `produce`,
|
|
1768
|
+
* journaling every audit that actually mutated state (plus any ids minted
|
|
1769
|
+
* during that transact).
|
|
1770
|
+
*
|
|
1771
|
+
* Known benign difference vs `MirrorVideoDocumentAdapter`: the editor's
|
|
1772
|
+
* `setPart` assigns a fresh part object on every call, so a same-value rewrite
|
|
1773
|
+
* produces a new immer state and IS journaled here, while the mirror's deep
|
|
1774
|
+
* diff emits no commit. Terminal `snapshot()` stays equal; replay is idempotent.
|
|
1775
|
+
*/
|
|
1776
|
+
var PlainMemoryAdapter = class {
|
|
1777
|
+
state;
|
|
1778
|
+
_journal = [];
|
|
1779
|
+
baseIdFactory;
|
|
1780
|
+
/** Non-null only while a `transact` edit callback is running. */
|
|
1781
|
+
pendingIds = null;
|
|
1782
|
+
/**
|
|
1783
|
+
* Recording wrapper around the underlying factory. Callers (sandbox editor)
|
|
1784
|
+
* use this so every minted id is appended to the current transact's list.
|
|
1785
|
+
*/
|
|
1786
|
+
idFactory;
|
|
1787
|
+
constructor(document, options) {
|
|
1788
|
+
assertValidVideoDocument(document);
|
|
1789
|
+
const draft = {};
|
|
1790
|
+
writeVideoDocumentToDraft(draft, document);
|
|
1791
|
+
this.state = draft;
|
|
1792
|
+
this.baseIdFactory = options?.idFactory ?? generatePartId;
|
|
1793
|
+
this.idFactory = (prefix) => {
|
|
1794
|
+
const id = this.baseIdFactory(prefix);
|
|
1795
|
+
if (this.pendingIds != null) this.pendingIds.push(id);
|
|
1796
|
+
return id;
|
|
1797
|
+
};
|
|
1798
|
+
}
|
|
1799
|
+
get journal() {
|
|
1800
|
+
return this._journal;
|
|
1801
|
+
}
|
|
1802
|
+
hasContent() {
|
|
1803
|
+
return (this.state.meta?.schema_version ?? "") !== "";
|
|
1804
|
+
}
|
|
1805
|
+
snapshot() {
|
|
1806
|
+
return readVideoDocumentFromDraft(this.state);
|
|
1807
|
+
}
|
|
1808
|
+
/**
|
|
1809
|
+
* Apply one op via immer. A throw in `edit` discards the draft (state and
|
|
1810
|
+
* journal unchanged). When `produce` returns the same reference, there was
|
|
1811
|
+
* no structural change — skip journal, matching mirror "no change, no commit".
|
|
1812
|
+
*/
|
|
1813
|
+
transact(edit, audit) {
|
|
1814
|
+
this.pendingIds = [];
|
|
1815
|
+
try {
|
|
1816
|
+
const next = produce(this.state, edit);
|
|
1817
|
+
if (next !== this.state) {
|
|
1818
|
+
this.state = next;
|
|
1819
|
+
this._journal.push({
|
|
1820
|
+
...audit,
|
|
1821
|
+
generated_ids: this.pendingIds
|
|
1822
|
+
});
|
|
1823
|
+
}
|
|
1824
|
+
} finally {
|
|
1825
|
+
this.pendingIds = null;
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
};
|
|
1829
|
+
/** Build a `PlainMemoryAdapter` seeded with `document`. */
|
|
1830
|
+
function createPlainMemoryAdapter(document, options) {
|
|
1831
|
+
return new PlainMemoryAdapter(document, options);
|
|
1832
|
+
}
|
|
1833
|
+
//#endregion
|
|
1834
|
+
export { resolveAllSpeechOverlaps as A, effectiveVideoClipDurationMs as B, solveVideoDocument as C, reassignSpeechesToVideoClipsByTime as D, fillMainTrackTimeGaps as E, isEmptyVideoClip as F, base64ToBytes as G, partUnionToDraft as H, partDurationMs as I, bytesToBase64 as K, safeDurationMs as L, resolveSpeechOverlapByShiftingVideos as M, syncAggregatedClipsTimePosition as N, recalculateTimelineDuration as O, TIMELINE_SKELETON_DURATION_MS as P, DEFAULT_UNIT_TIME_MS as R, laneTrackId as S, arrangeMainTrackSeamlessly as T, recordEntries as U, speedOf as V, videoDocumentMirrorSchema 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, resolveAnchoredClipsTimePosition as j, recoverOrphanedSpeechAnchorsByTime 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, VIDEO_DOCUMENT_SCHEMA_VERSION as z };
|