diff --git a/src/camera/frigate/engine-frigate.ts b/src/camera/frigate/engine-frigate.ts index 07ddcd2b..646f4ecd 100644 --- a/src/camera/frigate/engine-frigate.ts +++ b/src/camera/frigate/engine-frigate.ts @@ -197,6 +197,7 @@ export class FrigateCameraManagerEngine implements CameraManagerEngine { engine: Engine.Frigate, events: await getEvents(hass, nativeQuery), expiry: add(new Date(), { seconds: EVENT_REQUEST_CACHE_MAX_AGE_SECONDS }), + cached: false, }; } @@ -257,6 +258,7 @@ export class FrigateCameraManagerEngine implements CameraManagerEngine { expiry: add(new Date(), { seconds: RECORDING_SUMMARY_REQUEST_CACHE_MAX_AGE_SECONDS, }), + cached: false, }; } @@ -285,6 +287,7 @@ export class FrigateCameraManagerEngine implements CameraManagerEngine { type: QueryResultsType.RecordingSegments, engine: Engine.Frigate, segments: cachedSegments, + cached: true, }; } @@ -304,6 +307,7 @@ export class FrigateCameraManagerEngine implements CameraManagerEngine { type: QueryResultsType.RecordingSegments, engine: Engine.Frigate, segments: segments, + cached: false, }; } diff --git a/src/camera/manager.ts b/src/camera/manager.ts index 609791bb..c0bcb851 100644 --- a/src/camera/manager.ts +++ b/src/camera/manager.ts @@ -210,10 +210,7 @@ export class CameraManager { orderBy( // Ensure uniqueness by the ID (if specified), otherwise all elements // are assumed to be unique. - uniqBy( - mediaArray, - (media) => media.getID() ?? media, - ), + uniqBy(mediaArray, (media) => media.getID() ?? media), // Sort all items leading with the most recent. (media) => media.getStartTime(), @@ -315,9 +312,15 @@ export class CameraManager { result = await engine.getRecordingSegments(hass, this._cameras, query); } + // The engine may independently cached the results. Respect that in our + // debug logging. + if (result?.cached) { + queryCachedCount++; + } + if (result) { if (result.expiry) { - this._requestCache.set(query, result, result.expiry); + this._requestCache.set(query, { ...result, cached: true }, result.expiry); } results.set(query, result as QueryReturnType); } diff --git a/src/camera/types.ts b/src/camera/types.ts index 159f02db..f8a9f831 100644 --- a/src/camera/types.ts +++ b/src/camera/types.ts @@ -45,6 +45,7 @@ export interface QueryResults { type: QueryResultsType; engine: Engine; expiry?: Date; + cached?: boolean; } export type QueryReturnType = QT extends EventQuery diff --git a/src/components/timeline-core.ts b/src/components/timeline-core.ts index f33d2149..3e9c3404 100644 --- a/src/components/timeline-core.ts +++ b/src/components/timeline-core.ts @@ -47,6 +47,7 @@ import { getAllDependentCameras, getCameraTitle } from '../utils/camera.js'; import { createViewForEvents, createViewForRecordings, + findClosestMediaIndex, generateMediaViewerContext, } from '../utils/media-to-view'; import { CameraManager } from '../camera/manager'; @@ -387,51 +388,66 @@ export class FrigateCardTimelineCore extends LitElement { * @param properties The range change properties. * @returns */ - protected _setViewDuringRangeChange( - _targetTime: Date, - _properties: TimelineRangeChange, - ): void { + protected async _setViewDuringRangeChange( + targetTime: Date, + properties: TimelineRangeChange, + ): Promise { + const results = this.view?.queryResults; + const media = results?.getResults(); if ( + !media || + !results || !this._timeline || !this.view || - // !this.view.target?.length || + !this.hass || + !this.cameraManager || !this.cameraManager ) { return; } - // TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO - // const canSeek = !!this.view?.isViewerView(); - // const context = canSeek - // ? generateMediaViewerContextForChildren( - // this.cameraManager, - // this.view.target, - // targetTime, - // ) - // : null; + const canSeek = !!this.view?.isViewerView(); - // const childIndex = this._locked - // ? null - // : findChildIndex( - // this.view.target.children, - // targetTime, - // this._getTimelineCameraIDs(), - // properties.event.additionalEvent === 'panright' ? 'end' : 'start', - // ); + const context = canSeek + ? await generateMediaViewerContext( + this.hass, + this.cameraManager, + media, + targetTime, + ) + : null; - // if (canSeek || (childIndex !== null && childIndex !== this.view.childIndex)) { - // this.view - // .evolve({ - // ...(childIndex !== null && { - // childIndex: childIndex, - // }), - // }) // Whether or not to set the timeline window. - // .mergeInContext({ - // ...this._generateTimelineContext({ noSetWindow: true }), - // ...context, - // }) - // .dispatchChangeEvent(this); - // } + const newResults = this._locked + ? null + : results + .clone() + .resetSelectedResult() + .selectBestResult((media) => + findClosestMediaIndex( + media, + targetTime, + this._getTimelineCameraIDs(), + properties.event.additionalEvent === 'panright' ? 'end' : 'start', + ), + ); + + if ( + canSeek || + (newResults && + newResults.hasSelectedResult() && + newResults.getResult() !== results.getResult()) + ) { + this.view + .evolve({ + ...(newResults && + newResults.hasSelectedResult() && { queryResults: newResults }), + }) // Whether or not to set the timeline window. + .mergeInContext({ + ...this._generateTimelineContext({ noSetWindow: true }), + ...context, + }) + .dispatchChangeEvent(this); + } } /** @@ -537,9 +553,7 @@ export class FrigateCardTimelineCore extends LitElement { ?.clone() .resetSelectedResult() .selectResultIfFound( - (media) => - !!this.cameras && - media.getID(this.cameras.get(media.getCameraID())) === properties.item, + (media) => !!this.cameras && media.getID() === properties.item, ), }); } @@ -662,9 +676,7 @@ export class FrigateCardTimelineCore extends LitElement { ); if (options?.selectedItem) { view.queryResults?.selectResultIfFound( - (media) => - !!this.cameras && - media.getID(this.cameras.get(media.getCameraID())) === options.selectedItem, + (media) => !!this.cameras && media.getID() === options.selectedItem, ); } return view; @@ -799,7 +811,7 @@ export class FrigateCardTimelineCore extends LitElement { } const media = this.view?.queryResults?.getSelectedResult(); - const selectedId = media?.getID(this.cameras.get(media.getCameraID())); + const selectedId = media?.getID(); const firstMedia = (first).media; const secondMedia = (second).media; @@ -810,7 +822,7 @@ export class FrigateCardTimelineCore extends LitElement { first.type !== 'background' && first.type === second.type && first.id !== selectedId && - second.id != selectedId && + second.id !== selectedId && !!firstMedia && !!secondMedia && ViewMediaClassifier.isEvent(firstMedia) && diff --git a/src/view/view.ts b/src/view/view.ts index bcc83f77..e8cd9f97 100644 --- a/src/view/view.ts +++ b/src/view/view.ts @@ -1,6 +1,7 @@ -// Medium: +// Minor / later: // - TODO: ts-prune https://camchenry.com/blog/deleting-dead-code-in-typescript // - TODO: getRecordingTitle should use getCameraTitle but need hass. +// - TODO: Pass timezone to recordings & event summary endpoint. // Hard: // - TODO: Implement dragging the timeline seeking forward in both Frigate recordings & events.