From d93da88bff71c3089a3c54c1efada8ea439a460c Mon Sep 17 00:00:00 2001 From: Dave Horton Date: Wed, 20 Aug 2025 10:29:11 -0400 Subject: [PATCH] modify tts and say task to track current playback id and match against start and stop events --- lib/tasks/say.js | 23 ++++++++++++++++------- lib/tasks/tts-task.js | 13 ++++++++++++- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/lib/tasks/say.js b/lib/tasks/say.js index c45bc6e8..8e8ed96e 100644 --- a/lib/tasks/say.js +++ b/lib/tasks/say.js @@ -264,7 +264,6 @@ class TaskSay extends TtsTask { await this.playToConfMember(ep, memberId, confName, confUuid, filepath[segment]); } else { - let playbackId; const isStreaming = filepath[segment].startsWith('say:{'); if (isStreaming) { const arr = /^say:\{.*\}\s*(.*)$/.exec(filepath[segment]); @@ -283,10 +282,10 @@ class TaskSay extends TtsTask { * If we got a playback id on both the start and stop events, and they don't match, * then we must have received a playback-stop event for an earlier play request. */ - const unmatchedResponse = (!!playbackId && !!evt.variable_tts_playback_id) && - evt.variable_tts_playback_id !== playbackId; + // eslint-disable-next-line max-len + const unmatchedResponse = (!!this.playbackId && !!evt.variable_tts_playback_id) && evt.variable_tts_playback_id !== this.playbackId; if (unmatchedResponse) { - this.logger.info({currentPlaybackId: playbackId, stopPPlaybackId: evt.variable_tts_playback_id}, + this.logger.info({currentPlaybackId: this.playbackId, stopPlaybackId: evt.variable_tts_playback_id}, 'Say:exec discarding playback-stop for earlier play'); ep.once('playback-stop', this._boundOnPlaybackStop); @@ -358,9 +357,16 @@ class TaskSay extends TtsTask { }; this._boundOnPlaybackStop = onPlaybackStop.bind(this); - ep.once('playback-start', (evt) => { + const onPlaybackStart = (evt) => { try { - playbackId = evt.variable_tts_playback_id; + // eslint-disable-next-line max-len + const unmatchedResponse = (!!this.playbackId && !!evt.variable_tts_playback_id) && evt.variable_tts_playback_id !== this.playbackId; + if (unmatchedResponse) { + this.logger.info({currentPlaybackId: this.playbackId, stopPlaybackId: evt.variable_tts_playback_id}, + 'Say:exec playback-start - unmatched playback_id'); + ep.once('playback-start', this._boundOnPlaybackStart); + return; + } this.logger.debug({evt}, `Say got playback-start ${evt.variable_tts_playback_id ? evt.variable_tts_playback_id : ''}`); if (this.otelSpan) { @@ -374,8 +380,11 @@ class TaskSay extends TtsTask { } catch (err) { this.logger.info({err}, 'Error handling playback-start event'); } - }); + }; + this._boundOnPlaybackStart = onPlaybackStart.bind(this); + ep.once('playback-stop', this._boundOnPlaybackStop); + ep.once('playback-start', this._boundOnPlaybackStart); // wait for playback-stop event received to confirm if the playback is successful this._playPromise = new Promise((resolve, reject) => { diff --git a/lib/tasks/tts-task.js b/lib/tasks/tts-task.js index 8ab25de1..d9e2aa5c 100644 --- a/lib/tasks/tts-task.js +++ b/lib/tasks/tts-task.js @@ -3,6 +3,16 @@ const { TaskPreconditions } = require('../utils/constants'); const { SpeechCredentialError } = require('../utils/error'); const dbUtils = require('../utils/db-utils'); +const extractPlaybackId = (str) => { + // Match say:{...} and capture the content inside braces + const match = str.match(/say:\{([^}]*)\}/); + if (!match) return null; + + // Look for playback_id=value within the captured content + const playbackMatch = match[1].match(/playback_id=([^,]*)/); + return playbackMatch ? playbackMatch[1] : null; +}; + class TtsTask extends Task { constructor(logger, data, parentTask) { @@ -309,7 +319,8 @@ class TtsTask extends Task { } } else { - this.logger.debug('Say: a streaming tts api will be used'); + this.playbackId = extractPlaybackId(filePath); + this.logger.debug(`Say: a streaming tts api will be used, playback_id=${this.playbackId}`); const modifiedPath = filePath.replace('say:{', `say:{session-uuid=${ep.uuid},`); return modifiedPath; }