nodejs-audio-visualizer 4.5.0 → 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.
- package/LICENSE +21 -21
- package/README.md +80 -80
- package/dist/audio.d.ts +12 -12
- package/dist/audio.js +120 -120
- package/dist/beats.d.ts +19 -19
- package/dist/beats.js +43 -43
- package/dist/bpmEncoder.d.ts +14 -14
- package/dist/bpmEncoder.js +97 -97
- package/dist/config.d.ts +77 -77
- package/dist/config.js +369 -369
- package/dist/dsp.d.ts +16 -16
- package/dist/dsp.js +76 -76
- package/dist/highlight.d.ts +32 -32
- package/dist/highlight.js +325 -325
- package/dist/image.d.ts +79 -79
- package/dist/image.js +458 -458
- package/dist/index.d.ts +79 -79
- package/dist/index.js +531 -531
- package/dist/subtitleConvert.d.ts +3 -3
- package/dist/subtitleConvert.js +61 -61
- package/dist/video.d.ts +80 -80
- package/dist/video.js +434 -426
- package/dist/video.js.map +1 -1
- package/dist/waitForEventLoop.d.ts +1 -1
- package/dist/waitForEventLoop.js +5 -5
- package/package.json +61 -61
- package/src/audio.ts +120 -120
- package/src/beats.ts +68 -68
- package/src/bpmEncoder.ts +97 -97
- package/src/config.ts +476 -476
- package/src/dsp.ts +95 -95
- package/src/highlight.ts +284 -284
- package/src/image.ts +602 -602
- package/src/index.ts +713 -713
- package/src/subtitleConvert.ts +63 -63
- package/src/tests/audio.test.ts +96 -96
- package/src/tests/config.test.ts +496 -496
- package/src/tests/dsp.test.ts +24 -24
- package/src/tests/highlight.test.ts +178 -178
- package/src/tests/image.test.ts +222 -222
- package/src/tests/index.test.ts +112 -112
- package/src/tests/subtitleConvert.test.ts +23 -23
- package/src/tests/video.test.ts +285 -216
- package/src/typings.d.ts +6 -6
- package/src/video.ts +550 -542
- package/src/waitForEventLoop.ts +2 -2
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare const isLikelyLrc: (text: string) => boolean;
|
|
2
|
-
export declare const lrcToSrt: (lrc: string) => string;
|
|
3
|
-
export declare const normalizeInlineSubtitlesToSrt: (text: string) => string;
|
|
1
|
+
export declare const isLikelyLrc: (text: string) => boolean;
|
|
2
|
+
export declare const lrcToSrt: (lrc: string) => string;
|
|
3
|
+
export declare const normalizeInlineSubtitlesToSrt: (text: string) => string;
|
package/dist/subtitleConvert.js
CHANGED
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
/** LRC timestamp line: [mm:ss.xx] text — hundredths optional after dot. */
|
|
4
|
-
var LRC_LINE = /^\[(\d+):(\d+)(?:\.(\d+))?\]\s*(.*)$/;
|
|
5
|
-
exports.isLikelyLrc = function (text) {
|
|
6
|
-
var body = text.replace(/^\uFEFF/, '');
|
|
7
|
-
for (var _i = 0, _a = body.split(/\r?\n/); _i < _a.length; _i++) {
|
|
8
|
-
var line = _a[_i];
|
|
9
|
-
var t = line.trim();
|
|
10
|
-
if (t.length === 0) {
|
|
11
|
-
continue;
|
|
12
|
-
}
|
|
13
|
-
return LRC_LINE.test(t);
|
|
14
|
-
}
|
|
15
|
-
return false;
|
|
16
|
-
};
|
|
17
|
-
var msToSrtTime = function (ms) {
|
|
18
|
-
var h = Math.floor(ms / 3600000);
|
|
19
|
-
var m = Math.floor((ms % 3600000) / 60000);
|
|
20
|
-
var s = Math.floor((ms % 60000) / 1000);
|
|
21
|
-
var milli = ms % 1000;
|
|
22
|
-
var pad = function (n, w) { return String(n).padStart(w, '0'); };
|
|
23
|
-
return pad(h, 2) + ":" + pad(m, 2) + ":" + pad(s, 2) + "," + pad(milli, 3);
|
|
24
|
-
};
|
|
25
|
-
var DEFAULT_TAIL_MS = 2000;
|
|
26
|
-
exports.lrcToSrt = function (lrc) {
|
|
27
|
-
var body = lrc.replace(/^\uFEFF/, '');
|
|
28
|
-
var entries = [];
|
|
29
|
-
for (var _i = 0, _a = body.split(/\r?\n/); _i < _a.length; _i++) {
|
|
30
|
-
var line = _a[_i];
|
|
31
|
-
var match = line.trim().match(LRC_LINE);
|
|
32
|
-
if (!match) {
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
var min = parseInt(match[1], 10);
|
|
36
|
-
var sec = parseInt(match[2], 10);
|
|
37
|
-
var centisec = match[3] !== undefined
|
|
38
|
-
? parseInt(match[3].padEnd(2, '0').slice(0, 2), 10)
|
|
39
|
-
: 0;
|
|
40
|
-
var startMs = (min * 60 + sec) * 1000 + centisec * 10;
|
|
41
|
-
var text = (match[4] || '').trim();
|
|
42
|
-
if (text.length > 0) {
|
|
43
|
-
entries.push({ startMs: startMs, text: text });
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
entries.sort(function (a, b) { return a.startMs - b.startMs; });
|
|
47
|
-
var out = '';
|
|
48
|
-
for (var i = 0; i < entries.length; i++) {
|
|
49
|
-
var start = entries[i].startMs;
|
|
50
|
-
var end = i + 1 < entries.length
|
|
51
|
-
? Math.max(entries[i + 1].startMs, start + 1)
|
|
52
|
-
: start + DEFAULT_TAIL_MS;
|
|
53
|
-
out += i + 1 + "\n";
|
|
54
|
-
out += msToSrtTime(start) + " --> " + msToSrtTime(end) + "\n";
|
|
55
|
-
out += entries[i].text + "\n\n";
|
|
56
|
-
}
|
|
57
|
-
return out.length > 0 ? out.replace(/\n*$/, '\n') : '';
|
|
58
|
-
};
|
|
59
|
-
exports.normalizeInlineSubtitlesToSrt = function (text) {
|
|
60
|
-
return exports.isLikelyLrc(text) ? exports.lrcToSrt(text) : text;
|
|
61
|
-
};
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/** LRC timestamp line: [mm:ss.xx] text — hundredths optional after dot. */
|
|
4
|
+
var LRC_LINE = /^\[(\d+):(\d+)(?:\.(\d+))?\]\s*(.*)$/;
|
|
5
|
+
exports.isLikelyLrc = function (text) {
|
|
6
|
+
var body = text.replace(/^\uFEFF/, '');
|
|
7
|
+
for (var _i = 0, _a = body.split(/\r?\n/); _i < _a.length; _i++) {
|
|
8
|
+
var line = _a[_i];
|
|
9
|
+
var t = line.trim();
|
|
10
|
+
if (t.length === 0) {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
return LRC_LINE.test(t);
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
};
|
|
17
|
+
var msToSrtTime = function (ms) {
|
|
18
|
+
var h = Math.floor(ms / 3600000);
|
|
19
|
+
var m = Math.floor((ms % 3600000) / 60000);
|
|
20
|
+
var s = Math.floor((ms % 60000) / 1000);
|
|
21
|
+
var milli = ms % 1000;
|
|
22
|
+
var pad = function (n, w) { return String(n).padStart(w, '0'); };
|
|
23
|
+
return pad(h, 2) + ":" + pad(m, 2) + ":" + pad(s, 2) + "," + pad(milli, 3);
|
|
24
|
+
};
|
|
25
|
+
var DEFAULT_TAIL_MS = 2000;
|
|
26
|
+
exports.lrcToSrt = function (lrc) {
|
|
27
|
+
var body = lrc.replace(/^\uFEFF/, '');
|
|
28
|
+
var entries = [];
|
|
29
|
+
for (var _i = 0, _a = body.split(/\r?\n/); _i < _a.length; _i++) {
|
|
30
|
+
var line = _a[_i];
|
|
31
|
+
var match = line.trim().match(LRC_LINE);
|
|
32
|
+
if (!match) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
var min = parseInt(match[1], 10);
|
|
36
|
+
var sec = parseInt(match[2], 10);
|
|
37
|
+
var centisec = match[3] !== undefined
|
|
38
|
+
? parseInt(match[3].padEnd(2, '0').slice(0, 2), 10)
|
|
39
|
+
: 0;
|
|
40
|
+
var startMs = (min * 60 + sec) * 1000 + centisec * 10;
|
|
41
|
+
var text = (match[4] || '').trim();
|
|
42
|
+
if (text.length > 0) {
|
|
43
|
+
entries.push({ startMs: startMs, text: text });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
entries.sort(function (a, b) { return a.startMs - b.startMs; });
|
|
47
|
+
var out = '';
|
|
48
|
+
for (var i = 0; i < entries.length; i++) {
|
|
49
|
+
var start = entries[i].startMs;
|
|
50
|
+
var end = i + 1 < entries.length
|
|
51
|
+
? Math.max(entries[i + 1].startMs, start + 1)
|
|
52
|
+
: start + DEFAULT_TAIL_MS;
|
|
53
|
+
out += i + 1 + "\n";
|
|
54
|
+
out += msToSrtTime(start) + " --> " + msToSrtTime(end) + "\n";
|
|
55
|
+
out += entries[i].text + "\n\n";
|
|
56
|
+
}
|
|
57
|
+
return out.length > 0 ? out.replace(/\n*$/, '\n') : '';
|
|
58
|
+
};
|
|
59
|
+
exports.normalizeInlineSubtitlesToSrt = function (text) {
|
|
60
|
+
return exports.isLikelyLrc(text) ? exports.lrcToSrt(text) : text;
|
|
61
|
+
};
|
|
62
62
|
//# sourceMappingURL=subtitleConvert.js.map
|
package/dist/video.d.ts
CHANGED
|
@@ -1,80 +1,80 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
import { Readable, Writable } from 'stream';
|
|
3
|
-
export interface AudioMuxSegment {
|
|
4
|
-
seekSeconds: number;
|
|
5
|
-
durationSeconds: number;
|
|
6
|
-
}
|
|
7
|
-
interface FfmpegVideoWriterConfig {
|
|
8
|
-
audioFilename: string;
|
|
9
|
-
subtitleFilename?: string;
|
|
10
|
-
/** ASS `Alignment` in FFmpeg `subtitles` `force_style` (e.g. 2 bottom, 5 middle, 8 top). Default 2. */
|
|
11
|
-
subtitleAlignmentAss?: number;
|
|
12
|
-
videoFileName: string;
|
|
13
|
-
fps: number;
|
|
14
|
-
crf?: string;
|
|
15
|
-
preset?: string;
|
|
16
|
-
onStderr?: (data: any) => any;
|
|
17
|
-
/** When set with `audioDurationSeconds`, passed to ffmpeg before `-i` for muxed highlight. */
|
|
18
|
-
audioSeekSeconds?: number;
|
|
19
|
-
audioDurationSeconds?: number;
|
|
20
|
-
/** One contiguous trim of the same audio file (muxed highlight). */
|
|
21
|
-
audioSegment?: AudioMuxSegment;
|
|
22
|
-
}
|
|
23
|
-
export declare const spawnFfmpegVideoWriter: (config: FfmpegVideoWriterConfig) => import("child_process").ChildProcessWithoutNullStreams;
|
|
24
|
-
export interface VideoInfo {
|
|
25
|
-
width: number;
|
|
26
|
-
height: number;
|
|
27
|
-
duration: number;
|
|
28
|
-
}
|
|
29
|
-
export interface SceneChange {
|
|
30
|
-
frameNumber: number;
|
|
31
|
-
pts: number;
|
|
32
|
-
ptsTime: number;
|
|
33
|
-
}
|
|
34
|
-
export declare const detectSceneChanges: (videoPath: string, threshold?: number) => Promise<SceneChange[]>;
|
|
35
|
-
export declare const getVideoInfo: (videoPath: string) => Promise<VideoInfo>;
|
|
36
|
-
export declare const buildVideoFilter: (sourceWidth: number, sourceHeight: number, targetWidth: number, targetHeight: number) => string;
|
|
37
|
-
export declare const spawnVideoFrameReader: (config: {
|
|
38
|
-
videoPath: string;
|
|
39
|
-
fps: number;
|
|
40
|
-
totalFrames: number;
|
|
41
|
-
width?: number | undefined;
|
|
42
|
-
height?: number | undefined;
|
|
43
|
-
sourceWidth?: number | undefined;
|
|
44
|
-
sourceHeight?: number | undefined;
|
|
45
|
-
}) => import("child_process").ChildProcessWithoutNullStreams;
|
|
46
|
-
export declare const readVideoFrame: (stream: Readable, frameSize: number, timeoutMs?: number) => Promise<Buffer | null>;
|
|
47
|
-
export declare const getProgress: (onProgress: (currentFrame: number) => any) => (stderrOutput: Buffer) => void;
|
|
48
|
-
export declare const calculateProgress: (framesCount: number, callback: (progress: number) => any) => (currentFrame: number) => any;
|
|
49
|
-
export declare const waitDrain: (stream: Writable, processStream?: {
|
|
50
|
-
once: (eventName: string, listener: (...args: any[]) => void) => any;
|
|
51
|
-
removeListener: (eventName: string, listener: (...args: any[]) => void) => any;
|
|
52
|
-
} | undefined, timeoutMs?: number) => Promise<boolean>;
|
|
53
|
-
export interface ProcessExitResult {
|
|
54
|
-
exitCode: number;
|
|
55
|
-
reason?: string;
|
|
56
|
-
}
|
|
57
|
-
export declare const waitForProcessExit: (processStream: {
|
|
58
|
-
once: (eventName: string, listener: (...args: any[]) => void) => any;
|
|
59
|
-
removeListener: (eventName: string, listener: (...args: any[]) => void) => any;
|
|
60
|
-
}, timeoutMs?: number) => Promise<ProcessExitResult>;
|
|
61
|
-
export interface VideoSegment {
|
|
62
|
-
outputStartFrame: number;
|
|
63
|
-
videoSeekSeconds: number;
|
|
64
|
-
frameCount: number;
|
|
65
|
-
}
|
|
66
|
-
export declare const buildBeatSyncedSegments: (beatFrameIndices: number[], totalFrames: number, sceneChanges: SceneChange[], videoDuration: number) => VideoSegment[];
|
|
67
|
-
export declare const writeConcatFile: (segments: VideoSegment[], videoPath: string, videoDuration: number, fps: number) => string;
|
|
68
|
-
export declare const writeSubtitlesFile: (subtitles: string) => string;
|
|
69
|
-
export declare const spawnConcatVideoFrameReader: (config: {
|
|
70
|
-
concatFilePath: string;
|
|
71
|
-
fps: number;
|
|
72
|
-
totalFrames: number;
|
|
73
|
-
width?: number | undefined;
|
|
74
|
-
height?: number | undefined;
|
|
75
|
-
sourceWidth?: number | undefined;
|
|
76
|
-
sourceHeight?: number | undefined;
|
|
77
|
-
}) => import("child_process").ChildProcessWithoutNullStreams;
|
|
78
|
-
export declare const cleanupTempFile: (filePath: string) => void;
|
|
79
|
-
export declare const cleanupConcatFile: (filePath: string) => void;
|
|
80
|
-
export {};
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Readable, Writable } from 'stream';
|
|
3
|
+
export interface AudioMuxSegment {
|
|
4
|
+
seekSeconds: number;
|
|
5
|
+
durationSeconds: number;
|
|
6
|
+
}
|
|
7
|
+
interface FfmpegVideoWriterConfig {
|
|
8
|
+
audioFilename: string;
|
|
9
|
+
subtitleFilename?: string;
|
|
10
|
+
/** ASS `Alignment` in FFmpeg `subtitles` `force_style` (e.g. 2 bottom, 5 middle, 8 top). Default 2. */
|
|
11
|
+
subtitleAlignmentAss?: number;
|
|
12
|
+
videoFileName: string;
|
|
13
|
+
fps: number;
|
|
14
|
+
crf?: string;
|
|
15
|
+
preset?: string;
|
|
16
|
+
onStderr?: (data: any) => any;
|
|
17
|
+
/** When set with `audioDurationSeconds`, passed to ffmpeg before `-i` for muxed highlight. */
|
|
18
|
+
audioSeekSeconds?: number;
|
|
19
|
+
audioDurationSeconds?: number;
|
|
20
|
+
/** One contiguous trim of the same audio file (muxed highlight). */
|
|
21
|
+
audioSegment?: AudioMuxSegment;
|
|
22
|
+
}
|
|
23
|
+
export declare const spawnFfmpegVideoWriter: (config: FfmpegVideoWriterConfig) => import("child_process").ChildProcessWithoutNullStreams;
|
|
24
|
+
export interface VideoInfo {
|
|
25
|
+
width: number;
|
|
26
|
+
height: number;
|
|
27
|
+
duration: number;
|
|
28
|
+
}
|
|
29
|
+
export interface SceneChange {
|
|
30
|
+
frameNumber: number;
|
|
31
|
+
pts: number;
|
|
32
|
+
ptsTime: number;
|
|
33
|
+
}
|
|
34
|
+
export declare const detectSceneChanges: (videoPath: string, threshold?: number) => Promise<SceneChange[]>;
|
|
35
|
+
export declare const getVideoInfo: (videoPath: string) => Promise<VideoInfo>;
|
|
36
|
+
export declare const buildVideoFilter: (sourceWidth: number, sourceHeight: number, targetWidth: number, targetHeight: number) => string;
|
|
37
|
+
export declare const spawnVideoFrameReader: (config: {
|
|
38
|
+
videoPath: string;
|
|
39
|
+
fps: number;
|
|
40
|
+
totalFrames: number;
|
|
41
|
+
width?: number | undefined;
|
|
42
|
+
height?: number | undefined;
|
|
43
|
+
sourceWidth?: number | undefined;
|
|
44
|
+
sourceHeight?: number | undefined;
|
|
45
|
+
}) => import("child_process").ChildProcessWithoutNullStreams;
|
|
46
|
+
export declare const readVideoFrame: (stream: Readable, frameSize: number, timeoutMs?: number) => Promise<Buffer | null>;
|
|
47
|
+
export declare const getProgress: (onProgress: (currentFrame: number) => any) => (stderrOutput: Buffer) => void;
|
|
48
|
+
export declare const calculateProgress: (framesCount: number, callback: (progress: number) => any) => (currentFrame: number) => any;
|
|
49
|
+
export declare const waitDrain: (stream: Writable, processStream?: {
|
|
50
|
+
once: (eventName: string, listener: (...args: any[]) => void) => any;
|
|
51
|
+
removeListener: (eventName: string, listener: (...args: any[]) => void) => any;
|
|
52
|
+
} | undefined, timeoutMs?: number) => Promise<boolean>;
|
|
53
|
+
export interface ProcessExitResult {
|
|
54
|
+
exitCode: number;
|
|
55
|
+
reason?: string;
|
|
56
|
+
}
|
|
57
|
+
export declare const waitForProcessExit: (processStream: {
|
|
58
|
+
once: (eventName: string, listener: (...args: any[]) => void) => any;
|
|
59
|
+
removeListener: (eventName: string, listener: (...args: any[]) => void) => any;
|
|
60
|
+
}, timeoutMs?: number) => Promise<ProcessExitResult>;
|
|
61
|
+
export interface VideoSegment {
|
|
62
|
+
outputStartFrame: number;
|
|
63
|
+
videoSeekSeconds: number;
|
|
64
|
+
frameCount: number;
|
|
65
|
+
}
|
|
66
|
+
export declare const buildBeatSyncedSegments: (beatFrameIndices: number[], totalFrames: number, sceneChanges: SceneChange[], videoDuration: number) => VideoSegment[];
|
|
67
|
+
export declare const writeConcatFile: (segments: VideoSegment[], videoPath: string, videoDuration: number, fps: number) => string;
|
|
68
|
+
export declare const writeSubtitlesFile: (subtitles: string) => string;
|
|
69
|
+
export declare const spawnConcatVideoFrameReader: (config: {
|
|
70
|
+
concatFilePath: string;
|
|
71
|
+
fps: number;
|
|
72
|
+
totalFrames: number;
|
|
73
|
+
width?: number | undefined;
|
|
74
|
+
height?: number | undefined;
|
|
75
|
+
sourceWidth?: number | undefined;
|
|
76
|
+
sourceHeight?: number | undefined;
|
|
77
|
+
}) => import("child_process").ChildProcessWithoutNullStreams;
|
|
78
|
+
export declare const cleanupTempFile: (filePath: string) => void;
|
|
79
|
+
export declare const cleanupConcatFile: (filePath: string) => void;
|
|
80
|
+
export {};
|