Files
fx_cast/app/src/bridge/components/cast/discovery.ts
2022-08-26 21:21:24 +01:00

90 lines
2.3 KiB
TypeScript

"use strict";
import mdns from "mdns";
import type { ReceiverDevice } from "../../messagingTypes";
/**
* Chromecast TXT record
*/
interface CastRecord {
// Device ID
id: string;
// Model name (e.g. Chromecast, Google Nest Mini, etc...)
md: string;
// Friendly name (user-visible)
fn: string;
// Capabilities
ca: string;
// Version (?)
ve: string;
// Icon path (?)
ic: string;
cd: string;
rm: string;
st: string;
bs: string;
nf: string;
rs: string;
}
interface DiscoveryOptions {
onDeviceFound(device: ReceiverDevice): void;
onDeviceDown(deviceId: string): void;
}
export default class Discovery {
browser = mdns.createBrowser(mdns.tcp("googlecast"), {
resolverSequence: [
mdns.rst.DNSServiceResolve(),
"DNSServiceGetAddrInfo" in mdns.dns_sd
? mdns.rst.DNSServiceGetAddrInfo()
: // Some issues on Linux with IPv6, so restrict to IPv4
mdns.rst.getaddrinfo({ families: [4] }),
mdns.rst.makeAddressesUnique()
]
});
constructor(opts: DiscoveryOptions) {
/**
* When a service is found, gather device info from service
* object and TXT record, then send a `main:deviceUp` message.
*/
this.browser.on("serviceUp", service => {
// Filter invalid results
if (!service.txtRecord || !service.name) return;
const record = service.txtRecord as CastRecord;
const device: ReceiverDevice = {
id: record.id,
friendlyName: record.fn,
modelName: record.md,
capabilities: parseInt(record.ca),
host: service.addresses[0],
port: service.port
};
opts.onDeviceFound(device);
});
/**
* When a service is lost, send a `main:deviceDown` message with
* the service name as the `deviceId`.
*/
this.browser.on("serviceDown", service => {
// Filter invalid results
if (!service.name) return;
opts.onDeviceDown(service.name);
});
}
start() {
this.browser.start();
}
stop() {
this.browser.stop();
}
}