@mengine/medeo-client 1.3.1-alpha.0 → 1.3.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.
- package/README.md +26 -11
- package/dist/{document-Caidonf8.js → document-BLYcbv8E.js} +378 -109
- package/dist/{index-WxMNUbOa.d.ts → index-Dxxu0Mi1.d.ts} +36 -9
- package/dist/index.d.ts +82 -16
- package/dist/index.js +199 -133
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/package.json +4 -4
|
@@ -1536,11 +1536,255 @@ function createId(value, label) {
|
|
|
1536
1536
|
return value;
|
|
1537
1537
|
}
|
|
1538
1538
|
//#endregion
|
|
1539
|
+
//#region ../medeo-dsl/src/json-values.ts
|
|
1540
|
+
const nativeObjectConstructorSource = Function.prototype.toString.call(Object);
|
|
1541
|
+
function isJsonObject(value) {
|
|
1542
|
+
return isJsonValue(value, /* @__PURE__ */ new Set()) && !Array.isArray(value) && value !== null;
|
|
1543
|
+
}
|
|
1544
|
+
function isJsonValue(value, ancestors) {
|
|
1545
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
1546
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
1547
|
+
if (typeof value !== "object") return false;
|
|
1548
|
+
if (!Array.isArray(value) && !isPlainObject(value)) return false;
|
|
1549
|
+
if (ancestors.has(value)) return false;
|
|
1550
|
+
ancestors.add(value);
|
|
1551
|
+
const valid = Array.isArray(value) ? value.every((item) => isJsonValue(item, ancestors)) : Object.values(value).every((item) => isJsonValue(item, ancestors));
|
|
1552
|
+
ancestors.delete(value);
|
|
1553
|
+
return valid;
|
|
1554
|
+
}
|
|
1555
|
+
/** Recognize an ordinary object from any VM realm without admitting class instances. */
|
|
1556
|
+
function isPlainObject(value) {
|
|
1557
|
+
try {
|
|
1558
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1559
|
+
if (prototype === null) return true;
|
|
1560
|
+
if (Object.getPrototypeOf(prototype) !== null) return false;
|
|
1561
|
+
const constructor = Object.getOwnPropertyDescriptor(prototype, "constructor")?.value;
|
|
1562
|
+
return typeof constructor === "function" && constructor.prototype === prototype && Function.prototype.toString.call(constructor) === nativeObjectConstructorSource;
|
|
1563
|
+
} catch {
|
|
1564
|
+
return false;
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
//#endregion
|
|
1568
|
+
//#region ../medeo-dsl/src/composition.ts
|
|
1569
|
+
/** Assembly fails explicitly when declared bases cannot provide valid, unambiguous content. */
|
|
1570
|
+
var ScriptCompositionError = class extends Error {
|
|
1571
|
+
code;
|
|
1572
|
+
entityId;
|
|
1573
|
+
constructor(code, entityId, message) {
|
|
1574
|
+
super(message);
|
|
1575
|
+
this.code = code;
|
|
1576
|
+
this.entityId = entityId;
|
|
1577
|
+
this.name = "ScriptCompositionError";
|
|
1578
|
+
}
|
|
1579
|
+
};
|
|
1580
|
+
/** Direct base IDs are structural fields, never ordinary Relation endpoints. */
|
|
1581
|
+
function variantBaseEntityIds(entity) {
|
|
1582
|
+
if (!Object.hasOwn(entity.payload, "baseEntityIds")) return [];
|
|
1583
|
+
const ids = entity.payload.baseEntityIds;
|
|
1584
|
+
if (!Array.isArray(ids) || ids.length === 0 || !ids.every((id) => typeof id === "string" && id.trim() === id && id.length > 0) || new Set(ids).size !== ids.length) throw new ScriptCompositionError("invalid_bases", entity.entityId, `Entity "${entity.entityId}" requires non-empty, unique baseEntityIds`);
|
|
1585
|
+
return ids.map(createEntityId);
|
|
1586
|
+
}
|
|
1587
|
+
/** Validate all base providers before applying explicit own fields; ordering cannot resolve ambiguity. */
|
|
1588
|
+
function assembleEntityContent(rows, entityId) {
|
|
1589
|
+
const byId = /* @__PURE__ */ new Map();
|
|
1590
|
+
for (const row of rows.entities) {
|
|
1591
|
+
if (byId.has(row.entityId)) throw new ScriptCompositionError("composition_ambiguous", row.entityId, `duplicate entity id "${row.entityId}"`);
|
|
1592
|
+
byId.set(row.entityId, row);
|
|
1593
|
+
}
|
|
1594
|
+
const active = /* @__PURE__ */ new Set();
|
|
1595
|
+
const cache = /* @__PURE__ */ new Map();
|
|
1596
|
+
const visit = (id) => {
|
|
1597
|
+
const cached = cache.get(id);
|
|
1598
|
+
if (cached !== void 0) return cached;
|
|
1599
|
+
if (active.has(id)) throw new ScriptCompositionError("composition_cycle", id, `Cyclic baseEntityIds at "${id}"`);
|
|
1600
|
+
const row = byId.get(id);
|
|
1601
|
+
if (row === void 0) throw new ScriptCompositionError("composition_dangling", id, `Missing base entity "${id}" in this document`);
|
|
1602
|
+
if (!isJsonObject(row.payload) || Object.hasOwn(row.payload, "entityId") || Object.hasOwn(row.payload, "entityKind")) throw new ScriptCompositionError("invalid_bases", id, `Entity "${id}" requires JSON own fields without reserved identity fields`);
|
|
1603
|
+
active.add(id);
|
|
1604
|
+
const inherited = {};
|
|
1605
|
+
const providers = /* @__PURE__ */ new Map();
|
|
1606
|
+
for (const baseId of variantBaseEntityIds(row)) {
|
|
1607
|
+
const base = visit(baseId);
|
|
1608
|
+
for (const [field, value] of Object.entries(base.payload)) {
|
|
1609
|
+
if (field === "baseEntityIds") continue;
|
|
1610
|
+
const previous = providers.get(field);
|
|
1611
|
+
if (previous !== void 0) throw new ScriptCompositionError("field_conflict", id, `Entity "${id}" field "${field}" conflicts between bases "${previous}" and "${baseId}"; own fields cannot resolve base ambiguity`);
|
|
1612
|
+
providers.set(field, baseId);
|
|
1613
|
+
Object.defineProperty(inherited, field, {
|
|
1614
|
+
value,
|
|
1615
|
+
enumerable: true,
|
|
1616
|
+
configurable: true,
|
|
1617
|
+
writable: true
|
|
1618
|
+
});
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
const assembled = {
|
|
1622
|
+
...row,
|
|
1623
|
+
payload: JSON.parse(JSON.stringify({
|
|
1624
|
+
...inherited,
|
|
1625
|
+
...row.payload
|
|
1626
|
+
}))
|
|
1627
|
+
};
|
|
1628
|
+
active.delete(id);
|
|
1629
|
+
cache.set(id, assembled);
|
|
1630
|
+
return assembled;
|
|
1631
|
+
};
|
|
1632
|
+
return visit(entityId);
|
|
1633
|
+
}
|
|
1634
|
+
/** Resolve a field's declaring entity after validating the entire composition. */
|
|
1635
|
+
function resolveEntityFieldOwner(rows, entityId, field) {
|
|
1636
|
+
assembleEntityContent(rows, entityId);
|
|
1637
|
+
const byId = new Map(rows.entities.map((row) => [row.entityId, row]));
|
|
1638
|
+
const find = (id) => {
|
|
1639
|
+
const row = byId.get(id);
|
|
1640
|
+
if (Object.hasOwn(row.payload, field)) return id;
|
|
1641
|
+
if (field === "baseEntityIds") return void 0;
|
|
1642
|
+
for (const baseId of variantBaseEntityIds(row)) {
|
|
1643
|
+
const owner = find(baseId);
|
|
1644
|
+
if (owner !== void 0) return owner;
|
|
1645
|
+
}
|
|
1646
|
+
};
|
|
1647
|
+
return find(entityId) ?? entityId;
|
|
1648
|
+
}
|
|
1649
|
+
/** Patch assembled fields without copying inherited fields into the variant's stored payload. */
|
|
1650
|
+
function updateEntityFields(rows, entityId, fields) {
|
|
1651
|
+
assembleEntityContent(rows, entityId);
|
|
1652
|
+
if (!isJsonObject(fields) || Object.hasOwn(fields, "entityId") || Object.hasOwn(fields, "entityKind")) throw new ScriptCompositionError("invalid_bases", entityId, "Field updates require JSON without reserved identity fields");
|
|
1653
|
+
const updates = /* @__PURE__ */ new Map();
|
|
1654
|
+
for (const [field, value] of Object.entries(fields)) {
|
|
1655
|
+
const ownerId = resolveEntityFieldOwner(rows, entityId, field);
|
|
1656
|
+
const owner = updates.get(ownerId) ?? rows.entities.find((row) => row.entityId === ownerId);
|
|
1657
|
+
updates.set(ownerId, {
|
|
1658
|
+
...owner,
|
|
1659
|
+
payload: {
|
|
1660
|
+
...owner.payload,
|
|
1661
|
+
[field]: value
|
|
1662
|
+
}
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1665
|
+
return [...updates.values()].map((row) => JSON.parse(JSON.stringify(row)));
|
|
1666
|
+
}
|
|
1667
|
+
/** Find the actual AudioScript text owner through direct variant bases. */
|
|
1668
|
+
function findComposedAudioScript(rows, variantEntityId, variantKind) {
|
|
1669
|
+
const variant = requireEntityKind(rows, variantEntityId, variantKind);
|
|
1670
|
+
assembleEntityContent(rows, variantEntityId);
|
|
1671
|
+
const sources = /* @__PURE__ */ new Map();
|
|
1672
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1673
|
+
const visit = (row) => {
|
|
1674
|
+
for (const id of variantBaseEntityIds(row)) {
|
|
1675
|
+
if (seen.has(id)) continue;
|
|
1676
|
+
seen.add(id);
|
|
1677
|
+
const base = rows.entities.find((candidate) => candidate.entityId === id);
|
|
1678
|
+
if (base.entityKind === "audio-script") sources.set(id, assembleEntityContent(rows, id));
|
|
1679
|
+
else visit(base);
|
|
1680
|
+
}
|
|
1681
|
+
};
|
|
1682
|
+
visit(variant);
|
|
1683
|
+
if (sources.size !== 1) throw new ScriptCompositionError(sources.size === 0 ? "composition_missing" : "composition_ambiguous", variantEntityId, `${variantKind} "${variantEntityId}" baseEntityIds must resolve one AudioScript text owner, got ${sources.size}`);
|
|
1684
|
+
return [...sources.values()][0];
|
|
1685
|
+
}
|
|
1686
|
+
/** Join the whole AudioScript text in segment order. */
|
|
1687
|
+
function assembleScriptText(script) {
|
|
1688
|
+
return scriptSegments(script).map((segment) => segment.text).join("");
|
|
1689
|
+
}
|
|
1690
|
+
function scriptSegments(script) {
|
|
1691
|
+
const segments = script.payload.segments;
|
|
1692
|
+
if (!Array.isArray(segments)) throw new ScriptCompositionError("composition_dangling", script.entityId, `AudioScript "${script.entityId}" requires segments`);
|
|
1693
|
+
if (!segments.every(isScriptSegment) || new Set(segments.map((s) => s.segmentId)).size !== segments.length) throw new ScriptCompositionError("invalid_script", script.entityId, "AudioScript requires valid segments with unique local segmentId and text");
|
|
1694
|
+
return segments;
|
|
1695
|
+
}
|
|
1696
|
+
function isScriptSegment(value) {
|
|
1697
|
+
if (typeof value !== "object" || value == null || Array.isArray(value)) return false;
|
|
1698
|
+
const segment = value;
|
|
1699
|
+
return typeof segment.segmentId === "string" && segment.segmentId.trim() !== "" && typeof segment.text === "string" && (segment.language === void 0 || typeof segment.language === "string");
|
|
1700
|
+
}
|
|
1701
|
+
/** Assemble selected text after base-field validation and explicit variant overrides. */
|
|
1702
|
+
function assembleCaptionContent(rows, captionEntityId) {
|
|
1703
|
+
requireEntityKind(rows, captionEntityId, "caption");
|
|
1704
|
+
const caption = assembleEntityContent(rows, captionEntityId);
|
|
1705
|
+
const script = findComposedAudioScript(rows, captionEntityId, "caption");
|
|
1706
|
+
const segments = selectAudioScriptSegments({
|
|
1707
|
+
...script,
|
|
1708
|
+
payload: caption.payload
|
|
1709
|
+
}, captionSelections(caption));
|
|
1710
|
+
return {
|
|
1711
|
+
caption,
|
|
1712
|
+
audioScript: script,
|
|
1713
|
+
segments,
|
|
1714
|
+
text: segments.map((segment) => segment.text).join("")
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1717
|
+
/**
|
|
1718
|
+
* Assemble the complete Phonetic Script content for TTS: the composed
|
|
1719
|
+
* AudioScript's base text plus the variant's own phoneme and prosody fields.
|
|
1720
|
+
*/
|
|
1721
|
+
function assemblePhoneticScriptContent(rows, phoneticScriptEntityId) {
|
|
1722
|
+
requireEntityKind(rows, phoneticScriptEntityId, "phonetic-script");
|
|
1723
|
+
const phoneticScript = assembleEntityContent(rows, phoneticScriptEntityId);
|
|
1724
|
+
const audioScript = findComposedAudioScript(rows, phoneticScriptEntityId, "phonetic-script");
|
|
1725
|
+
const segments = scriptSegments({
|
|
1726
|
+
...audioScript,
|
|
1727
|
+
payload: phoneticScript.payload
|
|
1728
|
+
});
|
|
1729
|
+
return {
|
|
1730
|
+
phoneticScript,
|
|
1731
|
+
audioScript,
|
|
1732
|
+
segments,
|
|
1733
|
+
text: segments.map((segment) => segment.text).join("")
|
|
1734
|
+
};
|
|
1735
|
+
}
|
|
1736
|
+
function captionSelections(caption) {
|
|
1737
|
+
const selections = caption.payload.selections;
|
|
1738
|
+
if (!Array.isArray(selections) || selections.length === 0) throw new ScriptCompositionError("empty_selection", caption.entityId, `Caption "${caption.entityId}" requires a non-empty segment selection into its AudioScript`);
|
|
1739
|
+
if (!selections.every(isSegmentSelection)) throw new ScriptCompositionError("invalid_selection", caption.entityId, "Caption selections require a segmentId and optional textRange");
|
|
1740
|
+
return selections;
|
|
1741
|
+
}
|
|
1742
|
+
function isSegmentSelection(value) {
|
|
1743
|
+
return typeof value === "object" && value != null && !Array.isArray(value) && typeof value.segmentId === "string" && value.segmentId.trim() !== "" && Object.keys(value).every((key) => key === "segmentId" || key === "textRange");
|
|
1744
|
+
}
|
|
1745
|
+
/** Select source text without creating another authoritative text field. */
|
|
1746
|
+
function selectAudioScriptSegments(script, selections) {
|
|
1747
|
+
if (!Array.isArray(selections) || selections.length === 0 || !selections.every(isSegmentSelection) || new Set(selections.map((s) => s.segmentId)).size !== selections.length) throw new ScriptCompositionError("invalid_selection", script.entityId, "Caption selections must be non-empty and name unique source segments");
|
|
1748
|
+
const bySegmentId = new Map(scriptSegments(script).map((segment) => [segment.segmentId, segment]));
|
|
1749
|
+
const selected = selections.map((selection) => {
|
|
1750
|
+
const segment = bySegmentId.get(selection.segmentId);
|
|
1751
|
+
if (segment === void 0) throw new ScriptCompositionError("unknown_segment", script.entityId, `Caption selection "${selection.segmentId}" does not name a segment of AudioScript "${script.entityId}"`);
|
|
1752
|
+
if (selection.textRange === void 0) return segment;
|
|
1753
|
+
const range = selection.textRange;
|
|
1754
|
+
const points = Array.from(segment.text);
|
|
1755
|
+
if (typeof range !== "object" || range === null || !Number.isSafeInteger(range.start) || !Number.isSafeInteger(range.end) || range.start < 0 || range.end <= range.start || range.end > points.length || Object.keys(range).some((key) => key !== "start" && key !== "end")) throw new ScriptCompositionError("invalid_selection", script.entityId, "Caption textRange must be a non-empty half-open Unicode code-point range within its source segment");
|
|
1756
|
+
return {
|
|
1757
|
+
...segment,
|
|
1758
|
+
text: points.slice(range.start, range.end).join("")
|
|
1759
|
+
};
|
|
1760
|
+
});
|
|
1761
|
+
if (!selected.map((segment) => segment.text).join("").trim()) throw new ScriptCompositionError("empty_selection", script.entityId, "Caption selection must contain visible source text");
|
|
1762
|
+
return selected;
|
|
1763
|
+
}
|
|
1764
|
+
function requireEntityKind(rows, entityIdValue, entityKind) {
|
|
1765
|
+
const entity = rows.entities.find((candidate) => candidate.entityId === entityIdValue);
|
|
1766
|
+
if (entity === void 0) throw new ScriptCompositionError("composition_dangling", entityIdValue, `Entity "${entityIdValue}" does not exist`);
|
|
1767
|
+
if (entity.entityKind !== entityKind) throw new ScriptCompositionError("composition_dangling", entityIdValue, `Entity "${entityIdValue}" must have kind "${entityKind}", got "${entity.entityKind}"`);
|
|
1768
|
+
return entity;
|
|
1769
|
+
}
|
|
1770
|
+
//#endregion
|
|
1771
|
+
//#region ../medeo-dsl/src/rows.ts
|
|
1772
|
+
function entityToRow(entity) {
|
|
1773
|
+
const { entityId, entityKind, ...payload } = entity;
|
|
1774
|
+
if (!isJsonObject(payload)) throw new Error(`Entity "${entityId}" payload must contain only JSON values`);
|
|
1775
|
+
return {
|
|
1776
|
+
entityId,
|
|
1777
|
+
entityKind,
|
|
1778
|
+
payload
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1781
|
+
//#endregion
|
|
1539
1782
|
//#region ../medeo-dsl/src/invariants.ts
|
|
1540
1783
|
/**
|
|
1541
1784
|
* Media variants own their Asset identity directly: a `physical-asset`
|
|
1542
|
-
* Relation may not bind them. Caption
|
|
1543
|
-
*
|
|
1785
|
+
* Relation may not bind them. Caption text is composed domain content, so
|
|
1786
|
+
* Captions do not bind Physical Assets either; the Relation vocabulary stays
|
|
1787
|
+
* open for future physical pipeline semantics.
|
|
1544
1788
|
*/
|
|
1545
1789
|
function validateMediaAssetIdentity(entity, index) {
|
|
1546
1790
|
return [...index.relationsOf(entity)].filter((relation) => relation.kind === "physical-asset").map(() => ({
|
|
@@ -1553,6 +1797,19 @@ function validateMediaAssetIdentity(entity, index) {
|
|
|
1553
1797
|
function validateEntityRelationSet(entityRefs, index, options) {
|
|
1554
1798
|
const { entities, issues } = collectRelatedEntities(entityRefs, index);
|
|
1555
1799
|
const entityIds = new Set(entities.map((entity) => entity.entityId));
|
|
1800
|
+
const rows = {
|
|
1801
|
+
entities: entities.map((entity) => entityToRow(entity.current())),
|
|
1802
|
+
relations: []
|
|
1803
|
+
};
|
|
1804
|
+
for (const entity of entities) try {
|
|
1805
|
+
assembleEntityContent(rows, entity.entityId);
|
|
1806
|
+
} catch (error) {
|
|
1807
|
+
issues.push({
|
|
1808
|
+
code: "invalid_variant_composition",
|
|
1809
|
+
entityId: entity.entityId,
|
|
1810
|
+
message: error instanceof Error ? error.message : "Invalid variant composition"
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1556
1813
|
for (const entity of entities) {
|
|
1557
1814
|
const current = entity.current();
|
|
1558
1815
|
const entityIssues = validateEntity(entity, entityIds);
|
|
@@ -1573,7 +1830,8 @@ function validateEntityRelationSet(entityRefs, index, options) {
|
|
|
1573
1830
|
issues.push(...validateClipPlacement(entity, index));
|
|
1574
1831
|
}
|
|
1575
1832
|
if (current.entityKind === "axvideo") issues.push(...validateAXVideoAdmission(entity, index));
|
|
1576
|
-
if (current.entityKind === "caption") issues.push(...
|
|
1833
|
+
if (current.entityKind === "caption") issues.push(...validateCaptionComposition(entity, index, entities));
|
|
1834
|
+
if (current.entityKind === "phonetic-script") issues.push(...validatePhoneticComposition(entity, index, entities));
|
|
1577
1835
|
issues.push(...validateSequenceComposition(entity));
|
|
1578
1836
|
}
|
|
1579
1837
|
issues.push(...validateClipAnchorCycles(entities, index));
|
|
@@ -1676,25 +1934,35 @@ function validateAXVideoAdmission(axVideo, index) {
|
|
|
1676
1934
|
message: "AXVideo must have exactly one live AXVideo-Marker Relation"
|
|
1677
1935
|
}];
|
|
1678
1936
|
}
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1937
|
+
/** Validate a Caption against the complete entity set, including its directly held bases. */
|
|
1938
|
+
function validateCaptionComposition(caption, index, entities = []) {
|
|
1939
|
+
return validateVariantComposition(caption, index, entities, "caption");
|
|
1940
|
+
}
|
|
1941
|
+
function validatePhoneticComposition(phonetic, index, entities = []) {
|
|
1942
|
+
return validateVariantComposition(phonetic, index, entities, "phonetic-script");
|
|
1943
|
+
}
|
|
1944
|
+
function validateVariantComposition(variant, index, entities, kind) {
|
|
1945
|
+
const issues = [];
|
|
1946
|
+
if (kind === "caption" && ofKind([...index.relationsOf(variant)], "physical-asset").length > 0) issues.push({
|
|
1947
|
+
code: "caption_physical_asset_forbidden",
|
|
1948
|
+
entityId: variant.entityId,
|
|
1949
|
+
message: "Caption text is composed from its AudioScript; Caption must not bind a Physical Asset"
|
|
1950
|
+
});
|
|
1951
|
+
const rows = {
|
|
1952
|
+
entities: collectRelatedEntities([...entities, variant], index).entities.map((ref) => entityToRow(ref.current())),
|
|
1953
|
+
relations: []
|
|
1954
|
+
};
|
|
1955
|
+
try {
|
|
1956
|
+
if (kind === "caption") assembleCaptionContent(rows, variant.entityId);
|
|
1957
|
+
else assemblePhoneticScriptContent(rows, variant.entityId);
|
|
1958
|
+
} catch (error) {
|
|
1959
|
+
issues.push({
|
|
1960
|
+
code: kind === "caption" ? "caption_composition_required" : "phonetic_script_composition_required",
|
|
1961
|
+
entityId: variant.entityId,
|
|
1962
|
+
message: error instanceof Error ? error.message : "Invalid variant composition"
|
|
1963
|
+
});
|
|
1692
1964
|
}
|
|
1693
|
-
return
|
|
1694
|
-
code: "caption_asset_required",
|
|
1695
|
-
entityId: caption.entityId,
|
|
1696
|
-
message: "Caption must have a Physical Asset Relation"
|
|
1697
|
-
}];
|
|
1965
|
+
return issues;
|
|
1698
1966
|
}
|
|
1699
1967
|
function validateClipPlacement(clip, index) {
|
|
1700
1968
|
const marker = ofKind([...index.relationsOf(clip)], "clip-marker")[0]?.other(clip)?.deref();
|
|
@@ -1822,6 +2090,18 @@ function validateKnownEntityPayload(entity) {
|
|
|
1822
2090
|
const value = entity;
|
|
1823
2091
|
const problems = [];
|
|
1824
2092
|
if (value.lifecycle !== void 0 && !isRecord(value.lifecycle)) problems.push("lifecycle must be an object when present");
|
|
2093
|
+
if (entity.entityKind === "audio-script" || entity.entityKind === "phonetic-script") {
|
|
2094
|
+
for (const key of [
|
|
2095
|
+
"extent",
|
|
2096
|
+
"sampling",
|
|
2097
|
+
"coordinateSpace",
|
|
2098
|
+
"sourceRange",
|
|
2099
|
+
"targetRange",
|
|
2100
|
+
"duration",
|
|
2101
|
+
"startMs",
|
|
2102
|
+
"endMs"
|
|
2103
|
+
]) if (Object.hasOwn(value, key)) problems.push(`${key} is not intrinsic script data; assign an external annotation Marker`);
|
|
2104
|
+
}
|
|
1825
2105
|
switch (entity.entityKind) {
|
|
1826
2106
|
case "track":
|
|
1827
2107
|
validateOptionalField(value, "hidden", "boolean", problems);
|
|
@@ -1848,9 +2128,11 @@ function validateKnownEntityPayload(entity) {
|
|
|
1848
2128
|
validateMarkerPayload(value, problems);
|
|
1849
2129
|
break;
|
|
1850
2130
|
case "audio-script":
|
|
1851
|
-
case "phonetic-script":
|
|
1852
2131
|
validateScriptPayload(value, problems);
|
|
1853
2132
|
break;
|
|
2133
|
+
case "phonetic-script":
|
|
2134
|
+
validatePhoneticScriptPayload(value, problems);
|
|
2135
|
+
break;
|
|
1854
2136
|
case "timeline":
|
|
1855
2137
|
case "viewport": break;
|
|
1856
2138
|
case "clip":
|
|
@@ -1914,24 +2196,18 @@ function validateSegmentRanges(value, problems) {
|
|
|
1914
2196
|
function validateAssetPayload(value, problems) {
|
|
1915
2197
|
validateExternalLocator(value, problems);
|
|
1916
2198
|
validateStorageKey(value, problems);
|
|
1917
|
-
|
|
1918
|
-
if (inline !== void 0) if (!isRecord(inline)) problems.push("inline must be an object when present");
|
|
1919
|
-
else {
|
|
1920
|
-
if (inline.mediaType !== "text/plain") problems.push("inline.mediaType must be \"text/plain\"");
|
|
1921
|
-
if (typeof inline.text !== "string") problems.push("inline.text must be a string");
|
|
1922
|
-
}
|
|
1923
|
-
if (value.external !== void 0 && value.inline !== void 0) problems.push("Asset must not combine external and inline physical locations");
|
|
2199
|
+
if (value.inline !== void 0) problems.push("inline is not a physical location; text is domain content owned by its AudioScript");
|
|
1924
2200
|
}
|
|
1925
2201
|
/**
|
|
1926
2202
|
* Media variants own their Asset locator directly: `external` is required and
|
|
1927
|
-
*
|
|
2203
|
+
* textual domain content is owned by AudioScript; the physical-location
|
|
1928
2204
|
* vocabularies cannot mix.
|
|
1929
2205
|
*/
|
|
1930
2206
|
function validateMediaAssetFields(value, problems) {
|
|
1931
2207
|
if (value.external === void 0) problems.push("external is required; media variants own their Asset identity");
|
|
1932
2208
|
else validateExternalLocator(value, problems);
|
|
1933
2209
|
validateStorageKey(value, problems);
|
|
1934
|
-
if (value.inline !== void 0) problems.push("
|
|
2210
|
+
if (value.inline !== void 0) problems.push("media variants use external locators, not inline domain text");
|
|
1935
2211
|
}
|
|
1936
2212
|
function validateExternalLocator(value, problems) {
|
|
1937
2213
|
const external = value.external;
|
|
@@ -1958,7 +2234,26 @@ function validateVoicePayload(value, problems) {
|
|
|
1958
2234
|
if (voice.name !== void 0 && typeof voice.name !== "string") problems.push("voice.name must be a string when present");
|
|
1959
2235
|
}
|
|
1960
2236
|
function validateCaptionPayload(value, problems) {
|
|
1961
|
-
|
|
2237
|
+
const selections = value.selections;
|
|
2238
|
+
if (!Array.isArray(selections)) problems.push("selections must be a non-empty array of AudioScript segment selections");
|
|
2239
|
+
else {
|
|
2240
|
+
if (selections.length === 0) problems.push("selections must name at least one AudioScript segment");
|
|
2241
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2242
|
+
for (const [index, selection] of selections.entries()) {
|
|
2243
|
+
if (!isRecord(selection)) {
|
|
2244
|
+
problems.push(`selections[${index}] must be an object`);
|
|
2245
|
+
continue;
|
|
2246
|
+
}
|
|
2247
|
+
if (typeof selection.segmentId !== "string" || selection.segmentId.trim() === "") problems.push(`selections[${index}].segmentId must be a string`);
|
|
2248
|
+
else if (seen.has(selection.segmentId)) problems.push(`selections[${index}].segmentId must be unique within the Caption`);
|
|
2249
|
+
else seen.add(selection.segmentId);
|
|
2250
|
+
if (Object.keys(selection).some((key) => key !== "segmentId" && key !== "textRange")) problems.push(`selections[${index}] may only contain segmentId and textRange`);
|
|
2251
|
+
if (selection.textRange !== void 0) {
|
|
2252
|
+
const range = selection.textRange;
|
|
2253
|
+
if (!isRecord(range) || !Number.isSafeInteger(range.start) || !Number.isSafeInteger(range.end) || range.start < 0 || range.end <= range.start || Object.keys(range).some((key) => key !== "start" && key !== "end")) problems.push(`selections[${index}].textRange must be a non-empty half-open code-point range`);
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
1962
2257
|
const style = value.style;
|
|
1963
2258
|
if (style === void 0) return;
|
|
1964
2259
|
if (!isRecord(style)) {
|
|
@@ -1993,6 +2288,12 @@ function validateRange(value, path, required, problems) {
|
|
|
1993
2288
|
if (!Object.hasOwn(value, "start") || value.start === void 0) problems.push(`${path}.start is required`);
|
|
1994
2289
|
if (!Object.hasOwn(value, "end") || value.end === void 0) problems.push(`${path}.end is required`);
|
|
1995
2290
|
}
|
|
2291
|
+
/** Phonetic variants store pronunciation fields only; base text stays in the AudioScript. */
|
|
2292
|
+
function validatePhoneticScriptPayload(value, problems) {
|
|
2293
|
+
if (value.segments !== void 0) validateScriptPayload(value, problems);
|
|
2294
|
+
if (value.phonemeScript !== void 0 && (typeof value.phonemeScript !== "string" || value.phonemeScript.trim() === "")) problems.push("phonemeScript must be a non-empty string when present");
|
|
2295
|
+
if (value.prosody !== void 0 && !isRecord(value.prosody)) problems.push("prosody must be an object when present");
|
|
2296
|
+
}
|
|
1996
2297
|
function validateScriptPayload(value, problems) {
|
|
1997
2298
|
if (!Array.isArray(value.segments)) {
|
|
1998
2299
|
problems.push("segments must be an array");
|
|
@@ -2035,6 +2336,7 @@ function visitPeerEntityIdPaths(value, entityKind, parentPath, ancestors, paths)
|
|
|
2035
2336
|
if (Array.isArray(value)) for (const [index, item] of value.entries()) visitPeerEntityIdPaths(item, entityKind, `${parentPath}[${index}]`, ancestors, paths);
|
|
2036
2337
|
else for (const [key, child] of Object.entries(value)) {
|
|
2037
2338
|
const path = parentPath.length === 0 ? key : `${parentPath}.${key}`;
|
|
2339
|
+
if (parentPath.length === 0 && key === "baseEntityIds") continue;
|
|
2038
2340
|
if (!(parentPath.length === 0 && key === "entityId") && !isOwnedLocalIdPath(entityKind, path) && isEntityIdFieldName(key)) paths.push(path);
|
|
2039
2341
|
visitPeerEntityIdPaths(child, entityKind, path, ancestors, paths);
|
|
2040
2342
|
}
|
|
@@ -2054,7 +2356,7 @@ function visitPeerEntityValues(value, entityKind, ownEntityId, entityIds, path,
|
|
|
2054
2356
|
ancestors.add(value);
|
|
2055
2357
|
if (Array.isArray(value)) for (const [index, item] of value.entries()) visitPeerEntityValues(item, entityKind, ownEntityId, entityIds, `${path}[${index}]`, ancestors, paths);
|
|
2056
2358
|
else for (const [key, child] of Object.entries(value)) {
|
|
2057
|
-
if (path.length === 0 && (key === "entityId" || key === "entityKind")) continue;
|
|
2359
|
+
if (path.length === 0 && (key === "entityId" || key === "entityKind" || key === "baseEntityIds")) continue;
|
|
2058
2360
|
visitPeerEntityValues(child, entityKind, ownEntityId, entityIds, path.length === 0 ? key : `${path}.${key}`, ancestors, paths);
|
|
2059
2361
|
}
|
|
2060
2362
|
ancestors.delete(value);
|
|
@@ -2071,16 +2373,25 @@ function isOwnedLocalEntityValuePath(entityKind, path) {
|
|
|
2071
2373
|
].includes(entityKind) && /^(?:sampling|coordinateSpace|coordinateSpace\.unit|extent\.kind)$/.test(path)) return true;
|
|
2072
2374
|
if (entityKind === "sequence-marker" && /^(?:durationPolicy|duration\.mode|timeRemapping\.(?:kind|mode))$/.test(path)) return true;
|
|
2073
2375
|
if (entityKind === "sequence-marker" && /^segmentRanges\[\d+\]\.segmentId$/.test(path)) return true;
|
|
2074
|
-
if (entityKind === "asset" && /^(?:external\.(?:system|key)|storageKey
|
|
2376
|
+
if (entityKind === "asset" && /^(?:external\.(?:system|key)|storageKey)$/.test(path)) return true;
|
|
2075
2377
|
if (isMediaAssetVariantKind(entityKind) && /^(?:external\.(?:system|key)|storageKey)$/.test(path)) return true;
|
|
2076
2378
|
if (entityKind === "voice" && /^voice\.(?:system|key|name)$/.test(path)) return true;
|
|
2077
|
-
if (entityKind === "caption" && (path
|
|
2078
|
-
if (
|
|
2379
|
+
if (entityKind === "caption" && (path.startsWith("style.") || /^selections\[\d+\]\.segmentId$/.test(path))) return true;
|
|
2380
|
+
if ([
|
|
2381
|
+
"audio-script",
|
|
2382
|
+
"caption",
|
|
2383
|
+
"phonetic-script"
|
|
2384
|
+
].includes(entityKind) && /^segments\[\d+\]\.(?:segmentId|text|language)$/.test(path)) return true;
|
|
2079
2385
|
return false;
|
|
2080
2386
|
}
|
|
2081
2387
|
function isOwnedLocalIdPath(entityKind, path) {
|
|
2082
2388
|
if (path === "lifecycle.actorId") return true;
|
|
2083
|
-
if (
|
|
2389
|
+
if ([
|
|
2390
|
+
"audio-script",
|
|
2391
|
+
"caption",
|
|
2392
|
+
"phonetic-script"
|
|
2393
|
+
].includes(entityKind) && /^segments\[\d+\]\.segmentId$/.test(path)) return true;
|
|
2394
|
+
if (entityKind === "caption" && /^selections\[\d+\]\.segmentId$/.test(path)) return true;
|
|
2084
2395
|
if (entityKind === "sequence-marker" && /^segmentRanges\[\d+\]\.segmentId$/.test(path)) return true;
|
|
2085
2396
|
if (entityKind === "asset" && /^(?:tracks\[\d+\]\.trackId|renditions\[\d+\]\.renditionId)$/.test(path)) return true;
|
|
2086
2397
|
return false;
|
|
@@ -2136,35 +2447,6 @@ function ofKind(relations, kind) {
|
|
|
2136
2447
|
return relations.filter((relation) => relation.kind === kind);
|
|
2137
2448
|
}
|
|
2138
2449
|
//#endregion
|
|
2139
|
-
//#region ../medeo-dsl/src/json-values.ts
|
|
2140
|
-
const nativeObjectConstructorSource = Function.prototype.toString.call(Object);
|
|
2141
|
-
function isJsonObject(value) {
|
|
2142
|
-
return isJsonValue(value, /* @__PURE__ */ new Set()) && !Array.isArray(value) && value !== null;
|
|
2143
|
-
}
|
|
2144
|
-
function isJsonValue(value, ancestors) {
|
|
2145
|
-
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
2146
|
-
if (typeof value === "number") return Number.isFinite(value);
|
|
2147
|
-
if (typeof value !== "object") return false;
|
|
2148
|
-
if (!Array.isArray(value) && !isPlainObject(value)) return false;
|
|
2149
|
-
if (ancestors.has(value)) return false;
|
|
2150
|
-
ancestors.add(value);
|
|
2151
|
-
const valid = Array.isArray(value) ? value.every((item) => isJsonValue(item, ancestors)) : Object.values(value).every((item) => isJsonValue(item, ancestors));
|
|
2152
|
-
ancestors.delete(value);
|
|
2153
|
-
return valid;
|
|
2154
|
-
}
|
|
2155
|
-
/** Recognize an ordinary object from any VM realm without admitting class instances. */
|
|
2156
|
-
function isPlainObject(value) {
|
|
2157
|
-
try {
|
|
2158
|
-
const prototype = Object.getPrototypeOf(value);
|
|
2159
|
-
if (prototype === null) return true;
|
|
2160
|
-
if (Object.getPrototypeOf(prototype) !== null) return false;
|
|
2161
|
-
const constructor = Object.getOwnPropertyDescriptor(prototype, "constructor")?.value;
|
|
2162
|
-
return typeof constructor === "function" && constructor.prototype === prototype && Function.prototype.toString.call(constructor) === nativeObjectConstructorSource;
|
|
2163
|
-
} catch {
|
|
2164
|
-
return false;
|
|
2165
|
-
}
|
|
2166
|
-
}
|
|
2167
|
-
//#endregion
|
|
2168
2450
|
//#region ../medeo-dsl/src/relation-specs.ts
|
|
2169
2451
|
const timelineTrackRelationSpec = emptySpec("timeline-track", "timeline", "track");
|
|
2170
2452
|
const trackClipRelationSpec = emptySpec("track-clip", "track", "clip");
|
|
@@ -2187,8 +2469,6 @@ const generatedRelationSpec = Object.freeze({
|
|
|
2187
2469
|
validateEndpoints: (endpoints) => endpoints.every((endpoint) => isGeneratedMedia(endpoint.current())),
|
|
2188
2470
|
validateMetadata: isEmptyMetadata
|
|
2189
2471
|
});
|
|
2190
|
-
const phoneticScriptProvenanceRelationSpec = metadataSpec("phonetic-script-provenance", "phonetic-script", "audio-script", isSegmentAlignmentMetadata);
|
|
2191
|
-
const captionProvenanceRelationSpec = metadataSpec("caption-provenance", "caption", "audio-script", isSegmentAlignmentMetadata);
|
|
2192
2472
|
const captionAlignmentRelationSpec = Object.freeze({
|
|
2193
2473
|
kind: "caption-alignment",
|
|
2194
2474
|
validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["caption"]), new Set(["audio", "voice"])),
|
|
@@ -2200,19 +2480,20 @@ const clipAnchorRelationSpec = Object.freeze({
|
|
|
2200
2480
|
validateEndpoints: (endpoints) => endpoints[0].current().entityKind === "clip" && endpoints[1].current().entityKind === "clip",
|
|
2201
2481
|
validateMetadata: isEmptyMetadata
|
|
2202
2482
|
});
|
|
2203
|
-
/**
|
|
2204
|
-
const
|
|
2205
|
-
kind: "
|
|
2206
|
-
validateEndpoints: (endpoints) =>
|
|
2207
|
-
const outputKind = endpoints[0].current().entityKind;
|
|
2208
|
-
return (outputKind === "audio" || outputKind === "voice") && endpoints[1].current().entityKind === "audio-script";
|
|
2209
|
-
},
|
|
2483
|
+
/** Voice is generated from PhoneticScript; kinds determine roles regardless of endpoint positions. */
|
|
2484
|
+
const phoneticScriptRenderRelationSpec = Object.freeze({
|
|
2485
|
+
kind: "phonetic-script-render",
|
|
2486
|
+
validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["voice"]), new Set(["phonetic-script"])),
|
|
2210
2487
|
validateMetadata: isEmptyMetadata
|
|
2211
2488
|
});
|
|
2212
|
-
/**
|
|
2489
|
+
/** AudioScript was transcribed from Audio, Video, or recorded Voice; kinds determine roles. */
|
|
2213
2490
|
const audioScriptSourceRelationSpec = Object.freeze({
|
|
2214
2491
|
kind: "audio-script-source",
|
|
2215
|
-
validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["audio-script"]), new Set([
|
|
2492
|
+
validateEndpoints: (endpoints) => hasKinds(endpoints, new Set(["audio-script"]), new Set([
|
|
2493
|
+
"audio",
|
|
2494
|
+
"video",
|
|
2495
|
+
"voice"
|
|
2496
|
+
])),
|
|
2216
2497
|
validateMetadata: isEmptyMetadata
|
|
2217
2498
|
});
|
|
2218
2499
|
/**
|
|
@@ -2236,11 +2517,9 @@ const builtInRelationSpecs = Object.freeze([
|
|
|
2236
2517
|
markerTimelineRelationSpec,
|
|
2237
2518
|
physicalAssetRelationSpec,
|
|
2238
2519
|
generatedRelationSpec,
|
|
2239
|
-
phoneticScriptProvenanceRelationSpec,
|
|
2240
|
-
captionProvenanceRelationSpec,
|
|
2241
2520
|
captionAlignmentRelationSpec,
|
|
2242
2521
|
clipAnchorRelationSpec,
|
|
2243
|
-
|
|
2522
|
+
phoneticScriptRenderRelationSpec,
|
|
2244
2523
|
audioScriptSourceRelationSpec,
|
|
2245
2524
|
audioScriptMarkerRelationSpec
|
|
2246
2525
|
]);
|
|
@@ -2280,9 +2559,6 @@ function isAssetBinding(value) {
|
|
|
2280
2559
|
const orderingKey = /(order|ordinal|position|rank|index|z[_-]?index)/i;
|
|
2281
2560
|
return Object.keys(value).every((key) => !orderingKey.test(key));
|
|
2282
2561
|
}
|
|
2283
|
-
function isSegmentAlignmentMetadata(value) {
|
|
2284
|
-
return isJsonObject(value) && Object.hasOwn(value, "segmentAlignment");
|
|
2285
|
-
}
|
|
2286
2562
|
function isCaptionAlignmentMetadata(value) {
|
|
2287
2563
|
return isJsonObject(value) && Object.hasOwn(value, "alignment");
|
|
2288
2564
|
}
|
|
@@ -2339,7 +2615,7 @@ var BiRelationIndex = class {
|
|
|
2339
2615
|
byRelationId = /* @__PURE__ */ new Map();
|
|
2340
2616
|
link(input) {
|
|
2341
2617
|
if (input.spec.kind === "generated") throw new Error("Author generated Relations with linkGenerated({ output, input })");
|
|
2342
|
-
if (input.spec.kind === "clip-anchor" || input.spec.kind === "
|
|
2618
|
+
if (input.spec.kind === "clip-anchor" || input.spec.kind === "phonetic-script-render" || input.spec.kind === "audio-script-source") throw new Error(`Author ordered ${input.spec.kind} Relations with the dedicated role-named method`);
|
|
2343
2619
|
return this.linkValidated(input);
|
|
2344
2620
|
}
|
|
2345
2621
|
/** Author `generated(output, input)` without exposing positional arguments. */
|
|
@@ -2362,12 +2638,12 @@ var BiRelationIndex = class {
|
|
|
2362
2638
|
trace: input.trace
|
|
2363
2639
|
});
|
|
2364
2640
|
}
|
|
2365
|
-
/** Author `
|
|
2366
|
-
|
|
2641
|
+
/** Author `phonetic-script-render(output, phoneticScript)` without exposing positional arguments. */
|
|
2642
|
+
linkPhoneticScriptRender(input) {
|
|
2367
2643
|
return this.linkValidated({
|
|
2368
2644
|
relationId: input.relationId,
|
|
2369
|
-
spec:
|
|
2370
|
-
endpoints: [input.output, input.
|
|
2645
|
+
spec: phoneticScriptRenderRelationSpec,
|
|
2646
|
+
endpoints: [input.output, input.phoneticScript],
|
|
2371
2647
|
metadata: {},
|
|
2372
2648
|
trace: input.trace
|
|
2373
2649
|
});
|
|
@@ -2468,7 +2744,13 @@ function decodeEntityRelationRows(rows, options) {
|
|
|
2468
2744
|
const entitiesById = /* @__PURE__ */ new Map();
|
|
2469
2745
|
const extensionEntityKinds = new Set(options.entityKinds ?? []);
|
|
2470
2746
|
for (const row of rows.entities) {
|
|
2471
|
-
|
|
2747
|
+
let entity = decodeEntityRow(row, extensionEntityKinds, issues);
|
|
2748
|
+
if (entity != null) try {
|
|
2749
|
+
entity = decodeEntityRow(assembleEntityContent(rows, row.entityId), extensionEntityKinds, issues);
|
|
2750
|
+
} catch (error) {
|
|
2751
|
+
issues.push(errorMessage(error));
|
|
2752
|
+
continue;
|
|
2753
|
+
}
|
|
2472
2754
|
if (entity == null) continue;
|
|
2473
2755
|
const ref = createEntityRef(entity);
|
|
2474
2756
|
if (entitiesById.has(ref.entityId)) {
|
|
@@ -2648,9 +2930,9 @@ var EntityProjectionGraph = class {
|
|
|
2648
2930
|
timeline;
|
|
2649
2931
|
constructor(rows) {
|
|
2650
2932
|
this.rows = rows;
|
|
2651
|
-
decodeEntityRelationRows(rows, { compareMarkerPoints: (_marker, _range, left, right) => finite(left, "range point") - finite(right, "range point") });
|
|
2652
|
-
this.entities = new Map(
|
|
2653
|
-
const roots =
|
|
2933
|
+
const decoded = decodeEntityRelationRows(rows, { compareMarkerPoints: (_marker, _range, left, right) => finite(left, "range point") - finite(right, "range point") });
|
|
2934
|
+
this.entities = new Map([...decoded.entitiesById.values()].map((ref) => [String(ref.entityId), entityToRow(ref.current())]));
|
|
2935
|
+
const roots = [...this.entities.values()].filter((row) => row.entityKind === "timeline");
|
|
2654
2936
|
if (roots.length !== 1) fail("compatibility projection requires exactly one Timeline");
|
|
2655
2937
|
this.timeline = roots[0];
|
|
2656
2938
|
}
|
|
@@ -2860,7 +3142,7 @@ function projectEntityTimeline(rows, meta) {
|
|
|
2860
3142
|
const asset = mediaAssetFacts(graph, content);
|
|
2861
3143
|
requireUntrimmedAudio(content, marker, placement);
|
|
2862
3144
|
const external = externalLocator(asset, "memota-speech");
|
|
2863
|
-
const
|
|
3145
|
+
const assembled = graph.adjacent(content.entityId, "phonetic-script-render").length > 0 || content.payload.voice !== void 0 ? assemblePhoneticScriptContent(rows, graph.one(content.entityId, "phonetic-script-render", "phonetic-script").entityId) : void 0;
|
|
2864
3146
|
const voice = content.payload.voice;
|
|
2865
3147
|
if (voice !== void 0 && (!object(voice) || voice.system !== "voice-library" || typeof voice.key !== "string")) fail("Voice must use an explicit voice-library locator");
|
|
2866
3148
|
const captionIds = layout.tracks.flatMap((candidate) => candidate.role === "caption" ? candidate.clips.filter((caption) => caption.anchorClipEntityId === clip.entityId).map((caption) => caption.clipEntityId) : []);
|
|
@@ -2870,7 +3152,7 @@ function projectEntityTimeline(rows, meta) {
|
|
|
2870
3152
|
media_duration_ms: placement.duration,
|
|
2871
3153
|
audio_storage_key: storageKey(asset),
|
|
2872
3154
|
origin_speech_id: external.key,
|
|
2873
|
-
audio_script:
|
|
3155
|
+
audio_script: assembled?.text ?? (graph.adjacent(content.entityId, "audio-script-source").length > 0 ? assembleScriptText(graph.one(content.entityId, "audio-script-source", "audio-script")) : ""),
|
|
2874
3156
|
volume: volume.data,
|
|
2875
3157
|
voice: object(voice) ? {
|
|
2876
3158
|
id: typeof voice.key === "string" ? voice.key : void 0,
|
|
@@ -2879,12 +3161,7 @@ function projectEntityTimeline(rows, meta) {
|
|
|
2879
3161
|
caption_ids: captionIds
|
|
2880
3162
|
} };
|
|
2881
3163
|
} else if (track.role === "caption") {
|
|
2882
|
-
const text = content.
|
|
2883
|
-
if (typeof text !== "string") fail("Caption " + content.entityId + " requires its text");
|
|
2884
|
-
const asset = graph.one(content.entityId, "physical-asset", "asset");
|
|
2885
|
-
const inline = asset.payload.inline;
|
|
2886
|
-
if (inline !== void 0 && (!object(inline) || inline.mediaType !== "text/plain" || inline.text !== text)) fail("Inline Caption Asset and Caption text must agree");
|
|
2887
|
-
if (inline === void 0 && !object(asset.payload.external)) fail("Caption Asset must identify physical text or contain actual inline text");
|
|
3164
|
+
const text = assembleCaptionContent(rows, content.entityId).text;
|
|
2888
3165
|
if (marker.payload.timeRemapping !== void 0) fail("Caption timeRemapping is not supported by this reader");
|
|
2889
3166
|
const style = captionStyle(content.payload.style);
|
|
2890
3167
|
const anchor = placement.anchorClipEntityId === void 0 ? void 0 : layout.clips.get(placement.anchorClipEntityId);
|
|
@@ -2972,14 +3249,6 @@ function storageKey(asset) {
|
|
|
2972
3249
|
if (typeof asset.payload.storageKey !== "string" || asset.payload.storageKey.trim() === "") fail("Asset " + asset.entityId + " requires its factual storageKey");
|
|
2973
3250
|
return asset.payload.storageKey;
|
|
2974
3251
|
}
|
|
2975
|
-
function scriptText(script) {
|
|
2976
|
-
const segments = script.payload.segments;
|
|
2977
|
-
if (!Array.isArray(segments)) fail("AudioScript requires segments");
|
|
2978
|
-
return segments.map((segment) => {
|
|
2979
|
-
if (!object(segment) || typeof segment.text !== "string") fail("AudioScript segment requires text");
|
|
2980
|
-
return segment.text;
|
|
2981
|
-
}).join("");
|
|
2982
|
-
}
|
|
2983
3252
|
function captionStyle(value) {
|
|
2984
3253
|
if (value === void 0) return void 0;
|
|
2985
3254
|
if (!object(value)) fail("Caption style must be an object");
|
|
@@ -3351,4 +3620,4 @@ function createPlainMemoryAdapter(document, options) {
|
|
|
3351
3620
|
return new PlainMemoryAdapter(document, options);
|
|
3352
3621
|
}
|
|
3353
3622
|
//#endregion
|
|
3354
|
-
export {
|
|
3623
|
+
export { isEmptyVideoClip as $, buildSpeechHostMap as A, ensureLaneTrack as B, variantBaseEntityIds as C, hasSequence as D, createRelationId as E, assertValidVideoDocument as F, arrangeMainTrackSeamlessly as G, laneTrackId as H, validateVideoDocument as I, recalculateTimelineDuration as J, fillMainTrackTimeGaps as K, partUnionSchema as L, fromVideoDocument as M, toVideoDocument as N, isMediaAssetVariantKind as O, VideoDocumentValidationError as P, TIMELINE_SKELETON_DURATION_MS as Q, videoDocumentSchema as R, updateEntityFields as S, createEntityId as T, solveVideoDocument as U, findLaneTrack as V, cascadeAfterVideoClipChanges as W, resolveSpeechOverlapByShiftingVideos as X, resolveAllSpeechOverlaps as Y, syncAggregatedClipsTimePosition as Z, assembleEntityContent as _, applyEntityTimelineProjection as a, effectiveVideoClipDurationMs as at, findComposedAudioScript as b, writeVideoDocumentToDraft as c, recordEntries as ct, EntityProjectionGraph as d, bytesToBase64 as dt, partDurationMs as et, resolveEntityTimelineLayout as f, assembleCaptionContent as g, ScriptCompositionError as h, MirrorVideoDocumentAdapter as i, VIDEO_DOCUMENT_SCHEMA_VERSION as it, derivePositionFromAbs as j, buildInitialVideoDocument as k, readVideoDocumentFromDraft as l, videoDocumentMirrorSchema as lt, decodeEntityRelationRows as m, createPlainMemoryAdapter as n, DEFAULT_UNIT_TIME_MS as nt, createMirrorVideoDocument as o, speedOf as ot, EntityTimelineProjectionError as p, reassignSpeechesToVideoClipsByTime as q, generatePartId as r, ENTITY_TIMELINE_PROJECTION_SCHEMA_VERSION as rt, createMirrorVideoDocumentAdapter as s, partUnionToDraft as st, PlainMemoryAdapter as t, safeDurationMs as tt, projectEntityTimeline as u, base64ToBytes as ut, assemblePhoneticScriptContent as v, isJsonObject as w, selectAudioScriptSegments as x, assembleScriptText as y, LANE_KINDS_IN_STACK_ORDER as z };
|