loukai-app 0.3.1 → 0.4.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loukai-app",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "Loukai Karaoke - Free and open source karaoke system for playing and creating stem-based karaoke files",
6
6
  "main": "src/main/main.js",
@@ -231,7 +231,7 @@
231
231
  ]
232
232
  },
233
233
  "bin": {
234
- "loukai": "bin/loukai.js"
234
+ "loukai-app": "bin/loukai.js"
235
235
  },
236
236
  "files": [
237
237
  "src/",
@@ -1,4 +1,5 @@
1
1
  import { EventEmitter } from 'events';
2
+ import { log } from './logger.js';
2
3
 
3
4
  class AudioEngine extends EventEmitter {
4
5
  constructor() {
@@ -42,7 +43,7 @@ class AudioEngine extends EventEmitter {
42
43
  try {
43
44
  this.initialized = true;
44
45
  this.scanDevices();
45
- console.log('Audio engine initialized');
46
+ log('Audio engine initialized');
46
47
  } catch (error) {
47
48
  console.error('Failed to initialize audio engine:', error);
48
49
  throw error;
@@ -77,7 +78,7 @@ class AudioEngine extends EventEmitter {
77
78
  this.devices.IEM = 'default-output';
78
79
  this.devices.input = 'default-input';
79
80
 
80
- console.log(`Audio engine initialized with ${this.availableDevices.length} fallback devices`);
81
+ log(`Audio engine initialized with ${this.availableDevices.length} fallback devices`);
81
82
  } catch (error) {
82
83
  console.error('Failed to scan audio devices:', error);
83
84
  this.availableDevices = [];
@@ -130,7 +131,7 @@ class AudioEngine extends EventEmitter {
130
131
  },
131
132
  };
132
133
 
133
- console.log(`${busType} output stream created`);
134
+ log(`${busType} output stream created`);
134
135
  } catch (error) {
135
136
  console.error(`Failed to create ${busType} output stream:`, error);
136
137
  }
@@ -154,7 +155,7 @@ class AudioEngine extends EventEmitter {
154
155
  },
155
156
  };
156
157
 
157
- // console.log('Input stream created');
158
+ // log('Input stream created');
158
159
  } catch (error) {
159
160
  console.error('Failed to create input stream:', error);
160
161
  }
@@ -260,7 +261,7 @@ class AudioEngine extends EventEmitter {
260
261
  }
261
262
 
262
263
  this.emit('mixChanged', this.getMixerState());
263
- console.log(`Loaded song with ${this.stems.size} stems`);
264
+ log(`Loaded song with ${this.stems.size} stems`);
264
265
  } catch (error) {
265
266
  console.error('Failed to load song:', error);
266
267
  throw error;
@@ -268,7 +269,7 @@ class AudioEngine extends EventEmitter {
268
269
  }
269
270
 
270
271
  play() {
271
- console.log('šŸŽµ MAIN AudioEngine.play() called');
272
+ log('šŸŽµ MAIN AudioEngine.play() called');
272
273
  if (!this.initialized || !this.songData) return false;
273
274
 
274
275
  try {
@@ -279,7 +280,7 @@ class AudioEngine extends EventEmitter {
279
280
  this.playStartPosition = this.currentPosition;
280
281
  this.startPositionTimer();
281
282
 
282
- console.log('šŸŽµ MAIN AudioEngine.play() - started timer, isPlaying =', this.isPlaying);
283
+ log('šŸŽµ MAIN AudioEngine.play() - started timer, isPlaying =', this.isPlaying);
283
284
 
284
285
  if (this.audioStreams.PA) this.audioStreams.PA.start();
285
286
  if (this.audioStreams.IEM) this.audioStreams.IEM.start();
@@ -437,13 +438,13 @@ class AudioEngine extends EventEmitter {
437
438
  }
438
439
 
439
440
  setAutotuneEnabled(enabled) {
440
- console.log('Auto-tune enabled set to:', enabled);
441
+ log('Auto-tune enabled set to:', enabled);
441
442
  // TODO: Implement actual auto-tune processing
442
443
  return true;
443
444
  }
444
445
 
445
446
  setAutotuneSettings(settings) {
446
- console.log('Auto-tune settings updated:', settings);
447
+ log('Auto-tune settings updated:', settings);
447
448
  // TODO: Implement actual settings application
448
449
  return true;
449
450
  }
@@ -468,7 +469,7 @@ class AudioEngine extends EventEmitter {
468
469
  }
469
470
 
470
471
  this.initialized = false;
471
- console.log('Audio engine stopped');
472
+ log('Audio engine stopped');
472
473
  } catch (error) {
473
474
  console.error('Error stopping audio engine:', error);
474
475
  }
@@ -1,3 +1,4 @@
1
+ import { log } from '../logger.js';
1
2
  /**
2
3
  * Conversion Service - Orchestrates the full karaoke creation pipeline
3
4
  *
@@ -162,7 +163,7 @@ export async function runConversion(
162
163
  // ========================================
163
164
  // LYRICS-ONLY MODE: Skip stem separation
164
165
  // ========================================
165
- console.log('šŸŽ¤ Lyrics-only mode: extracting vocals from existing stem file');
166
+ log('šŸŽ¤ Lyrics-only mode: extracting vocals from existing stem file');
166
167
 
167
168
  // Step 1: Extract vocals track to temp WAV (0-10%)
168
169
  onProgress('extract', `[${STEPS.extract}] Extracting vocals track...`, 0);
@@ -258,7 +259,7 @@ export async function runConversion(
258
259
  checkCancelled();
259
260
 
260
261
  if (initialPrompt) {
261
- console.log(`šŸŽ¤ Whisper prompt: ${initialPrompt}`);
262
+ log(`šŸŽ¤ Whisper prompt: ${initialPrompt}`);
262
263
  }
263
264
 
264
265
  let whisperResult = await runWhisper(
@@ -342,7 +343,7 @@ export async function runConversion(
342
343
  if (pitchData?.pitch_data) {
343
344
  const keyResult = detectKey(pitchData);
344
345
  if (keyResult.key !== 'unknown') {
345
- console.log(
346
+ log(
346
347
  `šŸŽµ Detected key: ${keyResult.key} (confidence: ${(keyResult.confidence * 100).toFixed(0)}%)`
347
348
  );
348
349
  pitchData.detected_key = keyResult;
@@ -1,3 +1,4 @@
1
+ import { log } from '../logger.js';
1
2
  /**
2
3
  * Download Manager - Handles downloading and installing AI components for Creator
3
4
  *
@@ -106,8 +107,8 @@ function getPythonBuildUrl() {
106
107
  // SECURITY FIX (#29): Add max redirects parameter to prevent infinite loops
107
108
  function downloadFile(url, destPath, onProgress = null, maxRedirects = 5) {
108
109
  return new Promise((resolve, reject) => {
109
- console.log(`šŸ“„ Downloading: ${url}`);
110
- console.log(` Destination: ${destPath}`);
110
+ log(`šŸ“„ Downloading: ${url}`);
111
+ log(` Destination: ${destPath}`);
111
112
 
112
113
  let protocol;
113
114
  try {
@@ -125,7 +126,7 @@ function downloadFile(url, destPath, onProgress = null, maxRedirects = 5) {
125
126
  if (!existsSync(dir)) {
126
127
  try {
127
128
  mkdirSync(dir, { recursive: true });
128
- console.log(`āœ… Created directory: ${dir}`);
129
+ log(`āœ… Created directory: ${dir}`);
129
130
  } catch (error) {
130
131
  console.error(`āŒ Failed to create directory: ${dir}`);
131
132
  console.error('Error:', error);
@@ -136,13 +137,13 @@ function downloadFile(url, destPath, onProgress = null, maxRedirects = 5) {
136
137
  }
137
138
 
138
139
  const request = protocol.get(url, (response) => {
139
- console.log(`šŸ“” Response status: ${response.statusCode} ${response.statusMessage}`);
140
- console.log(` Headers:`, JSON.stringify(response.headers, null, 2));
140
+ log(`šŸ“” Response status: ${response.statusCode} ${response.statusMessage}`);
141
+ log(` Headers:`, JSON.stringify(response.headers, null, 2));
141
142
 
142
143
  // Handle redirects
143
144
  if (response.statusCode === 301 || response.statusCode === 302) {
144
145
  const location = response.headers.location;
145
- console.log(`šŸ”€ Redirect to: ${location}`);
146
+ log(`šŸ”€ Redirect to: ${location}`);
146
147
 
147
148
  // SECURITY FIX (#29): Check redirect limit to prevent infinite loops/SSRF
148
149
  if (maxRedirects <= 0) {
@@ -153,8 +154,10 @@ function downloadFile(url, destPath, onProgress = null, maxRedirects = 5) {
153
154
  try {
154
155
  // Handle relative redirects by resolving against original URL
155
156
  const redirectUrl = location.startsWith('http') ? location : new URL(location, url).href;
156
- console.log(`šŸ”€ Resolved redirect URL: ${redirectUrl} (${maxRedirects - 1} redirects remaining)`);
157
- downloadFile(redirectUrl, destPath, onProgress, maxRedirects - 1).then(resolve).catch(reject);
157
+ log(`šŸ”€ Resolved redirect URL: ${redirectUrl} (${maxRedirects - 1} redirects remaining)`);
158
+ downloadFile(redirectUrl, destPath, onProgress, maxRedirects - 1)
159
+ .then(resolve)
160
+ .catch(reject);
158
161
  } catch (error) {
159
162
  console.error(`āŒ Failed to resolve redirect URL`);
160
163
  console.error('Original URL:', url);
@@ -176,7 +179,7 @@ function downloadFile(url, destPath, onProgress = null, maxRedirects = 5) {
176
179
  }
177
180
 
178
181
  const totalBytes = parseInt(response.headers['content-length'] || '0', 10);
179
- console.log(`šŸ“¦ Content length: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`);
182
+ log(`šŸ“¦ Content length: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`);
180
183
  let downloadedBytes = 0;
181
184
  let lastLoggedPercent = -1;
182
185
 
@@ -191,7 +194,7 @@ function downloadFile(url, destPath, onProgress = null, maxRedirects = 5) {
191
194
 
192
195
  // Log progress every 10%
193
196
  if (percent >= lastLoggedPercent + 10 || percent === 100) {
194
- console.log(
197
+ log(
195
198
  ` Progress: ${percent}% (${(downloadedBytes / 1024 / 1024).toFixed(2)} / ${(totalBytes / 1024 / 1024).toFixed(2)} MB)`
196
199
  );
197
200
  lastLoggedPercent = percent;
@@ -209,8 +212,8 @@ function downloadFile(url, destPath, onProgress = null, maxRedirects = 5) {
209
212
 
210
213
  fileStream.on('finish', () => {
211
214
  fileStream.close();
212
- console.log(`āœ… Download complete: ${destPath}`);
213
- console.log(` Size: ${(downloadedBytes / 1024 / 1024).toFixed(2)} MB`);
215
+ log(`āœ… Download complete: ${destPath}`);
216
+ log(` Size: ${(downloadedBytes / 1024 / 1024).toFixed(2)} MB`);
214
217
  resolve();
215
218
  });
216
219
 
@@ -391,67 +394,67 @@ function detectGPU() {
391
394
  * Download and install Python
392
395
  */
393
396
  export async function downloadPython(onProgress = null) {
394
- console.log('šŸ Starting Python installation...');
397
+ log('šŸ Starting Python installation...');
395
398
  const cacheDir = getCacheDir();
396
399
  const pythonDir = join(cacheDir, 'python');
397
- console.log(` Cache dir: ${cacheDir}`);
398
- console.log(` Python dir: ${pythonDir}`);
400
+ log(` Cache dir: ${cacheDir}`);
401
+ log(` Python dir: ${pythonDir}`);
399
402
 
400
403
  // Check if already installed
401
404
  const pythonPath = getPythonPath();
402
- console.log(` Checking for existing Python: ${pythonPath}`);
405
+ log(` Checking for existing Python: ${pythonPath}`);
403
406
  if (existsSync(pythonPath)) {
404
- console.log('āœ… Python already installed');
407
+ log('āœ… Python already installed');
405
408
  if (onProgress) onProgress('complete', 'Python already installed');
406
409
  return { success: true, path: pythonPath };
407
410
  }
408
411
 
409
412
  try {
410
413
  const url = getPythonBuildUrl();
411
- console.log(`🌐 Python download URL: ${url}`);
414
+ log(`🌐 Python download URL: ${url}`);
412
415
  const tarPath = join(cacheDir, 'python.tar.gz');
413
416
 
414
417
  // Download
415
- console.log('šŸ“„ Starting Python download...');
418
+ log('šŸ“„ Starting Python download...');
416
419
  if (onProgress) onProgress('downloading', 'Downloading Python...');
417
420
  await downloadFile(url, tarPath, (percent) => {
418
421
  if (onProgress) onProgress('downloading', `Downloading Python... ${percent}%`);
419
422
  });
420
423
 
421
424
  // Extract
422
- console.log('šŸ“¦ Extracting Python...');
425
+ log('šŸ“¦ Extracting Python...');
423
426
  if (onProgress) onProgress('extracting', 'Extracting Python...');
424
427
 
425
428
  // Create python directory
426
429
  if (!existsSync(pythonDir)) {
427
- console.log(` Creating Python directory: ${pythonDir}`);
430
+ log(` Creating Python directory: ${pythonDir}`);
428
431
  mkdirSync(pythonDir, { recursive: true });
429
432
  }
430
433
 
431
434
  // Use tar to extract (available on all platforms)
432
- console.log(' Loading tar module...');
435
+ log(' Loading tar module...');
433
436
  const tar = await import('tar');
434
- console.log(' Extracting tarball...');
437
+ log(' Extracting tarball...');
435
438
  await tar.extract({
436
439
  file: tarPath,
437
440
  cwd: pythonDir,
438
441
  strip: 1,
439
442
  });
440
- console.log('āœ… Extraction complete');
443
+ log('āœ… Extraction complete');
441
444
 
442
445
  // Remove quarantine on macOS
443
446
  if (process.platform === 'darwin') {
444
- console.log('šŸŽ Removing macOS quarantine attributes...');
447
+ log('šŸŽ Removing macOS quarantine attributes...');
445
448
  try {
446
449
  execSync(`xattr -cr "${pythonDir}"`, { stdio: 'ignore' });
447
- console.log('āœ… Quarantine removed');
450
+ log('āœ… Quarantine removed');
448
451
  } catch (error) {
449
452
  console.warn('āš ļø Failed to remove quarantine (non-fatal):', error.message);
450
453
  }
451
454
  }
452
455
 
453
456
  // Clean up tarball
454
- console.log('🧹 Cleaning up tarball...');
457
+ log('🧹 Cleaning up tarball...');
455
458
  rmSync(tarPath, { force: true });
456
459
 
457
460
  // Upgrade pip and setuptools, fix common conflicts
@@ -465,7 +468,7 @@ export async function downloadPython(onProgress = null) {
465
468
  // Non-fatal - coverage may not be installed
466
469
  }
467
470
 
468
- console.log('āœ… Python installation complete');
471
+ log('āœ… Python installation complete');
469
472
  if (onProgress) onProgress('complete', 'Python installed successfully');
470
473
  return { success: true, path: pythonPath };
471
474
  } catch (error) {
@@ -807,14 +810,14 @@ except Exception as e:
807
810
  * Download FFmpeg binary
808
811
  */
809
812
  export async function downloadFFmpeg(onProgress = null) {
810
- console.log('šŸŽ¬ Starting FFmpeg installation...');
813
+ log('šŸŽ¬ Starting FFmpeg installation...');
811
814
  const cacheDir = getCacheDir();
812
815
  const binDir = join(cacheDir, 'bin');
813
- console.log(` Binary dir: ${binDir}`);
816
+ log(` Binary dir: ${binDir}`);
814
817
 
815
818
  if (!existsSync(binDir)) {
816
819
  mkdirSync(binDir, { recursive: true });
817
- console.log(` Created binary directory`);
820
+ log(` Created binary directory`);
818
821
  }
819
822
 
820
823
  const plat = process.platform;
@@ -822,13 +825,13 @@ export async function downloadFFmpeg(onProgress = null) {
822
825
  const ffprobeName = plat === 'win32' ? 'ffprobe.exe' : 'ffprobe';
823
826
  const ffmpegPath = join(binDir, ffmpegName);
824
827
  const ffprobePath = join(binDir, ffprobeName);
825
- console.log(` Platform: ${plat}`);
826
- console.log(` FFmpeg path: ${ffmpegPath}`);
827
- console.log(` FFprobe path: ${ffprobePath}`);
828
+ log(` Platform: ${plat}`);
829
+ log(` FFmpeg path: ${ffmpegPath}`);
830
+ log(` FFprobe path: ${ffprobePath}`);
828
831
 
829
832
  // Check if both already exist
830
833
  if (existsSync(ffmpegPath) && existsSync(ffprobePath)) {
831
- console.log('āœ… FFmpeg and FFprobe already installed');
834
+ log('āœ… FFmpeg and FFprobe already installed');
832
835
  if (onProgress) onProgress('complete', 'FFmpeg already downloaded');
833
836
  return { success: true, ffmpegPath, ffprobePath };
834
837
  }
@@ -849,16 +852,16 @@ export async function downloadFFmpeg(onProgress = null) {
849
852
  // John Van Sickle builds include both ffmpeg and ffprobe
850
853
  url = 'https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz';
851
854
  }
852
- console.log(`🌐 FFmpeg download URL: ${url}`);
855
+ log(`🌐 FFmpeg download URL: ${url}`);
853
856
  if (ffprobeUrl) {
854
- console.log(`🌐 FFprobe download URL: ${ffprobeUrl}`);
857
+ log(`🌐 FFprobe download URL: ${ffprobeUrl}`);
855
858
  }
856
859
 
857
860
  const archivePath = join(binDir, plat === 'linux' ? 'ffmpeg.tar.xz' : 'ffmpeg.zip');
858
- console.log(` Archive path: ${archivePath}`);
861
+ log(` Archive path: ${archivePath}`);
859
862
 
860
863
  // Download ffmpeg
861
- console.log('šŸ“„ Starting FFmpeg download...');
864
+ log('šŸ“„ Starting FFmpeg download...');
862
865
  if (onProgress) onProgress('downloading', 'Downloading FFmpeg...');
863
866
  await downloadFile(url, archivePath, (percent) => {
864
867
  if (onProgress) onProgress('downloading', `Downloading FFmpeg... ${percent}%`);
@@ -868,7 +871,7 @@ export async function downloadFFmpeg(onProgress = null) {
868
871
  let ffprobeArchivePath = null;
869
872
  if (ffprobeUrl) {
870
873
  ffprobeArchivePath = join(binDir, 'ffprobe.zip');
871
- console.log('šŸ“„ Starting FFprobe download...');
874
+ log('šŸ“„ Starting FFprobe download...');
872
875
  if (onProgress) onProgress('downloading', 'Downloading FFprobe...');
873
876
  await downloadFile(ffprobeUrl, ffprobeArchivePath, (percent) => {
874
877
  if (onProgress) onProgress('downloading', `Downloading FFprobe... ${percent}%`);
@@ -876,7 +879,7 @@ export async function downloadFFmpeg(onProgress = null) {
876
879
  }
877
880
 
878
881
  // Extract
879
- console.log('šŸ“¦ Extracting FFmpeg...');
882
+ log('šŸ“¦ Extracting FFmpeg...');
880
883
  if (onProgress) onProgress('extracting', 'Extracting FFmpeg...');
881
884
 
882
885
  // Extract and find ffmpeg binary
@@ -885,15 +888,15 @@ export async function downloadFFmpeg(onProgress = null) {
885
888
  const tempDir = mkdtempSync(join(tmpdir(), 'ffmpeg-'));
886
889
 
887
890
  try {
888
- console.log(` Extracting to temp dir: ${tempDir}`);
891
+ log(` Extracting to temp dir: ${tempDir}`);
889
892
  if (plat === 'linux') {
890
- console.log(' Using tar to extract...');
893
+ log(' Using tar to extract...');
891
894
  execSync(`tar -xf "${archivePath}" -C "${tempDir}"`);
892
895
  } else {
893
- console.log(' Using yauzl to extract...');
896
+ log(' Using yauzl to extract...');
894
897
  await extractZip(archivePath, tempDir);
895
898
  }
896
- console.log(' Extraction complete, searching for binary...');
899
+ log(' Extraction complete, searching for binary...');
897
900
 
898
901
  // Find binary recursively by name
899
902
  const findBinary = (dir, name) => {
@@ -923,13 +926,13 @@ export async function downloadFFmpeg(onProgress = null) {
923
926
 
924
927
  if (ffmpegFound) {
925
928
  const ffmpegDest = join(binDir, ffmpegName);
926
- console.log(` Found ffmpeg: ${ffmpegFound}`);
927
- console.log(` Copying to: ${ffmpegDest}`);
929
+ log(` Found ffmpeg: ${ffmpegFound}`);
930
+ log(` Copying to: ${ffmpegDest}`);
928
931
  copyFileSync(ffmpegFound, ffmpegDest);
929
932
  if (plat !== 'win32') {
930
933
  chmodSync(ffmpegDest, 0o755);
931
934
  }
932
- console.log('āœ… ffmpeg binary installed');
935
+ log('āœ… ffmpeg binary installed');
933
936
  } else {
934
937
  console.error('āŒ ffmpeg binary not found in archive');
935
938
  console.error(` Searched in: ${tempDir}`);
@@ -937,26 +940,26 @@ export async function downloadFFmpeg(onProgress = null) {
937
940
  }
938
941
 
939
942
  if (ffprobeFound) {
940
- console.log(` Found ffprobe: ${ffprobeFound}`);
941
- console.log(` Copying to: ${ffprobePath}`);
943
+ log(` Found ffprobe: ${ffprobeFound}`);
944
+ log(` Copying to: ${ffprobePath}`);
942
945
  copyFileSync(ffprobeFound, ffprobePath);
943
946
  if (plat !== 'win32') {
944
947
  chmodSync(ffprobePath, 0o755);
945
948
  }
946
- console.log('āœ… ffprobe binary installed');
949
+ log('āœ… ffprobe binary installed');
947
950
  } else if (ffprobeArchivePath) {
948
951
  // macOS: Extract ffprobe from separate archive
949
- console.log('šŸ“¦ Extracting FFprobe from separate archive...');
952
+ log('šŸ“¦ Extracting FFprobe from separate archive...');
950
953
  const ffprobeTempDir = mkdtempSync(join(tmpdir(), 'ffprobe-'));
951
954
  try {
952
955
  await extractZip(ffprobeArchivePath, ffprobeTempDir);
953
956
  const ffprobeExtracted = findBinary(ffprobeTempDir, ffprobeName);
954
957
  if (ffprobeExtracted) {
955
- console.log(` Found ffprobe: ${ffprobeExtracted}`);
956
- console.log(` Copying to: ${ffprobePath}`);
958
+ log(` Found ffprobe: ${ffprobeExtracted}`);
959
+ log(` Copying to: ${ffprobePath}`);
957
960
  copyFileSync(ffprobeExtracted, ffprobePath);
958
961
  chmodSync(ffprobePath, 0o755);
959
- console.log('āœ… ffprobe binary installed');
962
+ log('āœ… ffprobe binary installed');
960
963
  } else {
961
964
  console.warn('āš ļø ffprobe not found in separate archive');
962
965
  }
@@ -971,11 +974,11 @@ export async function downloadFFmpeg(onProgress = null) {
971
974
  }
972
975
 
973
976
  // Clean up
974
- console.log('🧹 Cleaning up temporary files...');
977
+ log('🧹 Cleaning up temporary files...');
975
978
  rmSync(tempDir, { recursive: true, force: true });
976
979
  rmSync(archivePath, { force: true });
977
980
 
978
- console.log('āœ… FFmpeg installation complete');
981
+ log('āœ… FFmpeg installation complete');
979
982
  if (onProgress) onProgress('complete', 'FFmpeg installed');
980
983
  return { success: true, ffmpegPath, ffprobePath };
981
984
  } catch (extractError) {
@@ -997,9 +1000,9 @@ export async function downloadFFmpeg(onProgress = null) {
997
1000
  * Install all components in order
998
1001
  */
999
1002
  export async function installAllComponents(onProgress = null) {
1000
- console.log('šŸš€ Starting installation of all components...');
1001
- console.log(` Platform: ${process.platform}`);
1002
- console.log(` Architecture: ${process.arch}`);
1003
+ log('šŸš€ Starting installation of all components...');
1004
+ log(` Platform: ${process.platform}`);
1005
+ log(` Architecture: ${process.arch}`);
1003
1006
 
1004
1007
  const results = {};
1005
1008
 
@@ -1036,9 +1039,9 @@ export async function installAllComponents(onProgress = null) {
1036
1039
  },
1037
1040
  ];
1038
1041
 
1039
- console.log(`šŸ“‹ Installation plan: ${steps.length} components`);
1042
+ log(`šŸ“‹ Installation plan: ${steps.length} components`);
1040
1043
  steps.forEach((s, i) => {
1041
- console.log(` ${i + 1}. ${s.label} (${s.size})`);
1044
+ log(` ${i + 1}. ${s.label} (${s.size})`);
1042
1045
  });
1043
1046
 
1044
1047
  let completedWeight = 0;
@@ -1050,7 +1053,7 @@ export async function installAllComponents(onProgress = null) {
1050
1053
  const totalSteps = steps.length;
1051
1054
  const action = step.action || 'Installing';
1052
1055
 
1053
- console.log(`\nšŸ“¦ [${stepNumber}/${totalSteps}] ${action} ${step.label}...`);
1056
+ log(`\nšŸ“¦ [${stepNumber}/${totalSteps}] ${action} ${step.label}...`);
1054
1057
 
1055
1058
  if (onProgress) {
1056
1059
  const percent = Math.floor((completedWeight / totalWeight) * 100);
@@ -1094,7 +1097,7 @@ export async function installAllComponents(onProgress = null) {
1094
1097
  return { success: false, failed: step.name, error: result.error, results };
1095
1098
  }
1096
1099
 
1097
- console.log(`āœ… [${stepNumber}/${totalSteps}] ${step.label} installed successfully`);
1100
+ log(`āœ… [${stepNumber}/${totalSteps}] ${step.label} installed successfully`);
1098
1101
 
1099
1102
  completedWeight += step.weight;
1100
1103
 
@@ -1104,10 +1107,10 @@ export async function installAllComponents(onProgress = null) {
1104
1107
  }
1105
1108
  }
1106
1109
 
1107
- console.log('\nšŸŽ‰ All components installed successfully!');
1108
- console.log('Installation results:');
1110
+ log('\nšŸŽ‰ All components installed successfully!');
1111
+ log('Installation results:');
1109
1112
  Object.entries(results).forEach(([name, result]) => {
1110
- console.log(` ${result.success ? 'āœ…' : 'āŒ'} ${name}`);
1113
+ log(` ${result.success ? 'āœ…' : 'āŒ'} ${name}`);
1111
1114
  });
1112
1115
 
1113
1116
  if (onProgress) onProgress(100, 'All components installed successfully!');
@@ -1,3 +1,4 @@
1
+ import { log } from '../logger.js';
1
2
  /**
2
3
  * FFmpeg Service - Audio conversion and processing
3
4
  *
@@ -451,7 +452,7 @@ export function extractStemTrack(inputPath, outputPath, trackIndex, options = {}
451
452
  outputPath,
452
453
  ];
453
454
 
454
- console.log(`šŸŽ¤ Extracting audio track ${trackIndex} to WAV...`);
455
+ log(`šŸŽ¤ Extracting audio track ${trackIndex} to WAV...`);
455
456
  const proc = spawn(ffmpeg, args, { timeout: 300000 }); // 5 min timeout
456
457
 
457
458
  let stderr = '';
@@ -465,7 +466,7 @@ export function extractStemTrack(inputPath, outputPath, trackIndex, options = {}
465
466
  reject(new Error(`Track extraction failed: ${stderr.slice(-500)}`));
466
467
  return;
467
468
  }
468
- console.log(`āœ… Track ${trackIndex} extracted successfully`);
469
+ log(`āœ… Track ${trackIndex} extracted successfully`);
469
470
  resolve();
470
471
  });
471
472
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Installation Logger - Intercepts console.log and sends to UI
2
+ * Installation Logger - Intercepts log and sends to UI
3
3
  */
4
4
 
5
5
  let logCallback = null;
@@ -19,13 +19,11 @@ export function clearLogCallback() {
19
19
  }
20
20
 
21
21
  /**
22
- * Log a message (sends to console AND callback)
22
+ * Log a message (sends to callback for UI display)
23
+ * Note: In production mode, console output is suppressed by the main logger
23
24
  */
24
25
  export function log(...args) {
25
- // Always log to console
26
- console.log(...args);
27
-
28
- // Also send to UI if callback is set
26
+ // Send to UI if callback is set
29
27
  if (logCallback) {
30
28
  const message = args
31
29
  .map((arg) => (typeof arg === 'object' ? JSON.stringify(arg) : String(arg)))
@@ -38,7 +36,7 @@ export function log(...args) {
38
36
  * Log an error (sends to console AND callback)
39
37
  */
40
38
  export function error(...args) {
41
- // Always log to console
39
+ // Always log errors to console
42
40
  console.error(...args);
43
41
 
44
42
  // Also send to UI if callback is set
@@ -1,3 +1,4 @@
1
+ import { log } from '../logger.js';
1
2
  /**
2
3
  * LLM Service for lyrics correction
3
4
  * Supports OpenAI, Anthropic Claude, Google Gemini, and local LM Studio
@@ -49,7 +50,7 @@ function getLLMProvider(settings) {
49
50
  */
50
51
  export async function correctLyrics(whisperOutput, referenceLyrics, settings) {
51
52
  if (!settings.enabled) {
52
- console.log('šŸ¤– LLM correction disabled, using Whisper output as-is');
53
+ log('šŸ¤– LLM correction disabled, using Whisper output as-is');
53
54
  return {
54
55
  output: whisperOutput,
55
56
  stats: null,
@@ -57,14 +58,14 @@ export async function correctLyrics(whisperOutput, referenceLyrics, settings) {
57
58
  }
58
59
 
59
60
  if (!referenceLyrics || !referenceLyrics.trim()) {
60
- console.log('šŸ¤– No reference lyrics provided, skipping LLM correction');
61
+ log('šŸ¤– No reference lyrics provided, skipping LLM correction');
61
62
  return {
62
63
  output: whisperOutput,
63
64
  stats: null,
64
65
  };
65
66
  }
66
67
 
67
- console.log(`šŸ¤– Starting LLM lyrics correction with ${settings.provider}...`);
68
+ log(`šŸ¤– Starting LLM lyrics correction with ${settings.provider}...`);
68
69
 
69
70
  try {
70
71
  const provider = getLLMProvider(settings);
@@ -77,9 +78,9 @@ export async function correctLyrics(whisperOutput, referenceLyrics, settings) {
77
78
  missingLines,
78
79
  } = parseCorrection(llmResponse, whisperOutput);
79
80
 
80
- console.log(`āœ… LLM correction complete (${corrections.length} lines changed)`);
81
+ log(`āœ… LLM correction complete (${corrections.length} lines changed)`);
81
82
  if (missingLines.length > 0) {
82
- console.log(`šŸ“ LLM suggested ${missingLines.length} missing lines`);
83
+ log(`šŸ“ LLM suggested ${missingLines.length} missing lines`);
83
84
  }
84
85
 
85
86
  return {
@@ -101,7 +102,7 @@ export async function correctLyrics(whisperOutput, referenceLyrics, settings) {
101
102
  };
102
103
  } catch (error) {
103
104
  console.error('āŒ LLM correction failed:', error.message);
104
- console.log('āš ļø Falling back to original Whisper output');
105
+ log('āš ļø Falling back to original Whisper output');
105
106
  return {
106
107
  output: whisperOutput,
107
108
  stats: {
@@ -304,7 +305,12 @@ export function getLLMSettings(settingsManager) {
304
305
  const apiKey = llmConfig.apiKey || LLM_DEFAULTS.apiKey;
305
306
 
306
307
  // SECURITY FIX (#25): Mask API key - only show last 4 chars to renderer
307
- const maskedApiKey = apiKey && apiKey.length > 8 ? `${'•'.repeat(apiKey.length - 4)}${apiKey.slice(-4)}` : apiKey ? '••••••••' : '';
308
+ const maskedApiKey =
309
+ apiKey && apiKey.length > 8
310
+ ? `${'•'.repeat(apiKey.length - 4)}${apiKey.slice(-4)}`
311
+ : apiKey
312
+ ? '••••••••'
313
+ : '';
308
314
 
309
315
  return {
310
316
  enabled: llmConfig.enabled ?? LLM_DEFAULTS.enabled,