Skip to content

Commit

Permalink
Add TTS health indicator to Chat
Browse files Browse the repository at this point in the history
  • Loading branch information
DJDavid98 committed Dec 17, 2023
1 parent 2480579 commit fd93adb
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 1 deletion.
5 changes: 5 additions & 0 deletions src/js/chat/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import DurationUnitFormat from 'intl-unofficial-duration-unit-format';
import { useSettings } from '../contexts/settings-context';
import { SettingName } from '../model/settings';
import { useTts } from '../hooks/use-tts';
import { TtsHealth } from './TtsHealth';
import { RemovableElement } from '../RemovableElement';
import { RemovableElementId } from '../model/removable-element-id';

const MAX_MESSAGE_COUNT = 12;

Expand Down Expand Up @@ -199,6 +202,8 @@ export const Chat: FC = () => {
}, [addMessage, chatSongPreviews, df, socket, tts]);

return <Fragment>
{ttsEnabled && elevenLabsToken &&
<RemovableElement id={RemovableElementId.TTS_HEALTH}><TtsHealth token={elevenLabsToken} /></RemovableElement>}
{messages.map(message => <ChatMessage key={message.id} message={message} />)}
</Fragment>;
};
61 changes: 61 additions & 0 deletions src/js/chat/TtsHealth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { FC, useId, useMemo } from 'react';
import useSWR from 'swr';
import * as styles from '../../scss/modules/TtsHealth.module.scss';

export const ELEVEN_LABS_SUBSCRIPTION_ENDPOINT = 'https://api.elevenlabs.io/v1/user/subscription';

export interface TtsHealthProps {
token: string;
}

export const TtsHealth: FC<TtsHealthProps> = ({ token }) => {
const pf = useMemo(() => new Intl.NumberFormat('en-US', {
style: 'percent',
minimumFractionDigits: 0,
maximumFractionDigits: 0
}), []);
const nf = useMemo(() => new Intl.NumberFormat('en-US'), []);
const progressBarId = useId();

const { data: subscriptionData } = useSWR(ELEVEN_LABS_SUBSCRIPTION_ENDPOINT, (key: string) => fetch(key, {
method: 'GET',
headers: {
'xi-api-key': token,
},
}).then(r => r.json()), {
refreshInterval: 60e3,
revalidateOnFocus: false,
keepPreviousData: true,
});

const limits = useMemo(() => {
let maxChars = 0;
let usedChars = 0;
if (typeof subscriptionData === 'object' && subscriptionData !== null) {
if ('character_limit' in subscriptionData && typeof subscriptionData.character_limit === 'number') {
maxChars = subscriptionData.character_limit;
}
if ('character_count' in subscriptionData && typeof subscriptionData.character_count === 'number') {
usedChars = subscriptionData.character_count;
}
}
return { maxChars, usedChars };
}, [subscriptionData]);

if (limits.maxChars === 0) return null;

const charsAvailable = limits.maxChars - limits.usedChars;

return <div className={styles['tts-health']}>
<label htmlFor={progressBarId} className={styles['tts-label']}>
<div className={styles['tts-name']}>❤️ TTS Health</div>
{limits.maxChars > 0 &&
<div className={styles['tts-percent']}>{nf.format(charsAvailable)} / {nf.format(limits.maxChars)} &bull; {pf.format(1 - limits.usedChars / limits.maxChars)}</div>}
</label>
<progress
id={progressBarId}
max={limits.maxChars}
value={charsAvailable}
/>
</div>;
};
6 changes: 5 additions & 1 deletion src/js/hooks/use-tts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
ttsNameSubstitutions,
VoiceGender
} from '../utils/chat-messages';
import { useSWRConfig } from 'swr';
import { ELEVEN_LABS_SUBSCRIPTION_ENDPOINT } from '../chat/TtsHealth';

interface TtsInput {
id?: string;
Expand All @@ -32,6 +34,7 @@ export const useTts = (token: string | null, enabled: boolean | null): TtsApi =>
return age === 'young' && gender === targetGender && useCase === 'narration';
});
}, []);
const { mutate } = useSWRConfig();

const clearPlayingAudio = useCallback(() => {
if (audioPlayerRef.current) {
Expand Down Expand Up @@ -87,6 +90,7 @@ export const useTts = (token: string | null, enabled: boolean | null): TtsApi =>
body: JSON.stringify({ text: textToRead })
});
const audioBlob = await response.blob();
void mutate(ELEVEN_LABS_SUBSCRIPTION_ENDPOINT);

if (!audioPlayerRef.current) {
audioPlayerRef.current = new Audio();
Expand All @@ -100,7 +104,7 @@ export const useTts = (token: string | null, enabled: boolean | null): TtsApi =>
processQueue('ended handler').then(resolve);
});
});
}, [enabled, token, getVoice, clearPlayingAudio]);
}, [enabled, token, getVoice, mutate, clearPlayingAudio]);

const readText = useCallback(async (text: TtsInput) => {
if (!enabled) return;
Expand Down
8 changes: 8 additions & 0 deletions src/js/model/removable-element-id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export enum RemovableElementId {
BEAT_SABER = 'beat-saber-root',
BOUNCY = 'bouncy-root',
CHAT = 'chat-root',
TTS_HEALTH = 'tts-health-root',
CONNECTION = 'connection-root',
CHANNEL_BUG = 'credits-root',
HEART_RATE = 'heart-rate-root',
Expand Down Expand Up @@ -72,6 +73,9 @@ export const elementsTree: RemovableElementsTree = {
[RemovableElementId.CHAT]: {
name: 'Chat Overlay',
description: 'Shows incoming chat messages based on the provided configuration',
children: [
RemovableElementId.TTS_HEALTH
],
},
[RemovableElementId.HEART_RATE]: {
name: 'Heart Rate',
Expand All @@ -88,6 +92,10 @@ export const elementsTree: RemovableElementsTree = {
name: 'Channel Bug',
description: 'Also known as "digital on-screen graphic", a dynamically changing element that cycles between various states'
},
[RemovableElementId.TTS_HEALTH]: {
name: 'TTS Health Bar',
description: 'Shows the percentage of available TTS characters in the current subscription tier'
},
};

export const isRemovableElementId = (value?: string): value is RemovableElementId => value !== undefined && value in elementsTree;
43 changes: 43 additions & 0 deletions src/scss/modules/TtsHealth.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
.tts-health {
position: absolute;
top: 0;
left: 1rem;
right: 0;
z-index: 10;
box-sizing: border-box;
color: rgba(255, 0, 0, .75);
background-color: rgb(40, 0, 0);
padding: .5em;
border-radius: .4em;
font-size: .6em;
width: calc(100% - 2em);

.tts-label {
display: flex;
flex-flow: row nowrap;
justify-content: space-between;

.tts-name {
font-weight: bold;
}

.tts-percent {
text-align: right;
}
}

progress {
height: .2em;
border-radius: .2em;
background-color: #202020;
width: 100%;
border: .125em solid #202020;
display: block;


&::-moz-progress-bar, &::-webkit-progress-bar {
background-color: currentColor;
border-radius: inherit;
}
}
}

0 comments on commit fd93adb

Please sign in to comment.