nodejs-audio-visualizer 4.4.12 → 4.5.1

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.
Files changed (48) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +80 -75
  3. package/dist/audio.d.ts +12 -12
  4. package/dist/audio.js +120 -120
  5. package/dist/beats.d.ts +19 -19
  6. package/dist/beats.js +43 -43
  7. package/dist/bpmEncoder.d.ts +14 -14
  8. package/dist/bpmEncoder.js +97 -97
  9. package/dist/config.d.ts +77 -67
  10. package/dist/config.js +369 -356
  11. package/dist/config.js.map +1 -1
  12. package/dist/dsp.d.ts +16 -16
  13. package/dist/dsp.js +76 -76
  14. package/dist/highlight.d.ts +32 -32
  15. package/dist/highlight.js +325 -325
  16. package/dist/image.d.ts +79 -79
  17. package/dist/image.js +458 -458
  18. package/dist/index.d.ts +79 -74
  19. package/dist/index.js +531 -528
  20. package/dist/index.js.map +1 -1
  21. package/dist/subtitleConvert.d.ts +3 -3
  22. package/dist/subtitleConvert.js +61 -61
  23. package/dist/video.d.ts +80 -80
  24. package/dist/video.js +434 -430
  25. package/dist/video.js.map +1 -1
  26. package/dist/waitForEventLoop.d.ts +1 -1
  27. package/dist/waitForEventLoop.js +5 -5
  28. package/package.json +61 -61
  29. package/src/audio.ts +120 -120
  30. package/src/beats.ts +68 -68
  31. package/src/bpmEncoder.ts +97 -97
  32. package/src/config.ts +476 -462
  33. package/src/dsp.ts +95 -95
  34. package/src/highlight.ts +284 -284
  35. package/src/image.ts +602 -602
  36. package/src/index.ts +713 -685
  37. package/src/subtitleConvert.ts +63 -63
  38. package/src/tests/audio.test.ts +96 -96
  39. package/src/tests/config.test.ts +496 -475
  40. package/src/tests/dsp.test.ts +24 -24
  41. package/src/tests/highlight.test.ts +178 -178
  42. package/src/tests/image.test.ts +222 -222
  43. package/src/tests/index.test.ts +112 -112
  44. package/src/tests/subtitleConvert.test.ts +23 -23
  45. package/src/tests/video.test.ts +285 -216
  46. package/src/typings.d.ts +6 -6
  47. package/src/video.ts +550 -547
  48. package/src/waitForEventLoop.ts +2 -2
@@ -1,23 +1,23 @@
1
- import { expect } from 'chai';
2
- import { lrcToSrt, isLikelyLrc, normalizeInlineSubtitlesToSrt } from '../subtitleConvert';
3
-
4
- describe('subtitleConvert', function () {
5
- it('isLikelyLrc', function () {
6
- expect(isLikelyLrc('[00:01.00]Hello')).equal(true);
7
- expect(isLikelyLrc('1\n00:00:01,000 --> 00:00:02,000\nSRT')).equal(false);
8
- });
9
-
10
- it('lrcToSrt converts basic LRC to SRT', function () {
11
- const lrc = '[00:01.00]Line one\n[00:03.50]Line two';
12
- const srt = lrcToSrt(lrc);
13
- expect(srt).to.include('00:00:01,000 --> 00:00:03,500');
14
- expect(srt).to.include('Line one');
15
- expect(srt).to.include('00:00:03,500 -->');
16
- expect(srt).to.include('Line two');
17
- });
18
-
19
- it('normalizeInlineSubtitlesToSrt leaves SRT unchanged', function () {
20
- const s = '1\n00:00:00,000 --> 00:00:01,000\nX';
21
- expect(normalizeInlineSubtitlesToSrt(s)).equal(s);
22
- });
23
- });
1
+ import { expect } from 'chai';
2
+ import { lrcToSrt, isLikelyLrc, normalizeInlineSubtitlesToSrt } from '../subtitleConvert';
3
+
4
+ describe('subtitleConvert', function () {
5
+ it('isLikelyLrc', function () {
6
+ expect(isLikelyLrc('[00:01.00]Hello')).equal(true);
7
+ expect(isLikelyLrc('1\n00:00:01,000 --> 00:00:02,000\nSRT')).equal(false);
8
+ });
9
+
10
+ it('lrcToSrt converts basic LRC to SRT', function () {
11
+ const lrc = '[00:01.00]Line one\n[00:03.50]Line two';
12
+ const srt = lrcToSrt(lrc);
13
+ expect(srt).to.include('00:00:01,000 --> 00:00:03,500');
14
+ expect(srt).to.include('Line one');
15
+ expect(srt).to.include('00:00:03,500 -->');
16
+ expect(srt).to.include('Line two');
17
+ });
18
+
19
+ it('normalizeInlineSubtitlesToSrt leaves SRT unchanged', function () {
20
+ const s = '1\n00:00:00,000 --> 00:00:01,000\nX';
21
+ expect(normalizeInlineSubtitlesToSrt(s)).equal(s);
22
+ });
23
+ });
@@ -1,216 +1,285 @@
1
- import { expect } from 'chai';
2
- import { Writable, Readable, Pipe } from 'stream';
3
- import { EventEmitter } from 'events';
4
- import { spawnFfmpegVideoWriter, waitDrain, readVideoFrame, waitForProcessExit } from '../video';
5
- import { createSandbox, SinonStub } from 'sinon';
6
- import child_process, { ChildProcessWithoutNullStreams } from 'child_process';
7
-
8
- let childProcessStream = {
9
- stdin: new Writable(),
10
- stderr: new Readable(),
11
- };
12
-
13
- const videoSandbox = createSandbox();
14
-
15
- describe('video', function () {
16
-
17
- this.beforeAll(function () {
18
- videoSandbox.stub(child_process, 'spawn').returns(childProcessStream as ChildProcessWithoutNullStreams);
19
- });
20
-
21
- this.afterAll(function () {
22
- videoSandbox.restore();
23
- });
24
-
25
- it('spawnFfmpegVideoWriter returns truthy value', function () {
26
- const childProcessReadableStream = new Readable();
27
- childProcessReadableStream._read = () => { };
28
- const childProcessWritableStream = new Writable();
29
- (<Pipe>childProcessWritableStream.pipe) = () => childProcessWritableStream;
30
-
31
- childProcessStream.stdin = childProcessWritableStream;
32
- childProcessStream.stderr = childProcessReadableStream;
33
-
34
- const result = spawnFfmpegVideoWriter({ audioFilename: 'test', videoFileName: 'test', fps: 11 });
35
- expect(!!result).equal(true);
36
- });
37
-
38
- it('spawnFfmpegVideoWriter uses -ss and -t for single audioSegment', function () {
39
- const childProcessReadableStream = new Readable();
40
- childProcessReadableStream._read = () => { };
41
- const childProcessWritableStream = new Writable();
42
- (<Pipe>childProcessWritableStream.pipe) = () => childProcessWritableStream;
43
-
44
- childProcessStream.stdin = childProcessWritableStream;
45
- childProcessStream.stderr = childProcessReadableStream;
46
-
47
- let spawnArgs: string[] = [];
48
- const spawnStub = child_process.spawn as SinonStub;
49
- spawnStub.callsFake((_cmd: string, args: string[]) => {
50
- spawnArgs = args;
51
- return childProcessStream as ChildProcessWithoutNullStreams;
52
- });
53
-
54
- spawnFfmpegVideoWriter({
55
- audioFilename: 'audio.mp3',
56
- videoFileName: 'out.mp4',
57
- fps: 25,
58
- audioSegment: { seekSeconds: 12, durationSeconds: 15 },
59
- });
60
-
61
- spawnStub.resetBehavior();
62
- spawnStub.returns(childProcessStream as ChildProcessWithoutNullStreams);
63
-
64
- expect(spawnArgs).to.not.include('-filter_complex');
65
- const ssIdx = spawnArgs.indexOf('-ss');
66
- expect(ssIdx).greaterThan(-1);
67
- expect(spawnArgs[ssIdx + 1]).to.equal('12');
68
- const tIdx = spawnArgs.indexOf('-t');
69
- expect(tIdx).greaterThan(-1);
70
- expect(spawnArgs[tIdx + 1]).to.equal('15');
71
- });
72
-
73
- it('spawnFfmpegVideoWriter adds subtitles filter when subtitle file is provided', function () {
74
- const childProcessReadableStream = new Readable();
75
- childProcessReadableStream._read = () => { };
76
- const childProcessWritableStream = new Writable();
77
- (<Pipe>childProcessWritableStream.pipe) = () => childProcessWritableStream;
78
-
79
- childProcessStream.stdin = childProcessWritableStream;
80
- childProcessStream.stderr = childProcessReadableStream;
81
-
82
- let spawnArgs: string[] = [];
83
- const spawnStub = child_process.spawn as SinonStub;
84
- spawnStub.callsFake((_cmd: string, args: string[]) => {
85
- spawnArgs = args;
86
- return childProcessStream as ChildProcessWithoutNullStreams;
87
- });
88
-
89
- spawnFfmpegVideoWriter({
90
- audioFilename: 'audio.mp3',
91
- subtitleFilename: '/tmp/subtitles.srt',
92
- videoFileName: 'out.mp4',
93
- fps: 25,
94
- });
95
-
96
- spawnStub.resetBehavior();
97
- spawnStub.returns(childProcessStream as ChildProcessWithoutNullStreams);
98
-
99
- const vfIdx = spawnArgs.indexOf('-vf');
100
- expect(vfIdx).greaterThan(-1);
101
- expect(spawnArgs[vfIdx + 1]).to.equal(
102
- "subtitles='/tmp/subtitles.srt':force_style='Alignment=2'",
103
- );
104
- });
105
-
106
- it('spawnFfmpegVideoWriter passes subtitle Alignment from subtitleAlignmentAss', function () {
107
- const childProcessReadableStream = new Readable();
108
- childProcessReadableStream._read = () => { };
109
- const childProcessWritableStream = new Writable();
110
- (<Pipe>childProcessWritableStream.pipe) = () => childProcessWritableStream;
111
-
112
- childProcessStream.stdin = childProcessWritableStream;
113
- childProcessStream.stderr = childProcessReadableStream;
114
-
115
- let spawnArgs: string[] = [];
116
- const spawnStub = child_process.spawn as SinonStub;
117
- spawnStub.callsFake((_cmd: string, args: string[]) => {
118
- spawnArgs = args;
119
- return childProcessStream as ChildProcessWithoutNullStreams;
120
- });
121
-
122
- spawnFfmpegVideoWriter({
123
- audioFilename: 'audio.mp3',
124
- subtitleFilename: '/tmp/subtitles.srt',
125
- subtitleAlignmentAss: 8,
126
- videoFileName: 'out.mp4',
127
- fps: 25,
128
- });
129
-
130
- spawnStub.resetBehavior();
131
- spawnStub.returns(childProcessStream as ChildProcessWithoutNullStreams);
132
-
133
- const vfIdx = spawnArgs.indexOf('-vf');
134
- expect(vfIdx).greaterThan(-1);
135
- expect(spawnArgs[vfIdx + 1]).to.equal(
136
- "subtitles='/tmp/subtitles.srt':force_style='Alignment=8'",
137
- );
138
- });
139
-
140
- it('spawnFfmpegVideoWriter configures image pipe input before output file', function () {
141
- const childProcessReadableStream = new Readable();
142
- childProcessReadableStream._read = () => { };
143
- const childProcessWritableStream = new Writable();
144
- (<Pipe>childProcessWritableStream.pipe) = () => childProcessWritableStream;
145
-
146
- childProcessStream.stdin = childProcessWritableStream;
147
- childProcessStream.stderr = childProcessReadableStream;
148
-
149
- let spawnArgs: string[] = [];
150
- const spawnStub = child_process.spawn as SinonStub;
151
- spawnStub.callsFake((_cmd: string, args: string[]) => {
152
- spawnArgs = args;
153
- return childProcessStream as ChildProcessWithoutNullStreams;
154
- });
155
-
156
- spawnFfmpegVideoWriter({
157
- audioFilename: 'audio.mp3',
158
- videoFileName: 'out.mp4',
159
- fps: 23.976,
160
- });
161
-
162
- spawnStub.resetBehavior();
163
- spawnStub.returns(childProcessStream as ChildProcessWithoutNullStreams);
164
-
165
- const audioInputIndex = spawnArgs.indexOf('-i');
166
- expect(audioInputIndex).greaterThan(-1);
167
- expect(spawnArgs[audioInputIndex + 1]).to.equal('audio.mp3');
168
- const imagePipeFlagIndex = spawnArgs.indexOf('image2pipe');
169
- expect(imagePipeFlagIndex).greaterThan(-1);
170
- expect(spawnArgs[imagePipeFlagIndex - 1]).to.equal('-f');
171
- const inputFpsIndex = spawnArgs.indexOf('-framerate');
172
- expect(inputFpsIndex).greaterThan(-1);
173
- expect(spawnArgs[inputFpsIndex + 1]).to.equal('23.976');
174
- const pipeInputIndex = spawnArgs.lastIndexOf('-i');
175
- expect(spawnArgs[pipeInputIndex + 1]).to.equal('-');
176
- expect(spawnArgs.includes('-shortest')).to.equal(true);
177
- expect(spawnArgs[spawnArgs.length - 1]).to.equal('out.mp4');
178
- });
179
-
180
- it('waitDrain resolves true on drain', async function () {
181
- const writable = new Writable();
182
- const waitPromise = waitDrain(writable, undefined, 50);
183
- setTimeout(() => writable.emit('drain'), 5);
184
- const isDrained = await waitPromise;
185
- expect(isDrained).equal(true);
186
- });
187
-
188
- it('waitDrain resolves false on timeout', async function () {
189
- const writable = new Writable();
190
- const isDrained = await waitDrain(writable, undefined, 5);
191
- expect(isDrained).equal(false);
192
- });
193
-
194
- it('readVideoFrame resolves null on timeout', async function () {
195
- const readable = new Readable();
196
- readable._read = () => {};
197
- const frame = await readVideoFrame(readable, 4, 5);
198
- expect(frame).equal(null);
199
- });
200
-
201
- it('waitForProcessExit resolves exit code', async function () {
202
- const processEmitter = new EventEmitter();
203
- const waitPromise = waitForProcessExit(processEmitter, 50);
204
- setTimeout(() => processEmitter.emit('exit', 0), 5);
205
- const { exitCode, reason } = await waitPromise;
206
- expect(exitCode).equal(0);
207
- expect(reason).equal(undefined);
208
- });
209
-
210
- it('waitForProcessExit resolves non-zero on timeout', async function () {
211
- const processEmitter = new EventEmitter();
212
- const { exitCode, reason } = await waitForProcessExit(processEmitter, 5);
213
- expect(exitCode).equal(1);
214
- expect(reason).equal('waitForProcessExit timeout (5ms)');
215
- });
216
- });
1
+ import { expect } from 'chai';
2
+ import { Writable, Readable, Pipe } from 'stream';
3
+ import { EventEmitter } from 'events';
4
+ import { spawnFfmpegVideoWriter, waitDrain, readVideoFrame, waitForProcessExit, spawnConcatVideoFrameReader } from '../video';
5
+ import { createSandbox, SinonStub } from 'sinon';
6
+ import child_process, { ChildProcessWithoutNullStreams } from 'child_process';
7
+
8
+ let childProcessStream = {
9
+ stdin: new Writable(),
10
+ stderr: new Readable(),
11
+ };
12
+
13
+ const videoSandbox = createSandbox();
14
+
15
+ describe('video', function () {
16
+
17
+ this.beforeAll(function () {
18
+ videoSandbox.stub(child_process, 'spawn').returns(childProcessStream as ChildProcessWithoutNullStreams);
19
+ });
20
+
21
+ this.afterAll(function () {
22
+ videoSandbox.restore();
23
+ });
24
+
25
+ it('spawnFfmpegVideoWriter returns truthy value', function () {
26
+ const childProcessReadableStream = new Readable();
27
+ childProcessReadableStream._read = () => { };
28
+ const childProcessWritableStream = new Writable();
29
+ (<Pipe>childProcessWritableStream.pipe) = () => childProcessWritableStream;
30
+
31
+ childProcessStream.stdin = childProcessWritableStream;
32
+ childProcessStream.stderr = childProcessReadableStream;
33
+
34
+ const result = spawnFfmpegVideoWriter({ audioFilename: 'test', videoFileName: 'test', fps: 11 });
35
+ expect(!!result).equal(true);
36
+ });
37
+
38
+ it('spawnFfmpegVideoWriter uses -ss and -t for single audioSegment', function () {
39
+ const childProcessReadableStream = new Readable();
40
+ childProcessReadableStream._read = () => { };
41
+ const childProcessWritableStream = new Writable();
42
+ (<Pipe>childProcessWritableStream.pipe) = () => childProcessWritableStream;
43
+
44
+ childProcessStream.stdin = childProcessWritableStream;
45
+ childProcessStream.stderr = childProcessReadableStream;
46
+
47
+ let spawnArgs: string[] = [];
48
+ const spawnStub = child_process.spawn as SinonStub;
49
+ spawnStub.callsFake((_cmd: string, args: string[]) => {
50
+ spawnArgs = args;
51
+ return childProcessStream as ChildProcessWithoutNullStreams;
52
+ });
53
+
54
+ spawnFfmpegVideoWriter({
55
+ audioFilename: 'audio.mp3',
56
+ videoFileName: 'out.mp4',
57
+ fps: 25,
58
+ audioSegment: { seekSeconds: 12, durationSeconds: 15 },
59
+ });
60
+
61
+ spawnStub.resetBehavior();
62
+ spawnStub.returns(childProcessStream as ChildProcessWithoutNullStreams);
63
+
64
+ expect(spawnArgs).to.not.include('-filter_complex');
65
+ const ssIdx = spawnArgs.indexOf('-ss');
66
+ expect(ssIdx).greaterThan(-1);
67
+ expect(spawnArgs[ssIdx + 1]).to.equal('12');
68
+ const tIdx = spawnArgs.indexOf('-t');
69
+ expect(tIdx).greaterThan(-1);
70
+ expect(spawnArgs[tIdx + 1]).to.equal('15');
71
+ });
72
+
73
+ it('spawnFfmpegVideoWriter adds subtitles filter when subtitle file is provided', function () {
74
+ const childProcessReadableStream = new Readable();
75
+ childProcessReadableStream._read = () => { };
76
+ const childProcessWritableStream = new Writable();
77
+ (<Pipe>childProcessWritableStream.pipe) = () => childProcessWritableStream;
78
+
79
+ childProcessStream.stdin = childProcessWritableStream;
80
+ childProcessStream.stderr = childProcessReadableStream;
81
+
82
+ let spawnArgs: string[] = [];
83
+ const spawnStub = child_process.spawn as SinonStub;
84
+ spawnStub.callsFake((_cmd: string, args: string[]) => {
85
+ spawnArgs = args;
86
+ return childProcessStream as ChildProcessWithoutNullStreams;
87
+ });
88
+
89
+ spawnFfmpegVideoWriter({
90
+ audioFilename: 'audio.mp3',
91
+ subtitleFilename: '/tmp/subtitles.srt',
92
+ videoFileName: 'out.mp4',
93
+ fps: 25,
94
+ });
95
+
96
+ spawnStub.resetBehavior();
97
+ spawnStub.returns(childProcessStream as ChildProcessWithoutNullStreams);
98
+
99
+ const vfIdx = spawnArgs.indexOf('-vf');
100
+ expect(vfIdx).greaterThan(-1);
101
+ expect(spawnArgs[vfIdx + 1]).to.equal(
102
+ "subtitles='/tmp/subtitles.srt':force_style='Alignment=2'",
103
+ );
104
+ });
105
+
106
+ it('spawnFfmpegVideoWriter passes subtitle Alignment from subtitleAlignmentAss', function () {
107
+ const childProcessReadableStream = new Readable();
108
+ childProcessReadableStream._read = () => { };
109
+ const childProcessWritableStream = new Writable();
110
+ (<Pipe>childProcessWritableStream.pipe) = () => childProcessWritableStream;
111
+
112
+ childProcessStream.stdin = childProcessWritableStream;
113
+ childProcessStream.stderr = childProcessReadableStream;
114
+
115
+ let spawnArgs: string[] = [];
116
+ const spawnStub = child_process.spawn as SinonStub;
117
+ spawnStub.callsFake((_cmd: string, args: string[]) => {
118
+ spawnArgs = args;
119
+ return childProcessStream as ChildProcessWithoutNullStreams;
120
+ });
121
+
122
+ spawnFfmpegVideoWriter({
123
+ audioFilename: 'audio.mp3',
124
+ subtitleFilename: '/tmp/subtitles.srt',
125
+ subtitleAlignmentAss: 8,
126
+ videoFileName: 'out.mp4',
127
+ fps: 25,
128
+ });
129
+
130
+ spawnStub.resetBehavior();
131
+ spawnStub.returns(childProcessStream as ChildProcessWithoutNullStreams);
132
+
133
+ const vfIdx = spawnArgs.indexOf('-vf');
134
+ expect(vfIdx).greaterThan(-1);
135
+ expect(spawnArgs[vfIdx + 1]).to.equal(
136
+ "subtitles='/tmp/subtitles.srt':force_style='Alignment=8'",
137
+ );
138
+ });
139
+
140
+ it('spawnFfmpegVideoWriter configures image pipe input before output file', function () {
141
+ const childProcessReadableStream = new Readable();
142
+ childProcessReadableStream._read = () => { };
143
+ const childProcessWritableStream = new Writable();
144
+ (<Pipe>childProcessWritableStream.pipe) = () => childProcessWritableStream;
145
+
146
+ childProcessStream.stdin = childProcessWritableStream;
147
+ childProcessStream.stderr = childProcessReadableStream;
148
+
149
+ let spawnArgs: string[] = [];
150
+ const spawnStub = child_process.spawn as SinonStub;
151
+ spawnStub.callsFake((_cmd: string, args: string[]) => {
152
+ spawnArgs = args;
153
+ return childProcessStream as ChildProcessWithoutNullStreams;
154
+ });
155
+
156
+ spawnFfmpegVideoWriter({
157
+ audioFilename: 'audio.mp3',
158
+ videoFileName: 'out.mp4',
159
+ fps: 23.976,
160
+ });
161
+
162
+ spawnStub.resetBehavior();
163
+ spawnStub.returns(childProcessStream as ChildProcessWithoutNullStreams);
164
+
165
+ const audioInputIndex = spawnArgs.indexOf('-i');
166
+ expect(audioInputIndex).greaterThan(-1);
167
+ expect(spawnArgs[audioInputIndex + 1]).to.equal('audio.mp3');
168
+ const imagePipeFlagIndex = spawnArgs.indexOf('image2pipe');
169
+ expect(imagePipeFlagIndex).greaterThan(-1);
170
+ expect(spawnArgs[imagePipeFlagIndex - 1]).to.equal('-f');
171
+ const inputFpsIndex = spawnArgs.indexOf('-framerate');
172
+ expect(inputFpsIndex).greaterThan(-1);
173
+ expect(spawnArgs[inputFpsIndex + 1]).to.equal('23.976');
174
+ const pipeInputIndex = spawnArgs.lastIndexOf('-i');
175
+ expect(spawnArgs[pipeInputIndex + 1]).to.equal('-');
176
+ expect(spawnArgs.includes('-shortest')).to.equal(true);
177
+ expect(spawnArgs[spawnArgs.length - 1]).to.equal('out.mp4');
178
+ });
179
+
180
+ it('waitDrain resolves true on drain', async function () {
181
+ const writable = new Writable();
182
+ const waitPromise = waitDrain(writable, undefined, 50);
183
+ setTimeout(() => writable.emit('drain'), 5);
184
+ const isDrained = await waitPromise;
185
+ expect(isDrained).equal(true);
186
+ });
187
+
188
+ it('waitDrain resolves false on timeout', async function () {
189
+ const writable = new Writable();
190
+ const isDrained = await waitDrain(writable, undefined, 5);
191
+ expect(isDrained).equal(false);
192
+ });
193
+
194
+ it('readVideoFrame resolves null on timeout', async function () {
195
+ const readable = new Readable();
196
+ readable._read = () => {};
197
+ const frame = await readVideoFrame(readable, 4, 5);
198
+ expect(frame).equal(null);
199
+ });
200
+
201
+ it('readVideoFrame assembles a frame from multiple chunks', async function () {
202
+ const readable = new Readable();
203
+ readable._read = () => {};
204
+ const framePromise = readVideoFrame(readable, 6, 200);
205
+ await new Promise(resolve => setImmediate(resolve));
206
+ readable.push(Buffer.from([1, 2]));
207
+ await new Promise(resolve => setImmediate(resolve));
208
+ readable.push(Buffer.from([3, 4]));
209
+ await new Promise(resolve => setImmediate(resolve));
210
+ readable.push(Buffer.from([5, 6]));
211
+ const frame = await framePromise;
212
+ expect(!!frame && frame.equals(Buffer.from([1, 2, 3, 4, 5, 6]))).equal(true);
213
+ });
214
+
215
+ it('readVideoFrame does not timeout after a successful chunked read', async function () {
216
+ const errors: any[] = [];
217
+ const originalError = console.error;
218
+ console.error = (...args: any[]) => {
219
+ errors.push(args);
220
+ };
221
+ const readable = new Readable();
222
+ readable._read = () => {};
223
+ try {
224
+ const timeoutMs = 40;
225
+ const framePromise = readVideoFrame(readable, 4, timeoutMs);
226
+ await new Promise(resolve => setImmediate(resolve));
227
+ readable.push(Buffer.from([1, 2]));
228
+ await new Promise(resolve => setImmediate(resolve));
229
+ readable.push(Buffer.from([3, 4]));
230
+ const frame = await framePromise;
231
+ expect(!!frame && frame.equals(Buffer.from([1, 2, 3, 4]))).equal(true);
232
+ await new Promise(resolve => setTimeout(resolve, timeoutMs + 30));
233
+ expect(errors).deep.equal([]);
234
+ } finally {
235
+ console.error = originalError;
236
+ }
237
+ });
238
+
239
+ it('spawnConcatVideoFrameReader regenerates timestamps for auto-edit concat', function () {
240
+ const childProcessReadableStream = new Readable();
241
+ childProcessReadableStream._read = () => { };
242
+ const childProcessWritableStream = new Writable();
243
+ (<Pipe>childProcessWritableStream.pipe) = () => childProcessWritableStream;
244
+
245
+ childProcessStream.stdin = childProcessWritableStream;
246
+ childProcessStream.stderr = childProcessReadableStream;
247
+
248
+ let spawnArgs: string[] = [];
249
+ const spawnStub = child_process.spawn as SinonStub;
250
+ spawnStub.callsFake((_cmd: string, args: string[]) => {
251
+ spawnArgs = args;
252
+ return childProcessStream as ChildProcessWithoutNullStreams;
253
+ });
254
+
255
+ spawnConcatVideoFrameReader({
256
+ concatFilePath: '/tmp/av-concat.txt',
257
+ fps: 30,
258
+ totalFrames: 90,
259
+ });
260
+
261
+ spawnStub.resetBehavior();
262
+ spawnStub.returns(childProcessStream as ChildProcessWithoutNullStreams);
263
+
264
+ expect(spawnArgs.indexOf('-fflags')).greaterThan(-1);
265
+ expect(spawnArgs[spawnArgs.indexOf('-fflags') + 1]).equal('+genpts');
266
+ expect(spawnArgs.indexOf('-an')).greaterThan(-1);
267
+ expect(spawnArgs[spawnArgs.indexOf('-avoid_negative_ts') + 1]).equal('make_zero');
268
+ });
269
+
270
+ it('waitForProcessExit resolves exit code', async function () {
271
+ const processEmitter = new EventEmitter();
272
+ const waitPromise = waitForProcessExit(processEmitter, 50);
273
+ setTimeout(() => processEmitter.emit('exit', 0), 5);
274
+ const { exitCode, reason } = await waitPromise;
275
+ expect(exitCode).equal(0);
276
+ expect(reason).equal(undefined);
277
+ });
278
+
279
+ it('waitForProcessExit resolves non-zero on timeout', async function () {
280
+ const processEmitter = new EventEmitter();
281
+ const { exitCode, reason } = await waitForProcessExit(processEmitter, 5);
282
+ expect(exitCode).equal(1);
283
+ expect(reason).equal('waitForProcessExit timeout (5ms)');
284
+ });
285
+ });
package/src/typings.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- declare module 'fft-js' {
2
- export function fft(signal: number[]): [number, number][];
3
- export const util: {
4
- fftFreq(phasors: [number, number][], sampleRate: number): number[];
5
- fftMag(phasors: [number, number][]): number[];
6
- };
1
+ declare module 'fft-js' {
2
+ export function fft(signal: number[]): [number, number][];
3
+ export const util: {
4
+ fftFreq(phasors: [number, number][], sampleRate: number): number[];
5
+ fftMag(phasors: [number, number][]): number[];
6
+ };
7
7
  }