From 980ba1fc6f9806ddbca02934361300c54e83e04b Mon Sep 17 00:00:00 2001 From: Dermot Duffy Date: Tue, 3 Jan 2023 20:42:31 -0800 Subject: [PATCH] Break apart view.ts into multiple files. --- src/camera/engine.ts | 5 +- src/camera/frigate/engine-frigate.ts | 7 +- src/camera/manager.ts | 5 +- src/card.ts | 2 +- src/components/gallery.ts | 2 +- src/components/image.ts | 2 +- src/components/live/live.ts | 2 +- src/components/surround.ts | 2 +- src/components/thumbnail-carousel.ts | 3 +- src/components/thumbnail.ts | 4 +- src/components/timeline-core.ts | 7 +- src/components/timeline.ts | 2 +- src/components/viewer.ts | 7 +- src/utils/media-to-view.ts | 6 +- src/utils/timeline-source.ts | 4 +- src/view/media-classifier.ts | 53 +++++++++ src/view/media-queries-classifier.ts | 15 +++ src/view/media-queries-results.ts | 91 ++++++++++++++ src/view/media-queries.ts | 61 ++++++++++ src/{view-media.ts => view/media.ts} | 62 +--------- src/{ => view}/view.ts | 172 +-------------------------- 21 files changed, 264 insertions(+), 250 deletions(-) create mode 100644 src/view/media-classifier.ts create mode 100644 src/view/media-queries-classifier.ts create mode 100644 src/view/media-queries-results.ts create mode 100644 src/view/media-queries.ts rename src/{view-media.ts => view/media.ts} (84%) rename src/{ => view}/view.ts (63%) diff --git a/src/camera/engine.ts b/src/camera/engine.ts index f1c27aae..a4f9c554 100644 --- a/src/camera/engine.ts +++ b/src/camera/engine.ts @@ -1,7 +1,7 @@ import { HomeAssistant } from 'custom-card-helpers'; import { CameraConfig } from '../types'; -import { MediaQueries, MediaQueriesResults } from '../view'; -import { ViewMedia } from '../view-media'; +import { MediaQueriesResults } from "../view/media-queries-results"; +import { ViewMedia } from '../view/media'; import { EventQuery, PartialEventQuery, @@ -11,6 +11,7 @@ import { RecordingQuery, RecordingSegmentsQuery, } from './types'; +import { MediaQueries } from '../view/media-queries'; export const CAMERA_MANAGER_ENGINE_EVENT_LIMIT_DEFAULT = 10000; diff --git a/src/camera/frigate/engine-frigate.ts b/src/camera/frigate/engine-frigate.ts index 9d9bf05d..21823a80 100644 --- a/src/camera/frigate/engine-frigate.ts +++ b/src/camera/frigate/engine-frigate.ts @@ -5,8 +5,10 @@ import getUnixTime from 'date-fns/getUnixTime'; import startOfHour from 'date-fns/startOfHour'; import { CAMERA_BIRDSEYE } from '../../const'; import { CameraConfig, RecordingSegment } from '../../types'; -import { MediaQueries, MediaQueriesClassifier, MediaQueriesResults } from '../../view'; -import { ViewMedia, ViewMediaClassifier, ViewMediaFactory } from '../../view-media'; +import { MediaQueriesResults } from "../../view/media-queries-results"; +import { MediaQueriesClassifier } from "../../view/media-queries-classifier"; +import { ViewMedia, ViewMediaFactory } from '../../view/media'; +import { ViewMediaClassifier } from "../../view/media-classifier"; import { errorToConsole } from '../../utils/basic'; import { RecordingSegmentsCache } from '../cache'; import { @@ -33,6 +35,7 @@ import { } from '../types'; import { FrigateRecording, RecordingSummary } from './types'; import { getEvents, getRecordingSegments, getRecordingsSummary, NativeFrigateEventQuery, NativeFrigateRecordingSegmentsQuery, retainEvent } from './requests'; +import { MediaQueries } from '../../view/media-queries'; const EVENT_REQUEST_CACHE_MAX_AGE_SECONDS = 60; const RECORDING_SUMMARY_REQUEST_CACHE_MAX_AGE_SECONDS = 60; diff --git a/src/camera/manager.ts b/src/camera/manager.ts index 7c79c31b..c433dba0 100644 --- a/src/camera/manager.ts +++ b/src/camera/manager.ts @@ -21,9 +21,10 @@ import { } from './types.js'; import orderBy from 'lodash-es/orderBy'; import { CameraManagerEngineFactory } from './engine-factory.js'; -import { ViewMedia } from '../view-media.js'; -import { MediaQueries, MediaQueriesResults } from '../view.js'; +import { ViewMedia } from '../view/media.js'; +import { MediaQueriesResults } from '../view/media-queries-results'; import { MemoryRequestCache } from './cache.js'; +import { MediaQueries } from '../view/media-queries.js'; export class QueryClassifier { public static isEventQuery(query: DataQuery | PartialDataQuery): query is EventQuery { diff --git a/src/card.ts b/src/card.ts index 4ead268e..049d4ffe 100644 --- a/src/card.ts +++ b/src/card.ts @@ -88,7 +88,7 @@ import { import { ResolvedMediaCache } from './utils/ha/resolved-media.js'; import { supportsFeature } from './utils/ha/update.js'; import { isValidMediaLoadedInfo } from './utils/media-info.js'; -import { View } from './view.js'; +import { View } from './view/view.js'; import pkg from '../package.json'; import { ViewContext } from 'view'; import { CameraManager } from './camera/manager.js'; diff --git a/src/components/gallery.ts b/src/components/gallery.ts index 46658f46..3ada96bd 100644 --- a/src/components/gallery.ts +++ b/src/components/gallery.ts @@ -23,7 +23,7 @@ import { } from '../utils/ha/browse-media'; import { changeViewToRecentEventsForCameraAndDependents, changeViewToRecentRecordingForCameraAndDependents } from '../utils/media-to-view.js'; import { CameraManager } from '../camera/manager.js'; -import { View } from '../view.js'; +import { View } from '../view/view.js'; import { renderProgressIndicator } from './message.js'; import './thumbnail.js'; import { THUMBNAIL_DETAILS_WIDTH_MIN } from './thumbnail.js'; diff --git a/src/components/image.ts b/src/components/image.ts index 2975f33e..7f9144c9 100644 --- a/src/components/image.ts +++ b/src/components/image.ts @@ -22,7 +22,7 @@ import { createMediaLoadedInfo, dispatchExistingMediaLoadedInfoAsEvent, } from '../utils/media-info.js'; -import { View } from '../view.js'; +import { View } from '../view/view.js'; import { dispatchErrorMessageEvent } from './message.js'; import { contentsChanged } from '../utils/basic.js'; import isEqual from 'lodash-es/isEqual'; diff --git a/src/components/live/live.ts b/src/components/live/live.ts index d1c609f7..a121bdcc 100644 --- a/src/components/live/live.ts +++ b/src/components/live/live.ts @@ -37,7 +37,7 @@ import { dispatchExistingMediaLoadedInfoAsEvent, dispatchMediaUnloadedEvent, } from '../../utils/media-info.js'; -import { dispatchViewContextChangeEvent, View } from '../../view.js'; +import { dispatchViewContextChangeEvent, View } from '../../view/view.js'; import { AutoMediaPlugin } from './../embla-plugins/automedia.js'; import { Lazyload } from './../embla-plugins/lazyload.js'; import { diff --git a/src/components/surround.ts b/src/components/surround.ts index 460dbabe..2b271514 100644 --- a/src/components/surround.ts +++ b/src/components/surround.ts @@ -17,7 +17,7 @@ import { } from '../types.js'; import { contentsChanged, dispatchFrigateCardEvent } from '../utils/basic.js'; import { CameraManager } from '../camera/manager.js'; -import { View } from '../view.js'; +import { View } from '../view/view.js'; import { ThumbnailCarouselTap } from './thumbnail-carousel.js'; import './surround-basic.js'; import { changeViewToRecentEventsForCameraAndDependents } from '../utils/media-to-view'; diff --git a/src/components/thumbnail-carousel.ts b/src/components/thumbnail-carousel.ts index d4e3becd..d061165e 100644 --- a/src/components/thumbnail-carousel.ts +++ b/src/components/thumbnail-carousel.ts @@ -19,7 +19,8 @@ import { } from '../types.js'; import { stopEventFromActivatingCardWideActions } from '../utils/action.js'; import { dispatchFrigateCardEvent } from '../utils/basic.js'; -import { MediaQueriesResults, View } from '../view.js'; +import { View } from '../view/view.js'; +import { MediaQueriesResults } from "../view/media-queries-results"; import { FrigateCardCarousel } from './carousel.js'; import './thumbnail.js'; import './carousel.js'; diff --git a/src/components/thumbnail.ts b/src/components/thumbnail.ts index 25786eaa..eed00459 100644 --- a/src/components/thumbnail.ts +++ b/src/components/thumbnail.ts @@ -13,12 +13,12 @@ import { getDurationString, prettifyTitle } from '../utils/basic.js'; import { getCameraTitle } from '../utils/camera.js'; import { renderTask } from '../utils/task.js'; import { createFetchThumbnailTask } from '../utils/thumbnail.js'; -import { View } from '../view.js'; +import { View } from '../view/view.js'; import type { MediaSeek } from './viewer.js'; import { TaskStatus } from '@lit-labs/task'; import type { CameraConfig, ExtendedHomeAssistant } from '../types.js'; -import { ViewMedia } from '../view-media.js'; +import { ViewMedia } from '../view/media.js'; import { CameraManager } from '../camera/manager.js'; // The minimum width of a thumbnail with details enabled. diff --git a/src/components/timeline-core.ts b/src/components/timeline-core.ts index edd6f04f..dce25cd6 100644 --- a/src/components/timeline-core.ts +++ b/src/components/timeline-core.ts @@ -50,12 +50,15 @@ import { generateMediaViewerContext, } from '../utils/media-to-view'; import { CameraManager } from '../camera/manager'; -import { EventMediaQueries, MediaQueries, MediaQueriesClassifier, View } from '../view'; +import { EventMediaQueries, MediaQueries } from "../view/media-queries"; +import { MediaQueriesClassifier } from "../view/media-queries-classifier"; import { dispatchMessageEvent } from './message.js'; import './thumbnail.js'; import { FrigateCardTimelineItem, TimelineDataSource } from '../utils/timeline-source'; -import { ViewMedia, ViewMediaClassifier } from '../view-media'; +import { ViewMedia } from '../view/media'; +import { ViewMediaClassifier } from "../view/media-classifier"; import { rangesOverlap } from '../camera/range'; +import { View } from '../view/view'; interface FrigateCardGroupData { id: string; diff --git a/src/components/timeline.ts b/src/components/timeline.ts index 064a70fe..581167fa 100644 --- a/src/components/timeline.ts +++ b/src/components/timeline.ts @@ -3,7 +3,7 @@ import { customElement, property } from 'lit/decorators.js'; import timelineStyle from '../scss/timeline.scss'; import { CameraConfig, ExtendedHomeAssistant, TimelineConfig } from '../types'; import { CameraManager } from '../camera/manager'; -import { View } from '../view'; +import { View } from '../view/view'; import './surround.js'; import './timeline-core.js'; diff --git a/src/components/viewer.ts b/src/components/viewer.ts index 98243ec2..56224076 100644 --- a/src/components/viewer.ts +++ b/src/components/viewer.ts @@ -30,7 +30,9 @@ import { stopEventFromActivatingCardWideActions } from '../utils/action.js'; import { contentsChanged } from '../utils/basic.js'; import { getFullDependentBrowseMediaQueryParametersOrDispatchError } from '../utils/ha/browse-media.js'; import { ResolvedMediaCache, resolveMedia } from '../utils/ha/resolved-media.js'; -import { MediaQueriesClassifier, MediaQueriesResults, View } from '../view.js'; +import { View } from '../view/view.js'; +import { MediaQueriesResults } from "../view/media-queries-results"; +import { MediaQueriesClassifier } from "../view/media-queries-classifier"; import { AutoMediaPlugin } from './embla-plugins/automedia.js'; import { Lazyload } from './embla-plugins/lazyload.js'; import { @@ -51,7 +53,8 @@ import { changeViewToRecentEventsForCameraAndDependents, changeViewToRecentRecordingForCameraAndDependents, } from '../utils/media-to-view.js'; -import { ViewMedia, ViewMediaClassifier } from '../view-media.js'; +import { ViewMedia } from '../view/media.js'; +import { ViewMediaClassifier } from "../view/media-classifier"; import { guard } from 'lit/directives/guard.js'; import { localize } from '../localize/localize.js'; diff --git a/src/utils/media-to-view.ts b/src/utils/media-to-view.ts index 0e8633b4..ba1df50d 100644 --- a/src/utils/media-to-view.ts +++ b/src/utils/media-to-view.ts @@ -4,10 +4,12 @@ import startOfHour from 'date-fns/startOfHour'; import sub from 'date-fns/sub'; import { ViewContext } from 'view'; import { CameraConfig, ClipsOrSnapshotsOrAll, FrigateCardView, RecordingSegment } from '../types'; -import { EventMediaQueries, RecordingMediaQueries, View } from '../view'; +import { View } from '../view/view'; +import { EventMediaQueries, RecordingMediaQueries } from "../view/media-queries"; import { CameraManager } from '../camera/manager'; import { getAllDependentCameras } from './camera.js'; -import { ViewMedia, ViewMediaClassifier } from '../view-media'; +import { ViewMedia } from '../view/media'; +import { ViewMediaClassifier } from "../view/media-classifier"; import { HomeAssistant } from 'custom-card-helpers'; export const changeViewToRecentEventsForCameraAndDependents = async ( diff --git a/src/utils/timeline-source.ts b/src/utils/timeline-source.ts index 3e3ee7b1..d31e513b 100644 --- a/src/utils/timeline-source.ts +++ b/src/utils/timeline-source.ts @@ -7,8 +7,8 @@ import { CameraConfig, ClipsOrSnapshotsOrAll, RecordingSegment } from '../types' import { CameraManager } from '../camera/manager'; import { EventQuery } from '../camera/types'; import { capEndDate, convertRangeToCacheFriendlyTimes } from '../camera/util'; -import { EventMediaQueries } from '../view'; -import { ViewMedia } from '../view-media'; +import { EventMediaQueries } from "../view/media-queries"; +import { ViewMedia } from '../view/media'; import { compressRanges, ExpiringMemoryRangeSet, MemoryRangeSet } from '../camera/range'; import { ModifyInterface } from './basic'; diff --git a/src/view/media-classifier.ts b/src/view/media-classifier.ts new file mode 100644 index 00000000..c5324bea --- /dev/null +++ b/src/view/media-classifier.ts @@ -0,0 +1,53 @@ +import { ModifyInterface } from '../utils/basic.js'; +import { ViewMedia, FrigateEventViewMedia, FrigateRecordingViewMedia } from './media'; + +export class ViewMediaClassifier { + public static isFrigateMedia( + media: ViewMedia, + ): media is FrigateEventViewMedia | FrigateRecordingViewMedia { + return this.isFrigateEvent(media) || this.isFrigateRecording(media); + } + public static isFrigateEvent(media: ViewMedia): media is FrigateEventViewMedia { + return media instanceof FrigateEventViewMedia; + } + public static isFrigateRecording( + media: ViewMedia, + ): media is FrigateRecordingViewMedia { + return media instanceof FrigateRecordingViewMedia; + } + + // Typescript conveniences. + public static isMediaWithStartEndTime(media: ViewMedia): media is ModifyInterface< + ViewMedia, + { + getStartTime(): Date; + getEndTime(): Date; + } + > { + return !!media.getStartTime() && !!media.getEndTime(); + } + public static isMediaWithStartTime(media: ViewMedia): media is ModifyInterface< + ViewMedia, + { + getStartTime(): Date; + } + > { + return !!media.getStartTime(); + } + public static isMediaWithEndTime(media: ViewMedia): media is ModifyInterface< + ViewMedia, + { + getEndTime(): Date; + } + > { + return !!media.getEndTime(); + } + public static isMediaWithID(media: ViewMedia): media is ModifyInterface< + ViewMedia, + { + getID(): string; + } + > { + return !!media.getID(); + } +} diff --git a/src/view/media-queries-classifier.ts b/src/view/media-queries-classifier.ts new file mode 100644 index 00000000..4508e97f --- /dev/null +++ b/src/view/media-queries-classifier.ts @@ -0,0 +1,15 @@ +import { EventMediaQueries, MediaQueries, RecordingMediaQueries } from './media-queries'; + +export class MediaQueriesClassifier { + public static areEventQueries( + queries?: MediaQueries | null, + ): queries is EventMediaQueries { + return queries instanceof EventMediaQueries; + } + + public static areRecordingQueries( + queries?: MediaQueries | null, + ): queries is RecordingMediaQueries { + return queries instanceof RecordingMediaQueries; + } +} diff --git a/src/view/media-queries-results.ts b/src/view/media-queries-results.ts new file mode 100644 index 00000000..5dc5b13e --- /dev/null +++ b/src/view/media-queries-results.ts @@ -0,0 +1,91 @@ +import clone from 'lodash-es/clone.js'; +import { ViewMedia } from './media.js'; + +export class MediaQueriesResults { + protected _results: ViewMedia[] | null = null; + protected _resultsTimestamp: Date | null = null; + protected _selectedIndex: number | null = null; + + constructor(results?: ViewMedia[], selectedIndex?: number | null) { + if (results) { + this.setResults(results); + } + if (selectedIndex !== undefined) { + this.selectResult(selectedIndex); + } + } + + public clone(): MediaQueriesResults { + // Shallow clone -- will reuse the same _results object (as there are no + // methods that support modification of the results themselves, and since + // changing the selectedIndex on a consistent set of results is a common + // operation). + return clone(this); + } + + public getResults(): ViewMedia[] | null { + return this._results; + } + public getResultsCount(): number { + return this._results?.length ?? 0; + } + public hasResults(): boolean { + return !!this._results; + } + public setResults(results: ViewMedia[]) { + this._results = results; + this._resultsTimestamp = new Date(); + } + public getResult(index?: number): ViewMedia | null { + if (!this._results || index === undefined) { + return null; + } + return this._results[index]; + } + public getSelectedResult(): ViewMedia | null { + return this._selectedIndex === null ? null : this.getResult(this._selectedIndex); + } + public getSelectedIndex(): number | null { + return this._selectedIndex; + } + public hasSelectedResult(): boolean { + return this.getSelectedResult() !== null; + } + public resetSelectedResult(): MediaQueriesResults { + this._selectedIndex = null; + return this; + } + public getResultsTimestamp(): Date | null { + return this._resultsTimestamp; + } + + public selectResult(index: number | null): MediaQueriesResults { + if ( + index === null || + (this._results && index >= 0 && index < this._results.length) + ) { + this._selectedIndex = index; + } + return this; + } + public selectResultIfFound(func: (media: ViewMedia) => boolean): MediaQueriesResults { + for (const [index, result] of this._results?.entries() ?? []) { + if (func(result)) { + this._selectedIndex = index; + break; + } + } + return this; + } + public selectBestResult( + func: (media: ViewMedia[]) => number | null, + ): MediaQueriesResults { + if (this._results) { + const resultIndex = func(this._results); + if (resultIndex !== null) { + this._selectedIndex = resultIndex; + } + } + return this; + } +} diff --git a/src/view/media-queries.ts b/src/view/media-queries.ts new file mode 100644 index 00000000..0a674619 --- /dev/null +++ b/src/view/media-queries.ts @@ -0,0 +1,61 @@ +import isEqual from 'lodash-es/isEqual'; +import cloneDeep from 'lodash-es/cloneDeep.js'; +import { EventQuery, MediaQuery, RecordingQuery } from '../camera/types.js'; + +export type MediaQueries = EventMediaQueries | RecordingMediaQueries; + +export class MediaQueriesBase { + protected _queries: T[] | null = null; + + protected constructor(queries?: T[]) { + if (queries) { + this._queries = queries; + } + } + + public clone(): MediaQueriesBase { + return cloneDeep(this); + } + + public isEqual(that: MediaQueries): boolean { + return isEqual(this.getQueries(), that.getQueries()); + } + + public getQueries(): T[] | null { + return this._queries; + } + + public setQueries(queries: T[]): void { + this._queries = queries; + } + + public setQueriesTime(start: Date, end: Date) { + for (const query of this._queries ?? []) { + query.start = start; + query.end = end; + } + } +} + +export class EventMediaQueries extends MediaQueriesBase { + constructor(queries?: EventQuery[]) { + super(queries); + } + + public convertToClipsQueries(): void { + for (const query of this._queries ?? []) { + delete query.hasSnapshot; + query.hasClip = true; + } + } + + public clone(): EventMediaQueries { + return cloneDeep(this); + } +} + +export class RecordingMediaQueries extends MediaQueriesBase { + constructor(queries?: RecordingQuery[]) { + super(queries); + } +} diff --git a/src/view-media.ts b/src/view/media.ts similarity index 84% rename from src/view-media.ts rename to src/view/media.ts index 67952a7d..13347ff2 100644 --- a/src/view-media.ts +++ b/src/view/media.ts @@ -1,74 +1,18 @@ import fromUnixTime from 'date-fns/fromUnixTime'; import isEqual from 'lodash-es/isEqual'; -import { - BrowseMediaSource, - CameraConfig, - MEDIA_TYPE_IMAGE, -} from './types.js'; -import { ModifyInterface } from './utils/basic.js'; +import { BrowseMediaSource, CameraConfig, MEDIA_TYPE_IMAGE } from '../types.js'; import { getEventMediaContentID, getEventThumbnailURL, getEventTitle, getRecordingMediaContentID, getRecordingTitle, -} from './camera/frigate/util.js'; -import { FrigateEvent, FrigateRecording } from './camera/frigate/types.js'; +} from '../camera/frigate/util.js'; +import { FrigateEvent, FrigateRecording } from '../camera/frigate/types.js'; export type ViewMediaType = 'clip' | 'snapshot' | 'recording'; export type ViewMediaSourceType = FrigateEvent | FrigateRecording | BrowseMediaSource; -export class ViewMediaClassifier { - public static isFrigateMedia( - media: ViewMedia, - ): media is FrigateEventViewMedia | FrigateRecordingViewMedia { - return this.isFrigateEvent(media) || this.isFrigateRecording(media); - } - public static isFrigateEvent(media: ViewMedia): media is FrigateEventViewMedia { - return media instanceof FrigateEventViewMedia; - } - public static isFrigateRecording( - media: ViewMedia, - ): media is FrigateRecordingViewMedia { - return media instanceof FrigateRecordingViewMedia; - } - - // Typescript conveniences. - public static isMediaWithStartEndTime(media: ViewMedia): media is ModifyInterface< - ViewMedia, - { - getStartTime(): Date; - getEndTime(): Date; - } - > { - return !!media.getStartTime() && !!media.getEndTime(); - } - public static isMediaWithStartTime(media: ViewMedia): media is ModifyInterface< - ViewMedia, - { - getStartTime(): Date; - } - > { - return !!media.getStartTime(); - } - public static isMediaWithEndTime(media: ViewMedia): media is ModifyInterface< - ViewMedia, - { - getEndTime(): Date; - } - > { - return !!media.getEndTime(); - } - public static isMediaWithID(media: ViewMedia): media is ModifyInterface< - ViewMedia, - { - getID(): string; - } - > { - return !!media.getID(); - } -} - class ViewMediaBase { protected _mediaType: ViewMediaType; protected _cameraID: string; diff --git a/src/view.ts b/src/view/view.ts similarity index 63% rename from src/view.ts rename to src/view/view.ts index 3cc0c2fe..d969c69b 100644 --- a/src/view.ts +++ b/src/view/view.ts @@ -23,19 +23,16 @@ // - TODO: What should the timeline do when an event is clicked on that is not in the queryResults (or if queryResults is empty)? // - TODO: Should the timeline data source clear events (as it currently does) when the query changes? -import isEqual from 'lodash-es/isEqual'; -import clone from 'lodash-es/clone.js'; -import cloneDeep from 'lodash-es/cloneDeep.js'; import { ViewContext } from 'view'; import { FrigateCardUserSpecifiedView, FrigateCardView, FRIGATE_CARD_VIEWS_USER_SPECIFIED, FRIGATE_CARD_VIEW_DEFAULT, -} from './types.js'; -import { dispatchFrigateCardEvent } from './utils/basic.js'; -import { EventQuery, MediaQuery, RecordingQuery } from './camera/types.js'; -import { ViewMedia } from './view-media.js'; +} from '../types.js'; +import { dispatchFrigateCardEvent } from '../utils/basic.js'; +import { MediaQueries } from './media-queries'; +import { MediaQueriesResults } from './media-queries-results'; export interface ViewEvolveParameters { view?: FrigateCardView; @@ -50,167 +47,6 @@ export interface ViewParameters extends ViewEvolveParameters { camera: string; } -export class MediaQueriesClassifier { - public static areEventQueries( - queries?: MediaQueries | null, - ): queries is EventMediaQueries { - return queries instanceof EventMediaQueries; - } - - public static areRecordingQueries( - queries?: MediaQueries | null, - ): queries is RecordingMediaQueries { - return queries instanceof RecordingMediaQueries; - } -} - -export class MediaQueriesBase { - protected _queries: T[] | null = null; - - protected constructor(queries?: T[]) { - if (queries) { - this._queries = queries; - } - } - - public clone(): MediaQueriesBase { - return cloneDeep(this); - } - - public isEqual(that: MediaQueries): boolean { - return isEqual(this.getQueries(), that.getQueries()); - } - - public getQueries(): T[] | null { - return this._queries; - } - - public setQueries(queries: T[]): void { - this._queries = queries; - } - - public setQueriesTime(start: Date, end: Date) { - for (const query of this._queries ?? []) { - query.start = start; - query.end = end; - } - } -} - -export class EventMediaQueries extends MediaQueriesBase { - constructor(queries?: EventQuery[]) { - super(queries); - } - - public convertToClipsQueries(): void { - for (const query of this._queries ?? []) { - delete query.hasSnapshot; - query.hasClip = true; - } - } - - public clone(): EventMediaQueries { - return cloneDeep(this); - } -} - -export class RecordingMediaQueries extends MediaQueriesBase { - constructor(queries?: RecordingQuery[]) { - super(queries); - } -} - -export type MediaQueries = EventMediaQueries | RecordingMediaQueries; - -export class MediaQueriesResults { - protected _results: ViewMedia[] | null = null; - protected _resultsTimestamp: Date | null = null; - protected _selectedIndex: number | null = null; - - constructor(results?: ViewMedia[], selectedIndex?: number | null) { - if (results) { - this.setResults(results); - } - if (selectedIndex !== undefined) { - this.selectResult(selectedIndex); - } - } - - public clone(): MediaQueriesResults { - // Shallow clone -- will reuse the same _results object (as there are no - // methods that support modification of the results themselves, and since - // changing the selectedIndex on a consistent set of results is a common - // operation). - return clone(this); - } - - public getResults(): ViewMedia[] | null { - return this._results; - } - public getResultsCount(): number { - return this._results?.length ?? 0; - } - public hasResults(): boolean { - return !!this._results; - } - public setResults(results: ViewMedia[]) { - this._results = results; - this._resultsTimestamp = new Date(); - } - public getResult(index?: number): ViewMedia | null { - if (!this._results || index === undefined) { - return null; - } - return this._results[index]; - } - public getSelectedResult(): ViewMedia | null { - return this._selectedIndex === null ? null : this.getResult(this._selectedIndex); - } - public getSelectedIndex(): number | null { - return this._selectedIndex; - } - public hasSelectedResult(): boolean { - return this.getSelectedResult() !== null; - } - public resetSelectedResult(): MediaQueriesResults { - this._selectedIndex = null; - return this; - } - public getResultsTimestamp(): Date | null { - return this._resultsTimestamp; - } - - public selectResult(index: number | null): MediaQueriesResults { - if ( - index === null || - (this._results && index >= 0 && index < this._results.length) - ) { - this._selectedIndex = index; - } - return this; - } - public selectResultIfFound(func: (media: ViewMedia) => boolean): MediaQueriesResults { - for (const [index, result] of this._results?.entries() ?? []) { - if (func(result)) { - this._selectedIndex = index; - break; - } - } - return this; - } - public selectBestResult( - func: (media: ViewMedia[]) => number | null, - ): MediaQueriesResults { - if (this._results) { - const resultIndex = func(this._results); - if (resultIndex !== null) { - this._selectedIndex = resultIndex; - } - } - return this; - } -} - export class View { public view: FrigateCardView; public camera: string;