m4k 0.2.2 → 0.2.3

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.
@@ -6,7 +6,7 @@ import { type AudioOptions } from "@m4k/common";
6
6
  * @param signal - An optional abort signal.
7
7
  * @returns An iterable of the processed videos.
8
8
  */
9
- export declare function processAudio(input: string | AsyncIterable<Uint8Array> | Blob, opts: AudioOptions, { signal }?: {
9
+ export declare function processAudio(input: string | AsyncIterable<Uint8Array> | Blob, opts: AudioOptions | AudioOptions[], { signal }?: {
10
10
  signal?: AbortSignal;
11
11
  }): AsyncGenerator<import("@m4k/common").ProcessedFile | {
12
12
  progress: number;
@@ -1,7 +1,7 @@
1
1
  import {} from "@m4k/common";
2
2
  import { mkdir } from "node:fs/promises";
3
3
  import { createQueue } from "tasque";
4
- import { processFfmpeg } from "../util/ffmpeg-processor";
4
+ import { getExtension, processFfmpeg } from "../util/ffmpeg-processor";
5
5
  /**
6
6
  * Process a video.
7
7
  * @param input - The input video. Can be a file path, a stream or a blob.
@@ -11,28 +11,29 @@ import { processFfmpeg } from "../util/ffmpeg-processor";
11
11
  */
12
12
  export function processAudio(input, opts, { signal } = {}) {
13
13
  return processFfmpeg(input, () => {
14
- const inArgs = [];
15
- if (opts.inputFormat)
16
- inArgs.push("-f", opts.inputFormat);
17
- if (opts.seek)
18
- inArgs.push("-ss", opts.seek.toString());
19
- if (opts.duration)
20
- inArgs.push("-t", opts.duration.toString());
21
- const outArgs = [];
22
- if (opts.format)
23
- outArgs.push("-f", opts.format);
24
- if (opts.bitrate)
25
- outArgs.push("-b:a", opts.bitrate.toString());
26
- if (opts.filters)
27
- outArgs.push("-af", opts.filters);
28
- if (opts.complexFilters)
29
- outArgs.push("-filter_complex", opts.complexFilters);
30
- outArgs.push("-c:a", opts.codec ?? "copy");
31
- return {
32
- input: inArgs,
33
- out: outArgs,
34
- format: opts.format ?? "mp4",
35
- };
14
+ let args = [];
15
+ for (const o of Array.isArray(opts) ? opts : [opts]) {
16
+ const a = [];
17
+ if (o.format)
18
+ a.push("-f", o.format);
19
+ if (o.seek)
20
+ a.push("-ss", o.seek.toString());
21
+ if (o.duration)
22
+ a.push("-t", o.duration.toString());
23
+ if (o.bitrate)
24
+ a.push("-b:a", o.bitrate.toString());
25
+ if (o.filters)
26
+ a.push("-af", o.filters);
27
+ if (o.complexFilters)
28
+ a.push("-filter_complex", o.complexFilters);
29
+ if (o.codec)
30
+ a.push("-c:a", o.codec);
31
+ args.push({
32
+ args: a,
33
+ ext: o.ext ?? getExtension(o.format ?? "mp3"),
34
+ });
35
+ }
36
+ return args;
36
37
  }, { signal, queue: audioQueue, tmpDir });
37
38
  }
38
39
  /**
@@ -8,10 +8,9 @@ import { type Tasque } from "tasque";
8
8
  * @returns An iterable of the processed videos.
9
9
  */
10
10
  export declare function processFfmpeg(input: string | AsyncIterable<Uint8Array> | Blob, buildArgs: () => {
11
- input: string[];
12
- out: string[];
13
- format: string;
14
- }, { signal, queue, tmpDir, }: {
11
+ args: string[];
12
+ ext: string;
13
+ }[], { signal, queue, tmpDir, }: {
15
14
  signal?: AbortSignal;
16
15
  queue: Tasque;
17
16
  tmpDir: string;
@@ -17,13 +17,21 @@ export function processFfmpeg(input, buildArgs, { signal, queue, tmpDir, }) {
17
17
  const id = randomId();
18
18
  const inputPath = typeof input === "string" ? input : createTmp(tmpDir, id, input);
19
19
  const iterable = queue.iterate(async function* () {
20
- const { input: inArgs, out: outArgs, format } = buildArgs();
21
- const outputPath = `${tmpDir}/out-${id}.${getExtension(format)}`;
20
+ const outputPaths = [];
22
21
  try {
23
22
  signal?.throwIfAborted();
24
23
  const p = await inputPath;
25
24
  signal?.throwIfAborted();
26
- const child = spawn(ffmpeg, ["-y", ...inArgs, "-i", p, ...outArgs, outputPath], { stdio: ["pipe", "pipe", "pipe"] });
25
+ const outArgs = [];
26
+ let i = 1;
27
+ for (const { args, ext } of buildArgs()) {
28
+ const outputPath = `${tmpDir}/out-${id}-${i++}.${ext}`;
29
+ outArgs.push(...args, outputPath);
30
+ outputPaths.push(outputPath);
31
+ }
32
+ const child = spawn(ffmpeg, ["-y", "-i", p, ...outArgs], {
33
+ stdio: ["pipe", "pipe", "pipe"],
34
+ });
27
35
  // Use manual abort handling to avoid some uncatchable error, at least in bun.
28
36
  signal?.addEventListener("abort", () => child.kill());
29
37
  const decoder = new TextDecoder();
@@ -63,14 +71,19 @@ export function processFfmpeg(input, buildArgs, { signal, queue, tmpDir, }) {
63
71
  if (code !== 0) {
64
72
  throw new Error(`ffmpeg exited with code ${code}: ${errStr}`);
65
73
  }
66
- yield new ProcessedFile(outputPath, mimeTypes[format]);
74
+ for (const outputPath of outputPaths) {
75
+ yield new ProcessedFile(outputPath, mimeTypes[outputPath.split(".").pop()]);
76
+ }
67
77
  }
68
78
  catch (e) {
69
79
  signal?.throwIfAborted();
70
80
  throw e;
71
81
  }
72
82
  finally {
73
- await rm(outputPath, { force: true });
83
+ await new Promise((resolve) => setTimeout(resolve, 1));
84
+ for (const outputPath of outputPaths) {
85
+ await rm(outputPath, { force: true });
86
+ }
74
87
  if (typeof inputPath !== "string") {
75
88
  await rm(await inputPath, { force: true });
76
89
  }
@@ -6,7 +6,7 @@ import { type VideoOptions } from "@m4k/common";
6
6
  * @param signal - An optional abort signal.
7
7
  * @returns An iterable of the processed videos.
8
8
  */
9
- export declare function processVideo(input: string | AsyncIterable<Uint8Array> | Blob, opts: VideoOptions, { signal }?: {
9
+ export declare function processVideo(input: string | AsyncIterable<Uint8Array> | Blob, opts: VideoOptions | VideoOptions[], { signal }?: {
10
10
  signal?: AbortSignal;
11
11
  }): AsyncGenerator<import("@m4k/common").ProcessedFile | {
12
12
  progress: number;
@@ -1,7 +1,7 @@
1
1
  import {} from "@m4k/common";
2
2
  import { mkdir } from "node:fs/promises";
3
3
  import { createQueue } from "tasque";
4
- import { processFfmpeg } from "../util/ffmpeg-processor";
4
+ import { getExtension, processFfmpeg } from "../util/ffmpeg-processor";
5
5
  /**
6
6
  * Process a video.
7
7
  * @param input - The input video. Can be a file path, a stream or a blob.
@@ -11,41 +11,45 @@ import { processFfmpeg } from "../util/ffmpeg-processor";
11
11
  */
12
12
  export function processVideo(input, opts, { signal } = {}) {
13
13
  return processFfmpeg(input, () => {
14
- const inArgs = [];
15
- if (opts.inputFormat)
16
- inArgs.push("-f", opts.inputFormat);
17
- if (opts.seek)
18
- inArgs.push("-ss", opts.seek.toString());
19
- if (opts.duration)
20
- inArgs.push("-t", opts.duration.toString());
21
- const outArgs = [];
22
- if (opts.format)
23
- outArgs.push("-f", opts.format);
24
- if (opts.audioBitrate)
25
- outArgs.push("-b:a", opts.audioBitrate.toString());
26
- if (opts.videoBitrate)
27
- outArgs.push("-b:v", opts.videoBitrate.toString());
28
- if (opts.audioFilters)
29
- outArgs.push("-af", opts.audioFilters);
30
- if (opts.videoFilters)
31
- outArgs.push("-vf", opts.videoFilters);
32
- if (opts.complexFilters)
33
- outArgs.push("-filter_complex", opts.complexFilters);
34
- if (opts.aspect)
35
- outArgs.push("-aspect", opts.aspect.toString());
36
- if (opts.pad)
37
- outArgs.push("-pad", opts.pad);
38
- if (opts.fps)
39
- outArgs.push("-r", opts.fps.toString());
40
- if (opts.size)
41
- outArgs.push("-scale", opts.size);
42
- outArgs.push("-c:v", opts.videoCodec ?? "copy");
43
- outArgs.push("-c:a", opts.audioCodec ?? "copy");
44
- return {
45
- input: inArgs,
46
- out: outArgs,
47
- format: opts.format ?? "mp4",
48
- };
14
+ let args = [];
15
+ for (const o of Array.isArray(opts) ? opts : [opts]) {
16
+ const a = [];
17
+ if (o.frames)
18
+ a.push("-frames:v", o.frames.toString());
19
+ if (o.format)
20
+ a.push("-f", o.format);
21
+ if (o.seek)
22
+ a.push("-ss", o.seek.toString());
23
+ if (o.duration)
24
+ a.push("-t", o.duration.toString());
25
+ if (o.audioBitrate)
26
+ a.push("-b:a", o.audioBitrate.toString());
27
+ if (o.videoBitrate)
28
+ a.push("-b:v", o.videoBitrate.toString());
29
+ if (o.audioFilters)
30
+ a.push("-af", o.audioFilters);
31
+ if (o.videoFilters)
32
+ a.push("-vf", o.videoFilters);
33
+ if (o.complexFilters)
34
+ a.push("-filter_complex", o.complexFilters);
35
+ if (o.aspect)
36
+ a.push("-aspect", o.aspect.toString());
37
+ if (o.pad)
38
+ a.push("-pad", o.pad);
39
+ if (o.fps)
40
+ a.push("-r", o.fps.toString());
41
+ if (o.size)
42
+ a.push("-scale", o.size);
43
+ if (o.videoCodec)
44
+ a.push("-c:v", o.videoCodec);
45
+ if (o.audioCodec)
46
+ a.push("-c:a", o.audioCodec);
47
+ args.push({
48
+ args: a,
49
+ ext: o.ext ?? getExtension(o.format ?? "mp4"),
50
+ });
51
+ }
52
+ return args;
49
53
  }, { signal, queue: videoQueue, tmpDir });
50
54
  }
51
55
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "m4k",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "type": "module",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",
@@ -18,7 +18,7 @@
18
18
  "typescript": "^5.0.0"
19
19
  },
20
20
  "dependencies": {
21
- "@m4k/common": "^0.2.0"
21
+ "@m4k/common": "^0.2.2"
22
22
  },
23
23
  "peerDependencies": {
24
24
  "sharp": "^0.33.5"