feat(subtitles): Don't show delayed final

If a non final transcript was displayed and then hidden and then we receive a final transcript we remove the part that has already been shown before. If the final transcript is the same as the non final that was already displayed we don't show the final.
This commit is contained in:
Hristo Terezov
2024-11-15 17:29:30 -06:00
parent 8db769b174
commit 91e1007e5b
4 changed files with 97 additions and 9 deletions

View File

@@ -9,6 +9,17 @@
*/
export const REMOVE_TRANSCRIPT_MESSAGE = 'REMOVE_TRANSCRIPT_MESSAGE';
/**
* The type of (redux) action which indicates that an cached transcript
* has to be removed from the state.
*
* {
* type: REMOVE_CACHED_TRANSCRIPT_MESSAGE,
* transciptMessageID: string,
* }
*/
export const REMOVE_CACHED_TRANSCRIPT_MESSAGE = 'REMOVE_CACHED_TRANSCRIPT_MESSAGE';
/**
* The type of (redux) action which indicates that a transcript with an
* given message_id to be added or updated is received.

View File

@@ -1,6 +1,7 @@
import { DEFAULT_LANGUAGE } from '../base/i18n/i18next';
import {
REMOVE_CACHED_TRANSCRIPT_MESSAGE,
REMOVE_TRANSCRIPT_MESSAGE,
SET_REQUESTING_SUBTITLES,
TOGGLE_REQUESTING_SUBTITLES,
@@ -23,6 +24,22 @@ export function removeTranscriptMessage(transcriptMessageID: string) {
};
}
/**
* Signals that a cached transcript has to be removed from the state.
*
* @param {string} transcriptMessageID - The message_id to be removed.
* @returns {{
* type: REMOVE_CACHED_TRANSCRIPT_MESSAGE,
* transcriptMessageID: string,
* }}
*/
export function removeCachedTranscriptMessage(transcriptMessageID: string) {
return {
type: REMOVE_CACHED_TRANSCRIPT_MESSAGE,
transcriptMessageID
};
}
/**
* Signals that a transcript with the given message_id to be added or updated
* is received.

View File

@@ -13,6 +13,7 @@ import {
TOGGLE_REQUESTING_SUBTITLES
} from './actionTypes';
import {
removeCachedTranscriptMessage,
removeTranscriptMessage,
setRequestingSubtitles,
updateTranscriptMessage
@@ -134,18 +135,16 @@ function _endpointMessageReceived(store: IStore, next: Function, action: AnyActi
name
};
let newTranscriptMessage: ITranscriptMessage | undefined;
if (json.type === JSON_TYPE_TRANSLATION_RESULT && json.language === language) {
// Displays final results in the target language if translation is
// enabled.
const newTranscriptMessage = {
newTranscriptMessage = {
clearTimeOut: undefined,
final: json.text,
final: json.text?.trim(),
participant
};
_setClearerOnTranscriptMessage(dispatch, transcriptMessageID, newTranscriptMessage);
dispatch(updateTranscriptMessage(transcriptMessageID, newTranscriptMessage));
} else if (json.type === JSON_TYPE_TRANSCRIPTION_RESULT) {
// Displays interim and final results without any translation if
// translations are disabled.
@@ -209,13 +208,12 @@ function _endpointMessageReceived(store: IStore, next: Function, action: AnyActi
// message ID or adds a new transcript message if it does not
// exist in the map.
const existingMessage = state['features/subtitles']._transcriptMessages.get(transcriptMessageID);
const newTranscriptMessage: ITranscriptMessage = {
newTranscriptMessage = {
clearTimeOut: existingMessage?.clearTimeOut,
participant
};
_setClearerOnTranscriptMessage(dispatch, transcriptMessageID, newTranscriptMessage);
// If this is final result, update the state as a final result
// and start a count down to remove the subtitle from the state
if (!json.is_interim) {
@@ -231,7 +229,31 @@ function _endpointMessageReceived(store: IStore, next: Function, action: AnyActi
// after the stable part.
newTranscriptMessage.unstable = text;
}
}
if (newTranscriptMessage) {
if (newTranscriptMessage.final) {
const cachedTranscriptMessage
= state['features/subtitles']._cachedTranscriptMessages?.get(transcriptMessageID);
if (cachedTranscriptMessage) {
const cachedText = (cachedTranscriptMessage.stable || cachedTranscriptMessage.unstable)?.trim();
const newText = newTranscriptMessage.final;
if (cachedText && cachedText.length > 0 && newText && newText.length > 0
&& newText.toLowerCase().startsWith(cachedText.toLowerCase())) {
newTranscriptMessage.final = newText.slice(cachedText.length)?.trim();
}
dispatch(removeCachedTranscriptMessage(transcriptMessageID));
if (!newTranscriptMessage.final || newTranscriptMessage.final.length === 0) {
return next(action);
}
}
}
_setClearerOnTranscriptMessage(dispatch, transcriptMessageID, newTranscriptMessage);
dispatch(updateTranscriptMessage(transcriptMessageID, newTranscriptMessage));
}

View File

@@ -2,6 +2,7 @@ import ReducerRegistry from '../base/redux/ReducerRegistry';
import { TRANSCRIBER_LEFT } from '../transcribing/actionTypes';
import {
REMOVE_CACHED_TRANSCRIPT_MESSAGE,
REMOVE_TRANSCRIPT_MESSAGE,
SET_REQUESTING_SUBTITLES,
TOGGLE_REQUESTING_SUBTITLES,
@@ -13,6 +14,7 @@ import { ITranscriptMessage } from './types';
* Default State for 'features/transcription' feature.
*/
const defaultState = {
_cachedTranscriptMessages: new Map(),
_displaySubtitles: false,
_transcriptMessages: new Map(),
_requestingSubtitles: false,
@@ -20,6 +22,7 @@ const defaultState = {
};
export interface ISubtitlesState {
_cachedTranscriptMessages: Map<string, ITranscriptMessage>;
_displaySubtitles: boolean;
_language: string | null;
_requestingSubtitles: boolean;
@@ -35,6 +38,8 @@ ReducerRegistry.register<ISubtitlesState>('features/subtitles', (
switch (action.type) {
case REMOVE_TRANSCRIPT_MESSAGE:
return _removeTranscriptMessage(state, action);
case REMOVE_CACHED_TRANSCRIPT_MESSAGE:
return _removeCachedTranscriptMessage(state, action);
case UPDATE_TRANSCRIPT_MESSAGE:
return _updateTranscriptMessage(state, action);
case SET_REQUESTING_SUBTITLES:
@@ -70,16 +75,45 @@ ReducerRegistry.register<ISubtitlesState>('features/subtitles', (
*/
function _removeTranscriptMessage(state: ISubtitlesState, { transcriptMessageID }: { transcriptMessageID: string; }) {
const newTranscriptMessages = new Map(state._transcriptMessages);
const message = newTranscriptMessages.get(transcriptMessageID);
let { _cachedTranscriptMessages } = state;
if (message && !message.final) {
_cachedTranscriptMessages = new Map(_cachedTranscriptMessages);
_cachedTranscriptMessages.set(transcriptMessageID, message);
}
// Deletes the key from Map once a final message arrives.
newTranscriptMessages.delete(transcriptMessageID);
return {
...state,
_cachedTranscriptMessages,
_transcriptMessages: newTranscriptMessages
};
}
/**
* Reduces a specific Redux action REMOVE_CACHED_TRANSCRIPT_MESSAGE of the feature transcription.
*
* @param {Object} state - The Redux state of the feature transcription.
* @param {Action} action -The Redux action REMOVE_CACHED_TRANSCRIPT_MESSAGE to reduce.
* @returns {Object} The new state of the feature transcription after the reduction of the specified action.
*/
function _removeCachedTranscriptMessage(state: ISubtitlesState,
{ transcriptMessageID }: { transcriptMessageID: string; }) {
const newCachedTranscriptMessages = new Map(state._cachedTranscriptMessages);
// Deletes the key from Map once a final message arrives.
newCachedTranscriptMessages.delete(transcriptMessageID);
return {
...state,
_cachedTranscriptMessages: newCachedTranscriptMessages
};
}
/**
* Reduces a specific Redux action UPDATE_TRANSCRIPT_MESSAGE of the feature
* transcription.
@@ -92,12 +126,16 @@ function _removeTranscriptMessage(state: ISubtitlesState, { transcriptMessageID
function _updateTranscriptMessage(state: ISubtitlesState, { transcriptMessageID, newTranscriptMessage }:
{ newTranscriptMessage: ITranscriptMessage; transcriptMessageID: string; }) {
const newTranscriptMessages = new Map(state._transcriptMessages);
const _cachedTranscriptMessages = new Map(state._cachedTranscriptMessages);
_cachedTranscriptMessages.delete(transcriptMessageID);
// Updates the new message for the given key in the Map.
newTranscriptMessages.set(transcriptMessageID, newTranscriptMessage);
return {
...state,
_cachedTranscriptMessages,
_transcriptMessages: newTranscriptMessages
};
}