Compare commits

..
41 Commits
Author SHA1 Message Date
Josh HawkinsandGitHub 430cebecda Fix trigger sync (#21264)
CI / AMD64 Build (push) Has been cancelled
CI / ARM Build (push) Has been cancelled
CI / Jetson Jetpack 6 (push) Has been cancelled
CI / AMD64 Extra Build (push) Has been cancelled
CI / ARM Extra Build (push) Has been cancelled
CI / Synaptics Build (push) Has been cancelled
CI / Assemble and push default build (push) Has been cancelled
- don't look for event ids when trigger type is description
- don't try to delete thumbnail whe trigger type is description
- pass correct event ID into thumbnail deletion
2025-12-13 12:15:25 -06:00
af7af33645 Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (120 of 120 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (25 of 25 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (72 of 72 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (654 of 654 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (92 of 92 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (41 of 41 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (46 of 46 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (41 of 41 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (48 of 48 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (72 of 72 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (92 of 92 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (53 of 53 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (652 of 652 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (120 of 120 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 99.1% (119 of 120 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (119 of 119 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (40 of 40 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (72 of 72 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (639 of 639 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (52 of 52 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (116 of 116 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (55 of 55 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (48 of 48 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: OverTheHillsAndFarAway <prosjektx@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-filter/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-search/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/nb_NO/
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/components-filter
Translation: Frigate NVR/components-player
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-search
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2025-12-13 08:02:05 -07:00
9822716df5 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (41 of 41 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (53 of 53 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (654 of 654 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (120 of 120 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (652 of 652 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (639 of 639 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (120 of 120 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (119 of 119 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (40 of 40 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (92 of 92 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (46 of 46 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (639 of 639 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (118 of 118 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (72 of 72 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (501 of 501 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (55 of 55 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (40 of 40 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (92 of 92 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (119 of 119 strings)

Co-authored-by: GuoQing Liu <842607283@qq.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-filter/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/objects/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/zh_Hans/
Translation: Frigate NVR/audio
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/components-filter
Translation: Frigate NVR/objects
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2025-12-13 08:02:05 -07:00
66bc4e6f96 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 92.3% (85 of 92 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 44.5% (53 of 119 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 44.5% (53 of 119 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (52 of 52 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (10 of 10 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (48 of 48 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (2 of 2 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (40 of 40 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 15.7% (79 of 501 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 11.8% (76 of 639 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 92.1% (118 of 128 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 98.1% (54 of 55 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 85.3% (111 of 130 strings)

Co-authored-by: Ban <3637117+Ban921@users.noreply.github.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Yu Chun Huang <yujun@bo2.tw>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-auth/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-input/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-exports/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-search/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/zh_Hant/
Translation: Frigate NVR/audio
Translation: Frigate NVR/common
Translation: Frigate NVR/components-auth
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/components-input
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-exports
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-search
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2025-12-13 08:02:05 -07:00
23dbe098a5 Translated using Weblate (Slovenian)
Currently translated at 98.0% (51 of 52 strings)

Co-authored-by: Emircanos <emircan368@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/sl/
Translation: Frigate NVR/views-facelibrary
2025-12-13 08:02:05 -07:00
c7e6c23f59 Translated using Weblate (Slovak)
Currently translated at 100.0% (40 of 40 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (Slovak)

Currently translated at 99.1% (118 of 119 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (116 of 116 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (55 of 55 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (501 of 501 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (52 of 52 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (639 of 639 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (128 of 128 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jakub K <klacanjakub0@gmail.com>
Co-authored-by: Michal Klacan <mkbebe@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/sk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/sk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/sk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/sk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/sk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/sk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/sk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/sk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/sk/
Translation: Frigate NVR/audio
Translation: Frigate NVR/common
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2025-12-13 08:02:05 -07:00
de968de878 Translated using Weblate (Swedish)
Currently translated at 100.0% (654 of 654 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (120 of 120 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (53 of 53 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (92 of 92 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (41 of 41 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (652 of 652 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (120 of 120 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (652 of 652 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (118 of 118 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (40 of 40 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (Swedish)

Currently translated at 98.3% (117 of 119 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (55 of 55 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Kristian Johansson <knmjohansson@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/objects/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/sv/
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/objects
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-settings
2025-12-13 08:02:05 -07:00
b951cd9aef Translated using Weblate (French)
Currently translated at 100.0% (654 of 654 strings)

Translated using Weblate (French)

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (French)

Currently translated at 100.0% (53 of 53 strings)

Translated using Weblate (French)

Currently translated at 100.0% (41 of 41 strings)

Translated using Weblate (French)

Currently translated at 100.0% (652 of 652 strings)

Translated using Weblate (French)

Currently translated at 100.0% (639 of 639 strings)

Translated using Weblate (French)

Currently translated at 100.0% (120 of 120 strings)

Translated using Weblate (French)

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (French)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (French)

Currently translated at 100.0% (72 of 72 strings)

Translated using Weblate (French)

Currently translated at 100.0% (40 of 40 strings)

Translated using Weblate (French)

Currently translated at 100.0% (55 of 55 strings)

Translated using Weblate (French)

Currently translated at 100.0% (2 of 2 strings)

Translated using Weblate (French)

Currently translated at 100.0% (639 of 639 strings)

Translated using Weblate (French)

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (French)

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (French)

Currently translated at 100.0% (119 of 119 strings)

Translated using Weblate (French)

Currently translated at 100.0% (40 of 40 strings)

Translated using Weblate (French)

Currently translated at 100.0% (55 of 55 strings)

Translated using Weblate (French)

Currently translated at 100.0% (128 of 128 strings)

Co-authored-by: Apocoloquintose <bertrand.moreux@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/fr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/fr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-filter/fr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-input/fr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/fr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/fr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/fr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/fr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/fr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/fr/
Translation: Frigate NVR/common
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/components-filter
Translation: Frigate NVR/components-input
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2025-12-13 08:02:05 -07:00
6286010014 Translated using Weblate (Spanish)
Currently translated at 90.2% (83 of 92 strings)

Co-authored-by: Hernán Rossetto <hmronline@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/es/
Translation: Frigate NVR/views-live
2025-12-13 08:02:05 -07:00
1a0111a683 Translated using Weblate (Dutch)
Currently translated at 100.0% (41 of 41 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (53 of 53 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (652 of 652 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (120 of 120 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (639 of 639 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (119 of 119 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (639 of 639 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (119 of 119 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (40 of 40 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (119 of 119 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (55 of 55 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (128 of 128 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Marijn <168113859+Marijn0@users.noreply.github.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/nl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/nl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/nl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/nl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/nl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/nl/
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-settings
2025-12-13 08:02:05 -07:00
5716674703 Translated using Weblate (Italian)
Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (40 of 40 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (119 of 119 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (55 of 55 strings)

Co-authored-by: Gringo <ita.translations@tiscali.it>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/it/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/it/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/it/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/it/
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-explore
2025-12-13 08:02:05 -07:00
dd22c8e2a3 Translated using Weblate (Polish)
Currently translated at 55.8% (67 of 120 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (52 of 52 strings)

Translated using Weblate (Polish)

Currently translated at 84.6% (110 of 130 strings)

Translated using Weblate (Polish)

Currently translated at 95.0% (38 of 40 strings)

Translated using Weblate (Polish)

Currently translated at 83.0% (108 of 130 strings)

Translated using Weblate (Polish)

Currently translated at 38.6% (46 of 119 strings)

Translated using Weblate (Polish)

Currently translated at 98.1% (54 of 55 strings)

Translated using Weblate (Polish)

Currently translated at 89.2% (570 of 639 strings)

Translated using Weblate (Polish)

Currently translated at 92.9% (119 of 128 strings)

Translated using Weblate (Polish)

Currently translated at 98.1% (210 of 214 strings)

Translated using Weblate (Polish)

Currently translated at 85.4% (546 of 639 strings)

Translated using Weblate (Polish)

Currently translated at 95.0% (38 of 40 strings)

Translated using Weblate (Polish)

Currently translated at 83.5% (107 of 128 strings)

Translated using Weblate (Polish)

Currently translated at 98.0% (51 of 52 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (92 of 92 strings)

Translated using Weblate (Polish)

Currently translated at 37.8% (45 of 119 strings)

Co-authored-by: Artur <wy66m6xm@anonaddy.me>
Co-authored-by: Bartlomiej Puls <bartlomiej.puls@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Mateusz Kulis <kulis.matis@gmail.com>
Co-authored-by: Paweł Bauer <pawol87@gmail.com>
Co-authored-by: piesu <dogiiee@proton.me>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/pl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/pl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/pl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/pl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/pl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/pl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/pl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/pl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/pl/
Translation: Frigate NVR/common
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2025-12-13 08:02:05 -07:00
61ca688b14 Translated using Weblate (Vietnamese)
Currently translated at 59.0% (385 of 652 strings)

Translated using Weblate (Vietnamese)

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (Vietnamese)

Currently translated at 100.0% (92 of 92 strings)

Translated using Weblate (Vietnamese)

Currently translated at 100.0% (52 of 52 strings)

Translated using Weblate (Vietnamese)

Currently translated at 32.5% (39 of 120 strings)

Translated using Weblate (Vietnamese)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Vietnamese)

Currently translated at 100.0% (40 of 40 strings)

Translated using Weblate (Vietnamese)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Vietnamese)

Currently translated at 56.4% (368 of 652 strings)

Translated using Weblate (Vietnamese)

Currently translated at 100.0% (55 of 55 strings)

Translated using Weblate (Vietnamese)

Currently translated at 23.3% (28 of 120 strings)

Translated using Weblate (Vietnamese)

Currently translated at 72.3% (94 of 130 strings)

Translated using Weblate (Vietnamese)

Currently translated at 96.1% (50 of 52 strings)

Translated using Weblate (Vietnamese)

Currently translated at 100.0% (128 of 128 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Hào Nguyễn Xuân <it.xuanhao@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/vi/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/vi/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/vi/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/vi/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/vi/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-exports/vi/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/vi/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/vi/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/vi/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/vi/
Translation: Frigate NVR/common
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-exports
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2025-12-13 08:02:05 -07:00
68f5eab839 Translated using Weblate (Czech)
Currently translated at 63.5% (406 of 639 strings)

Translated using Weblate (Czech)

Currently translated at 92.3% (48 of 52 strings)

Translated using Weblate (Czech)

Currently translated at 75.0% (30 of 40 strings)

Translated using Weblate (Czech)

Currently translated at 16.8% (20 of 119 strings)

Translated using Weblate (Czech)

Currently translated at 63.2% (404 of 639 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Libor Vymětalík <vymetalik.libor@gmail.com>
Co-authored-by: Martin Brož <code@martin-broz.cz>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/cs/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/cs/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/cs/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/cs/
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-settings
2025-12-13 08:02:05 -07:00
2e125dcab2 Translated using Weblate (Catalan)
Currently translated at 100.0% (654 of 654 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (41 of 41 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (53 of 53 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (652 of 652 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (120 of 120 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (119 of 119 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (639 of 639 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (119 of 119 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (639 of 639 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (40 of 40 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (119 of 119 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (55 of 55 strings)

Co-authored-by: Eduardo Pastor Fernández <123eduardoneko123@gmail.com>
Co-authored-by: Gerard Ricart Castells <gerard.ricart@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/ca/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/ca/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/ca/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/ca/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/ca/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/ca/
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-settings
2025-12-13 08:02:05 -07:00
83753fb2ce Translated using Weblate (Ukrainian)
Currently translated at 100.0% (654 of 654 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (41 of 41 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (53 of 53 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (652 of 652 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (120 of 120 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (639 of 639 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (639 of 639 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (119 of 119 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (40 of 40 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (119 of 119 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (55 of 55 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (128 of 128 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Максим Горпиніч <gorpinicmaksim0@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/uk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/uk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/uk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/uk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/uk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/uk/
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-settings
2025-12-13 08:02:05 -07:00
95368d39d1 Translated using Weblate (Bulgarian)
Currently translated at 100.0% (2 of 2 strings)

Translated using Weblate (Bulgarian)

Currently translated at 9.0% (5 of 55 strings)

Translated using Weblate (Bulgarian)

Currently translated at 0.7% (5 of 639 strings)

Translated using Weblate (Bulgarian)

Currently translated at 15.3% (2 of 13 strings)

Translated using Weblate (Bulgarian)

Currently translated at 31.5% (29 of 92 strings)

Translated using Weblate (Bulgarian)

Currently translated at 2.3% (3 of 128 strings)

Translated using Weblate (Bulgarian)

Currently translated at 20.0% (2 of 10 strings)

Translated using Weblate (Bulgarian)

Currently translated at 9.6% (5 of 52 strings)

Translated using Weblate (Bulgarian)

Currently translated at 22.5% (9 of 40 strings)

Translated using Weblate (Bulgarian)

Currently translated at 20.0% (2 of 10 strings)

Translated using Weblate (Bulgarian)

Currently translated at 6.2% (3 of 48 strings)

Translated using Weblate (Bulgarian)

Currently translated at 0.8% (1 of 119 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (2 of 2 strings)

Translated using Weblate (Bulgarian)

Currently translated at 2.3% (3 of 128 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Skye Fox <mardymcfly1985@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-auth/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-icons/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-input/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-configeditor/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-exports/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-search/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/bg/
Translation: Frigate NVR/components-auth
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/components-icons
Translation: Frigate NVR/components-input
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-configeditor
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-exports
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-search
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2025-12-13 08:02:05 -07:00
8cb1fafed3 Translated using Weblate (Romanian)
Currently translated at 100.0% (41 of 41 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (53 of 53 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (120 of 120 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (120 of 120 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (652 of 652 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (119 of 119 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (639 of 639 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (40 of 40 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (119 of 119 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (55 of 55 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (128 of 128 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: lukasig <lukasig@hotmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/ro/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/ro/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/ro/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/ro/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/ro/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/ro/
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-settings
2025-12-13 08:02:05 -07:00
b75c85114e Translated using Weblate (Russian)
Currently translated at 92.9% (608 of 654 strings)

Co-authored-by: Artem Vladimirov <artyomka71@mail.ru>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/ru/
Translation: Frigate NVR/views-settings
2025-12-13 08:02:05 -07:00
e1a1baa98d Translated using Weblate (Estonian)
Currently translated at 63.0% (29 of 46 strings)

Translated using Weblate (Estonian)

Currently translated at 41.3% (19 of 46 strings)

Translated using Weblate (Estonian)

Currently translated at 42.3% (50 of 118 strings)

Translated using Weblate (Estonian)

Currently translated at 1.8% (1 of 53 strings)

Translated using Weblate (Estonian)

Currently translated at 5.1% (26 of 501 strings)

Translated using Weblate (Estonian)

Currently translated at 4.1% (2 of 48 strings)

Translated using Weblate (Estonian)

Currently translated at 9.5% (62 of 652 strings)

Translated using Weblate (Estonian)

Currently translated at 39.2% (84 of 214 strings)

Translated using Weblate (Estonian)

Currently translated at 0.7% (1 of 128 strings)

Translated using Weblate (Estonian)

Currently translated at 51.2% (21 of 41 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (10 of 10 strings)

Translated using Weblate (Estonian)

Currently translated at 36.4% (43 of 118 strings)

Translated using Weblate (Estonian)

Currently translated at 2.8% (6 of 214 strings)

Translated using Weblate (Estonian)

Currently translated at 9.0% (59 of 652 strings)

Translated using Weblate (Estonian)

Currently translated at 4.6% (6 of 130 strings)

Translated using Weblate (Estonian)

Currently translated at 7.5% (3 of 40 strings)

Translated using Weblate (Estonian)

Currently translated at 8.6% (8 of 92 strings)

Translated using Weblate (Estonian)

Currently translated at 29.1% (21 of 72 strings)

Translated using Weblate (Estonian)

Currently translated at 8.6% (4 of 46 strings)

Translated using Weblate (Estonian)

Currently translated at 4.9% (25 of 501 strings)

Translated using Weblate (Estonian)

Currently translated at 2.0% (1 of 48 strings)

Translated using Weblate (Estonian)

Currently translated at 35.5% (42 of 118 strings)

Translated using Weblate (Estonian)

Currently translated at 2.3% (12 of 501 strings)

Translated using Weblate (Estonian)

Currently translated at 0.8% (1 of 120 strings)

Translated using Weblate (Estonian)

Currently translated at 29.6% (35 of 118 strings)

Translated using Weblate (Estonian)

Currently translated at 2.1% (11 of 501 strings)

Translated using Weblate (Estonian)

Currently translated at 1.4% (3 of 214 strings)

Translated using Weblate (Estonian)

Currently translated at 4.3% (28 of 639 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (2 of 2 strings)

Translated using Weblate (Estonian)

Currently translated at 7.6% (1 of 13 strings)

Translated using Weblate (Estonian)

Currently translated at 2.1% (1 of 46 strings)

Translated using Weblate (Estonian)

Currently translated at 2.5% (1 of 40 strings)

Translated using Weblate (Estonian)

Currently translated at 1.3% (1 of 72 strings)

Translated using Weblate (Estonian)

Currently translated at 3.6% (2 of 55 strings)

Translated using Weblate (Estonian)

Currently translated at 20.0% (2 of 10 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (6 of 6 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (2 of 2 strings)

Translated using Weblate (Estonian)

Currently translated at 4.0% (1 of 25 strings)

Added translation using Weblate (Estonian)

Added translation using Weblate (Estonian)

Added translation using Weblate (Estonian)

Added translation using Weblate (Estonian)

Added translation using Weblate (Estonian)

Added translation using Weblate (Estonian)

Added translation using Weblate (Estonian)

Added translation using Weblate (Estonian)

Added translation using Weblate (Estonian)

Added translation using Weblate (Estonian)

Added translation using Weblate (Estonian)

Added translation using Weblate (Estonian)

Added translation using Weblate (Estonian)

Added translation using Weblate (Estonian)

Added translation using Weblate (Estonian)

Added translation using Weblate (Estonian)

Added translation using Weblate (Estonian)

Added translation using Weblate (Estonian)

Added translation using Weblate (Estonian)

Added translation using Weblate (Estonian)

Added translation using Weblate (Estonian)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Priit Jõerüüt <jrthwlate@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-auth/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-filter/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-icons/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-input/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/objects/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-exports/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-recording/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-search/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/et/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/et/
Translation: Frigate NVR/audio
Translation: Frigate NVR/common
Translation: Frigate NVR/components-auth
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/components-filter
Translation: Frigate NVR/components-icons
Translation: Frigate NVR/components-input
Translation: Frigate NVR/components-player
Translation: Frigate NVR/objects
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-exports
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-recording
Translation: Frigate NVR/views-search
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2025-12-13 08:02:05 -07:00
4098db2441 Translated using Weblate (Danish)
Currently translated at 85.5% (183 of 214 strings)

Translated using Weblate (Danish)

Currently translated at 36.1% (26 of 72 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: kklar <karred.larsen@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/da/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-filter/da/
Translation: Frigate NVR/common
Translation: Frigate NVR/components-filter
2025-12-13 08:02:05 -07:00
71f4417314 Translated using Weblate (German)
Currently translated at 100.0% (120 of 120 strings)

Translated using Weblate (German)

Currently translated at 100.0% (53 of 53 strings)

Translated using Weblate (German)

Currently translated at 100.0% (41 of 41 strings)

Translated using Weblate (German)

Currently translated at 100.0% (652 of 652 strings)

Translated using Weblate (German)

Currently translated at 100.0% (120 of 120 strings)

Translated using Weblate (German)

Currently translated at 100.0% (639 of 639 strings)

Translated using Weblate (German)

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (German)

Currently translated at 100.0% (52 of 52 strings)

Translated using Weblate (German)

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (German)

Currently translated at 100.0% (119 of 119 strings)

Translated using Weblate (German)

Currently translated at 100.0% (639 of 639 strings)

Translated using Weblate (German)

Currently translated at 100.0% (501 of 501 strings)

Translated using Weblate (German)

Currently translated at 100.0% (52 of 52 strings)

Translated using Weblate (German)

Currently translated at 99.8% (638 of 639 strings)

Translated using Weblate (German)

Currently translated at 99.8% (638 of 639 strings)

Translated using Weblate (German)

Currently translated at 100.0% (119 of 119 strings)

Translated using Weblate (German)

Currently translated at 100.0% (119 of 119 strings)

Translated using Weblate (German)

Currently translated at 100.0% (10 of 10 strings)

Translated using Weblate (German)

Currently translated at 100.0% (119 of 119 strings)

Translated using Weblate (German)

Currently translated at 100.0% (639 of 639 strings)

Translated using Weblate (German)

Currently translated at 100.0% (119 of 119 strings)

Translated using Weblate (German)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (German)

Currently translated at 100.0% (40 of 40 strings)

Translated using Weblate (German)

Currently translated at 100.0% (92 of 92 strings)

Translated using Weblate (German)

Currently translated at 100.0% (501 of 501 strings)

Translated using Weblate (German)

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (German)

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (German)

Currently translated at 99.5% (213 of 214 strings)

Translated using Weblate (German)

Currently translated at 99.5% (213 of 214 strings)

Translated using Weblate (German)

Currently translated at 83.5% (534 of 639 strings)

Translated using Weblate (German)

Currently translated at 93.8% (470 of 501 strings)

Translated using Weblate (German)

Currently translated at 98.9% (91 of 92 strings)

Translated using Weblate (German)

Currently translated at 100.0% (52 of 52 strings)

Translated using Weblate (German)

Currently translated at 100.0% (39 of 39 strings)

Translated using Weblate (German)

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (German)

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (German)

Currently translated at 100.0% (116 of 116 strings)

Translated using Weblate (German)

Currently translated at 100.0% (116 of 116 strings)

Translated using Weblate (German)

Currently translated at 34.4% (40 of 116 strings)

Translated using Weblate (German)

Currently translated at 94.8% (37 of 39 strings)

Translated using Weblate (German)

Currently translated at 100.0% (55 of 55 strings)

Translated using Weblate (German)

Currently translated at 78.0% (499 of 639 strings)

Translated using Weblate (German)

Currently translated at 98.4% (126 of 128 strings)

Translated using Weblate (German)

Currently translated at 29.3% (34 of 116 strings)

Translated using Weblate (German)

Currently translated at 96.0% (123 of 128 strings)

Translated using Weblate (German)

Currently translated at 78.0% (499 of 639 strings)

Co-authored-by: Emircanos <emircan368@gmail.com>
Co-authored-by: Fuxle <moritz.hofmann2005@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Sebastian Sie <sebastian.neuplanitz@googlemail.com>
Co-authored-by: bgriese0 <kontakt@bjoern-griese.de>
Co-authored-by: jmtatsch <julian@tatsch.it>
Co-authored-by: mvdberge <micha.vordemberge@christmann.info>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-auth/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/de/
Translation: Frigate NVR/audio
Translation: Frigate NVR/common
Translation: Frigate NVR/components-auth
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2025-12-13 08:02:05 -07:00
4002feb9f8 Translated using Weblate (Portuguese (Brazil))
Currently translated at 29.3% (34 of 116 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jose Machado <machado.jm4@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/pt_BR/
Translation: Frigate NVR/views-classificationmodel
2025-12-13 08:02:05 -07:00
7a399c842d Translated using Weblate (Turkish)
Currently translated at 100.0% (41 of 41 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (53 of 53 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (652 of 652 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (639 of 639 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (501 of 501 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (120 of 120 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (92 of 92 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (128 of 128 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (52 of 52 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (130 of 130 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (40 of 40 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (119 of 119 strings)

Translated using Weblate (Turkish)

Currently translated at 64.6% (413 of 639 strings)

Translated using Weblate (Turkish)

Currently translated at 98.5% (211 of 214 strings)

Translated using Weblate (Turkish)

Currently translated at 66.3% (77 of 116 strings)

Translated using Weblate (Turkish)

Currently translated at 63.7% (74 of 116 strings)

Translated using Weblate (Turkish)

Currently translated at 97.6% (209 of 214 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (55 of 55 strings)

Translated using Weblate (Turkish)

Currently translated at 94.5% (121 of 128 strings)

Translated using Weblate (Turkish)

Currently translated at 93.7% (120 of 128 strings)

Translated using Weblate (Turkish)

Currently translated at 94.5% (87 of 92 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (39 of 39 strings)

Translated using Weblate (Turkish)

Currently translated at 58.9% (377 of 639 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (52 of 52 strings)

Co-authored-by: Emircanos <emircan368@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/tr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/tr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/tr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/tr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-events/tr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/tr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/tr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/tr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/tr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/tr/
Translation: Frigate NVR/audio
Translation: Frigate NVR/common
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-events
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2025-12-13 08:02:05 -07:00
Josh HawkinsandGitHub bc5d6cf1c2 docs tweaks (#21261) 2025-12-13 07:59:49 -07:00
Martin WeineltandGitHub dde02cadb2 Update peewee-migrate to 0.14.x (#21243)
Replaces two functions calls, that were deprecated and aliases for the
new function name:

- Migrator.python -> Migration.run
- Migrator.change_column -> Migrator.change_field
2025-12-13 07:13:04 -07:00
8ddcbf9a8d Improve handling of backchannel audio in camera wizard (#21250)
* Improve handling of backchannel audio in camera wizard

* Cleanup

* look for backchannel on all registered streams on save

avoids potential issues with a timeout in stream registration

---------

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>
2025-12-13 07:12:37 -07:00
wozzandGitHub 6b9b3778f5 fix: attribute error in embedding maintainer ini… (#21252)
* fix: Resolve deadlock and attribute error in embedding maintainer initialization

Updates the trigger embedding calculation to call embedding methods directly instead of using ZMQ. This prevents a deadlock during initialization where the ZMQ responder is not yet polling for requests.

Also updates sync_triggers to pass the camera name and trigger name to the calculation method, fixing an AttributeError where trigger.name was accessed on a TriggerConfig object.

* mocked repro

* Revert "mocked repro"

This reverts commit dea5b5d4db.

* fix formatting

* Update embeddings.py

new line
2025-12-13 07:12:09 -07:00
308e692732 Miscellaneous Fixes (#21241)
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
* only show jwt secret tip for admin users

* fix preview endpoint 403 for viewer role when "all" param is used

* Update docs dependencies

* add warning if ffmpeg isn't selected for reolink http streams

* Update the motion for motion masks

* Also update objects

* Add docs about backchannel and two way talk takeover

* don't require restart when deleting zone or mask

* Ensure motion is correctly set when adjusting masks

* don't use python style raw prefixes in yaml examples in LPR docs

* wording

---------

Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
2025-12-12 07:45:03 -06:00
Martin WeineltandGitHub 67e18eff94 Replace stringy paths with constants (#21247) 2025-12-12 06:22:09 -07:00
Nicolas MowenandGitHub 649ca49e55 Beta discussion template (#21239)
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
* Add beta support template for discussions

* Add note to bug
2025-12-11 09:37:46 -06:00
fa6dda6735 Miscellaneous Fixes (#21208)
* conditionally display actions for admin role only

* only allow admins to save annotation offset

* Fix classification reset filter

* fix explore context menu from blocking pointer events on the body element after dialog close

applying modal=false to the menu (not to the dialog) to fix this in the same way as elsewhere in the codebase

* add select all link to face library, classification, and explore

* Disable iOS image dragging for classification card

* add proxmox ballooning comment

* lpr docs tweaks

* yaml list

* clarify tls_insecure

* Improve security summary format and usefulness

---------

Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
2025-12-11 07:23:34 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
9cdc10008d Bump actions/checkout from 5 to 6 (#20987)
CI / AMD64 Build (push) Has been cancelled
CI / ARM Build (push) Has been cancelled
CI / Jetson Jetpack 6 (push) Has been cancelled
CI / AMD64 Extra Build (push) Has been cancelled
CI / ARM Extra Build (push) Has been cancelled
CI / Synaptics Build (push) Has been cancelled
CI / Assemble and push default build (push) Has been cancelled
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-09 12:52:14 -07:00
4cf4520ea7 Miscellaneous Fixes (#21193)
* Fix saving zone friendly name when it wasn't set

* Fix UTF-8 handling for Onvif

* Don't remove none directory for classes

* Lookup all event IDs for review item immediately

* Cleanup typing

* Only fetch events when review group is open

* Cleanup

* disable debug paths switch for autotracking cameras

* fix clickable birdseye

---------

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>
2025-12-09 12:08:44 -06:00
Josh HawkinsandGitHub dfd837cfb0 refactor to use react-hook-form and zod (#21195)
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / AMD64 Build (push) Waiting to run
CI / Assemble and push default build (push) Blocked by required conditions
2025-12-08 09:19:34 -07:00
Josh HawkinsandGitHub 152e585206 Authentication improvements (#21194)
* jwt permissions

* add old password to body req

* add model and migration

need to track the datetime that passwords were changed for the jwt

* auth api backend changes

- use os.open to create jwt secret with restrictive permissions (0o600: read/write for owner only)
- add backend validation for password strength
- add iat claim to jwt so the server can determine when a token was issued and reject any jwts issued before a user's password_changed_at timestamp, ensuring old tokens are invalidated after a password change
- set logout route to public to avoid 401 when logging out
- issue new jwt for users who change their own password so they stay logged in

* improve set password dialog

- add field to verify old password
- add password strength requirements

* frontend tweaks for password dialog

* i18n

* use verify endpoint for existing password verification

avoid /login side effects (creating a new session)

* public logout

* only check if password has changed on jwt refresh

* fix tests

Fix migration 030 by using raw sql to select usernames (avoid ORM selecting nonexistent columns)

* add multi device warning to password dialog

* remove password verification endpoint

Just send old_password + new password in one request, let the backend handle verification in a single operation
2025-12-08 09:02:28 -07:00
Josh HawkinsandGitHub 28b0ad782a Fix intermittent hangs in Tracking Details videos (#21185)
CI / AMD64 Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
CI / ARM Build (push) Waiting to run
CI / ARM Extra Build (push) Blocked by required conditions
* remove extra gap controller overrides

* new vod endpoint for clips to set discontinuity

ensure tracking-detail playlists emit #EXT-X-DISCONTINUITY (avoids fMP4 timestamp rewrites and playback stalls) while leaving standard recordings behavior unchanged

* use new endpoint
2025-12-07 12:58:33 -06:00
GuoQing LiuandGitHub 644c7fa6b4 fix: fix classification missing i18n (#21179) 2025-12-07 11:35:48 -07:00
88a8de0b1c Miscellaneous Fixes (#21166)
* Improve model titles

* remove deprecated strftime_fmt

* remove

* remove restart wording

* add copilot instructions

* fix docs

* Move files into try for classification rollover

* Use friendly names for zones in notifications

---------

Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
2025-12-07 07:57:46 -07:00
c136e5e8bd Miscellaneous fixes (#21141)
CI / AMD64 Build (push) Has been cancelled
CI / ARM Build (push) Has been cancelled
CI / Jetson Jetpack 6 (push) Has been cancelled
CI / AMD64 Extra Build (push) Has been cancelled
CI / ARM Extra Build (push) Has been cancelled
CI / Synaptics Build (push) Has been cancelled
CI / Assemble and push default build (push) Has been cancelled
* Remove source_type from API

* Don't require state classification models to select all classes

* Specifically validate provided end_time for manual events

* Remove yolov9 specification for warning

* Remove warning for coral

* clarify zone name tip

* clarify replace rules in lpr docs

* remove periods

* Add explanation for review report

* adjust HLS gap controller params

defaults to false, should help to recover from hangs and stalling in tracking details videos on chrome

* only redirect to login page once on 401

attempt to fix ios pwa safari redirect storm

* Use contextual information from other cameras to inform report summary

* Formatting and prompt improvements for review summary report

* More improvements to prompt

* Remove examples

* Don't show admin action buttons on export card

* fix redirect race condition

Coordinate 401 redirect logic between ApiProvider and ProtectedRoute using a shared flag to prevent multiple simultaneous redirects that caused UI flashing. Ensure both auth error paths check and set the redirect flag before navigating to login, eliminating race conditions where both mechanisms could trigger at once

---------

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>
2025-12-04 12:19:07 -06:00
9ab78f496c Adds support for YOLO v9 models running on Google Coral (#21124)
CI / AMD64 Build (push) Has been cancelled
CI / ARM Build (push) Has been cancelled
CI / Jetson Jetpack 6 (push) Has been cancelled
CI / AMD64 Extra Build (push) Has been cancelled
CI / ARM Extra Build (push) Has been cancelled
CI / Synaptics Build (push) Has been cancelled
CI / Assemble and push default build (push) Has been cancelled
* Adds support for YOLO v9 models running on Google Coral

* fix format by using ruff instead of black

* Remove comment

Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>

* Remove log message

Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>

* revert to hard-coded settings. use ModelTypeEnum directly

* remove log messages. detect invalid output tensor count

* remove 1-tensor processing. add pre_process() function

* check for valid model type

* fix formatting

* remove unused import and variable

* remove tip that indicates other YOLO models may be supported.

---------

Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
2025-12-02 13:26:57 -07:00
296 changed files with 8322 additions and 3298 deletions
+2 -1
View File
@@ -22,6 +22,7 @@ autotrack
autotracked
autotracker
autotracking
backchannel
balena
Beelink
BGRA
@@ -316,4 +317,4 @@ yolo
yolonas
yolox
zeep
zerolatency
zerolatency
@@ -0,0 +1,129 @@
title: "[Beta Support]: "
labels: ["support", "triage", "beta"]
body:
- type: markdown
attributes:
value: |
Thank you for testing Frigate beta versions! Use this form for support with beta releases.
**Note:** Beta versions may have incomplete features, known issues, or unexpected behavior. Please check the [release notes](https://github.com/blakeblackshear/frigate/releases) and [recent discussions][discussions] for known beta issues before submitting.
Before submitting, read the [beta documentation][docs].
[docs]: https://deploy-preview-19787--frigate-docs.netlify.app/
- type: textarea
id: description
attributes:
label: Describe the problem you are having
description: Please be as detailed as possible. Include what you expected to happen vs what actually happened.
validations:
required: true
- type: input
id: version
attributes:
label: Beta Version
description: Visible on the System page in the Web UI. Please include the full version including the build identifier (eg. 0.17.0-beta1)
placeholder: "0.17.0-beta1"
validations:
required: true
- type: dropdown
id: issue-category
attributes:
label: Issue Category
description: What area is your issue related to? This helps us understand the context.
options:
- Object Detection / Detectors
- Hardware Acceleration
- Configuration / Setup
- WebUI / Frontend
- Recordings / Storage
- Notifications / Events
- Integration (Home Assistant, etc)
- Performance / Stability
- Installation / Updates
- Other
validations:
required: true
- type: textarea
id: config
attributes:
label: Frigate config file
description: This will be automatically formatted into code, so no need for backticks. Remove any sensitive information like passwords or URLs.
render: yaml
validations:
required: true
- type: textarea
id: frigatelogs
attributes:
label: Relevant Frigate log output
description: Please copy and paste any relevant Frigate log output. Include logs before and after your exact error when possible. This will be automatically formatted into code, so no need for backticks.
render: shell
validations:
required: true
- type: textarea
id: go2rtclogs
attributes:
label: Relevant go2rtc log output (if applicable)
description: If your issue involves cameras, streams, or playback, please include go2rtc logs. Logs can be viewed via the Frigate UI, Docker, or the go2rtc dashboard. This will be automatically formatted into code, so no need for backticks.
render: shell
- type: dropdown
id: install-method
attributes:
label: Install method
options:
- Home Assistant Add-on
- Docker Compose
- Docker CLI
- Proxmox via Docker
- Proxmox via TTeck Script
- Windows WSL2
validations:
required: true
- type: textarea
id: docker
attributes:
label: docker-compose file or Docker CLI command
description: This will be automatically formatted into code, so no need for backticks. Include relevant environment variables and device mappings.
render: yaml
validations:
required: true
- type: dropdown
id: os
attributes:
label: Operating system
options:
- Home Assistant OS
- Debian
- Ubuntu
- Other Linux
- Proxmox
- UNRAID
- Windows
- Other
validations:
required: true
- type: input
id: hardware
attributes:
label: CPU / GPU / Hardware
description: Provide details about your hardware (e.g., Intel i5-9400, NVIDIA RTX 3060, Raspberry Pi 4, etc)
placeholder: "Intel i7-10700, NVIDIA GTX 1660"
- type: textarea
id: screenshots
attributes:
label: Screenshots
description: Screenshots of the issue, System metrics pages, or any relevant UI. Drag and drop or paste images directly.
- type: textarea
id: steps-to-reproduce
attributes:
label: Steps to reproduce
description: If applicable, provide detailed steps to reproduce the issue
placeholder: |
1. Go to '...'
2. Click on '...'
3. See error
- type: textarea
id: other
attributes:
label: Any other information that may be helpful
description: Additional context, related issues, when the problem started appearing, etc.
@@ -6,6 +6,8 @@ body:
value: |
Use this form to submit a reproducible bug in Frigate or Frigate's UI.
**⚠️ If you are running a beta version (0.17.0-beta or similar), please use the [Beta Support template](https://github.com/blakeblackshear/frigate/discussions/new?category=beta-support) instead.**
Before submitting your bug report, please ask the AI with the "Ask AI" button on the [official documentation site][ai] about your issue, [search the discussions][discussions], look at recent open and closed [pull requests][prs], read the [official Frigate documentation][docs], and read the [Frigate FAQ][faq] pinned at the Discussion page to see if your bug has already been fixed by the developers or reported by the community.
**If you are unsure if your issue is actually a bug or not, please submit a support request first.**
+2
View File
@@ -0,0 +1,2 @@
Never write strings in the frontend directly, always write to and reference the relevant translations file.
Always conform new and refactored code to the existing coding style in the project.
+6 -6
View File
@@ -23,7 +23,7 @@ jobs:
name: AMD64 Build
steps:
- name: Check out code
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up QEMU and Buildx
@@ -47,7 +47,7 @@ jobs:
name: ARM Build
steps:
- name: Check out code
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up QEMU and Buildx
@@ -82,7 +82,7 @@ jobs:
name: Jetson Jetpack 6
steps:
- name: Check out code
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up QEMU and Buildx
@@ -113,7 +113,7 @@ jobs:
- amd64_build
steps:
- name: Check out code
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up QEMU and Buildx
@@ -154,7 +154,7 @@ jobs:
- arm64_build
steps:
- name: Check out code
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up QEMU and Buildx
@@ -179,7 +179,7 @@ jobs:
- arm64_build
steps:
- name: Check out code
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up QEMU and Buildx
+4 -4
View File
@@ -16,7 +16,7 @@ jobs:
name: Web - Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: actions/setup-node@master
@@ -32,7 +32,7 @@ jobs:
name: Web - Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: actions/setup-node@master
@@ -52,7 +52,7 @@ jobs:
name: Python Checks
steps:
- name: Check out the repository
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
@@ -75,7 +75,7 @@ jobs:
name: Python Tests
steps:
- name: Check out code
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- uses: actions/setup-node@master
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- id: lowercaseRepo
+1 -1
View File
@@ -21,7 +21,7 @@ onvif-zeep-async == 4.0.*
paho-mqtt == 2.1.*
pandas == 2.2.*
peewee == 3.17.*
peewee_migrate == 1.13.*
peewee_migrate == 1.14.*
psutil == 7.1.*
pydantic == 2.10.*
git+https://github.com/fbcotter/py3nvml#egg=py3nvml
@@ -111,3 +111,9 @@ review:
## Review Reports
Along with individual review item summaries, Generative AI provides the ability to request a report of a given time period. For example, you can get a daily report while on a vacation of any suspicious activity or other concerns that may require review.
### Requesting Reports Programmatically
Review reports can be requested via the [API](/integrations/api#review-summarization) by sending a POST request to `/api/review/summarize/start/{start_ts}/end/{end_ts}` with Unix timestamps.
For Home Assistant users, there is a built-in service (`frigate.review_summarize`) that makes it easy to request review reports as part of automations or scripts. This allows you to automatically generate daily summaries, vacation reports, or custom time period reports based on your specific needs.
@@ -107,23 +107,23 @@ Fine-tune the LPR feature using these optional parameters at the global level of
### Normalization Rules
- **`replace_rules`**: List of regex replacement rules to normalize detected plates. These rules are applied sequentially. Each rule must have a `pattern` (which can be a string or a regex, prepended by `r`) and `replacement` (a string, which also supports [backrefs](https://docs.python.org/3/library/re.html#re.sub) like `\1`). These rules are useful for dealing with common OCR issues like noise characters, separators, or confusions (e.g., 'O'→'0').
- **`replace_rules`**: List of regex replacement rules to normalize detected plates. These rules are applied sequentially and are applied _before_ the `format` regex, if specified. Each rule must have a `pattern` (which can be a string or a regex) and `replacement` (a string, which also supports [backrefs](https://docs.python.org/3/library/re.html#re.sub) like `\1`). These rules are useful for dealing with common OCR issues like noise characters, separators, or confusions (e.g., 'O'→'0').
These rules must be defined at the global level of your `lpr` config.
```yaml
lpr:
replace_rules:
- pattern: r'[%#*?]' # Remove noise symbols
- pattern: "[%#*?]" # Remove noise symbols
replacement: ""
- pattern: r'[= ]' # Normalize = or space to dash
- pattern: "[= ]" # Normalize = or space to dash
replacement: "-"
- pattern: "O" # Swap 'O' to '0' (common OCR error)
replacement: "0"
- pattern: r'I' # Swap 'I' to '1'
- pattern: "I" # Swap 'I' to '1'
replacement: "1"
- pattern: r'(\w{3})(\w{3})' # Split 6 chars into groups (e.g., ABC123 → ABC-123)
replacement: r'\1-\2'
- pattern: '(\w{3})(\w{3})' # Split 6 chars into groups (e.g., ABC123 → ABC-123) - use single quotes to preserve backslashes
replacement: '\1-\2'
```
- Rules fire in order: In the example above: clean noise first, then separators, then swaps, then splits.
@@ -374,9 +374,19 @@ Use `match_distance` to allow small character mismatches. Alternatively, define
Start with ["Why isn't my license plate being detected and recognized?"](#why-isnt-my-license-plate-being-detected-and-recognized). If you are still having issues, work through these steps.
1. Enable debug logs to see exactly what Frigate is doing.
1. Start with a simplified LPR config.
- Enable debug logs for LPR by adding `frigate.data_processing.common.license_plate: debug` to your `logger` configuration. These logs are _very_ verbose, so only keep this enabled when necessary.
- Remove or comment out everything in your LPR config, including `min_area`, `min_plate_length`, `format`, `known_plates`, or `enhancement` values so that the only values left are `enabled` and `debug_save_plates`. This will run LPR with Frigate's default values.
```yaml
lpr:
enabled: true
debug_save_plates: true
```
2. Enable debug logs to see exactly what Frigate is doing.
- Enable debug logs for LPR by adding `frigate.data_processing.common.license_plate: debug` to your `logger` configuration. These logs are _very_ verbose, so only keep this enabled when necessary. Restart Frigate after this change.
```yaml
logger:
@@ -385,7 +395,7 @@ Start with ["Why isn't my license plate being detected and recognized?"](#why-is
frigate.data_processing.common.license_plate: debug
```
2. Ensure your plates are being _detected_.
3. Ensure your plates are being _detected_.
If you are using a Frigate+ or `license_plate` detecting model:
@@ -398,7 +408,7 @@ Start with ["Why isn't my license plate being detected and recognized?"](#why-is
- Watch the debug logs for messages from the YOLOv9 plate detector.
- You may need to adjust your `detection_threshold` if your plates are not being detected.
3. Ensure the characters on detected plates are being _recognized_.
4. Ensure the characters on detected plates are being _recognized_.
- Enable `debug_save_plates` to save images of detected text on plates to the clips directory (`/media/frigate/clips/lpr`). Ensure these images are readable and the text is clear.
- Watch the debug view to see plates recognized in real-time. For non-dedicated LPR cameras, the `car` or `motorcycle` label will change to the recognized plate when LPR is enabled and working.
+2
View File
@@ -178,6 +178,8 @@ To use the Reolink Doorbell with two way talk, you should use the [recommended R
As a starting point to check compatibility for your camera, view the list of cameras supported for two-way talk on the [go2rtc repository](https://github.com/AlexxIT/go2rtc?tab=readme-ov-file#two-way-audio). For cameras in the category `ONVIF Profile T`, you can use the [ONVIF Conformant Products Database](https://www.onvif.org/conformant-products/)'s FeatureList to check for the presence of `AudioOutput`. A camera that supports `ONVIF Profile T` _usually_ supports this, but due to inconsistent support, a camera that explicitly lists this feature may still not work. If no entry for your camera exists on the database, it is recommended not to buy it or to consult with the manufacturer's support on the feature availability.
To prevent go2rtc from blocking other applications from accessing your camera's two-way audio, you must configure your stream with `#backchannel=0`. See [preventing go2rtc from blocking two-way audio](/configuration/restream#two-way-talk-restream) in the restream documentation.
### Streaming options on camera group dashboards
Frigate provides a dialog in the Camera Group Edit pane with several options for streaming on a camera group's dashboard. These settings are _per device_ and are saved in your device's local storage.
-1
View File
@@ -28,7 +28,6 @@ To create a poly mask:
5. Click the plus icon under the type of mask or zone you would like to create
6. Click on the camera's latest image to create the points for a masked area. Click the first point again to close the polygon.
7. When you've finished creating your mask, press Save.
8. Restart Frigate to apply your changes.
Your config file will be updated with the relative coordinates of the mask/zone:
+42 -6
View File
@@ -13,7 +13,7 @@ Frigate supports multiple different detectors that work on different types of ha
**Most Hardware**
- [Coral EdgeTPU](#edge-tpu-detector): The Google Coral EdgeTPU is available in USB and m.2 format allowing for a wide range of compatibility with devices.
- [Coral EdgeTPU](#edge-tpu-detector): The Google Coral EdgeTPU is available in USB, Mini PCIe, and m.2 formats allowing for a wide range of compatibility with devices.
- [Hailo](#hailo-8): The Hailo8 and Hailo8L AI Acceleration module is available in m.2 format with a HAT for RPi devices, offering a wide range of compatibility with devices.
- <CommunityBadge /> [MemryX](#memryx-mx3): The MX3 Acceleration module is available in m.2 format, offering broad compatibility across various platforms.
- <CommunityBadge /> [DeGirum](#degirum): Service for using hardware devices in the cloud or locally. Hardware and models provided on the cloud on [their website](https://hub.degirum.com).
@@ -69,12 +69,10 @@ Frigate provides the following builtin detector types: `cpu`, `edgetpu`, `hailo8
## Edge TPU Detector
The Edge TPU detector type runs a TensorFlow Lite model utilizing the Google Coral delegate for hardware acceleration. To configure an Edge TPU detector, set the `"type"` attribute to `"edgetpu"`.
The Edge TPU detector type runs TensorFlow Lite models utilizing the Google Coral delegate for hardware acceleration. To configure an Edge TPU detector, set the `"type"` attribute to `"edgetpu"`.
The Edge TPU device can be specified using the `"device"` attribute according to the [Documentation for the TensorFlow Lite Python API](https://coral.ai/docs/edgetpu/multiple-edgetpu/#using-the-tensorflow-lite-python-api). If not set, the delegate will use the first device it finds.
A TensorFlow Lite model is provided in the container at `/edgetpu_model.tflite` and is used by this detector type by default. To provide your own model, bind mount the file into the container and provide the path with `model.path`.
:::tip
See [common Edge TPU troubleshooting steps](/troubleshooting/edgetpu) if the Edge TPU is not detected.
@@ -146,6 +144,44 @@ detectors:
device: pci
```
### EdgeTPU Supported Models
| Model | Notes |
| ------------------------------------- | ------------------------------------------- |
| [MobileNet v2](#ssdlite-mobilenet-v2) | Default model |
| [YOLOv9](#yolo-v9) | More accurate but slower than default model |
#### SSDLite MobileNet v2
A TensorFlow Lite model is provided in the container at `/edgetpu_model.tflite` and is used by this detector type by default. To provide your own model, bind mount the file into the container and provide the path with `model.path`.
#### YOLO v9
[YOLOv9](https://github.com/dbro/frigate-detector-edgetpu-yolo9/releases/download/v1.0/yolov9-s-relu6-best_320_int8_edgetpu.tflite) models that are compiled for Tensorflow Lite and properly quantized are supported, but not included by default. To provide your own model, bind mount the file into the container and provide the path with `model.path`. Note that the model may require a custom label file (eg. [use this 17 label file](https://raw.githubusercontent.com/dbro/frigate-detector-edgetpu-yolo9/refs/heads/main/labels-coco17.txt) for the model linked above.)
<details>
<summary>YOLOv9 Setup & Config</summary>
After placing the downloaded files for the tflite model and labels in your config folder, you can use the following configuration:
```yaml
detectors:
coral:
type: edgetpu
device: usb
model:
model_type: yolo-generic
width: 320 # <--- should match the imgsize of the model, typically 320
height: 320 # <--- should match the imgsize of the model, typically 320
path: /config/model_cache/yolov9-s-relu6-best_320_int8_edgetpu.tflite
labelmap_path: /labelmap/labels-coco-17.txt
```
Note that the labelmap uses a subset of the complete COCO label set that has only 17 objects.
</details>
---
## Hailo-8
@@ -364,7 +400,7 @@ The YOLO detector has been designed to support YOLOv3, YOLOv4, YOLOv7, and YOLOv
:::warning
If you are using a Frigate+ YOLOv9 model, you should not define any of the below `model` parameters in your config except for `path`. See [the Frigate+ model docs](/plus/first_model#step-3-set-your-model-id-in-the-config) for more information on setting up your model.
If you are using a Frigate+ model, you should not define any of the below `model` parameters in your config except for `path`. See [the Frigate+ model docs](/plus/first_model#step-3-set-your-model-id-in-the-config) for more information on setting up your model.
:::
@@ -704,7 +740,7 @@ The YOLO detector has been designed to support YOLOv3, YOLOv4, YOLOv7, and YOLOv
:::warning
If you are using a Frigate+ YOLOv9 model, you should not define any of the below `model` parameters in your config except for `path`. See [the Frigate+ model docs](/plus/first_model#step-3-set-your-model-id-in-the-config) for more information on setting up your model.
If you are using a Frigate+ model, you should not define any of the below `model` parameters in your config except for `path`. See [the Frigate+ model docs](/plus/first_model#step-3-set-your-model-id-in-the-config) for more information on setting up your model.
:::
+2 -6
View File
@@ -123,7 +123,7 @@ auth:
# Optional: Refresh time in seconds (default: shown below)
# When the session is going to expire in less time than this setting,
# it will be refreshed back to the session_length.
refresh_time: 43200 # 12 hours
refresh_time: 1800 # 30 minutes
# Optional: Rate limiting for login failures to help prevent brute force
# login attacks (default: shown below)
# See the docs for more information on valid values
@@ -911,7 +911,7 @@ cameras:
user: admin
# Optional: password for login.
password: admin
# Optional: Skip TLS verification from the ONVIF server (default: shown below)
# Optional: Skip TLS verification and disable digest authentication for the ONVIF server (default: shown below)
tls_insecure: False
# Optional: Ignores time synchronization mismatches between the camera and the server during authentication.
# Using NTP on both ends is recommended and this should only be set to True in a "safe" environment due to the security risk it represents.
@@ -1002,10 +1002,6 @@ ui:
# full: 8:15:22 PM Mountain Standard Time
# (default: shown below).
time_style: medium
# Optional: Ability to manually override the date / time styling to use strftime format
# https://www.gnu.org/software/libc/manual/html_node/Formatting-Calendar-Time.html
# possible values are shown above (default: not set)
strftime_fmt: "%Y/%m/%d %H:%M"
# Optional: Set the unit system to either "imperial" or "metric" (default: metric)
# Used in the UI and in MQTT topics
unit_system: metric
+29 -3
View File
@@ -24,11 +24,12 @@ birdseye:
restream: True
```
:::tip
:::tip
To improve connection speed when using Birdseye via restream you can enable a small idle heartbeat by setting `birdseye.idle_heartbeat_fps` to a low value (e.g. `12`). This makes Frigate periodically push the last frame even when no motion is detected, reducing initial connection latency.
To improve connection speed when using Birdseye via restream you can enable a small idle heartbeat by setting `birdseye.idle_heartbeat_fps` to a low value (e.g. `12`). This makes Frigate periodically push the last frame even when no motion is detected, reducing initial connection latency.
:::
### Securing Restream With Authentication
The go2rtc restream can be secured with RTSP based username / password authentication. Ex:
@@ -159,6 +160,31 @@ go2rtc:
See [this comment](https://github.com/AlexxIT/go2rtc/issues/1217#issuecomment-2242296489) for more information.
## Preventing go2rtc from blocking two-way audio {#two-way-talk-restream}
For cameras that support two-way talk, go2rtc will automatically establish an audio output backchannel when connecting to an RTSP stream. This backchannel blocks access to the camera's audio output for two-way talk functionality, preventing both Frigate and other applications from using it.
To prevent this, you must configure two separate stream instances:
1. One stream instance with `#backchannel=0` for Frigate's viewing, recording, and detection (prevents go2rtc from establishing the blocking backchannel)
2. A second stream instance without `#backchannel=0` for two-way talk functionality (can be used by Frigate's WebRTC viewer or other applications)
Configuration example:
```yaml
go2rtc:
streams:
front_door:
- rtsp://user:password@10.0.10.10:554/cam/realmonitor?channel=1&subtype=2#backchannel=0
front_door_twoway:
- rtsp://user:password@10.0.10.10:554/cam/realmonitor?channel=1&subtype=2
```
In this configuration:
- `front_door` stream is used by Frigate for viewing, recording, and detection. The `#backchannel=0` parameter prevents go2rtc from establishing the audio output backchannel, so it won't block two-way talk access.
- `front_door_twoway` stream is used for two-way talk functionality. This stream can be used by Frigate's WebRTC viewer when two-way talk is enabled, or by other applications (like Home Assistant Advanced Camera Card) that need access to the camera's audio output channel.
## Advanced Restream Configurations
The [exec](https://github.com/AlexxIT/go2rtc/tree/v1.9.10#source-exec) source in go2rtc can be used for custom ffmpeg commands. An example is below:
@@ -169,4 +195,4 @@ NOTE: The output will need to be passed with two curly braces `{{output}}`
go2rtc:
streams:
stream1: exec:ffmpeg -hide_banner -re -stream_loop -1 -i /media/BigBuckBunny.mp4 -c copy -rtsp_transport tcp -f rtsp {{output}}
```
```
+4 -4
View File
@@ -135,6 +135,7 @@ Finally, configure [hardware object detection](/configuration/object_detectors#h
### MemryX MX3
The MemryX MX3 Accelerator is available in the M.2 2280 form factor (like an NVMe SSD), and supports a variety of configurations:
- x86 (Intel/AMD) PCs
- Raspberry Pi 5
- Orange Pi 5 Plus/Max
@@ -142,7 +143,6 @@ The MemryX MX3 Accelerator is available in the M.2 2280 form factor (like an NVM
#### Configuration
#### Installation
To get started with MX3 hardware setup for your system, refer to the [Hardware Setup Guide](https://developer.memryx.com/get_started/hardware_setup.html).
@@ -156,7 +156,7 @@ Then follow these steps for installing the correct driver/runtime configuration:
#### Setup
To set up Frigate, follow the default installation instructions, for example: `ghcr.io/blakeblackshear/frigate:stable`
To set up Frigate, follow the default installation instructions, for example: `ghcr.io/blakeblackshear/frigate:stable`
Next, grant Docker permissions to access your hardware by adding the following lines to your `docker-compose.yml` file:
@@ -173,7 +173,7 @@ In your `docker-compose.yml`, also add:
privileged: true
volumes:
/run/mxa_manager:/run/mxa_manager
- /run/mxa_manager:/run/mxa_manager
```
If you can't use Docker Compose, you can run the container with something similar to this:
@@ -411,7 +411,7 @@ To install make sure you have the [community app plugin here](https://forums.unr
## Proxmox
[According to Proxmox documentation](https://pve.proxmox.com/pve-docs/pve-admin-guide.html#chapter_pct) it is recommended that you run application containers like Frigate inside a Proxmox QEMU VM. This will give you all the advantages of application containerization, while also providing the benefits that VMs offer, such as strong isolation from the host and the ability to live-migrate, which otherwise isnt possible with containers.
[According to Proxmox documentation](https://pve.proxmox.com/pve-docs/pve-admin-guide.html#chapter_pct) it is recommended that you run application containers like Frigate inside a Proxmox QEMU VM. This will give you all the advantages of application containerization, while also providing the benefits that VMs offer, such as strong isolation from the host and the ability to live-migrate, which otherwise isnt possible with containers. Ensure that ballooning is **disabled**, especially if you are passing through a GPU to the VM.
:::warning
+2 -1
View File
@@ -113,7 +113,8 @@ section.
1. If the stream you added to go2rtc is also used by Frigate for the `record` or `detect` role, you can migrate your config to pull from the RTSP restream to reduce the number of connections to your camera as shown [here](/configuration/restream#reduce-connections-to-camera).
2. You can [set up WebRTC](/configuration/live#webrtc-extra-configuration) if your camera supports two-way talk. Note that WebRTC only supports specific audio formats and may require opening ports on your router.
3. If your camera supports two-way talk, you must configure your stream with `#backchannel=0` to prevent go2rtc from blocking other applications from accessing the camera's audio output. See [preventing go2rtc from blocking two-way audio](/configuration/restream#two-way-talk-restream) in the restream documentation.
## Homekit Configuration
To add camera streams to Homekit Frigate must be configured in docker to use `host` networking mode. Once that is done, you can use the go2rtc WebUI (accessed via port 1984, which is disabled by default) to share export a camera to Homekit. Any changes made will automatically be saved to `/config/go2rtc_homekit.yml`.
To add camera streams to Homekit Frigate must be configured in docker to use `host` networking mode. Once that is done, you can use the go2rtc WebUI (accessed via port 1984, which is disabled by default) to share export a camera to Homekit. Any changes made will automatically be saved to `/config/go2rtc_homekit.yml`.
+1790 -1409
View File
File diff suppressed because it is too large Load Diff
+7 -7
View File
@@ -18,14 +18,14 @@
},
"dependencies": {
"@docusaurus/core": "^3.7.0",
"@docusaurus/plugin-content-docs": "^3.6.3",
"@docusaurus/plugin-content-docs": "^3.7.0",
"@docusaurus/preset-classic": "^3.7.0",
"@docusaurus/theme-mermaid": "^3.6.3",
"@docusaurus/theme-mermaid": "^3.7.0",
"@inkeep/docusaurus": "^2.0.16",
"@mdx-js/react": "^3.1.0",
"clsx": "^2.1.1",
"docusaurus-plugin-openapi-docs": "^4.3.1",
"docusaurus-theme-openapi-docs": "^4.3.1",
"docusaurus-plugin-openapi-docs": "^4.5.1",
"docusaurus-theme-openapi-docs": "^4.5.1",
"prism-react-renderer": "^2.4.1",
"raw-loader": "^4.0.2",
"react": "^18.3.1",
@@ -44,9 +44,9 @@
]
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^3.4.0",
"@docusaurus/types": "^3.4.0",
"@types/react": "^18.3.7"
"@docusaurus/module-type-aliases": "^3.7.0",
"@docusaurus/types": "^3.7.0",
"@types/react": "^18.3.27"
},
"engines": {
"node": ">=18.0"
+564 -571
View File
File diff suppressed because it is too large Load Diff
+106 -10
View File
@@ -55,8 +55,8 @@ def require_admin_by_default():
"/auth",
"/auth/first_time_login",
"/login",
# Authenticated user endpoints (allow_any_authenticated)
"/logout",
# Authenticated user endpoints (allow_any_authenticated)
"/profile",
# Public info endpoints (allow_public)
"/",
@@ -311,7 +311,10 @@ def get_jwt_secret() -> str:
)
jwt_secret = secrets.token_hex(64)
try:
with open(jwt_secret_file, "w") as f:
fd = os.open(
jwt_secret_file, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600
)
with os.fdopen(fd, "w") as f:
f.write(str(jwt_secret))
except Exception:
logger.warning(
@@ -356,9 +359,35 @@ def verify_password(password, password_hash):
return secrets.compare_digest(password_hash, compare_hash)
def validate_password_strength(password: str) -> tuple[bool, Optional[str]]:
"""
Validate password strength.
Returns a tuple of (is_valid, error_message).
"""
if not password:
return False, "Password cannot be empty"
if len(password) < 8:
return False, "Password must be at least 8 characters long"
if not any(c.isupper() for c in password):
return False, "Password must contain at least one uppercase letter"
if not any(c.isdigit() for c in password):
return False, "Password must contain at least one digit"
if not any(c in '!@#$%^&*(),.?":{}|<>' for c in password):
return False, "Password must contain at least one special character"
return True, None
def create_encoded_jwt(user, role, expiration, secret):
return jwt.encode(
{"alg": "HS256"}, {"sub": user, "role": role, "exp": expiration}, secret
{"alg": "HS256"},
{"sub": user, "role": role, "exp": expiration, "iat": int(time.time())},
secret,
)
@@ -619,13 +648,27 @@ def auth(request: Request):
return fail_response
# if the jwt cookie is expiring soon
elif jwt_source == "cookie" and expiration - JWT_REFRESH <= current_time:
if jwt_source == "cookie" and expiration - JWT_REFRESH <= current_time:
logger.debug("jwt token expiring soon, refreshing cookie")
# ensure the user hasn't been deleted
# Check if password has been changed since token was issued
# If so, force re-login by rejecting the refresh
try:
User.get_by_id(user)
user_obj = User.get_by_id(user)
if user_obj.password_changed_at is not None:
token_iat = int(token.claims.get("iat", 0))
password_changed_timestamp = int(
user_obj.password_changed_at.timestamp()
)
if token_iat < password_changed_timestamp:
logger.debug(
"jwt token issued before password change, rejecting refresh"
)
return fail_response
except DoesNotExist:
logger.debug("user not found")
return fail_response
new_expiration = current_time + JWT_SESSION_LENGTH
new_encoded_jwt = create_encoded_jwt(
user, role, new_expiration, request.app.jwt_token
@@ -660,7 +703,7 @@ def profile(request: Request):
)
@router.get("/logout", dependencies=[Depends(allow_any_authenticated())])
@router.get("/logout", dependencies=[Depends(allow_public())])
def logout(request: Request):
auth_config: AuthConfig = request.app.frigate_config.auth
response = RedirectResponse("/login", status_code=303)
@@ -782,10 +825,63 @@ async def update_password(
HASH_ITERATIONS = request.app.frigate_config.auth.hash_iterations
password_hash = hash_password(body.password, iterations=HASH_ITERATIONS)
User.set_by_id(username, {User.password_hash: password_hash})
try:
user = User.get_by_id(username)
except DoesNotExist:
return JSONResponse(content={"message": "User not found"}, status_code=404)
return JSONResponse(content={"success": True})
# Require old_password when:
# 1. Non-admin user is changing another user's password (admin only action)
# 2. Any user is changing their own password
is_changing_own_password = current_username == username
is_non_admin = current_role != "admin"
if is_changing_own_password or is_non_admin:
if not body.old_password:
return JSONResponse(
content={"message": "Current password is required"},
status_code=400,
)
if not verify_password(body.old_password, user.password_hash):
return JSONResponse(
content={"message": "Current password is incorrect"},
status_code=401,
)
# Validate new password strength
is_valid, error_message = validate_password_strength(body.password)
if not is_valid:
return JSONResponse(
content={"message": error_message},
status_code=400,
)
password_hash = hash_password(body.password, iterations=HASH_ITERATIONS)
User.update(
{
User.password_hash: password_hash,
User.password_changed_at: datetime.now(),
}
).where(User.username == username).execute()
response = JSONResponse(content={"success": True})
# If user changed their own password, issue a new JWT to keep them logged in
if current_username == username:
JWT_COOKIE_NAME = request.app.frigate_config.auth.cookie_name
JWT_COOKIE_SECURE = request.app.frigate_config.auth.cookie_secure
JWT_SESSION_LENGTH = request.app.frigate_config.auth.session_length
expiration = int(time.time()) + JWT_SESSION_LENGTH
encoded_jwt = create_encoded_jwt(
username, current_role, expiration, request.app.jwt_token
)
# Set new JWT cookie on response
set_jwt_cookie(
response, JWT_COOKIE_NAME, encoded_jwt, expiration, JWT_COOKIE_SECURE
)
return response
@router.put(
+1 -1
View File
@@ -710,7 +710,7 @@ def delete_classification_dataset_images(
if os.path.isfile(file_path):
os.unlink(file_path)
if os.path.exists(folder) and not os.listdir(folder):
if os.path.exists(folder) and not os.listdir(folder) and category.lower() != "none":
os.rmdir(folder)
return JSONResponse(
+1
View File
@@ -11,6 +11,7 @@ class AppConfigSetBody(BaseModel):
class AppPutPasswordBody(BaseModel):
password: str
old_password: Optional[str] = None
class AppPostUsersBody(BaseModel):
-1
View File
@@ -29,7 +29,6 @@ class EventsDescriptionBody(BaseModel):
class EventsCreateBody(BaseModel):
source_type: Optional[str] = "api"
sub_label: Optional[str] = None
score: Optional[float] = 0
duration: Optional[int] = 30
+38 -21
View File
@@ -346,7 +346,7 @@ def events(
"/events/explore",
response_model=list[EventResponse],
dependencies=[Depends(allow_any_authenticated())],
summary="Get summary of objects.",
summary="Get summary of objects",
description="""Gets a summary of objects from the database.
Returns a list of objects with a max of `limit` objects for each label.
""",
@@ -439,7 +439,7 @@ def events_explore(
"/event_ids",
response_model=list[EventResponse],
dependencies=[Depends(allow_any_authenticated())],
summary="Get events by ids.",
summary="Get events by ids",
description="""Gets events by a list of ids.
Returns a list of events.
""",
@@ -473,7 +473,7 @@ async def event_ids(ids: str, request: Request):
@router.get(
"/events/search",
dependencies=[Depends(allow_any_authenticated())],
summary="Search events.",
summary="Search events",
description="""Searches for events in the database.
Returns a list of events.
""",
@@ -924,7 +924,7 @@ def events_summary(
"/events/{event_id}",
response_model=EventResponse,
dependencies=[Depends(allow_any_authenticated())],
summary="Get event by id.",
summary="Get event by id",
description="Gets an event by its id.",
)
async def event(event_id: str, request: Request):
@@ -968,7 +968,7 @@ def set_retain(event_id: str):
"/events/{event_id}/plus",
response_model=EventUploadPlusResponse,
dependencies=[Depends(require_role(["admin"]))],
summary="Send event to Frigate+.",
summary="Send event to Frigate+",
description="""Sends an event to Frigate+.
Returns a success message or an error if the event is not found.
""",
@@ -1207,7 +1207,7 @@ async def false_positive(request: Request, event_id: str):
"/events/{event_id}/retain",
response_model=GenericResponse,
dependencies=[Depends(require_role(["admin"]))],
summary="Stop event from being retained indefinitely.",
summary="Stop event from being retained indefinitely",
description="""Stops an event from being retained indefinitely.
Returns a success message or an error if the event is not found.
NOTE: This is a legacy endpoint and is not supported in the frontend.
@@ -1236,7 +1236,7 @@ async def delete_retain(event_id: str, request: Request):
"/events/{event_id}/sub_label",
response_model=GenericResponse,
dependencies=[Depends(require_role(["admin"]))],
summary="Set event sub label.",
summary="Set event sub label",
description="""Sets an event's sub label.
Returns a success message or an error if the event is not found.
""",
@@ -1295,7 +1295,7 @@ async def set_sub_label(
"/events/{event_id}/recognized_license_plate",
response_model=GenericResponse,
dependencies=[Depends(require_role(["admin"]))],
summary="Set event license plate.",
summary="Set event license plate",
description="""Sets an event's license plate.
Returns a success message or an error if the event is not found.
""",
@@ -1355,7 +1355,7 @@ async def set_plate(
"/events/{event_id}/description",
response_model=GenericResponse,
dependencies=[Depends(require_role(["admin"]))],
summary="Set event description.",
summary="Set event description",
description="""Sets an event's description.
Returns a success message or an error if the event is not found.
""",
@@ -1411,7 +1411,7 @@ async def set_description(
"/events/{event_id}/description/regenerate",
response_model=GenericResponse,
dependencies=[Depends(require_role(["admin"]))],
summary="Regenerate event description.",
summary="Regenerate event description",
description="""Regenerates an event's description.
Returns a success message or an error if the event is not found.
""",
@@ -1463,8 +1463,8 @@ async def regenerate_description(
@router.post(
"/description/generate",
response_model=GenericResponse,
# dependencies=[Depends(require_role(["admin"]))],
summary="Generate description embedding.",
dependencies=[Depends(require_role(["admin"]))],
summary="Generate description embedding",
description="""Generates an embedding for an event's description.
Returns a success message or an error if the event is not found.
""",
@@ -1529,7 +1529,7 @@ async def delete_single_event(event_id: str, request: Request) -> dict:
"/events/{event_id}",
response_model=GenericResponse,
dependencies=[Depends(require_role(["admin"]))],
summary="Delete event.",
summary="Delete event",
description="""Deletes an event from the database.
Returns a success message or an error if the event is not found.
""",
@@ -1544,7 +1544,7 @@ async def delete_event(request: Request, event_id: str):
"/events/",
response_model=EventMultiDeleteResponse,
dependencies=[Depends(require_role(["admin"]))],
summary="Delete events.",
summary="Delete events",
description="""Deletes a list of events from the database.
Returns a success message or an error if the events are not found.
""",
@@ -1578,7 +1578,7 @@ async def delete_events(request: Request, body: EventsDeleteBody):
"/events/{camera_name}/{label}/create",
response_model=EventCreateResponse,
dependencies=[Depends(require_role(["admin"]))],
summary="Create manual event.",
summary="Create manual event",
description="""Creates a manual event in the database.
Returns a success message or an error if the event is not found.
NOTES:
@@ -1620,7 +1620,7 @@ def create_event(
body.score,
body.sub_label,
body.duration,
body.source_type,
"api",
body.draw,
),
EventMetadataTypeEnum.manual_event_create.value,
@@ -1642,7 +1642,7 @@ def create_event(
"/events/{event_id}/end",
response_model=GenericResponse,
dependencies=[Depends(require_role(["admin"]))],
summary="End manual event.",
summary="End manual event",
description="""Ends a manual event.
Returns a success message or an error if the event is not found.
NOTE: This should only be used for manual events.
@@ -1652,10 +1652,27 @@ async def end_event(request: Request, event_id: str, body: EventsEndBody):
try:
event: Event = Event.get(Event.id == event_id)
await require_camera_access(event.camera, request=request)
if body.end_time is not None and body.end_time < event.start_time:
return JSONResponse(
content=(
{
"success": False,
"message": f"end_time ({body.end_time}) cannot be before start_time ({event.start_time}).",
}
),
status_code=400,
)
end_time = body.end_time or datetime.datetime.now().timestamp()
request.app.event_metadata_updater.publish(
(event_id, end_time), EventMetadataTypeEnum.manual_event_end.value
)
except DoesNotExist:
return JSONResponse(
content=({"success": False, "message": f"Event {event_id} not found."}),
status_code=404,
)
except Exception:
return JSONResponse(
content=(
@@ -1674,7 +1691,7 @@ async def end_event(request: Request, event_id: str, body: EventsEndBody):
"/trigger/embedding",
response_model=dict,
dependencies=[Depends(require_role(["admin"]))],
summary="Create trigger embedding.",
summary="Create trigger embedding",
description="""Creates a trigger embedding for a specific trigger.
Returns a success message or an error if the trigger is not found.
""",
@@ -1832,7 +1849,7 @@ def create_trigger_embedding(
"/trigger/embedding/{camera_name}/{name}",
response_model=dict,
dependencies=[Depends(require_role(["admin"]))],
summary="Update trigger embedding.",
summary="Update trigger embedding",
description="""Updates a trigger embedding for a specific trigger.
Returns a success message or an error if the trigger is not found.
""",
@@ -1997,7 +2014,7 @@ def update_trigger_embedding(
"/trigger/embedding/{camera_name}/{name}",
response_model=dict,
dependencies=[Depends(require_role(["admin"]))],
summary="Delete trigger embedding.",
summary="Delete trigger embedding",
description="""Deletes a trigger embedding for a specific trigger.
Returns a success message or an error if the trigger is not found.
""",
@@ -2071,7 +2088,7 @@ def delete_trigger_embedding(
"/triggers/status/{camera_name}",
response_model=dict,
dependencies=[Depends(require_role(["admin"]))],
summary="Get triggers status.",
summary="Get triggers status",
description="""Gets the status of all triggers for a specific camera.
Returns a success message or an error if the camera is not found.
""",
+51 -2
View File
@@ -837,7 +837,19 @@ async def recording_clip(
dependencies=[Depends(require_camera_access)],
description="Returns an HLS playlist for the specified timestamp-range on the specified camera. Append /master.m3u8 or /index.m3u8 for HLS playback.",
)
async def vod_ts(camera_name: str, start_ts: float, end_ts: float):
async def vod_ts(
camera_name: str,
start_ts: float,
end_ts: float,
force_discontinuity: bool = False,
):
logger.debug(
"VOD: Generating VOD for %s from %s to %s with force_discontinuity=%s",
camera_name,
start_ts,
end_ts,
force_discontinuity,
)
recordings = (
Recordings.select(
Recordings.path,
@@ -862,6 +874,14 @@ async def vod_ts(camera_name: str, start_ts: float, end_ts: float):
recording: Recordings
for recording in recordings:
logger.debug(
"VOD: processing recording: %s start=%s end=%s duration=%s",
recording.path,
recording.start_time,
recording.end_time,
recording.duration,
)
clip = {"type": "source", "path": recording.path}
duration = int(recording.duration * 1000)
@@ -870,6 +890,11 @@ async def vod_ts(camera_name: str, start_ts: float, end_ts: float):
inpoint = int((start_ts - recording.start_time) * 1000)
clip["clipFrom"] = inpoint
duration -= inpoint
logger.debug(
"VOD: applied clipFrom %sms to %s",
inpoint,
recording.path,
)
# adjust end if recording.end_time is after end_ts
if recording.end_time > end_ts:
@@ -877,12 +902,23 @@ async def vod_ts(camera_name: str, start_ts: float, end_ts: float):
if duration < min_duration_ms:
# skip if the clip has no valid duration (too short to contain frames)
logger.debug(
"VOD: skipping recording %s - resulting duration %sms too short",
recording.path,
duration,
)
continue
if min_duration_ms <= duration < max_duration_ms:
clip["keyFrameDurations"] = [duration]
clips.append(clip)
durations.append(duration)
logger.debug(
"VOD: added clip %s duration_ms=%s clipFrom=%s",
recording.path,
duration,
clip.get("clipFrom"),
)
else:
logger.warning(f"Recording clip is missing or empty: {recording.path}")
@@ -902,7 +938,7 @@ async def vod_ts(camera_name: str, start_ts: float, end_ts: float):
return JSONResponse(
content={
"cache": hour_ago.timestamp() > start_ts,
"discontinuity": False,
"discontinuity": force_discontinuity,
"consistentSequenceMediaInfo": True,
"durations": durations,
"segment_duration": max(durations),
@@ -986,6 +1022,19 @@ async def vod_event(
return vod_response
@router.get(
"/vod/clip/{camera_name}/start/{start_ts}/end/{end_ts}",
dependencies=[Depends(require_camera_access)],
description="Returns an HLS playlist for a timestamp range with HLS discontinuity enabled. Append /master.m3u8 or /index.m3u8 for HLS playback.",
)
async def vod_clip(
camera_name: str,
start_ts: float,
end_ts: float,
):
return await vod_ts(camera_name, start_ts, end_ts, force_discontinuity=True)
@router.get(
"/events/{event_id}/snapshot.jpg",
description="Returns a snapshot image for the specified object id. NOTE: The query params only take affect while the event is in-progress. Once the event has ended the snapshot configuration is used.",
+34 -10
View File
@@ -5,10 +5,14 @@ import os
from datetime import datetime, timedelta, timezone
import pytz
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import JSONResponse
from frigate.api.auth import require_camera_access
from frigate.api.auth import (
allow_any_authenticated,
get_allowed_cameras_for_filter,
require_camera_access,
)
from frigate.api.defs.response.preview_response import (
PreviewFramesResponse,
PreviewsResponse,
@@ -26,19 +30,32 @@ router = APIRouter(tags=[Tags.preview])
@router.get(
"/preview/{camera_name}/start/{start_ts}/end/{end_ts}",
response_model=PreviewsResponse,
dependencies=[Depends(require_camera_access)],
dependencies=[Depends(allow_any_authenticated())],
summary="Get preview clips for time range",
description="""Gets all preview clips for a specified camera and time range.
Returns a list of preview video clips that overlap with the requested time period,
ordered by start time. Use camera_name='all' to get previews from all cameras.
Returns an error if no previews are found.""",
)
def preview_ts(camera_name: str, start_ts: float, end_ts: float):
def preview_ts(
camera_name: str,
start_ts: float,
end_ts: float,
allowed_cameras: list[str] = Depends(get_allowed_cameras_for_filter),
):
"""Get all mp4 previews relevant for time period."""
if camera_name != "all":
camera_clause = Previews.camera == camera_name
if camera_name not in allowed_cameras:
raise HTTPException(status_code=403, detail="Access denied for camera")
camera_list = [camera_name]
else:
camera_clause = True
camera_list = allowed_cameras
if not camera_list:
return JSONResponse(
content={"success": False, "message": "No previews found."},
status_code=404,
)
previews = (
Previews.select(
@@ -53,7 +70,7 @@ def preview_ts(camera_name: str, start_ts: float, end_ts: float):
| Previews.end_time.between(start_ts, end_ts)
| ((start_ts > Previews.start_time) & (end_ts < Previews.end_time))
)
.where(camera_clause)
.where(Previews.camera << camera_list)
.order_by(Previews.start_time.asc())
.dicts()
.iterator()
@@ -88,14 +105,21 @@ def preview_ts(camera_name: str, start_ts: float, end_ts: float):
@router.get(
"/preview/{year_month}/{day}/{hour}/{camera_name}/{tz_name}",
response_model=PreviewsResponse,
dependencies=[Depends(require_camera_access)],
dependencies=[Depends(allow_any_authenticated())],
summary="Get preview clips for specific hour",
description="""Gets all preview clips for a specific hour in a given timezone.
Converts the provided date/time from the specified timezone to UTC and retrieves
all preview clips for that hour. Use camera_name='all' to get previews from all cameras.
The tz_name should be a timezone like 'America/New_York' (use commas instead of slashes).""",
)
def preview_hour(year_month: str, day: int, hour: int, camera_name: str, tz_name: str):
def preview_hour(
year_month: str,
day: int,
hour: int,
camera_name: str,
tz_name: str,
allowed_cameras: list[str] = Depends(get_allowed_cameras_for_filter),
):
"""Get all mp4 previews relevant for time period given the timezone"""
parts = year_month.split("-")
start_date = (
@@ -106,7 +130,7 @@ def preview_hour(year_month: str, day: int, hour: int, camera_name: str, tz_name
start_ts = start_date.timestamp()
end_ts = end_date.timestamp()
return preview_ts(camera_name, start_ts, end_ts)
return preview_ts(camera_name, start_ts, end_ts, allowed_cameras)
@router.get(
+11 -7
View File
@@ -607,23 +607,27 @@ class Dispatcher:
)
self.publish(f"{camera_name}/snapshots/state", payload, retain=True)
def _on_ptz_command(self, camera_name: str, payload: str) -> None:
def _on_ptz_command(self, camera_name: str, payload: str | bytes) -> None:
"""Callback for ptz topic."""
try:
if "preset" in payload.lower():
preset: str = (
payload.decode("utf-8") if isinstance(payload, bytes) else payload
).lower()
if "preset" in preset:
command = OnvifCommandEnum.preset
param = payload.lower()[payload.index("_") + 1 :]
elif "move_relative" in payload.lower():
param = preset[preset.index("_") + 1 :]
elif "move_relative" in preset:
command = OnvifCommandEnum.move_relative
param = payload.lower()[payload.index("_") + 1 :]
param = preset[preset.index("_") + 1 :]
else:
command = OnvifCommandEnum[payload.lower()]
command = OnvifCommandEnum[preset]
param = ""
self.onvif.handle_command(camera_name, command, param)
logger.info(f"Setting ptz command to {command} for {camera_name}")
except KeyError as k:
logger.error(f"Invalid PTZ command {payload}: {k}")
logger.error(f"Invalid PTZ command {preset}: {k}")
def _on_birdseye_command(self, camera_name: str, payload: str) -> None:
"""Callback for birdseye topic."""
+16 -3
View File
@@ -21,7 +21,7 @@ from frigate.config.camera.updater import (
CameraConfigUpdateEnum,
CameraConfigUpdateSubscriber,
)
from frigate.const import CONFIG_DIR
from frigate.const import BASE_DIR, CONFIG_DIR
from frigate.models import User
logger = logging.getLogger(__name__)
@@ -371,7 +371,7 @@ class WebPushClient(Communicator):
sorted_objects.update(payload["after"]["data"]["sub_labels"])
image = f"{payload['after']['thumb_path'].replace('/media/frigate', '')}"
image = f"{payload['after']['thumb_path'].replace(BASE_DIR, '')}"
ended = state == "end" or state == "genai"
if state == "genai" and payload["after"]["data"]["metadata"]:
@@ -390,7 +390,20 @@ class WebPushClient(Communicator):
message = payload["after"]["data"]["metadata"]["scene"]
else:
title = f"{titlecase(', '.join(sorted_objects).replace('_', ' '))}{' was' if state == 'end' else ''} detected in {titlecase(', '.join(payload['after']['data']['zones']).replace('_', ' '))}"
zone_names = payload["after"]["data"]["zones"]
formatted_zone_names = []
for zone_name in zone_names:
if zone_name in self.config.cameras[camera].zones:
formatted_zone_names.append(
self.config.cameras[camera]
.zones[zone_name]
.get_formatted_name(zone_name)
)
else:
formatted_zone_names.append(titlecase(zone_name.replace("_", " ")))
title = f"{titlecase(', '.join(sorted_objects).replace('_', ' '))}{' was' if state == 'end' else ''} detected in {', '.join(formatted_zone_names)}"
message = f"Detected on {camera_name}"
if ended:
+1 -1
View File
@@ -20,7 +20,7 @@ class AuthConfig(FrigateBaseModel):
default=86400, title="Session length for jwt session tokens", ge=60
)
refresh_time: int = Field(
default=43200,
default=1800,
title="Refresh the session if it is going to expire in this many seconds",
ge=30,
)
-3
View File
@@ -37,9 +37,6 @@ class UIConfig(FrigateBaseModel):
time_style: DateTimeStyleEnum = Field(
default=DateTimeStyleEnum.medium, title="Override UI timeStyle."
)
strftime_fmt: Optional[str] = Field(
default=None, title="Override date and time format using strftime syntax."
)
unit_system: UnitSystemEnum = Field(
default=UnitSystemEnum.metric, title="The unit system to use for measurements."
)
@@ -209,10 +209,22 @@ class ReviewDescriptionProcessor(PostProcessorApi):
logger.debug(
f"Found GenAI Review Summary request for {start_ts} to {end_ts}"
)
items: list[dict[str, Any]] = [
r["data"]["metadata"]
# Query all review segments with camera and time information
segments: list[dict[str, Any]] = [
{
"camera": r["camera"].replace("_", " ").title(),
"start_time": r["start_time"],
"end_time": r["end_time"],
"metadata": r["data"]["metadata"],
}
for r in (
ReviewSegment.select(ReviewSegment.data)
ReviewSegment.select(
ReviewSegment.camera,
ReviewSegment.start_time,
ReviewSegment.end_time,
ReviewSegment.data,
)
.where(
(ReviewSegment.data["metadata"].is_null(False))
& (ReviewSegment.start_time < end_ts)
@@ -224,21 +236,72 @@ class ReviewDescriptionProcessor(PostProcessorApi):
)
]
if len(items) == 0:
if len(segments) == 0:
logger.debug("No review items with metadata found during time period")
return "No activity was found during this time."
return "No activity was found during this time period."
important_items = list(
filter(
lambda item: item.get("potential_threat_level", 0) > 0
or item.get("other_concerns"),
items,
)
)
# Identify primary items (important items that need review)
primary_segments = [
seg
for seg in segments
if seg["metadata"].get("potential_threat_level", 0) > 0
or seg["metadata"].get("other_concerns")
]
if not important_items:
if not primary_segments:
return "No concerns were found during this time period."
# Build hierarchical structure: each primary event with its contextual items
events_with_context = []
for primary_seg in primary_segments:
# Start building the primary event structure
primary_item = copy.deepcopy(primary_seg["metadata"])
primary_item["camera"] = primary_seg["camera"]
primary_item["start_time"] = primary_seg["start_time"]
primary_item["end_time"] = primary_seg["end_time"]
# Find overlapping contextual items from other cameras
primary_start = primary_seg["start_time"]
primary_end = primary_seg["end_time"]
primary_camera = primary_seg["camera"]
contextual_items = []
seen_contextual_cameras = set()
for seg in segments:
seg_camera = seg["camera"]
if seg_camera == primary_camera:
continue
if seg in primary_segments:
continue
seg_start = seg["start_time"]
seg_end = seg["end_time"]
if seg_start < primary_end and primary_start < seg_end:
# Avoid duplicates if same camera has multiple overlapping segments
if seg_camera not in seen_contextual_cameras:
contextual_item = copy.deepcopy(seg["metadata"])
contextual_item["camera"] = seg_camera
contextual_item["start_time"] = seg_start
contextual_item["end_time"] = seg_end
contextual_items.append(contextual_item)
seen_contextual_cameras.add(seg_camera)
# Add context array to primary item
primary_item["context"] = contextual_items
events_with_context.append(primary_item)
total_context_items = sum(
len(event.get("context", [])) for event in events_with_context
)
logger.debug(
f"Summary includes {len(events_with_context)} primary events with "
f"{total_context_items} total contextual items"
)
if self.config.review.genai.debug_save_thumbnails:
Path(
os.path.join(CLIPS_DIR, "genai-requests", f"{start_ts}-{end_ts}")
@@ -247,7 +310,7 @@ class ReviewDescriptionProcessor(PostProcessorApi):
return self.genai_client.generate_review_summary(
start_ts,
end_ts,
important_items,
events_with_context,
self.config.review.genai.debug_save_thumbnails,
)
else:
@@ -639,14 +639,14 @@ def write_classification_attempt(
os.makedirs(folder, exist_ok=True)
cv2.imwrite(file, frame)
files = sorted(
filter(lambda f: (f.endswith(".webp")), os.listdir(folder)),
key=lambda f: os.path.getctime(os.path.join(folder, f)),
reverse=True,
)
# delete oldest face image if maximum is reached
try:
files = sorted(
filter(lambda f: (f.endswith(".webp")), os.listdir(folder)),
key=lambda f: os.path.getctime(os.path.join(folder, f)),
reverse=True,
)
if len(files) > max_files:
os.unlink(os.path.join(folder, files[-1]))
except FileNotFoundError:
+288 -20
View File
@@ -1,19 +1,20 @@
import logging
import math
import os
import cv2
import numpy as np
from pydantic import Field
from typing_extensions import Literal
from frigate.detectors.detection_api import DetectionApi
from frigate.detectors.detector_config import BaseDetectorConfig
from frigate.detectors.detector_config import BaseDetectorConfig, ModelTypeEnum
try:
from tflite_runtime.interpreter import Interpreter, load_delegate
except ModuleNotFoundError:
from tensorflow.lite.python.interpreter import Interpreter, load_delegate
logger = logging.getLogger(__name__)
DETECTOR_KEY = "edgetpu"
@@ -26,6 +27,10 @@ class EdgeTpuDetectorConfig(BaseDetectorConfig):
class EdgeTpuTfl(DetectionApi):
type_key = DETECTOR_KEY
supported_models = [
ModelTypeEnum.ssd,
ModelTypeEnum.yologeneric,
]
def __init__(self, detector_config: EdgeTpuDetectorConfig):
device_config = {}
@@ -63,31 +68,294 @@ class EdgeTpuTfl(DetectionApi):
self.tensor_input_details = self.interpreter.get_input_details()
self.tensor_output_details = self.interpreter.get_output_details()
self.model_width = detector_config.model.width
self.model_height = detector_config.model.height
self.min_score = 0.4
self.max_detections = 20
self.model_type = detector_config.model.model_type
self.model_requires_int8 = self.tensor_input_details[0]["dtype"] == np.int8
if self.model_type == ModelTypeEnum.yologeneric:
logger.debug("Using YOLO preprocessing/postprocessing")
if len(self.tensor_output_details) not in [2, 3]:
logger.error(
f"Invalid count of output tensors in YOLO model. Found {len(self.tensor_output_details)}, expecting 2 or 3."
)
raise
self.reg_max = 16 # = 64 dfl_channels // 4 # YOLO standard
self.min_logit_value = np.log(
self.min_score / (1 - self.min_score)
) # for filtering
self._generate_anchors_and_strides() # decode bounding box DFL
self.project = np.arange(
self.reg_max, dtype=np.float32
) # for decoding bounding box DFL information
# Determine YOLO tensor indices and quantization scales for
# boxes and class_scores the tensor ordering and names are
# not reliable, so use tensor shape to detect which tensor
# holds boxes or class scores.
# The tensors have shapes (B, N, C)
# where N is the number of candidates (=2100 for 320x320)
# this may guess wrong if the number of classes is exactly 64
output_boxes_index = None
output_classes_index = None
for i, x in enumerate(self.tensor_output_details):
# the nominal index seems to start at 1 instead of 0
if len(x["shape"]) == 3 and x["shape"][2] == 64:
output_boxes_index = i
elif len(x["shape"]) == 3 and x["shape"][2] > 1:
# require the number of classes to be more than 1
# to differentiate from (not used) max score tensor
output_classes_index = i
if output_boxes_index is None or output_classes_index is None:
logger.warning("Unrecognized model output, unexpected tensor shapes.")
output_classes_index = (
0
if (output_boxes_index is None or output_classes_index == 1)
else 1
) # 0 is default guess
output_boxes_index = 1 if (output_boxes_index == 0) else 0
scores_details = self.tensor_output_details[output_classes_index]
self.scores_tensor_index = scores_details["index"]
self.scores_scale, self.scores_zero_point = scores_details["quantization"]
# calculate the quantized version of the min_score
self.min_score_quantized = int(
(self.min_logit_value / self.scores_scale) + self.scores_zero_point
)
self.logit_shift_to_positive_values = (
max(0, math.ceil((128 + self.scores_zero_point) * self.scores_scale))
+ 1
) # round up
boxes_details = self.tensor_output_details[output_boxes_index]
self.boxes_tensor_index = boxes_details["index"]
self.boxes_scale, self.boxes_zero_point = boxes_details["quantization"]
elif self.model_type == ModelTypeEnum.ssd:
logger.debug("Using SSD preprocessing/postprocessing")
# SSD model indices (4 outputs: boxes, class_ids, scores, count)
for x in self.tensor_output_details:
if len(x["shape"]) == 3:
self.output_boxes_index = x["index"]
elif len(x["shape"]) == 1:
self.output_count_index = x["index"]
self.output_class_ids_index = None
self.output_class_scores_index = None
else:
raise Exception(
f"{self.model_type} is currently not supported for edgetpu. See the docs for more info on supported models."
)
def _generate_anchors_and_strides(self):
# for decoding the bounding box DFL information into xy coordinates
all_anchors = []
all_strides = []
strides = (8, 16, 32) # YOLO's small, medium, large detection heads
for stride in strides:
feat_h, feat_w = self.model_height // stride, self.model_width // stride
grid_y, grid_x = np.meshgrid(
np.arange(feat_h, dtype=np.float32),
np.arange(feat_w, dtype=np.float32),
indexing="ij",
)
grid_coords = np.stack((grid_x.flatten(), grid_y.flatten()), axis=1)
anchor_points = grid_coords + 0.5
all_anchors.append(anchor_points)
all_strides.append(np.full((feat_h * feat_w, 1), stride, dtype=np.float32))
self.anchors = np.concatenate(all_anchors, axis=0)
self.anchor_strides = np.concatenate(all_strides, axis=0)
def determine_indexes_for_non_yolo_models(self):
"""Legacy method for SSD models."""
if (
self.output_class_ids_index is None
or self.output_class_scores_index is None
):
for i in range(4):
index = self.tensor_output_details[i]["index"]
if (
index != self.output_boxes_index
and index != self.output_count_index
):
if (
np.mod(np.float32(self.interpreter.tensor(index)()[0][0]), 1)
== 0.0
):
self.output_class_ids_index = index
else:
self.output_scores_index = index
def pre_process(self, tensor_input):
if self.model_requires_int8:
tensor_input = np.bitwise_xor(tensor_input, 128).view(
np.int8
) # shift by -128
return tensor_input
def detect_raw(self, tensor_input):
tensor_input = self.pre_process(tensor_input)
self.interpreter.set_tensor(self.tensor_input_details[0]["index"], tensor_input)
self.interpreter.invoke()
boxes = self.interpreter.tensor(self.tensor_output_details[0]["index"])()[0]
class_ids = self.interpreter.tensor(self.tensor_output_details[1]["index"])()[0]
scores = self.interpreter.tensor(self.tensor_output_details[2]["index"])()[0]
count = int(
self.interpreter.tensor(self.tensor_output_details[3]["index"])()[0]
)
if self.model_type == ModelTypeEnum.yologeneric:
# Multi-tensor YOLO model with (non-standard B(H*W)C output format).
# (the comments indicate the shape of tensors,
# using "2100" as the anchor count (for image size of 320x320),
# "NC" as number of classes,
# "N" as the count that survive after min-score filtering)
# TENSOR A) class scores (1, 2100, NC) with logit values
# TENSOR B) box coordinates (1, 2100, 64) encoded as dfl scores
# Recommend that the model clamp the logit values in tensor (A)
# to the range [-4,+4] to preserve precision from [2%,98%]
# and because NMS requires the min_score parameter to be >= 0
detections = np.zeros((20, 6), np.float32)
# don't dequantize scores data yet, wait until the low-confidence
# candidates are filtered out from the overall result set.
# This reduces the work and makes post-processing faster.
# this method works with raw quantized numbers when possible,
# which relies on the value of the scale factor to be >0.
# This speeds up max and argmax operations.
# Get max confidence for each detection and create the mask
detections = np.zeros(
(self.max_detections, 6), np.float32
) # initialize zero results
scores_output_quantized = self.interpreter.get_tensor(
self.scores_tensor_index
)[0] # (2100, NC)
max_scores_quantized = np.max(scores_output_quantized, axis=1) # (2100,)
mask = max_scores_quantized >= self.min_score_quantized # (2100,)
for i in range(count):
if scores[i] < 0.4 or i == 20:
break
detections[i] = [
class_ids[i],
float(scores[i]),
boxes[i][0],
boxes[i][1],
boxes[i][2],
boxes[i][3],
if not np.any(mask):
return detections # empty results
max_scores_filtered_shiftedpositive = (
(max_scores_quantized[mask] - self.scores_zero_point)
* self.scores_scale
) + self.logit_shift_to_positive_values # (N,1) shifted logit values
scores_output_quantized_filtered = scores_output_quantized[mask]
# dequantize boxes. NMS needs them to be in float format
# remove candidates with probabilities < threshold
boxes_output_quantized_filtered = (
self.interpreter.get_tensor(self.boxes_tensor_index)[0]
)[mask] # (N, 64)
boxes_output_filtered = (
boxes_output_quantized_filtered.astype(np.float32)
- self.boxes_zero_point
) * self.boxes_scale
# 2. Decode DFL to distances (ltrb)
dfl_distributions = boxes_output_filtered.reshape(
-1, 4, self.reg_max
) # (N, 4, 16)
# Softmax over the 16 bins
dfl_max = np.max(dfl_distributions, axis=2, keepdims=True)
dfl_exp = np.exp(dfl_distributions - dfl_max)
dfl_probs = dfl_exp / np.sum(dfl_exp, axis=2, keepdims=True) # (N, 4, 16)
# Weighted sum: (N, 4, 16) * (16,) -> (N, 4)
distances = np.einsum("pcr,r->pc", dfl_probs, self.project)
# Calculate box corners in pixel coordinates
anchors_filtered = self.anchors[mask]
anchor_strides_filtered = self.anchor_strides[mask]
x1y1 = (
anchors_filtered - distances[:, [0, 1]]
) * anchor_strides_filtered # (N, 2)
x2y2 = (
anchors_filtered + distances[:, [2, 3]]
) * anchor_strides_filtered # (N, 2)
boxes_filtered_decoded = np.concatenate((x1y1, x2y2), axis=-1) # (N, 4)
# 9. Apply NMS. Use logit scores here to defer sigmoid()
# until after filtering out redundant boxes
# Shift the logit scores to be non-negative (required by cv2)
indices = cv2.dnn.NMSBoxes(
bboxes=boxes_filtered_decoded,
scores=max_scores_filtered_shiftedpositive,
score_threshold=(
self.min_logit_value + self.logit_shift_to_positive_values
),
nms_threshold=0.4, # should this be a model config setting?
)
num_detections = len(indices)
if num_detections == 0:
return detections # empty results
nms_indices = np.array(indices, dtype=np.int32).ravel() # or .flatten()
if num_detections > self.max_detections:
nms_indices = nms_indices[: self.max_detections]
num_detections = self.max_detections
kept_logits_quantized = scores_output_quantized_filtered[nms_indices]
class_ids_post_nms = np.argmax(kept_logits_quantized, axis=1)
# Extract the final boxes and scores using fancy indexing
final_boxes = boxes_filtered_decoded[nms_indices]
final_scores_logits = (
max_scores_filtered_shiftedpositive[nms_indices]
- self.logit_shift_to_positive_values
) # Unshifted logits
# Detections array format: [class_id, score, ymin, xmin, ymax, xmax]
detections[:num_detections, 0] = class_ids_post_nms
detections[:num_detections, 1] = 1.0 / (
1.0 + np.exp(-final_scores_logits)
) # sigmoid
detections[:num_detections, 2] = final_boxes[:, 1] / self.model_height
detections[:num_detections, 3] = final_boxes[:, 0] / self.model_width
detections[:num_detections, 4] = final_boxes[:, 3] / self.model_height
detections[:num_detections, 5] = final_boxes[:, 2] / self.model_width
return detections
elif self.model_type == ModelTypeEnum.ssd:
self.determine_indexes_for_non_yolo_models()
boxes = self.interpreter.tensor(self.tensor_output_details[0]["index"])()[0]
class_ids = self.interpreter.tensor(
self.tensor_output_details[1]["index"]
)()[0]
scores = self.interpreter.tensor(self.tensor_output_details[2]["index"])()[
0
]
count = int(
self.interpreter.tensor(self.tensor_output_details[3]["index"])()[0]
)
return detections
detections = np.zeros((self.max_detections, 6), np.float32)
for i in range(count):
if scores[i] < self.min_score:
break
if i == self.max_detections:
logger.debug(f"Too many detections ({count})!")
break
detections[i] = [
class_ids[i],
float(scores[i]),
boxes[i][0],
boxes[i][1],
boxes[i][2],
boxes[i][3],
]
return detections
else:
raise Exception(
f"{self.model_type} is currently not supported for edgetpu. See the docs for more info on supported models."
)
+47 -49
View File
@@ -12,9 +12,6 @@ from peewee import DoesNotExist, IntegrityError
from PIL import Image
from playhouse.shortcuts import model_to_dict
from frigate.comms.embeddings_updater import (
EmbeddingsRequestEnum,
)
from frigate.comms.inter_process import InterProcessRequestor
from frigate.config import FrigateConfig
from frigate.config.classification import SemanticSearchModelEnum
@@ -495,44 +492,49 @@ class Embeddings:
or thumbnail_missing
):
existing_trigger.embedding = self._calculate_trigger_embedding(
trigger
trigger, trigger_name, camera.name
)
needs_embedding_update = True
if needs_embedding_update:
existing_trigger.save()
continue
else:
# Create new trigger
try:
try:
event: Event = Event.get(Event.id == trigger.data)
except DoesNotExist:
logger.warning(
f"Event ID {trigger.data} for trigger {trigger_name} does not exist."
# For thumbnail triggers, validate the event exists
if trigger.type == "thumbnail":
try:
event: Event = Event.get(Event.id == trigger.data)
except DoesNotExist:
logger.warning(
f"Event ID {trigger.data} for trigger {trigger_name} does not exist."
)
continue
# Skip the event if not an object
if event.data.get("type") != "object":
logger.warning(
f"Event ID {trigger.data} for trigger {trigger_name} is not a tracked object."
)
continue
thumbnail = get_event_thumbnail_bytes(event)
if not thumbnail:
logger.warning(
f"Unable to retrieve thumbnail for event ID {trigger.data} for {trigger_name}."
)
continue
self.write_trigger_thumbnail(
camera.name, trigger.data, thumbnail
)
continue
# Skip the event if not an object
if event.data.get("type") != "object":
logger.warning(
f"Event ID {trigger.data} for trigger {trigger_name} is not a tracked object."
)
continue
thumbnail = get_event_thumbnail_bytes(event)
if not thumbnail:
logger.warning(
f"Unable to retrieve thumbnail for event ID {trigger.data} for {trigger_name}."
)
continue
self.write_trigger_thumbnail(
camera.name, trigger.data, thumbnail
)
# Calculate embedding for new trigger
embedding = self._calculate_trigger_embedding(trigger)
embedding = self._calculate_trigger_embedding(
trigger, trigger_name, camera.name
)
Trigger.create(
camera=camera.name,
@@ -558,7 +560,11 @@ class Embeddings:
Trigger.camera == camera.name, Trigger.name.in_(triggers_to_remove)
).execute()
for trigger_name in triggers_to_remove:
self.remove_trigger_thumbnail(camera.name, trigger_name)
# Only remove thumbnail files for thumbnail triggers
if existing_triggers[trigger_name].type == "thumbnail":
self.remove_trigger_thumbnail(
camera.name, existing_triggers[trigger_name].data
)
def write_trigger_thumbnail(
self, camera: str, event_id: str, thumbnail: bytes
@@ -588,14 +594,13 @@ class Embeddings:
f"Failed to delete thumbnail for trigger with data {event_id} in {camera}: {e}"
)
def _calculate_trigger_embedding(self, trigger) -> bytes:
def _calculate_trigger_embedding(
self, trigger, trigger_name: str, camera_name: str
) -> bytes:
"""Calculate embedding for a trigger based on its type and data."""
if trigger.type == "description":
logger.debug(f"Generating embedding for trigger description {trigger.name}")
embedding = self.requestor.send_data(
EmbeddingsRequestEnum.embed_description.value,
{"id": None, "description": trigger.data, "upsert": False},
)
logger.debug(f"Generating embedding for trigger description {trigger_name}")
embedding = self.embed_description(None, trigger.data, upsert=False)
return embedding.astype(np.float32).tobytes()
elif trigger.type == "thumbnail":
@@ -615,28 +620,21 @@ class Embeddings:
try:
with open(
os.path.join(
TRIGGER_DIR, trigger.camera, f"{trigger.data}.webp"
),
os.path.join(TRIGGER_DIR, camera_name, f"{trigger.data}.webp"),
"rb",
) as f:
thumbnail = f.read()
except Exception as e:
logger.error(
f"Failed to read thumbnail for trigger {trigger.name} with ID {trigger.data}: {e}"
f"Failed to read thumbnail for trigger {trigger_name} with ID {trigger.data}: {e}"
)
return b""
logger.debug(
f"Generating embedding for trigger thumbnail {trigger.name} with ID {trigger.data}"
f"Generating embedding for trigger thumbnail {trigger_name} with ID {trigger.data}"
)
embedding = self.requestor.send_data(
EmbeddingsRequestEnum.embed_thumbnail.value,
{
"id": str(trigger.data),
"thumbnail": str(thumbnail),
"upsert": False,
},
embedding = self.embed_thumbnail(
str(trigger.data), thumbnail, upsert=False
)
return embedding.astype(np.float32).tobytes()
+40 -36
View File
@@ -177,56 +177,60 @@ Each line represents a detection state, not necessarily unique individuals. Pare
self,
start_ts: float,
end_ts: float,
segments: list[dict[str, Any]],
events: list[dict[str, Any]],
debug_save: bool,
) -> str | None:
"""Generate a summary of review item descriptions over a period of time."""
time_range = f"{datetime.datetime.fromtimestamp(start_ts).strftime('%B %d, %Y at %I:%M %p')} to {datetime.datetime.fromtimestamp(end_ts).strftime('%B %d, %Y at %I:%M %p')}"
timeline_summary_prompt = f"""
You are a security officer.
Time range: {time_range}.
Input: JSON list with "title", "scene", "confidence", "potential_threat_level" (1-2), "other_concerns".
You are a security officer writing a concise security report.
Task: Write a concise, human-presentable security report in markdown format.
Time range: {time_range}
Rules for the report:
Input format: Each event is a JSON object with:
- "title", "scene", "confidence", "potential_threat_level" (0-2), "other_concerns", "camera", "time", "start_time", "end_time"
- "context": array of related events from other cameras that occurred during overlapping time periods
- Title & overview
- Start with:
# Security Summary - {time_range}
- Write a 1-2 sentence situational overview capturing the general pattern of the period.
Report Structure - Use this EXACT format:
- Event details
- Present events in chronological order as a bullet list.
- **If multiple events occur within the same minute or overlapping time range, COMBINE them into a single bullet.**
- Summarize the distinct activities as sub-points under the shared timestamp.
- If no timestamp is given, preserve order but label as Time not specified.
- Use bold timestamps for clarity.
- Group bullets under subheadings when multiple events fall into the same category (e.g., Vehicle Activity, Porch Activity, Unusual Behavior).
# Security Summary - {time_range}
- Threat levels
- Always show the threat level for each event using these labels:
- Threat level 0: "Normal"
- Threat level 1: "Needs review"
- Threat level 2: "Security concern"
- Format as (threat level: Normal), (threat level: Needs review), or (threat level: Security concern).
- If multiple events at the same time share the same threat level, only state it once.
## Overview
[Write 1-2 sentences summarizing the overall activity pattern during this period.]
- Final assessment
- End with a Final Assessment section.
- If all events are threat level 0:
Final assessment: Only normal residential activity observed during this period.
- If threat level 1 events are present:
Final assessment: Some activity requires review but no security concerns identified.
- If threat level 2 events are present, clearly summarize them as Security concerns requiring immediate attention.
---
- Conciseness
- Do not repeat benign clothing/appearance details unless they distinguish individuals.
- Summarize similar routine events instead of restating full scene descriptions.
## Timeline
[Group events by time periods (e.g., "Morning (6:00 AM - 12:00 PM)", "Afternoon (12:00 PM - 5:00 PM)", "Evening (5:00 PM - 9:00 PM)", "Night (9:00 PM - 6:00 AM)"). Use appropriate time blocks based on when events occurred.]
### [Time Block Name]
**HH:MM AM/PM** | [Camera Name] | [Threat Level Indicator]
- [Event title]: [Clear description incorporating contextual information from the "context" array]
- Context: [If context array has items, mention them here, e.g., "Delivery truck present on Front Driveway Cam (HH:MM AM/PM)"]
- Assessment: [Brief assessment incorporating context - if context explains the event, note it here]
[Repeat for each event in chronological order within the time block]
---
## Summary
[One sentence summarizing the period. If all events are normal/explained: "Routine activity observed." If review needed: "Some activity requires review but no security concerns." If security concerns: "Security concerns requiring immediate attention."]
Guidelines:
- List ALL events in chronological order, grouped by time blocks
- Threat level indicators: Normal, Needs review, 🔴 Security concern
- Integrate contextual information naturally - use the "context" array to enrich each event's description
- If context explains the event (e.g., delivery truck explains person at door), describe it accordingly (e.g., "delivery person" not "unidentified person")
- Be concise but informative - focus on what happened and what it means
- If contextual information makes an event clearly normal, reflect that in your assessment
- Only create time blocks that have events - don't create empty sections
"""
for item in segments:
timeline_summary_prompt += f"\n{item}"
timeline_summary_prompt += "\n\nEvents:\n"
for event in events:
timeline_summary_prompt += f"\n{event}\n"
if debug_save:
with open(
+1
View File
@@ -133,6 +133,7 @@ class User(Model):
default="admin",
)
password_hash = CharField(null=False, max_length=120)
password_changed_at = DateTimeField(null=True)
notification_tokens = JSONField()
@classmethod
+6
View File
@@ -239,6 +239,12 @@ class ImprovedMotionDetector(MotionDetector):
)
self.mask = np.where(resized_mask == [0])
# Reset motion detection state when mask changes
# so motion detection can quickly recalibrate with the new mask
self.avg_frame = np.zeros(self.motion_frame_size, np.float32)
self.calibrating = True
self.motion_frame_count = 0
def stop(self) -> None:
"""stop the motion detector."""
pass
+25 -5
View File
@@ -95,12 +95,21 @@ class OnvifController:
cam = self.camera_configs[cam_name]
try:
user = cam.onvif.user
password = cam.onvif.password
if user is not None and isinstance(user, bytes):
user = user.decode("utf-8")
if password is not None and isinstance(password, bytes):
password = password.decode("utf-8")
self.cams[cam_name] = {
"onvif": ONVIFCamera(
cam.onvif.host,
cam.onvif.port,
cam.onvif.user,
cam.onvif.password,
user,
password,
wsdl_dir=str(Path(find_spec("onvif").origin).parent / "wsdl"),
adjust_time=cam.onvif.ignore_time_mismatch,
encrypt=not cam.onvif.tls_insecure,
@@ -325,9 +334,15 @@ class OnvifController:
presets = []
for preset in presets:
self.cams[camera_name]["presets"][
(getattr(preset, "Name") or f"preset {preset['token']}").lower()
] = preset["token"]
# Ensure preset name is a Unicode string and handle UTF-8 characters correctly
preset_name = getattr(preset, "Name") or f"preset {preset['token']}"
if isinstance(preset_name, bytes):
preset_name = preset_name.decode("utf-8")
# Convert to lowercase while preserving UTF-8 characters
preset_name_lower = preset_name.lower()
self.cams[camera_name]["presets"][preset_name_lower] = preset["token"]
# get list of supported features
supported_features = []
@@ -563,6 +578,11 @@ class OnvifController:
self.cams[camera_name]["active"] = False
async def _move_to_preset(self, camera_name: str, preset: str) -> None:
if isinstance(preset, bytes):
preset = preset.decode("utf-8")
preset = preset.lower()
if preset not in self.cams[camera_name]["presets"]:
logger.error(f"{preset} is not a valid preset for {camera_name}")
return
+2
View File
@@ -78,6 +78,8 @@ class TrackedObjectProcessor(threading.Thread):
[
CameraConfigUpdateEnum.add,
CameraConfigUpdateEnum.enabled,
CameraConfigUpdateEnum.motion,
CameraConfigUpdateEnum.objects,
CameraConfigUpdateEnum.remove,
CameraConfigUpdateEnum.zones,
],
+3 -2
View File
@@ -14,6 +14,7 @@ from setproctitle import setproctitle
import frigate.log
from frigate.config.logger import LoggerConfig
from frigate.const import CONFIG_DIR
class BaseProcess(mp.Process):
@@ -92,7 +93,7 @@ class FrigateProcess(BaseProcess):
try:
import memray
reports_dir = pathlib.Path("/config/memray_reports")
reports_dir = pathlib.Path(CONFIG_DIR) / "memray_reports"
reports_dir.mkdir(parents=True, exist_ok=True)
safe_name = (
process_name.replace(":", "_").replace("/", "_").replace("\\", "_")
@@ -126,7 +127,7 @@ class FrigateProcess(BaseProcess):
self.__memray_tracker.__exit__(None, None, None)
self.__memray_tracker = None
reports_dir = pathlib.Path("/config/memray_reports")
reports_dir = pathlib.Path(CONFIG_DIR) / "memray_reports"
html_file = reports_dir / f"{safe_name}.html"
result = subprocess.run(
+1
View File
@@ -771,6 +771,7 @@ def process_frames(
camera_enabled = camera_config.enabled
if "motion" in updated_configs:
motion_detector.config = camera_config.motion
motion_detector.update_mask()
if (
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+2 -2
View File
@@ -4,8 +4,8 @@ from frigate.models import Event
def migrate(migrator, database, fake=False, **kwargs):
migrator.change_columns(Event, sub_label=pw.CharField(max_length=100, null=True))
migrator.change_fields(Event, sub_label=pw.CharField(max_length=100, null=True))
def rollback(migrator, database, fake=False, **kwargs):
migrator.change_columns(Event, sub_label=pw.CharField(max_length=20, null=True))
migrator.change_fields(Event, sub_label=pw.CharField(max_length=20, null=True))
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+1 -1
View File
@@ -5,7 +5,7 @@ Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+6 -4
View File
@@ -8,7 +8,7 @@ Some examples (model - class or model_name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
@@ -54,7 +54,9 @@ def migrate(migrator, database, fake=False, **kwargs):
# Migrate existing has_been_reviewed data to UserReviewStatus for all users
def migrate_data():
all_users = list(User.select())
# Use raw SQL to avoid ORM issues with columns that don't exist yet
cursor = database.execute_sql('SELECT "username" FROM "user"')
all_users = cursor.fetchall()
if not all_users:
return
@@ -63,7 +65,7 @@ def migrate(migrator, database, fake=False, **kwargs):
)
reviewed_segment_ids = [row[0] for row in cursor.fetchall()]
# also migrate for anonymous (unauthenticated users)
usernames = [user.username for user in all_users] + ["anonymous"]
usernames = [user[0] for user in all_users] + ["anonymous"]
for segment_id in reviewed_segment_ids:
for username in usernames:
@@ -74,7 +76,7 @@ def migrate(migrator, database, fake=False, **kwargs):
)
if not fake: # Only run data migration if not faking
migrator.python(migrate_data)
migrator.run(migrate_data)
migrator.sql('ALTER TABLE "reviewsegment" DROP COLUMN "has_been_reviewed"')
+1 -1
View File
@@ -6,7 +6,7 @@ Some examples (model - class or model_name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
+42
View File
@@ -0,0 +1,42 @@
"""Peewee migrations -- 032_add_password_changed_at.py.
Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.run(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
> migrator.change_fields(model, **fields) # Change fields
> migrator.remove_fields(model, *field_names, cascade=True)
> migrator.rename_field(model, old_field_name, new_field_name)
> migrator.rename_table(model, new_table_name)
> migrator.add_index(model, *col_names, unique=False)
> migrator.drop_index(model, *col_names)
> migrator.add_not_null(model, *field_names)
> migrator.drop_not_null(model, *field_names)
> migrator.add_default(model, field_name, default)
"""
import peewee as pw
SQL = pw.SQL
def migrate(migrator, database, fake=False, **kwargs):
migrator.sql(
"""
ALTER TABLE user ADD COLUMN password_changed_at DATETIME NULL
"""
)
def rollback(migrator, database, fake=False, **kwargs):
migrator.sql(
"""
ALTER TABLE user DROP COLUMN password_changed_at
"""
)
+2 -1
View File
@@ -1,5 +1,6 @@
{
"form": {
"user": "Потребителско име"
"user": "Потребителско име",
"password": "Парола"
}
}
+2 -1
View File
@@ -10,6 +10,7 @@
"select": "Избери"
},
"restart": {
"title": "Сигурен ли сте, че искате да рестартирате Frigate?"
"title": "Сигурен ли сте, че искате да рестартирате Frigate?",
"button": "Рестартирай"
}
}
+4 -1
View File
@@ -1,5 +1,8 @@
{
"iconPicker": {
"selectIcon": "Изберете иконка"
"selectIcon": "Изберете иконка",
"search": {
"placeholder": "Потърси за икона…"
}
}
}
+4 -1
View File
@@ -1,7 +1,10 @@
{
"button": {
"downloadVideo": {
"label": "Свали видео"
"label": "Свали видео",
"toast": {
"success": "Вашето видео за преглеждане почна да се изтегля."
}
}
}
}
@@ -1 +1,3 @@
{}
{
"documentTitle": "Модели за класификация"
}
@@ -1 +1,4 @@
{}
{
"documentTitle": "Настройки на конфигурацията - Фригейт",
"configEditor": "Настройки на конфигурацията"
}
+3 -1
View File
@@ -9,5 +9,7 @@
"aria": "Избери събития",
"noFoundForTimePeriod": "Няма намерени събития за този времеви период."
},
"allCameras": "Всички камери"
"allCameras": "Всички камери",
"alerts": "Известия",
"detections": "Засичания"
}
+3 -1
View File
@@ -8,5 +8,7 @@
}
},
"trackedObjectsCount_one": "{{count}} проследен обект ",
"trackedObjectsCount_other": "{{count}} проследени обекта "
"trackedObjectsCount_other": "{{count}} проследени обекта ",
"documentTitle": "Разгледай - Фригейт",
"generativeAI": "Генериращ Изкъствен Интелект"
}
+2 -1
View File
@@ -1,3 +1,4 @@
{
"documentTitle": "Експорт - Frigate"
"documentTitle": "Експорт - Frigate",
"search": "Търси"
}
@@ -10,5 +10,9 @@
"deletedName_one": "{{count}} лице бе изтрито успешно.",
"deletedName_other": "{{count}} лица бяха изтрити успешно."
}
},
"description": {
"addFace": "Добавете нова колекция във библиотеката за лица при качването на първата ви снимка.",
"placeholder": "Напишете име за тази колекция"
}
}
+2 -1
View File
@@ -64,5 +64,6 @@
"cameraSettings": {
"cameraEnabled": "Камерата е включена"
},
"documentTitle": "Наживо - Frigate"
"documentTitle": "Наживо - Frigate",
"documentTitle.withCamera": "{{camera}} - На живо - Фригейт"
}
+3 -1
View File
@@ -1,5 +1,7 @@
{
"button": {
"save": "Запазване на търсенето"
}
},
"search": "Търси",
"savedSearches": "Запазени търсения"
}
@@ -12,5 +12,9 @@
"point_one": "{{count}} точка",
"point_other": "{{count}} точки"
}
},
"documentTitle": {
"default": "Настройки - Фригейт",
"authentication": "Настройки за сигурността - Фругейт"
}
}
+4
View File
@@ -1,5 +1,9 @@
{
"stats": {
"healthy": "Системата е изправна"
},
"documentTitle": {
"cameras": "Статистики за Камери - Фригейт",
"storage": "Статистика за паметта - Фригейт"
}
}
+2 -1
View File
@@ -58,7 +58,8 @@
"endTimeMustAfterStartTime": "L'hora de finalització ha de ser posterior a l'hora d'inici",
"noVaildTimeSelected": "No s'ha seleccionat un rang de temps vàlid",
"failed": "No s'ha pogut inciar l'exportació: {{error}}"
}
},
"view": "Vista"
},
"fromTimeline": {
"saveExport": "Guardar exportació",
@@ -1,5 +1,5 @@
{
"documentTitle": "Models de classificació",
"documentTitle": "Models de classificació - Frigate",
"button": {
"deleteClassificationAttempts": "Suprimeix les imatges de classificació",
"renameCategory": "Reanomena la classe",
@@ -152,7 +152,12 @@
"generateSuccess": "Imatges de mostra generades amb èxit",
"allImagesRequired_one": "Classifiqueu totes les imatges. Queda {{count}} imatge.",
"allImagesRequired_many": "Classifiqueu totes les imatges. Queden {{count}} imatges.",
"allImagesRequired_other": "Classifiqueu totes les imatges. Queden {{count}} imatges."
"allImagesRequired_other": "Classifiqueu totes les imatges. Queden {{count}} imatges.",
"modelCreated": "El model s'ha creat correctament. Utilitzeu la vista Classificacions recents per a afegir imatges per als estats que falten i, a continuació, entrenar el model.",
"missingStatesWarning": {
"title": "Falten exemples d'estat",
"description": "Es recomana seleccionar exemples per a tots els estats per obtenir els millors resultats. Podeu continuar sense seleccionar tots els estats, però el model no serà entrenat fins que tots els estats tinguin imatges. Després de continuar, utilitzeu la vista Classificacions recents per classificar imatges per als estats que falten, i després entrenar el model."
}
}
},
"deleteModel": {
@@ -180,5 +185,6 @@
"noNewImages": "Sense noves imatges per entrenar. Classifica més imatges primer.",
"modelNotReady": "El model no está preparat per entrenar",
"noChanges": "No hi ha canvis al conjunt de dades des de l'última formació."
}
},
"none": "Cap"
}
+5 -1
View File
@@ -55,5 +55,9 @@
"trackedPoint": "Punt de seguiment"
},
"zoomIn": "Amplia",
"zoomOut": "Redueix"
"zoomOut": "Redueix",
"normalActivity": "Normal",
"needsReview": "Necessita revisió",
"securityConcern": "Preocupació per la seguretat",
"select_all": "Tots"
}
+7 -2
View File
@@ -220,6 +220,10 @@
"viewTrackingDetails": {
"label": "Veure detalls de seguiment",
"aria": "Mostra els detalls de seguiment"
},
"downloadCleanSnapshot": {
"label": "Descarrega la instantània neta",
"aria": "Descarrega la instantània neta"
}
},
"noTrackedObjects": "No s'han trobat objectes rastrejats",
@@ -263,7 +267,8 @@
"header": {
"zones": "Zones",
"ratio": "Ràtio",
"area": "Àrea"
"area": "Àrea",
"score": "Puntuació"
}
},
"annotationSettings": {
@@ -278,7 +283,7 @@
"millisecondsToOffset": "Millisegons per l'òfset de detecció d'anotacions per. <em>Per defecte: 0</em>",
"tips": "Reduïu el valor si la reproducció del vídeo es troba per davant dels quadres i els punts de ruta, i augmenteu-lo si es troba per darrere. Aquest valor pot ser negatiu.",
"toast": {
"success": "L'Òfset d'anotació per a {{camera}} s'ha desat al fitxer de configuració. Reinicieu Frigate per aplicar els canvis."
"success": "El desplaçament de l'anotació per {{camera}} s'ha desat al fitxer de configuració."
}
}
},
+2 -1
View File
@@ -13,7 +13,8 @@
"train": {
"empty": "No hi ha intents recents de reconeixement de rostres",
"title": "Reconeixements recents",
"aria": "Selecciona els reconeixements recents"
"aria": "Selecciona els reconeixements recents",
"titleShort": "Recent"
},
"description": {
"addFace": "Afegiu una col·lecció nova a la biblioteca de cares pujant la vostra primera imatge.",
+29 -10
View File
@@ -172,7 +172,7 @@
"name": {
"inputPlaceHolder": "Introduïu un nom…",
"title": "Nom",
"tips": "El nom ha de tenir almenys 2 caràcters, ha de tenir almenys una lletra, i no ha de ser el nom d'una càmera o una altra zona."
"tips": "El nom ha de tenir almenys 2 caràcters, ha de tenir almenys una lletra, i no ha de ser el nom d'una càmera o una altra zona en aquesta càmera."
},
"label": "Zones",
"desc": {
@@ -199,7 +199,7 @@
},
"clickDrawPolygon": "Fes click per a dibuixar un polígon a la imatge.",
"toast": {
"success": "La zona {{zoneName}} ha estat desada. Reinicia Frigate per a aplicar els canvis."
"success": "S'ha desat la zona ({{zoneName}})."
}
},
"filter": {
@@ -229,8 +229,8 @@
"clickDrawPolygon": "Fes click per a dibuixar un polígon a la imatge.",
"toast": {
"success": {
"title": "{{polygonName}} s'ha desat. Reinicia Frigate per a aplicar els canvis.",
"noName": "La màscara de moviment ha estat desada. Reinicia Frigate per aplicar els canvis."
"title": "{{polygonName}} s'ha desat.",
"noName": "La màscara de moviment ha estat desada."
}
}
},
@@ -254,8 +254,8 @@
"clickDrawPolygon": "Fes click per a dibuixar un polígon a la imatge.",
"toast": {
"success": {
"title": "{{polygonName}} s'ha desat. Reinicia Frigate per a aplicar els canvis.",
"noName": "La màscara d'objectes ha estat desada. Reincia Frigate per a aplicar els canvis."
"title": "{{polygonName}} s'ha desat.",
"noName": "La màscara d'objectes ha estat desada."
}
},
"context": "Les màscares de filtratge dobjectes sutilitzen per descartar falsos positius dun tipus dobjecte concret segons la seva ubicació."
@@ -527,7 +527,16 @@
"notMatch": "Les contrasenyes no coincideixen",
"match": "Les contrasenyes coincideixen",
"placeholder": "Introdueix la contrasenya",
"title": "Contrasenya"
"title": "Contrasenya",
"show": "Mostra contrasenya",
"hide": "Amaga contrasenya",
"requirements": {
"title": "Requisits contrasenya:",
"length": "Com a mínim 8 carácters",
"uppercase": "Com a mínim una majúscula",
"digit": "Com a mínim un digit",
"special": "Com a mínim un carácter especial (!@#$%^&*(),.?\":{}|<>)"
}
},
"newPassword": {
"title": "Nova contrasenya",
@@ -537,14 +546,23 @@
}
},
"usernameIsRequired": "El nom d'usuari és obligatori",
"passwordIsRequired": "La contrasenya és obligatoria"
"passwordIsRequired": "La contrasenya és obligatoria",
"currentPassword": {
"title": "Constrasenya actual",
"placeholder": "Entra l'actual contrasenya"
}
},
"passwordSetting": {
"updatePassword": "Contrasenya actualitzada per {{username}}",
"setPassword": "Estableix Contrasenya",
"cannotBeEmpty": "La contrasenya no pot ser buida",
"doNotMatch": "Les contrasenyes no coincideixen",
"desc": "Crea un nova contrasenya segura per protegir aquest compte."
"desc": "Crea un nova contrasenya segura per protegir aquest compte.",
"currentPasswordRequired": "L'actual contrasenya es requerida",
"incorrectCurrentPassword": "L'actual contrasenya es incorrecte",
"passwordVerificationFailed": "Falla en la verificació de la contrasenya",
"multiDeviceWarning": "Serà necesari loguejarte en qualsevol altre dispositiu en que estiguis loguejat en {{refresh_time}}.",
"multiDeviceAdmin": "També pots forçar a tots els usuaris a tornar a autenticar-se immediatament rotant el teu secret JWT."
},
"deleteUser": {
"title": "Suprimir usuari",
@@ -1138,7 +1156,8 @@
"audioCodecRequired": "Es requereix un flux d'àudio per admetre la detecció d'àudio.",
"restreamingWarning": "Reduir les connexions a la càmera per al flux de registre pot augmentar lleugerament l'ús de la CPU.",
"brands": {
"reolink-rtsp": "No és racomana utilitzar Reolink RSTP. Activeu HTTP a la configuració del microprogramari de la càmera i reinicieu l'assistent."
"reolink-rtsp": "No és racomana utilitzar Reolink RSTP. Activeu HTTP a la configuració del microprogramari de la càmera i reinicieu l'assistent.",
"reolink-http": "Els fluxos HTTP de reenllaç haurien d'utilitzar FFmpeg per a una millor compatibilitat. Habilita «Utilitza el mode de compatibilitat del flux» per a aquest flux."
},
"dahua": {
"substreamWarning": "El substream 1 està bloquejat a una resolució baixa. Moltes càmeres Dahua / Amcrest / EmpireTech suporten subfluxos addicionals que han d'estar habilitats a la configuració de la càmera. Es recomana comprovar i utilitzar aquests corrents si estan disponibles."
@@ -25,7 +25,10 @@
"deletedModel_one": "Úspešne odstranený {{count}} model",
"deletedModel_few": "Úspešne odstranené {{count}} modely",
"deletedModel_other": "Úspěšne ostranených {{count}} modelov",
"deletedCategory": "Zmazať triedu"
"deletedCategory": "Zmazať triedu",
"categorizedImage": "Obrázek úspěšně klasifikován",
"trainedModel": "Úspěšně vytrénovaný model.",
"trainingModel": "Trénování modelu bylo úspěšně zahájeno."
}
}
}
+3 -1
View File
@@ -41,6 +41,8 @@
"zoomOut": "Oddálit",
"detail": {
"label": "Detail",
"noDataFound": "Žádná detailní data k prohlédnutí"
"noDataFound": "Žádná detailní data k prohlédnutí",
"aria": "Přepnout detailní zobrazení",
"trackedObject_other": "{{count}} objektů"
}
}
+1 -1
View File
@@ -36,7 +36,7 @@
"desc": "Skutečně chcete vymazat kolekci {{name}}? Toto trvale vymaže všechny přiřazené obličeje."
},
"train": {
"title": "Trénovat",
"title": "Nedávná rozpoznání",
"empty": "Nejsou zde žádné předchozí pokusy o rozpoznání obličeje",
"aria": "Vybrat trénink"
},
+4 -2
View File
@@ -8,7 +8,7 @@
"masksAndZones": "Editor masky a zón - Frigate",
"motionTuner": "Ladění detekce pohybu - Frigate",
"object": "Ladění - Frigate",
"general": "Obecné nastavení - Frigate",
"general": "Nastavení rozhraní- Frigate",
"frigatePlus": "Frigate+ nastavení - Frigate",
"enrichments": "Nastavení obohacení - Frigate",
"cameraManagement": "Správa kamer - Frigate",
@@ -307,7 +307,9 @@
"frigateplus": "Frigate+",
"enrichments": "Obohacení",
"triggers": "Spouštěče",
"cameraManagement": "Správa"
"cameraManagement": "Správa",
"cameraReview": "Kontrola",
"roles": "Role"
},
"dialog": {
"unsavedChanges": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"time": {
"untilForTime": "Indtil{{time}}",
"untilForTime": "Indtil {{time}}",
"untilForRestart": "Indtil Frigate genstarter.",
"untilRestart": "Indtil genstart",
"ago": "{{timeAgo}} siden",
@@ -15,5 +15,36 @@
},
"count_one": "{{count}} Label",
"label": "Etiketter"
},
"zones": {
"label": "Zoner",
"all": {
"title": "Alle zoner",
"short": "Zoner"
}
},
"more": "Flere filtre",
"sort": {
"label": "Sortér",
"dateAsc": "Dato (Stigende)",
"dateDesc": "Dato (Faldende)",
"speedAsc": "Anslået hastighed (Stigende)",
"speedDesc": "Anslået hastighed (Faldende)",
"relevance": "Relevans"
},
"dates": {
"selectPreset": "Vælg en forudindstilling…",
"all": {
"title": "Alle datoer",
"short": "Datoer"
}
},
"reset": {
"label": "Nulstille filtre til standardværdier"
},
"timeRange": "Tidsinterval",
"estimatedSpeed": "Anslået hastighed ({{unit}})",
"features": {
"hasVideoClip": "Har et videoklip"
}
}

Some files were not shown because too many files have changed in this diff Show More