@outerlayer/cli 0.33.0-next.20261021194303 → 0.33.0-next.20261021234516
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 +1 -1
- package/dist/{agent-setup-P5PWEGZM.js → agent-setup-UY5NBT7C.js} +10 -10
- package/dist/{artifact-link-cmd-IGVFGHC2.js → artifact-link-cmd-2MBHG3PO.js} +2 -2
- package/dist/build-info.json +1 -1
- package/dist/{builder-X4PIGYS6.js → builder-K2XIVSSZ.js} +3 -3
- package/dist/{check-DPVNDAQQ.js → check-AXDPZJU4.js} +24 -24
- package/dist/{chunk-ZAJKEHIE.js → chunk-4IG6EZHU.js} +2 -2
- package/dist/{chunk-VFW6I4GG.js → chunk-5RUH3PCG.js} +4 -4
- package/dist/{chunk-ZPW6PUNK.js → chunk-6TFTRVOA.js} +2 -2
- package/dist/{chunk-TGXYNEI7.js → chunk-72YES3RL.js} +11 -11
- package/dist/{chunk-3WW2TL7V.js → chunk-AKHGQKNR.js} +64 -4
- package/dist/{chunk-XT5SOJRC.js → chunk-AKR32WHV.js} +1 -1
- package/dist/{chunk-KUOY5UIR.js → chunk-CHXYLCMA.js} +1 -1
- package/dist/{chunk-XQTT3222.js → chunk-CMU6QJHE.js} +1 -1
- package/dist/{chunk-ENV3AIWQ.js → chunk-CV2S5L4J.js} +8 -8
- package/dist/{chunk-543ZX5TM.js → chunk-DE7YSNDX.js} +2 -2
- package/dist/{chunk-3H7NZPYG.js → chunk-DM4EGXDO.js} +1 -1
- package/dist/{chunk-YZIC5JXC.js → chunk-EG7KPSSP.js} +6 -6
- package/dist/{chunk-OXSFOLWE.js → chunk-HT7LVCTX.js} +2 -2
- package/dist/{chunk-LTZVMKQS.js → chunk-KDM77TVX.js} +2 -2
- package/dist/{chunk-YW4PEGLN.js → chunk-KUTZ6XLS.js} +1 -1
- package/dist/{chunk-OVSUSOHL.js → chunk-LBPK2TPR.js} +4 -4
- package/dist/{chunk-WXEPMPHK.js → chunk-LN4WDONI.js} +1 -1
- package/dist/{chunk-FE3RTGJN.js → chunk-M5OBRUQE.js} +2 -2
- package/dist/{chunk-5ZUT3IK3.js → chunk-N3KK5BKF.js} +7 -7
- package/dist/{chunk-C4N5QRQJ.js → chunk-NOXK3QW6.js} +3 -3
- package/dist/{chunk-CG5TWQ23.js → chunk-O6NG67Y7.js} +2 -2
- package/dist/{chunk-JUXATN46.js → chunk-OGCG5LOX.js} +1 -1
- package/dist/{chunk-XRQ7DABQ.js → chunk-Q2D4M3GC.js} +1 -1
- package/dist/{chunk-UVZ43SW3.js → chunk-Q4YVHYHT.js} +3 -3
- package/dist/{chunk-4OKOLFPH.js → chunk-RQKQTRXM.js} +4 -4
- package/dist/{chunk-WRX2MCDV.js → chunk-RVWADVBN.js} +3 -3
- package/dist/{chunk-6C3ZVE62.js → chunk-RW52CUFX.js} +1 -1
- package/dist/{chunk-NNNUME4Y.js → chunk-SFJD4MRY.js} +5 -5
- package/dist/{chunk-GI6LOIHS.js → chunk-TNJXCNQ6.js} +4 -4
- package/dist/{chunk-XRZGT54P.js → chunk-UIBTEDLA.js} +5 -5
- package/dist/{chunk-QCDVKCHA.js → chunk-UWEJ6HGB.js} +2 -2
- package/dist/{chunk-BBK62GXM.js → chunk-X5LFLYSD.js} +6 -6
- package/dist/{chunk-KURUM765.js → chunk-Y3LIJIRH.js} +4 -4
- package/dist/{chunk-TAZJDD2U.js → chunk-YH2632RT.js} +5 -5
- package/dist/{chunk-LPIUIRHA.js → chunk-ZRTMS4C7.js} +9 -9
- package/dist/{claude-launcher-Q7YAPLY2.js → claude-launcher-STSKZNW3.js} +5 -5
- package/dist/{cli-OGX6JNIN.js → cli-WAXIJAES.js} +94 -89
- package/dist/{command-UIAQUHHU.js → command-2JEY6X6U.js} +16 -16
- package/dist/{connect-cmd-OLESV5O3.js → connect-cmd-WFHAPGCZ.js} +8 -8
- package/dist/{container-start-MGY5VFHW.js → container-start-TTEHOSJK.js} +17 -17
- package/dist/{context-cleanup-VSDT77BZ.js → context-cleanup-UBOJHGQM.js} +3 -3
- package/dist/{control-plane-launch-7PXJSMVM.js → control-plane-launch-R4X63DYW.js} +6 -6
- package/dist/{dist-DY2ZNEBS.js → dist-ADFLG3AY.js} +2 -2
- package/dist/{docker-XRUAUQIG.js → docker-UYS5N5B7.js} +3 -3
- package/dist/{doctor-ZMKO3F7I.js → doctor-3XPT5F2Y.js} +18 -18
- package/dist/{emit-artifact-cmd-RI7HA37S.js → emit-artifact-cmd-FR5PUUJR.js} +13 -13
- package/dist/{emit-cmd-T7NIBSF3.js → emit-cmd-JOO6ZTEI.js} +6 -6
- package/dist/{emit-criteria-cmd-T5HZZ7XJ.js → emit-criteria-cmd-DKPZJTIN.js} +9 -9
- package/dist/emit-demo-cmd-Q3BCRULM.js +3022 -0
- package/dist/{emit-finding-cmd-RN7LSGJ7.js → emit-finding-cmd-VM4KIVEM.js} +10 -10
- package/dist/{emit-result-cmd-72STGWMP.js → emit-result-cmd-SEKL6HUU.js} +12 -12
- package/dist/{emit-step-cmd-POFAEM7G.js → emit-step-cmd-4YAWK4VE.js} +10 -10
- package/dist/{findings-cmd-TMURLT3A.js → findings-cmd-RXYIWXY4.js} +10 -10
- package/dist/{fleet-readiness-N6Y3F7R6.js → fleet-readiness-VIX76RNL.js} +8 -8
- package/dist/{governing-source-2R7IS7LZ.js → governing-source-APGCKDTS.js} +13 -13
- package/dist/{hook-fast-GO7UTNN5.js → hook-fast-NVBMSE3N.js} +1 -1
- package/dist/index.js +4 -4
- package/dist/{init-YMU4QFRV.js → init-BR4Z4T2Q.js} +17 -17
- package/dist/{init-E3VWTJFQ.js → init-XWQJEW6H.js} +17 -17
- package/dist/{init-cmd-WBPE3X7T.js → init-cmd-IEMYX6KA.js} +18 -18
- package/dist/{init-update-MONZSG7L.js → init-update-HOTJISWA.js} +17 -17
- package/dist/{layout-OQ7EJUMH.js → layout-ZI4RJ6MT.js} +4 -4
- package/dist/{login-browser-KP7YVYTN.js → login-browser-WUSIVD5E.js} +2 -2
- package/dist/{loop-N3IUN4F3.js → loop-3NRAMJA4.js} +24 -24
- package/dist/{machine-J7QFINBX.js → machine-LUXL7SFT.js} +16 -16
- package/dist/{machine-skills-XWWYFNOZ.js → machine-skills-REHDWINX.js} +4 -4
- package/dist/{runner-identity-MSMI2PD4.js → runner-identity-DR5I3NBT.js} +3 -3
- package/dist/{runner-key-QTNYQ2I7.js → runner-key-OLMCAOTD.js} +17 -17
- package/dist/{session-sender-USH6NOZL.js → session-sender-PLQ5NOEW.js} +13 -13
- package/dist/{spec-cmd-ABGR2ZDH.js → spec-cmd-NIAQWMLE.js} +10 -10
- package/dist/{starter-pack-6O7F6P7C.js → starter-pack-OESK7UX2.js} +5 -5
- package/dist/{status-FHEVPRWN.js → status-4HNX45UZ.js} +20 -20
- package/dist/{sync-cmd-GC2TBSUO.js → sync-cmd-UTESQMDU.js} +12 -12
- package/dist/{watch-F3LCBTNO.js → watch-4UC4FGI5.js} +12 -12
- package/dist/{work-claim-cmd-X7KNYGU7.js → work-claim-cmd-ODYRE44S.js} +12 -12
- package/dist/{work-cmd-AI3ZPCP7.js → work-cmd-QQDUI3YX.js} +10 -10
- package/dist/{work-comment-cmd-6UXS342S.js → work-comment-cmd-FIOMDPIZ.js} +10 -10
- package/dist/{work-logs-cmd-TVKWMDOW.js → work-logs-cmd-DA6MPQ6O.js} +10 -10
- package/dist/{work-open-pr-cmd-WZ7W2HZ7.js → work-open-pr-cmd-QG2FT7OW.js} +11 -11
- package/dist/{work-pr-cmd-KTNNR46X.js → work-pr-cmd-XNXWWLS5.js} +10 -10
- package/dist/{work-stop-cmd-2BUDSXUU.js → work-stop-cmd-2KLZ2UH2.js} +10 -10
- package/package.json +3 -1
- package/dist/emit-demo-cmd-MWRFZHSX.js +0 -808
|
@@ -0,0 +1,3022 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { runEmitArtifact } from './chunk-YH2632RT.js';
|
|
4
|
+
import './chunk-SFJD4MRY.js';
|
|
5
|
+
import './chunk-RQKQTRXM.js';
|
|
6
|
+
import './chunk-ESVW4MJY.js';
|
|
7
|
+
import './chunk-TBT347UY.js';
|
|
8
|
+
import { VoiceDownloadError, downloadPinned, sha256OfFile } from './chunk-2PGI6RDE.js';
|
|
9
|
+
import { VOICE_PACK_FILES, VOICE_PACK_VERSION } from './chunk-LHYO4RDP.js';
|
|
10
|
+
import { detectActiveSession, autoConnect } from './chunk-6TFTRVOA.js';
|
|
11
|
+
import './chunk-5RUH3PCG.js';
|
|
12
|
+
import './chunk-DM4EGXDO.js';
|
|
13
|
+
import { resolveRepoIdentity } from './chunk-AKR32WHV.js';
|
|
14
|
+
import './chunk-4IG6EZHU.js';
|
|
15
|
+
import './chunk-RW52CUFX.js';
|
|
16
|
+
import './chunk-G2JWZDFA.js';
|
|
17
|
+
import { C } from './chunk-ANPNT6PM.js';
|
|
18
|
+
import { defaultFetch, resolveGatewayCredentials } from './chunk-VQ2JGZSC.js';
|
|
19
|
+
import './chunk-PJDQPF5N.js';
|
|
20
|
+
import './chunk-JT3YO5MH.js';
|
|
21
|
+
import './chunk-ESP5VZBD.js';
|
|
22
|
+
import './chunk-HDSDUTX4.js';
|
|
23
|
+
import './chunk-HT7LVCTX.js';
|
|
24
|
+
import './chunk-ZJSNLVCE.js';
|
|
25
|
+
import './chunk-2O5KA67H.js';
|
|
26
|
+
import './chunk-BKO6JEZI.js';
|
|
27
|
+
import './chunk-HCVLP7XA.js';
|
|
28
|
+
import './chunk-BJ3KTMDH.js';
|
|
29
|
+
import './chunk-KUTZ6XLS.js';
|
|
30
|
+
import './chunk-5BHIBJDA.js';
|
|
31
|
+
import './chunk-CDDED64E.js';
|
|
32
|
+
import './chunk-RA3O54FC.js';
|
|
33
|
+
import { launchedItemForSession } from './chunk-T2Q7ISPE.js';
|
|
34
|
+
import './chunk-WZBHBH3J.js';
|
|
35
|
+
import './chunk-CRB3POP4.js';
|
|
36
|
+
import './chunk-URIZRCRY.js';
|
|
37
|
+
import './chunk-TUSYOL2O.js';
|
|
38
|
+
import './chunk-OGCG5LOX.js';
|
|
39
|
+
import { ARTIFACT_MAX_BYTES, ARTIFACT_MAX_CAPTION_LENGTH, DEMO_RECORD_MEDIA_TYPE, DemoManifestSchema } from './chunk-AKHGQKNR.js';
|
|
40
|
+
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
41
|
+
import './chunk-XD4ZCMA4.js';
|
|
42
|
+
import './chunk-MESOOLF4.js';
|
|
43
|
+
import { __commonJS, __require, __toESM } from './chunk-Z5GEFMRW.js';
|
|
44
|
+
import { createHash } from 'crypto';
|
|
45
|
+
import { mkdtempSync, statSync, readFileSync, writeFileSync, rmSync, mkdirSync, accessSync, constants, realpathSync, existsSync, createReadStream, createWriteStream } from 'fs';
|
|
46
|
+
import { tmpdir } from 'os';
|
|
47
|
+
import { resolve, dirname, join, delimiter, normalize, sep } from 'path';
|
|
48
|
+
import { execFileSync } from 'child_process';
|
|
49
|
+
import { createRequire as createRequire$1 } from 'module';
|
|
50
|
+
import { createGunzip } from 'zlib';
|
|
51
|
+
|
|
52
|
+
createRequire(import.meta.url);
|
|
53
|
+
|
|
54
|
+
// ../../node_modules/pngjs/lib/chunkstream.js
|
|
55
|
+
var require_chunkstream = __commonJS({
|
|
56
|
+
"../../node_modules/pngjs/lib/chunkstream.js"(exports$1, module) {
|
|
57
|
+
var util = __require("util");
|
|
58
|
+
var Stream = __require("stream");
|
|
59
|
+
var ChunkStream = module.exports = function() {
|
|
60
|
+
Stream.call(this);
|
|
61
|
+
this._buffers = [];
|
|
62
|
+
this._buffered = 0;
|
|
63
|
+
this._reads = [];
|
|
64
|
+
this._paused = false;
|
|
65
|
+
this._encoding = "utf8";
|
|
66
|
+
this.writable = true;
|
|
67
|
+
};
|
|
68
|
+
util.inherits(ChunkStream, Stream);
|
|
69
|
+
ChunkStream.prototype.read = function(length, callback) {
|
|
70
|
+
this._reads.push({
|
|
71
|
+
length: Math.abs(length),
|
|
72
|
+
// if length < 0 then at most this length
|
|
73
|
+
allowLess: length < 0,
|
|
74
|
+
func: callback
|
|
75
|
+
});
|
|
76
|
+
process.nextTick(
|
|
77
|
+
function() {
|
|
78
|
+
this._process();
|
|
79
|
+
if (this._paused && this._reads && this._reads.length > 0) {
|
|
80
|
+
this._paused = false;
|
|
81
|
+
this.emit("drain");
|
|
82
|
+
}
|
|
83
|
+
}.bind(this)
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
ChunkStream.prototype.write = function(data, encoding) {
|
|
87
|
+
if (!this.writable) {
|
|
88
|
+
this.emit("error", new Error("Stream not writable"));
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
let dataBuffer;
|
|
92
|
+
if (Buffer.isBuffer(data)) {
|
|
93
|
+
dataBuffer = data;
|
|
94
|
+
} else {
|
|
95
|
+
dataBuffer = Buffer.from(data, encoding || this._encoding);
|
|
96
|
+
}
|
|
97
|
+
this._buffers.push(dataBuffer);
|
|
98
|
+
this._buffered += dataBuffer.length;
|
|
99
|
+
this._process();
|
|
100
|
+
if (this._reads && this._reads.length === 0) {
|
|
101
|
+
this._paused = true;
|
|
102
|
+
}
|
|
103
|
+
return this.writable && !this._paused;
|
|
104
|
+
};
|
|
105
|
+
ChunkStream.prototype.end = function(data, encoding) {
|
|
106
|
+
if (data) {
|
|
107
|
+
this.write(data, encoding);
|
|
108
|
+
}
|
|
109
|
+
this.writable = false;
|
|
110
|
+
if (!this._buffers) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (this._buffers.length === 0) {
|
|
114
|
+
this._end();
|
|
115
|
+
} else {
|
|
116
|
+
this._buffers.push(null);
|
|
117
|
+
this._process();
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
ChunkStream.prototype.destroySoon = ChunkStream.prototype.end;
|
|
121
|
+
ChunkStream.prototype._end = function() {
|
|
122
|
+
if (this._reads.length > 0) {
|
|
123
|
+
this.emit("error", new Error("Unexpected end of input"));
|
|
124
|
+
}
|
|
125
|
+
this.destroy();
|
|
126
|
+
};
|
|
127
|
+
ChunkStream.prototype.destroy = function() {
|
|
128
|
+
if (!this._buffers) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
this.writable = false;
|
|
132
|
+
this._reads = null;
|
|
133
|
+
this._buffers = null;
|
|
134
|
+
this.emit("close");
|
|
135
|
+
};
|
|
136
|
+
ChunkStream.prototype._processReadAllowingLess = function(read) {
|
|
137
|
+
this._reads.shift();
|
|
138
|
+
let smallerBuf = this._buffers[0];
|
|
139
|
+
if (smallerBuf.length > read.length) {
|
|
140
|
+
this._buffered -= read.length;
|
|
141
|
+
this._buffers[0] = smallerBuf.slice(read.length);
|
|
142
|
+
read.func.call(this, smallerBuf.slice(0, read.length));
|
|
143
|
+
} else {
|
|
144
|
+
this._buffered -= smallerBuf.length;
|
|
145
|
+
this._buffers.shift();
|
|
146
|
+
read.func.call(this, smallerBuf);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
ChunkStream.prototype._processRead = function(read) {
|
|
150
|
+
this._reads.shift();
|
|
151
|
+
let pos = 0;
|
|
152
|
+
let count = 0;
|
|
153
|
+
let data = Buffer.alloc(read.length);
|
|
154
|
+
while (pos < read.length) {
|
|
155
|
+
let buf = this._buffers[count++];
|
|
156
|
+
let len = Math.min(buf.length, read.length - pos);
|
|
157
|
+
buf.copy(data, pos, 0, len);
|
|
158
|
+
pos += len;
|
|
159
|
+
if (len !== buf.length) {
|
|
160
|
+
this._buffers[--count] = buf.slice(len);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (count > 0) {
|
|
164
|
+
this._buffers.splice(0, count);
|
|
165
|
+
}
|
|
166
|
+
this._buffered -= read.length;
|
|
167
|
+
read.func.call(this, data);
|
|
168
|
+
};
|
|
169
|
+
ChunkStream.prototype._process = function() {
|
|
170
|
+
try {
|
|
171
|
+
while (this._buffered > 0 && this._reads && this._reads.length > 0) {
|
|
172
|
+
let read = this._reads[0];
|
|
173
|
+
if (read.allowLess) {
|
|
174
|
+
this._processReadAllowingLess(read);
|
|
175
|
+
} else if (this._buffered >= read.length) {
|
|
176
|
+
this._processRead(read);
|
|
177
|
+
} else {
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (this._buffers && !this.writable) {
|
|
182
|
+
this._end();
|
|
183
|
+
}
|
|
184
|
+
} catch (ex) {
|
|
185
|
+
this.emit("error", ex);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// ../../node_modules/pngjs/lib/interlace.js
|
|
192
|
+
var require_interlace = __commonJS({
|
|
193
|
+
"../../node_modules/pngjs/lib/interlace.js"(exports$1) {
|
|
194
|
+
var imagePasses = [
|
|
195
|
+
{
|
|
196
|
+
// pass 1 - 1px
|
|
197
|
+
x: [0],
|
|
198
|
+
y: [0]
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
// pass 2 - 1px
|
|
202
|
+
x: [4],
|
|
203
|
+
y: [0]
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
// pass 3 - 2px
|
|
207
|
+
x: [0, 4],
|
|
208
|
+
y: [4]
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
// pass 4 - 4px
|
|
212
|
+
x: [2, 6],
|
|
213
|
+
y: [0, 4]
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
// pass 5 - 8px
|
|
217
|
+
x: [0, 2, 4, 6],
|
|
218
|
+
y: [2, 6]
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
// pass 6 - 16px
|
|
222
|
+
x: [1, 3, 5, 7],
|
|
223
|
+
y: [0, 2, 4, 6]
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
// pass 7 - 32px
|
|
227
|
+
x: [0, 1, 2, 3, 4, 5, 6, 7],
|
|
228
|
+
y: [1, 3, 5, 7]
|
|
229
|
+
}
|
|
230
|
+
];
|
|
231
|
+
exports$1.getImagePasses = function(width, height) {
|
|
232
|
+
let images = [];
|
|
233
|
+
let xLeftOver = width % 8;
|
|
234
|
+
let yLeftOver = height % 8;
|
|
235
|
+
let xRepeats = (width - xLeftOver) / 8;
|
|
236
|
+
let yRepeats = (height - yLeftOver) / 8;
|
|
237
|
+
for (let i = 0; i < imagePasses.length; i++) {
|
|
238
|
+
let pass = imagePasses[i];
|
|
239
|
+
let passWidth = xRepeats * pass.x.length;
|
|
240
|
+
let passHeight = yRepeats * pass.y.length;
|
|
241
|
+
for (let j = 0; j < pass.x.length; j++) {
|
|
242
|
+
if (pass.x[j] < xLeftOver) {
|
|
243
|
+
passWidth++;
|
|
244
|
+
} else {
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
for (let j = 0; j < pass.y.length; j++) {
|
|
249
|
+
if (pass.y[j] < yLeftOver) {
|
|
250
|
+
passHeight++;
|
|
251
|
+
} else {
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (passWidth > 0 && passHeight > 0) {
|
|
256
|
+
images.push({ width: passWidth, height: passHeight, index: i });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return images;
|
|
260
|
+
};
|
|
261
|
+
exports$1.getInterlaceIterator = function(width) {
|
|
262
|
+
return function(x, y, pass) {
|
|
263
|
+
let outerXLeftOver = x % imagePasses[pass].x.length;
|
|
264
|
+
let outerX = (x - outerXLeftOver) / imagePasses[pass].x.length * 8 + imagePasses[pass].x[outerXLeftOver];
|
|
265
|
+
let outerYLeftOver = y % imagePasses[pass].y.length;
|
|
266
|
+
let outerY = (y - outerYLeftOver) / imagePasses[pass].y.length * 8 + imagePasses[pass].y[outerYLeftOver];
|
|
267
|
+
return outerX * 4 + outerY * width * 4;
|
|
268
|
+
};
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// ../../node_modules/pngjs/lib/paeth-predictor.js
|
|
274
|
+
var require_paeth_predictor = __commonJS({
|
|
275
|
+
"../../node_modules/pngjs/lib/paeth-predictor.js"(exports$1, module) {
|
|
276
|
+
module.exports = function paethPredictor(left, above, upLeft) {
|
|
277
|
+
let paeth = left + above - upLeft;
|
|
278
|
+
let pLeft = Math.abs(paeth - left);
|
|
279
|
+
let pAbove = Math.abs(paeth - above);
|
|
280
|
+
let pUpLeft = Math.abs(paeth - upLeft);
|
|
281
|
+
if (pLeft <= pAbove && pLeft <= pUpLeft) {
|
|
282
|
+
return left;
|
|
283
|
+
}
|
|
284
|
+
if (pAbove <= pUpLeft) {
|
|
285
|
+
return above;
|
|
286
|
+
}
|
|
287
|
+
return upLeft;
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// ../../node_modules/pngjs/lib/filter-parse.js
|
|
293
|
+
var require_filter_parse = __commonJS({
|
|
294
|
+
"../../node_modules/pngjs/lib/filter-parse.js"(exports$1, module) {
|
|
295
|
+
var interlaceUtils = require_interlace();
|
|
296
|
+
var paethPredictor = require_paeth_predictor();
|
|
297
|
+
function getByteWidth(width, bpp, depth) {
|
|
298
|
+
let byteWidth = width * bpp;
|
|
299
|
+
if (depth !== 8) {
|
|
300
|
+
byteWidth = Math.ceil(byteWidth / (8 / depth));
|
|
301
|
+
}
|
|
302
|
+
return byteWidth;
|
|
303
|
+
}
|
|
304
|
+
var Filter = module.exports = function(bitmapInfo, dependencies) {
|
|
305
|
+
let width = bitmapInfo.width;
|
|
306
|
+
let height = bitmapInfo.height;
|
|
307
|
+
let interlace = bitmapInfo.interlace;
|
|
308
|
+
let bpp = bitmapInfo.bpp;
|
|
309
|
+
let depth = bitmapInfo.depth;
|
|
310
|
+
this.read = dependencies.read;
|
|
311
|
+
this.write = dependencies.write;
|
|
312
|
+
this.complete = dependencies.complete;
|
|
313
|
+
this._imageIndex = 0;
|
|
314
|
+
this._images = [];
|
|
315
|
+
if (interlace) {
|
|
316
|
+
let passes = interlaceUtils.getImagePasses(width, height);
|
|
317
|
+
for (let i = 0; i < passes.length; i++) {
|
|
318
|
+
this._images.push({
|
|
319
|
+
byteWidth: getByteWidth(passes[i].width, bpp, depth),
|
|
320
|
+
height: passes[i].height,
|
|
321
|
+
lineIndex: 0
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
} else {
|
|
325
|
+
this._images.push({
|
|
326
|
+
byteWidth: getByteWidth(width, bpp, depth),
|
|
327
|
+
height,
|
|
328
|
+
lineIndex: 0
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
if (depth === 8) {
|
|
332
|
+
this._xComparison = bpp;
|
|
333
|
+
} else if (depth === 16) {
|
|
334
|
+
this._xComparison = bpp * 2;
|
|
335
|
+
} else {
|
|
336
|
+
this._xComparison = 1;
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
Filter.prototype.start = function() {
|
|
340
|
+
this.read(
|
|
341
|
+
this._images[this._imageIndex].byteWidth + 1,
|
|
342
|
+
this._reverseFilterLine.bind(this)
|
|
343
|
+
);
|
|
344
|
+
};
|
|
345
|
+
Filter.prototype._unFilterType1 = function(rawData, unfilteredLine, byteWidth) {
|
|
346
|
+
let xComparison = this._xComparison;
|
|
347
|
+
let xBiggerThan = xComparison - 1;
|
|
348
|
+
for (let x = 0; x < byteWidth; x++) {
|
|
349
|
+
let rawByte = rawData[1 + x];
|
|
350
|
+
let f1Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
|
|
351
|
+
unfilteredLine[x] = rawByte + f1Left;
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
Filter.prototype._unFilterType2 = function(rawData, unfilteredLine, byteWidth) {
|
|
355
|
+
let lastLine = this._lastLine;
|
|
356
|
+
for (let x = 0; x < byteWidth; x++) {
|
|
357
|
+
let rawByte = rawData[1 + x];
|
|
358
|
+
let f2Up = lastLine ? lastLine[x] : 0;
|
|
359
|
+
unfilteredLine[x] = rawByte + f2Up;
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
Filter.prototype._unFilterType3 = function(rawData, unfilteredLine, byteWidth) {
|
|
363
|
+
let xComparison = this._xComparison;
|
|
364
|
+
let xBiggerThan = xComparison - 1;
|
|
365
|
+
let lastLine = this._lastLine;
|
|
366
|
+
for (let x = 0; x < byteWidth; x++) {
|
|
367
|
+
let rawByte = rawData[1 + x];
|
|
368
|
+
let f3Up = lastLine ? lastLine[x] : 0;
|
|
369
|
+
let f3Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
|
|
370
|
+
let f3Add = Math.floor((f3Left + f3Up) / 2);
|
|
371
|
+
unfilteredLine[x] = rawByte + f3Add;
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
Filter.prototype._unFilterType4 = function(rawData, unfilteredLine, byteWidth) {
|
|
375
|
+
let xComparison = this._xComparison;
|
|
376
|
+
let xBiggerThan = xComparison - 1;
|
|
377
|
+
let lastLine = this._lastLine;
|
|
378
|
+
for (let x = 0; x < byteWidth; x++) {
|
|
379
|
+
let rawByte = rawData[1 + x];
|
|
380
|
+
let f4Up = lastLine ? lastLine[x] : 0;
|
|
381
|
+
let f4Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
|
|
382
|
+
let f4UpLeft = x > xBiggerThan && lastLine ? lastLine[x - xComparison] : 0;
|
|
383
|
+
let f4Add = paethPredictor(f4Left, f4Up, f4UpLeft);
|
|
384
|
+
unfilteredLine[x] = rawByte + f4Add;
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
Filter.prototype._reverseFilterLine = function(rawData) {
|
|
388
|
+
let filter = rawData[0];
|
|
389
|
+
let unfilteredLine;
|
|
390
|
+
let currentImage = this._images[this._imageIndex];
|
|
391
|
+
let byteWidth = currentImage.byteWidth;
|
|
392
|
+
if (filter === 0) {
|
|
393
|
+
unfilteredLine = rawData.slice(1, byteWidth + 1);
|
|
394
|
+
} else {
|
|
395
|
+
unfilteredLine = Buffer.alloc(byteWidth);
|
|
396
|
+
switch (filter) {
|
|
397
|
+
case 1:
|
|
398
|
+
this._unFilterType1(rawData, unfilteredLine, byteWidth);
|
|
399
|
+
break;
|
|
400
|
+
case 2:
|
|
401
|
+
this._unFilterType2(rawData, unfilteredLine, byteWidth);
|
|
402
|
+
break;
|
|
403
|
+
case 3:
|
|
404
|
+
this._unFilterType3(rawData, unfilteredLine, byteWidth);
|
|
405
|
+
break;
|
|
406
|
+
case 4:
|
|
407
|
+
this._unFilterType4(rawData, unfilteredLine, byteWidth);
|
|
408
|
+
break;
|
|
409
|
+
default:
|
|
410
|
+
throw new Error("Unrecognised filter type - " + filter);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
this.write(unfilteredLine);
|
|
414
|
+
currentImage.lineIndex++;
|
|
415
|
+
if (currentImage.lineIndex >= currentImage.height) {
|
|
416
|
+
this._lastLine = null;
|
|
417
|
+
this._imageIndex++;
|
|
418
|
+
currentImage = this._images[this._imageIndex];
|
|
419
|
+
} else {
|
|
420
|
+
this._lastLine = unfilteredLine;
|
|
421
|
+
}
|
|
422
|
+
if (currentImage) {
|
|
423
|
+
this.read(currentImage.byteWidth + 1, this._reverseFilterLine.bind(this));
|
|
424
|
+
} else {
|
|
425
|
+
this._lastLine = null;
|
|
426
|
+
this.complete();
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// ../../node_modules/pngjs/lib/filter-parse-async.js
|
|
433
|
+
var require_filter_parse_async = __commonJS({
|
|
434
|
+
"../../node_modules/pngjs/lib/filter-parse-async.js"(exports$1, module) {
|
|
435
|
+
var util = __require("util");
|
|
436
|
+
var ChunkStream = require_chunkstream();
|
|
437
|
+
var Filter = require_filter_parse();
|
|
438
|
+
var FilterAsync = module.exports = function(bitmapInfo) {
|
|
439
|
+
ChunkStream.call(this);
|
|
440
|
+
let buffers = [];
|
|
441
|
+
let that = this;
|
|
442
|
+
this._filter = new Filter(bitmapInfo, {
|
|
443
|
+
read: this.read.bind(this),
|
|
444
|
+
write: function(buffer) {
|
|
445
|
+
buffers.push(buffer);
|
|
446
|
+
},
|
|
447
|
+
complete: function() {
|
|
448
|
+
that.emit("complete", Buffer.concat(buffers));
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
this._filter.start();
|
|
452
|
+
};
|
|
453
|
+
util.inherits(FilterAsync, ChunkStream);
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
// ../../node_modules/pngjs/lib/constants.js
|
|
458
|
+
var require_constants = __commonJS({
|
|
459
|
+
"../../node_modules/pngjs/lib/constants.js"(exports$1, module) {
|
|
460
|
+
module.exports = {
|
|
461
|
+
PNG_SIGNATURE: [137, 80, 78, 71, 13, 10, 26, 10],
|
|
462
|
+
TYPE_IHDR: 1229472850,
|
|
463
|
+
TYPE_IEND: 1229278788,
|
|
464
|
+
TYPE_IDAT: 1229209940,
|
|
465
|
+
TYPE_PLTE: 1347179589,
|
|
466
|
+
TYPE_tRNS: 1951551059,
|
|
467
|
+
// eslint-disable-line camelcase
|
|
468
|
+
TYPE_gAMA: 1732332865,
|
|
469
|
+
// eslint-disable-line camelcase
|
|
470
|
+
// color-type bits
|
|
471
|
+
COLORTYPE_GRAYSCALE: 0,
|
|
472
|
+
COLORTYPE_PALETTE: 1,
|
|
473
|
+
COLORTYPE_COLOR: 2,
|
|
474
|
+
COLORTYPE_ALPHA: 4,
|
|
475
|
+
// e.g. grayscale and alpha
|
|
476
|
+
// color-type combinations
|
|
477
|
+
COLORTYPE_PALETTE_COLOR: 3,
|
|
478
|
+
COLORTYPE_COLOR_ALPHA: 6,
|
|
479
|
+
COLORTYPE_TO_BPP_MAP: {
|
|
480
|
+
0: 1,
|
|
481
|
+
2: 3,
|
|
482
|
+
3: 1,
|
|
483
|
+
4: 2,
|
|
484
|
+
6: 4
|
|
485
|
+
},
|
|
486
|
+
GAMMA_DIVISION: 1e5
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
// ../../node_modules/pngjs/lib/crc.js
|
|
492
|
+
var require_crc = __commonJS({
|
|
493
|
+
"../../node_modules/pngjs/lib/crc.js"(exports$1, module) {
|
|
494
|
+
var crcTable = [];
|
|
495
|
+
(function() {
|
|
496
|
+
for (let i = 0; i < 256; i++) {
|
|
497
|
+
let currentCrc = i;
|
|
498
|
+
for (let j = 0; j < 8; j++) {
|
|
499
|
+
if (currentCrc & 1) {
|
|
500
|
+
currentCrc = 3988292384 ^ currentCrc >>> 1;
|
|
501
|
+
} else {
|
|
502
|
+
currentCrc = currentCrc >>> 1;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
crcTable[i] = currentCrc;
|
|
506
|
+
}
|
|
507
|
+
})();
|
|
508
|
+
var CrcCalculator = module.exports = function() {
|
|
509
|
+
this._crc = -1;
|
|
510
|
+
};
|
|
511
|
+
CrcCalculator.prototype.write = function(data) {
|
|
512
|
+
for (let i = 0; i < data.length; i++) {
|
|
513
|
+
this._crc = crcTable[(this._crc ^ data[i]) & 255] ^ this._crc >>> 8;
|
|
514
|
+
}
|
|
515
|
+
return true;
|
|
516
|
+
};
|
|
517
|
+
CrcCalculator.prototype.crc32 = function() {
|
|
518
|
+
return this._crc ^ -1;
|
|
519
|
+
};
|
|
520
|
+
CrcCalculator.crc32 = function(buf) {
|
|
521
|
+
let crc = -1;
|
|
522
|
+
for (let i = 0; i < buf.length; i++) {
|
|
523
|
+
crc = crcTable[(crc ^ buf[i]) & 255] ^ crc >>> 8;
|
|
524
|
+
}
|
|
525
|
+
return crc ^ -1;
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
// ../../node_modules/pngjs/lib/parser.js
|
|
531
|
+
var require_parser = __commonJS({
|
|
532
|
+
"../../node_modules/pngjs/lib/parser.js"(exports$1, module) {
|
|
533
|
+
var constants2 = require_constants();
|
|
534
|
+
var CrcCalculator = require_crc();
|
|
535
|
+
var Parser = module.exports = function(options, dependencies) {
|
|
536
|
+
this._options = options;
|
|
537
|
+
options.checkCRC = options.checkCRC !== false;
|
|
538
|
+
this._hasIHDR = false;
|
|
539
|
+
this._hasIEND = false;
|
|
540
|
+
this._emittedHeadersFinished = false;
|
|
541
|
+
this._palette = [];
|
|
542
|
+
this._colorType = 0;
|
|
543
|
+
this._chunks = {};
|
|
544
|
+
this._chunks[constants2.TYPE_IHDR] = this._handleIHDR.bind(this);
|
|
545
|
+
this._chunks[constants2.TYPE_IEND] = this._handleIEND.bind(this);
|
|
546
|
+
this._chunks[constants2.TYPE_IDAT] = this._handleIDAT.bind(this);
|
|
547
|
+
this._chunks[constants2.TYPE_PLTE] = this._handlePLTE.bind(this);
|
|
548
|
+
this._chunks[constants2.TYPE_tRNS] = this._handleTRNS.bind(this);
|
|
549
|
+
this._chunks[constants2.TYPE_gAMA] = this._handleGAMA.bind(this);
|
|
550
|
+
this.read = dependencies.read;
|
|
551
|
+
this.error = dependencies.error;
|
|
552
|
+
this.metadata = dependencies.metadata;
|
|
553
|
+
this.gamma = dependencies.gamma;
|
|
554
|
+
this.transColor = dependencies.transColor;
|
|
555
|
+
this.palette = dependencies.palette;
|
|
556
|
+
this.parsed = dependencies.parsed;
|
|
557
|
+
this.inflateData = dependencies.inflateData;
|
|
558
|
+
this.finished = dependencies.finished;
|
|
559
|
+
this.simpleTransparency = dependencies.simpleTransparency;
|
|
560
|
+
this.headersFinished = dependencies.headersFinished || function() {
|
|
561
|
+
};
|
|
562
|
+
};
|
|
563
|
+
Parser.prototype.start = function() {
|
|
564
|
+
this.read(constants2.PNG_SIGNATURE.length, this._parseSignature.bind(this));
|
|
565
|
+
};
|
|
566
|
+
Parser.prototype._parseSignature = function(data) {
|
|
567
|
+
let signature = constants2.PNG_SIGNATURE;
|
|
568
|
+
for (let i = 0; i < signature.length; i++) {
|
|
569
|
+
if (data[i] !== signature[i]) {
|
|
570
|
+
this.error(new Error("Invalid file signature"));
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
this.read(8, this._parseChunkBegin.bind(this));
|
|
575
|
+
};
|
|
576
|
+
Parser.prototype._parseChunkBegin = function(data) {
|
|
577
|
+
let length = data.readUInt32BE(0);
|
|
578
|
+
let type = data.readUInt32BE(4);
|
|
579
|
+
let name = "";
|
|
580
|
+
for (let i = 4; i < 8; i++) {
|
|
581
|
+
name += String.fromCharCode(data[i]);
|
|
582
|
+
}
|
|
583
|
+
let ancillary = Boolean(data[4] & 32);
|
|
584
|
+
if (!this._hasIHDR && type !== constants2.TYPE_IHDR) {
|
|
585
|
+
this.error(new Error("Expected IHDR on beggining"));
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
this._crc = new CrcCalculator();
|
|
589
|
+
this._crc.write(Buffer.from(name));
|
|
590
|
+
if (this._chunks[type]) {
|
|
591
|
+
return this._chunks[type](length);
|
|
592
|
+
}
|
|
593
|
+
if (!ancillary) {
|
|
594
|
+
this.error(new Error("Unsupported critical chunk type " + name));
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
this.read(length + 4, this._skipChunk.bind(this));
|
|
598
|
+
};
|
|
599
|
+
Parser.prototype._skipChunk = function() {
|
|
600
|
+
this.read(8, this._parseChunkBegin.bind(this));
|
|
601
|
+
};
|
|
602
|
+
Parser.prototype._handleChunkEnd = function() {
|
|
603
|
+
this.read(4, this._parseChunkEnd.bind(this));
|
|
604
|
+
};
|
|
605
|
+
Parser.prototype._parseChunkEnd = function(data) {
|
|
606
|
+
let fileCrc = data.readInt32BE(0);
|
|
607
|
+
let calcCrc = this._crc.crc32();
|
|
608
|
+
if (this._options.checkCRC && calcCrc !== fileCrc) {
|
|
609
|
+
this.error(new Error("Crc error - " + fileCrc + " - " + calcCrc));
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
if (!this._hasIEND) {
|
|
613
|
+
this.read(8, this._parseChunkBegin.bind(this));
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
Parser.prototype._handleIHDR = function(length) {
|
|
617
|
+
this.read(length, this._parseIHDR.bind(this));
|
|
618
|
+
};
|
|
619
|
+
Parser.prototype._parseIHDR = function(data) {
|
|
620
|
+
this._crc.write(data);
|
|
621
|
+
let width = data.readUInt32BE(0);
|
|
622
|
+
let height = data.readUInt32BE(4);
|
|
623
|
+
let depth = data[8];
|
|
624
|
+
let colorType = data[9];
|
|
625
|
+
let compr = data[10];
|
|
626
|
+
let filter = data[11];
|
|
627
|
+
let interlace = data[12];
|
|
628
|
+
if (depth !== 8 && depth !== 4 && depth !== 2 && depth !== 1 && depth !== 16) {
|
|
629
|
+
this.error(new Error("Unsupported bit depth " + depth));
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
if (!(colorType in constants2.COLORTYPE_TO_BPP_MAP)) {
|
|
633
|
+
this.error(new Error("Unsupported color type"));
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
if (compr !== 0) {
|
|
637
|
+
this.error(new Error("Unsupported compression method"));
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
if (filter !== 0) {
|
|
641
|
+
this.error(new Error("Unsupported filter method"));
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
if (interlace !== 0 && interlace !== 1) {
|
|
645
|
+
this.error(new Error("Unsupported interlace method"));
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
this._colorType = colorType;
|
|
649
|
+
let bpp = constants2.COLORTYPE_TO_BPP_MAP[this._colorType];
|
|
650
|
+
this._hasIHDR = true;
|
|
651
|
+
this.metadata({
|
|
652
|
+
width,
|
|
653
|
+
height,
|
|
654
|
+
depth,
|
|
655
|
+
interlace: Boolean(interlace),
|
|
656
|
+
palette: Boolean(colorType & constants2.COLORTYPE_PALETTE),
|
|
657
|
+
color: Boolean(colorType & constants2.COLORTYPE_COLOR),
|
|
658
|
+
alpha: Boolean(colorType & constants2.COLORTYPE_ALPHA),
|
|
659
|
+
bpp,
|
|
660
|
+
colorType
|
|
661
|
+
});
|
|
662
|
+
this._handleChunkEnd();
|
|
663
|
+
};
|
|
664
|
+
Parser.prototype._handlePLTE = function(length) {
|
|
665
|
+
this.read(length, this._parsePLTE.bind(this));
|
|
666
|
+
};
|
|
667
|
+
Parser.prototype._parsePLTE = function(data) {
|
|
668
|
+
this._crc.write(data);
|
|
669
|
+
let entries = Math.floor(data.length / 3);
|
|
670
|
+
for (let i = 0; i < entries; i++) {
|
|
671
|
+
this._palette.push([data[i * 3], data[i * 3 + 1], data[i * 3 + 2], 255]);
|
|
672
|
+
}
|
|
673
|
+
this.palette(this._palette);
|
|
674
|
+
this._handleChunkEnd();
|
|
675
|
+
};
|
|
676
|
+
Parser.prototype._handleTRNS = function(length) {
|
|
677
|
+
this.simpleTransparency();
|
|
678
|
+
this.read(length, this._parseTRNS.bind(this));
|
|
679
|
+
};
|
|
680
|
+
Parser.prototype._parseTRNS = function(data) {
|
|
681
|
+
this._crc.write(data);
|
|
682
|
+
if (this._colorType === constants2.COLORTYPE_PALETTE_COLOR) {
|
|
683
|
+
if (this._palette.length === 0) {
|
|
684
|
+
this.error(new Error("Transparency chunk must be after palette"));
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
if (data.length > this._palette.length) {
|
|
688
|
+
this.error(new Error("More transparent colors than palette size"));
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
for (let i = 0; i < data.length; i++) {
|
|
692
|
+
this._palette[i][3] = data[i];
|
|
693
|
+
}
|
|
694
|
+
this.palette(this._palette);
|
|
695
|
+
}
|
|
696
|
+
if (this._colorType === constants2.COLORTYPE_GRAYSCALE) {
|
|
697
|
+
this.transColor([data.readUInt16BE(0)]);
|
|
698
|
+
}
|
|
699
|
+
if (this._colorType === constants2.COLORTYPE_COLOR) {
|
|
700
|
+
this.transColor([
|
|
701
|
+
data.readUInt16BE(0),
|
|
702
|
+
data.readUInt16BE(2),
|
|
703
|
+
data.readUInt16BE(4)
|
|
704
|
+
]);
|
|
705
|
+
}
|
|
706
|
+
this._handleChunkEnd();
|
|
707
|
+
};
|
|
708
|
+
Parser.prototype._handleGAMA = function(length) {
|
|
709
|
+
this.read(length, this._parseGAMA.bind(this));
|
|
710
|
+
};
|
|
711
|
+
Parser.prototype._parseGAMA = function(data) {
|
|
712
|
+
this._crc.write(data);
|
|
713
|
+
this.gamma(data.readUInt32BE(0) / constants2.GAMMA_DIVISION);
|
|
714
|
+
this._handleChunkEnd();
|
|
715
|
+
};
|
|
716
|
+
Parser.prototype._handleIDAT = function(length) {
|
|
717
|
+
if (!this._emittedHeadersFinished) {
|
|
718
|
+
this._emittedHeadersFinished = true;
|
|
719
|
+
this.headersFinished();
|
|
720
|
+
}
|
|
721
|
+
this.read(-length, this._parseIDAT.bind(this, length));
|
|
722
|
+
};
|
|
723
|
+
Parser.prototype._parseIDAT = function(length, data) {
|
|
724
|
+
this._crc.write(data);
|
|
725
|
+
if (this._colorType === constants2.COLORTYPE_PALETTE_COLOR && this._palette.length === 0) {
|
|
726
|
+
throw new Error("Expected palette not found");
|
|
727
|
+
}
|
|
728
|
+
this.inflateData(data);
|
|
729
|
+
let leftOverLength = length - data.length;
|
|
730
|
+
if (leftOverLength > 0) {
|
|
731
|
+
this._handleIDAT(leftOverLength);
|
|
732
|
+
} else {
|
|
733
|
+
this._handleChunkEnd();
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
Parser.prototype._handleIEND = function(length) {
|
|
737
|
+
this.read(length, this._parseIEND.bind(this));
|
|
738
|
+
};
|
|
739
|
+
Parser.prototype._parseIEND = function(data) {
|
|
740
|
+
this._crc.write(data);
|
|
741
|
+
this._hasIEND = true;
|
|
742
|
+
this._handleChunkEnd();
|
|
743
|
+
if (this.finished) {
|
|
744
|
+
this.finished();
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
// ../../node_modules/pngjs/lib/bitmapper.js
|
|
751
|
+
var require_bitmapper = __commonJS({
|
|
752
|
+
"../../node_modules/pngjs/lib/bitmapper.js"(exports$1) {
|
|
753
|
+
var interlaceUtils = require_interlace();
|
|
754
|
+
var pixelBppMapper = [
|
|
755
|
+
// 0 - dummy entry
|
|
756
|
+
function() {
|
|
757
|
+
},
|
|
758
|
+
// 1 - L
|
|
759
|
+
// 0: 0, 1: 0, 2: 0, 3: 0xff
|
|
760
|
+
function(pxData, data, pxPos, rawPos) {
|
|
761
|
+
if (rawPos === data.length) {
|
|
762
|
+
throw new Error("Ran out of data");
|
|
763
|
+
}
|
|
764
|
+
let pixel = data[rawPos];
|
|
765
|
+
pxData[pxPos] = pixel;
|
|
766
|
+
pxData[pxPos + 1] = pixel;
|
|
767
|
+
pxData[pxPos + 2] = pixel;
|
|
768
|
+
pxData[pxPos + 3] = 255;
|
|
769
|
+
},
|
|
770
|
+
// 2 - LA
|
|
771
|
+
// 0: 0, 1: 0, 2: 0, 3: 1
|
|
772
|
+
function(pxData, data, pxPos, rawPos) {
|
|
773
|
+
if (rawPos + 1 >= data.length) {
|
|
774
|
+
throw new Error("Ran out of data");
|
|
775
|
+
}
|
|
776
|
+
let pixel = data[rawPos];
|
|
777
|
+
pxData[pxPos] = pixel;
|
|
778
|
+
pxData[pxPos + 1] = pixel;
|
|
779
|
+
pxData[pxPos + 2] = pixel;
|
|
780
|
+
pxData[pxPos + 3] = data[rawPos + 1];
|
|
781
|
+
},
|
|
782
|
+
// 3 - RGB
|
|
783
|
+
// 0: 0, 1: 1, 2: 2, 3: 0xff
|
|
784
|
+
function(pxData, data, pxPos, rawPos) {
|
|
785
|
+
if (rawPos + 2 >= data.length) {
|
|
786
|
+
throw new Error("Ran out of data");
|
|
787
|
+
}
|
|
788
|
+
pxData[pxPos] = data[rawPos];
|
|
789
|
+
pxData[pxPos + 1] = data[rawPos + 1];
|
|
790
|
+
pxData[pxPos + 2] = data[rawPos + 2];
|
|
791
|
+
pxData[pxPos + 3] = 255;
|
|
792
|
+
},
|
|
793
|
+
// 4 - RGBA
|
|
794
|
+
// 0: 0, 1: 1, 2: 2, 3: 3
|
|
795
|
+
function(pxData, data, pxPos, rawPos) {
|
|
796
|
+
if (rawPos + 3 >= data.length) {
|
|
797
|
+
throw new Error("Ran out of data");
|
|
798
|
+
}
|
|
799
|
+
pxData[pxPos] = data[rawPos];
|
|
800
|
+
pxData[pxPos + 1] = data[rawPos + 1];
|
|
801
|
+
pxData[pxPos + 2] = data[rawPos + 2];
|
|
802
|
+
pxData[pxPos + 3] = data[rawPos + 3];
|
|
803
|
+
}
|
|
804
|
+
];
|
|
805
|
+
var pixelBppCustomMapper = [
|
|
806
|
+
// 0 - dummy entry
|
|
807
|
+
function() {
|
|
808
|
+
},
|
|
809
|
+
// 1 - L
|
|
810
|
+
// 0: 0, 1: 0, 2: 0, 3: 0xff
|
|
811
|
+
function(pxData, pixelData, pxPos, maxBit) {
|
|
812
|
+
let pixel = pixelData[0];
|
|
813
|
+
pxData[pxPos] = pixel;
|
|
814
|
+
pxData[pxPos + 1] = pixel;
|
|
815
|
+
pxData[pxPos + 2] = pixel;
|
|
816
|
+
pxData[pxPos + 3] = maxBit;
|
|
817
|
+
},
|
|
818
|
+
// 2 - LA
|
|
819
|
+
// 0: 0, 1: 0, 2: 0, 3: 1
|
|
820
|
+
function(pxData, pixelData, pxPos) {
|
|
821
|
+
let pixel = pixelData[0];
|
|
822
|
+
pxData[pxPos] = pixel;
|
|
823
|
+
pxData[pxPos + 1] = pixel;
|
|
824
|
+
pxData[pxPos + 2] = pixel;
|
|
825
|
+
pxData[pxPos + 3] = pixelData[1];
|
|
826
|
+
},
|
|
827
|
+
// 3 - RGB
|
|
828
|
+
// 0: 0, 1: 1, 2: 2, 3: 0xff
|
|
829
|
+
function(pxData, pixelData, pxPos, maxBit) {
|
|
830
|
+
pxData[pxPos] = pixelData[0];
|
|
831
|
+
pxData[pxPos + 1] = pixelData[1];
|
|
832
|
+
pxData[pxPos + 2] = pixelData[2];
|
|
833
|
+
pxData[pxPos + 3] = maxBit;
|
|
834
|
+
},
|
|
835
|
+
// 4 - RGBA
|
|
836
|
+
// 0: 0, 1: 1, 2: 2, 3: 3
|
|
837
|
+
function(pxData, pixelData, pxPos) {
|
|
838
|
+
pxData[pxPos] = pixelData[0];
|
|
839
|
+
pxData[pxPos + 1] = pixelData[1];
|
|
840
|
+
pxData[pxPos + 2] = pixelData[2];
|
|
841
|
+
pxData[pxPos + 3] = pixelData[3];
|
|
842
|
+
}
|
|
843
|
+
];
|
|
844
|
+
function bitRetriever(data, depth) {
|
|
845
|
+
let leftOver = [];
|
|
846
|
+
let i = 0;
|
|
847
|
+
function split() {
|
|
848
|
+
if (i === data.length) {
|
|
849
|
+
throw new Error("Ran out of data");
|
|
850
|
+
}
|
|
851
|
+
let byte = data[i];
|
|
852
|
+
i++;
|
|
853
|
+
let byte8, byte7, byte6, byte5, byte4, byte3, byte2, byte1;
|
|
854
|
+
switch (depth) {
|
|
855
|
+
default:
|
|
856
|
+
throw new Error("unrecognised depth");
|
|
857
|
+
case 16:
|
|
858
|
+
byte2 = data[i];
|
|
859
|
+
i++;
|
|
860
|
+
leftOver.push((byte << 8) + byte2);
|
|
861
|
+
break;
|
|
862
|
+
case 4:
|
|
863
|
+
byte2 = byte & 15;
|
|
864
|
+
byte1 = byte >> 4;
|
|
865
|
+
leftOver.push(byte1, byte2);
|
|
866
|
+
break;
|
|
867
|
+
case 2:
|
|
868
|
+
byte4 = byte & 3;
|
|
869
|
+
byte3 = byte >> 2 & 3;
|
|
870
|
+
byte2 = byte >> 4 & 3;
|
|
871
|
+
byte1 = byte >> 6 & 3;
|
|
872
|
+
leftOver.push(byte1, byte2, byte3, byte4);
|
|
873
|
+
break;
|
|
874
|
+
case 1:
|
|
875
|
+
byte8 = byte & 1;
|
|
876
|
+
byte7 = byte >> 1 & 1;
|
|
877
|
+
byte6 = byte >> 2 & 1;
|
|
878
|
+
byte5 = byte >> 3 & 1;
|
|
879
|
+
byte4 = byte >> 4 & 1;
|
|
880
|
+
byte3 = byte >> 5 & 1;
|
|
881
|
+
byte2 = byte >> 6 & 1;
|
|
882
|
+
byte1 = byte >> 7 & 1;
|
|
883
|
+
leftOver.push(byte1, byte2, byte3, byte4, byte5, byte6, byte7, byte8);
|
|
884
|
+
break;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
return {
|
|
888
|
+
get: function(count) {
|
|
889
|
+
while (leftOver.length < count) {
|
|
890
|
+
split();
|
|
891
|
+
}
|
|
892
|
+
let returner = leftOver.slice(0, count);
|
|
893
|
+
leftOver = leftOver.slice(count);
|
|
894
|
+
return returner;
|
|
895
|
+
},
|
|
896
|
+
resetAfterLine: function() {
|
|
897
|
+
leftOver.length = 0;
|
|
898
|
+
},
|
|
899
|
+
end: function() {
|
|
900
|
+
if (i !== data.length) {
|
|
901
|
+
throw new Error("extra data found");
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
function mapImage8Bit(image, pxData, getPxPos, bpp, data, rawPos) {
|
|
907
|
+
let imageWidth = image.width;
|
|
908
|
+
let imageHeight = image.height;
|
|
909
|
+
let imagePass = image.index;
|
|
910
|
+
for (let y = 0; y < imageHeight; y++) {
|
|
911
|
+
for (let x = 0; x < imageWidth; x++) {
|
|
912
|
+
let pxPos = getPxPos(x, y, imagePass);
|
|
913
|
+
pixelBppMapper[bpp](pxData, data, pxPos, rawPos);
|
|
914
|
+
rawPos += bpp;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
return rawPos;
|
|
918
|
+
}
|
|
919
|
+
function mapImageCustomBit(image, pxData, getPxPos, bpp, bits, maxBit) {
|
|
920
|
+
let imageWidth = image.width;
|
|
921
|
+
let imageHeight = image.height;
|
|
922
|
+
let imagePass = image.index;
|
|
923
|
+
for (let y = 0; y < imageHeight; y++) {
|
|
924
|
+
for (let x = 0; x < imageWidth; x++) {
|
|
925
|
+
let pixelData = bits.get(bpp);
|
|
926
|
+
let pxPos = getPxPos(x, y, imagePass);
|
|
927
|
+
pixelBppCustomMapper[bpp](pxData, pixelData, pxPos, maxBit);
|
|
928
|
+
}
|
|
929
|
+
bits.resetAfterLine();
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
exports$1.dataToBitMap = function(data, bitmapInfo) {
|
|
933
|
+
let width = bitmapInfo.width;
|
|
934
|
+
let height = bitmapInfo.height;
|
|
935
|
+
let depth = bitmapInfo.depth;
|
|
936
|
+
let bpp = bitmapInfo.bpp;
|
|
937
|
+
let interlace = bitmapInfo.interlace;
|
|
938
|
+
let bits;
|
|
939
|
+
if (depth !== 8) {
|
|
940
|
+
bits = bitRetriever(data, depth);
|
|
941
|
+
}
|
|
942
|
+
let pxData;
|
|
943
|
+
if (depth <= 8) {
|
|
944
|
+
pxData = Buffer.alloc(width * height * 4);
|
|
945
|
+
} else {
|
|
946
|
+
pxData = new Uint16Array(width * height * 4);
|
|
947
|
+
}
|
|
948
|
+
let maxBit = Math.pow(2, depth) - 1;
|
|
949
|
+
let rawPos = 0;
|
|
950
|
+
let images;
|
|
951
|
+
let getPxPos;
|
|
952
|
+
if (interlace) {
|
|
953
|
+
images = interlaceUtils.getImagePasses(width, height);
|
|
954
|
+
getPxPos = interlaceUtils.getInterlaceIterator(width, height);
|
|
955
|
+
} else {
|
|
956
|
+
let nonInterlacedPxPos = 0;
|
|
957
|
+
getPxPos = function() {
|
|
958
|
+
let returner = nonInterlacedPxPos;
|
|
959
|
+
nonInterlacedPxPos += 4;
|
|
960
|
+
return returner;
|
|
961
|
+
};
|
|
962
|
+
images = [{ width, height }];
|
|
963
|
+
}
|
|
964
|
+
for (let imageIndex = 0; imageIndex < images.length; imageIndex++) {
|
|
965
|
+
if (depth === 8) {
|
|
966
|
+
rawPos = mapImage8Bit(
|
|
967
|
+
images[imageIndex],
|
|
968
|
+
pxData,
|
|
969
|
+
getPxPos,
|
|
970
|
+
bpp,
|
|
971
|
+
data,
|
|
972
|
+
rawPos
|
|
973
|
+
);
|
|
974
|
+
} else {
|
|
975
|
+
mapImageCustomBit(
|
|
976
|
+
images[imageIndex],
|
|
977
|
+
pxData,
|
|
978
|
+
getPxPos,
|
|
979
|
+
bpp,
|
|
980
|
+
bits,
|
|
981
|
+
maxBit
|
|
982
|
+
);
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
if (depth === 8) {
|
|
986
|
+
if (rawPos !== data.length) {
|
|
987
|
+
throw new Error("extra data found");
|
|
988
|
+
}
|
|
989
|
+
} else {
|
|
990
|
+
bits.end();
|
|
991
|
+
}
|
|
992
|
+
return pxData;
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
|
|
997
|
+
// ../../node_modules/pngjs/lib/format-normaliser.js
|
|
998
|
+
var require_format_normaliser = __commonJS({
|
|
999
|
+
"../../node_modules/pngjs/lib/format-normaliser.js"(exports$1, module) {
|
|
1000
|
+
function dePalette(indata, outdata, width, height, palette) {
|
|
1001
|
+
let pxPos = 0;
|
|
1002
|
+
for (let y = 0; y < height; y++) {
|
|
1003
|
+
for (let x = 0; x < width; x++) {
|
|
1004
|
+
let color = palette[indata[pxPos]];
|
|
1005
|
+
if (!color) {
|
|
1006
|
+
throw new Error("index " + indata[pxPos] + " not in palette");
|
|
1007
|
+
}
|
|
1008
|
+
for (let i = 0; i < 4; i++) {
|
|
1009
|
+
outdata[pxPos + i] = color[i];
|
|
1010
|
+
}
|
|
1011
|
+
pxPos += 4;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
function replaceTransparentColor(indata, outdata, width, height, transColor) {
|
|
1016
|
+
let pxPos = 0;
|
|
1017
|
+
for (let y = 0; y < height; y++) {
|
|
1018
|
+
for (let x = 0; x < width; x++) {
|
|
1019
|
+
let makeTrans = false;
|
|
1020
|
+
if (transColor.length === 1) {
|
|
1021
|
+
if (transColor[0] === indata[pxPos]) {
|
|
1022
|
+
makeTrans = true;
|
|
1023
|
+
}
|
|
1024
|
+
} else if (transColor[0] === indata[pxPos] && transColor[1] === indata[pxPos + 1] && transColor[2] === indata[pxPos + 2]) {
|
|
1025
|
+
makeTrans = true;
|
|
1026
|
+
}
|
|
1027
|
+
if (makeTrans) {
|
|
1028
|
+
for (let i = 0; i < 4; i++) {
|
|
1029
|
+
outdata[pxPos + i] = 0;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
pxPos += 4;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
function scaleDepth(indata, outdata, width, height, depth) {
|
|
1037
|
+
let maxOutSample = 255;
|
|
1038
|
+
let maxInSample = Math.pow(2, depth) - 1;
|
|
1039
|
+
let pxPos = 0;
|
|
1040
|
+
for (let y = 0; y < height; y++) {
|
|
1041
|
+
for (let x = 0; x < width; x++) {
|
|
1042
|
+
for (let i = 0; i < 4; i++) {
|
|
1043
|
+
outdata[pxPos + i] = Math.floor(
|
|
1044
|
+
indata[pxPos + i] * maxOutSample / maxInSample + 0.5
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
pxPos += 4;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
module.exports = function(indata, imageData, skipRescale = false) {
|
|
1052
|
+
let depth = imageData.depth;
|
|
1053
|
+
let width = imageData.width;
|
|
1054
|
+
let height = imageData.height;
|
|
1055
|
+
let colorType = imageData.colorType;
|
|
1056
|
+
let transColor = imageData.transColor;
|
|
1057
|
+
let palette = imageData.palette;
|
|
1058
|
+
let outdata = indata;
|
|
1059
|
+
if (colorType === 3) {
|
|
1060
|
+
dePalette(indata, outdata, width, height, palette);
|
|
1061
|
+
} else {
|
|
1062
|
+
if (transColor) {
|
|
1063
|
+
replaceTransparentColor(indata, outdata, width, height, transColor);
|
|
1064
|
+
}
|
|
1065
|
+
if (depth !== 8 && !skipRescale) {
|
|
1066
|
+
if (depth === 16) {
|
|
1067
|
+
outdata = Buffer.alloc(width * height * 4);
|
|
1068
|
+
}
|
|
1069
|
+
scaleDepth(indata, outdata, width, height, depth);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
return outdata;
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
1076
|
+
|
|
1077
|
+
// ../../node_modules/pngjs/lib/parser-async.js
|
|
1078
|
+
var require_parser_async = __commonJS({
|
|
1079
|
+
"../../node_modules/pngjs/lib/parser-async.js"(exports$1, module) {
|
|
1080
|
+
var util = __require("util");
|
|
1081
|
+
var zlib = __require("zlib");
|
|
1082
|
+
var ChunkStream = require_chunkstream();
|
|
1083
|
+
var FilterAsync = require_filter_parse_async();
|
|
1084
|
+
var Parser = require_parser();
|
|
1085
|
+
var bitmapper = require_bitmapper();
|
|
1086
|
+
var formatNormaliser = require_format_normaliser();
|
|
1087
|
+
var ParserAsync = module.exports = function(options) {
|
|
1088
|
+
ChunkStream.call(this);
|
|
1089
|
+
this._parser = new Parser(options, {
|
|
1090
|
+
read: this.read.bind(this),
|
|
1091
|
+
error: this._handleError.bind(this),
|
|
1092
|
+
metadata: this._handleMetaData.bind(this),
|
|
1093
|
+
gamma: this.emit.bind(this, "gamma"),
|
|
1094
|
+
palette: this._handlePalette.bind(this),
|
|
1095
|
+
transColor: this._handleTransColor.bind(this),
|
|
1096
|
+
finished: this._finished.bind(this),
|
|
1097
|
+
inflateData: this._inflateData.bind(this),
|
|
1098
|
+
simpleTransparency: this._simpleTransparency.bind(this),
|
|
1099
|
+
headersFinished: this._headersFinished.bind(this)
|
|
1100
|
+
});
|
|
1101
|
+
this._options = options;
|
|
1102
|
+
this.writable = true;
|
|
1103
|
+
this._parser.start();
|
|
1104
|
+
};
|
|
1105
|
+
util.inherits(ParserAsync, ChunkStream);
|
|
1106
|
+
ParserAsync.prototype._handleError = function(err) {
|
|
1107
|
+
this.emit("error", err);
|
|
1108
|
+
this.writable = false;
|
|
1109
|
+
this.destroy();
|
|
1110
|
+
if (this._inflate && this._inflate.destroy) {
|
|
1111
|
+
this._inflate.destroy();
|
|
1112
|
+
}
|
|
1113
|
+
if (this._filter) {
|
|
1114
|
+
this._filter.destroy();
|
|
1115
|
+
this._filter.on("error", function() {
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
this.errord = true;
|
|
1119
|
+
};
|
|
1120
|
+
ParserAsync.prototype._inflateData = function(data) {
|
|
1121
|
+
if (!this._inflate) {
|
|
1122
|
+
if (this._bitmapInfo.interlace) {
|
|
1123
|
+
this._inflate = zlib.createInflate();
|
|
1124
|
+
this._inflate.on("error", this.emit.bind(this, "error"));
|
|
1125
|
+
this._filter.on("complete", this._complete.bind(this));
|
|
1126
|
+
this._inflate.pipe(this._filter);
|
|
1127
|
+
} else {
|
|
1128
|
+
let rowSize = (this._bitmapInfo.width * this._bitmapInfo.bpp * this._bitmapInfo.depth + 7 >> 3) + 1;
|
|
1129
|
+
let imageSize = rowSize * this._bitmapInfo.height;
|
|
1130
|
+
let chunkSize = Math.max(imageSize, zlib.Z_MIN_CHUNK);
|
|
1131
|
+
this._inflate = zlib.createInflate({ chunkSize });
|
|
1132
|
+
let leftToInflate = imageSize;
|
|
1133
|
+
let emitError = this.emit.bind(this, "error");
|
|
1134
|
+
this._inflate.on("error", function(err) {
|
|
1135
|
+
if (!leftToInflate) {
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
emitError(err);
|
|
1139
|
+
});
|
|
1140
|
+
this._filter.on("complete", this._complete.bind(this));
|
|
1141
|
+
let filterWrite = this._filter.write.bind(this._filter);
|
|
1142
|
+
this._inflate.on("data", function(chunk) {
|
|
1143
|
+
if (!leftToInflate) {
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
if (chunk.length > leftToInflate) {
|
|
1147
|
+
chunk = chunk.slice(0, leftToInflate);
|
|
1148
|
+
}
|
|
1149
|
+
leftToInflate -= chunk.length;
|
|
1150
|
+
filterWrite(chunk);
|
|
1151
|
+
});
|
|
1152
|
+
this._inflate.on("end", this._filter.end.bind(this._filter));
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
this._inflate.write(data);
|
|
1156
|
+
};
|
|
1157
|
+
ParserAsync.prototype._handleMetaData = function(metaData) {
|
|
1158
|
+
this._metaData = metaData;
|
|
1159
|
+
this._bitmapInfo = Object.create(metaData);
|
|
1160
|
+
this._filter = new FilterAsync(this._bitmapInfo);
|
|
1161
|
+
};
|
|
1162
|
+
ParserAsync.prototype._handleTransColor = function(transColor) {
|
|
1163
|
+
this._bitmapInfo.transColor = transColor;
|
|
1164
|
+
};
|
|
1165
|
+
ParserAsync.prototype._handlePalette = function(palette) {
|
|
1166
|
+
this._bitmapInfo.palette = palette;
|
|
1167
|
+
};
|
|
1168
|
+
ParserAsync.prototype._simpleTransparency = function() {
|
|
1169
|
+
this._metaData.alpha = true;
|
|
1170
|
+
};
|
|
1171
|
+
ParserAsync.prototype._headersFinished = function() {
|
|
1172
|
+
this.emit("metadata", this._metaData);
|
|
1173
|
+
};
|
|
1174
|
+
ParserAsync.prototype._finished = function() {
|
|
1175
|
+
if (this.errord) {
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
if (!this._inflate) {
|
|
1179
|
+
this.emit("error", "No Inflate block");
|
|
1180
|
+
} else {
|
|
1181
|
+
this._inflate.end();
|
|
1182
|
+
}
|
|
1183
|
+
};
|
|
1184
|
+
ParserAsync.prototype._complete = function(filteredData) {
|
|
1185
|
+
if (this.errord) {
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
let normalisedBitmapData;
|
|
1189
|
+
try {
|
|
1190
|
+
let bitmapData = bitmapper.dataToBitMap(filteredData, this._bitmapInfo);
|
|
1191
|
+
normalisedBitmapData = formatNormaliser(
|
|
1192
|
+
bitmapData,
|
|
1193
|
+
this._bitmapInfo,
|
|
1194
|
+
this._options.skipRescale
|
|
1195
|
+
);
|
|
1196
|
+
bitmapData = null;
|
|
1197
|
+
} catch (ex) {
|
|
1198
|
+
this._handleError(ex);
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1201
|
+
this.emit("parsed", normalisedBitmapData);
|
|
1202
|
+
};
|
|
1203
|
+
}
|
|
1204
|
+
});
|
|
1205
|
+
|
|
1206
|
+
// ../../node_modules/pngjs/lib/bitpacker.js
|
|
1207
|
+
var require_bitpacker = __commonJS({
|
|
1208
|
+
"../../node_modules/pngjs/lib/bitpacker.js"(exports$1, module) {
|
|
1209
|
+
var constants2 = require_constants();
|
|
1210
|
+
module.exports = function(dataIn, width, height, options) {
|
|
1211
|
+
let outHasAlpha = [constants2.COLORTYPE_COLOR_ALPHA, constants2.COLORTYPE_ALPHA].indexOf(
|
|
1212
|
+
options.colorType
|
|
1213
|
+
) !== -1;
|
|
1214
|
+
if (options.colorType === options.inputColorType) {
|
|
1215
|
+
let bigEndian = (function() {
|
|
1216
|
+
let buffer = new ArrayBuffer(2);
|
|
1217
|
+
new DataView(buffer).setInt16(
|
|
1218
|
+
0,
|
|
1219
|
+
256,
|
|
1220
|
+
true
|
|
1221
|
+
/* littleEndian */
|
|
1222
|
+
);
|
|
1223
|
+
return new Int16Array(buffer)[0] !== 256;
|
|
1224
|
+
})();
|
|
1225
|
+
if (options.bitDepth === 8 || options.bitDepth === 16 && bigEndian) {
|
|
1226
|
+
return dataIn;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
let data = options.bitDepth !== 16 ? dataIn : new Uint16Array(dataIn.buffer);
|
|
1230
|
+
let maxValue = 255;
|
|
1231
|
+
let inBpp = constants2.COLORTYPE_TO_BPP_MAP[options.inputColorType];
|
|
1232
|
+
if (inBpp === 4 && !options.inputHasAlpha) {
|
|
1233
|
+
inBpp = 3;
|
|
1234
|
+
}
|
|
1235
|
+
let outBpp = constants2.COLORTYPE_TO_BPP_MAP[options.colorType];
|
|
1236
|
+
if (options.bitDepth === 16) {
|
|
1237
|
+
maxValue = 65535;
|
|
1238
|
+
outBpp *= 2;
|
|
1239
|
+
}
|
|
1240
|
+
let outData = Buffer.alloc(width * height * outBpp);
|
|
1241
|
+
let inIndex = 0;
|
|
1242
|
+
let outIndex = 0;
|
|
1243
|
+
let bgColor = options.bgColor || {};
|
|
1244
|
+
if (bgColor.red === void 0) {
|
|
1245
|
+
bgColor.red = maxValue;
|
|
1246
|
+
}
|
|
1247
|
+
if (bgColor.green === void 0) {
|
|
1248
|
+
bgColor.green = maxValue;
|
|
1249
|
+
}
|
|
1250
|
+
if (bgColor.blue === void 0) {
|
|
1251
|
+
bgColor.blue = maxValue;
|
|
1252
|
+
}
|
|
1253
|
+
function getRGBA() {
|
|
1254
|
+
let red;
|
|
1255
|
+
let green;
|
|
1256
|
+
let blue;
|
|
1257
|
+
let alpha = maxValue;
|
|
1258
|
+
switch (options.inputColorType) {
|
|
1259
|
+
case constants2.COLORTYPE_COLOR_ALPHA:
|
|
1260
|
+
alpha = data[inIndex + 3];
|
|
1261
|
+
red = data[inIndex];
|
|
1262
|
+
green = data[inIndex + 1];
|
|
1263
|
+
blue = data[inIndex + 2];
|
|
1264
|
+
break;
|
|
1265
|
+
case constants2.COLORTYPE_COLOR:
|
|
1266
|
+
red = data[inIndex];
|
|
1267
|
+
green = data[inIndex + 1];
|
|
1268
|
+
blue = data[inIndex + 2];
|
|
1269
|
+
break;
|
|
1270
|
+
case constants2.COLORTYPE_ALPHA:
|
|
1271
|
+
alpha = data[inIndex + 1];
|
|
1272
|
+
red = data[inIndex];
|
|
1273
|
+
green = red;
|
|
1274
|
+
blue = red;
|
|
1275
|
+
break;
|
|
1276
|
+
case constants2.COLORTYPE_GRAYSCALE:
|
|
1277
|
+
red = data[inIndex];
|
|
1278
|
+
green = red;
|
|
1279
|
+
blue = red;
|
|
1280
|
+
break;
|
|
1281
|
+
default:
|
|
1282
|
+
throw new Error(
|
|
1283
|
+
"input color type:" + options.inputColorType + " is not supported at present"
|
|
1284
|
+
);
|
|
1285
|
+
}
|
|
1286
|
+
if (options.inputHasAlpha) {
|
|
1287
|
+
if (!outHasAlpha) {
|
|
1288
|
+
alpha /= maxValue;
|
|
1289
|
+
red = Math.min(
|
|
1290
|
+
Math.max(Math.round((1 - alpha) * bgColor.red + alpha * red), 0),
|
|
1291
|
+
maxValue
|
|
1292
|
+
);
|
|
1293
|
+
green = Math.min(
|
|
1294
|
+
Math.max(Math.round((1 - alpha) * bgColor.green + alpha * green), 0),
|
|
1295
|
+
maxValue
|
|
1296
|
+
);
|
|
1297
|
+
blue = Math.min(
|
|
1298
|
+
Math.max(Math.round((1 - alpha) * bgColor.blue + alpha * blue), 0),
|
|
1299
|
+
maxValue
|
|
1300
|
+
);
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
return { red, green, blue, alpha };
|
|
1304
|
+
}
|
|
1305
|
+
for (let y = 0; y < height; y++) {
|
|
1306
|
+
for (let x = 0; x < width; x++) {
|
|
1307
|
+
let rgba = getRGBA();
|
|
1308
|
+
switch (options.colorType) {
|
|
1309
|
+
case constants2.COLORTYPE_COLOR_ALPHA:
|
|
1310
|
+
case constants2.COLORTYPE_COLOR:
|
|
1311
|
+
if (options.bitDepth === 8) {
|
|
1312
|
+
outData[outIndex] = rgba.red;
|
|
1313
|
+
outData[outIndex + 1] = rgba.green;
|
|
1314
|
+
outData[outIndex + 2] = rgba.blue;
|
|
1315
|
+
if (outHasAlpha) {
|
|
1316
|
+
outData[outIndex + 3] = rgba.alpha;
|
|
1317
|
+
}
|
|
1318
|
+
} else {
|
|
1319
|
+
outData.writeUInt16BE(rgba.red, outIndex);
|
|
1320
|
+
outData.writeUInt16BE(rgba.green, outIndex + 2);
|
|
1321
|
+
outData.writeUInt16BE(rgba.blue, outIndex + 4);
|
|
1322
|
+
if (outHasAlpha) {
|
|
1323
|
+
outData.writeUInt16BE(rgba.alpha, outIndex + 6);
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
break;
|
|
1327
|
+
case constants2.COLORTYPE_ALPHA:
|
|
1328
|
+
case constants2.COLORTYPE_GRAYSCALE: {
|
|
1329
|
+
let grayscale = (rgba.red + rgba.green + rgba.blue) / 3;
|
|
1330
|
+
if (options.bitDepth === 8) {
|
|
1331
|
+
outData[outIndex] = grayscale;
|
|
1332
|
+
if (outHasAlpha) {
|
|
1333
|
+
outData[outIndex + 1] = rgba.alpha;
|
|
1334
|
+
}
|
|
1335
|
+
} else {
|
|
1336
|
+
outData.writeUInt16BE(grayscale, outIndex);
|
|
1337
|
+
if (outHasAlpha) {
|
|
1338
|
+
outData.writeUInt16BE(rgba.alpha, outIndex + 2);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
break;
|
|
1342
|
+
}
|
|
1343
|
+
default:
|
|
1344
|
+
throw new Error("unrecognised color Type " + options.colorType);
|
|
1345
|
+
}
|
|
1346
|
+
inIndex += inBpp;
|
|
1347
|
+
outIndex += outBpp;
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
return outData;
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1353
|
+
});
|
|
1354
|
+
|
|
1355
|
+
// ../../node_modules/pngjs/lib/filter-pack.js
|
|
1356
|
+
var require_filter_pack = __commonJS({
|
|
1357
|
+
"../../node_modules/pngjs/lib/filter-pack.js"(exports$1, module) {
|
|
1358
|
+
var paethPredictor = require_paeth_predictor();
|
|
1359
|
+
function filterNone(pxData, pxPos, byteWidth, rawData, rawPos) {
|
|
1360
|
+
for (let x = 0; x < byteWidth; x++) {
|
|
1361
|
+
rawData[rawPos + x] = pxData[pxPos + x];
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
function filterSumNone(pxData, pxPos, byteWidth) {
|
|
1365
|
+
let sum = 0;
|
|
1366
|
+
let length = pxPos + byteWidth;
|
|
1367
|
+
for (let i = pxPos; i < length; i++) {
|
|
1368
|
+
sum += Math.abs(pxData[i]);
|
|
1369
|
+
}
|
|
1370
|
+
return sum;
|
|
1371
|
+
}
|
|
1372
|
+
function filterSub(pxData, pxPos, byteWidth, rawData, rawPos, bpp) {
|
|
1373
|
+
for (let x = 0; x < byteWidth; x++) {
|
|
1374
|
+
let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
|
|
1375
|
+
let val = pxData[pxPos + x] - left;
|
|
1376
|
+
rawData[rawPos + x] = val;
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
function filterSumSub(pxData, pxPos, byteWidth, bpp) {
|
|
1380
|
+
let sum = 0;
|
|
1381
|
+
for (let x = 0; x < byteWidth; x++) {
|
|
1382
|
+
let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
|
|
1383
|
+
let val = pxData[pxPos + x] - left;
|
|
1384
|
+
sum += Math.abs(val);
|
|
1385
|
+
}
|
|
1386
|
+
return sum;
|
|
1387
|
+
}
|
|
1388
|
+
function filterUp(pxData, pxPos, byteWidth, rawData, rawPos) {
|
|
1389
|
+
for (let x = 0; x < byteWidth; x++) {
|
|
1390
|
+
let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
|
|
1391
|
+
let val = pxData[pxPos + x] - up;
|
|
1392
|
+
rawData[rawPos + x] = val;
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
function filterSumUp(pxData, pxPos, byteWidth) {
|
|
1396
|
+
let sum = 0;
|
|
1397
|
+
let length = pxPos + byteWidth;
|
|
1398
|
+
for (let x = pxPos; x < length; x++) {
|
|
1399
|
+
let up = pxPos > 0 ? pxData[x - byteWidth] : 0;
|
|
1400
|
+
let val = pxData[x] - up;
|
|
1401
|
+
sum += Math.abs(val);
|
|
1402
|
+
}
|
|
1403
|
+
return sum;
|
|
1404
|
+
}
|
|
1405
|
+
function filterAvg(pxData, pxPos, byteWidth, rawData, rawPos, bpp) {
|
|
1406
|
+
for (let x = 0; x < byteWidth; x++) {
|
|
1407
|
+
let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
|
|
1408
|
+
let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
|
|
1409
|
+
let val = pxData[pxPos + x] - (left + up >> 1);
|
|
1410
|
+
rawData[rawPos + x] = val;
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
function filterSumAvg(pxData, pxPos, byteWidth, bpp) {
|
|
1414
|
+
let sum = 0;
|
|
1415
|
+
for (let x = 0; x < byteWidth; x++) {
|
|
1416
|
+
let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
|
|
1417
|
+
let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
|
|
1418
|
+
let val = pxData[pxPos + x] - (left + up >> 1);
|
|
1419
|
+
sum += Math.abs(val);
|
|
1420
|
+
}
|
|
1421
|
+
return sum;
|
|
1422
|
+
}
|
|
1423
|
+
function filterPaeth(pxData, pxPos, byteWidth, rawData, rawPos, bpp) {
|
|
1424
|
+
for (let x = 0; x < byteWidth; x++) {
|
|
1425
|
+
let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
|
|
1426
|
+
let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
|
|
1427
|
+
let upleft = pxPos > 0 && x >= bpp ? pxData[pxPos + x - (byteWidth + bpp)] : 0;
|
|
1428
|
+
let val = pxData[pxPos + x] - paethPredictor(left, up, upleft);
|
|
1429
|
+
rawData[rawPos + x] = val;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
function filterSumPaeth(pxData, pxPos, byteWidth, bpp) {
|
|
1433
|
+
let sum = 0;
|
|
1434
|
+
for (let x = 0; x < byteWidth; x++) {
|
|
1435
|
+
let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
|
|
1436
|
+
let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
|
|
1437
|
+
let upleft = pxPos > 0 && x >= bpp ? pxData[pxPos + x - (byteWidth + bpp)] : 0;
|
|
1438
|
+
let val = pxData[pxPos + x] - paethPredictor(left, up, upleft);
|
|
1439
|
+
sum += Math.abs(val);
|
|
1440
|
+
}
|
|
1441
|
+
return sum;
|
|
1442
|
+
}
|
|
1443
|
+
var filters = {
|
|
1444
|
+
0: filterNone,
|
|
1445
|
+
1: filterSub,
|
|
1446
|
+
2: filterUp,
|
|
1447
|
+
3: filterAvg,
|
|
1448
|
+
4: filterPaeth
|
|
1449
|
+
};
|
|
1450
|
+
var filterSums = {
|
|
1451
|
+
0: filterSumNone,
|
|
1452
|
+
1: filterSumSub,
|
|
1453
|
+
2: filterSumUp,
|
|
1454
|
+
3: filterSumAvg,
|
|
1455
|
+
4: filterSumPaeth
|
|
1456
|
+
};
|
|
1457
|
+
module.exports = function(pxData, width, height, options, bpp) {
|
|
1458
|
+
let filterTypes;
|
|
1459
|
+
if (!("filterType" in options) || options.filterType === -1) {
|
|
1460
|
+
filterTypes = [0, 1, 2, 3, 4];
|
|
1461
|
+
} else if (typeof options.filterType === "number") {
|
|
1462
|
+
filterTypes = [options.filterType];
|
|
1463
|
+
} else {
|
|
1464
|
+
throw new Error("unrecognised filter types");
|
|
1465
|
+
}
|
|
1466
|
+
if (options.bitDepth === 16) {
|
|
1467
|
+
bpp *= 2;
|
|
1468
|
+
}
|
|
1469
|
+
let byteWidth = width * bpp;
|
|
1470
|
+
let rawPos = 0;
|
|
1471
|
+
let pxPos = 0;
|
|
1472
|
+
let rawData = Buffer.alloc((byteWidth + 1) * height);
|
|
1473
|
+
let sel = filterTypes[0];
|
|
1474
|
+
for (let y = 0; y < height; y++) {
|
|
1475
|
+
if (filterTypes.length > 1) {
|
|
1476
|
+
let min = Infinity;
|
|
1477
|
+
for (let i = 0; i < filterTypes.length; i++) {
|
|
1478
|
+
let sum = filterSums[filterTypes[i]](pxData, pxPos, byteWidth, bpp);
|
|
1479
|
+
if (sum < min) {
|
|
1480
|
+
sel = filterTypes[i];
|
|
1481
|
+
min = sum;
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
rawData[rawPos] = sel;
|
|
1486
|
+
rawPos++;
|
|
1487
|
+
filters[sel](pxData, pxPos, byteWidth, rawData, rawPos, bpp);
|
|
1488
|
+
rawPos += byteWidth;
|
|
1489
|
+
pxPos += byteWidth;
|
|
1490
|
+
}
|
|
1491
|
+
return rawData;
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
});
|
|
1495
|
+
|
|
1496
|
+
// ../../node_modules/pngjs/lib/packer.js
|
|
1497
|
+
var require_packer = __commonJS({
|
|
1498
|
+
"../../node_modules/pngjs/lib/packer.js"(exports$1, module) {
|
|
1499
|
+
var constants2 = require_constants();
|
|
1500
|
+
var CrcStream = require_crc();
|
|
1501
|
+
var bitPacker = require_bitpacker();
|
|
1502
|
+
var filter = require_filter_pack();
|
|
1503
|
+
var zlib = __require("zlib");
|
|
1504
|
+
var Packer = module.exports = function(options) {
|
|
1505
|
+
this._options = options;
|
|
1506
|
+
options.deflateChunkSize = options.deflateChunkSize || 32 * 1024;
|
|
1507
|
+
options.deflateLevel = options.deflateLevel != null ? options.deflateLevel : 9;
|
|
1508
|
+
options.deflateStrategy = options.deflateStrategy != null ? options.deflateStrategy : 3;
|
|
1509
|
+
options.inputHasAlpha = options.inputHasAlpha != null ? options.inputHasAlpha : true;
|
|
1510
|
+
options.deflateFactory = options.deflateFactory || zlib.createDeflate;
|
|
1511
|
+
options.bitDepth = options.bitDepth || 8;
|
|
1512
|
+
options.colorType = typeof options.colorType === "number" ? options.colorType : constants2.COLORTYPE_COLOR_ALPHA;
|
|
1513
|
+
options.inputColorType = typeof options.inputColorType === "number" ? options.inputColorType : constants2.COLORTYPE_COLOR_ALPHA;
|
|
1514
|
+
if ([
|
|
1515
|
+
constants2.COLORTYPE_GRAYSCALE,
|
|
1516
|
+
constants2.COLORTYPE_COLOR,
|
|
1517
|
+
constants2.COLORTYPE_COLOR_ALPHA,
|
|
1518
|
+
constants2.COLORTYPE_ALPHA
|
|
1519
|
+
].indexOf(options.colorType) === -1) {
|
|
1520
|
+
throw new Error(
|
|
1521
|
+
"option color type:" + options.colorType + " is not supported at present"
|
|
1522
|
+
);
|
|
1523
|
+
}
|
|
1524
|
+
if ([
|
|
1525
|
+
constants2.COLORTYPE_GRAYSCALE,
|
|
1526
|
+
constants2.COLORTYPE_COLOR,
|
|
1527
|
+
constants2.COLORTYPE_COLOR_ALPHA,
|
|
1528
|
+
constants2.COLORTYPE_ALPHA
|
|
1529
|
+
].indexOf(options.inputColorType) === -1) {
|
|
1530
|
+
throw new Error(
|
|
1531
|
+
"option input color type:" + options.inputColorType + " is not supported at present"
|
|
1532
|
+
);
|
|
1533
|
+
}
|
|
1534
|
+
if (options.bitDepth !== 8 && options.bitDepth !== 16) {
|
|
1535
|
+
throw new Error(
|
|
1536
|
+
"option bit depth:" + options.bitDepth + " is not supported at present"
|
|
1537
|
+
);
|
|
1538
|
+
}
|
|
1539
|
+
};
|
|
1540
|
+
Packer.prototype.getDeflateOptions = function() {
|
|
1541
|
+
return {
|
|
1542
|
+
chunkSize: this._options.deflateChunkSize,
|
|
1543
|
+
level: this._options.deflateLevel,
|
|
1544
|
+
strategy: this._options.deflateStrategy
|
|
1545
|
+
};
|
|
1546
|
+
};
|
|
1547
|
+
Packer.prototype.createDeflate = function() {
|
|
1548
|
+
return this._options.deflateFactory(this.getDeflateOptions());
|
|
1549
|
+
};
|
|
1550
|
+
Packer.prototype.filterData = function(data, width, height) {
|
|
1551
|
+
let packedData = bitPacker(data, width, height, this._options);
|
|
1552
|
+
let bpp = constants2.COLORTYPE_TO_BPP_MAP[this._options.colorType];
|
|
1553
|
+
let filteredData = filter(packedData, width, height, this._options, bpp);
|
|
1554
|
+
return filteredData;
|
|
1555
|
+
};
|
|
1556
|
+
Packer.prototype._packChunk = function(type, data) {
|
|
1557
|
+
let len = data ? data.length : 0;
|
|
1558
|
+
let buf = Buffer.alloc(len + 12);
|
|
1559
|
+
buf.writeUInt32BE(len, 0);
|
|
1560
|
+
buf.writeUInt32BE(type, 4);
|
|
1561
|
+
if (data) {
|
|
1562
|
+
data.copy(buf, 8);
|
|
1563
|
+
}
|
|
1564
|
+
buf.writeInt32BE(
|
|
1565
|
+
CrcStream.crc32(buf.slice(4, buf.length - 4)),
|
|
1566
|
+
buf.length - 4
|
|
1567
|
+
);
|
|
1568
|
+
return buf;
|
|
1569
|
+
};
|
|
1570
|
+
Packer.prototype.packGAMA = function(gamma) {
|
|
1571
|
+
let buf = Buffer.alloc(4);
|
|
1572
|
+
buf.writeUInt32BE(Math.floor(gamma * constants2.GAMMA_DIVISION), 0);
|
|
1573
|
+
return this._packChunk(constants2.TYPE_gAMA, buf);
|
|
1574
|
+
};
|
|
1575
|
+
Packer.prototype.packIHDR = function(width, height) {
|
|
1576
|
+
let buf = Buffer.alloc(13);
|
|
1577
|
+
buf.writeUInt32BE(width, 0);
|
|
1578
|
+
buf.writeUInt32BE(height, 4);
|
|
1579
|
+
buf[8] = this._options.bitDepth;
|
|
1580
|
+
buf[9] = this._options.colorType;
|
|
1581
|
+
buf[10] = 0;
|
|
1582
|
+
buf[11] = 0;
|
|
1583
|
+
buf[12] = 0;
|
|
1584
|
+
return this._packChunk(constants2.TYPE_IHDR, buf);
|
|
1585
|
+
};
|
|
1586
|
+
Packer.prototype.packIDAT = function(data) {
|
|
1587
|
+
return this._packChunk(constants2.TYPE_IDAT, data);
|
|
1588
|
+
};
|
|
1589
|
+
Packer.prototype.packIEND = function() {
|
|
1590
|
+
return this._packChunk(constants2.TYPE_IEND, null);
|
|
1591
|
+
};
|
|
1592
|
+
}
|
|
1593
|
+
});
|
|
1594
|
+
|
|
1595
|
+
// ../../node_modules/pngjs/lib/packer-async.js
|
|
1596
|
+
var require_packer_async = __commonJS({
|
|
1597
|
+
"../../node_modules/pngjs/lib/packer-async.js"(exports$1, module) {
|
|
1598
|
+
var util = __require("util");
|
|
1599
|
+
var Stream = __require("stream");
|
|
1600
|
+
var constants2 = require_constants();
|
|
1601
|
+
var Packer = require_packer();
|
|
1602
|
+
var PackerAsync = module.exports = function(opt) {
|
|
1603
|
+
Stream.call(this);
|
|
1604
|
+
let options = opt || {};
|
|
1605
|
+
this._packer = new Packer(options);
|
|
1606
|
+
this._deflate = this._packer.createDeflate();
|
|
1607
|
+
this.readable = true;
|
|
1608
|
+
};
|
|
1609
|
+
util.inherits(PackerAsync, Stream);
|
|
1610
|
+
PackerAsync.prototype.pack = function(data, width, height, gamma) {
|
|
1611
|
+
this.emit("data", Buffer.from(constants2.PNG_SIGNATURE));
|
|
1612
|
+
this.emit("data", this._packer.packIHDR(width, height));
|
|
1613
|
+
if (gamma) {
|
|
1614
|
+
this.emit("data", this._packer.packGAMA(gamma));
|
|
1615
|
+
}
|
|
1616
|
+
let filteredData = this._packer.filterData(data, width, height);
|
|
1617
|
+
this._deflate.on("error", this.emit.bind(this, "error"));
|
|
1618
|
+
this._deflate.on(
|
|
1619
|
+
"data",
|
|
1620
|
+
function(compressedData) {
|
|
1621
|
+
this.emit("data", this._packer.packIDAT(compressedData));
|
|
1622
|
+
}.bind(this)
|
|
1623
|
+
);
|
|
1624
|
+
this._deflate.on(
|
|
1625
|
+
"end",
|
|
1626
|
+
function() {
|
|
1627
|
+
this.emit("data", this._packer.packIEND());
|
|
1628
|
+
this.emit("end");
|
|
1629
|
+
}.bind(this)
|
|
1630
|
+
);
|
|
1631
|
+
this._deflate.end(filteredData);
|
|
1632
|
+
};
|
|
1633
|
+
}
|
|
1634
|
+
});
|
|
1635
|
+
|
|
1636
|
+
// ../../node_modules/pngjs/lib/sync-inflate.js
|
|
1637
|
+
var require_sync_inflate = __commonJS({
|
|
1638
|
+
"../../node_modules/pngjs/lib/sync-inflate.js"(exports$1, module) {
|
|
1639
|
+
var assert = __require("assert").ok;
|
|
1640
|
+
var zlib = __require("zlib");
|
|
1641
|
+
var util = __require("util");
|
|
1642
|
+
var kMaxLength = __require("buffer").kMaxLength;
|
|
1643
|
+
function Inflate(opts) {
|
|
1644
|
+
if (!(this instanceof Inflate)) {
|
|
1645
|
+
return new Inflate(opts);
|
|
1646
|
+
}
|
|
1647
|
+
if (opts && opts.chunkSize < zlib.Z_MIN_CHUNK) {
|
|
1648
|
+
opts.chunkSize = zlib.Z_MIN_CHUNK;
|
|
1649
|
+
}
|
|
1650
|
+
zlib.Inflate.call(this, opts);
|
|
1651
|
+
this._offset = this._offset === void 0 ? this._outOffset : this._offset;
|
|
1652
|
+
this._buffer = this._buffer || this._outBuffer;
|
|
1653
|
+
if (opts && opts.maxLength != null) {
|
|
1654
|
+
this._maxLength = opts.maxLength;
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
function createInflate(opts) {
|
|
1658
|
+
return new Inflate(opts);
|
|
1659
|
+
}
|
|
1660
|
+
function _close(engine, callback) {
|
|
1661
|
+
if (!engine._handle) {
|
|
1662
|
+
return;
|
|
1663
|
+
}
|
|
1664
|
+
engine._handle.close();
|
|
1665
|
+
engine._handle = null;
|
|
1666
|
+
}
|
|
1667
|
+
Inflate.prototype._processChunk = function(chunk, flushFlag, asyncCb) {
|
|
1668
|
+
if (typeof asyncCb === "function") {
|
|
1669
|
+
return zlib.Inflate._processChunk.call(this, chunk, flushFlag, asyncCb);
|
|
1670
|
+
}
|
|
1671
|
+
let self = this;
|
|
1672
|
+
let availInBefore = chunk && chunk.length;
|
|
1673
|
+
let availOutBefore = this._chunkSize - this._offset;
|
|
1674
|
+
let leftToInflate = this._maxLength;
|
|
1675
|
+
let inOff = 0;
|
|
1676
|
+
let buffers = [];
|
|
1677
|
+
let nread = 0;
|
|
1678
|
+
let error;
|
|
1679
|
+
this.on("error", function(err) {
|
|
1680
|
+
error = err;
|
|
1681
|
+
});
|
|
1682
|
+
function handleChunk(availInAfter, availOutAfter) {
|
|
1683
|
+
if (self._hadError) {
|
|
1684
|
+
return;
|
|
1685
|
+
}
|
|
1686
|
+
let have = availOutBefore - availOutAfter;
|
|
1687
|
+
assert(have >= 0, "have should not go down");
|
|
1688
|
+
if (have > 0) {
|
|
1689
|
+
let out = self._buffer.slice(self._offset, self._offset + have);
|
|
1690
|
+
self._offset += have;
|
|
1691
|
+
if (out.length > leftToInflate) {
|
|
1692
|
+
out = out.slice(0, leftToInflate);
|
|
1693
|
+
}
|
|
1694
|
+
buffers.push(out);
|
|
1695
|
+
nread += out.length;
|
|
1696
|
+
leftToInflate -= out.length;
|
|
1697
|
+
if (leftToInflate === 0) {
|
|
1698
|
+
return false;
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
if (availOutAfter === 0 || self._offset >= self._chunkSize) {
|
|
1702
|
+
availOutBefore = self._chunkSize;
|
|
1703
|
+
self._offset = 0;
|
|
1704
|
+
self._buffer = Buffer.allocUnsafe(self._chunkSize);
|
|
1705
|
+
}
|
|
1706
|
+
if (availOutAfter === 0) {
|
|
1707
|
+
inOff += availInBefore - availInAfter;
|
|
1708
|
+
availInBefore = availInAfter;
|
|
1709
|
+
return true;
|
|
1710
|
+
}
|
|
1711
|
+
return false;
|
|
1712
|
+
}
|
|
1713
|
+
assert(this._handle, "zlib binding closed");
|
|
1714
|
+
let res;
|
|
1715
|
+
do {
|
|
1716
|
+
res = this._handle.writeSync(
|
|
1717
|
+
flushFlag,
|
|
1718
|
+
chunk,
|
|
1719
|
+
// in
|
|
1720
|
+
inOff,
|
|
1721
|
+
// in_off
|
|
1722
|
+
availInBefore,
|
|
1723
|
+
// in_len
|
|
1724
|
+
this._buffer,
|
|
1725
|
+
// out
|
|
1726
|
+
this._offset,
|
|
1727
|
+
//out_off
|
|
1728
|
+
availOutBefore
|
|
1729
|
+
);
|
|
1730
|
+
res = res || this._writeState;
|
|
1731
|
+
} while (!this._hadError && handleChunk(res[0], res[1]));
|
|
1732
|
+
if (this._hadError) {
|
|
1733
|
+
throw error;
|
|
1734
|
+
}
|
|
1735
|
+
if (nread >= kMaxLength) {
|
|
1736
|
+
_close(this);
|
|
1737
|
+
throw new RangeError(
|
|
1738
|
+
"Cannot create final Buffer. It would be larger than 0x" + kMaxLength.toString(16) + " bytes"
|
|
1739
|
+
);
|
|
1740
|
+
}
|
|
1741
|
+
let buf = Buffer.concat(buffers, nread);
|
|
1742
|
+
_close(this);
|
|
1743
|
+
return buf;
|
|
1744
|
+
};
|
|
1745
|
+
util.inherits(Inflate, zlib.Inflate);
|
|
1746
|
+
function zlibBufferSync(engine, buffer) {
|
|
1747
|
+
if (typeof buffer === "string") {
|
|
1748
|
+
buffer = Buffer.from(buffer);
|
|
1749
|
+
}
|
|
1750
|
+
if (!(buffer instanceof Buffer)) {
|
|
1751
|
+
throw new TypeError("Not a string or buffer");
|
|
1752
|
+
}
|
|
1753
|
+
let flushFlag = engine._finishFlushFlag;
|
|
1754
|
+
if (flushFlag == null) {
|
|
1755
|
+
flushFlag = zlib.Z_FINISH;
|
|
1756
|
+
}
|
|
1757
|
+
return engine._processChunk(buffer, flushFlag);
|
|
1758
|
+
}
|
|
1759
|
+
function inflateSync(buffer, opts) {
|
|
1760
|
+
return zlibBufferSync(new Inflate(opts), buffer);
|
|
1761
|
+
}
|
|
1762
|
+
module.exports = exports$1 = inflateSync;
|
|
1763
|
+
exports$1.Inflate = Inflate;
|
|
1764
|
+
exports$1.createInflate = createInflate;
|
|
1765
|
+
exports$1.inflateSync = inflateSync;
|
|
1766
|
+
}
|
|
1767
|
+
});
|
|
1768
|
+
|
|
1769
|
+
// ../../node_modules/pngjs/lib/sync-reader.js
|
|
1770
|
+
var require_sync_reader = __commonJS({
|
|
1771
|
+
"../../node_modules/pngjs/lib/sync-reader.js"(exports$1, module) {
|
|
1772
|
+
var SyncReader = module.exports = function(buffer) {
|
|
1773
|
+
this._buffer = buffer;
|
|
1774
|
+
this._reads = [];
|
|
1775
|
+
};
|
|
1776
|
+
SyncReader.prototype.read = function(length, callback) {
|
|
1777
|
+
this._reads.push({
|
|
1778
|
+
length: Math.abs(length),
|
|
1779
|
+
// if length < 0 then at most this length
|
|
1780
|
+
allowLess: length < 0,
|
|
1781
|
+
func: callback
|
|
1782
|
+
});
|
|
1783
|
+
};
|
|
1784
|
+
SyncReader.prototype.process = function() {
|
|
1785
|
+
while (this._reads.length > 0 && this._buffer.length) {
|
|
1786
|
+
let read = this._reads[0];
|
|
1787
|
+
if (this._buffer.length && (this._buffer.length >= read.length || read.allowLess)) {
|
|
1788
|
+
this._reads.shift();
|
|
1789
|
+
let buf = this._buffer;
|
|
1790
|
+
this._buffer = buf.slice(read.length);
|
|
1791
|
+
read.func.call(this, buf.slice(0, read.length));
|
|
1792
|
+
} else {
|
|
1793
|
+
break;
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
if (this._reads.length > 0) {
|
|
1797
|
+
throw new Error("There are some read requests waitng on finished stream");
|
|
1798
|
+
}
|
|
1799
|
+
if (this._buffer.length > 0) {
|
|
1800
|
+
throw new Error("unrecognised content at end of stream");
|
|
1801
|
+
}
|
|
1802
|
+
};
|
|
1803
|
+
}
|
|
1804
|
+
});
|
|
1805
|
+
|
|
1806
|
+
// ../../node_modules/pngjs/lib/filter-parse-sync.js
|
|
1807
|
+
var require_filter_parse_sync = __commonJS({
|
|
1808
|
+
"../../node_modules/pngjs/lib/filter-parse-sync.js"(exports$1) {
|
|
1809
|
+
var SyncReader = require_sync_reader();
|
|
1810
|
+
var Filter = require_filter_parse();
|
|
1811
|
+
exports$1.process = function(inBuffer, bitmapInfo) {
|
|
1812
|
+
let outBuffers = [];
|
|
1813
|
+
let reader = new SyncReader(inBuffer);
|
|
1814
|
+
let filter = new Filter(bitmapInfo, {
|
|
1815
|
+
read: reader.read.bind(reader),
|
|
1816
|
+
write: function(bufferPart) {
|
|
1817
|
+
outBuffers.push(bufferPart);
|
|
1818
|
+
},
|
|
1819
|
+
complete: function() {
|
|
1820
|
+
}
|
|
1821
|
+
});
|
|
1822
|
+
filter.start();
|
|
1823
|
+
reader.process();
|
|
1824
|
+
return Buffer.concat(outBuffers);
|
|
1825
|
+
};
|
|
1826
|
+
}
|
|
1827
|
+
});
|
|
1828
|
+
|
|
1829
|
+
// ../../node_modules/pngjs/lib/parser-sync.js
|
|
1830
|
+
var require_parser_sync = __commonJS({
|
|
1831
|
+
"../../node_modules/pngjs/lib/parser-sync.js"(exports$1, module) {
|
|
1832
|
+
var hasSyncZlib = true;
|
|
1833
|
+
var zlib = __require("zlib");
|
|
1834
|
+
var inflateSync = require_sync_inflate();
|
|
1835
|
+
if (!zlib.deflateSync) {
|
|
1836
|
+
hasSyncZlib = false;
|
|
1837
|
+
}
|
|
1838
|
+
var SyncReader = require_sync_reader();
|
|
1839
|
+
var FilterSync = require_filter_parse_sync();
|
|
1840
|
+
var Parser = require_parser();
|
|
1841
|
+
var bitmapper = require_bitmapper();
|
|
1842
|
+
var formatNormaliser = require_format_normaliser();
|
|
1843
|
+
module.exports = function(buffer, options) {
|
|
1844
|
+
if (!hasSyncZlib) {
|
|
1845
|
+
throw new Error(
|
|
1846
|
+
"To use the sync capability of this library in old node versions, please pin pngjs to v2.3.0"
|
|
1847
|
+
);
|
|
1848
|
+
}
|
|
1849
|
+
let err;
|
|
1850
|
+
function handleError(_err_) {
|
|
1851
|
+
err = _err_;
|
|
1852
|
+
}
|
|
1853
|
+
let metaData;
|
|
1854
|
+
function handleMetaData(_metaData_) {
|
|
1855
|
+
metaData = _metaData_;
|
|
1856
|
+
}
|
|
1857
|
+
function handleTransColor(transColor) {
|
|
1858
|
+
metaData.transColor = transColor;
|
|
1859
|
+
}
|
|
1860
|
+
function handlePalette(palette) {
|
|
1861
|
+
metaData.palette = palette;
|
|
1862
|
+
}
|
|
1863
|
+
function handleSimpleTransparency() {
|
|
1864
|
+
metaData.alpha = true;
|
|
1865
|
+
}
|
|
1866
|
+
let gamma;
|
|
1867
|
+
function handleGamma(_gamma_) {
|
|
1868
|
+
gamma = _gamma_;
|
|
1869
|
+
}
|
|
1870
|
+
let inflateDataList = [];
|
|
1871
|
+
function handleInflateData(inflatedData2) {
|
|
1872
|
+
inflateDataList.push(inflatedData2);
|
|
1873
|
+
}
|
|
1874
|
+
let reader = new SyncReader(buffer);
|
|
1875
|
+
let parser = new Parser(options, {
|
|
1876
|
+
read: reader.read.bind(reader),
|
|
1877
|
+
error: handleError,
|
|
1878
|
+
metadata: handleMetaData,
|
|
1879
|
+
gamma: handleGamma,
|
|
1880
|
+
palette: handlePalette,
|
|
1881
|
+
transColor: handleTransColor,
|
|
1882
|
+
inflateData: handleInflateData,
|
|
1883
|
+
simpleTransparency: handleSimpleTransparency
|
|
1884
|
+
});
|
|
1885
|
+
parser.start();
|
|
1886
|
+
reader.process();
|
|
1887
|
+
if (err) {
|
|
1888
|
+
throw err;
|
|
1889
|
+
}
|
|
1890
|
+
let inflateData = Buffer.concat(inflateDataList);
|
|
1891
|
+
inflateDataList.length = 0;
|
|
1892
|
+
let inflatedData;
|
|
1893
|
+
if (metaData.interlace) {
|
|
1894
|
+
inflatedData = zlib.inflateSync(inflateData);
|
|
1895
|
+
} else {
|
|
1896
|
+
let rowSize = (metaData.width * metaData.bpp * metaData.depth + 7 >> 3) + 1;
|
|
1897
|
+
let imageSize = rowSize * metaData.height;
|
|
1898
|
+
inflatedData = inflateSync(inflateData, {
|
|
1899
|
+
chunkSize: imageSize,
|
|
1900
|
+
maxLength: imageSize
|
|
1901
|
+
});
|
|
1902
|
+
}
|
|
1903
|
+
inflateData = null;
|
|
1904
|
+
if (!inflatedData || !inflatedData.length) {
|
|
1905
|
+
throw new Error("bad png - invalid inflate data response");
|
|
1906
|
+
}
|
|
1907
|
+
let unfilteredData = FilterSync.process(inflatedData, metaData);
|
|
1908
|
+
inflateData = null;
|
|
1909
|
+
let bitmapData = bitmapper.dataToBitMap(unfilteredData, metaData);
|
|
1910
|
+
unfilteredData = null;
|
|
1911
|
+
let normalisedBitmapData = formatNormaliser(
|
|
1912
|
+
bitmapData,
|
|
1913
|
+
metaData,
|
|
1914
|
+
options.skipRescale
|
|
1915
|
+
);
|
|
1916
|
+
metaData.data = normalisedBitmapData;
|
|
1917
|
+
metaData.gamma = gamma || 0;
|
|
1918
|
+
return metaData;
|
|
1919
|
+
};
|
|
1920
|
+
}
|
|
1921
|
+
});
|
|
1922
|
+
|
|
1923
|
+
// ../../node_modules/pngjs/lib/packer-sync.js
|
|
1924
|
+
var require_packer_sync = __commonJS({
|
|
1925
|
+
"../../node_modules/pngjs/lib/packer-sync.js"(exports$1, module) {
|
|
1926
|
+
var hasSyncZlib = true;
|
|
1927
|
+
var zlib = __require("zlib");
|
|
1928
|
+
if (!zlib.deflateSync) {
|
|
1929
|
+
hasSyncZlib = false;
|
|
1930
|
+
}
|
|
1931
|
+
var constants2 = require_constants();
|
|
1932
|
+
var Packer = require_packer();
|
|
1933
|
+
module.exports = function(metaData, opt) {
|
|
1934
|
+
if (!hasSyncZlib) {
|
|
1935
|
+
throw new Error(
|
|
1936
|
+
"To use the sync capability of this library in old node versions, please pin pngjs to v2.3.0"
|
|
1937
|
+
);
|
|
1938
|
+
}
|
|
1939
|
+
let options = opt || {};
|
|
1940
|
+
let packer = new Packer(options);
|
|
1941
|
+
let chunks = [];
|
|
1942
|
+
chunks.push(Buffer.from(constants2.PNG_SIGNATURE));
|
|
1943
|
+
chunks.push(packer.packIHDR(metaData.width, metaData.height));
|
|
1944
|
+
if (metaData.gamma) {
|
|
1945
|
+
chunks.push(packer.packGAMA(metaData.gamma));
|
|
1946
|
+
}
|
|
1947
|
+
let filteredData = packer.filterData(
|
|
1948
|
+
metaData.data,
|
|
1949
|
+
metaData.width,
|
|
1950
|
+
metaData.height
|
|
1951
|
+
);
|
|
1952
|
+
let compressedData = zlib.deflateSync(
|
|
1953
|
+
filteredData,
|
|
1954
|
+
packer.getDeflateOptions()
|
|
1955
|
+
);
|
|
1956
|
+
filteredData = null;
|
|
1957
|
+
if (!compressedData || !compressedData.length) {
|
|
1958
|
+
throw new Error("bad png - invalid compressed data response");
|
|
1959
|
+
}
|
|
1960
|
+
chunks.push(packer.packIDAT(compressedData));
|
|
1961
|
+
chunks.push(packer.packIEND());
|
|
1962
|
+
return Buffer.concat(chunks);
|
|
1963
|
+
};
|
|
1964
|
+
}
|
|
1965
|
+
});
|
|
1966
|
+
|
|
1967
|
+
// ../../node_modules/pngjs/lib/png-sync.js
|
|
1968
|
+
var require_png_sync = __commonJS({
|
|
1969
|
+
"../../node_modules/pngjs/lib/png-sync.js"(exports$1) {
|
|
1970
|
+
var parse = require_parser_sync();
|
|
1971
|
+
var pack = require_packer_sync();
|
|
1972
|
+
exports$1.read = function(buffer, options) {
|
|
1973
|
+
return parse(buffer, options || {});
|
|
1974
|
+
};
|
|
1975
|
+
exports$1.write = function(png, options) {
|
|
1976
|
+
return pack(png, options);
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
});
|
|
1980
|
+
|
|
1981
|
+
// ../../node_modules/pngjs/lib/png.js
|
|
1982
|
+
var require_png = __commonJS({
|
|
1983
|
+
"../../node_modules/pngjs/lib/png.js"(exports$1) {
|
|
1984
|
+
var util = __require("util");
|
|
1985
|
+
var Stream = __require("stream");
|
|
1986
|
+
var Parser = require_parser_async();
|
|
1987
|
+
var Packer = require_packer_async();
|
|
1988
|
+
var PNGSync = require_png_sync();
|
|
1989
|
+
var PNG2 = exports$1.PNG = function(options) {
|
|
1990
|
+
Stream.call(this);
|
|
1991
|
+
options = options || {};
|
|
1992
|
+
this.width = options.width | 0;
|
|
1993
|
+
this.height = options.height | 0;
|
|
1994
|
+
this.data = this.width > 0 && this.height > 0 ? Buffer.alloc(4 * this.width * this.height) : null;
|
|
1995
|
+
if (options.fill && this.data) {
|
|
1996
|
+
this.data.fill(0);
|
|
1997
|
+
}
|
|
1998
|
+
this.gamma = 0;
|
|
1999
|
+
this.readable = this.writable = true;
|
|
2000
|
+
this._parser = new Parser(options);
|
|
2001
|
+
this._parser.on("error", this.emit.bind(this, "error"));
|
|
2002
|
+
this._parser.on("close", this._handleClose.bind(this));
|
|
2003
|
+
this._parser.on("metadata", this._metadata.bind(this));
|
|
2004
|
+
this._parser.on("gamma", this._gamma.bind(this));
|
|
2005
|
+
this._parser.on(
|
|
2006
|
+
"parsed",
|
|
2007
|
+
function(data) {
|
|
2008
|
+
this.data = data;
|
|
2009
|
+
this.emit("parsed", data);
|
|
2010
|
+
}.bind(this)
|
|
2011
|
+
);
|
|
2012
|
+
this._packer = new Packer(options);
|
|
2013
|
+
this._packer.on("data", this.emit.bind(this, "data"));
|
|
2014
|
+
this._packer.on("end", this.emit.bind(this, "end"));
|
|
2015
|
+
this._parser.on("close", this._handleClose.bind(this));
|
|
2016
|
+
this._packer.on("error", this.emit.bind(this, "error"));
|
|
2017
|
+
};
|
|
2018
|
+
util.inherits(PNG2, Stream);
|
|
2019
|
+
PNG2.sync = PNGSync;
|
|
2020
|
+
PNG2.prototype.pack = function() {
|
|
2021
|
+
if (!this.data || !this.data.length) {
|
|
2022
|
+
this.emit("error", "No data provided");
|
|
2023
|
+
return this;
|
|
2024
|
+
}
|
|
2025
|
+
process.nextTick(
|
|
2026
|
+
function() {
|
|
2027
|
+
this._packer.pack(this.data, this.width, this.height, this.gamma);
|
|
2028
|
+
}.bind(this)
|
|
2029
|
+
);
|
|
2030
|
+
return this;
|
|
2031
|
+
};
|
|
2032
|
+
PNG2.prototype.parse = function(data, callback) {
|
|
2033
|
+
if (callback) {
|
|
2034
|
+
let onParsed, onError;
|
|
2035
|
+
onParsed = function(parsedData) {
|
|
2036
|
+
this.removeListener("error", onError);
|
|
2037
|
+
this.data = parsedData;
|
|
2038
|
+
callback(null, this);
|
|
2039
|
+
}.bind(this);
|
|
2040
|
+
onError = function(err) {
|
|
2041
|
+
this.removeListener("parsed", onParsed);
|
|
2042
|
+
callback(err, null);
|
|
2043
|
+
}.bind(this);
|
|
2044
|
+
this.once("parsed", onParsed);
|
|
2045
|
+
this.once("error", onError);
|
|
2046
|
+
}
|
|
2047
|
+
this.end(data);
|
|
2048
|
+
return this;
|
|
2049
|
+
};
|
|
2050
|
+
PNG2.prototype.write = function(data) {
|
|
2051
|
+
this._parser.write(data);
|
|
2052
|
+
return true;
|
|
2053
|
+
};
|
|
2054
|
+
PNG2.prototype.end = function(data) {
|
|
2055
|
+
this._parser.end(data);
|
|
2056
|
+
};
|
|
2057
|
+
PNG2.prototype._metadata = function(metadata) {
|
|
2058
|
+
this.width = metadata.width;
|
|
2059
|
+
this.height = metadata.height;
|
|
2060
|
+
this.emit("metadata", metadata);
|
|
2061
|
+
};
|
|
2062
|
+
PNG2.prototype._gamma = function(gamma) {
|
|
2063
|
+
this.gamma = gamma;
|
|
2064
|
+
};
|
|
2065
|
+
PNG2.prototype._handleClose = function() {
|
|
2066
|
+
if (!this._parser.writable && !this._packer.readable) {
|
|
2067
|
+
this.emit("close");
|
|
2068
|
+
}
|
|
2069
|
+
};
|
|
2070
|
+
PNG2.bitblt = function(src, dst, srcX, srcY, width, height, deltaX, deltaY) {
|
|
2071
|
+
srcX |= 0;
|
|
2072
|
+
srcY |= 0;
|
|
2073
|
+
width |= 0;
|
|
2074
|
+
height |= 0;
|
|
2075
|
+
deltaX |= 0;
|
|
2076
|
+
deltaY |= 0;
|
|
2077
|
+
if (srcX > src.width || srcY > src.height || srcX + width > src.width || srcY + height > src.height) {
|
|
2078
|
+
throw new Error("bitblt reading outside image");
|
|
2079
|
+
}
|
|
2080
|
+
if (deltaX > dst.width || deltaY > dst.height || deltaX + width > dst.width || deltaY + height > dst.height) {
|
|
2081
|
+
throw new Error("bitblt writing outside image");
|
|
2082
|
+
}
|
|
2083
|
+
for (let y = 0; y < height; y++) {
|
|
2084
|
+
src.data.copy(
|
|
2085
|
+
dst.data,
|
|
2086
|
+
(deltaY + y) * dst.width + deltaX << 2,
|
|
2087
|
+
(srcY + y) * src.width + srcX << 2,
|
|
2088
|
+
(srcY + y) * src.width + srcX + width << 2
|
|
2089
|
+
);
|
|
2090
|
+
}
|
|
2091
|
+
};
|
|
2092
|
+
PNG2.prototype.bitblt = function(dst, srcX, srcY, width, height, deltaX, deltaY) {
|
|
2093
|
+
PNG2.bitblt(this, dst, srcX, srcY, width, height, deltaX, deltaY);
|
|
2094
|
+
return this;
|
|
2095
|
+
};
|
|
2096
|
+
PNG2.adjustGamma = function(src) {
|
|
2097
|
+
if (src.gamma) {
|
|
2098
|
+
for (let y = 0; y < src.height; y++) {
|
|
2099
|
+
for (let x = 0; x < src.width; x++) {
|
|
2100
|
+
let idx = src.width * y + x << 2;
|
|
2101
|
+
for (let i = 0; i < 3; i++) {
|
|
2102
|
+
let sample = src.data[idx + i] / 255;
|
|
2103
|
+
sample = Math.pow(sample, 1 / 2.2 / src.gamma);
|
|
2104
|
+
src.data[idx + i] = Math.round(sample * 255);
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
src.gamma = 0;
|
|
2109
|
+
}
|
|
2110
|
+
};
|
|
2111
|
+
PNG2.prototype.adjustGamma = function() {
|
|
2112
|
+
PNG2.adjustGamma(this);
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
});
|
|
2116
|
+
|
|
2117
|
+
// src/demo/manifest-checks.ts
|
|
2118
|
+
var END_TOLERANCE_SECONDS = 0.05;
|
|
2119
|
+
function chapterLabel(index, title) {
|
|
2120
|
+
return `chapter ${index + 1} ("${title}")`;
|
|
2121
|
+
}
|
|
2122
|
+
function checkManifest(params) {
|
|
2123
|
+
const { manifest, videoSeconds, recordedIds } = params;
|
|
2124
|
+
const seen = /* @__PURE__ */ new Map();
|
|
2125
|
+
let previousEnd = 0;
|
|
2126
|
+
for (const [index, chapter] of manifest.chapters.entries()) {
|
|
2127
|
+
const label = chapterLabel(index, chapter.title);
|
|
2128
|
+
if (chapter.end > videoSeconds + END_TOLERANCE_SECONDS) {
|
|
2129
|
+
return `${label} ends at ${chapter.end} s, but the video is ${videoSeconds.toFixed(1)} s long`;
|
|
2130
|
+
}
|
|
2131
|
+
if (chapter.start < previousEnd - END_TOLERANCE_SECONDS) {
|
|
2132
|
+
return `${label} starts at ${chapter.start} s, before the previous chapter ends at ${previousEnd} s \u2014 chapters must be in order and must not overlap`;
|
|
2133
|
+
}
|
|
2134
|
+
previousEnd = chapter.end;
|
|
2135
|
+
if (chapter.criterionId === null) {
|
|
2136
|
+
if (index !== 0) {
|
|
2137
|
+
return `${label} names no criterion \u2014 only the opening chapter may omit one`;
|
|
2138
|
+
}
|
|
2139
|
+
continue;
|
|
2140
|
+
}
|
|
2141
|
+
const first = seen.get(chapter.criterionId);
|
|
2142
|
+
if (first !== void 0) {
|
|
2143
|
+
return `${label} names criterion ${chapter.criterionId}, which chapter ${first + 1} already shows \u2014 one chapter proves each criterion`;
|
|
2144
|
+
}
|
|
2145
|
+
seen.set(chapter.criterionId, index);
|
|
2146
|
+
if (recordedIds !== void 0 && !recordedIds.has(chapter.criterionId)) {
|
|
2147
|
+
return `${label} names criterion ${chapter.criterionId}, which is not on the item's recorded criteria \u2014 record it with \`emit criteria\`, or fix the id`;
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
return void 0;
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
// src/demo/stills.ts
|
|
2154
|
+
var import_pngjs = __toESM(require_png(), 1);
|
|
2155
|
+
var StillError = class extends Error {
|
|
2156
|
+
};
|
|
2157
|
+
function readStill(dir, file, sha256) {
|
|
2158
|
+
const path = resolve(dir, file);
|
|
2159
|
+
let bytes;
|
|
2160
|
+
try {
|
|
2161
|
+
bytes = readFileSync(path);
|
|
2162
|
+
} catch (err) {
|
|
2163
|
+
throw new StillError(`cannot read the still "${file}" (${path}): ${err instanceof Error ? err.message : String(err)}`);
|
|
2164
|
+
}
|
|
2165
|
+
if (createHash("sha256").update(bytes).digest("hex") !== sha256) {
|
|
2166
|
+
throw new StillError(`the still "${file}" does not match the SHA-256 in its manifest \u2014 the file changed after the run`);
|
|
2167
|
+
}
|
|
2168
|
+
return bytes;
|
|
2169
|
+
}
|
|
2170
|
+
function decode(bytes) {
|
|
2171
|
+
try {
|
|
2172
|
+
return import_pngjs.PNG.sync.read(bytes);
|
|
2173
|
+
} catch (err) {
|
|
2174
|
+
throw new StillError(`a still is not a valid PNG: ${err instanceof Error ? err.message : String(err)}`);
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
function diffStills(first, second) {
|
|
2178
|
+
const a = decode(first);
|
|
2179
|
+
const b = decode(second);
|
|
2180
|
+
if (a.width !== b.width || a.height !== b.height) {
|
|
2181
|
+
return { equal: false, region: { x: 0, y: 0, width: Math.max(a.width, b.width), height: Math.max(a.height, b.height) } };
|
|
2182
|
+
}
|
|
2183
|
+
let minX = a.width;
|
|
2184
|
+
let minY = a.height;
|
|
2185
|
+
let maxX = -1;
|
|
2186
|
+
let maxY = -1;
|
|
2187
|
+
for (let at = 0; at < a.data.length; at += 4) {
|
|
2188
|
+
if (a.data.compare(b.data, at, at + 4, at, at + 4) === 0) continue;
|
|
2189
|
+
const pixel = at / 4;
|
|
2190
|
+
const x = pixel % a.width;
|
|
2191
|
+
const y = Math.floor(pixel / a.width);
|
|
2192
|
+
minX = Math.min(minX, x);
|
|
2193
|
+
maxX = Math.max(maxX, x);
|
|
2194
|
+
minY = Math.min(minY, y);
|
|
2195
|
+
maxY = Math.max(maxY, y);
|
|
2196
|
+
}
|
|
2197
|
+
if (maxX < 0) return { equal: true };
|
|
2198
|
+
return { equal: false, region: { x: minX, y: minY, width: maxX - minX + 1, height: maxY - minY + 1 } };
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
// src/demo/compare.ts
|
|
2202
|
+
function stillsOf(chapter) {
|
|
2203
|
+
return "stills" in chapter ? chapter.stills : [];
|
|
2204
|
+
}
|
|
2205
|
+
function pairManifests(first, second) {
|
|
2206
|
+
if (first.chapters.length !== second.chapters.length) {
|
|
2207
|
+
return `the runs differ in chapters: the first run has ${first.chapters.length} chapters and the second has ${second.chapters.length}`;
|
|
2208
|
+
}
|
|
2209
|
+
for (const [index, a] of first.chapters.entries()) {
|
|
2210
|
+
const b = second.chapters[index];
|
|
2211
|
+
const label = chapterLabel(index, a.title);
|
|
2212
|
+
if (a.criterionId !== b.criterionId) {
|
|
2213
|
+
return `${label} shows ${a.criterionId ?? "no criterion"} in the first run and ${b.criterionId ?? "no criterion"} in the second`;
|
|
2214
|
+
}
|
|
2215
|
+
if (a.title !== b.title) {
|
|
2216
|
+
return `chapter ${index + 1} differs in title: "${a.title}" in the first run and "${b.title}" in the second`;
|
|
2217
|
+
}
|
|
2218
|
+
if (a.checks.length !== b.checks.length) {
|
|
2219
|
+
return `${label} has ${a.checks.length} checks in the first run and ${b.checks.length} in the second`;
|
|
2220
|
+
}
|
|
2221
|
+
for (const [at, check] of a.checks.entries()) {
|
|
2222
|
+
const other = b.checks[at];
|
|
2223
|
+
if (check.text !== other.text) {
|
|
2224
|
+
return `${label}, check ${at + 1} differs: "${check.text}" in the first run and "${other.text}" in the second`;
|
|
2225
|
+
}
|
|
2226
|
+
if (check.passed !== other.passed) {
|
|
2227
|
+
return `${label}, check ${at + 1} "${check.text}" ${check.passed ? "passed" : "failed"} in the first run and ${other.passed ? "passed" : "failed"} in the second`;
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
const listed = (chapter) => stillsOf(chapter).map((still) => still.check + 1).join(", ") || "none";
|
|
2231
|
+
if (listed(a) !== listed(b)) {
|
|
2232
|
+
return `${label} has stills after checks ${listed(a)} in the first run and ${listed(b)} in the second`;
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
return void 0;
|
|
2236
|
+
}
|
|
2237
|
+
function compareChapters(params) {
|
|
2238
|
+
const { first, second } = params;
|
|
2239
|
+
return first.manifest.chapters.map((chapter, index) => {
|
|
2240
|
+
const mine = stillsOf(chapter);
|
|
2241
|
+
const stills = mine.map(({ check, sha256 }) => ({ check, sha256 }));
|
|
2242
|
+
if (second === void 0 || mine.length === 0) return { comparable: "not checked", stills };
|
|
2243
|
+
const theirs = stillsOf(second.manifest.chapters[index]);
|
|
2244
|
+
let difference;
|
|
2245
|
+
for (const [at, still] of mine.entries()) {
|
|
2246
|
+
const other = theirs[at];
|
|
2247
|
+
const bytes = readStill(first.dir, still.file, still.sha256);
|
|
2248
|
+
const otherBytes = readStill(second.dir, other.file, other.sha256);
|
|
2249
|
+
const diff = diffStills(bytes, otherBytes);
|
|
2250
|
+
if (!diff.equal && difference === void 0) difference = { check: still.check, region: diff.region };
|
|
2251
|
+
}
|
|
2252
|
+
if (difference !== void 0) return { comparable: "no", stills, difference };
|
|
2253
|
+
const followed = new Set(mine.map((still) => still.check));
|
|
2254
|
+
if (chapter.checks.some((check, at) => check.passed && !followed.has(at))) return { comparable: "not checked", stills };
|
|
2255
|
+
return { comparable: "yes", stills };
|
|
2256
|
+
});
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
// src/demo/criteria.ts
|
|
2260
|
+
async function readRecordedCriteriaIds(params) {
|
|
2261
|
+
const endpoint = new URL(`/v1/work-items/${params.item}`, params.url).toString();
|
|
2262
|
+
const response = await params.fetchImpl(endpoint, {
|
|
2263
|
+
method: "GET",
|
|
2264
|
+
headers: { authorization: `Bearer ${params.apiKey}`, "x-outerlayer-app-id": params.appId }
|
|
2265
|
+
});
|
|
2266
|
+
if (!response.ok) throw new Error(`reading item ${params.item} failed (${response.status})`);
|
|
2267
|
+
const body = await response.json();
|
|
2268
|
+
const items = body.data?.criteria?.items;
|
|
2269
|
+
if (!Array.isArray(items)) return void 0;
|
|
2270
|
+
return new Set(items.flatMap((entry) => typeof entry.id === "string" ? [entry.id] : []));
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
// src/demo/fit.ts
|
|
2274
|
+
var MAX_TEMPO = 1.3;
|
|
2275
|
+
var SPEECH_LEAD_IN = 0.2;
|
|
2276
|
+
var SPEECH_TAIL = 0.2;
|
|
2277
|
+
var EPSILON = 1e-6;
|
|
2278
|
+
function fitSpeech(speechSeconds, chapterSeconds) {
|
|
2279
|
+
const budget = chapterSeconds - SPEECH_LEAD_IN - SPEECH_TAIL;
|
|
2280
|
+
if (budget > 0 && speechSeconds <= budget + EPSILON) return { ok: true, tempo: 1 };
|
|
2281
|
+
if (budget > 0 && speechSeconds / budget <= MAX_TEMPO + EPSILON) {
|
|
2282
|
+
return { ok: true, tempo: Math.min(MAX_TEMPO, speechSeconds / budget) };
|
|
2283
|
+
}
|
|
2284
|
+
return { ok: false, overrunSeconds: speechSeconds / MAX_TEMPO - budget };
|
|
2285
|
+
}
|
|
2286
|
+
function findProgram(name, env, platform = process.platform) {
|
|
2287
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(platform === "win32" ? ";" : delimiter).filter((dir) => dir !== "");
|
|
2288
|
+
const extensions = platform === "win32" ? ["", ...(env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter((ext) => ext !== "")] : [""];
|
|
2289
|
+
for (const dir of dirs) {
|
|
2290
|
+
for (const extension of extensions) {
|
|
2291
|
+
const candidate = join(dir, name + extension);
|
|
2292
|
+
try {
|
|
2293
|
+
if (!statSync(candidate).isFile()) continue;
|
|
2294
|
+
if (platform !== "win32") accessSync(candidate, constants.X_OK);
|
|
2295
|
+
return candidate;
|
|
2296
|
+
} catch {
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
return void 0;
|
|
2301
|
+
}
|
|
2302
|
+
var INSTALL_HINT = {
|
|
2303
|
+
ffmpeg: "ffmpeg (apt install ffmpeg, brew install ffmpeg, or winget install Gyan.FFmpeg)",
|
|
2304
|
+
ffprobe: "ffmpeg, which includes ffprobe (apt install ffmpeg, brew install ffmpeg, or winget install Gyan.FFmpeg)",
|
|
2305
|
+
"espeak-ng": "espeak-ng (apt install espeak-ng, brew install espeak-ng, or the eSpeak NG installer on Windows)"
|
|
2306
|
+
};
|
|
2307
|
+
function missingProgramMessage(name, narrated) {
|
|
2308
|
+
const hint = INSTALL_HINT[name] ?? name;
|
|
2309
|
+
const base = `install ${hint}, then run the command again`;
|
|
2310
|
+
return name === "espeak-ng" && narrated ? `espeak-ng is not on PATH \u2014 ${base}, or pass --no-narration to store the clips without speech` : `${name} is not on PATH \u2014 ${base}`;
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
// src/demo/media.ts
|
|
2314
|
+
function clipArgs(plan) {
|
|
2315
|
+
const duration = plan.end - plan.start;
|
|
2316
|
+
const audio = plan.audio;
|
|
2317
|
+
const pad = `apad=whole_dur=${duration.toFixed(3)}`;
|
|
2318
|
+
const audioInput = audio === void 0 ? [] : audio === "silence" ? ["-f", "lavfi", "-t", duration.toFixed(3), "-i", "anullsrc=channel_layout=mono:sample_rate=48000"] : ["-i", audio.wavPath];
|
|
2319
|
+
const audioFilter = audio === void 0 ? void 0 : audio === "silence" ? `[1:a]${pad}[speech]` : `[1:a]${audio.tempo === 1 ? "" : `atempo=${audio.tempo.toFixed(4)},`}adelay=${Math.round(audio.delaySeconds * 1e3)}:all=1,aresample=48000,${pad}[speech]`;
|
|
2320
|
+
const speech = audioFilter === void 0 ? [] : ["-filter_complex", audioFilter, "-map", "[speech]", "-c:a", "libopus", "-b:a", "48k"];
|
|
2321
|
+
return [
|
|
2322
|
+
"-y",
|
|
2323
|
+
"-v",
|
|
2324
|
+
"error",
|
|
2325
|
+
// The cut sits before the input and the clip is re-encoded, so it starts on the exact frame, not the nearest key frame.
|
|
2326
|
+
"-ss",
|
|
2327
|
+
plan.start.toFixed(3),
|
|
2328
|
+
"-t",
|
|
2329
|
+
duration.toFixed(3),
|
|
2330
|
+
"-i",
|
|
2331
|
+
plan.video,
|
|
2332
|
+
...audioInput,
|
|
2333
|
+
"-map",
|
|
2334
|
+
"0:v:0",
|
|
2335
|
+
...audio === void 0 ? ["-an"] : speech,
|
|
2336
|
+
"-c:v",
|
|
2337
|
+
"libvpx-vp9",
|
|
2338
|
+
"-b:v",
|
|
2339
|
+
"0",
|
|
2340
|
+
"-crf",
|
|
2341
|
+
"38",
|
|
2342
|
+
"-deadline",
|
|
2343
|
+
"realtime",
|
|
2344
|
+
"-cpu-used",
|
|
2345
|
+
"6",
|
|
2346
|
+
"-row-mt",
|
|
2347
|
+
"1",
|
|
2348
|
+
"-pix_fmt",
|
|
2349
|
+
"yuv420p",
|
|
2350
|
+
// Without these, the WebM muxer writes a random segment id, and the same cut gives different bytes on every run.
|
|
2351
|
+
"-fflags",
|
|
2352
|
+
"+bitexact",
|
|
2353
|
+
"-flags:v",
|
|
2354
|
+
"+bitexact",
|
|
2355
|
+
"-flags:a",
|
|
2356
|
+
"+bitexact",
|
|
2357
|
+
"-t",
|
|
2358
|
+
duration.toFixed(3),
|
|
2359
|
+
plan.output
|
|
2360
|
+
];
|
|
2361
|
+
}
|
|
2362
|
+
function durationFromProbe(formatDuration, lastPacketLines) {
|
|
2363
|
+
const declared = Number(formatDuration);
|
|
2364
|
+
if (Number.isFinite(declared) && declared > 0) return declared;
|
|
2365
|
+
const packets = lastPacketLines.split("\n").filter((line) => line.trim() !== "").map((line) => line.split(",").map(Number)).filter((fields) => Number.isFinite(fields[0]));
|
|
2366
|
+
const last = packets.at(-1);
|
|
2367
|
+
if (last === void 0) return void 0;
|
|
2368
|
+
return last[0] + (Number.isFinite(last[1]) ? last[1] : 0);
|
|
2369
|
+
}
|
|
2370
|
+
var TOOL_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
2371
|
+
function createMediaTools(env, exec = execFileSync) {
|
|
2372
|
+
const ffmpeg = findProgram("ffmpeg", env) ?? "ffmpeg";
|
|
2373
|
+
const ffprobe = findProgram("ffprobe", env) ?? "ffprobe";
|
|
2374
|
+
const run = (program, args) => exec(program, args, {
|
|
2375
|
+
encoding: "utf8",
|
|
2376
|
+
env,
|
|
2377
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
2378
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2379
|
+
timeout: TOOL_TIMEOUT_MS
|
|
2380
|
+
});
|
|
2381
|
+
return {
|
|
2382
|
+
probe(video) {
|
|
2383
|
+
const format = run(ffprobe, ["-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", video]);
|
|
2384
|
+
const declared = durationFromProbe(format, "");
|
|
2385
|
+
if (declared !== void 0) return declared;
|
|
2386
|
+
const lines = run(ffprobe, ["-v", "error", "-select_streams", "v:0", "-show_entries", "packet=pts_time,duration_time", "-of", "csv=p=0", video]);
|
|
2387
|
+
const duration = durationFromProbe("", lines);
|
|
2388
|
+
if (duration === void 0) throw new Error(`cannot read the length of ${video}`);
|
|
2389
|
+
return duration;
|
|
2390
|
+
},
|
|
2391
|
+
renderClip(plan) {
|
|
2392
|
+
run(ffmpeg, clipArgs(plan));
|
|
2393
|
+
}
|
|
2394
|
+
};
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2397
|
+
// src/demo/record.ts
|
|
2398
|
+
function chapterPassed(checks) {
|
|
2399
|
+
return checks.length > 0 && checks.every((check) => check.passed);
|
|
2400
|
+
}
|
|
2401
|
+
function buildDemoRecord(manifest, clips, comparisons) {
|
|
2402
|
+
if (clips.length !== manifest.chapters.length) {
|
|
2403
|
+
throw new Error(`the demo has ${manifest.chapters.length} chapters but ${clips.length} clips`);
|
|
2404
|
+
}
|
|
2405
|
+
if (comparisons.length !== manifest.chapters.length) {
|
|
2406
|
+
throw new Error(`the demo has ${manifest.chapters.length} chapters but ${comparisons.length} comparisons`);
|
|
2407
|
+
}
|
|
2408
|
+
return {
|
|
2409
|
+
schemaVersion: 2,
|
|
2410
|
+
chapters: manifest.chapters.map((chapter, index) => {
|
|
2411
|
+
const comparison = comparisons[index];
|
|
2412
|
+
return {
|
|
2413
|
+
criterionId: chapter.criterionId,
|
|
2414
|
+
start: chapter.start,
|
|
2415
|
+
end: chapter.end,
|
|
2416
|
+
title: chapter.title,
|
|
2417
|
+
narration: chapter.narration,
|
|
2418
|
+
checks: chapter.checks,
|
|
2419
|
+
clipArtifactId: clips[index].clipArtifactId,
|
|
2420
|
+
bound: clips[index].bound,
|
|
2421
|
+
comparable: comparison.comparable,
|
|
2422
|
+
stills: comparison.stills,
|
|
2423
|
+
...comparison.difference === void 0 ? {} : { difference: comparison.difference }
|
|
2424
|
+
};
|
|
2425
|
+
})
|
|
2426
|
+
};
|
|
2427
|
+
}
|
|
2428
|
+
var PUNCTUATION = ';:,.!?\xA1\xBF\u2014\u2026"\xAB\xBB\u201C\u201D(){}[]';
|
|
2429
|
+
var PUNCTUATION_RUN = new RegExp(`(\\s*[${PUNCTUATION.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}]+\\s*)+`, "g");
|
|
2430
|
+
function splitYear(text) {
|
|
2431
|
+
if (text.includes(".")) return text;
|
|
2432
|
+
if (text.includes(":")) {
|
|
2433
|
+
const [hours, minutes] = text.split(":").map(Number);
|
|
2434
|
+
return minutes === 0 ? `${hours} o'clock` : minutes < 10 ? `${hours} oh ${minutes}` : `${hours} ${minutes}`;
|
|
2435
|
+
}
|
|
2436
|
+
const year = parseInt(text.slice(0, 4), 10);
|
|
2437
|
+
if (year < 1100 || year % 1e3 < 10) return text;
|
|
2438
|
+
const high = text.slice(0, 2);
|
|
2439
|
+
const low = parseInt(text.slice(2, 4), 10);
|
|
2440
|
+
const plural = text.endsWith("s") ? "s" : "";
|
|
2441
|
+
if (low === 0) return `${high} hundred${plural}`;
|
|
2442
|
+
if (low < 10) return `${high} oh ${low}${plural}`;
|
|
2443
|
+
return `${high} ${low}${plural}`;
|
|
2444
|
+
}
|
|
2445
|
+
function spellMoney(text) {
|
|
2446
|
+
const unit = text[0] === "$" ? "dollar" : "pound";
|
|
2447
|
+
if (Number.isNaN(Number(text.slice(1)))) return `${text.slice(1)} ${unit}s`;
|
|
2448
|
+
if (!text.includes(".")) return `${text.slice(1)} ${unit}${text.slice(1) === "1" ? "" : "s"}`;
|
|
2449
|
+
const [whole, fraction] = text.slice(1).split(".");
|
|
2450
|
+
const cents = parseInt(fraction.padEnd(2, "0"), 10);
|
|
2451
|
+
const small = text[0] === "$" ? cents === 1 ? "cent" : "cents" : cents === 1 ? "penny" : "pence";
|
|
2452
|
+
return `${whole} ${unit}${whole === "1" ? "" : "s"} and ${cents} ${small}`;
|
|
2453
|
+
}
|
|
2454
|
+
function spellDecimal(text) {
|
|
2455
|
+
const [whole, fraction] = text.split(".");
|
|
2456
|
+
return `${whole} point ${fraction.split("").join(" ")}`;
|
|
2457
|
+
}
|
|
2458
|
+
function prepareText(text) {
|
|
2459
|
+
return text.replace(/[‘’]/g, "'").replace(/«/g, "\u201C").replace(/»/g, "\u201D").replace(/[“”]/g, '"').replace(/\(/g, "\xAB").replace(/\)/g, "\xBB").replace(/、/g, ", ").replace(/。/g, ". ").replace(/!/g, "! ").replace(/,/g, ", ").replace(/:/g, ": ").replace(/;/g, "; ").replace(/?/g, "? ").replace(/[^\S \n]/g, " ").replace(/ {2,}/g, " ").replace(/(?<=\n) +(?=\n)/g, "").replace(/\bD[Rr]\.(?= [A-Z])/g, "Doctor").replace(/\b(?:Mr\.|MR\.(?= [A-Z]))/g, "Mister").replace(/\b(?:Ms\.|MS\.(?= [A-Z]))/g, "Miss").replace(/\b(?:Mrs\.|MRS\.(?= [A-Z]))/g, "Mrs").replace(/\betc\.(?! [A-Z])/gi, "etc").replace(/\b(y)eah?\b/gi, "$1e'a").replace(/\d*\.\d+|\b\d{4}s?\b|(?<!:)\b(?:[1-9]|1[0-2]):[0-5]\d\b(?!:)/g, splitYear).replace(/(?<=\d),(?=\d)/g, "").replace(/[$£]\d+(?:\.\d+)?(?: hundred| thousand| (?:[bm]|tr)illion)*\b|[$£]\d+\.\d\d?\b/gi, spellMoney).replace(/\d*\.\d+/g, spellDecimal).replace(/(?<=\d)-(?=\d)/g, " to ").replace(/(?<=\d)S/g, " S").replace(/(?<=[BCDFGHJ-NP-TV-Z])'?s\b/g, "'S").replace(/(?<=X')S\b/g, "s").replace(/(?:[A-Za-z]\.){2,} [a-z]/g, (match) => match.replace(/\./g, "-")).replace(/(?<=[A-Z])\.(?=[A-Z])/gi, "-").trim();
|
|
2460
|
+
}
|
|
2461
|
+
function splitOnPunctuation(text) {
|
|
2462
|
+
const parts = [];
|
|
2463
|
+
let at = 0;
|
|
2464
|
+
for (const match of text.matchAll(PUNCTUATION_RUN)) {
|
|
2465
|
+
const index = match.index ?? 0;
|
|
2466
|
+
if (at < index) parts.push({ punctuation: false, text: text.slice(at, index) });
|
|
2467
|
+
parts.push({ punctuation: true, text: match[0] });
|
|
2468
|
+
at = index + match[0].length;
|
|
2469
|
+
}
|
|
2470
|
+
if (at < text.length) parts.push({ punctuation: false, text: text.slice(at) });
|
|
2471
|
+
return parts;
|
|
2472
|
+
}
|
|
2473
|
+
function normalizePhonemes(ipa) {
|
|
2474
|
+
return ipa.replace(/kəkˈoːɹoʊ/g, "k\u02C8o\u028Ak\u0259\u0279o\u028A").replace(/kəkˈɔːɹəʊ/g, "k\u02C8\u0259\u028Ak\u0259\u0279\u0259\u028A").replace(/ʲ/g, "j").replace(/r/g, "\u0279").replace(/x/g, "k").replace(/ɬ/g, "l").replace(/(?<=[a-zɹː])(?=hˈʌndɹɪd)/g, " ").replace(/ z(?=[;:,.!?¡¿—…"«»“” ]|$)/g, "z").replace(/(?<=nˈaɪn)ti(?!ː)/g, "di").trim();
|
|
2475
|
+
}
|
|
2476
|
+
var ESPEAK_TIMEOUT_MS = 60 * 1e3;
|
|
2477
|
+
function espeakRunner(espeakPath, run = execFileSync) {
|
|
2478
|
+
return (words) => run(espeakPath, ["-q", "--ipa", "-v", "en-us"], { input: words, encoding: "utf8", maxBuffer: 16 * 1024 * 1024, timeout: ESPEAK_TIMEOUT_MS }).split("\n").map((line) => line.trim()).filter((line) => line !== "").join(" ");
|
|
2479
|
+
}
|
|
2480
|
+
function phonemize(text, ipa) {
|
|
2481
|
+
const parts = splitOnPunctuation(prepareText(text));
|
|
2482
|
+
return normalizePhonemes(parts.map((part) => part.punctuation ? part.text : ipa(part.text)).join(""));
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
// src/demo/voice/kokoro.ts
|
|
2486
|
+
var SAMPLE_RATE = 24e3;
|
|
2487
|
+
var VOICE_ROWS = 510;
|
|
2488
|
+
var STYLE_SIZE = 256;
|
|
2489
|
+
var SENTENCE_GAP = 0.18;
|
|
2490
|
+
function tokenize(phonemes, vocab) {
|
|
2491
|
+
const ids = [];
|
|
2492
|
+
for (const symbol of phonemes) {
|
|
2493
|
+
const id = vocab[symbol];
|
|
2494
|
+
if (id !== void 0) ids.push(id);
|
|
2495
|
+
}
|
|
2496
|
+
return [0, ...ids, 0];
|
|
2497
|
+
}
|
|
2498
|
+
function styleRow(voice, tokenCount) {
|
|
2499
|
+
const row = Math.min(Math.max(tokenCount - 2, 0), VOICE_ROWS - 1);
|
|
2500
|
+
return voice.slice(row * STYLE_SIZE, (row + 1) * STYLE_SIZE);
|
|
2501
|
+
}
|
|
2502
|
+
function splitSentences(text) {
|
|
2503
|
+
return text.split(/(?<=[.!?])\s+/).map((sentence) => sentence.trim()).filter((sentence) => sentence !== "");
|
|
2504
|
+
}
|
|
2505
|
+
function encodeWav(samples, sampleRate = SAMPLE_RATE) {
|
|
2506
|
+
const data = Buffer.alloc(samples.length * 2);
|
|
2507
|
+
samples.forEach((sample, index) => data.writeInt16LE(Math.round(Math.max(-1, Math.min(1, sample)) * 32767), index * 2));
|
|
2508
|
+
const header = Buffer.alloc(44);
|
|
2509
|
+
header.write("RIFF", 0);
|
|
2510
|
+
header.writeUInt32LE(36 + data.length, 4);
|
|
2511
|
+
header.write("WAVEfmt ", 8);
|
|
2512
|
+
header.writeUInt32LE(16, 16);
|
|
2513
|
+
header.writeUInt16LE(1, 20);
|
|
2514
|
+
header.writeUInt16LE(1, 22);
|
|
2515
|
+
header.writeUInt32LE(sampleRate, 24);
|
|
2516
|
+
header.writeUInt32LE(sampleRate * 2, 28);
|
|
2517
|
+
header.writeUInt16LE(2, 32);
|
|
2518
|
+
header.writeUInt16LE(16, 34);
|
|
2519
|
+
header.write("data", 36);
|
|
2520
|
+
header.writeUInt32LE(data.length, 40);
|
|
2521
|
+
return Buffer.concat([header, data]);
|
|
2522
|
+
}
|
|
2523
|
+
function createKokoroSpeaker(opts) {
|
|
2524
|
+
const ipa = opts.ipa ?? espeakRunner(opts.espeakPath);
|
|
2525
|
+
let loaded;
|
|
2526
|
+
let counter = 0;
|
|
2527
|
+
const load = async () => {
|
|
2528
|
+
if (loaded !== void 0) return loaded;
|
|
2529
|
+
const ort = createRequire$1(join(opts.runtimeDir, "package.json"))("onnxruntime-node");
|
|
2530
|
+
const session = await ort.InferenceSession.create(join(opts.packDir, "model.onnx"));
|
|
2531
|
+
const vocab = JSON.parse(readFileSync(join(opts.packDir, "tokenizer.json"), "utf8")).model.vocab;
|
|
2532
|
+
const bytes = readFileSync(join(opts.packDir, "af_heart.bin"));
|
|
2533
|
+
const voice = new Float32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 4);
|
|
2534
|
+
loaded = { ort, session, vocab, voice };
|
|
2535
|
+
return loaded;
|
|
2536
|
+
};
|
|
2537
|
+
return async (text) => {
|
|
2538
|
+
const { ort, session, vocab, voice } = await load();
|
|
2539
|
+
const pieces = [];
|
|
2540
|
+
for (const sentence of splitSentences(text)) {
|
|
2541
|
+
const ids = tokenize(phonemize(sentence, ipa), vocab).slice(0, VOICE_ROWS + 1);
|
|
2542
|
+
if (ids.length <= 2) continue;
|
|
2543
|
+
const out = await session.run({
|
|
2544
|
+
input_ids: new ort.Tensor("int64", BigInt64Array.from(ids.map(BigInt)), [1, ids.length]),
|
|
2545
|
+
style: new ort.Tensor("float32", styleRow(voice, ids.length), [1, STYLE_SIZE]),
|
|
2546
|
+
speed: new ort.Tensor("float32", Float32Array.of(1), [1])
|
|
2547
|
+
});
|
|
2548
|
+
if (pieces.length > 0) pieces.push(new Float32Array(Math.round(SENTENCE_GAP * SAMPLE_RATE)));
|
|
2549
|
+
pieces.push(out.waveform.data);
|
|
2550
|
+
}
|
|
2551
|
+
const length = pieces.reduce((sum, piece) => sum + piece.length, 0);
|
|
2552
|
+
if (length === 0) throw new Error(`the narration line "${text}" has nothing to say`);
|
|
2553
|
+
const samples = new Float32Array(length);
|
|
2554
|
+
let at = 0;
|
|
2555
|
+
for (const piece of pieces) {
|
|
2556
|
+
samples.set(piece, at);
|
|
2557
|
+
at += piece.length;
|
|
2558
|
+
}
|
|
2559
|
+
counter += 1;
|
|
2560
|
+
const wavPath = join(opts.workDir, `speech-${counter}.wav`);
|
|
2561
|
+
writeFileSync(wavPath, encodeWav(samples));
|
|
2562
|
+
return { wavPath, seconds: samples.length / SAMPLE_RATE };
|
|
2563
|
+
};
|
|
2564
|
+
}
|
|
2565
|
+
var BLOCK = 512;
|
|
2566
|
+
function field(block, start, length) {
|
|
2567
|
+
const raw = block.subarray(start, start + length);
|
|
2568
|
+
const end = raw.indexOf(0);
|
|
2569
|
+
return raw.subarray(0, end === -1 ? raw.length : end).toString("utf8");
|
|
2570
|
+
}
|
|
2571
|
+
function octal(block, start, length) {
|
|
2572
|
+
return parseInt(field(block, start, length), 8) || 0;
|
|
2573
|
+
}
|
|
2574
|
+
function paxPath(data) {
|
|
2575
|
+
let at = 0;
|
|
2576
|
+
while (at < data.length) {
|
|
2577
|
+
const space = data.indexOf(32, at);
|
|
2578
|
+
if (space === -1) return void 0;
|
|
2579
|
+
const length = Number(data.subarray(at, space).toString("utf8"));
|
|
2580
|
+
if (!Number.isFinite(length) || length <= 0) return void 0;
|
|
2581
|
+
const record = data.subarray(space + 1, at + length - 1).toString("utf8");
|
|
2582
|
+
if (record.startsWith("path=")) return record.slice("path=".length);
|
|
2583
|
+
at += length;
|
|
2584
|
+
}
|
|
2585
|
+
return void 0;
|
|
2586
|
+
}
|
|
2587
|
+
async function extractNpmTarball(tarball, destDir, keep = () => true) {
|
|
2588
|
+
let pending = Buffer.alloc(0);
|
|
2589
|
+
let header;
|
|
2590
|
+
let longPath;
|
|
2591
|
+
let remaining = 0;
|
|
2592
|
+
let padding = 0;
|
|
2593
|
+
let collected = [];
|
|
2594
|
+
let sink;
|
|
2595
|
+
const finished = [];
|
|
2596
|
+
const closeSink = () => {
|
|
2597
|
+
const done = sink;
|
|
2598
|
+
sink = void 0;
|
|
2599
|
+
if (done !== void 0) {
|
|
2600
|
+
finished.push(new Promise((resolve3, reject) => done.on("close", resolve3).on("error", reject)));
|
|
2601
|
+
done.end();
|
|
2602
|
+
}
|
|
2603
|
+
};
|
|
2604
|
+
const startEntry = (entry) => {
|
|
2605
|
+
header = entry;
|
|
2606
|
+
remaining = entry.size;
|
|
2607
|
+
padding = (BLOCK - entry.size % BLOCK) % BLOCK;
|
|
2608
|
+
collected = [];
|
|
2609
|
+
if (entry.type !== "0" && entry.type !== "\0") return;
|
|
2610
|
+
const relative = entry.path.split("/").slice(1).join("/");
|
|
2611
|
+
if (relative === "" || !keep(relative)) return;
|
|
2612
|
+
const target = normalize(join(destDir, relative));
|
|
2613
|
+
if (!target.startsWith(normalize(destDir) + sep)) throw new Error(`the archive has a path outside its folder: ${entry.path}`);
|
|
2614
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
2615
|
+
sink = createWriteStream(target, { mode: 493 });
|
|
2616
|
+
};
|
|
2617
|
+
const consume = (chunk) => {
|
|
2618
|
+
pending = Buffer.concat([pending, chunk]);
|
|
2619
|
+
for (; ; ) {
|
|
2620
|
+
if (header === void 0) {
|
|
2621
|
+
if (pending.length < BLOCK) return;
|
|
2622
|
+
const block = pending.subarray(0, BLOCK);
|
|
2623
|
+
pending = pending.subarray(BLOCK);
|
|
2624
|
+
const name = field(block, 0, 100);
|
|
2625
|
+
const prefix = field(block, 345, 155);
|
|
2626
|
+
const type = String.fromCharCode(block[156] ?? 48);
|
|
2627
|
+
const path = longPath ?? (prefix === "" ? name : `${prefix}/${name}`);
|
|
2628
|
+
longPath = void 0;
|
|
2629
|
+
startEntry({ path, size: octal(block, 124, 12), type });
|
|
2630
|
+
continue;
|
|
2631
|
+
}
|
|
2632
|
+
if (remaining > 0) {
|
|
2633
|
+
if (pending.length === 0) return;
|
|
2634
|
+
const take = Math.min(remaining, pending.length);
|
|
2635
|
+
const part = pending.subarray(0, take);
|
|
2636
|
+
pending = pending.subarray(take);
|
|
2637
|
+
remaining -= take;
|
|
2638
|
+
if (sink !== void 0) sink.write(part);
|
|
2639
|
+
else if (header.type === "x") collected.push(Buffer.from(part));
|
|
2640
|
+
continue;
|
|
2641
|
+
}
|
|
2642
|
+
if (pending.length < padding) return;
|
|
2643
|
+
pending = pending.subarray(padding);
|
|
2644
|
+
if (header.type === "x") longPath = paxPath(Buffer.concat(collected));
|
|
2645
|
+
closeSink();
|
|
2646
|
+
header = void 0;
|
|
2647
|
+
}
|
|
2648
|
+
};
|
|
2649
|
+
await new Promise((resolve3, reject) => {
|
|
2650
|
+
const input = createReadStream(tarball).on("error", reject);
|
|
2651
|
+
const gunzip = createGunzip();
|
|
2652
|
+
input.pipe(gunzip);
|
|
2653
|
+
gunzip.on("data", (chunk) => {
|
|
2654
|
+
try {
|
|
2655
|
+
consume(chunk);
|
|
2656
|
+
} catch (err) {
|
|
2657
|
+
input.destroy();
|
|
2658
|
+
reject(err);
|
|
2659
|
+
return;
|
|
2660
|
+
}
|
|
2661
|
+
const writing = sink;
|
|
2662
|
+
if (writing !== void 0 && writing.writableNeedDrain) {
|
|
2663
|
+
gunzip.pause();
|
|
2664
|
+
writing.once("drain", () => gunzip.resume());
|
|
2665
|
+
}
|
|
2666
|
+
}).on("end", resolve3).on("error", reject);
|
|
2667
|
+
});
|
|
2668
|
+
closeSink();
|
|
2669
|
+
await Promise.all(finished);
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2672
|
+
// src/demo/voice/pack.ts
|
|
2673
|
+
var VoicePackError = class extends Error {
|
|
2674
|
+
};
|
|
2675
|
+
var DEFAULT_VOICE_PACK_PINS = { version: VOICE_PACK_VERSION, files: VOICE_PACK_FILES };
|
|
2676
|
+
var IMAGE_VOICE_ROOT = "/opt/outerlayer/voice";
|
|
2677
|
+
function voicePackHomeDir(home, version) {
|
|
2678
|
+
return join(home, ".outerlayer", "voice", version);
|
|
2679
|
+
}
|
|
2680
|
+
function mismatch(file, actual, dir) {
|
|
2681
|
+
return new VoicePackError(
|
|
2682
|
+
`the voice pack file ${file.name} does not match its pinned sha256 (expected ${file.sha256}, found ${actual}). Delete ${join(dir, file.name)} to download it again. Nothing was spoken.`
|
|
2683
|
+
);
|
|
2684
|
+
}
|
|
2685
|
+
async function verifyPresent(dir, pins) {
|
|
2686
|
+
const missing = [];
|
|
2687
|
+
for (const file of pins.files) {
|
|
2688
|
+
const path = join(dir, file.name);
|
|
2689
|
+
if (!existsSync(path)) {
|
|
2690
|
+
missing.push(file.name);
|
|
2691
|
+
continue;
|
|
2692
|
+
}
|
|
2693
|
+
const actual = await sha256OfFile(path);
|
|
2694
|
+
if (actual !== file.sha256) throw mismatch(file, actual, dir);
|
|
2695
|
+
}
|
|
2696
|
+
return missing;
|
|
2697
|
+
}
|
|
2698
|
+
async function ensureVoicePack(opts) {
|
|
2699
|
+
const pins = opts.pins ?? DEFAULT_VOICE_PACK_PINS;
|
|
2700
|
+
const env = opts.env ?? process.env;
|
|
2701
|
+
const log = opts.log ?? (() => void 0);
|
|
2702
|
+
const installed = join(opts.installedRoot ?? IMAGE_VOICE_ROOT, pins.version);
|
|
2703
|
+
if (existsSync(installed)) {
|
|
2704
|
+
const missing2 = await verifyPresent(installed, pins);
|
|
2705
|
+
if (missing2.length === 0) return { dir: installed, version: pins.version };
|
|
2706
|
+
}
|
|
2707
|
+
const dir = voicePackHomeDir(opts.home, pins.version);
|
|
2708
|
+
const missing = await verifyPresent(dir, pins);
|
|
2709
|
+
if (missing.length === 0) return { dir, version: pins.version };
|
|
2710
|
+
const mirror = env.OUTERLAYER_VOICE_MIRROR?.replace(/\/+$/, "");
|
|
2711
|
+
log(`Downloading the narration voice pack to ${dir} (first narrated run only; later runs use the copy there)`);
|
|
2712
|
+
mkdirSync(dir, { recursive: true });
|
|
2713
|
+
for (const file of pins.files.filter((entry) => missing.includes(entry.name))) {
|
|
2714
|
+
await downloadPinned({
|
|
2715
|
+
url: mirror !== void 0 ? `${mirror}/${encodeURIComponent(file.name)}` : file.url,
|
|
2716
|
+
dest: join(dir, file.name),
|
|
2717
|
+
sha256: file.sha256,
|
|
2718
|
+
name: file.name,
|
|
2719
|
+
fetchImpl: opts.fetchImpl ?? fetch,
|
|
2720
|
+
progress: log
|
|
2721
|
+
});
|
|
2722
|
+
}
|
|
2723
|
+
return { dir, version: pins.version };
|
|
2724
|
+
}
|
|
2725
|
+
var RUNTIME_PACKAGES = [
|
|
2726
|
+
{ prefix: "onnxruntime-node-", folder: "onnxruntime-node" },
|
|
2727
|
+
{ prefix: "onnxruntime-common-", folder: "onnxruntime-common" }
|
|
2728
|
+
];
|
|
2729
|
+
async function ensureVoiceRuntime(pack, pins, home) {
|
|
2730
|
+
const dir = join(voicePackHomeDir(home, pack.version), "runtime", `${process.platform}-${process.arch}`);
|
|
2731
|
+
const archives = RUNTIME_PACKAGES.map(({ prefix, folder }) => {
|
|
2732
|
+
const file = pins.files.find((entry) => entry.name.startsWith(prefix) && entry.name.endsWith(".tgz"));
|
|
2733
|
+
if (file === void 0) throw new VoicePackError(`the voice pack pins no ${folder} archive`);
|
|
2734
|
+
return { file, folder };
|
|
2735
|
+
});
|
|
2736
|
+
const marker = join(dir, ".complete");
|
|
2737
|
+
const wanted = JSON.stringify(archives.map(({ file }) => [file.name, file.sha256]));
|
|
2738
|
+
if (existsSync(marker) && readFileSync(marker, "utf8") === wanted) return { dir };
|
|
2739
|
+
rmSync(dir, { recursive: true, force: true });
|
|
2740
|
+
mkdirSync(dir, { recursive: true });
|
|
2741
|
+
const nativeDir = `bin/napi-v3/${process.platform}/${process.arch}/`;
|
|
2742
|
+
for (const { file, folder } of archives) {
|
|
2743
|
+
await extractNpmTarball(join(pack.dir, file.name), join(dir, "node_modules", folder), (path) => {
|
|
2744
|
+
if (path.startsWith("bin/")) return path.startsWith(nativeDir);
|
|
2745
|
+
return path === "package.json" || path === "LICENSE" || path.startsWith("dist/");
|
|
2746
|
+
});
|
|
2747
|
+
}
|
|
2748
|
+
if (!existsSync(join(dir, "node_modules", "onnxruntime-node", nativeDir))) {
|
|
2749
|
+
rmSync(dir, { recursive: true, force: true });
|
|
2750
|
+
throw new VoicePackError(`the ONNX Runtime has no build for ${process.platform} ${process.arch}, so narration cannot run here; pass --no-narration`);
|
|
2751
|
+
}
|
|
2752
|
+
writeFileSync(join(dir, "package.json"), '{"private":true}\n');
|
|
2753
|
+
writeFileSync(marker, wanted);
|
|
2754
|
+
return { dir };
|
|
2755
|
+
}
|
|
2756
|
+
|
|
2757
|
+
// src/emit-demo-cmd.ts
|
|
2758
|
+
var EmitDemoError = class extends Error {
|
|
2759
|
+
};
|
|
2760
|
+
var MIB = 1024 * 1024;
|
|
2761
|
+
function parseManifest(path, options) {
|
|
2762
|
+
let text;
|
|
2763
|
+
try {
|
|
2764
|
+
text = readFileSync(path, "utf8");
|
|
2765
|
+
} catch (err) {
|
|
2766
|
+
throw new EmitDemoError(`cannot read the manifest "${path}": ${String(err)}`);
|
|
2767
|
+
}
|
|
2768
|
+
let raw;
|
|
2769
|
+
try {
|
|
2770
|
+
raw = JSON.parse(text);
|
|
2771
|
+
} catch (err) {
|
|
2772
|
+
throw new EmitDemoError(`the manifest "${path}" is not valid JSON: ${String(err)}`);
|
|
2773
|
+
}
|
|
2774
|
+
const parsed = DemoManifestSchema.safeParse(raw);
|
|
2775
|
+
if (!parsed.success) {
|
|
2776
|
+
const issue = parsed.error.issues[0];
|
|
2777
|
+
const where = issue.path.length > 0 ? ` at ${issue.path.join(".")}` : "";
|
|
2778
|
+
throw new EmitDemoError(`the manifest "${path}" is not valid${where}: ${issue.message}`);
|
|
2779
|
+
}
|
|
2780
|
+
const videoPath = resolve(dirname(path), parsed.data.video);
|
|
2781
|
+
if (!options.needVideo) return { manifest: parsed.data, videoPath, text };
|
|
2782
|
+
try {
|
|
2783
|
+
if (!statSync(videoPath).isFile()) throw new Error("not a file");
|
|
2784
|
+
} catch {
|
|
2785
|
+
throw new EmitDemoError(`the manifest names the video "${parsed.data.video}", but ${videoPath} does not exist`);
|
|
2786
|
+
}
|
|
2787
|
+
return { manifest: parsed.data, videoPath, text };
|
|
2788
|
+
}
|
|
2789
|
+
async function runEmitDemo(opts) {
|
|
2790
|
+
const env = opts.env ?? process.env;
|
|
2791
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
2792
|
+
const home = opts.home ?? homeFromEnv(env);
|
|
2793
|
+
const say = (line) => {
|
|
2794
|
+
if (!opts.quiet) process.stderr.write(`${line}
|
|
2795
|
+
`);
|
|
2796
|
+
};
|
|
2797
|
+
const manifestPath = resolve(cwd, opts.manifest);
|
|
2798
|
+
const { manifest, videoPath, text: manifestText } = parseManifest(manifestPath, { needVideo: true });
|
|
2799
|
+
const comparisons = compareRuns({ first: { manifest, dir: dirname(manifestPath) }, firstPath: manifestPath, secondPath: opts.compare === void 0 ? void 0 : resolve(cwd, opts.compare) });
|
|
2800
|
+
const narrated = opts.noNarration !== true && manifest.chapters.some((chapter) => chapter.narration.trim() !== "");
|
|
2801
|
+
for (const program of narrated ? ["ffmpeg", "ffprobe", "espeak-ng"] : ["ffmpeg", "ffprobe"]) {
|
|
2802
|
+
if (findProgram(program, env) === void 0) throw new EmitDemoError(missingProgramMessage(program, narrated));
|
|
2803
|
+
}
|
|
2804
|
+
const fetchImpl = opts.fetchImpl ?? defaultFetch;
|
|
2805
|
+
const media = opts.media ?? createMediaTools(env);
|
|
2806
|
+
const recordedIds = await recordedCriteria({ opts, env, cwd, home, fetchImpl, say });
|
|
2807
|
+
let videoSeconds;
|
|
2808
|
+
try {
|
|
2809
|
+
videoSeconds = media.probe(videoPath);
|
|
2810
|
+
} catch (err) {
|
|
2811
|
+
throw new EmitDemoError(`cannot read the video ${videoPath}: ${String(err)}`);
|
|
2812
|
+
}
|
|
2813
|
+
const problem = checkManifest({ manifest, videoSeconds, recordedIds });
|
|
2814
|
+
if (problem !== void 0) throw new EmitDemoError(problem);
|
|
2815
|
+
const work = mkdtempSync(join(tmpdir(), "outerlayer-demo-"));
|
|
2816
|
+
try {
|
|
2817
|
+
const speaker = narrated ? opts.speaker ?? await realSpeaker({ env, home, work, say, installedRoot: opts.installedVoiceRoot }) : void 0;
|
|
2818
|
+
const plans = [];
|
|
2819
|
+
for (const [index, chapter] of manifest.chapters.entries()) {
|
|
2820
|
+
const plan = {
|
|
2821
|
+
video: videoPath,
|
|
2822
|
+
start: chapter.start,
|
|
2823
|
+
end: chapter.end,
|
|
2824
|
+
output: join(work, `chapter-${String(index + 1).padStart(2, "0")}.webm`)
|
|
2825
|
+
};
|
|
2826
|
+
if (speaker !== void 0 && chapter.narration.trim() !== "") {
|
|
2827
|
+
let speech;
|
|
2828
|
+
try {
|
|
2829
|
+
speech = await speaker(chapter.narration);
|
|
2830
|
+
} catch (err) {
|
|
2831
|
+
throw new EmitDemoError(`${chapterLabel(index, chapter.title)}: the narration could not be spoken: ${err instanceof Error ? err.message : String(err)}`);
|
|
2832
|
+
}
|
|
2833
|
+
const fit = fitSpeech(speech.seconds, chapter.end - chapter.start);
|
|
2834
|
+
if (!fit.ok) {
|
|
2835
|
+
throw new EmitDemoError(
|
|
2836
|
+
`${chapterLabel(index, chapter.title)}: the narration runs ${fit.overrunSeconds.toFixed(1)} s too long even at 1.3 times speed \u2014 shorten the line or lengthen the chapter`
|
|
2837
|
+
);
|
|
2838
|
+
}
|
|
2839
|
+
plan.audio = { wavPath: speech.wavPath, tempo: fit.tempo, delaySeconds: SPEECH_LEAD_IN };
|
|
2840
|
+
} else if (speaker !== void 0) {
|
|
2841
|
+
plan.audio = "silence";
|
|
2842
|
+
}
|
|
2843
|
+
plans.push(plan);
|
|
2844
|
+
}
|
|
2845
|
+
for (const [index, plan] of plans.entries()) {
|
|
2846
|
+
const chapter = manifest.chapters[index];
|
|
2847
|
+
try {
|
|
2848
|
+
media.renderClip(plan);
|
|
2849
|
+
} catch (err) {
|
|
2850
|
+
throw new EmitDemoError(`${chapterLabel(index, chapter.title)}: ffmpeg could not cut the clip: ${String(err)}`);
|
|
2851
|
+
}
|
|
2852
|
+
const bytes = statSync(plan.output).size;
|
|
2853
|
+
if (bytes > ARTIFACT_MAX_BYTES) {
|
|
2854
|
+
throw new EmitDemoError(
|
|
2855
|
+
`${chapterLabel(index, chapter.title)}: the clip is ${(bytes / MIB).toFixed(1)} MiB, over the ${ARTIFACT_MAX_BYTES / MIB} MiB limit \u2014 shorten the chapter or record a smaller window`
|
|
2856
|
+
);
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
const seed = createHash("sha256").update(manifestText).update("\0").update(readFileSync(videoPath)).update(`\0${opts.item ?? ""}\0${opts.pr ?? ""}`).digest("hex");
|
|
2860
|
+
const idFor = (label, file) => `demo-${createHash("sha256").update(seed).update("\0").update(label).update("\0").update(readFileSync(file)).digest("hex").slice(0, 40)}`;
|
|
2861
|
+
const clipIds = plans.map((plan, index) => idFor(`clip-${index + 1}`, plan.output));
|
|
2862
|
+
const plannedRecord = JSON.stringify(
|
|
2863
|
+
buildDemoRecord(
|
|
2864
|
+
manifest,
|
|
2865
|
+
manifest.chapters.map((chapter, index) => ({ clipArtifactId: clipIds[index], bound: chapter.criterionId !== null && chapterPassed(chapter.checks) })),
|
|
2866
|
+
comparisons
|
|
2867
|
+
)
|
|
2868
|
+
);
|
|
2869
|
+
const recordBytes = Buffer.byteLength(plannedRecord);
|
|
2870
|
+
if (recordBytes > ARTIFACT_MAX_BYTES) {
|
|
2871
|
+
throw new EmitDemoError(
|
|
2872
|
+
`the demo record is ${(recordBytes / MIB).toFixed(1)} MiB, over the ${ARTIFACT_MAX_BYTES / MIB} MiB limit \u2014 shorten the checks, titles and narration`
|
|
2873
|
+
);
|
|
2874
|
+
}
|
|
2875
|
+
const emit = opts.emitArtifact ?? runEmitArtifact;
|
|
2876
|
+
const base = {
|
|
2877
|
+
pr: opts.pr,
|
|
2878
|
+
url: opts.url,
|
|
2879
|
+
apiKey: opts.apiKey,
|
|
2880
|
+
appId: opts.appId,
|
|
2881
|
+
quiet: true,
|
|
2882
|
+
json: true,
|
|
2883
|
+
cwd,
|
|
2884
|
+
home,
|
|
2885
|
+
env,
|
|
2886
|
+
fetchImpl: opts.fetchImpl
|
|
2887
|
+
};
|
|
2888
|
+
const clips = [];
|
|
2889
|
+
const failures = [];
|
|
2890
|
+
for (const [index, chapter] of manifest.chapters.entries()) {
|
|
2891
|
+
const bound = chapter.criterionId !== null && chapterPassed(chapter.checks);
|
|
2892
|
+
const failed = chapter.checks.filter((check) => !check.passed);
|
|
2893
|
+
if (failed.length > 0) {
|
|
2894
|
+
failures.push(`${chapterLabel(index, chapter.title)} failed: ${failed.map((check) => `"${check.text}"`).join(", ")}`);
|
|
2895
|
+
} else if (chapter.criterionId !== null && chapter.checks.length === 0) {
|
|
2896
|
+
say(`${C.yellow}!${C.reset} ${chapterLabel(index, chapter.title)} ran no checks, so its clip is not bound to ${chapter.criterionId}`);
|
|
2897
|
+
}
|
|
2898
|
+
const caption = `Demo chapter ${index + 1}: ${chapter.title}${failed.length > 0 ? ` (${failed.length} failed check${failed.length === 1 ? "" : "s"})` : ""}`;
|
|
2899
|
+
const result = await emitOrExplain(emit, {
|
|
2900
|
+
...base,
|
|
2901
|
+
file: plans[index].output,
|
|
2902
|
+
artifactId: clipIds[index],
|
|
2903
|
+
caption: caption.slice(0, ARTIFACT_MAX_CAPTION_LENGTH),
|
|
2904
|
+
...bound ? { criterionId: chapter.criterionId } : {}
|
|
2905
|
+
}, clips.length, manifest.chapters.length);
|
|
2906
|
+
clips.push({ clipArtifactId: artifactIdOf(result), bound, title: chapter.title, comparable: comparisons[index].comparable });
|
|
2907
|
+
}
|
|
2908
|
+
const record = buildDemoRecord(manifest, clips, comparisons);
|
|
2909
|
+
const recordPath = join(work, "demo.json");
|
|
2910
|
+
writeFileSync(recordPath, JSON.stringify(record));
|
|
2911
|
+
const recorded = await emitOrExplain(emit, {
|
|
2912
|
+
...base,
|
|
2913
|
+
file: recordPath,
|
|
2914
|
+
artifactId: idFor("record", recordPath),
|
|
2915
|
+
caption: `Demo record: ${clips.length} chapter${clips.length === 1 ? "" : "s"}, ${clips.filter((clip) => clip.bound).length} bound to a criterion`,
|
|
2916
|
+
mediaType: DEMO_RECORD_MEDIA_TYPE
|
|
2917
|
+
}, clips.length, manifest.chapters.length);
|
|
2918
|
+
const recordArtifactId = artifactIdOf(recorded);
|
|
2919
|
+
const exitCode = failures.length > 0 ? 1 : 0;
|
|
2920
|
+
const differences = manifest.chapters.flatMap((chapter, index) => {
|
|
2921
|
+
const difference = comparisons[index].difference;
|
|
2922
|
+
if (difference === void 0) return [];
|
|
2923
|
+
const { x, y, width, height } = difference.region;
|
|
2924
|
+
return [`${chapterLabel(index, chapter.title)} is not comparable: the still after check ${difference.check + 1} differs in the region x ${x}, y ${y}, ${width} \xD7 ${height} (width \xD7 height)`];
|
|
2925
|
+
});
|
|
2926
|
+
const compared = comparisons.filter((comparison) => comparison.comparable !== "not checked").length;
|
|
2927
|
+
const summary = `${C.green}\u2713${C.reset} demo emitted ${C.dim}\xB7${C.reset} ${clips.length} clip${clips.length === 1 ? "" : "s"}, ${clips.filter((clip) => clip.bound).length} bound to a criterion ${C.dim}\xB7${C.reset} record ${recordArtifactId}`;
|
|
2928
|
+
const output = opts.json ? JSON.stringify({ recordArtifactId, clips, failures, differences, exitCode }) : [
|
|
2929
|
+
...differences.map((line) => `${C.yellow}!${C.reset} ${line}`),
|
|
2930
|
+
summary + (opts.compare === void 0 ? "" : ` ${C.dim}\xB7${C.reset} ${compared} of ${clips.length} chapter${clips.length === 1 ? "" : "s"} compared, ${comparisons.filter((comparison) => comparison.comparable === "yes").length} comparable`)
|
|
2931
|
+
].join("\n");
|
|
2932
|
+
if (!opts.quiet) process.stdout.write(output + "\n");
|
|
2933
|
+
return { exitCode, output, failures, recordArtifactId, clips, differences };
|
|
2934
|
+
} finally {
|
|
2935
|
+
rmSync(work, { recursive: true, force: true });
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
function compareRuns(params) {
|
|
2939
|
+
const { first, firstPath, secondPath } = params;
|
|
2940
|
+
try {
|
|
2941
|
+
if (secondPath === void 0) return compareChapters({ first });
|
|
2942
|
+
if (sameFile(firstPath, secondPath)) {
|
|
2943
|
+
throw new EmitDemoError(`the two runs are the same manifest "${firstPath}" \u2014 one run cannot confirm itself, so pass the manifest of a second run`);
|
|
2944
|
+
}
|
|
2945
|
+
const second = { manifest: parseManifest(secondPath, { needVideo: false }).manifest, dir: dirname(secondPath) };
|
|
2946
|
+
const unpaired = pairManifests(first.manifest, second.manifest);
|
|
2947
|
+
if (unpaired !== void 0) throw new EmitDemoError(`the two runs cannot be compared \u2014 ${unpaired}`);
|
|
2948
|
+
return compareChapters({ first, second });
|
|
2949
|
+
} catch (err) {
|
|
2950
|
+
if (err instanceof StillError) throw new EmitDemoError(err.message);
|
|
2951
|
+
throw err;
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
function sameFile(a, b) {
|
|
2955
|
+
const real = (path) => {
|
|
2956
|
+
try {
|
|
2957
|
+
return realpathSync(path);
|
|
2958
|
+
} catch {
|
|
2959
|
+
return path;
|
|
2960
|
+
}
|
|
2961
|
+
};
|
|
2962
|
+
return real(a) === real(b);
|
|
2963
|
+
}
|
|
2964
|
+
function artifactIdOf(result) {
|
|
2965
|
+
const id = result.record?.artifactId ?? result.data?.clientArtifactId;
|
|
2966
|
+
if (typeof id !== "string") throw new EmitDemoError("the artifact was emitted but its id was not returned");
|
|
2967
|
+
return id;
|
|
2968
|
+
}
|
|
2969
|
+
async function emitOrExplain(emit, opts, emitted, total) {
|
|
2970
|
+
try {
|
|
2971
|
+
return await emit(opts);
|
|
2972
|
+
} catch (err) {
|
|
2973
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2974
|
+
throw new EmitDemoError(
|
|
2975
|
+
emitted > 0 ? `${message} \u2014 ${emitted} of ${total} clips were already emitted; run the command again to emit the whole demo` : message
|
|
2976
|
+
);
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
async function recordedCriteria(params) {
|
|
2980
|
+
const { opts, env, cwd, home, fetchImpl, say } = params;
|
|
2981
|
+
let item = opts.item;
|
|
2982
|
+
if (item === void 0) {
|
|
2983
|
+
const identity = resolveRepoIdentity(cwd);
|
|
2984
|
+
const sessionId = detectActiveSession({
|
|
2985
|
+
env,
|
|
2986
|
+
home,
|
|
2987
|
+
cwds: identity.repoRoot !== void 0 ? [identity.repoRoot, cwd] : [cwd],
|
|
2988
|
+
nowMs: Date.now()
|
|
2989
|
+
});
|
|
2990
|
+
item = sessionId === void 0 ? void 0 : launchedItemForSession(sessionId, home);
|
|
2991
|
+
}
|
|
2992
|
+
if (item === void 0) {
|
|
2993
|
+
say(`${C.yellow}!${C.reset} no work item, so the chapters' criterion ids are not checked against the recorded criteria (pass --item <number>)`);
|
|
2994
|
+
return void 0;
|
|
2995
|
+
}
|
|
2996
|
+
try {
|
|
2997
|
+
const refusal = await autoConnect({ url: opts.url, apiKey: opts.apiKey, appId: opts.appId, cwd, home, env, fetchImpl: opts.fetchImpl });
|
|
2998
|
+
if (refusal !== void 0) throw new Error(refusal);
|
|
2999
|
+
const resolved = resolveGatewayCredentials({ url: opts.url, apiKey: opts.apiKey, appId: opts.appId, cwd, home, env });
|
|
3000
|
+
if (!resolved.ok) throw new Error(resolved.message);
|
|
3001
|
+
const ids = await readRecordedCriteriaIds({ url: resolved.url, apiKey: resolved.credential, appId: resolved.appId, item, fetchImpl });
|
|
3002
|
+
if (ids === void 0) say(`${C.yellow}!${C.reset} item ${item} has no recorded criteria, so the chapters' criterion ids are not checked`);
|
|
3003
|
+
return ids;
|
|
3004
|
+
} catch (err) {
|
|
3005
|
+
say(`${C.yellow}!${C.reset} could not read the recorded criteria of item ${item}, so the chapters' criterion ids are not checked: ${err instanceof Error ? err.message : String(err)}`);
|
|
3006
|
+
return void 0;
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
async function realSpeaker(params) {
|
|
3010
|
+
const { env, home, work, say, installedRoot } = params;
|
|
3011
|
+
try {
|
|
3012
|
+
const pack = await ensureVoicePack({ home, env, log: say, ...installedRoot ? { installedRoot } : {} });
|
|
3013
|
+
const runtime = await ensureVoiceRuntime(pack, DEFAULT_VOICE_PACK_PINS, home);
|
|
3014
|
+
mkdirSync(work, { recursive: true });
|
|
3015
|
+
return createKokoroSpeaker({ packDir: pack.dir, runtimeDir: runtime.dir, espeakPath: findProgram("espeak-ng", env) ?? "espeak-ng", workDir: work });
|
|
3016
|
+
} catch (err) {
|
|
3017
|
+
if (err instanceof VoicePackError || err instanceof VoiceDownloadError) throw new EmitDemoError(err.message);
|
|
3018
|
+
throw err;
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
|
|
3022
|
+
export { EmitDemoError, runEmitDemo };
|