- {formatUnixTimestampToDateTime(event.start_time, locale, timezone)}
+ {formatUnixTimestampToDateTime(event.start_time, { ...config.ui })}
-
diff --git a/web/src/utils/dateUtil.ts b/web/src/utils/dateUtil.ts
index 39cc993b4..2891a690a 100644
--- a/web/src/utils/dateUtil.ts
+++ b/web/src/utils/dateUtil.ts
@@ -1,7 +1,8 @@
+import strftime from 'strftime';
+import { fromUnixTime, intervalToDuration, formatDuration } from 'date-fns';
export const longToDate = (long: number): Date => new Date(long * 1000);
export const epochToLong = (date: number): number => date / 1000;
export const dateToLong = (date: Date): number => epochToLong(date.getTime());
-import { fromUnixTime, intervalToDuration, formatDuration } from 'date-fns';
const getDateTimeYesterday = (dateTime: Date): Date => {
const twentyFourHoursInMilliseconds = 24 * 60 * 60 * 1000;
@@ -17,28 +18,53 @@ export const getNowYesterdayInLong = (): number => {
};
/**
- * This function takes in a unix timestamp, locale, timezone,
- * and returns a dateTime string.
- * If unixTimestamp is not provided, it returns 'Invalid time'
- * @param unixTimestamp: number
- * @param locale: string
- * @param timezone: string
- * @returns string - dateTime or 'Invalid time' if unixTimestamp is not provided
+ * This function takes in a Unix timestamp, configuration options for date/time display, and an optional strftime format string,
+ * and returns a formatted date/time string.
+ *
+ * If the Unix timestamp is not provided, it returns "Invalid time".
+ *
+ * The configuration options determine how the date and time are formatted.
+ * The `timezone` option allows you to specify a specific timezone for the output, otherwise the user's browser timezone will be used.
+ * The `use12hour` option allows you to display time in a 12-hour format if true, and 24-hour format if false.
+ * The `dateStyle` and `timeStyle` options allow you to specify pre-defined formats for displaying the date and time.
+ * The `strftime_fmt` option allows you to specify a custom format using the strftime syntax.
+ *
+ * If both `strftime_fmt` and `dateStyle`/`timeStyle` are provided, `strftime_fmt` takes precedence.
+ *
+ * @param unixTimestamp The Unix timestamp to format
+ * @param config An object containing the configuration options for date/time display
+ * @returns The formatted date/time string, or "Invalid time" if the Unix timestamp is not provided or invalid.
*/
-export const formatUnixTimestampToDateTime = (unixTimestamp: number, locale: string, timezone: string): string => {
+interface DateTimeStyle {
+ timezone: string;
+ use12hour: boolean | undefined;
+ date_style: 'full' | 'long' | 'medium' | 'short';
+ time_style: 'full' | 'long' | 'medium' | 'short';
+ strftime_fmt: string;
+}
+
+export const formatUnixTimestampToDateTime = (unixTimestamp: number, config: DateTimeStyle): string => {
+ const { timezone, use12hour, date_style, time_style, strftime_fmt } = config;
+ const locale = window.navigator?.language || 'en-US';
+
if (isNaN(unixTimestamp)) {
return 'Invalid time';
}
try {
const date = new Date(unixTimestamp * 1000);
+
+ // use strftime_fmt if defined in config file
+ if (strftime_fmt) {
+ const strftime_locale = strftime.localizeByIdentifier(locale);
+ return strftime_locale(strftime_fmt, date);
+ }
+
+ // else use Intl.DateTimeFormat
const formatter = new Intl.DateTimeFormat(locale, {
- day: '2-digit',
- month: '2-digit',
- year: 'numeric',
- hour: '2-digit',
- minute: '2-digit',
- second: '2-digit',
- timeZone: timezone,
+ dateStyle: date_style,
+ timeStyle: time_style,
+ timeZone: timezone || Intl.DateTimeFormat().resolvedOptions().timeZone,
+ hour12: use12hour !== null ? use12hour : undefined,
});
return formatter.format(date);
} catch (error) {
From 9621b4b9a1f9377079f334851f2de57942daf0df Mon Sep 17 00:00:00 2001
From: Nicolas Mowen
Date: Thu, 23 Feb 2023 20:23:14 -0700
Subject: [PATCH 092/212] Add tips for debugging playback issues (#5574)
---
docs/docs/troubleshooting/faqs.md | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/docs/docs/troubleshooting/faqs.md b/docs/docs/troubleshooting/faqs.md
index 15ce9bc12..4b166b037 100644
--- a/docs/docs/troubleshooting/faqs.md
+++ b/docs/docs/troubleshooting/faqs.md
@@ -17,16 +17,18 @@ ffmpeg:
record: preset-record-generic-audio-aac
```
+### I can't view events or recordings in the Web UI.
+
+Ensure your cameras send h264 encoded video, or [transcode them](/configuration/restream.md).
+
+You can open `chrome://media-internals/` in another tab and then try to playback, the media internals page will give information about why playback is failing.
+
### My mjpeg stream or snapshots look green and crazy
This almost always means that the width/height defined for your camera are not correct. Double check the resolution with VLC or another player. Also make sure you don't have the width and height values backwards.

-### I can't view events or recordings in the Web UI.
-
-Ensure your cameras send h264 encoded video, or [transcode them](/configuration/restream.md).
-
### "[mov,mp4,m4a,3gp,3g2,mj2 @ 0x5639eeb6e140] moov atom not found"
These messages in the logs are expected in certain situations. Frigate checks the integrity of the recordings before storing. Occasionally these cached files will be invalid and cleaned up automatically.
From a554b22968f524cd883fdec7ae6dbde4b5a2f2c7 Mon Sep 17 00:00:00 2001
From: herostrat
Date: Fri, 24 Feb 2023 04:29:14 +0100
Subject: [PATCH 093/212] Add docs about additions (#4504)
* Add docs about additions
* Fix review findings: Spelling
* Add suggestions from PR
---
.../integrations/third_party_extensions.md | 19 +++++++++++++++++++
docs/sidebars.js | 1 +
2 files changed, 20 insertions(+)
create mode 100644 docs/docs/integrations/third_party_extensions.md
diff --git a/docs/docs/integrations/third_party_extensions.md b/docs/docs/integrations/third_party_extensions.md
new file mode 100644
index 000000000..6c708d27d
--- /dev/null
+++ b/docs/docs/integrations/third_party_extensions.md
@@ -0,0 +1,19 @@
+---
+id: third_party_extensions
+title: Third Party Extensions
+---
+
+Being open source, others have the possibility to modify and extend the rich functionality Frigate already offers.
+This page is meant to be an overview over additions one can make to the home NVR setup. The list is not exhaustive and can be extended via PR to the Frigate docs.
+
+:::caution
+
+This page does not recommend or rate the presented projects.
+Please use your own knowledge to assess and vet them before you install anything on your system.
+
+:::
+
+## [Double Take](https://github.com/jakowenko/double-take)
+
+[Double Take](https://github.com/jakowenko/double-take) provides an unified UI and API for processing and training images for facial recognition.
+It supports automatically setting the sub labels in Frigate for person objects that are detected and recognized.
diff --git a/docs/sidebars.js b/docs/sidebars.js
index 14794c910..adc3688ee 100644
--- a/docs/sidebars.js
+++ b/docs/sidebars.js
@@ -34,6 +34,7 @@ module.exports = {
"integrations/home-assistant",
"integrations/api",
"integrations/mqtt",
+ "integrations/third_party_extensions",
],
Troubleshooting: [
"troubleshooting/faqs",
From d97fa99ec5e0a159df2b1d5795c09672d4096ed4 Mon Sep 17 00:00:00 2001
From: Nicolas Mowen
Date: Fri, 24 Feb 2023 18:13:33 -0700
Subject: [PATCH 094/212] Fix logging for corrupt segments (#5582)
* Fix logging for corrupt segments
* Formatting
---
frigate/record.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/frigate/record.py b/frigate/record.py
index 4b7a25a7c..7693db914 100644
--- a/frigate/record.py
+++ b/frigate/record.py
@@ -171,10 +171,12 @@ class RecordingMaintainer(threading.Thread):
else:
if duration == -1:
logger.warning(
- f"Failed to probe corrupt segment {f}: {p.returncode} - {p.stderr}"
+ f"Failed to probe corrupt segment {cache_path}: {p.returncode} - {p.stderr}"
)
- logger.warning(f"Discarding a corrupt recording segment: {f}")
+ logger.warning(
+ f"Discarding a corrupt recording segment: {cache_path}"
+ )
Path(cache_path).unlink(missing_ok=True)
continue
From 34bdf2fc105bd0a249bc9f1dfa7e0ffe1be4abaf Mon Sep 17 00:00:00 2001
From: Nicolas Mowen
Date: Sun, 26 Feb 2023 06:05:27 -0700
Subject: [PATCH 095/212] Clean up output for vainfo and ffprobe (#5586)
* Clean up output for vainfo and ffprobe
* Fix cleanup
* Format vainfo display
* Fix ffprobe output
* Fix stringification
* remove unused
---
frigate/http.py | 10 ++---
web/src/components/Dialog.jsx | 2 +-
web/src/routes/System.jsx | 69 +++++++++++++++++++++++++++++++----
3 files changed, 67 insertions(+), 14 deletions(-)
diff --git a/frigate/http.py b/frigate/http.py
index a5f9834eb..1649ab55e 100644
--- a/frigate/http.py
+++ b/frigate/http.py
@@ -1296,12 +1296,12 @@ def ffprobe():
output.append(
{
"return_code": ffprobe.returncode,
- "stderr": json.loads(ffprobe.stderr.decode("unicode_escape").strip())
- if ffprobe.stderr.decode()
- else {},
+ "stderr": ffprobe.stderr.decode("unicode_escape").strip()
+ if ffprobe.returncode != 0
+ else "",
"stdout": json.loads(ffprobe.stdout.decode("unicode_escape").strip())
- if ffprobe.stdout.decode()
- else {},
+ if ffprobe.returncode == 0
+ else "",
}
)
diff --git a/web/src/components/Dialog.jsx b/web/src/components/Dialog.jsx
index ad4f57d72..6bf9e3105 100644
--- a/web/src/components/Dialog.jsx
+++ b/web/src/components/Dialog.jsx
@@ -21,7 +21,7 @@ export default function Dialog({ children, portalRootID = 'dialogs' }) {
>
diff --git a/web/src/routes/System.jsx b/web/src/routes/System.jsx
index e6b383114..8a5f2c28f 100644
--- a/web/src/routes/System.jsx
+++ b/web/src/routes/System.jsx
@@ -49,14 +49,14 @@ export default function System() {
});
if (response.status === 200) {
- setState({ ...state, showFfprobe: true, ffprobe: JSON.stringify(response.data, null, 2) });
+ setState({ ...state, showFfprobe: true, ffprobe: response.data });
} else {
setState({ ...state, showFfprobe: true, ffprobe: 'There was an error getting the ffprobe output.' });
}
};
const onCopyFfprobe = async () => {
- copy(JSON.stringify(state.ffprobe, null, 2));
+ copy(JSON.stringify(state.ffprobe).replace(/[\\\s]+/gi, ''));
setState({ ...state, ffprobe: '', showFfprobe: false });
};
@@ -68,14 +68,18 @@ export default function System() {
const response = await axios.get('vainfo');
if (response.status === 200) {
- setState({ ...state, showVainfo: true, vainfo: JSON.stringify(response.data, null, 2) });
+ setState({
+ ...state,
+ showVainfo: true,
+ vainfo: response.data,
+ });
} else {
setState({ ...state, showVainfo: true, vainfo: 'There was an error getting the vainfo output.' });
}
};
const onCopyVainfo = async () => {
- copy(JSON.stringify(state.vainfo, null, 2));
+ copy(JSON.stringify(state.vainfo).replace(/[\\\s]+/gi, ''));
setState({ ...state, vainfo: '', showVainfo: false });
};
@@ -107,9 +111,52 @@ export default function System() {
{state.showFfprobe && (