Skip to content
This repository was archived by the owner on Apr 13, 2025. It is now read-only.

Commit 1b2a12d

Browse files
feat: Add opentts service and sample (#749)
* feat: Add opentts service and sample * fix: Add sample to package-lock.json
1 parent c6a56cc commit 1b2a12d

File tree

12 files changed

+405
-2
lines changed

12 files changed

+405
-2
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ nodecg-io is the successor of [ChatOverflow](https://github.com/codeoverflow-org
3737
- MQTT
3838
- Nanoleafs
3939
- OBS
40+
- [OpenTTS](https://github.com/synesthesiam/opentts)
4041
- Philips Hue
4142
- RCON
4243
- Reddit

package-lock.json

Lines changed: 100 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

samples/opentts/extension/index.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { NodeCG } from "nodecg-types/types/server";
2+
import { OpenTTSClient } from "nodecg-io-opentts";
3+
import { requireService } from "nodecg-io-core";
4+
5+
/**
6+
* Plays a "hello world" tts message inside the graphic of this sample bundle.
7+
*/
8+
async function playTTSInGraphic(client: OpenTTSClient, nodecg: NodeCG) {
9+
const voices = await client.getVoices("en");
10+
11+
// Get random voice
12+
const voiceName = Object.keys(voices)[Math.floor(Math.random() * Object.keys(voices).length)];
13+
if (voiceName === undefined) throw new Error("no voice available");
14+
15+
const helloWorldUrl = client.generateWavUrl("Hello World", voiceName);
16+
await nodecg.sendMessage("setSrc", helloWorldUrl);
17+
}
18+
19+
module.exports = function (nodecg: NodeCG) {
20+
nodecg.log.info("Sample bundle for the OpenTTS service started.");
21+
22+
const opentts = requireService<OpenTTSClient>(nodecg, "opentts");
23+
24+
nodecg.listenFor("ready", () => {
25+
const client = opentts?.getClient();
26+
if (client !== undefined){
27+
playTTSInGraphic(client, nodecg)
28+
.catch(err => nodecg.log.error(`Error while trying to play tts message: ${err.messages}`));
29+
}
30+
});
31+
32+
opentts?.onAvailable(async (client) => {
33+
nodecg.log.info("OpenTTS service available.");
34+
35+
const voices = await client.getVoices();
36+
const languages = await client.getLanguages();
37+
38+
nodecg.log.info(
39+
`OpenTTS server supports ${Object.entries(voices).length} voices in ${languages.length} languages.`,
40+
);
41+
42+
await playTTSInGraphic(client, nodecg);
43+
});
44+
45+
opentts?.onUnavailable(() => {
46+
nodecg.log.info("OpenTTS service unavailable.");
47+
});
48+
};

samples/opentts/graphics/index.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<title>OpenTTS Sample</title>
5+
</head>
6+
<body>
7+
<audio controls id="opentts-audio"></audio>
8+
<script src="index.js"></script>
9+
</body>
10+
</html>

samples/opentts/graphics/index.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/// <reference types="nodecg-types/types/browser" />
2+
3+
// Listens for event from opentts sample and plays the audio by the provided url.
4+
5+
const audioElement = document.getElementById("opentts-audio") as HTMLAudioElement;
6+
7+
// Play audio when the graphic is newly opened
8+
nodecg.sendMessage("ready");
9+
10+
nodecg.listenFor("setSrc", (newSrc) => {
11+
audioElement.src = newSrc;
12+
audioElement.currentTime = 0;
13+
audioElement.play();
14+
});

samples/opentts/package.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "opentts",
3+
"version": "0.3.0",
4+
"private": true,
5+
"nodecg": {
6+
"compatibleRange": "^1.1.1",
7+
"bundleDependencies": {
8+
"nodecg-io-template": "^0.3.0"
9+
},
10+
"graphics": [
11+
{
12+
"file": "index.html",
13+
"width": "1920",
14+
"height": "1080"
15+
}
16+
]
17+
},
18+
"license": "MIT",
19+
"dependencies": {
20+
"@types/node": "^18.7.13",
21+
"nodecg-types": "^1.9.0",
22+
"nodecg-io-core": "^0.3.0",
23+
"nodecg-io-opentts": "^0.3.0",
24+
"typescript": "^4.8.2",
25+
"nodecg-io-tsconfig": "^1.0.0"
26+
}
27+
}

samples/opentts/tsconfig.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"extends": "nodecg-io-tsconfig",
3+
"references": [
4+
{
5+
"path": "../../nodecg-io-core"
6+
},
7+
{
8+
"path": "../../services/nodecg-io-opentts"
9+
}
10+
]
11+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { NodeCG } from "nodecg-types/types/server";
2+
import { Result, emptySuccess, success, ServiceBundle, Logger, error } from "nodecg-io-core";
3+
import { OpenTTSClient } from "./openTtsClient";
4+
5+
export interface OpenTTSConfig {
6+
host: string;
7+
useHttps?: boolean;
8+
}
9+
10+
export { OpenTTSClient, OpenTTSVoice } from "./openTtsClient";
11+
12+
module.exports = (nodecg: NodeCG) => {
13+
new OpenTTSService(nodecg, "opentts", __dirname, "../schema.json").register();
14+
};
15+
16+
class OpenTTSService extends ServiceBundle<OpenTTSConfig, OpenTTSClient> {
17+
async validateConfig(config: OpenTTSConfig): Promise<Result<void>> {
18+
if (await OpenTTSClient.isOpenTTSAvailable(config)) return emptySuccess();
19+
else return error("Unable to reach OpenTTS server at the specified host address");
20+
}
21+
22+
async createClient(config: OpenTTSConfig, logger: Logger): Promise<Result<OpenTTSClient>> {
23+
const client = new OpenTTSClient(config);
24+
logger.info("Successfully created OpenTTS client.");
25+
return success(client);
26+
}
27+
28+
stopClient(_: OpenTTSClient, logger: Logger): void {
29+
// Client is stateless, no need to stop anything
30+
}
31+
}
32+

0 commit comments

Comments
 (0)