Add support for timeline seeking locked to a camera.
This commit is contained in:
@@ -854,6 +854,17 @@ timeline:
|
|||||||
|
|
||||||
<a name="dimensions"></a>
|
<a name="dimensions"></a>
|
||||||
|
|
||||||
|
#### 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 |
|
||||||
|
| - | - |
|
||||||
|
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>filmstrip-box-multiple</title><path d="M4,6H2V20A2,2 0 0,0 4,22H18V20H4V6M20,2H8A2,2 0 0,0 6,4V16A2,2 0 0,0 8,18H20A2,2 0 0,0 22,16V4A2,2 0 0,0 20,2M10,15H8V13H10V15M10,11H8V9H10V11M10,7H8V5H10V7M20,15H18V13H20V15M20,11H18V9H20V11M20,7H18V5H20V7Z" /></svg> | 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. |
|
||||||
|
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>play-box-lock</title><path d="M23 17.3V20.8C23 21.4 22.4 22 21.7 22H16.2C15.6 22 15 21.4 15 20.7V17.2C15 16.6 15.6 16 16.2 16V14.5C16.2 13.1 17.6 12 19 12C20.4 12 21.8 13.1 21.8 14.5V16C22.4 16 23 16.6 23 17.3M13 19V21H4C2.89 21 2 20.1 2 19V5C2 3.89 2.89 3 4 3H18C19.1 3 20 3.89 20 5V10.1L19 10L18 10.1C15.79 10.55 14.12 12.45 14 14.76C13.39 15.31 13 16.11 13 17V19M20.5 14.5C20.5 13.7 19.8 13.2 19 13.2C18.2 13.2 17.5 13.7 17.5 14.5V16H20.5V14.5M9 8V16L14 12L9 8Z" /></svg> | Dragging the timeline will seek within the selected media item only. |
|
||||||
|
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>camera-lock</title><path d="M4 4H7L9 2H15L17 4H20C21.11 4 22 4.89 22 6V12C21.16 11.37 20.13 11 19 11C18.21 11 17.46 11.18 16.79 11.5C16.18 9.22 14.27 7 12 7C9.24 7 7 9.24 7 12C7 14.76 9.24 17 12 17C12.42 17 12.84 16.95 13.23 16.85C13.08 17.2 13 17.59 13 18V20H4C2.9 20 2 19.11 2 18V6C2 4.89 2.9 4 4 4M12 9C13.66 9 15 10.34 15 12C15 13.66 13.66 15 12 15C10.34 15 9 13.66 9 12C9 10.34 10.34 9 12 9M23 18.3V21.8C23 22.4 22.4 23 21.7 23H16.2C15.6 23 15 22.4 15 21.7V18.2C15 17.6 15.6 17 16.2 17V15.5C16.2 14.1 17.6 13 19 13C20.4 13 21.8 14.1 21.8 15.5V17C22.4 17 23 17.6 23 18.3M20.5 15.5C20.5 14.7 19.8 14.2 19 14.2C18.2 14.2 17.5 14.7 17.5 15.5V17H20.5V15.5Z" /></svg> | Dragging the timeline will seek / select across all available media from the selected camera only, selecting the media item with the longest duration. |
|
||||||
|
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>pan-horizontal</title><path d="M7,8L2.5,12L7,16V8M17,8V16L21.5,12L17,8M12,10A2,2 0 0,0 10,12A2,2 0 0,0 12,14A2,2 0 0,0 14,12A2,2 0 0,0 12,10Z" /></svg> | Dragging the timeline will pan only without selected or seeking any media. |
|
||||||
|
|
||||||
### Dimensions Options
|
### Dimensions Options
|
||||||
|
|
||||||
These options control the aspect-ratio of the entire card to make placement in
|
These options control the aspect-ratio of the entire card to make placement in
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ import {
|
|||||||
MediaQueriesClassifier,
|
MediaQueriesClassifier,
|
||||||
MediaQueriesType,
|
MediaQueriesType,
|
||||||
} from '../view/media-queries-classifier';
|
} from '../view/media-queries-classifier';
|
||||||
|
import { MediaQueriesResults } from '../view/media-queries-results';
|
||||||
import { View } from '../view/view';
|
import { View } from '../view/view';
|
||||||
import './date-picker.js';
|
import './date-picker.js';
|
||||||
import { DatePickerEvent, FrigateCardDatePicker } from './date-picker.js';
|
import { DatePickerEvent, FrigateCardDatePicker } from './date-picker.js';
|
||||||
@@ -85,7 +86,7 @@ interface TimelineViewContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TimelineItemClickAction = 'play' | 'select';
|
type TimelineItemClickAction = 'play' | 'select';
|
||||||
type TimelinePanBehavior = 'pan' | 'seek' | 'seek-in-media';
|
type TimelinePanBehavior = 'pan' | 'seek' | 'seek-in-media' | 'seek-in-camera';
|
||||||
|
|
||||||
declare module 'view' {
|
declare module 'view' {
|
||||||
interface ViewContext {
|
interface ViewContext {
|
||||||
@@ -288,13 +289,17 @@ export class FrigateCardTimelineCore extends LitElement {
|
|||||||
? localize('timeline.pan_behavior.pan')
|
? localize('timeline.pan_behavior.pan')
|
||||||
: this._panBehavior === 'seek'
|
: this._panBehavior === 'seek'
|
||||||
? localize('timeline.pan_behavior.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 =
|
const panIcon =
|
||||||
this._panBehavior === 'pan'
|
this._panBehavior === 'pan'
|
||||||
? 'mdi:pan-horizontal'
|
? 'mdi:pan-horizontal'
|
||||||
: this._panBehavior === 'seek'
|
: this._panBehavior === 'seek'
|
||||||
? 'mdi:filmstrip'
|
? 'mdi:filmstrip-box-multiple'
|
||||||
: 'mdi:lock';
|
: this._panBehavior === 'seek-in-media'
|
||||||
|
? 'mdi:play-box-lock'
|
||||||
|
: 'mdi:camera-lock';
|
||||||
|
|
||||||
return html` ${capabilities?.supportsTimeline
|
return html` ${capabilities?.supportsTimeline
|
||||||
? html` <div
|
? html` <div
|
||||||
@@ -314,6 +319,8 @@ export class FrigateCardTimelineCore extends LitElement {
|
|||||||
? 'seek'
|
? 'seek'
|
||||||
: this._panBehavior === 'seek'
|
: this._panBehavior === 'seek'
|
||||||
? 'seek-in-media'
|
? 'seek-in-media'
|
||||||
|
: this._panBehavior === 'seek-in-media'
|
||||||
|
? 'seek-in-camera'
|
||||||
: 'pan';
|
: 'pan';
|
||||||
}}
|
}}
|
||||||
aria-label="${panTitle}"
|
aria-label="${panTitle}"
|
||||||
@@ -406,10 +413,13 @@ export class FrigateCardTimelineCore extends LitElement {
|
|||||||
const targetBarOn =
|
const targetBarOn =
|
||||||
this._shouldSupportSeeking() &&
|
this._shouldSupportSeeking() &&
|
||||||
(this._panBehavior === 'seek' ||
|
(this._panBehavior === 'seek' ||
|
||||||
|
this._panBehavior === 'seek-in-camera' ||
|
||||||
(this._panBehavior === 'seek-in-media' &&
|
(this._panBehavior === 'seek-in-media' &&
|
||||||
this._timeline.getSelection().some((id) => {
|
this._timeline.getSelection().some((id) => {
|
||||||
const item = this._timelineSource?.dataset?.get(id);
|
const item = this._timelineSource?.dataset?.get(id);
|
||||||
return (
|
return (
|
||||||
|
this._panBehavior !== 'seek-in-camera' ||
|
||||||
|
item?.media?.getCameraID() === this.view?.camera,
|
||||||
item &&
|
item &&
|
||||||
item.start &&
|
item.start &&
|
||||||
item.end &&
|
item.end &&
|
||||||
@@ -482,19 +492,30 @@ export class FrigateCardTimelineCore extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const canSeek = this._shouldSupportSeeking();
|
const canSeek = this._shouldSupportSeeking();
|
||||||
const newResults =
|
let newResults: MediaQueriesResults | null = null;
|
||||||
this._panBehavior === 'seek-in-media'
|
|
||||||
? results
|
if (this._panBehavior === 'seek') {
|
||||||
: results
|
newResults = results
|
||||||
.clone()
|
.clone()
|
||||||
.resetSelectedResult()
|
.resetSelectedResult()
|
||||||
.selectBestResult(
|
.selectBestResult(
|
||||||
(media) => findBestMediaIndex(media, targetTime, this.view?.camera),
|
(mediaArray) => findBestMediaIndex(mediaArray, targetTime, this.view?.camera),
|
||||||
{
|
{
|
||||||
allCameras: true,
|
allCameras: true,
|
||||||
main: 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
|
const desiredView: FrigateCardView = this.mini
|
||||||
? targetTime >= new Date()
|
? targetTime >= new Date()
|
||||||
|
|||||||
@@ -516,6 +516,7 @@
|
|||||||
"pan_behavior": {
|
"pan_behavior": {
|
||||||
"pan": "Pan",
|
"pan": "Pan",
|
||||||
"seek": "Pan seeks across all media",
|
"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"
|
"seek-in-media": "Pan seeks within selected media item only"
|
||||||
},
|
},
|
||||||
"select_date": "Choose date"
|
"select_date": "Choose date"
|
||||||
|
|||||||
@@ -504,6 +504,7 @@
|
|||||||
"pan_behavior": {
|
"pan_behavior": {
|
||||||
"pan": "",
|
"pan": "",
|
||||||
"seek": "",
|
"seek": "",
|
||||||
|
"seek-in-camera": "",
|
||||||
"seek-in-media": ""
|
"seek-in-media": ""
|
||||||
},
|
},
|
||||||
"select_date": "Scegli la data"
|
"select_date": "Scegli la data"
|
||||||
|
|||||||
@@ -515,6 +515,7 @@
|
|||||||
"pan_behavior": {
|
"pan_behavior": {
|
||||||
"pan": "",
|
"pan": "",
|
||||||
"seek": "",
|
"seek": "",
|
||||||
|
"seek-in-camera": "",
|
||||||
"seek-in-media": ""
|
"seek-in-media": ""
|
||||||
},
|
},
|
||||||
"select_date": "Escolha a data"
|
"select_date": "Escolha a data"
|
||||||
|
|||||||
@@ -495,9 +495,10 @@
|
|||||||
},
|
},
|
||||||
"timeline": {
|
"timeline": {
|
||||||
"pan_behavior": {
|
"pan_behavior": {
|
||||||
"pan": "Pan",
|
"pan": "",
|
||||||
"seek": "Pan seeks across all media",
|
"seek": "",
|
||||||
"seek-in-media": "Pan seeks within selected media item only"
|
"seek-in-camera": "",
|
||||||
|
"seek-in-media": ""
|
||||||
},
|
},
|
||||||
"select_date": "Selecionar a data"
|
"select_date": "Selecionar a data"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user