@cliangdev/conductor 0.21.0 → 0.23.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/assets/claude/skills/conductor-creative/SKILL.md +168 -0
- package/assets/claude/skills/conductor-publisher/SKILL.md +4 -0
- package/assets/cli-manifest.json +99 -0
- package/dist/commands/creative.d.ts +3 -0
- package/dist/commands/creative.d.ts.map +1 -0
- package/dist/commands/creative.js +63 -0
- package/dist/commands/creative.js.map +1 -0
- package/dist/creative/assertions.js +221 -0
- package/dist/creative/font.js +39 -0
- package/dist/creative/frame.css +126 -0
- package/dist/creative/frame.html +29 -0
- package/dist/creative/frame.js +76 -0
- package/dist/creative/job/render.mjs +203 -0
- package/dist/creative/job/server.mjs +72 -0
- package/dist/creative/job/transport.mjs +167 -0
- package/dist/creative/layouts/bleed/layout.css +47 -0
- package/dist/creative/layouts/bleed/layout.json +17 -0
- package/dist/creative/layouts/card/layout.css +24 -0
- package/dist/creative/layouts/card/layout.json +17 -0
- package/dist/creative/layouts/index.js +62 -0
- package/dist/creative/layouts/index.json +1 -0
- package/dist/creative/layouts/split/layout.css +22 -0
- package/dist/creative/layouts/split/layout.json +17 -0
- package/dist/creative/layouts/stacked/layout.css +40 -0
- package/dist/creative/layouts/stacked/layout.json +23 -0
- package/dist/creative/placements.js +228 -0
- package/dist/creative/placements.json +268 -0
- package/dist/creative/render.js +457 -0
- package/dist/creative/sheet.html +27 -0
- package/dist/creative/sheet.js +94 -0
- package/dist/creative/styles.css +314 -0
- package/dist/creative/tokens.css +33 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/creative-browser.d.ts +25 -0
- package/dist/lib/creative-browser.d.ts.map +1 -0
- package/dist/lib/creative-browser.js +36 -0
- package/dist/lib/creative-browser.js.map +1 -0
- package/dist/lib/creative-id.d.ts +8 -0
- package/dist/lib/creative-id.d.ts.map +1 -0
- package/dist/lib/creative-id.js +14 -0
- package/dist/lib/creative-id.js.map +1 -0
- package/dist/lib/creative-render.d.ts +38 -0
- package/dist/lib/creative-render.d.ts.map +1 -0
- package/dist/lib/creative-render.js +95 -0
- package/dist/lib/creative-render.js.map +1 -0
- package/dist/lib/image-dimensions.d.ts +20 -0
- package/dist/lib/image-dimensions.d.ts.map +1 -0
- package/dist/lib/image-dimensions.js +115 -0
- package/dist/lib/image-dimensions.js.map +1 -0
- package/dist/lib/media-probe.d.ts +55 -0
- package/dist/lib/media-probe.d.ts.map +1 -0
- package/dist/lib/media-probe.js +221 -0
- package/dist/lib/media-probe.js.map +1 -0
- package/dist/lib/plugin-assets.d.ts.map +1 -1
- package/dist/lib/plugin-assets.js +1 -0
- package/dist/lib/plugin-assets.js.map +1 -1
- package/dist/lib/poster-sheet.d.ts +10 -0
- package/dist/lib/poster-sheet.d.ts.map +1 -0
- package/dist/lib/poster-sheet.js +57 -0
- package/dist/lib/poster-sheet.js.map +1 -0
- package/dist/mcp/index.d.ts +18 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +313 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/tools/creatives.d.ts +114 -0
- package/dist/mcp/tools/creatives.d.ts.map +1 -0
- package/dist/mcp/tools/creatives.js +477 -0
- package/dist/mcp/tools/creatives.js.map +1 -0
- package/dist/mcp/tools/insights.d.ts.map +1 -1
- package/dist/mcp/tools/insights.js +14 -0
- package/dist/mcp/tools/insights.js.map +1 -1
- package/package.json +6 -3
- package/scripts/build-creative.mjs +93 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads an image's format and pixel dimensions straight from its file header — no decode, no
|
|
3
|
+
* external image library. Used by `upload_creative_photo`, which needs width/height and content
|
|
4
|
+
* type before minting an upload (the backend's `CreateCreativePhotoRequest` requires both, since
|
|
5
|
+
* Conductor has no server-side image pipeline and WebP in particular cannot be probed there).
|
|
6
|
+
*
|
|
7
|
+
* Covers exactly the three formats the Creative photo API accepts (image/png, image/jpeg,
|
|
8
|
+
* image/webp). Returns `null` for anything else or a header that doesn't parse — callers should
|
|
9
|
+
* treat that as "not a usable photo" rather than guessing.
|
|
10
|
+
*/
|
|
11
|
+
function readUInt32BE(bytes, offset) {
|
|
12
|
+
return ((bytes[offset] << 24) | (bytes[offset + 1] << 16) | (bytes[offset + 2] << 8) | bytes[offset + 3]) >>> 0;
|
|
13
|
+
}
|
|
14
|
+
function readUInt16BE(bytes, offset) {
|
|
15
|
+
return ((bytes[offset] << 8) | bytes[offset + 1]) >>> 0;
|
|
16
|
+
}
|
|
17
|
+
function readUInt24LE(bytes, offset) {
|
|
18
|
+
return (bytes[offset] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16)) >>> 0;
|
|
19
|
+
}
|
|
20
|
+
const PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
|
|
21
|
+
function readPng(bytes) {
|
|
22
|
+
if (bytes.length < 24)
|
|
23
|
+
return null;
|
|
24
|
+
if (!PNG_SIGNATURE.every((b, i) => bytes[i] === b))
|
|
25
|
+
return null;
|
|
26
|
+
// IHDR is always the first chunk: 4-byte length, 4-byte type "IHDR", then width/height (4 bytes
|
|
27
|
+
// each, big-endian) at offsets 16 and 20.
|
|
28
|
+
return { contentType: 'image/png', width: readUInt32BE(bytes, 16), height: readUInt32BE(bytes, 20) };
|
|
29
|
+
}
|
|
30
|
+
// SOFn (start-of-frame) markers that carry dimensions. Excludes 0xC4 (DHT), 0xC8 (JPG extension,
|
|
31
|
+
// unused in practice) and 0xCC (DAC) — those share the 0xC0-0xCF range but aren't SOF markers.
|
|
32
|
+
const JPEG_SOF_MARKERS = new Set([0xc0, 0xc1, 0xc2, 0xc3, 0xc5, 0xc6, 0xc7, 0xc9, 0xca, 0xcb, 0xcd, 0xce, 0xcf]);
|
|
33
|
+
// Standalone markers with no length-prefixed payload following them.
|
|
34
|
+
const JPEG_NO_PAYLOAD_MARKERS = new Set([0x01, 0xd8, 0xd9]);
|
|
35
|
+
function readJpeg(bytes) {
|
|
36
|
+
if (bytes.length < 4 || bytes[0] !== 0xff || bytes[1] !== 0xd8)
|
|
37
|
+
return null;
|
|
38
|
+
let offset = 2;
|
|
39
|
+
while (offset + 4 <= bytes.length) {
|
|
40
|
+
if (bytes[offset] !== 0xff) {
|
|
41
|
+
offset += 1; // resync on a stray fill byte (0xFF00 stuffing, etc.)
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const marker = bytes[offset + 1];
|
|
45
|
+
if (marker === 0xd9)
|
|
46
|
+
break; // EOI, no dimensions found
|
|
47
|
+
if (JPEG_NO_PAYLOAD_MARKERS.has(marker) || (marker >= 0xd0 && marker <= 0xd7)) {
|
|
48
|
+
offset += 2;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const segmentLength = readUInt16BE(bytes, offset + 2);
|
|
52
|
+
if (JPEG_SOF_MARKERS.has(marker)) {
|
|
53
|
+
const payloadStart = offset + 4;
|
|
54
|
+
if (payloadStart + 5 > bytes.length)
|
|
55
|
+
return null;
|
|
56
|
+
const height = readUInt16BE(bytes, payloadStart + 1);
|
|
57
|
+
const width = readUInt16BE(bytes, payloadStart + 3);
|
|
58
|
+
return { contentType: 'image/jpeg', width, height };
|
|
59
|
+
}
|
|
60
|
+
if (segmentLength < 2)
|
|
61
|
+
return null; // malformed — refuse to loop forever
|
|
62
|
+
offset += 2 + segmentLength;
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
function isRiffWebp(bytes) {
|
|
67
|
+
return (bytes.length >= 21 && // enough to read the fourCC at 12-15 and reach the chunk data at 20
|
|
68
|
+
bytes[0] === 0x52 &&
|
|
69
|
+
bytes[1] === 0x49 &&
|
|
70
|
+
bytes[2] === 0x46 &&
|
|
71
|
+
bytes[3] === 0x46 && // "RIFF"
|
|
72
|
+
bytes[8] === 0x57 &&
|
|
73
|
+
bytes[9] === 0x45 &&
|
|
74
|
+
bytes[10] === 0x42 &&
|
|
75
|
+
bytes[11] === 0x50 // "WEBP"
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
function readWebp(bytes) {
|
|
79
|
+
if (!isRiffWebp(bytes))
|
|
80
|
+
return null;
|
|
81
|
+
const fourCc = String.fromCharCode(bytes[12], bytes[13], bytes[14], bytes[15]);
|
|
82
|
+
const chunkStart = 20; // after the 8-byte RIFF header, "WEBP", fourCC and 4-byte chunk size
|
|
83
|
+
if (fourCc === 'VP8X') {
|
|
84
|
+
// flags (1 byte) + reserved (3 bytes), then 3-byte little-endian (width-1) and (height-1).
|
|
85
|
+
if (bytes.length < chunkStart + 10)
|
|
86
|
+
return null;
|
|
87
|
+
return {
|
|
88
|
+
contentType: 'image/webp',
|
|
89
|
+
width: readUInt24LE(bytes, chunkStart + 4) + 1,
|
|
90
|
+
height: readUInt24LE(bytes, chunkStart + 7) + 1,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (fourCc === 'VP8 ') {
|
|
94
|
+
// 3-byte frame tag + 3-byte start code (0x9d 0x01 0x2a), then 14-bit width/height, little-endian.
|
|
95
|
+
if (bytes.length < chunkStart + 10)
|
|
96
|
+
return null;
|
|
97
|
+
const widthRaw = bytes[chunkStart + 6] | (bytes[chunkStart + 7] << 8);
|
|
98
|
+
const heightRaw = bytes[chunkStart + 8] | (bytes[chunkStart + 9] << 8);
|
|
99
|
+
return { contentType: 'image/webp', width: widthRaw & 0x3fff, height: heightRaw & 0x3fff };
|
|
100
|
+
}
|
|
101
|
+
if (fourCc === 'VP8L') {
|
|
102
|
+
// 1-byte signature (0x2f), then a little-endian 32-bit field: 14-bit (width-1), 14-bit (height-1).
|
|
103
|
+
if (bytes.length < chunkStart + 5)
|
|
104
|
+
return null;
|
|
105
|
+
const bits = (bytes[chunkStart + 1] | (bytes[chunkStart + 2] << 8) | (bytes[chunkStart + 3] << 16) | (bytes[chunkStart + 4] << 24)) >>> 0;
|
|
106
|
+
return { contentType: 'image/webp', width: (bits & 0x3fff) + 1, height: ((bits >>> 14) & 0x3fff) + 1 };
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
/** Sniffs the format from magic bytes and parses width/height from the header. `null` when the
|
|
111
|
+
* bytes aren't a recognizable PNG, JPEG or WebP, or the header is truncated/malformed. */
|
|
112
|
+
export function readImageDimensions(bytes) {
|
|
113
|
+
return readPng(bytes) ?? readJpeg(bytes) ?? readWebp(bytes);
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=image-dimensions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-dimensions.js","sourceRoot":"","sources":["../../src/lib/image-dimensions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAUH,SAAS,YAAY,CAAC,KAAiB,EAAE,MAAc;IACrD,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,CAAE,IAAI,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,IAAI,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,KAAK,CAAC,CAAA;AACrH,CAAC;AAED,SAAS,YAAY,CAAC,KAAiB,EAAE,MAAc;IACrD,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,CAAE,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,KAAK,CAAC,CAAA;AAC3D,CAAC;AAED,SAAS,YAAY,CAAC,KAAiB,EAAE,MAAc;IACrD,OAAO,CAAC,KAAK,CAAC,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAA;AACxF,CAAC;AAED,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;AAEtE,SAAS,OAAO,CAAC,KAAiB;IAChC,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,IAAI,CAAA;IAClC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAA;IAC/D,gGAAgG;IAChG,0CAA0C;IAC1C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAA;AACtG,CAAC;AAED,iGAAiG;AACjG,+FAA+F;AAC/F,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAA;AAChH,qEAAqE;AACrE,MAAM,uBAAuB,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAA;AAE3D,SAAS,QAAQ,CAAC,KAAiB;IACjC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAC3E,IAAI,MAAM,GAAG,CAAC,CAAA;IACd,OAAO,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QAClC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;YAC3B,MAAM,IAAI,CAAC,CAAA,CAAC,sDAAsD;YAClE,SAAQ;QACV,CAAC;QACD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,CAAA;QACjC,IAAI,MAAM,KAAK,IAAI;YAAE,MAAK,CAAC,2BAA2B;QACtD,IAAI,uBAAuB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,CAAC,EAAE,CAAC;YAC9E,MAAM,IAAI,CAAC,CAAA;YACX,SAAQ;QACV,CAAC;QACD,MAAM,aAAa,GAAG,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC,CAAA;QACrD,IAAI,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACjC,MAAM,YAAY,GAAG,MAAM,GAAG,CAAC,CAAA;YAC/B,IAAI,YAAY,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAA;YAChD,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,CAAC,CAAC,CAAA;YACpD,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,CAAC,CAAC,CAAA;YACnD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;QACrD,CAAC;QACD,IAAI,aAAa,GAAG,CAAC;YAAE,OAAO,IAAI,CAAA,CAAC,qCAAqC;QACxE,MAAM,IAAI,CAAC,GAAG,aAAa,CAAA;IAC7B,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAS,UAAU,CAAC,KAAiB;IACnC,OAAO,CACL,KAAK,CAAC,MAAM,IAAI,EAAE,IAAI,oEAAoE;QAC1F,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;QACjB,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;QACjB,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;QACjB,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,SAAS;QAC9B,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;QACjB,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;QACjB,KAAK,CAAC,EAAE,CAAC,KAAK,IAAI;QAClB,KAAK,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,SAAS;KAC7B,CAAA;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,KAAiB;IACjC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACnC,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAE,EAAE,KAAK,CAAC,EAAE,CAAE,EAAE,KAAK,CAAC,EAAE,CAAE,EAAE,KAAK,CAAC,EAAE,CAAE,CAAC,CAAA;IAClF,MAAM,UAAU,GAAG,EAAE,CAAA,CAAC,qEAAqE;IAE3F,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,2FAA2F;QAC3F,IAAI,KAAK,CAAC,MAAM,GAAG,UAAU,GAAG,EAAE;YAAE,OAAO,IAAI,CAAA;QAC/C,OAAO;YACL,WAAW,EAAE,YAAY;YACzB,KAAK,EAAE,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC;YAC9C,MAAM,EAAE,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC;SAChD,CAAA;IACH,CAAC;IACD,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,kGAAkG;QAClG,IAAI,KAAK,CAAC,MAAM,GAAG,UAAU,GAAG,EAAE;YAAE,OAAO,IAAI,CAAA;QAC/C,MAAM,QAAQ,GAAG,KAAK,CAAC,UAAU,GAAG,CAAC,CAAE,GAAG,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAE,IAAI,CAAC,CAAC,CAAA;QACvE,MAAM,SAAS,GAAG,KAAK,CAAC,UAAU,GAAG,CAAC,CAAE,GAAG,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAE,IAAI,CAAC,CAAC,CAAA;QACxE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,GAAG,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,MAAM,EAAE,CAAA;IAC5F,CAAC;IACD,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,mGAAmG;QACnG,IAAI,KAAK,CAAC,MAAM,GAAG,UAAU,GAAG,CAAC;YAAE,OAAO,IAAI,CAAA;QAC9C,MAAM,IAAI,GACR,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAE,GAAG,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAE,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAE,IAAI,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAA;QAClI,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,CAAA;IACxG,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;0FAC0F;AAC1F,MAAM,UAAU,mBAAmB,CAAC,KAAiB;IACnD,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAA;AAC7D,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export type MediaKind = 'IMAGE' | 'VIDEO' | 'AUDIO';
|
|
2
|
+
export interface MediaProbeResult {
|
|
3
|
+
kind: MediaKind;
|
|
4
|
+
contentType: string;
|
|
5
|
+
width?: number;
|
|
6
|
+
height?: number;
|
|
7
|
+
durationSeconds?: number;
|
|
8
|
+
hasAudio?: boolean;
|
|
9
|
+
codec?: string;
|
|
10
|
+
}
|
|
11
|
+
interface FfprobeStream {
|
|
12
|
+
codec_type?: string;
|
|
13
|
+
codec_name?: string;
|
|
14
|
+
width?: number;
|
|
15
|
+
height?: number;
|
|
16
|
+
duration?: string;
|
|
17
|
+
nb_frames?: string;
|
|
18
|
+
disposition?: {
|
|
19
|
+
attached_pic?: number;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
interface FfprobeFormat {
|
|
23
|
+
duration?: string;
|
|
24
|
+
format_name?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface FfprobeJson {
|
|
27
|
+
streams?: FfprobeStream[];
|
|
28
|
+
format?: FfprobeFormat;
|
|
29
|
+
}
|
|
30
|
+
export declare const NO_FFPROBE_HINT: string;
|
|
31
|
+
export declare const NO_FFMPEG_HINT: string;
|
|
32
|
+
/**
|
|
33
|
+
* Pure parsing of an already-fetched `ffprobe -show_streams -show_format` JSON document — split out
|
|
34
|
+
* from `probeMedia` so tests can exercise it against fixture JSON without spawning a real process.
|
|
35
|
+
*
|
|
36
|
+
* A video stream marked `disposition.attached_pic` is cover art embedded in an audio file (common in
|
|
37
|
+
* mp3/m4a), not a picture track — it's excluded from the video-stream check so a song with cover art
|
|
38
|
+
* doesn't get classified as VIDEO. A video stream with no duration and at most one frame is a still
|
|
39
|
+
* image ffprobe happened to be pointed at (format `image2`/`png_pipe`/etc.), not a video.
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseProbeJson(json: FfprobeJson, filePath: string): MediaProbeResult;
|
|
42
|
+
export declare function resolveFfprobePath(): Promise<string>;
|
|
43
|
+
export declare function resolveFfmpegPath(): Promise<string>;
|
|
44
|
+
/** Test-only: binary discovery caches its result for the process lifetime (real launches are slow
|
|
45
|
+
* enough to matter within one CLI/MCP run), so tests that vary system-binary availability need to
|
|
46
|
+
* reset it between cases. */
|
|
47
|
+
export declare function resetMediaBinaryCache(): void;
|
|
48
|
+
/** Probes a local media file with ffprobe and classifies it as IMAGE, VIDEO or AUDIO. Throws
|
|
49
|
+
* `NO_FFPROBE_HINT` when no usable ffprobe binary can be found at all. */
|
|
50
|
+
export declare function probeMedia(filePath: string): Promise<MediaProbeResult>;
|
|
51
|
+
/** Extracts a single JPEG poster frame from a video at `atSeconds` (default 1s). Falls back to 0s
|
|
52
|
+
* when the clip is shorter than that (or the seek otherwise fails) rather than giving up. */
|
|
53
|
+
export declare function extractPoster(videoPath: string, outJpg: string, atSeconds?: number): Promise<void>;
|
|
54
|
+
export {};
|
|
55
|
+
//# sourceMappingURL=media-probe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-probe.d.ts","sourceRoot":"","sources":["../../src/lib/media-probe.ts"],"names":[],"mappings":"AAqBA,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAA;AAEnD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,SAAS,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,UAAU,aAAa;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CACxC;AAED,UAAU,aAAa;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,aAAa,EAAE,CAAA;IACzB,MAAM,CAAC,EAAE,aAAa,CAAA;CACvB;AAED,eAAO,MAAM,eAAe,QAE8D,CAAA;AAC1F,eAAO,MAAM,cAAc,QAEiD,CAAA;AA0B5E;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAmCpF;AA8BD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAiB1D;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC,CAgBzD;AAED;;6BAE6B;AAC7B,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AAwBD;0EAC0E;AAC1E,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAI5E;AAgBD;6FAC6F;AAC7F,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,SAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAQnG"}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inspects and extracts frames from video/audio Creative media with ffprobe/ffmpeg: a system binary
|
|
3
|
+
* on PATH first (respects whatever the user already has — no bundled Chromium-sized re-download),
|
|
4
|
+
* else the ones bundled by `ffprobe-static`/`ffmpeg-static` (lazy-imported so requiring them doesn't
|
|
5
|
+
* add to MCP server startup cost for sessions that never touch a video/audio Creative). Mirrors the
|
|
6
|
+
* browser discovery in creative-browser.ts.
|
|
7
|
+
*
|
|
8
|
+
* `upload_creative_media` uses `probeMedia` for VIDEO/AUDIO (images keep the existing magic-byte
|
|
9
|
+
* header parse in image-dimensions.ts — no decode needed there); it and `render_creative`/CLIP use
|
|
10
|
+
* `extractPoster` for a VIDEO's poster JPEG.
|
|
11
|
+
*/
|
|
12
|
+
import { spawn } from 'node:child_process';
|
|
13
|
+
import { existsSync } from 'node:fs';
|
|
14
|
+
import { extname } from 'node:path';
|
|
15
|
+
import { createRequire } from 'node:module';
|
|
16
|
+
// ffprobe-static/ffmpeg-static are plain CJS with no published types; `require` (rather than a typed
|
|
17
|
+
// `import`) keeps them lazy (only resolved the first time a binary is actually needed) without fighting
|
|
18
|
+
// TypeScript over declarations for an untyped module.
|
|
19
|
+
const require = createRequire(import.meta.url);
|
|
20
|
+
export const NO_FFPROBE_HINT = 'No ffprobe could be found to inspect this media. Install ffmpeg (it includes ffprobe) system-wide, ' +
|
|
21
|
+
'or reinstall @cliangdev/conductor so its bundled ffprobe-static dependency is present.';
|
|
22
|
+
export const NO_FFMPEG_HINT = 'No ffmpeg could be found to process this media. Install ffmpeg system-wide, or reinstall ' +
|
|
23
|
+
'@cliangdev/conductor so its bundled ffmpeg-static dependency is present.';
|
|
24
|
+
const EXTENSION_CONTENT_TYPES = {
|
|
25
|
+
'.mp4': 'video/mp4',
|
|
26
|
+
'.m4v': 'video/mp4',
|
|
27
|
+
'.mov': 'video/quicktime',
|
|
28
|
+
'.webm': 'video/webm',
|
|
29
|
+
'.mp3': 'audio/mpeg',
|
|
30
|
+
'.m4a': 'audio/mp4',
|
|
31
|
+
'.aac': 'audio/aac',
|
|
32
|
+
'.wav': 'audio/wav',
|
|
33
|
+
'.png': 'image/png',
|
|
34
|
+
'.jpg': 'image/jpeg',
|
|
35
|
+
'.jpeg': 'image/jpeg',
|
|
36
|
+
'.webp': 'image/webp',
|
|
37
|
+
};
|
|
38
|
+
function contentTypeFor(kind, filePath, codecName) {
|
|
39
|
+
const ext = extname(filePath).toLowerCase();
|
|
40
|
+
const byExtension = EXTENSION_CONTENT_TYPES[ext];
|
|
41
|
+
if (byExtension)
|
|
42
|
+
return byExtension;
|
|
43
|
+
if (kind === 'VIDEO')
|
|
44
|
+
return 'video/mp4';
|
|
45
|
+
if (kind === 'AUDIO')
|
|
46
|
+
return codecName === 'mp3' ? 'audio/mpeg' : 'audio/mp4';
|
|
47
|
+
return codecName === 'png' ? 'image/png' : 'image/jpeg';
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Pure parsing of an already-fetched `ffprobe -show_streams -show_format` JSON document — split out
|
|
51
|
+
* from `probeMedia` so tests can exercise it against fixture JSON without spawning a real process.
|
|
52
|
+
*
|
|
53
|
+
* A video stream marked `disposition.attached_pic` is cover art embedded in an audio file (common in
|
|
54
|
+
* mp3/m4a), not a picture track — it's excluded from the video-stream check so a song with cover art
|
|
55
|
+
* doesn't get classified as VIDEO. A video stream with no duration and at most one frame is a still
|
|
56
|
+
* image ffprobe happened to be pointed at (format `image2`/`png_pipe`/etc.), not a video.
|
|
57
|
+
*/
|
|
58
|
+
export function parseProbeJson(json, filePath) {
|
|
59
|
+
const streams = json.streams ?? [];
|
|
60
|
+
const format = json.format ?? {};
|
|
61
|
+
const videoStreams = streams.filter((s) => s.codec_type === 'video' && s.disposition?.attached_pic !== 1);
|
|
62
|
+
const audioStreams = streams.filter((s) => s.codec_type === 'audio');
|
|
63
|
+
if (videoStreams.length > 0) {
|
|
64
|
+
const v = videoStreams[0];
|
|
65
|
+
const durationRaw = format.duration ?? v.duration;
|
|
66
|
+
const duration = durationRaw ? parseFloat(durationRaw) : undefined;
|
|
67
|
+
const nbFrames = v.nb_frames ? parseInt(v.nb_frames, 10) : undefined;
|
|
68
|
+
const looksLikeStillImage = !duration && (nbFrames === undefined || nbFrames <= 1);
|
|
69
|
+
if (looksLikeStillImage && audioStreams.length === 0) {
|
|
70
|
+
return { kind: 'IMAGE', contentType: contentTypeFor('IMAGE', filePath, v.codec_name), width: v.width, height: v.height, codec: v.codec_name };
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
kind: 'VIDEO',
|
|
74
|
+
contentType: contentTypeFor('VIDEO', filePath, v.codec_name),
|
|
75
|
+
width: v.width,
|
|
76
|
+
height: v.height,
|
|
77
|
+
durationSeconds: duration,
|
|
78
|
+
hasAudio: audioStreams.length > 0,
|
|
79
|
+
codec: v.codec_name,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (audioStreams.length > 0) {
|
|
83
|
+
const a = audioStreams[0];
|
|
84
|
+
const durationRaw = format.duration ?? a.duration;
|
|
85
|
+
const duration = durationRaw ? parseFloat(durationRaw) : undefined;
|
|
86
|
+
return { kind: 'AUDIO', contentType: contentTypeFor('AUDIO', filePath, a.codec_name), durationSeconds: duration, codec: a.codec_name };
|
|
87
|
+
}
|
|
88
|
+
throw new Error(`ffprobe found no usable video or audio stream in "${filePath}"`);
|
|
89
|
+
}
|
|
90
|
+
let cachedFfprobePath;
|
|
91
|
+
let cachedFfmpegPath;
|
|
92
|
+
/** True once the named command launches at all (any exit code) rather than failing to spawn
|
|
93
|
+
* (ENOENT) — that's enough to know it's a real binary on PATH, without caring what `-version`
|
|
94
|
+
* prints. Bounded by a timeout so a binary that launches but hangs doesn't wedge discovery forever. */
|
|
95
|
+
function systemBinaryAvailable(command) {
|
|
96
|
+
return new Promise((resolve) => {
|
|
97
|
+
let settled = false;
|
|
98
|
+
const finish = (ok) => {
|
|
99
|
+
if (settled)
|
|
100
|
+
return;
|
|
101
|
+
settled = true;
|
|
102
|
+
resolve(ok);
|
|
103
|
+
};
|
|
104
|
+
let child;
|
|
105
|
+
try {
|
|
106
|
+
child = spawn(command, ['-version'], { stdio: 'ignore' });
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
finish(false);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
child.on('error', () => finish(false));
|
|
113
|
+
child.on('exit', (code) => finish(code === 0));
|
|
114
|
+
const timer = setTimeout(() => finish(false), 5000);
|
|
115
|
+
timer.unref?.();
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
export async function resolveFfprobePath() {
|
|
119
|
+
if (cachedFfprobePath)
|
|
120
|
+
return cachedFfprobePath;
|
|
121
|
+
if (await systemBinaryAvailable('ffprobe')) {
|
|
122
|
+
cachedFfprobePath = 'ffprobe';
|
|
123
|
+
return cachedFfprobePath;
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
const mod = require('ffprobe-static');
|
|
127
|
+
const bundledPath = mod.path;
|
|
128
|
+
if (bundledPath && existsSync(bundledPath)) {
|
|
129
|
+
cachedFfprobePath = bundledPath;
|
|
130
|
+
return bundledPath;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// fall through to the error below
|
|
135
|
+
}
|
|
136
|
+
throw new Error(NO_FFPROBE_HINT);
|
|
137
|
+
}
|
|
138
|
+
export async function resolveFfmpegPath() {
|
|
139
|
+
if (cachedFfmpegPath)
|
|
140
|
+
return cachedFfmpegPath;
|
|
141
|
+
if (await systemBinaryAvailable('ffmpeg')) {
|
|
142
|
+
cachedFfmpegPath = 'ffmpeg';
|
|
143
|
+
return cachedFfmpegPath;
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
const bundledPath = require('ffmpeg-static');
|
|
147
|
+
if (bundledPath && existsSync(bundledPath)) {
|
|
148
|
+
cachedFfmpegPath = bundledPath;
|
|
149
|
+
return bundledPath;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// fall through to the error below
|
|
154
|
+
}
|
|
155
|
+
throw new Error(NO_FFMPEG_HINT);
|
|
156
|
+
}
|
|
157
|
+
/** Test-only: binary discovery caches its result for the process lifetime (real launches are slow
|
|
158
|
+
* enough to matter within one CLI/MCP run), so tests that vary system-binary availability need to
|
|
159
|
+
* reset it between cases. */
|
|
160
|
+
export function resetMediaBinaryCache() {
|
|
161
|
+
cachedFfprobePath = undefined;
|
|
162
|
+
cachedFfmpegPath = undefined;
|
|
163
|
+
}
|
|
164
|
+
function runFfprobe(ffprobePath, filePath) {
|
|
165
|
+
return new Promise((resolve, reject) => {
|
|
166
|
+
const child = spawn(ffprobePath, ['-v', 'error', '-print_format', 'json', '-show_streams', '-show_format', filePath]);
|
|
167
|
+
let stdout = '';
|
|
168
|
+
let stderr = '';
|
|
169
|
+
child.stdout.on('data', (d) => (stdout += d));
|
|
170
|
+
child.stderr.on('data', (d) => (stderr += d));
|
|
171
|
+
child.on('error', reject);
|
|
172
|
+
child.on('exit', (code) => {
|
|
173
|
+
if (code !== 0) {
|
|
174
|
+
reject(new Error(`ffprobe failed (exit ${code}) for "${filePath}": ${stderr.trim() || 'no error output'}`));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
resolve(JSON.parse(stdout));
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
reject(new Error(`ffprobe produced unparsable JSON for "${filePath}": ${err instanceof Error ? err.message : String(err)}`));
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
/** Probes a local media file with ffprobe and classifies it as IMAGE, VIDEO or AUDIO. Throws
|
|
187
|
+
* `NO_FFPROBE_HINT` when no usable ffprobe binary can be found at all. */
|
|
188
|
+
export async function probeMedia(filePath) {
|
|
189
|
+
const ffprobePath = await resolveFfprobePath();
|
|
190
|
+
const json = await runFfprobe(ffprobePath, filePath);
|
|
191
|
+
return parseProbeJson(json, filePath);
|
|
192
|
+
}
|
|
193
|
+
function runFfmpegPoster(ffmpegPath, videoPath, outJpg, atSeconds) {
|
|
194
|
+
return new Promise((resolve, reject) => {
|
|
195
|
+
const args = ['-y', '-ss', String(atSeconds), '-i', videoPath, '-frames:v', '1', '-q:v', '2', outJpg];
|
|
196
|
+
const child = spawn(ffmpegPath, args, { stdio: ['ignore', 'ignore', 'pipe'] });
|
|
197
|
+
let stderr = '';
|
|
198
|
+
child.stderr.on('data', (d) => (stderr += d));
|
|
199
|
+
child.on('error', reject);
|
|
200
|
+
child.on('exit', (code) => {
|
|
201
|
+
if (code === 0 && existsSync(outJpg))
|
|
202
|
+
resolve();
|
|
203
|
+
else
|
|
204
|
+
reject(new Error(`ffmpeg poster extraction failed (exit ${code}) for "${videoPath}": ${stderr.trim().slice(-2000) || 'no error output'}`));
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
/** Extracts a single JPEG poster frame from a video at `atSeconds` (default 1s). Falls back to 0s
|
|
209
|
+
* when the clip is shorter than that (or the seek otherwise fails) rather than giving up. */
|
|
210
|
+
export async function extractPoster(videoPath, outJpg, atSeconds = 1) {
|
|
211
|
+
const ffmpegPath = await resolveFfmpegPath();
|
|
212
|
+
try {
|
|
213
|
+
await runFfmpegPoster(ffmpegPath, videoPath, outJpg, atSeconds);
|
|
214
|
+
}
|
|
215
|
+
catch (err) {
|
|
216
|
+
if (atSeconds === 0)
|
|
217
|
+
throw err;
|
|
218
|
+
await runFfmpegPoster(ffmpegPath, videoPath, outJpg, 0);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=media-probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-probe.js","sourceRoot":"","sources":["../../src/lib/media-probe.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-assets.d.ts","sourceRoot":"","sources":["../../src/lib/plugin-assets.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin-assets.d.ts","sourceRoot":"","sources":["../../src/lib/plugin-assets.ts"],"names":[],"mappings":"AA4CA,wBAAgB,cAAc,IAAI,MAAM,CAKvC;AAmCD,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,CAAA;AAE/D;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,GACnB,aAAa,CA4Cf;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,MAAM,GACrB;IAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CA0B9D"}
|
|
@@ -11,6 +11,7 @@ const PLUGIN_FILES = [
|
|
|
11
11
|
'skills/conductor-ux-ui-design/references/design-tokens.md',
|
|
12
12
|
'skills/conductor-coder/SKILL.md',
|
|
13
13
|
'skills/conductor-publisher/SKILL.md',
|
|
14
|
+
'skills/conductor-creative/SKILL.md',
|
|
14
15
|
];
|
|
15
16
|
// Paths removed in previous versions — deleted on `conductor init` to clean up stale installs.
|
|
16
17
|
const LEGACY_PATHS = [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-assets.js","sourceRoot":"","sources":["../../src/lib/plugin-assets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AACxB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAA;AAEnC,MAAM,YAAY,GAAG;IACnB,2BAA2B;IAC3B,iCAAiC;IACjC,2BAA2B;IAC3B,gCAAgC;IAChC,gCAAgC;IAChC,wCAAwC;IACxC,2DAA2D;IAC3D,iCAAiC;IACjC,qCAAqC;
|
|
1
|
+
{"version":3,"file":"plugin-assets.js","sourceRoot":"","sources":["../../src/lib/plugin-assets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** One `scale=-2:360` per input, then an hstack chaining every scaled output left to right. A single
|
|
2
|
+
* input skips the stack (nothing to stack) and just scales. */
|
|
3
|
+
export declare function buildSheetFilterComplex(count: number): string;
|
|
4
|
+
/**
|
|
5
|
+
* Composes the given local poster JPEGs left to right into one JPEG sheet, each scaled to ~360px
|
|
6
|
+
* high. Throws on any ffmpeg failure (no binary, a bad/corrupt poster, etc.) — callers should catch
|
|
7
|
+
* this and fall back to showing a single poster on its own rather than nothing.
|
|
8
|
+
*/
|
|
9
|
+
export declare function composePosterSheet(posterPaths: string[]): Promise<Buffer>;
|
|
10
|
+
//# sourceMappingURL=poster-sheet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poster-sheet.d.ts","sourceRoot":"","sources":["../../src/lib/poster-sheet.ts"],"names":[],"mappings":"AAiBA;+DAC+D;AAC/D,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAK7D;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAyB/E"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composes several poster JPEGs (one per CLIP placement) into a single small "key moments" sheet
|
|
3
|
+
* image with ffmpeg's hstack filter, so `preview_creative` has something to show for a CLIP
|
|
4
|
+
* creative — which has no server-rendered contact sheet the way a STILL creative's previewOnly
|
|
5
|
+
* render does (rendering one locally isn't possible either: CLIP has no browser-driven render path,
|
|
6
|
+
* see render_creative in tools/creatives.ts). Shares ffmpeg discovery with media-probe.ts rather
|
|
7
|
+
* than duplicating it.
|
|
8
|
+
*/
|
|
9
|
+
import { spawn } from 'node:child_process';
|
|
10
|
+
import { readFile, unlink } from 'node:fs/promises';
|
|
11
|
+
import { tmpdir } from 'node:os';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
import { randomUUID } from 'node:crypto';
|
|
14
|
+
import { resolveFfmpegPath } from './media-probe.js';
|
|
15
|
+
const SHEET_HEIGHT = 360;
|
|
16
|
+
/** One `scale=-2:360` per input, then an hstack chaining every scaled output left to right. A single
|
|
17
|
+
* input skips the stack (nothing to stack) and just scales. */
|
|
18
|
+
export function buildSheetFilterComplex(count) {
|
|
19
|
+
if (count === 1)
|
|
20
|
+
return `[0:v]scale=-2:${SHEET_HEIGHT}[v]`;
|
|
21
|
+
const scales = Array.from({ length: count }, (_, i) => `[${i}:v]scale=-2:${SHEET_HEIGHT}[v${i}]`).join(';');
|
|
22
|
+
const stackInputs = Array.from({ length: count }, (_, i) => `[v${i}]`).join('');
|
|
23
|
+
return `${scales};${stackInputs}hstack=inputs=${count}[v]`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Composes the given local poster JPEGs left to right into one JPEG sheet, each scaled to ~360px
|
|
27
|
+
* high. Throws on any ffmpeg failure (no binary, a bad/corrupt poster, etc.) — callers should catch
|
|
28
|
+
* this and fall back to showing a single poster on its own rather than nothing.
|
|
29
|
+
*/
|
|
30
|
+
export async function composePosterSheet(posterPaths) {
|
|
31
|
+
if (posterPaths.length === 0) {
|
|
32
|
+
throw new Error('composePosterSheet needs at least one poster image');
|
|
33
|
+
}
|
|
34
|
+
const ffmpegPath = await resolveFfmpegPath();
|
|
35
|
+
const outPath = join(tmpdir(), `conductor-sheet-${randomUUID()}.jpg`);
|
|
36
|
+
const inputs = posterPaths.flatMap((p) => ['-i', p]);
|
|
37
|
+
const args = ['-y', ...inputs, '-filter_complex', buildSheetFilterComplex(posterPaths.length), '-map', '[v]', '-q:v', '3', outPath];
|
|
38
|
+
await new Promise((resolve, reject) => {
|
|
39
|
+
const child = spawn(ffmpegPath, args, { stdio: ['ignore', 'ignore', 'pipe'] });
|
|
40
|
+
let stderr = '';
|
|
41
|
+
child.stderr.on('data', (d) => (stderr += d));
|
|
42
|
+
child.on('error', reject);
|
|
43
|
+
child.on('exit', (code) => {
|
|
44
|
+
if (code === 0)
|
|
45
|
+
resolve();
|
|
46
|
+
else
|
|
47
|
+
reject(new Error(`ffmpeg sheet composition failed (exit ${code}): ${stderr.trim().slice(-2000) || 'no error output'}`));
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
try {
|
|
51
|
+
return await readFile(outPath);
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
await unlink(outPath).catch(() => { });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=poster-sheet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poster-sheet.js","sourceRoot":"","sources":["../../src/lib/poster-sheet.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp/index.d.ts
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* An image content block (base64-encoded bytes + MIME type) plus an optional text block of
|
|
4
|
+
* metadata — the shape a client needs to actually render the image rather than just read about it.
|
|
5
|
+
* `preview_creative` is the first tool to need this; every other tool emits text only.
|
|
6
|
+
*/
|
|
7
|
+
export declare function imageResponse(image: {
|
|
8
|
+
data: Buffer;
|
|
9
|
+
mimeType: string;
|
|
10
|
+
}, meta?: unknown): {
|
|
11
|
+
content: ({
|
|
12
|
+
type: "image";
|
|
13
|
+
data: string;
|
|
14
|
+
mimeType: string;
|
|
15
|
+
} | {
|
|
16
|
+
type: "text";
|
|
17
|
+
text: string;
|
|
18
|
+
})[];
|
|
19
|
+
};
|
|
2
20
|
export declare function runMcpServer(): Promise<void>;
|
|
3
21
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/mcp/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";AAqyCA;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAAE,IAAI,CAAC,EAAE,OAAO;;cACxD,OAAO;cAAQ,MAAM;kBAAY,MAAM;;cAAa,MAAM;cAAQ,MAAM;;EAOtG;AAoBD,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CA8zBlD"}
|