committed by
dermotduffy
parent
149fce4823
commit
591f466b92
@@ -115,6 +115,7 @@ export interface NativeFrigateEventQuery {
|
|||||||
instance_id?: string;
|
instance_id?: string;
|
||||||
cameras?: string[];
|
cameras?: string[];
|
||||||
labels?: string[];
|
labels?: string[];
|
||||||
|
sub_labels?: string[];
|
||||||
zones?: string[];
|
zones?: string[];
|
||||||
after?: number;
|
after?: number;
|
||||||
before?: number;
|
before?: number;
|
||||||
|
|||||||
@@ -155,6 +155,11 @@ export type FrigateReviewSeverity =
|
|||||||
// Review data schema (only fields we need for display)
|
// Review data schema (only fields we need for display)
|
||||||
const frigateReviewDataSchema = z.object({
|
const frigateReviewDataSchema = z.object({
|
||||||
objects: z.string().array().optional(),
|
objects: z.string().array().optional(),
|
||||||
|
|
||||||
|
// Frigate adds sub_labels for non-attribute identifications (e.g. Frigate+
|
||||||
|
// species name for a bird). Note: The matching `objects` entry carries a
|
||||||
|
// `-verified` suffix in that case.
|
||||||
|
sub_labels: z.string().array().optional(),
|
||||||
zones: z.string().array().optional(),
|
zones: z.string().array().optional(),
|
||||||
metadata: z
|
metadata: z
|
||||||
.object({
|
.object({
|
||||||
|
|||||||
@@ -105,12 +105,24 @@ export const getRecordingID = (
|
|||||||
* @param review The Frigate review item.
|
* @param review The Frigate review item.
|
||||||
*/
|
*/
|
||||||
export const getReviewTitle = (review: FrigateReview): string => {
|
export const getReviewTitle = (review: FrigateReview): string => {
|
||||||
|
// Frigate flags Frigate+ verified detections by suffixing the object label
|
||||||
|
// with `-verified`. The user-meaningful identification (e.g. species) lives
|
||||||
|
// in `sub_labels` instead, so strip the suffix and surface sub_labels as a
|
||||||
|
// tag separated by ': ' (matching event title formatting).
|
||||||
const objects = review.data.objects?.length
|
const objects = review.data.objects?.length
|
||||||
? review.data.objects.map((o) => prettifyTitle(o)).join(', ')
|
? review.data.objects
|
||||||
|
.map((o) => prettifyTitle(o.replace(/-verified$/, '')))
|
||||||
|
.join(', ')
|
||||||
|
: '';
|
||||||
|
const subLabels = review.data.sub_labels?.length
|
||||||
|
? review.data.sub_labels.map((s) => prettifyTitle(s)).join(', ')
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
if (objects) {
|
if (objects && subLabels) {
|
||||||
return objects;
|
return `${objects}: ${subLabels}`;
|
||||||
|
}
|
||||||
|
if (objects || subLabels) {
|
||||||
|
return objects || subLabels;
|
||||||
}
|
}
|
||||||
|
|
||||||
const durationSeconds = Math.round(
|
const durationSeconds = Math.round(
|
||||||
@@ -121,7 +133,7 @@ export const getReviewTitle = (review: FrigateReview): string => {
|
|||||||
|
|
||||||
return `${formatDateAndTime(
|
return `${formatDateAndTime(
|
||||||
new Date(review.start_time * 1000),
|
new Date(review.start_time * 1000),
|
||||||
)} [${durationSeconds}s${objects}]`;
|
)} [${durationSeconds}s]`;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ describe('frigate requests', () => {
|
|||||||
instance_id: 'clientID',
|
instance_id: 'clientID',
|
||||||
cameras: ['camera.office'],
|
cameras: ['camera.office'],
|
||||||
labels: ['person'],
|
labels: ['person'],
|
||||||
|
sub_labels: ['John'],
|
||||||
zones: ['zone'],
|
zones: ['zone'],
|
||||||
after: 0,
|
after: 0,
|
||||||
before: 1,
|
before: 1,
|
||||||
@@ -163,6 +164,7 @@ describe('frigate requests', () => {
|
|||||||
instance_id: 'clientID',
|
instance_id: 'clientID',
|
||||||
cameras: ['camera.office'],
|
cameras: ['camera.office'],
|
||||||
labels: ['person'],
|
labels: ['person'],
|
||||||
|
sub_labels: ['John'],
|
||||||
zones: ['zone'],
|
zones: ['zone'],
|
||||||
after: 0,
|
after: 0,
|
||||||
before: 1,
|
before: 1,
|
||||||
|
|||||||
@@ -195,6 +195,43 @@ describe('getReviewTitle', () => {
|
|||||||
).toBe('Person, Dog');
|
).toBe('Person, Dog');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should append sub_labels as tags after objects', () => {
|
||||||
|
expect(
|
||||||
|
getReviewTitle(
|
||||||
|
createFrigateReview({
|
||||||
|
data: {
|
||||||
|
objects: ['bird-verified'],
|
||||||
|
sub_labels: ['Mourning Dove'],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
).toBe('Bird: Mourning Dove');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should strip -verified suffix from objects', () => {
|
||||||
|
expect(
|
||||||
|
getReviewTitle(
|
||||||
|
createFrigateReview({
|
||||||
|
data: {
|
||||||
|
objects: ['bird-verified', 'person'],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
).toBe('Bird, Person');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fall back to sub_labels when objects are absent', () => {
|
||||||
|
expect(
|
||||||
|
getReviewTitle(
|
||||||
|
createFrigateReview({
|
||||||
|
data: {
|
||||||
|
sub_labels: ['John'],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
).toBe('John');
|
||||||
|
});
|
||||||
|
|
||||||
it('should get in-progress review title', () => {
|
it('should get in-progress review title', () => {
|
||||||
vi.useFakeTimers();
|
vi.useFakeTimers();
|
||||||
vi.setSystemTime(add(start, { seconds: 60 }));
|
vi.setSystemTime(add(start, { seconds: 60 }));
|
||||||
|
|||||||
Reference in New Issue
Block a user