Skip to content

Commit 3351365

Browse files
committed
fix(chromecast): Better handling of client offline events
* Actually remove device if it fails after X retries * Properly close controllers/platform/client to prevent uncaught errors from castv2 * Move manually configured devices to device discovery function so they can be reconnected on heartbeat (if previously removed)
1 parent 56f1cac commit 3351365

File tree

1 file changed

+25
-9
lines changed

1 file changed

+25
-9
lines changed

src/backend/sources/ChromecastSource.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import {config, Logger} from "@foxxmd/winston";
2828
import {ContextualValidationError} from "@foxxmd/chromecast-client/dist/cjs/src/utils.js";
2929
import { buildTrackString } from "../../core/StringUtils.js";
3030
import { discoveryAvahi, discoveryNative } from "../utils/MDNSUtils.js";
31-
import {options} from "superagent";
3231

3332
interface ChromecastDeviceInfo {
3433
mdns: MdnsDeviceInfo
@@ -114,10 +113,6 @@ export class ChromecastSource extends MemorySource {
114113
} = {},
115114
} = this.config;
116115

117-
for (const device of devices) {
118-
await this.initializeDevice({name: device.name, addresses: [device.address], type: 'googlecast'});
119-
}
120-
121116
this.discoverDevices(logPayload);
122117
if(useAutoDiscovery) {
123118
this.logger.debug('Will run mDNS discovery on subsequent heartbeats.')
@@ -131,9 +126,16 @@ export class ChromecastSource extends MemorySource {
131126
data: {
132127
useAvahi,
133128
useAutoDiscovery,
129+
devices = [],
134130
} = {}
135131
} = this.config;
136132

133+
for (const device of devices) {
134+
this.initializeDevice({name: device.name, addresses: [device.address], type: 'googlecast'}).catch((err) => {
135+
this.logger.error(new ErrorWithCause('Uncaught error occurred while connecting to manually configured device', {cause: err}));
136+
});
137+
}
138+
137139
if (useAutoDiscovery) {
138140
if (useAvahi) {
139141
this.discoverAvahi(initial).catch((err) => {
@@ -273,6 +275,7 @@ export class ChromecastSource extends MemorySource {
273275
if(info === undefined) {
274276
this.logger.error(new ErrorWithCause(`(${clientName}) Encountered error in castv2 lib`, {cause: payload as Error}));
275277
} else {
278+
info.connected = false;
276279
info.logger.error(new ErrorWithCause(`Encountered error in castv2 lib`, {cause: payload as Error}));
277280
}
278281
break;
@@ -290,14 +293,14 @@ export class ChromecastSource extends MemorySource {
290293
apps = await getCurrentPlatformApplications(v.platform);
291294
v.retries = 0;
292295
} catch (e) {
293-
v.logger.warn(new ErrorWithCause('Could not refresh applications', {cause: e}));
296+
v.logger.warn(new ErrorWithCause(`Could not refresh applications. Will after ${5 - v.retries} retries if error does not resolve itself.`, {cause: e}));
294297
const validationError = findCauseByReference(e, ContextualValidationError);
295298
if(validationError && validationError.data !== undefined) {
296299
v.logger.warn(JSON.stringify(validationError.data));
297300
}
298301
v.retries++;
299-
if(v.retries >= 6) {
300-
this.removeApplications(k, 'Unable to refresh application more than 6 times consecutively! If this device comes back online it will be re-added on next heartbeat.');
302+
if(v.retries >= 5) {
303+
this.removeDevice(k, 'Unable to refresh application more than 6 times consecutively! If this device comes back online it will be re-added on next heartbeat.');
301304
}
302305
continue;
303306
}
@@ -375,13 +378,25 @@ export class ChromecastSource extends MemorySource {
375378
}
376379
}
377380

381+
protected removeDevice = (deviceName: string, reason?: string) => {
382+
this.removeApplications(deviceName, reason);
383+
if(reason !== undefined) {
384+
this.logger.warn(reason);
385+
}
386+
const device = this.devices.get(deviceName);
387+
device.platform.close();
388+
device.client.close();
389+
this.devices.delete(deviceName);
390+
}
391+
378392
protected removeApplications = (deviceName: string, reason?: string) => {
379393
const deviceInfo = this.devices.get(deviceName);
380394
if(deviceInfo === undefined) {
381395
this.logger.warn(`No device with ${deviceName} exists, no applications to remove.`);
382396
return;
383397
}
384398
for(const [tId, app] of deviceInfo.applications) {
399+
app.controller.dispose();
385400
this.deletePlayer(app.playerId, reason)
386401
//app.logger.close();
387402
deviceInfo.applications.delete(tId);
@@ -390,7 +405,7 @@ export class ChromecastSource extends MemorySource {
390405

391406
protected pruneApplications = (force: boolean = false) => {
392407
for(const [k, v] of this.devices.entries()) {
393-
if (!force && !v.connected) {
408+
if (!force && (!v.connected || v.retries > 0)) {
394409
continue;
395410
}
396411

@@ -400,6 +415,7 @@ export class ChromecastSource extends MemorySource {
400415
if(app.stale && Math.abs(app.staleAt.diff(dayjs(), 's')) > 60) {
401416
app.logger.info(`Removing due to being stale for 60 seconds`);
402417
//app.logger.close();
418+
app.controller.dispose();
403419
v.applications.delete(tId);
404420
forDeletion.push([app.playerId, 'No updates for 60 seconds']);
405421
} else if(app.badData && Math.abs(app.badDataAt.diff(dayjs(), 's')) > 60 && this.players.has(app.playerId)) {

0 commit comments

Comments
 (0)