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:
|
||||
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())
|
||||
|
||||
|
||||
|
|
|
@ -14,6 +14,14 @@ if __name__ == "__main__":
|
|||
print(f"CORS Origins: {config.CORS_ORIGINS}")
|
||||
print(f"Project Root: {config.PROJECT_ROOT}")
|
||||
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(
|
||||
"app.main:app",
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import { getSpeakers, addSpeaker, deleteSpeaker, generateDialog } from './api.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 --- //
|
||||
const noticeEl = document.getElementById('global-notice');
|
||||
const noticeContentEl = document.getElementById('global-notice-content');
|
||||
|
@ -399,10 +403,46 @@ async function initializeDialogEditor() {
|
|||
playBtn.disabled = !item.audioUrl;
|
||||
playBtn.onclick = () => {
|
||||
if (!item.audioUrl) return;
|
||||
let 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);
|
||||
audio.play();
|
||||
const audioUrl = item.audioUrl.startsWith('http') ? item.audioUrl : `${API_BASE_URL_FOR_FILES}${item.audioUrl}`;
|
||||
|
||||
// If something is already playing
|
||||
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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue