Compare commits
3 Commits
0defc829dd
...
6112799c79
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6112799c79 | ||
|
|
9a6ca5730b | ||
|
|
c05fdad8e4 |
@@ -288,9 +288,12 @@ function createWindow() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function startSidecar() {
|
function startSidecar() {
|
||||||
|
console.log("=== startSidecar() called ===");
|
||||||
const sidecarDir = app.isPackaged
|
const sidecarDir = app.isPackaged
|
||||||
? path.join(process.resourcesPath, "sidecar")
|
? path.join(process.resourcesPath, "sidecar")
|
||||||
: path.join(__dirname, "..", "..", "sidecar");
|
: path.join(__dirname, "..", "..", "sidecar");
|
||||||
|
console.log("Sidecar directory:", sidecarDir);
|
||||||
|
console.log("App is packaged:", app.isPackaged);
|
||||||
|
|
||||||
// Determine the sidecar executable path based on packaging and platform
|
// Determine the sidecar executable path based on packaging and platform
|
||||||
let sidecarExecutable;
|
let sidecarExecutable;
|
||||||
@@ -327,11 +330,13 @@ function startSidecar() {
|
|||||||
sidecarArgs = [sidecarScript];
|
sidecarArgs = [sidecarScript];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("Checking sidecar executable at:", sidecarExecutable);
|
||||||
if (!fs.existsSync(sidecarExecutable)) {
|
if (!fs.existsSync(sidecarExecutable)) {
|
||||||
console.log("Sidecar executable not found at:", sidecarExecutable);
|
console.log("ERROR: Sidecar executable not found at:", sidecarExecutable);
|
||||||
console.log("Transcription will not be available.");
|
console.log("Transcription will not be available.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
console.log("Sidecar executable found:", sidecarExecutable);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get Whisper configuration from config.json or environment variables
|
// Get Whisper configuration from config.json or environment variables
|
||||||
@@ -434,9 +439,15 @@ function startSidecar() {
|
|||||||
mainWindow.webContents.send("model-download-progress", msg);
|
mainWindow.webContents.send("model-download-progress", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward model error status
|
// Forward model error status and mark sidecar as not ready
|
||||||
if (msg.status === "model_error" && mainWindow) {
|
if (msg.status === "model_error") {
|
||||||
mainWindow.webContents.send("model-download-progress", msg);
|
sidecarReady = false;
|
||||||
|
if (activeWhisperConfig) {
|
||||||
|
activeWhisperConfig.error = msg.error || "Model load failed";
|
||||||
|
}
|
||||||
|
if (mainWindow) {
|
||||||
|
mainWindow.webContents.send("model-download-progress", msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Sidecar output:", line);
|
console.log("Sidecar output:", line);
|
||||||
|
|||||||
@@ -289,10 +289,17 @@
|
|||||||
try {
|
try {
|
||||||
const status = await window.electronAPI.getSidecarStatus();
|
const status = await window.electronAPI.getSidecarStatus();
|
||||||
if (status.whisper) {
|
if (status.whisper) {
|
||||||
const readyIcon = status.ready ? '✅' : '⏳';
|
// Check if there was an error loading the model
|
||||||
whisperStatusEl.textContent = `${readyIcon} Model: ${status.whisper.model} | Device: ${status.whisper.device} | Compute: ${status.whisper.compute}`;
|
if (status.whisper.error) {
|
||||||
whisperStatusEl.title = `Config source: ${status.whisper.configSource || 'unknown'}`;
|
whisperStatusEl.textContent = `❌ Model error: ${status.whisper.error}`;
|
||||||
whisperStatusEl.style.color = status.ready ? '#28a745' : '#ffc107';
|
whisperStatusEl.style.color = '#dc3545';
|
||||||
|
whisperStatusEl.title = 'Model failed to load';
|
||||||
|
} else {
|
||||||
|
const readyIcon = status.ready ? '✅' : '⏳';
|
||||||
|
whisperStatusEl.textContent = `${readyIcon} Model: ${status.whisper.model} | Device: ${status.whisper.device} | Compute: ${status.whisper.compute}`;
|
||||||
|
whisperStatusEl.title = `Config source: ${status.whisper.configSource || 'unknown'}`;
|
||||||
|
whisperStatusEl.style.color = status.ready ? '#28a745' : '#ffc107';
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
whisperStatusEl.textContent = status.ready ? '✅ Ready' : '⏳ Loading...';
|
whisperStatusEl.textContent = status.ready ? '✅ Ready' : '⏳ Loading...';
|
||||||
whisperStatusEl.style.color = status.ready ? '#28a745' : '#ffc107';
|
whisperStatusEl.style.color = status.ready ? '#28a745' : '#ffc107';
|
||||||
@@ -414,6 +421,9 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Debug: Check if we're in a secure context
|
||||||
|
console.log('isSecureContext:', window.isSecureContext);
|
||||||
|
|
||||||
// Check for available audio devices first
|
// Check for available audio devices first
|
||||||
const devices = await navigator.mediaDevices.enumerateDevices();
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||||
const audioInputs = devices.filter(d => d.kind === 'audioinput');
|
const audioInputs = devices.filter(d => d.kind === 'audioinput');
|
||||||
@@ -424,40 +434,56 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper to identify alias deviceIds (not real device IDs)
|
||||||
|
const isAlias = (id) => id === 'default' || id === 'communications';
|
||||||
|
|
||||||
// Filter out Stereo Mix (立體聲混音) - it's not a real microphone
|
// Filter out Stereo Mix (立體聲混音) - it's not a real microphone
|
||||||
const realMicrophones = audioInputs.filter(d =>
|
const realMicrophones = audioInputs.filter(d =>
|
||||||
!d.label.includes('立體聲混音') &&
|
!d.label.includes('立體聲混音') &&
|
||||||
!d.label.toLowerCase().includes('stereo mix') &&
|
!d.label.toLowerCase().includes('stereo mix')
|
||||||
d.deviceId !== 'default' // Skip default which might be Stereo Mix
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Prefer communications device or actual microphone
|
// Prefer actual device IDs (long strings), not aliases like 'default' or 'communications'
|
||||||
|
// First try to find a real microphone with actual deviceId
|
||||||
let selectedMic = realMicrophones.find(d =>
|
let selectedMic = realMicrophones.find(d =>
|
||||||
d.deviceId === 'communications' ||
|
!isAlias(d.deviceId) && (
|
||||||
d.label.includes('麥克風') ||
|
d.label.includes('麥克風') ||
|
||||||
d.label.toLowerCase().includes('microphone')
|
d.label.toLowerCase().includes('microphone')
|
||||||
) || realMicrophones[0];
|
)
|
||||||
|
) || realMicrophones.find(d => !isAlias(d.deviceId)) || realMicrophones[0];
|
||||||
|
|
||||||
console.log('Real microphones found:', realMicrophones.length);
|
console.log('Real microphones found:', realMicrophones.length);
|
||||||
console.log('Selected microphone:', selectedMic);
|
console.log('Selected microphone:', selectedMic);
|
||||||
|
console.log('Selected deviceId is alias:', selectedMic ? isAlias(selectedMic.deviceId) : 'N/A');
|
||||||
|
|
||||||
if (!selectedMic) {
|
if (!selectedMic) {
|
||||||
alert('No real microphone found. Only Stereo Mix detected. Please connect a microphone.');
|
alert('No real microphone found. Only Stereo Mix detected. Please connect a microphone.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try with the selected microphone
|
// Get microphone stream with proper constraints handling
|
||||||
try {
|
try {
|
||||||
mediaStream = await navigator.mediaDevices.getUserMedia({
|
if (isAlias(selectedMic.deviceId)) {
|
||||||
audio: { deviceId: { exact: selectedMic.deviceId } }
|
// For alias deviceIds (default/communications), let the system choose
|
||||||
});
|
console.log('Using system default (alias detected)');
|
||||||
|
mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||||
|
} else {
|
||||||
|
// For real deviceIds, try exact first, then ideal as fallback
|
||||||
|
try {
|
||||||
|
mediaStream = await navigator.mediaDevices.getUserMedia({
|
||||||
|
audio: { deviceId: { exact: selectedMic.deviceId } }
|
||||||
|
});
|
||||||
|
} catch (exactErr) {
|
||||||
|
console.warn('Exact device ID failed, trying ideal:', exactErr);
|
||||||
|
mediaStream = await navigator.mediaDevices.getUserMedia({
|
||||||
|
audio: { deviceId: { ideal: selectedMic.deviceId } }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
console.log('Successfully connected to:', selectedMic.label);
|
console.log('Successfully connected to:', selectedMic.label);
|
||||||
} catch (exactErr) {
|
} catch (err) {
|
||||||
console.warn('Exact device ID failed, trying preferred:', exactErr);
|
console.error('getUserMedia failed:', err.name, err.message);
|
||||||
// Fallback: try with preferred instead of exact
|
throw err; // Let outer catch handle the error message
|
||||||
mediaStream = await navigator.mediaDevices.getUserMedia({
|
|
||||||
audio: { deviceId: selectedMic.deviceId }
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isRecording = true;
|
isRecording = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user