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

Commit c6a56cc

Browse files
feat: Add google-cast service and sample (#748)
1 parent cbae0d5 commit c6a56cc

File tree

11 files changed

+893
-1
lines changed

11 files changed

+893
-1
lines changed

.prettierrc.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ module.exports = {
1212
arrowParens: "always",
1313
proseWrap: "preserve",
1414
vueIndentScriptAndStyle: false,
15-
endOfLine: "lf",
15+
endOfLine: "auto",
1616
};

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ nodecg-io is the successor of [ChatOverflow](https://github.com/codeoverflow-org
2929
- Elgato lights
3030
- GitHub
3131
- Google APIs
32+
- Google Cast
3233
- IntelliJ IDEs
3334
- IRC (Internet Relay Chat)
3435
- MIDI Input

package-lock.json

Lines changed: 662 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { NodeCG } from "nodecg-types/types/server";
2+
import { GoogleCastClient } from "nodecg-io-google-cast";
3+
import { requireService } from "nodecg-io-core";
4+
5+
module.exports = function (nodecg: NodeCG) {
6+
nodecg.log.info("Sample bundle for the google-cast service started.");
7+
8+
const template = requireService<GoogleCastClient>(nodecg, "google-cast");
9+
10+
let previousDevice: GoogleCastClient | undefined = undefined;
11+
12+
template?.onAvailable((castDevice) => {
13+
nodecg.log.info("google-cast service available.");
14+
previousDevice = castDevice;
15+
16+
const deviceName = castDevice.friendlyName.length !== 0 ? castDevice.friendlyName : castDevice.host;
17+
18+
castDevice.play("https://www.soundhelix.com/examples/mp3/SoundHelix-Song-4.mp3", (err) => {
19+
if (err === null)
20+
nodecg.log.info(`Successfully started playing sample media on google cast device ${deviceName}`);
21+
else nodecg.log.error(`Failed to play media on google cast device ${deviceName}: ${err}`);
22+
});
23+
});
24+
25+
template?.onUnavailable(() => {
26+
nodecg.log.info("google-cast service unavailable.");
27+
28+
if (previousDevice !== undefined) {
29+
previousDevice.stop((err) => {
30+
if (err !== null) nodecg.log.info("Stopped playback on previous google cast device.");
31+
});
32+
previousDevice = undefined;
33+
}
34+
});
35+
};

samples/google-cast/package.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "google-cast",
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+
},
11+
"license": "MIT",
12+
"dependencies": {
13+
"@types/node": "^18.7.8",
14+
"nodecg-types": "^1.9.0",
15+
"nodecg-io-core": "^0.3.0",
16+
"nodecg-io-google-cast": "^0.3.0",
17+
"typescript": "^4.7.4",
18+
"nodecg-io-tsconfig": "^1.0.0"
19+
}
20+
}

samples/google-cast/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-google-cast"
9+
}
10+
]
11+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { GoogleCastConfig } from "./index";
2+
import Device from "chromecast-api/lib/device";
3+
import { EventEmitter } from "events";
4+
5+
const CONNECT_TIMEOUT = 15000;
6+
7+
export class GoogleCastClient extends Device {
8+
static async createClient(config: GoogleCastConfig): Promise<GoogleCastClient> {
9+
const device = new GoogleCastClient({
10+
name: config.name ?? "",
11+
friendlyName: config.friendlyName ?? "",
12+
host: config.host,
13+
});
14+
15+
const d = device as GoogleCastClient & {
16+
client: EventEmitter;
17+
};
18+
19+
await new Promise((resolve, reject) => {
20+
d._tryConnect(() => resolve(undefined));
21+
22+
d.client.on("error", (err) => {
23+
// oh nein
24+
reject(err);
25+
});
26+
27+
setTimeout(() => reject(new Error("Connect timeout reached")), CONNECT_TIMEOUT);
28+
});
29+
30+
return device;
31+
}
32+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { NodeCG } from "nodecg-types/types/server";
2+
import { Result, emptySuccess, success, ServiceBundle, Logger } from "nodecg-io-core";
3+
import { GoogleCastClient } from "./castClient";
4+
import ChromecastAPI from "chromecast-api";
5+
6+
export interface GoogleCastConfig {
7+
name?: string;
8+
friendlyName?: string;
9+
host: string;
10+
}
11+
12+
export { GoogleCastClient } from "./castClient";
13+
14+
module.exports = (nodecg: NodeCG) => {
15+
new GoogleCastService(nodecg).register();
16+
};
17+
18+
class GoogleCastService extends ServiceBundle<GoogleCastConfig, GoogleCastClient> {
19+
private autoDiscoveryClient = new ChromecastAPI();
20+
21+
constructor(nodecg: NodeCG) {
22+
super(nodecg, "google-cast", __dirname, "../schema.json");
23+
24+
this.autoDiscoveryClient.on("device", (device) => {
25+
if (this.presets === undefined) this.presets = {};
26+
27+
this.presets[device.friendlyName] = {
28+
name: device.name,
29+
friendlyName: device.friendlyName,
30+
host: device.host,
31+
};
32+
});
33+
}
34+
35+
async validateConfig(_: GoogleCastConfig): Promise<Result<void>> {
36+
return emptySuccess();
37+
}
38+
39+
async createClient(config: GoogleCastConfig, logger: Logger): Promise<Result<GoogleCastClient>> {
40+
const client = await GoogleCastClient.createClient(config);
41+
logger.info("Successfully created google-cast client.");
42+
return success(client);
43+
}
44+
45+
stopClient(client: GoogleCastClient, logger: Logger): void {
46+
client.close();
47+
logger.info("Successfully stopped google-cast client.");
48+
}
49+
50+
removeHandlers(client: GoogleCastClient): void {
51+
client.removeAllListeners();
52+
}
53+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"name": "nodecg-io-google-cast",
3+
"version": "0.3.0",
4+
"description": "Allows playing media on Google Cast devices.",
5+
"homepage": "https://nodecg.io/RELEASE/samples/google-cast",
6+
"author": {
7+
"name": "CodeOverflow team",
8+
"url": "https://github.com/codeoverflow-org"
9+
},
10+
"contributors": [
11+
{
12+
"name": "SteffoSpieler",
13+
"url": "https://about.steffospieler.de"
14+
}
15+
],
16+
"repository": {
17+
"type": "git",
18+
"url": "https://github.com/codeoverflow-org/nodecg-io.git",
19+
"directory": "services/nodecg-io-google-cast"
20+
},
21+
"files": [
22+
"**/*.js",
23+
"**/*.js.map",
24+
"**/*.d.ts",
25+
"*.json"
26+
],
27+
"main": "extension/index",
28+
"keywords": [
29+
"nodecg-io",
30+
"nodecg-bundle"
31+
],
32+
"nodecg": {
33+
"compatibleRange": "^1.1.1",
34+
"bundleDependencies": {
35+
"nodecg-io-core": "^0.3.0"
36+
}
37+
},
38+
"license": "MIT",
39+
"devDependencies": {
40+
"@types/node": "^18.7.8",
41+
"@types/chromecast-api": "^0.4.1",
42+
"nodecg-types": "^1.9.0",
43+
"typescript": "^4.7.4",
44+
"nodecg-io-tsconfig": "^1.0.0"
45+
},
46+
"dependencies": {
47+
"nodecg-io-core": "^0.3.0",
48+
"chromecast-api": "^0.4.0"
49+
}
50+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"type": "object",
4+
"additionalProperties": false,
5+
"properties": {
6+
"name": {
7+
"type": "string",
8+
"description": "The unique name of the google cast device that is independent of host."
9+
},
10+
"friendlyName": {
11+
"type": "string",
12+
"description": "The user configured friendly display name of this google cast device."
13+
},
14+
"host": {
15+
"type": "string",
16+
"description": "The network host (ip or mDNS hostname) of this google cast device."
17+
}
18+
},
19+
"required": ["host"]
20+
}

0 commit comments

Comments
 (0)