Merge pull request #32 from dermotduffy/browse-folders
Add browsing of folders in media mini-gallery
This commit is contained in:
@@ -44,6 +44,20 @@
|
|||||||
@include image-list.shape-radius(5px);
|
@include image-list.shape-radius(5px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ha-card .frigate-card-image-list-folder {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
text-align: center;
|
||||||
|
border-style: 2px solid;
|
||||||
|
color: var(--secondary-text-color, white);
|
||||||
|
border-color: var(--secondary-text-color, black);
|
||||||
|
background-color: var(--primary-background-color, black);
|
||||||
|
--mdc-icon-size: 40px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
video, img {
|
video, img {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
@@ -61,8 +75,8 @@ ha-card {
|
|||||||
transform-style: preserve-3d; /* Safari brings video elements forward without this */
|
transform-style: preserve-3d; /* Safari brings video elements forward without this */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Don't drop shadow or have radius for nested cards: webrtc */
|
/* Don't drop shadow or have radius for nested webrtc card */
|
||||||
ha-card ha-card {
|
webrtc-camera ha-card {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
background-color: var(--secondary-background-color, black);
|
background-color: var(--secondary-background-color, black);
|
||||||
|
|||||||
+54
-30
@@ -120,10 +120,14 @@ export class FrigateCardMenu extends LitElement {
|
|||||||
|
|
||||||
// Call the callback.
|
// Call the callback.
|
||||||
protected _callAction(name: string): void {
|
protected _callAction(name: string): void {
|
||||||
if (name == 'frigate' && this.menuMode == 'hidden') {
|
if (this.menuMode == 'hidden') {
|
||||||
|
if (name == 'frigate') {
|
||||||
this.expand = !this.expand;
|
this.expand = !this.expand;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Collapse menu after the user clicks on something.
|
||||||
|
this.expand = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.actionCallback) {
|
if (this.actionCallback) {
|
||||||
this.actionCallback(name);
|
this.actionCallback(name);
|
||||||
@@ -133,7 +137,7 @@ export class FrigateCardMenu extends LitElement {
|
|||||||
// Render a menu button.
|
// Render a menu button.
|
||||||
protected _renderButton(name: string, button: MenuButton): TemplateResult {
|
protected _renderButton(name: string, button: MenuButton): TemplateResult {
|
||||||
return html` <ha-icon-button
|
return html` <ha-icon-button
|
||||||
class=${button.emphasize ? "emphasized-button" : "button"}
|
class=${button.emphasize ? 'emphasized-button' : 'button'}
|
||||||
icon=${button.icon || 'mdi:gesture-tap-button'}
|
icon=${button.icon || 'mdi:gesture-tap-button'}
|
||||||
data-toggle="tooltip"
|
data-toggle="tooltip"
|
||||||
title=${button.description}
|
title=${button.description}
|
||||||
@@ -215,7 +219,7 @@ export class FrigateCard extends LitElement {
|
|||||||
protected _interactionTimerID: number | null = null;
|
protected _interactionTimerID: number | null = null;
|
||||||
protected _webrtcElement: any | null = null;
|
protected _webrtcElement: any | null = null;
|
||||||
|
|
||||||
// Event specifically requested to be shown by the user.
|
// A specific media source requested by the user.
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected _requestedMediaSource: BrowseMediaSource | null = null;
|
protected _requestedMediaSource: BrowseMediaSource | null = null;
|
||||||
|
|
||||||
@@ -315,22 +319,14 @@ export class FrigateCard extends LitElement {
|
|||||||
this._changeView();
|
this._changeView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the card view.
|
||||||
protected _changeView(
|
protected _changeView(
|
||||||
view?: FrigateCardView | undefined,
|
view?: FrigateCardView | undefined,
|
||||||
mediaSource?: BrowseMediaSource | undefined,
|
mediaSource?: BrowseMediaSource | undefined,
|
||||||
): void {
|
): void {
|
||||||
if (view !== undefined) {
|
this._viewMode = view || this.config.view_default;
|
||||||
this._viewMode = view;
|
this._requestedMediaSource = mediaSource || null;
|
||||||
} else {
|
|
||||||
this._viewMode = this.config.view_default;
|
|
||||||
if (['clip', 'snapshot'].includes(this.config.view_default)) {
|
|
||||||
this._requestedMediaSource = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._mediaBeingShown = null;
|
this._mediaBeingShown = null;
|
||||||
if (mediaSource !== undefined) {
|
|
||||||
this._requestedMediaSource = mediaSource;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine whether the card should be updated.
|
// Determine whether the card should be updated.
|
||||||
@@ -388,17 +384,27 @@ export class FrigateCard extends LitElement {
|
|||||||
return parseResult.data;
|
return parseResult.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Browse Frigate media.
|
// Browse Frigate media with a media content id.
|
||||||
protected async _browseMedia(
|
protected async _browseMedia(
|
||||||
|
media_content_id: string,
|
||||||
|
): Promise<BrowseMediaSource | null> {
|
||||||
|
const request = {
|
||||||
|
type: 'media_source/browse_media',
|
||||||
|
media_content_id: media_content_id,
|
||||||
|
};
|
||||||
|
return this._makeWSRequest(browseMediaSourceSchema, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Browse Frigate media with query parameters.
|
||||||
|
protected async _browseMediaQuery(
|
||||||
want_clips?: boolean,
|
want_clips?: boolean,
|
||||||
before?: number,
|
before?: number,
|
||||||
after?: number,
|
after?: number,
|
||||||
): Promise<BrowseMediaSource | null> {
|
): Promise<BrowseMediaSource | null> {
|
||||||
|
return this._browseMedia(
|
||||||
// Defined in:
|
// Defined in:
|
||||||
// https://github.com/blakeblackshear/frigate-hass-integration/blob/master/custom_components/frigate/media_source.py
|
// https://github.com/blakeblackshear/frigate-hass-integration/blob/master/custom_components/frigate/media_source.py
|
||||||
const request = {
|
[
|
||||||
type: 'media_source/browse_media',
|
|
||||||
media_content_id: [
|
|
||||||
'media-source://frigate',
|
'media-source://frigate',
|
||||||
this.config.frigate_client_id,
|
this.config.frigate_client_id,
|
||||||
'event-search',
|
'event-search',
|
||||||
@@ -410,8 +416,7 @@ export class FrigateCard extends LitElement {
|
|||||||
this.config.label,
|
this.config.label,
|
||||||
this.config.zone,
|
this.config.zone,
|
||||||
].join('/'),
|
].join('/'),
|
||||||
};
|
);
|
||||||
return this._makeWSRequest(browseMediaSourceSchema, request);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve Frigate media identifier to a real URL.
|
// Resolve Frigate media identifier to a real URL.
|
||||||
@@ -453,7 +458,11 @@ export class FrigateCard extends LitElement {
|
|||||||
const want_clips = this._viewMode == 'clips';
|
const want_clips = this._viewMode == 'clips';
|
||||||
let media;
|
let media;
|
||||||
try {
|
try {
|
||||||
media = await this._browseMedia(want_clips);
|
if (this._requestedMediaSource) {
|
||||||
|
media = await this._browseMedia(this._requestedMediaSource.media_content_id);
|
||||||
|
} else {
|
||||||
|
media = await this._browseMediaQuery(want_clips);
|
||||||
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
return this._renderError(e.message);
|
return this._renderError(e.message);
|
||||||
}
|
}
|
||||||
@@ -466,12 +475,23 @@ export class FrigateCard extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html` <ul class="mdc-image-list frigate-card-image-list">
|
return html` <ul class="mdc-image-list frigate-card-image-list">
|
||||||
${media.children.map((mediaSource) =>
|
${media.children.map(
|
||||||
mediaSource.can_expand
|
(mediaSource) =>
|
||||||
? ''
|
html` <li class="mdc-image-list__item">
|
||||||
: html` <li class="mdc-image-list__item">
|
|
||||||
<div class="mdc-image-list__image-aspect-container">
|
<div class="mdc-image-list__image-aspect-container">
|
||||||
<img
|
${mediaSource.can_expand
|
||||||
|
? html`<div class="mdc-image-list__image">
|
||||||
|
<ha-card
|
||||||
|
@click=${() => {
|
||||||
|
this._changeView(this._viewMode, mediaSource);
|
||||||
|
}}
|
||||||
|
outlined=""
|
||||||
|
class="frigate-card-image-list-folder"
|
||||||
|
>
|
||||||
|
<div>${mediaSource.title}</div>
|
||||||
|
</ha-card>
|
||||||
|
</div>`
|
||||||
|
: html`<img
|
||||||
data-toggle="tooltip"
|
data-toggle="tooltip"
|
||||||
title="${mediaSource.title}"
|
title="${mediaSource.title}"
|
||||||
class="mdc-image-list__image"
|
class="mdc-image-list__image"
|
||||||
@@ -479,7 +499,7 @@ export class FrigateCard extends LitElement {
|
|||||||
@click=${() => {
|
@click=${() => {
|
||||||
this._changeView(want_clips ? 'clip' : 'snapshot', mediaSource);
|
this._changeView(want_clips ? 'clip' : 'snapshot', mediaSource);
|
||||||
}}
|
}}
|
||||||
/>
|
/>`}
|
||||||
</div>
|
</div>
|
||||||
</li>`,
|
</li>`,
|
||||||
)}
|
)}
|
||||||
@@ -645,7 +665,7 @@ export class FrigateCard extends LitElement {
|
|||||||
} else {
|
} else {
|
||||||
let media;
|
let media;
|
||||||
try {
|
try {
|
||||||
media = await this._browseMedia(true);
|
media = await this._browseMediaQuery(true);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
return this._renderError(e.message);
|
return this._renderError(e.message);
|
||||||
}
|
}
|
||||||
@@ -726,7 +746,11 @@ export class FrigateCard extends LitElement {
|
|||||||
if (startTime) {
|
if (startTime) {
|
||||||
try {
|
try {
|
||||||
// Fetch clips within the same second (same camera/zone/label, etc).
|
// Fetch clips within the same second (same camera/zone/label, etc).
|
||||||
const clipsAtSameTime = await this._browseMedia(true, startTime + 1, startTime);
|
const clipsAtSameTime = await this._browseMediaQuery(
|
||||||
|
true,
|
||||||
|
startTime + 1,
|
||||||
|
startTime,
|
||||||
|
);
|
||||||
if (clipsAtSameTime) {
|
if (clipsAtSameTime) {
|
||||||
return this._getFirstTrueMediaItem(clipsAtSameTime);
|
return this._getFirstTrueMediaItem(clipsAtSameTime);
|
||||||
}
|
}
|
||||||
@@ -745,7 +769,7 @@ export class FrigateCard extends LitElement {
|
|||||||
} else {
|
} else {
|
||||||
let media;
|
let media;
|
||||||
try {
|
try {
|
||||||
media = await this._browseMedia(false);
|
media = await this._browseMediaQuery(false);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
return this._renderError(e.message);
|
return this._renderError(e.message);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user