From 87f137b474565bfe1a07478f49f6979b56db2c85 Mon Sep 17 00:00:00 2001 From: Dermot Duffy Date: Sat, 26 Aug 2023 11:11:44 -0700 Subject: [PATCH] Add support for timeline seeking locked to a camera. --- README.md | 11 ++++++ src/components/timeline-core.ts | 63 ++++++++++++++++++++----------- src/localize/languages/en.json | 1 + src/localize/languages/it.json | 1 + src/localize/languages/pt-BR.json | 1 + src/localize/languages/pt-PT.json | 7 ++-- 6 files changed, 60 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 334d3166..124f6e59 100644 --- a/README.md +++ b/README.md @@ -854,6 +854,17 @@ timeline: +#### Timeline Seek Behavior + +The behavior of the timeline during seeking/dragging can be controlled by means of the icon on the bottom-right of the timeline. + +| Option | Behavior | +| - | - | +| filmstrip-box-multiple | Dragging the timeline will seek / select across all available media from all cameras, selecting the media item with the longest duration whilst favoring (but not limited to) the currently selected camera. | +| play-box-lock | Dragging the timeline will seek within the selected media item only. | +| camera-lock | Dragging the timeline will seek / select across all available media from the selected camera only, selecting the media item with the longest duration. | +| pan-horizontal | Dragging the timeline will pan only without selected or seeking any media. | + ### Dimensions Options These options control the aspect-ratio of the entire card to make placement in diff --git a/src/components/timeline-core.ts b/src/components/timeline-core.ts index 3e63d4d7..126ea111 100644 --- a/src/components/timeline-core.ts +++ b/src/components/timeline-core.ts @@ -63,6 +63,7 @@ import { MediaQueriesClassifier, MediaQueriesType, } from '../view/media-queries-classifier'; +import { MediaQueriesResults } from '../view/media-queries-results'; import { View } from '../view/view'; import './date-picker.js'; import { DatePickerEvent, FrigateCardDatePicker } from './date-picker.js'; @@ -85,7 +86,7 @@ interface TimelineViewContext { } type TimelineItemClickAction = 'play' | 'select'; -type TimelinePanBehavior = 'pan' | 'seek' | 'seek-in-media'; +type TimelinePanBehavior = 'pan' | 'seek' | 'seek-in-media' | 'seek-in-camera'; declare module 'view' { interface ViewContext { @@ -288,13 +289,17 @@ export class FrigateCardTimelineCore extends LitElement { ? localize('timeline.pan_behavior.pan') : this._panBehavior === 'seek' ? localize('timeline.pan_behavior.seek') - : localize('timeline.pan_behavior.seek-in-media'); + : this._panBehavior === 'seek-in-media' + ? localize('timeline.pan_behavior.seek-in-media') + : localize('timeline.pan_behavior.seek-in-camera'); const panIcon = this._panBehavior === 'pan' ? 'mdi:pan-horizontal' : this._panBehavior === 'seek' - ? 'mdi:filmstrip' - : 'mdi:lock'; + ? 'mdi:filmstrip-box-multiple' + : this._panBehavior === 'seek-in-media' + ? 'mdi:play-box-lock' + : 'mdi:camera-lock'; return html` ${capabilities?.supportsTimeline ? html`
{ const item = this._timelineSource?.dataset?.get(id); return ( + this._panBehavior !== 'seek-in-camera' || + item?.media?.getCameraID() === this.view?.camera, item && - item.start && - item.end && - targetTime.getTime() >= item.start && - targetTime.getTime() <= item.end + item.start && + item.end && + targetTime.getTime() >= item.start && + targetTime.getTime() <= item.end ); }))); @@ -482,19 +492,30 @@ export class FrigateCardTimelineCore extends LitElement { } const canSeek = this._shouldSupportSeeking(); - const newResults = - this._panBehavior === 'seek-in-media' - ? results - : results - .clone() - .resetSelectedResult() - .selectBestResult( - (media) => findBestMediaIndex(media, targetTime, this.view?.camera), - { - allCameras: true, - main: true, - }, - ); + let newResults: MediaQueriesResults | null = null; + + if (this._panBehavior === 'seek') { + newResults = results + .clone() + .resetSelectedResult() + .selectBestResult( + (mediaArray) => findBestMediaIndex(mediaArray, targetTime, this.view?.camera), + { + allCameras: true, + main: true, + }, + ); + } else if (this._panBehavior === 'seek-in-camera') { + newResults = results + .clone() + .resetSelectedResult() + .selectBestResult((mediaArray) => findBestMediaIndex(mediaArray, targetTime), { + cameraID: this.view.camera, + }) + .promoteCameraSelectionToMainSelection(this.view.camera); + } else if (this._panBehavior === 'seek-in-media') { + newResults = results; + } const desiredView: FrigateCardView = this.mini ? targetTime >= new Date() diff --git a/src/localize/languages/en.json b/src/localize/languages/en.json index e84d933f..d95f7d5b 100644 --- a/src/localize/languages/en.json +++ b/src/localize/languages/en.json @@ -516,6 +516,7 @@ "pan_behavior": { "pan": "Pan", "seek": "Pan seeks across all media", + "seek-in-camera": "Pan seeks within selected camera only", "seek-in-media": "Pan seeks within selected media item only" }, "select_date": "Choose date" diff --git a/src/localize/languages/it.json b/src/localize/languages/it.json index 35a150c4..25fdaf4a 100644 --- a/src/localize/languages/it.json +++ b/src/localize/languages/it.json @@ -504,6 +504,7 @@ "pan_behavior": { "pan": "", "seek": "", + "seek-in-camera": "", "seek-in-media": "" }, "select_date": "Scegli la data" diff --git a/src/localize/languages/pt-BR.json b/src/localize/languages/pt-BR.json index 1609d398..f5da9fb5 100644 --- a/src/localize/languages/pt-BR.json +++ b/src/localize/languages/pt-BR.json @@ -515,6 +515,7 @@ "pan_behavior": { "pan": "", "seek": "", + "seek-in-camera": "", "seek-in-media": "" }, "select_date": "Escolha a data" diff --git a/src/localize/languages/pt-PT.json b/src/localize/languages/pt-PT.json index 57fc800f..f25d95f6 100644 --- a/src/localize/languages/pt-PT.json +++ b/src/localize/languages/pt-PT.json @@ -495,9 +495,10 @@ }, "timeline": { "pan_behavior": { - "pan": "Pan", - "seek": "Pan seeks across all media", - "seek-in-media": "Pan seeks within selected media item only" + "pan": "", + "seek": "", + "seek-in-camera": "", + "seek-in-media": "" }, "select_date": "Selecionar a data" }