sfxmix 1.2.4 → 1.2.8

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.
@@ -0,0 +1,9 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(node:*)"
5
+ ],
6
+ "deny": [],
7
+ "ask": []
8
+ }
9
+ }
@@ -0,0 +1,23 @@
1
+ import SfxMix from '../index.js';
2
+ const sfx = new SfxMix();
3
+
4
+
5
+ // OGG conversion with custom options
6
+ await sfx
7
+ .add('part1.mp3')
8
+ .save('output.ogg', {
9
+ 'c:a': 'libopus',
10
+ 'b:a': '32k',
11
+ 'ar': '48000',
12
+ 'ac': '1',
13
+ 'vbr': 'off',
14
+ 'compression_level': '10',
15
+ 'frame_duration': '20',
16
+ 'application': 'voip'
17
+ })
18
+ .then(() => {
19
+ console.log('Successfully exported: output.ogg');
20
+ })
21
+ .catch((error) => {
22
+ console.error('Error during audio processing:', error);
23
+ });
package/index.js CHANGED
@@ -48,14 +48,65 @@ class SfxMix {
48
48
  return this;
49
49
  }
50
50
 
51
- save(output) {
51
+
52
+
53
+ convertAudio(inputFile, outputFile, outputOptions = {}) {
54
+ return new Promise((resolve, reject) => {
55
+ const command = ffmpeg().input(inputFile);
56
+
57
+ // Apply custom output options if provided
58
+ if (Object.keys(outputOptions).length > 0) {
59
+ // Convert object to array format for FFmpeg
60
+ const optionsArray = [];
61
+ for (const [key, value] of Object.entries(outputOptions)) {
62
+ optionsArray.push(`-${key}`, value);
63
+ }
64
+ command.outputOptions(optionsArray);
65
+ } else {
66
+ // Auto-detect format and apply defaults based on file extension
67
+ const ext = path.extname(outputFile).toLowerCase();
68
+ switch (ext) {
69
+ case '.ogg':
70
+ command.audioCodec('libopus').format('ogg');
71
+ break;
72
+ case '.wav':
73
+ command.audioCodec('pcm_s16le').format('wav');
74
+ break;
75
+ case '.flac':
76
+ command.audioCodec('flac').format('flac');
77
+ break;
78
+ case '.aac':
79
+ case '.m4a':
80
+ command.audioCodec('aac').format('mp4');
81
+ break;
82
+ case '.mp3':
83
+ default:
84
+ command.audioCodec('libmp3lame').format('mp3');
85
+ break;
86
+ }
87
+ }
88
+
89
+ command
90
+ .output(outputFile)
91
+ .on('end', () => resolve())
92
+ .on('error', (err) => reject(err))
93
+ .run();
94
+ });
95
+ }
96
+
97
+ // Keep convertToOgg for backward compatibility
98
+ convertToOgg(inputFile, outputFile, options = {}) {
99
+ return this.convertAudio(inputFile, outputFile, options);
100
+ }
101
+
102
+ save(output, outputOptions = {}) {
52
103
  return new Promise(async (resolve, reject) => {
53
104
  try {
105
+ // Process all actions
54
106
  for (let action of this.actions) {
55
107
  if (action.type === 'add') {
56
- // Existing add logic
57
108
  if (this.currentFile == null) {
58
- this.currentFile = action.input;
109
+ this.currentFile = path.isAbsolute(action.input) ? action.input : path.resolve(process.cwd(), action.input);
59
110
  } else {
60
111
  const tempFile = path.join(this.TMP_DIR, `temp_concat_${uuidv4()}.mp3`);
61
112
  await this.concatenateAudioFiles([this.currentFile, action.input], tempFile);
@@ -65,7 +116,6 @@ class SfxMix {
65
116
  this.currentFile = tempFile;
66
117
  }
67
118
  } else if (action.type === 'mix') {
68
- // Existing mix logic
69
119
  if (this.currentFile == null) {
70
120
  throw new Error('No audio to mix with. Add or concatenate audio before mixing.');
71
121
  }
@@ -76,7 +126,6 @@ class SfxMix {
76
126
  }
77
127
  this.currentFile = tempFile;
78
128
  } else if (action.type === 'silence') {
79
- // Existing silence logic
80
129
  const tempSilenceFile = path.join(this.TMP_DIR, `temp_silence_${uuidv4()}.mp3`);
81
130
  await this.generateSilence(action.duration, tempSilenceFile);
82
131
  if (this.currentFile == null) {
@@ -87,11 +136,10 @@ class SfxMix {
87
136
  if (this.isTempFile(this.currentFile)) {
88
137
  fs.unlinkSync(this.currentFile);
89
138
  }
90
- fs.unlinkSync(tempSilenceFile); // Remove the silence file
139
+ fs.unlinkSync(tempSilenceFile);
91
140
  this.currentFile = tempFile;
92
141
  }
93
142
  } else if (action.type === 'filter') {
94
- // New filter logic
95
143
  if (this.currentFile == null) {
96
144
  throw new Error('No audio to apply filter to. Add audio before applying filters.');
97
145
  }
@@ -103,32 +151,47 @@ class SfxMix {
103
151
  this.currentFile = tempFile;
104
152
  }
105
153
  }
106
- // Finalize output
154
+
155
+ // Prepare output
107
156
  const absoluteOutput = path.resolve(process.cwd(), output);
108
157
  const outputDir = path.dirname(absoluteOutput);
109
158
 
110
- // Ensure the output directory exists
111
159
  if (!fs.existsSync(outputDir)) {
112
160
  fs.mkdirSync(outputDir, { recursive: true });
113
161
  }
114
162
 
115
- // Check if the source file exists
116
163
  if (!fs.existsSync(this.currentFile)) {
117
164
  throw new Error(`Source file does not exist: ${this.currentFile}`);
118
165
  }
119
166
 
120
- // Attempt to rename the file
121
- try {
122
- fs.renameSync(this.currentFile, absoluteOutput);
123
- } catch (renameErr) {
124
- // If rename fails, try to copy the file instead
125
- fs.copyFileSync(this.currentFile, absoluteOutput);
167
+ // Check if we need format conversion
168
+ const needsConversion = Object.keys(outputOptions).length > 0 ||
169
+ output.toLowerCase().endsWith('.ogg') ||
170
+ output.toLowerCase().endsWith('.wav') ||
171
+ output.toLowerCase().endsWith('.flac') ||
172
+ output.toLowerCase().endsWith('.aac') ||
173
+ output.toLowerCase().endsWith('.m4a');
174
+
175
+ if (needsConversion) {
176
+ // Use conversion with custom options
177
+ await this.convertAudio(this.currentFile, absoluteOutput, outputOptions);
178
+ } else {
179
+ // Save as MP3 or original format (no conversion needed)
180
+ try {
181
+ fs.renameSync(this.currentFile, absoluteOutput);
182
+ } catch (renameErr) {
183
+ // If rename fails, try to copy the file instead
184
+ fs.copyFileSync(this.currentFile, absoluteOutput);
185
+ fs.unlinkSync(this.currentFile);
186
+ }
187
+ }
188
+
189
+ // Clean up temp file if it exists and wasn't renamed
190
+ if (this.isTempFile(this.currentFile) && fs.existsSync(this.currentFile)) {
126
191
  fs.unlinkSync(this.currentFile);
127
192
  }
128
193
 
129
- // Reset internal state
130
194
  this.reset();
131
-
132
195
  resolve(absoluteOutput);
133
196
  } catch (err) {
134
197
  console.error('Error during audio processing:', err);
@@ -202,8 +265,8 @@ class SfxMix {
202
265
  },
203
266
  },
204
267
  ])
205
- .audioCodec('libmp3lame') // Ensure the codec is MP3
206
- .format('mp3') // Ensure the format is MP3
268
+ .audioCodec('libmp3lame')
269
+ .format('mp3')
207
270
  .output(outputFile)
208
271
  .on('end', () => resolve())
209
272
  .on('error', (err) => reject(err))
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sfxmix",
3
3
  "description": "🎧 SfxMix - Powerful and easy-to-use module for processing audio.",
4
- "version": "1.2.4",
4
+ "version": "1.2.8",
5
5
  "main": "index.js",
6
6
  "dependencies": {
7
7
  "fluent-ffmpeg": "^2.1.3",
@@ -20,6 +20,8 @@
20
20
  "filters",
21
21
  "mp3",
22
22
  "wav",
23
+ "ogg",
24
+ "opus",
23
25
  "echo",
24
26
  "delay",
25
27
  "tremolo",