frontend: prevent overlapping per-line playback; backend: print idle eviction settings on startup\n\n- app.js: add shared Audio state, disable play button while playing, stop previous line when new one plays\n- start_server.py: print eviction enabled/timeout/check interval\n- app/main.py: log eviction settings during FastAPI startup
This commit is contained in:
parent
cbc164c7a3
commit
c9593fe6cc
|
@ -68,6 +68,14 @@ async def _start_model_reaper():
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("Model reaper encountered an error")
|
logger.exception("Model reaper encountered an error")
|
||||||
|
|
||||||
|
# Log eviction configuration at startup
|
||||||
|
logger.info(
|
||||||
|
"Model Eviction -> enabled: %s | idle_timeout: %ss | check_interval: %ss",
|
||||||
|
getattr(config, "MODEL_EVICTION_ENABLED", True),
|
||||||
|
getattr(config, "MODEL_IDLE_TIMEOUT_SECONDS", 0),
|
||||||
|
getattr(config, "MODEL_IDLE_CHECK_INTERVAL_SECONDS", 60),
|
||||||
|
)
|
||||||
|
|
||||||
app.state._model_reaper_task = asyncio.create_task(reaper())
|
app.state._model_reaper_task = asyncio.create_task(reaper())
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,14 @@ if __name__ == "__main__":
|
||||||
print(f"CORS Origins: {config.CORS_ORIGINS}")
|
print(f"CORS Origins: {config.CORS_ORIGINS}")
|
||||||
print(f"Project Root: {config.PROJECT_ROOT}")
|
print(f"Project Root: {config.PROJECT_ROOT}")
|
||||||
print(f"Device: {config.DEVICE}")
|
print(f"Device: {config.DEVICE}")
|
||||||
|
# Idle eviction settings
|
||||||
|
print(
|
||||||
|
"Model Eviction -> enabled: {} | idle_timeout: {}s | check_interval: {}s".format(
|
||||||
|
getattr(config, "MODEL_EVICTION_ENABLED", True),
|
||||||
|
getattr(config, "MODEL_IDLE_TIMEOUT_SECONDS", 0),
|
||||||
|
getattr(config, "MODEL_IDLE_CHECK_INTERVAL_SECONDS", 60),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
uvicorn.run(
|
uvicorn.run(
|
||||||
"app.main:app",
|
"app.main:app",
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import { getSpeakers, addSpeaker, deleteSpeaker, generateDialog } from './api.js';
|
import { getSpeakers, addSpeaker, deleteSpeaker, generateDialog } from './api.js';
|
||||||
import { API_BASE_URL, API_BASE_URL_FOR_FILES } from './config.js';
|
import { API_BASE_URL, API_BASE_URL_FOR_FILES } from './config.js';
|
||||||
|
|
||||||
|
// Shared per-line audio playback state to prevent overlapping playback
|
||||||
|
let currentLineAudio = null;
|
||||||
|
let currentLineAudioBtn = null;
|
||||||
|
|
||||||
// --- Global Inline Notification Helpers --- //
|
// --- Global Inline Notification Helpers --- //
|
||||||
const noticeEl = document.getElementById('global-notice');
|
const noticeEl = document.getElementById('global-notice');
|
||||||
const noticeContentEl = document.getElementById('global-notice-content');
|
const noticeContentEl = document.getElementById('global-notice-content');
|
||||||
|
@ -399,10 +403,46 @@ async function initializeDialogEditor() {
|
||||||
playBtn.disabled = !item.audioUrl;
|
playBtn.disabled = !item.audioUrl;
|
||||||
playBtn.onclick = () => {
|
playBtn.onclick = () => {
|
||||||
if (!item.audioUrl) return;
|
if (!item.audioUrl) return;
|
||||||
let audioUrl = item.audioUrl.startsWith('http') ? item.audioUrl : `${API_BASE_URL_FOR_FILES}${item.audioUrl}`;
|
const audioUrl = item.audioUrl.startsWith('http') ? item.audioUrl : `${API_BASE_URL_FOR_FILES}${item.audioUrl}`;
|
||||||
// Use a shared audio element or create one per play
|
|
||||||
let audio = new window.Audio(audioUrl);
|
// If something is already playing
|
||||||
audio.play();
|
if (currentLineAudio && !currentLineAudio.paused) {
|
||||||
|
if (currentLineAudioBtn === playBtn) {
|
||||||
|
// Same line: ignore click to prevent overlapping
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Stop previous audio and re-enable its button
|
||||||
|
try {
|
||||||
|
currentLineAudio.pause();
|
||||||
|
currentLineAudio.currentTime = 0;
|
||||||
|
} catch (e) { /* noop */ }
|
||||||
|
if (currentLineAudioBtn) {
|
||||||
|
try { currentLineAudioBtn.disabled = false; } catch (e) { /* detached */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const audio = new window.Audio(audioUrl);
|
||||||
|
currentLineAudio = audio;
|
||||||
|
currentLineAudioBtn = playBtn;
|
||||||
|
// Disable this play button while playing
|
||||||
|
playBtn.disabled = true;
|
||||||
|
|
||||||
|
const clearState = () => {
|
||||||
|
if (currentLineAudio === audio) {
|
||||||
|
currentLineAudio = null;
|
||||||
|
currentLineAudioBtn = null;
|
||||||
|
}
|
||||||
|
try { playBtn.disabled = false; } catch (e) { /* detached */ }
|
||||||
|
};
|
||||||
|
|
||||||
|
audio.addEventListener('ended', clearState, { once: true });
|
||||||
|
audio.addEventListener('error', clearState, { once: true });
|
||||||
|
|
||||||
|
audio.play().catch(err => {
|
||||||
|
console.error('Audio play failed:', err);
|
||||||
|
clearState();
|
||||||
|
showNotice('Could not play audio.', 'error', { timeout: 2000 });
|
||||||
|
});
|
||||||
};
|
};
|
||||||
actionsTd.appendChild(playBtn);
|
actionsTd.appendChild(playBtn);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue