Move seeking support into the engine.
This commit is contained in:
@@ -160,6 +160,7 @@ export class FrigateCardThumbnailCarousel extends LitElement {
|
||||
'slide-selected': this.selected === index,
|
||||
};
|
||||
|
||||
const seekTarget = this.view?.context?.mediaViewer?.seek;
|
||||
return html` <frigate-card-thumbnail
|
||||
class="${classMap(classes)}"
|
||||
.cameraManager=${this.cameraManager}
|
||||
@@ -167,7 +168,7 @@ export class FrigateCardThumbnailCarousel extends LitElement {
|
||||
.media=${media}
|
||||
.cameraConfig=${cameraConfig}
|
||||
.view=${this.view}
|
||||
.mediaSeek=${this.view?.context?.mediaViewer?.seek.get(index)}
|
||||
.seek=${seekTarget && media.includesTime(seekTarget) ? seekTarget : undefined}
|
||||
?details=${!!this.config?.show_details}
|
||||
?show_favorite_control=${this.config?.show_favorite_control}
|
||||
?show_timeline_control=${this.config?.show_timeline_control}
|
||||
|
||||
@@ -14,7 +14,6 @@ import { getCameraTitle } from '../utils/camera.js';
|
||||
import { renderTask } from '../utils/task.js';
|
||||
import { createFetchThumbnailTask } from '../utils/thumbnail.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';
|
||||
@@ -131,7 +130,7 @@ export class FrigateCardThumbnailDetailsEvent extends LitElement {
|
||||
public media?: EventViewMedia;
|
||||
|
||||
@property({ attribute: false })
|
||||
public mediaSeek?: MediaSeek;
|
||||
public seek?: Date;
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
if (!this.media) {
|
||||
@@ -158,10 +157,10 @@ export class FrigateCardThumbnailDetailsEvent extends LitElement {
|
||||
>
|
||||
</div>`
|
||||
: ``}
|
||||
${this.mediaSeek
|
||||
${this.seek
|
||||
? html` <div>
|
||||
<span class="heading">${localize('event.seek')}</span>
|
||||
<span>${format(fromUnixTime(this.mediaSeek.seekTime), 'HH:mm:ss')}</span>
|
||||
<span>${format(this.seek, 'HH:mm:ss')}</span>
|
||||
</div>`
|
||||
: html``}
|
||||
</div>
|
||||
@@ -183,7 +182,7 @@ export class FrigateCardThumbnailDetailsRecording extends LitElement {
|
||||
public media?: RecordingViewMedia;
|
||||
|
||||
@property({ attribute: false })
|
||||
public mediaSeek?: MediaSeek;
|
||||
public seek?: Date;
|
||||
|
||||
@property({ attribute: false })
|
||||
public cameraTitle?: string;
|
||||
@@ -195,10 +194,10 @@ export class FrigateCardThumbnailDetailsRecording extends LitElement {
|
||||
const eventCount = this.media.getEventCount();
|
||||
return html`<div class="left">
|
||||
<div class="larger">${this.cameraTitle ?? ''}</div>
|
||||
${this.mediaSeek
|
||||
${this.seek
|
||||
? html` <div>
|
||||
<span class="heading">${localize('recording.seek')}</span>
|
||||
<span>${format(fromUnixTime(this.mediaSeek.seekTime), 'HH:mm:ss')}</span>
|
||||
<span>${format(this.seek, 'HH:mm:ss')}</span>
|
||||
</div>`
|
||||
: html``}
|
||||
</div>
|
||||
@@ -242,7 +241,7 @@ export class FrigateCardThumbnail extends LitElement {
|
||||
public show_timeline_control = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
public mediaSeek?: MediaSeek;
|
||||
public seek?: Date;
|
||||
|
||||
@property({ attribute: false })
|
||||
public view?: Readonly<View>;
|
||||
@@ -314,13 +313,13 @@ export class FrigateCardThumbnail extends LitElement {
|
||||
${this.details && ViewMediaClassifier.isEvent(this.media)
|
||||
? html`<frigate-card-thumbnail-details-event
|
||||
.media=${this.media ?? undefined}
|
||||
.mediaSeek=${this.mediaSeek}
|
||||
.seek=${this.seek}
|
||||
></frigate-card-thumbnail-details-event>`
|
||||
: this.details && ViewMediaClassifier.isRecording(this.media)
|
||||
? html`<frigate-card-thumbnail-details-recording
|
||||
.media=${this.media ?? undefined}
|
||||
.cameraTitle=${getCameraTitle(this.hass, this.cameraConfig)}
|
||||
.mediaSeek=${this.mediaSeek}
|
||||
.seek=${this.seek}
|
||||
></frigate-card-thumbnail-details-recording>`
|
||||
: html``}
|
||||
${shouldShowTimelineControl
|
||||
|
||||
@@ -48,7 +48,6 @@ import {
|
||||
createViewForEvents,
|
||||
createViewForRecordings,
|
||||
findClosestMediaIndex,
|
||||
generateMediaViewerContext,
|
||||
} from '../utils/media-to-view';
|
||||
import { CameraManager } from '../camera/manager';
|
||||
import { EventMediaQueries, MediaQueries } from '../view/media-queries';
|
||||
@@ -407,16 +406,6 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
}
|
||||
|
||||
const canSeek = !!this.view?.isViewerView();
|
||||
|
||||
const context = canSeek
|
||||
? await generateMediaViewerContext(
|
||||
this.hass,
|
||||
this.cameraManager,
|
||||
media,
|
||||
targetTime,
|
||||
)
|
||||
: null;
|
||||
|
||||
const newResults = this._locked
|
||||
? null
|
||||
: results
|
||||
@@ -444,7 +433,7 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
}) // Whether or not to set the timeline window.
|
||||
.mergeInContext({
|
||||
...this._generateTimelineContext({ noSetWindow: true }),
|
||||
...context,
|
||||
...(canSeek && { mediaViewer: { seek: targetTime }})
|
||||
})
|
||||
.dispatchChangeEvent(this);
|
||||
}
|
||||
@@ -533,12 +522,7 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
// view change.
|
||||
if (eventView && results && results.length) {
|
||||
eventView.mergeInContext(
|
||||
await generateMediaViewerContext(
|
||||
this.hass,
|
||||
this.cameraManager,
|
||||
results,
|
||||
properties.time,
|
||||
),
|
||||
{mediaViewer: {seek: properties.time}}
|
||||
);
|
||||
view = eventView;
|
||||
}
|
||||
@@ -608,7 +592,7 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
}
|
||||
|
||||
const prefetchedWindow = this._getPrefetchWindow(properties);
|
||||
await this._timelineSource?.refresh(this.hass, this.cameras, prefetchedWindow);
|
||||
await this._timelineSource?.refresh(this.hass, prefetchedWindow);
|
||||
|
||||
// Don't show event thumbnails if the user is looking at recordings,
|
||||
// as the recording "hours" are the media, not the event
|
||||
@@ -918,7 +902,7 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
// (via fetchIfNecessary) may update the timeline contents which causes
|
||||
// the visjs timeline to stop dragging/panning operations which is very
|
||||
// disruptive to the user.
|
||||
await this._timelineSource?.refresh(this.hass, this.cameras, prefetchedWindow);
|
||||
await this._timelineSource?.refresh(this.hass, prefetchedWindow);
|
||||
}
|
||||
|
||||
const mediaID = media?.getID();
|
||||
|
||||
+26
-22
@@ -58,17 +58,8 @@ import { guard } from 'lit/directives/guard.js';
|
||||
import { localize } from '../localize/localize.js';
|
||||
import { MediaQueriesResults } from '../view/media-queries-results.js';
|
||||
|
||||
export interface MediaSeek {
|
||||
// Specifies the point at which this recording should be played, the
|
||||
// seek_time is the date of the desired play point (for display purposes
|
||||
// usually), and seek_seconds is the number of seconds to seek into the video
|
||||
// stream to reach that point.
|
||||
seekTime: number;
|
||||
seekSeconds: number;
|
||||
}
|
||||
|
||||
export interface MediaViewerViewContext {
|
||||
seek: Map<number, MediaSeek>;
|
||||
seek?: Date;
|
||||
}
|
||||
|
||||
declare module 'view' {
|
||||
@@ -260,6 +251,8 @@ export class FrigateCardViewerCarousel extends LitElement {
|
||||
* @param changedProperties The properties that were changed in this render.
|
||||
*/
|
||||
updated(changedProperties: PropertyValues): void {
|
||||
super.updated(changedProperties);
|
||||
|
||||
if (changedProperties.has('view')) {
|
||||
const oldView = changedProperties.get('view') as View | undefined;
|
||||
// Seek into the video if the seek time has changed (this is also called
|
||||
@@ -269,7 +262,6 @@ export class FrigateCardViewerCarousel extends LitElement {
|
||||
this._seekHandler();
|
||||
}
|
||||
}
|
||||
super.updated(changedProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -418,17 +410,26 @@ export class FrigateCardViewerCarousel extends LitElement {
|
||||
* Handle the user selecting a new slide in the carousel.
|
||||
*/
|
||||
protected _setViewHandler(ev: CustomEvent<CarouselSelect>): void {
|
||||
// The slide may already be selected on load, so don't dispatch a new view
|
||||
// unless necessary.
|
||||
if (ev.detail.index !== this.view?.queryResults?.getSelectedIndex()) {
|
||||
this._setViewSelectedIndex(ev.detail.index);
|
||||
}
|
||||
}
|
||||
|
||||
protected _setViewSelectedIndex(index: number): void {
|
||||
// The slide may already be selected on load, so don't dispatch a new view
|
||||
// unless necessary.
|
||||
const newResults = this.view?.queryResults?.clone().selectResult(index);
|
||||
if (!newResults) {
|
||||
return;
|
||||
}
|
||||
const cameraID = newResults.getSelectedResult()?.getCameraID();
|
||||
|
||||
this.view
|
||||
?.evolve({
|
||||
queryResults: this.view.queryResults?.clone().selectResult(index),
|
||||
queryResults: newResults,
|
||||
|
||||
// Always change the camera to the owner of the selected media.
|
||||
...(cameraID && { camera: cameraID }),
|
||||
})
|
||||
// Ensure the timeline is able to update its position.
|
||||
.mergeInContext({ timeline: { noSetWindow: false } })
|
||||
@@ -626,15 +627,18 @@ export class FrigateCardViewerCarousel extends LitElement {
|
||||
/**
|
||||
* Fire a media show event when a slide is selected.
|
||||
*/
|
||||
protected _seekHandler(): void {
|
||||
const selectedIndex = this.view?.queryResults?.getSelectedIndex() ?? null;
|
||||
const seek =
|
||||
selectedIndex !== null
|
||||
? this.view?.context?.mediaViewer?.seek.get(selectedIndex)
|
||||
: null;
|
||||
protected async _seekHandler(): Promise<void> {
|
||||
const seek = this.view?.context?.mediaViewer?.seek;
|
||||
const media = this.view?.queryResults?.getSelectedResult();
|
||||
if (!this.hass || !media || !seek) {
|
||||
return;
|
||||
}
|
||||
|
||||
const seekTime =
|
||||
(await this.cameraManager?.getMediaSeekTime(this.hass, media, seek)) ?? null;
|
||||
const player = this._getPlayer();
|
||||
if (player && seek) {
|
||||
player.seek(seek.seekSeconds);
|
||||
if (player && seekTime !== null) {
|
||||
player.seek(seekTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user