fix: Don't incorrectly change camera when a dependency supports media (#2171)

- Closes: #2122
This commit is contained in:
Dermot Duffy
2025-09-01 13:29:04 -07:00
committed by GitHub
parent 2dd7f328f6
commit 79c27c43b8
7 changed files with 130 additions and 47 deletions
+2 -2
View File
@@ -4,7 +4,7 @@ import {
capabilityKeys,
PTZCapabilities,
} from '../types';
import { CapabilitySearchOptions } from './types';
import { CapabilitySearchKeys } from './types';
export class Capabilities {
private _capabilities: CapabilitiesRaw;
@@ -32,7 +32,7 @@ export class Capabilities {
delete this._capabilities[capability];
}
public matches(capability: CapabilitySearchOptions): boolean {
public matches(capability: CapabilitySearchKeys): boolean {
let result = true;
if (typeof capability === 'string') {
result &&= this.has(capability);
+54 -25
View File
@@ -4,7 +4,7 @@ import { allPromises } from '../utils/basic';
import { ViewMedia } from '../view/item';
import { Camera } from './camera';
import { CameraManagerEngine } from './engine';
import { CapabilitySearchOptions, Engine } from './types';
import { CapabilitySearchKeys, CapabilitySearchOptions, Engine } from './types';
type CameraManagerEngineCameraIDMap = Map<CameraManagerEngine, Set<string>>;
@@ -27,11 +27,13 @@ export interface CameraManagerReadOnlyConfigStore {
getDefaultCameraID(): string | null;
getCameraIDsWithCapability(
capability: CapabilityKey | CapabilitySearchOptions,
capability: CapabilitySearchKeys,
options?: CapabilitySearchOptions,
): Set<string>;
getAllDependentCameras(
cameraID: string,
capability?: CapabilityKey | CapabilitySearchOptions,
capability?: CapabilitySearchKeys,
options?: CapabilitySearchOptions,
): Set<string>;
}
@@ -121,14 +123,23 @@ export class CameraManagerStore implements CameraManagerReadOnlyConfigStore {
}
public getCameraIDsWithCapability(
capability: CapabilityKey | CapabilitySearchOptions,
capability: CapabilityKey | CapabilitySearchKeys,
options?: CapabilitySearchOptions,
): Set<string> {
const output: Set<string> = new Set();
for (const camera of this._cameras.values()) {
if (camera.getCapabilities()?.matches(capability)) {
output.add(camera.getID());
}
// Must use getAllDependentCameras() to recursively get all relevant
// cameras respecting the capabilitiy.
// See: https://github.com/dermotduffy/advanced-camera-card/issues/2122
this.getAllDependentCameras(camera.getID(), capability, options).forEach(
(cameraID) => {
output.add(cameraID);
},
);
}
return output;
}
@@ -177,33 +188,51 @@ export class CameraManagerStore implements CameraManagerReadOnlyConfigStore {
*/
public getAllDependentCameras(
cameraID: string,
capability?: CapabilitySearchOptions,
capabilitySearchKeys?: CapabilitySearchKeys,
options?: CapabilitySearchOptions,
): Set<string> {
const visitedCameraIDs = new Set<string>();
const matchingCameraIDs: Set<string> = new Set();
const getDependentCameras = (cameraID: string): void => {
const getDependentCameras = (cameraID: string): Set<string> => {
visitedCameraIDs.add(cameraID);
const matchingCameraIDs: Set<string> = new Set();
const camera = this.getCamera(cameraID);
const cameraConfig = camera?.getConfig();
if (camera && cameraConfig) {
if (!capability || camera.getCapabilities()?.matches(capability)) {
matchingCameraIDs.add(cameraID);
}
const dependentCameras: Set<string> = new Set();
cameraConfig.dependencies.cameras.forEach((item) => dependentCameras.add(item));
if (cameraConfig.dependencies.all_cameras) {
this.getCameraIDs().forEach((cameraID) => dependentCameras.add(cameraID));
}
for (const dependentCameraID of dependentCameras) {
if (!visitedCameraIDs.has(dependentCameraID)) {
getDependentCameras(dependentCameraID);
}
if (!camera || !cameraConfig) {
return matchingCameraIDs;
}
// Gather all dependent cameras...
const dependentCameras: Set<string> = new Set();
cameraConfig.dependencies.cameras.forEach((item) => dependentCameras.add(item));
if (cameraConfig.dependencies.all_cameras) {
this.getCameraIDs().forEach((cameraID) => dependentCameras.add(cameraID));
}
const matchingChildCameraIDs: Set<string> = new Set();
// ...now recurse through them.
for (const dependentCameraID of dependentCameras) {
if (!visitedCameraIDs.has(dependentCameraID)) {
getDependentCameras(dependentCameraID).forEach((dependentCameraID) =>
matchingChildCameraIDs.add(dependentCameraID),
);
}
}
return new Set([
...(!capabilitySearchKeys ||
camera.getCapabilities()?.matches(capabilitySearchKeys) ||
(options?.inclusive && matchingChildCameraIDs.size)
? [cameraID]
: []),
...matchingChildCameraIDs,
]);
};
getDependentCameras(cameraID);
return matchingCameraIDs;
return getDependentCameras(cameraID);
}
}
+4 -1
View File
@@ -99,7 +99,10 @@ interface CapabilitySearchAllAny {
allCapabilities?: CapabilityKey[];
anyCapabilities?: CapabilityKey[];
}
export type CapabilitySearchOptions = CapabilityKey | CapabilitySearchAllAny;
export type CapabilitySearchKeys = CapabilityKey | CapabilitySearchAllAny;
export interface CapabilitySearchOptions {
inclusive?: boolean;
}
export interface CameraManagerCameraMetadata {
title: string;