Set the correct view depending on media filter selections.
This commit is contained in:
@@ -25,7 +25,8 @@ export const getRecordingTitle = (
|
|||||||
cameraConfig: CameraConfig,
|
cameraConfig: CameraConfig,
|
||||||
recording: FrigateRecording,
|
recording: FrigateRecording,
|
||||||
): string => {
|
): string => {
|
||||||
return `${prettifyTitle(cameraConfig.frigate.camera_name)} ${formatDateAndTime(
|
const cameraName = prettifyTitle(cameraConfig.frigate.camera_name ?? '');
|
||||||
|
return `${cameraName ? `${cameraName} ` : ''}${formatDateAndTime(
|
||||||
recording.startTime,
|
recording.startTime,
|
||||||
)}`;
|
)}`;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -94,7 +94,6 @@ import { ViewContext } from 'view';
|
|||||||
import { CameraManager } from './camera/manager.js';
|
import { CameraManager } from './camera/manager.js';
|
||||||
import { setLowPerformanceProfile, setPerformanceCSSStyles } from './performance.js';
|
import { setLowPerformanceProfile, setPerformanceCSSStyles } from './performance.js';
|
||||||
import { CameraManagerEngineFactory } from './camera/engine-factory.js';
|
import { CameraManagerEngineFactory } from './camera/engine-factory.js';
|
||||||
import { RequestCache } from './camera/cache.js';
|
|
||||||
|
|
||||||
/** A note on media callbacks:
|
/** A note on media callbacks:
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -232,6 +232,7 @@ export class FrigateCardMediaFilterCore extends LitElement {
|
|||||||
.label=${localize('media_filter.media_type')}
|
.label=${localize('media_filter.media_type')}
|
||||||
.items=${this._mediaTypeOptions}
|
.items=${this._mediaTypeOptions}
|
||||||
.allowCustomValue=${false}
|
.allowCustomValue=${false}
|
||||||
|
.value=${this.defaults?.mediaType}
|
||||||
@value-changed=${this._valueChangedHandler.bind(this)}
|
@value-changed=${this._valueChangedHandler.bind(this)}
|
||||||
></ha-combo-box>`
|
></ha-combo-box>`
|
||||||
: ''}
|
: ''}
|
||||||
@@ -242,6 +243,7 @@ export class FrigateCardMediaFilterCore extends LitElement {
|
|||||||
.label=${localize('media_filter.when')}
|
.label=${localize('media_filter.when')}
|
||||||
.items=${this._whenOptions}
|
.items=${this._whenOptions}
|
||||||
.allowCustomValue=${false}
|
.allowCustomValue=${false}
|
||||||
|
.value=${this.defaults?.when}
|
||||||
@value-changed=${this._valueChangedHandler.bind(this)}
|
@value-changed=${this._valueChangedHandler.bind(this)}
|
||||||
></ha-combo-box>`
|
></ha-combo-box>`
|
||||||
: ''}
|
: ''}
|
||||||
@@ -252,6 +254,9 @@ export class FrigateCardMediaFilterCore extends LitElement {
|
|||||||
.label=${localize('media_filter.camera')}
|
.label=${localize('media_filter.camera')}
|
||||||
.items=${this._cameraOptions}
|
.items=${this._cameraOptions}
|
||||||
.allowCustomValue=${false}
|
.allowCustomValue=${false}
|
||||||
|
.value=${this.defaults?.cameraIDs?.size === 1
|
||||||
|
? [...this.defaults.cameraIDs][0]
|
||||||
|
: undefined}
|
||||||
@value-changed=${this._valueChangedHandler.bind(this)}
|
@value-changed=${this._valueChangedHandler.bind(this)}
|
||||||
></ha-combo-box>`
|
></ha-combo-box>`
|
||||||
: ''}
|
: ''}
|
||||||
@@ -262,6 +267,9 @@ export class FrigateCardMediaFilterCore extends LitElement {
|
|||||||
.label=${localize('media_filter.what')}
|
.label=${localize('media_filter.what')}
|
||||||
.items="${this._whatOptions}"
|
.items="${this._whatOptions}"
|
||||||
.allowCustomValue=${false}
|
.allowCustomValue=${false}
|
||||||
|
.value=${this.defaults?.what?.size === 1
|
||||||
|
? [...this.defaults.what][0]
|
||||||
|
: undefined}
|
||||||
@value-changed=${this._valueChangedHandler.bind(this)}
|
@value-changed=${this._valueChangedHandler.bind(this)}
|
||||||
></ha-combo-box>`
|
></ha-combo-box>`
|
||||||
: ''}
|
: ''}
|
||||||
@@ -272,6 +280,9 @@ export class FrigateCardMediaFilterCore extends LitElement {
|
|||||||
.label=${localize('media_filter.where')}
|
.label=${localize('media_filter.where')}
|
||||||
.items=${this._whereOptions}
|
.items=${this._whereOptions}
|
||||||
.allowCustomValue=${false}
|
.allowCustomValue=${false}
|
||||||
|
.value=${this.defaults?.where?.size === 1
|
||||||
|
? [...this.defaults.where][0]
|
||||||
|
: undefined}
|
||||||
@value-changed=${this._valueChangedHandler.bind(this)}
|
@value-changed=${this._valueChangedHandler.bind(this)}
|
||||||
></ha-combo-box>`
|
></ha-combo-box>`
|
||||||
: ''}
|
: ''}
|
||||||
@@ -283,6 +294,7 @@ export class FrigateCardMediaFilterCore extends LitElement {
|
|||||||
.label=${localize('media_filter.favorite')}
|
.label=${localize('media_filter.favorite')}
|
||||||
.items=${this._favoriteOptions}
|
.items=${this._favoriteOptions}
|
||||||
.allowCustomValue=${false}
|
.allowCustomValue=${false}
|
||||||
|
.value=${this.defaults?.favorite}
|
||||||
@value-changed=${this._valueChangedHandler.bind(this)}
|
@value-changed=${this._valueChangedHandler.bind(this)}
|
||||||
></ha-combo-box>
|
></ha-combo-box>
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -16,9 +16,7 @@ import {
|
|||||||
unsafeCSS,
|
unsafeCSS,
|
||||||
} from 'lit';
|
} from 'lit';
|
||||||
import { customElement, property } from 'lit/decorators.js';
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
import {
|
import { CameraManager } from '../camera/manager';
|
||||||
CameraManager,
|
|
||||||
} from '../camera/manager';
|
|
||||||
import { DateRange } from '../camera/range';
|
import { DateRange } from '../camera/range';
|
||||||
import { CameraConfig, ExtendedHomeAssistant } from '../types';
|
import { CameraConfig, ExtendedHomeAssistant } from '../types';
|
||||||
import { getCameraTitle } from '../utils/camera';
|
import { getCameraTitle } from '../utils/camera';
|
||||||
@@ -44,6 +42,8 @@ import parse from 'date-fns/parse';
|
|||||||
import endOfMonth from 'date-fns/endOfMonth';
|
import endOfMonth from 'date-fns/endOfMonth';
|
||||||
import mediaFilterStyle from '../scss/media-filter.scss';
|
import mediaFilterStyle from '../scss/media-filter.scss';
|
||||||
import { MediaQueriesClassifier } from '../view/media-queries-classifier';
|
import { MediaQueriesClassifier } from '../view/media-queries-classifier';
|
||||||
|
import uniqWith from 'lodash-es/uniqWith';
|
||||||
|
import isEqual from 'lodash-es/isEqual';
|
||||||
|
|
||||||
@customElement('frigate-card-media-filter')
|
@customElement('frigate-card-media-filter')
|
||||||
export class FrigateCardMediaFilter extends LitElement {
|
export class FrigateCardMediaFilter extends LitElement {
|
||||||
@@ -114,21 +114,33 @@ export class FrigateCardMediaFilter extends LitElement {
|
|||||||
|
|
||||||
const convertedTime = this._convertWhenToDateRange(mediaFilter.when);
|
const convertedTime = this._convertWhenToDateRange(mediaFilter.when);
|
||||||
const convertedFavorite = this._convertFavoriteToBoolean(mediaFilter.favorite);
|
const convertedFavorite = this._convertFavoriteToBoolean(mediaFilter.favorite);
|
||||||
|
const cameraIDs = mediaFilter.cameraIDs ?? new Set(this.cameras.keys());
|
||||||
|
|
||||||
|
// A note on views:
|
||||||
|
// - In the below, if the user selects a camera to view media for, the main
|
||||||
|
// view camera is also set to that value (e.g. a user browsing the
|
||||||
|
// gallery, chooses a different camera in the media filter, then
|
||||||
|
// subsequently chooses the live button -- they would expect the live view
|
||||||
|
// for that filtered camera not the prior camera).
|
||||||
|
// - Similarly, if the user chooses clips or snapshots, set the actual view
|
||||||
|
// to 'clips' or 'snapshots' in order to ensure the right icon is shown as
|
||||||
|
// selected in the menu.
|
||||||
if (
|
if (
|
||||||
mediaFilter.mediaType === MediaFilterMediaType.Clips ||
|
mediaFilter.mediaType === MediaFilterMediaType.Clips ||
|
||||||
mediaFilter.mediaType === MediaFilterMediaType.Snapshots
|
mediaFilter.mediaType === MediaFilterMediaType.Snapshots
|
||||||
) {
|
) {
|
||||||
const query: EventQuery = {
|
const query: EventQuery = {
|
||||||
type: QueryType.Event,
|
type: QueryType.Event,
|
||||||
cameraIDs: mediaFilter.cameraIDs ?? new Set(this.cameras.keys()),
|
cameraIDs: cameraIDs,
|
||||||
...(mediaFilter.what && { what: mediaFilter.what }),
|
...(mediaFilter.what && { what: mediaFilter.what }),
|
||||||
...(mediaFilter.where && { where: mediaFilter.where }),
|
...(mediaFilter.where && { where: mediaFilter.where }),
|
||||||
...(convertedFavorite !== null && { favorite: convertedFavorite }),
|
...(convertedFavorite !== null && { favorite: convertedFavorite }),
|
||||||
...(convertedTime && { start: convertedTime.start, end: convertedTime.end }),
|
...(convertedTime && { start: convertedTime.start, end: convertedTime.end }),
|
||||||
...(this.mediaLimit && { limit: this.mediaLimit }),
|
...(this.mediaLimit && { limit: this.mediaLimit }),
|
||||||
...(mediaFilter.mediaType === MediaFilterMediaType.Clips && { hasClip: true }),
|
...(mediaFilter.mediaType === MediaFilterMediaType.Clips && { hasClip: true }),
|
||||||
...(mediaFilter.mediaType === MediaFilterMediaType.Snapshots && { hasSnapshot: true })
|
...(mediaFilter.mediaType === MediaFilterMediaType.Snapshots && {
|
||||||
|
hasSnapshot: true,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
(
|
(
|
||||||
@@ -140,13 +152,20 @@ export class FrigateCardMediaFilter extends LitElement {
|
|||||||
this.view,
|
this.view,
|
||||||
{
|
{
|
||||||
query: new EventMediaQueries([query]),
|
query: new EventMediaQueries([query]),
|
||||||
|
|
||||||
|
// See 'A note on views' above for these two arguments.
|
||||||
|
...(cameraIDs.size === 1 && { targetCameraID: [...cameraIDs][0] }),
|
||||||
|
targetView:
|
||||||
|
mediaFilter.mediaType === MediaFilterMediaType.Clips
|
||||||
|
? 'clips'
|
||||||
|
: 'snapshots',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
)?.dispatchChangeEvent(this);
|
)?.dispatchChangeEvent(this);
|
||||||
} else if (mediaFilter.mediaType === MediaFilterMediaType.Recordings) {
|
} else if (mediaFilter.mediaType === MediaFilterMediaType.Recordings) {
|
||||||
const query: RecordingQuery = {
|
const query: RecordingQuery = {
|
||||||
type: QueryType.Recording,
|
type: QueryType.Recording,
|
||||||
cameraIDs: mediaFilter.cameraIDs ?? new Set(this.cameras.keys()),
|
cameraIDs: cameraIDs,
|
||||||
...(convertedTime && { start: convertedTime.start, end: convertedTime.end }),
|
...(convertedTime && { start: convertedTime.start, end: convertedTime.end }),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -159,6 +178,10 @@ export class FrigateCardMediaFilter extends LitElement {
|
|||||||
this.view,
|
this.view,
|
||||||
{
|
{
|
||||||
query: new RecordingMediaQueries([query]),
|
query: new RecordingMediaQueries([query]),
|
||||||
|
|
||||||
|
// See 'A note on views' above for these two arguments.
|
||||||
|
...(cameraIDs.size === 1 && { targetCameraID: [...cameraIDs][0] }),
|
||||||
|
targetView: 'recordings',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
)?.dispatchChangeEvent(this);
|
)?.dispatchChangeEvent(this);
|
||||||
@@ -184,6 +207,82 @@ export class FrigateCardMediaFilter extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected _getDefaultsFromView(): MediaFilterCoreSelection | undefined {
|
||||||
|
if (!this.view) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mediaType: MediaFilterMediaType | undefined;
|
||||||
|
let cameraIDs: Set<string> | undefined;
|
||||||
|
let what: Set<string> | undefined;
|
||||||
|
let where: Set<string> | undefined;
|
||||||
|
let favorite: boolean | undefined;
|
||||||
|
|
||||||
|
if (MediaQueriesClassifier.areEventQueries(this.view.query)) {
|
||||||
|
const queries = this.view.query.getQueries();
|
||||||
|
if (!queries) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasClips = uniqWith(
|
||||||
|
queries.map((query) => query.hasClip),
|
||||||
|
isEqual,
|
||||||
|
);
|
||||||
|
const hasSnapshots = uniqWith(
|
||||||
|
queries.map((query) => query.hasSnapshot),
|
||||||
|
isEqual,
|
||||||
|
);
|
||||||
|
if (hasClips.length === 1 && hasSnapshots.length === 1) {
|
||||||
|
mediaType = !!hasClips[0]
|
||||||
|
? MediaFilterMediaType.Clips
|
||||||
|
: !!hasSnapshots[0]
|
||||||
|
? MediaFilterMediaType.Snapshots
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cameraIDSets = uniqWith(
|
||||||
|
queries.map((query) => query.cameraIDs),
|
||||||
|
isEqual,
|
||||||
|
);
|
||||||
|
if (cameraIDSets.length === 1) {
|
||||||
|
cameraIDs = queries[0].cameraIDs;
|
||||||
|
}
|
||||||
|
const whatSets = uniqWith(
|
||||||
|
queries.map((query) => query.what),
|
||||||
|
isEqual,
|
||||||
|
);
|
||||||
|
if (whatSets.length === 1) {
|
||||||
|
what = queries[0].what;
|
||||||
|
}
|
||||||
|
const whereSets = uniqWith(
|
||||||
|
queries.map((query) => query.where),
|
||||||
|
isEqual,
|
||||||
|
);
|
||||||
|
if (whereSets.length === 1) {
|
||||||
|
where = queries[0].where;
|
||||||
|
}
|
||||||
|
const favoriteValues = uniqWith(
|
||||||
|
queries.map((query) => query.favorite),
|
||||||
|
isEqual,
|
||||||
|
);
|
||||||
|
if (favoriteValues.length === 1) {
|
||||||
|
favorite = queries[0].favorite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...(mediaType && { mediaType: mediaType }),
|
||||||
|
...(cameraIDs && { cameraIDs: cameraIDs }),
|
||||||
|
...(what && { what: what }),
|
||||||
|
...(where && { where: where }),
|
||||||
|
...(favorite !== undefined && {
|
||||||
|
favorite: favorite
|
||||||
|
? MediaFilterCoreFavoriteSelection.Favorite
|
||||||
|
: MediaFilterCoreFavoriteSelection.NotFavorite,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
const areEvents = !!(
|
const areEvents = !!(
|
||||||
this.view?.query && MediaQueriesClassifier.areEventQueries(this.view.query)
|
this.view?.query && MediaQueriesClassifier.areEventQueries(this.view.query)
|
||||||
@@ -203,6 +302,7 @@ export class FrigateCardMediaFilter extends LitElement {
|
|||||||
? !!managerCapabilities?.canFavoriteRecordings
|
? !!managerCapabilities?.canFavoriteRecordings
|
||||||
: false,
|
: false,
|
||||||
};
|
};
|
||||||
|
const defaults = this._getDefaultsFromView();
|
||||||
|
|
||||||
return html` <frigate-card-media-filter-core
|
return html` <frigate-card-media-filter-core
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@@ -211,6 +311,7 @@ export class FrigateCardMediaFilter extends LitElement {
|
|||||||
.whatOptions=${this._mediaMetadataController?.whatOptions}
|
.whatOptions=${this._mediaMetadataController?.whatOptions}
|
||||||
.whereOptions=${this._mediaMetadataController?.whereOptions}
|
.whereOptions=${this._mediaMetadataController?.whereOptions}
|
||||||
.controls=${controls}
|
.controls=${controls}
|
||||||
|
.defaults=${defaults}
|
||||||
@frigate-card:media-filter-core:change=${this._mediaFilterHandler.bind(this)}
|
@frigate-card:media-filter-core:change=${this._mediaFilterHandler.bind(this)}
|
||||||
>
|
>
|
||||||
</frigate-card-media-filter-core>`;
|
</frigate-card-media-filter-core>`;
|
||||||
@@ -249,7 +350,6 @@ export class MediaMetadataController implements ReactiveController {
|
|||||||
errorToConsole(e as Error);
|
errorToConsole(e as Error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!metadata) {
|
if (!metadata) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ export class FrigateCardSurround extends LitElement {
|
|||||||
this.cameras,
|
this.cameras,
|
||||||
this.view,
|
this.view,
|
||||||
{
|
{
|
||||||
|
targetView: this.view.view,
|
||||||
mediaType: this.fetchMedia,
|
mediaType: this.fetchMedia,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
+21
-25
@@ -45,6 +45,7 @@ export const createViewForEvents = async (
|
|||||||
query?: EventMediaQueries;
|
query?: EventMediaQueries;
|
||||||
cameraIDs?: Set<string>;
|
cameraIDs?: Set<string>;
|
||||||
mediaType?: ClipsOrSnapshotsOrAll;
|
mediaType?: ClipsOrSnapshotsOrAll;
|
||||||
|
targetCameraID?: string;
|
||||||
targetView?: FrigateCardView;
|
targetView?: FrigateCardView;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
},
|
},
|
||||||
@@ -72,17 +73,11 @@ export const createViewForEvents = async (
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return executeMediaQueryForView(
|
return executeMediaQueryForView(element, hass, cameraManager, view, query, {
|
||||||
element,
|
|
||||||
hass,
|
|
||||||
cameraManager,
|
|
||||||
view,
|
|
||||||
query,
|
|
||||||
options?.targetView ?? 'clips',
|
|
||||||
{
|
|
||||||
cameraIDs: cameraIDs,
|
cameraIDs: cameraIDs,
|
||||||
},
|
targetView: options?.targetView,
|
||||||
);
|
targetCameraID: options?.targetCameraID,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -136,6 +131,7 @@ export const createViewForRecordings = async (
|
|||||||
options?: {
|
options?: {
|
||||||
query?: RecordingMediaQueries;
|
query?: RecordingMediaQueries;
|
||||||
cameraIDs?: Set<string>;
|
cameraIDs?: Set<string>;
|
||||||
|
targetCameraID?: string;
|
||||||
targetView?: 'recording' | 'recordings';
|
targetView?: 'recording' | 'recordings';
|
||||||
targetTime?: Date;
|
targetTime?: Date;
|
||||||
start?: Date;
|
start?: Date;
|
||||||
@@ -162,18 +158,12 @@ export const createViewForRecordings = async (
|
|||||||
query = new RecordingMediaQueries(recordingQueries);
|
query = new RecordingMediaQueries(recordingQueries);
|
||||||
}
|
}
|
||||||
|
|
||||||
return executeMediaQueryForView(
|
return executeMediaQueryForView(element, hass, cameraManager, view, query, {
|
||||||
element,
|
|
||||||
hass,
|
|
||||||
cameraManager,
|
|
||||||
view,
|
|
||||||
query,
|
|
||||||
options?.targetView ?? 'recordings',
|
|
||||||
{
|
|
||||||
cameraIDs: cameraIDs,
|
cameraIDs: cameraIDs,
|
||||||
...(options?.targetTime && { targetTime: options.targetTime }),
|
targetView: options?.targetView,
|
||||||
},
|
targetCameraID: options?.targetCameraID,
|
||||||
);
|
targetTime: options?.targetTime,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const executeMediaQueryForView = async (
|
const executeMediaQueryForView = async (
|
||||||
@@ -182,9 +172,10 @@ const executeMediaQueryForView = async (
|
|||||||
cameraManager: CameraManager,
|
cameraManager: CameraManager,
|
||||||
view: View,
|
view: View,
|
||||||
query: MediaQueries,
|
query: MediaQueries,
|
||||||
targetView: FrigateCardView,
|
|
||||||
options?: {
|
options?: {
|
||||||
cameraIDs: Set<string>;
|
cameraIDs?: Set<string>;
|
||||||
|
targetCameraID?: string;
|
||||||
|
targetView?: FrigateCardView;
|
||||||
targetTime?: Date;
|
targetTime?: Date;
|
||||||
},
|
},
|
||||||
): Promise<View | null> => {
|
): Promise<View | null> => {
|
||||||
@@ -214,9 +205,10 @@ const executeMediaQueryForView = async (
|
|||||||
return (
|
return (
|
||||||
view
|
view
|
||||||
?.evolve({
|
?.evolve({
|
||||||
view: targetView,
|
|
||||||
query: query,
|
query: query,
|
||||||
queryResults: queryResults,
|
queryResults: queryResults,
|
||||||
|
view: options?.targetView,
|
||||||
|
camera: options?.targetCameraID,
|
||||||
})
|
})
|
||||||
.mergeInContext(viewerContext) ?? null
|
.mergeInContext(viewerContext) ?? null
|
||||||
);
|
);
|
||||||
@@ -235,7 +227,7 @@ const executeMediaQueryForView = async (
|
|||||||
export const findClosestMediaIndex = (
|
export const findClosestMediaIndex = (
|
||||||
mediaArray: ViewMedia[],
|
mediaArray: ViewMedia[],
|
||||||
targetTime: Date,
|
targetTime: Date,
|
||||||
cameraIDs: Set<string>,
|
cameraIDs?: Set<string>,
|
||||||
refPoint?: 'start' | 'end',
|
refPoint?: 'start' | 'end',
|
||||||
): number | null => {
|
): number | null => {
|
||||||
let bestMatch:
|
let bestMatch:
|
||||||
@@ -245,6 +237,10 @@ export const findClosestMediaIndex = (
|
|||||||
}
|
}
|
||||||
| undefined;
|
| undefined;
|
||||||
|
|
||||||
|
if (!cameraIDs) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
for (const [i, media] of mediaArray.entries()) {
|
for (const [i, media] of mediaArray.entries()) {
|
||||||
if (!cameraIDs.has(media.getCameraID())) {
|
if (!cameraIDs.has(media.getCameraID())) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
Reference in New Issue
Block a user