Skip to content

Commit f2a7e95

Browse files
authored
Add useTranscription hook (#1109)
1 parent d5cba8e commit f2a7e95

File tree

9 files changed

+76
-24
lines changed

9 files changed

+76
-24
lines changed

.changeset/clean-toes-melt.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@livekit/components-core": patch
3+
"@livekit/components-react": patch
4+
---
5+
6+
Add useTranscription hook

packages/core/etc/components-core.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ export const cssPrefix = "lk";
156156
// @public (undocumented)
157157
export const DataTopic: {
158158
readonly CHAT: "lk.chat";
159+
readonly TRANSCRIPTION: "lk.transcription";
159160
};
160161

161162
// @public (undocumented)

packages/core/src/components/chat.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
sendMessage,
99
setupDataMessageHandler,
1010
} from '../observables/dataChannel';
11+
import { log } from '../logger';
1112

1213
/** @public */
1314
export type { ChatMessage };
@@ -165,10 +166,14 @@ export function setupChat(room: Room, options?: ChatOptions) {
165166
...chatMsg,
166167
ignoreLegacy: serverSupportsDataStreams(),
167168
});
168-
await sendMessage(room.localParticipant, encodedLegacyMsg, {
169-
reliable: true,
170-
topic: legacyTopic,
171-
});
169+
try {
170+
await sendMessage(room.localParticipant, encodedLegacyMsg, {
171+
reliable: true,
172+
topic: legacyTopic,
173+
});
174+
} catch (error) {
175+
log.info('could not send message in legacy chat format', error);
176+
}
172177
return chatMsg;
173178
} finally {
174179
isSending$.next(false);

packages/core/src/components/textStream.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ export function setupTextStream(room: Room, topic: string): Observable<TextStrea
9797

9898
// Add cleanup when room is disconnected
9999
room.once(RoomEvent.Disconnected, () => {
100+
room.unregisterTextStreamHandler(topic);
100101
textStreamsSubject.complete();
101102
getObservableCache().delete(cacheKey);
102103
});

packages/core/src/observables/dataChannel.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { ReceivedChatMessage } from '../components/chat';
1313

1414
export const DataTopic = {
1515
CHAT: 'lk.chat',
16+
TRANSCRIPTION: 'lk.transcription',
1617
} as const;
1718

1819
/** @deprecated */

packages/react/etc/components-react.api.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,6 +1213,17 @@ export function useTrackTranscription(trackRef: TrackReferenceOrPlaceholder_4 |
12131213
// @alpha
12141214
export function useTrackVolume(trackOrTrackReference?: LocalAudioTrack | RemoteAudioTrack | TrackReference_3, options?: AudioAnalyserOptions): number;
12151215

1216+
// @beta
1217+
export function useTranscriptions(opts?: UseTranscriptionsOptions): TextStreamData_2[];
1218+
1219+
// @beta (undocumented)
1220+
export interface UseTranscriptionsOptions {
1221+
// (undocumented)
1222+
participantIdentities?: string[];
1223+
// (undocumented)
1224+
trackSids?: string[];
1225+
}
1226+
12161227
// @public
12171228
export function useVisualStableUpdate(
12181229
trackReferences: TrackReferenceOrPlaceholder_4[], maxItemsOnPage: number, options?: UseVisualStableUpdateOptions): TrackReferenceOrPlaceholder_4[];

packages/react/src/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,4 @@ export * from './useVoiceAssistant';
5555
export * from './useParticipantAttributes';
5656
export * from './useIsRecording';
5757
export * from './useTextStream';
58+
export * from './useTranscriptions';

packages/react/src/hooks/useTrackTranscription.ts

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const TRACK_TRANSCRIPTION_DEFAULTS = {
3535
} as const satisfies TrackTranscriptionOptions;
3636

3737
/**
38-
* @returns An object consisting of `segments` with maximum length of opts.windowLength and `activeSegments` that are valid for the current track timestamp
38+
* @returns An object consisting of `segments` with maximum length of opts.bufferSize
3939
* @alpha
4040
*/
4141
export function useTrackTranscription(
@@ -44,10 +44,7 @@ export function useTrackTranscription(
4444
) {
4545
const opts = { ...TRACK_TRANSCRIPTION_DEFAULTS, ...options };
4646
const [segments, setSegments] = React.useState<Array<ReceivedTranscriptionSegment>>([]);
47-
// const [activeSegments, setActiveSegments] = React.useState<Array<ReceivedTranscriptionSegment>>(
48-
// [],
49-
// );
50-
// const prevActiveSegments = React.useRef<ReceivedTranscriptionSegment[]>([]);
47+
5148
const syncTimestamps = useTrackSyncTime(trackRef);
5249
const handleSegmentMessage = (newSegments: TranscriptionSegment[]) => {
5350
opts.onTranscription?.(newSegments);
@@ -72,20 +69,5 @@ export function useTrackTranscription(
7269
};
7370
}, [trackRef && getTrackReferenceId(trackRef), handleSegmentMessage]);
7471

75-
// React.useEffect(() => {
76-
// if (syncTimestamps) {
77-
// const newActiveSegments = getActiveTranscriptionSegments(
78-
// segments,
79-
// syncTimestamps,
80-
// opts.maxAge,
81-
// );
82-
// // only update active segment array if content actually changed
83-
// if (didActiveSegmentsChange(prevActiveSegments.current, newActiveSegments)) {
84-
// setActiveSegments(newActiveSegments);
85-
// prevActiveSegments.current = newActiveSegments;
86-
// }
87-
// }
88-
// }, [syncTimestamps, segments, opts.maxAge]);
89-
9072
return { segments };
9173
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import * as React from 'react';
2+
import { useTextStream } from './useTextStream';
3+
import { DataTopic } from '@livekit/components-core';
4+
5+
/**
6+
* @beta
7+
*/
8+
export interface UseTranscriptionsOptions {
9+
participantIdentities?: string[];
10+
trackSids?: string[];
11+
}
12+
13+
/**
14+
* @beta
15+
* useTranscriptions is a hook that returns the transcriptions for the given participant identities and track sids,
16+
* if no options are provided, it will return all transcriptions
17+
* @example
18+
* ```tsx
19+
* const transcriptions = useTranscriptions();
20+
* return <div>{transcriptions.map((transcription) => transcription.text)}</div>;
21+
* ```
22+
*/
23+
export function useTranscriptions(opts?: UseTranscriptionsOptions) {
24+
const { participantIdentities, trackSids } = opts ?? {};
25+
const { textStreams } = useTextStream(DataTopic.TRANSCRIPTION);
26+
27+
const filteredMessages = React.useMemo(
28+
() =>
29+
textStreams
30+
.filter((stream) =>
31+
participantIdentities
32+
? participantIdentities.includes(stream.participantInfo.identity)
33+
: true,
34+
)
35+
.filter((stream) =>
36+
trackSids
37+
? trackSids.includes(stream.streamInfo.attributes?.['lk.transcribed_track_id'] ?? '')
38+
: true,
39+
),
40+
[textStreams, participantIdentities, trackSids],
41+
);
42+
43+
return filteredMessages;
44+
}

0 commit comments

Comments
 (0)