Compare commits

..
37 Commits
Author SHA1 Message Date
12042c1d73 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.7% (1282 of 1285 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (473 of 473 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (809 of 809 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (1277 of 1277 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (475 of 475 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (49 of 49 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (1276 of 1276 strings)

Co-authored-by: GuoQing Liu <842607283@qq.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Yechi Yang <yechiyang93@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-search/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/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/views-search
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2026-06-29 18:15:47 +00:00
e6fcb60efb Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (473 of 473 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (109 of 109 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (62 of 62 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (23 of 23 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (807 of 807 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (473 of 473 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (129 of 129 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (1287 of 1287 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (100 of 100 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (145 of 145 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (74 of 74 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (108 of 108 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (239 of 239 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jamie HUANG <114514020@live.asia.edu.tw>
Co-authored-by: 莊凱鈞 <kcchuang88@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/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-filter/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-validation/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-chat/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-explore/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-motionsearch/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/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/Config - Validation
Translation: Frigate NVR/common
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/components-filter
Translation: Frigate NVR/views-chat
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-motionSearch
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2026-06-29 18:15:46 +00:00
Hosted WeblateandPavol Krnáč b6acc3c6c2 Translated using Weblate (Slovak)
Currently translated at 67.3% (68 of 101 strings)

Translated using Weblate (Slovak)

Currently translated at 0.9% (8 of 809 strings)

Translated using Weblate (Slovak)

Currently translated at 97.9% (234 of 239 strings)

Translated using Weblate (Slovak)

Currently translated at 98.0% (49 of 50 strings)

Translated using Weblate (Slovak)

Currently translated at 49.4% (631 of 1277 strings)

Translated using Weblate (Slovak)

Currently translated at 91.4% (118 of 129 strings)

Translated using Weblate (Slovak)

Currently translated at 1.6% (8 of 475 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Pavol Krnáč <palokrnac@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/sk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/sk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/sk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/sk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/sk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/sk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/sk/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-settings
2026-06-29 18:15:45 +00:00
c943ecfc85 Translated using Weblate (Swedish)
Currently translated at 2.7% (13 of 475 strings)

Translated using Weblate (Swedish)

Currently translated at 0.6% (5 of 809 strings)

Translated using Weblate (Swedish)

Currently translated at 50.7% (648 of 1277 strings)

Translated using Weblate (Swedish)

Currently translated at 0.1% (1 of 809 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (Swedish)

Currently translated at 0.6% (3 of 475 strings)

Translated using Weblate (Swedish)

Currently translated at 94.4% (137 of 145 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (26 of 26 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (101 of 101 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (239 of 239 strings)

Co-authored-by: Christian Bengtsson <bnccnb@gmail.com>
Co-authored-by: Fredrik Tuomas <fredrik.tuomas@gmail.com>
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/common/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/sv/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/sv/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/components-player
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-settings
2026-06-29 18:15:43 +00:00
2b4907a9fb Translated using Weblate (French)
Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (French)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (French)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (French)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (French)

Currently translated at 56.4% (35 of 62 strings)

Co-authored-by: Antoine de Champlain <mon.nom.tony@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: LeBuzzy <bwinster2@outlook.com>
Co-authored-by: NicoA08 <nicolasantunes08@gmail.com>
Co-authored-by: shdw <weblate@assez.biz>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/fr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-motionsearch/fr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/fr/
Translation: Frigate NVR/common
Translation: Frigate NVR/views-motionSearch
Translation: Frigate NVR/views-system
2026-06-29 18:15:42 +00:00
dac8f8e80b Translated using Weblate (Spanish)
Currently translated at 100.0% (473 of 473 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (108 of 108 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1285 of 1285 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (809 of 809 strings)

Translated using Weblate (Spanish)

Currently translated at 99.4% (187 of 188 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1277 of 1277 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (475 of 475 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Libre <6n0n1m0s@proton.me>
Co-authored-by: jjavin <javiernovoa@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/es/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/es/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/es/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/es/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/es/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2026-06-29 18:15:41 +00:00
Hosted Weblateandlaurensthedeveloper bfc074c91f Translated using Weblate (Dutch)
Currently translated at 91.0% (92 of 101 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (239 of 239 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: laurensthedeveloper <laurensg100@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/nl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/nl/
Translation: Frigate NVR/common
Translation: Frigate NVR/components-dialog
2026-06-29 18:15:40 +00:00
5902182730 Translated using Weblate (Indonesian)
Currently translated at 64.8% (70 of 108 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (Indonesian)

Currently translated at 57.4% (58 of 101 strings)

Translated using Weblate (Indonesian)

Currently translated at 43.5% (44 of 101 strings)

Translated using Weblate (Indonesian)

Currently translated at 94.0% (47 of 50 strings)

Translated using Weblate (Indonesian)

Currently translated at 42.5% (43 of 101 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (Indonesian)

Currently translated at 90.0% (45 of 50 strings)

Translated using Weblate (Indonesian)

Currently translated at 90.0% (45 of 50 strings)

Co-authored-by: Alberto-Audrix <alberto.suiwidjaya6@gmail.com>
Co-authored-by: Diazt Muhammad Firmansyah <diaztmuhammadfirmansyah@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Naufal F <fadhlurrahmannf0812@gmail.com>
Co-authored-by: Yeni Setiawan <yenisetiawan@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/id/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/id/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/id/
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
2026-06-29 18:15:39 +00:00
Hosted WeblateandAhmed Marzouq d6fe217f66 Translated using Weblate (Arabic)
Currently translated at 19.6% (25 of 127 strings)

Translated using Weblate (Arabic)

Currently translated at 30.7% (154 of 501 strings)

Co-authored-by: Ahmed Marzouq <ahmed.marzouq.co@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/ar/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/objects/ar/
Translation: Frigate NVR/audio
Translation: Frigate NVR/objects
2026-06-29 18:15:38 +00:00
2b627f12c1 Translated using Weblate (Italian)
Currently translated at 74.2% (601 of 809 strings)

Translated using Weblate (Italian)

Currently translated at 99.9% (1276 of 1277 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (145 of 145 strings)

Translated using Weblate (Italian)

Currently translated at 99.5% (473 of 475 strings)

Translated using Weblate (Italian)

Currently translated at 73.0% (591 of 809 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (1276 of 1276 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (475 of 475 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (100 of 100 strings)

Translated using Weblate (Italian)

Currently translated at 67.7% (548 of 809 strings)

Translated using Weblate (Italian)

Currently translated at 55.7% (451 of 809 strings)

Translated using Weblate (Italian)

Currently translated at 76.0% (361 of 475 strings)

Co-authored-by: Edoardo Sorrenti <ed.sorrenti@gmail.com>
Co-authored-by: Filippo-riccardo Franzin (filippo franzin) <filric01@gmail.com>
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/config-cameras/it/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/it/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-explore/it/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/it/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/it/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/views-explore
Translation: Frigate NVR/views-live
Translation: Frigate NVR/views-settings
2026-06-29 18:15:37 +00:00
e89e8ca356 Translated using Weblate (Polish)
Currently translated at 10.4% (84 of 807 strings)

Translated using Weblate (Polish)

Currently translated at 33.8% (160 of 473 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (108 of 108 strings)

Translated using Weblate (Polish)

Currently translated at 31.3% (149 of 475 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (101 of 101 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (Polish)

Currently translated at 98.4% (127 of 129 strings)

Translated using Weblate (Polish)

Currently translated at 9.1% (74 of 809 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Kamil Cybułka <kamil.cybulka@gmail.com>
Co-authored-by: Paweł Kapeluszny <cyberitsec@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/config-cameras/pl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/pl/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/pl/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/common
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/views-classificationmodel
2026-06-29 18:15:35 +00:00
Hosted WeblateandRaziel Zaarur 63cddfa6a4 Translated using Weblate (Hebrew)
Currently translated at 90.7% (217 of 239 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Raziel Zaarur <razielzaarur1@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/he/
Translation: Frigate NVR/common
2026-06-29 18:15:34 +00:00
Hosted WeblateandLaszlo Bana 0315f89d4b Translated using Weblate (Hungarian)
Currently translated at 31.4% (17 of 54 strings)

Translated using Weblate (Hungarian)

Currently translated at 86.8% (435 of 501 strings)

Translated using Weblate (Hungarian)

Currently translated at 72.8% (137 of 188 strings)

Translated using Weblate (Hungarian)

Currently translated at 85.6% (429 of 501 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Laszlo Bana <banalac@yahoo.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/hu/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-chat/hu/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/hu/
Translation: Frigate NVR/audio
Translation: Frigate NVR/views-chat
Translation: Frigate NVR/views-system
2026-06-29 18:15:33 +00:00
302cca6791 Translated using Weblate (Portuguese)
Currently translated at 50.4% (55 of 109 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (Portuguese)

Currently translated at 33.1% (427 of 1287 strings)

Translated using Weblate (Portuguese)

Currently translated at 99.5% (238 of 239 strings)

Translated using Weblate (Portuguese)

Currently translated at 99.4% (498 of 501 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: João Nuno <joaomnuno@gmail.com>
Co-authored-by: ssantos <ssantos@web.de>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/pt/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/pt/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/pt/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/pt/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/pt/
Translation: Frigate NVR/audio
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/views-settings
2026-06-29 18:15:32 +00:00
Hosted WeblateandEduardo Pastor Fernández d64dddadc3 Translated using Weblate (Catalan)
Currently translated at 100.0% (109 of 109 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (1287 of 1287 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (108 of 108 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (1285 of 1285 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (473 of 473 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (1277 of 1277 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (809 of 809 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (475 of 475 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (1277 of 1277 strings)

Co-authored-by: Eduardo Pastor Fernández <123eduardoneko123@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/config-cameras/ca/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/ca/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/ca/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/ca/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2026-06-29 18:15:31 +00:00
de9bb3945a Translated using Weblate (Ukrainian)
Currently translated at 81.3% (153 of 188 strings)

Translated using Weblate (Ukrainian)

Currently translated at 6.7% (32 of 473 strings)

Translated using Weblate (Ukrainian)

Currently translated at 2.7% (22 of 807 strings)

Translated using Weblate (Ukrainian)

Currently translated at 80.8% (152 of 188 strings)

Translated using Weblate (Ukrainian)

Currently translated at 6.3% (30 of 473 strings)

Translated using Weblate (Ukrainian)

Currently translated at 2.6% (21 of 807 strings)

Translated using Weblate (Ukrainian)

Currently translated at 6.1% (29 of 475 strings)

Translated using Weblate (Ukrainian)

Currently translated at 2.4% (20 of 809 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (501 of 501 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (501 of 501 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (501 of 501 strings)

Translated using Weblate (Ukrainian)

Currently translated at 1.2% (10 of 809 strings)

Translated using Weblate (Ukrainian)

Currently translated at 4.0% (19 of 475 strings)

Translated using Weblate (Ukrainian)

Currently translated at 0.7% (6 of 809 strings)

Translated using Weblate (Ukrainian)

Currently translated at 3.1% (15 of 475 strings)

Translated using Weblate (Ukrainian)

Currently translated at 0.4% (4 of 809 strings)

Translated using Weblate (Ukrainian)

Currently translated at 2.5% (12 of 475 strings)

Translated using Weblate (Ukrainian)

Currently translated at 1.6% (8 of 475 strings)

Translated using Weblate (Ukrainian)

Currently translated at 96.1% (25 of 26 strings)

Translated using Weblate (Ukrainian)

Currently translated at 0.1% (1 of 809 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (50 of 50 strings)

Co-authored-by: A T <andrey.timchenko@gmail.com>
Co-authored-by: Den <denis.ua22@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Vitaliy Kreminskiy <vkrmk13@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/uk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/uk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/uk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/uk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/uk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/uk/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/uk/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/audio
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-player
Translation: Frigate NVR/views-system
2026-06-29 18:15:30 +00:00
577d28beb5 Translated using Weblate (Bulgarian)
Currently translated at 11.6% (7 of 60 strings)

Translated using Weblate (Bulgarian)

Currently translated at 50.0% (43 of 86 strings)

Translated using Weblate (Bulgarian)

Currently translated at 20.0% (29 of 145 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (26 of 26 strings)

Translated using Weblate (Bulgarian)

Currently translated at 5.6% (27 of 475 strings)

Translated using Weblate (Bulgarian)

Currently translated at 7.0% (57 of 809 strings)

Translated using Weblate (Bulgarian)

Currently translated at 34.0% (34 of 100 strings)

Translated using Weblate (Bulgarian)

Currently translated at 3.1% (4 of 129 strings)

Translated using Weblate (Bulgarian)

Currently translated at 78.4% (393 of 501 strings)

Translated using Weblate (Bulgarian)

Currently translated at 15.6% (10 of 64 strings)

Translated using Weblate (Bulgarian)

Currently translated at 74.8% (375 of 501 strings)

Translated using Weblate (Bulgarian)

Currently translated at 20.2% (15 of 74 strings)

Translated using Weblate (Bulgarian)

Currently translated at 36.8% (88 of 239 strings)

Translated using Weblate (Bulgarian)

Currently translated at 36.8% (88 of 239 strings)

Translated using Weblate (Bulgarian)

Currently translated at 33.0% (33 of 100 strings)

Translated using Weblate (Bulgarian)

Currently translated at 0.2% (1 of 475 strings)

Translated using Weblate (Bulgarian)

Currently translated at 21.2% (27 of 127 strings)

Translated using Weblate (Bulgarian)

Currently translated at 2.3% (3 of 129 strings)

Translated using Weblate (Bulgarian)

Currently translated at 1.1% (9 of 809 strings)

Co-authored-by: Dobromir Kirov <kirov0407@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Plamen Stoyanov <fireto@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-filter/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/objects/bg/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/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/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/audio
Translation: Frigate NVR/common
Translation: Frigate NVR/components-filter
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
2026-06-29 18:15:29 +00:00
Hosted Weblateandlukasig ead1896c12 Translated using Weblate (Romanian)
Currently translated at 100.0% (473 of 473 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (1285 of 1285 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (809 of 809 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (475 of 475 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (1277 of 1277 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/config-cameras/ro/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/ro/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/ro/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/ro/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2026-06-29 18:15:28 +00:00
fb291f2eee Translated using Weblate (Russian)
Currently translated at 73.1% (79 of 108 strings)

Translated using Weblate (Russian)

Currently translated at 54.9% (706 of 1285 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (Russian)

Currently translated at 92.4% (221 of 239 strings)

Co-authored-by: Artem Vladimirov <artyomka71@mail.ru>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Vladimir Bely <vlwwwwww@gmail.com>
Co-authored-by: Дмитрий Власкин <vdvlaskin@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/ru/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/ru/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/ru/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/ru/
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/views-settings
2026-06-29 18:15:26 +00:00
Hosted WeblateandGeorge Rovolis 62effbeed6 Translated using Weblate (Greek)
Currently translated at 14.3% (72 of 501 strings)

Translated using Weblate (Greek)

Currently translated at 49.7% (119 of 239 strings)

Co-authored-by: George Rovolis <georgios@rovolis.co.uk>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/el/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/el/
Translation: Frigate NVR/audio
Translation: Frigate NVR/common
2026-06-29 18:15:25 +00:00
Hosted Weblateandnicoleise f80f92c484 Translated using Weblate (Danish)
Currently translated at 1.0% (13 of 1287 strings)

Translated using Weblate (Danish)

Currently translated at 28.4% (31 of 109 strings)

Translated using Weblate (Danish)

Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (Danish)

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (Danish)

Currently translated at 41.3% (207 of 501 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: nicoleise <niceggert@hotmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/da/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/da/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/da/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/da/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-settings/da/
Translation: Frigate NVR/audio
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/views-settings
2026-06-29 18:15:24 +00:00
e8096c271a Translated using Weblate (German)
Currently translated at 100.0% (1287 of 1287 strings)

Translated using Weblate (German)

Currently translated at 100.0% (108 of 108 strings)

Translated using Weblate (German)

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (German)

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (German)

Currently translated at 100.0% (473 of 473 strings)

Translated using Weblate (German)

Currently translated at 100.0% (127 of 127 strings)

Translated using Weblate (German)

Currently translated at 100.0% (501 of 501 strings)

Translated using Weblate (German)

Currently translated at 100.0% (1285 of 1285 strings)

Translated using Weblate (German)

Currently translated at 100.0% (188 of 188 strings)

Translated using Weblate (German)

Currently translated at 100.0% (807 of 807 strings)

Translated using Weblate (German)

Currently translated at 100.0% (475 of 475 strings)

Translated using Weblate (German)

Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (German)

Currently translated at 100.0% (809 of 809 strings)

Translated using Weblate (German)

Currently translated at 100.0% (62 of 62 strings)

Translated using Weblate (German)

Currently translated at 100.0% (1276 of 1276 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Nuendo-DE <tim.huehner@gmx.de>
Co-authored-by: Sebastian Sie <sebastian.neuplanitz@googlemail.com>
Co-authored-by: laurensthedeveloper <laurensg100@gmail.com>
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-camera/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/objects/de/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-motionsearch/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/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/audio
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/objects
Translation: Frigate NVR/views-motionSearch
Translation: Frigate NVR/views-settings
Translation: Frigate NVR/views-system
2026-06-29 18:15:23 +00:00
Hosted WeblateandRicardo RFPP 31abe9d1af Translated using Weblate (Portuguese (Brazil))
Currently translated at 90.7% (49 of 54 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (23 of 23 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (25 of 25 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 11.4% (92 of 807 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 17.7% (84 of 473 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 52.7% (68 of 129 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (100 of 100 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (26 of 26 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 95.4% (104 of 109 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.8% (500 of 501 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Ricardo RFPP <ricardo.inteli@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/audio/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-groups/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-validation/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-chat/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-classificationmodel/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-facelibrary/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-live/pt_BR/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/Config - Groups
Translation: Frigate NVR/Config - Validation
Translation: Frigate NVR/audio
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/components-player
Translation: Frigate NVR/views-chat
Translation: Frigate NVR/views-classificationmodel
Translation: Frigate NVR/views-facelibrary
Translation: Frigate NVR/views-live
2026-06-29 18:15:22 +00:00
b3d598b64b Translated using Weblate (Turkish)
Currently translated at 3.2% (26 of 807 strings)

Translated using Weblate (Turkish)

Currently translated at 6.1% (29 of 473 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (26 of 26 strings)

Translated using Weblate (Turkish)

Currently translated at 98.1% (106 of 108 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (50 of 50 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (239 of 239 strings)

Translated using Weblate (Turkish)

Currently translated at 73.4% (138 of 188 strings)

Translated using Weblate (Turkish)

Currently translated at 6.4% (4 of 62 strings)

Translated using Weblate (Turkish)

Currently translated at 98.0% (49 of 50 strings)

Translated using Weblate (Turkish)

Currently translated at 90.0% (91 of 101 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Nazım Sarp Tekbaş <sarptekbas07@gmail.com>
Co-authored-by: Selim Kundakçıoğlu <selimkundakcioglu@gmail.com>
Co-authored-by: Turhan Munis <turhan.munis@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/common/tr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-camera/tr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-dialog/tr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/components-player/tr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-cameras/tr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/config-global/tr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-motionsearch/tr/
Translate-URL: https://hosted.weblate.org/projects/frigate-nvr/views-system/tr/
Translation: Frigate NVR/Config - Cameras
Translation: Frigate NVR/Config - Global
Translation: Frigate NVR/common
Translation: Frigate NVR/components-camera
Translation: Frigate NVR/components-dialog
Translation: Frigate NVR/components-player
Translation: Frigate NVR/views-motionSearch
Translation: Frigate NVR/views-system
2026-06-29 18:15:20 +00:00
Blake BlackshearandClaude Opus 4.8 ea131e1663 Merge remote-tracking branch 'origin/master' into dev
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
Resolve conflicts in the export pipeline where dev's job-queue refactor
met master's chapter-metadata and security work.

- Unify chapter support under ChaptersEnum (none / recording_segments /
  review_items); the realtime stream-copy export selects the per-segment
  or per-review-item builder by the camera's configured mode. Thread
  chapters through ExportRecordingsBody -> _build_export_job -> ExportJob
  -> RecordingExporter.
- Keep master's creation_time/comment export metadata and fix a
  video_path duplication the textual merge introduced in the preview
  command.
- Move the chapters request field to ExportRecordingsBody (the single
  export endpoint) where it is actually honored.

Restore security fixes the automatic merge would have reverted:
- frigate/util/services.py: restore the #23493 rename to the public
  is_go2rtc_arbitrary_exec_allowed so create_config.py's dynamic-source
  exec guard imports and runs (the merge otherwise left a broken import).
- Preserve the export image-path ".." traversal check inside
  _sanitize_existing_image, applied to single/custom/batch exports.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 15:04:01 +02:00
Josh HawkinsandGitHub 3d4dd3ac4b allow non-admin users to send PTZ commands for cameras they have access to (#23578)
CI / AMD64 Build (push) Has been cancelled
CI / ARM Build (push) Has been cancelled
CI / Jetson Jetpack 6 (push) Has been cancelled
CI / Assemble and push default build (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
2026-06-27 15:55:39 -06:00
Josh HawkinsandGitHub 933a7f1a3f resolve the leaked Query default so media Cache-Control max-age is always a valid int (#23553)
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
CI / AMD64 Build (push) Has been cancelled
2026-06-24 07:57:46 -05:00
Josh HawkinsandGitHub 4e5e8e3c59 Offload preview encoding and Plus upload off the API event loop (#23552)
* offload preview ffmpeg encoding to a thread to avoid blocking the api event loop

* offload Frigate+ recording snapshot upload to a thread to avoid blocking the api event loop
2026-06-24 07:17:23 -05:00
Nicolas MowenandGitHub b3ce4486b9 Catch edge cases in security protections (#23493)
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
* Fix go2rtc nested key dict

* Don't allow path traversal
2026-06-16 08:07:12 -06:00
Nicolas MowenandGitHub 06e3d0ac5d Chapter tweaks (#23440)
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
* Add camera metadata and fix preview chapters

* Add config option for chapters
2026-06-09 09:07:42 -06:00
Nicolas MowenandGitHub 28e3e1ec74 Add ability to control chapters set on MP4 Export (#23310)
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 / Assemble and push default build (push) Has been cancelled
CI / Synaptics Build (push) Has been cancelled
2026-05-25 13:06:16 -05:00
Josh HawkinsandGitHub fa07109a85 filter motion review by allowed cameras (#23294)
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
2026-05-23 06:47:32 -06:00
Josh HawkinsandGitHub 910059281f update mask docs for more clarity (#23282) 2026-05-21 14:00:46 -06:00
Josh HawkinsandGitHub ef44c18c07 Docs update (#23280)
* stationary car detection troubleshooting tips

* tweak
2026-05-21 09:04:41 -05:00
Josh HawkinsandGitHub 06b059c36a fix admin response cache leak to non-admin users via nginx proxy_cache (#23261)
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
2026-05-20 07:29:37 -05:00
Nicolas MowenandGitHub 26d31300e6 Add metadata for creation time to recording segments and exports (#23239)
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 / Assemble and push default build (push) Has been cancelled
CI / Synaptics Build (push) Has been cancelled
2026-05-18 10:58:10 -05:00
0013555528 Fixes (#23235)
* use stable empty object reference for swr metadata default

* version bump

* Refactor get_min_region_size for dimension normalization

Refactor get_min_region_size to normalize dimensions for smaller models and ensure minimum region size is 320 for larger models.

* reject restricted go2rtc stream sources when added via api

* add env var check function

* fix typing

---------

Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
2026-05-18 10:32:39 -05:00
46 changed files with 1005 additions and 92 deletions
@@ -15,12 +15,24 @@ from frigate.const import (
)
from frigate.ffmpeg_presets import parse_preset_hardware_acceleration_encode
from frigate.util.config import find_config_file, resolve_ffmpeg_path
from frigate.util.services import is_restricted_go2rtc_source
from frigate.util.services import (
is_go2rtc_arbitrary_exec_allowed,
is_restricted_go2rtc_source,
)
sys.path.remove("/opt/frigate")
yaml = YAML()
FRIGATE_ENV_VARS = {k: v for k, v in os.environ.items() if k.startswith("FRIGATE_")}
# read docker secret files as env vars too
if os.path.isdir("/run/secrets"):
for secret_file in os.listdir("/run/secrets"):
if secret_file.startswith("FRIGATE_"):
FRIGATE_ENV_VARS[secret_file] = (
Path(os.path.join("/run/secrets", secret_file)).read_text().strip()
)
config_file = find_config_file()
try:
@@ -100,7 +112,7 @@ for name in list(go2rtc_config.get("streams", {})):
if isinstance(stream, str):
try:
formatted_stream = substitute_frigate_vars(stream)
formatted_stream = stream.format(**FRIGATE_ENV_VARS)
if is_restricted_go2rtc_source(formatted_stream):
print(
f"[ERROR] Stream '{name}' uses a restricted source (echo/expr/exec) which is disabled by default for security. "
@@ -119,7 +131,7 @@ for name in list(go2rtc_config.get("streams", {})):
filtered_streams = []
for i, stream_item in enumerate(stream):
try:
formatted_stream = substitute_frigate_vars(stream_item)
formatted_stream = stream_item.format(**FRIGATE_ENV_VARS)
if is_restricted_go2rtc_source(formatted_stream):
print(
f"[ERROR] Stream '{name}' item {i + 1} uses a restricted source (echo/expr/exec) which is disabled by default for security. "
@@ -143,6 +155,20 @@ for name in list(go2rtc_config.get("streams", {})):
)
del go2rtc_config["streams"][name]
elif isinstance(stream, dict):
# The map form ({"url": ...}) lets go2rtc resolve the source
# recursively, so it is effectively a dynamic way to generate the URL
# for a stream. That can only be backed by an exec source, so it cannot
# be allowed unless arbitrary exec is explicitly enabled. When it is
# enabled, leave the map untouched for go2rtc to resolve.
if not is_go2rtc_arbitrary_exec_allowed():
print(
f"[ERROR] Stream '{name}' uses a dynamic source format which is disabled by default for security. "
f"Set GO2RTC_ALLOW_ARBITRARY_EXEC=true to enable arbitrary exec sources."
)
del go2rtc_config["streams"][name]
continue
# add birdseye restream stream if enabled
if config.get("birdseye", {}).get("restream", False):
birdseye: dict[str, Any] = config.get("birdseye")
+23 -1
View File
@@ -7,6 +7,8 @@ import ConfigTabs from "@site/src/components/ConfigTabs";
import TabItem from "@theme/TabItem";
import NavPath from "@site/src/components/NavPath";
Frigate has two kinds of masks: motion masks and object filter masks. Both are narrow tools for fine-tuning, **not for hiding an area from Frigate**. Masks should be used sparingly; in most cases where users reach for one, a [zone](zones.md) with `required_zones` is the right tool instead. See [Which tool do I need?](#which-tool-do-i-need) and [Common mistakes](#common-mistakes) below if you're new to Frigate's mask behavior.
## Motion masks
Motion masks are used to prevent unwanted types of motion from triggering detection. Try watching the Debug feed (Settings --> Debug) with `Motion Boxes` enabled to see what may be regularly detected as motion. For example, you want to mask out your timestamp, the sky, rooftops, etc. Keep in mind that this mask only prevents motion from being detected and does not prevent objects from being detected if object detection was started due to motion in unmasked areas. Motion is also used during object tracking to refine the object detection area in the next frame. _Over-masking will make it more difficult for objects to be tracked._
@@ -21,7 +23,16 @@ Object filter masks can be used to filter out stubborn false positives in fixed
![object mask](/img/bottom-center-mask.jpg)
## Creating masks
## Which tool do I need?
| What you're trying to do | Recommended tool | How it works |
| ------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Don't get alerts or recordings for activity in an area (e.g., the sidewalk in front of your house) | A [zone](zones.md) combined with `review.alerts.required_zones` (and/or `review.detections.required_zones`) | Frigate keeps detecting and tracking activity in the area, but a review item is only created once the bottom-center of an object's bounding box enters a required zone. |
| Stop a stubborn false positive at a specific fixed spot (e.g., a tree base that keeps being detected as a person) | An **object filter mask** for that object type | Any detection of that object type whose bounding-box bottom-center lands inside the mask is treated as a false positive and discarded. |
| Ignore motion in an area that obviously isn't an object of interest (e.g., the camera timestamp, sky, flags, treetops swaying) | A **motion mask** | Motion inside the mask is ignored when deciding whether to run object detection. Objects can still be detected in a motion masked area if motion elsewhere in the frame triggers detection. |
| Stop tracking an object type altogether on this camera (e.g., you never care about cats) | Remove the object from the camera's [`objects.track`](objects.md) list | Frigate skips this object type entirely on this camera, regardless of where it appears. |
## Using the mask creator
<ConfigTabs>
<TabItem value="ui">
@@ -124,3 +135,14 @@ This is what `required_zones` are for. You should define a zone (remember this i
> Maybe my specific situation just warrants this. I've just been having a hard time understanding the relevance of this information - it seems to be that it's exactly what would be expected when "masking out" an area of ANY image.
That may be the case for you. Frigate will definitely work harder tracking people on the sidewalk to make sure it doesn't miss anyone who steps foot on your stoop. The trade off with the way you have it now is slower recognition of objects and potential misses. That may be acceptable based on your needs. Also, if your resolution is low enough on the detect stream, your regions may already be so big that they grab the entire object anyway.
## Common mistakes
**"I added a motion mask to ignore my driveway/sidewalk."**
A motion mask doesn't hide an area from Frigate. Objects can still be detected and tracked inside a masked area. The mask only stops motion _in that area_ from triggering object detection. If you want activity on the sidewalk to never produce a review item, define a [zone](zones.md) over the area you DO care about (your stoop, your driveway) and add it to `review.alerts.required_zones`. Frigate will still see people on the sidewalk, but it won't create an alert until they cross into the zone.
**"I added an object filter mask because I don't care about cars in my yard."**
Object filter masks are for stubborn false positives at fixed locations, not for filtering whole areas or whole object types. If you only want alerts when a car enters the driveway, use a [zone](zones.md) with `required_zones`. If you don't care about a whole object type on this camera, remove it from [`objects.track`](objects.md).
**"I masked everything except a thin strip on my stoop."**
Heavy masking hurts tracking. Frigate uses motion near a tracked object's previous bounding box to decide where to look in the next frame; with most of the frame masked, an object walking from an unmasked area into a masked one effectively disappears and gets picked up as a "new" object when it reappears. For example: someone walks down your sidewalk, stops under a tree (masked area) to tie their shoe, then continues. Frigate sees that as two separate people and can create two separate review items. Because Frigate needs several consecutive frames above the confidence threshold to commit to a detection, each re-appearance can also delay or miss alerts. Use `required_zones` for "only alert me about this spot" and leave the surrounding area unmasked so tracking stays intact.
+16
View File
@@ -124,3 +124,19 @@ cameras:
width: 1280
height: 720
```
### Why does Frigate keep creating new events for my parked car?
Stationary tracking is designed to _prevent_ this — a parked car should stay one tracked object and not generate new events. If you're getting repeated events for the same car, it's likely that Frigate is losing the tracked object and re-detecting it as a new one.
Open one of the events in Explore → **Tracking Details**. If the detection scores are low (< 70% or so), the model isn't confident the parked car is a car. This is common with the free [COCO-trained](https://cocodataset.org/#explore) object detection models on steep/top-down angles, partially occluded cars, foliage, or low-light footage. When detections fall below `min_score` for too many frames the tracker loses the object, and the next confident frame creates a brand new one.
What helps:
- **Improve the view** — even a small angle change that gets more of the car visible could lift scores enough to stabilize tracking.
- **Use a more accurate model** — switching from `mobiledet` to `yolov9`, or stepping up to a larger variant like `yolov9-s` over `yolov9-t`, can help (at the cost of inference time, and still on the COCO dataset). The biggest gains usually come from fine-tuning a model on images from your own cameras so it learns your specific scene. [Frigate+](https://frigate.video/plus) is a paid option that does this - models are trained on security-camera footage and can be fine-tuned on images you submit from your own setup.
- **Don't set `detect -> stationary -> max_frames` for `car`** — it artificially ends tracking and forces re-detection as a new object. See [Stationary Objects](../configuration/stationary_objects.md).
- **Restrict alerts to the areas you care about** with `required_zones` — see [Zones](../configuration/zones.md#restricting-alerts-and-detections-to-specific-zones). Make sure those zones use the default `loitering_time: 0` unless you specifically want the review item to stay open until the car leaves.
- **Filter impossible locations** with [object filter masks](../configuration/masks.md#object-filter-masks) if cars are being detected on rooftops, treetops, etc.
See [Object Filters](../configuration/object_filters.md) for more on tuning `min_score` and `threshold` — note that raising them too high will make this exact problem worse.
+14
View File
@@ -7393,6 +7393,13 @@ components:
required:
- value
title: CameraSetBody
ChaptersEnum:
type: string
enum:
- none
- recording_segments
- review_items
title: ChaptersEnum
ChatCompletionRequest:
properties:
messages:
@@ -8119,6 +8126,13 @@ components:
- type: 'null'
title: Export case ID
description: ID of the export case to assign this export to
chapters:
anyOf:
- $ref: '#/components/schemas/ChaptersEnum'
- type: 'null'
title: Chapter mode
description: Optional chapter metadata to embed in the export. When
omitted, the camera's configured export chapter mode is used.
type: object
title: ExportRecordingsBody
ExportRecordingsCustomBody:
+13
View File
@@ -147,6 +147,19 @@ def go2rtc_camera_stream(request: Request, stream_name: str):
)
def go2rtc_add_stream(request: Request, stream_name: str, src: str = ""):
"""Add or update a go2rtc stream configuration."""
if src and is_restricted_go2rtc_source(src):
logger.warning(
"Rejected go2rtc stream '%s' with restricted source type (echo/expr/exec)",
stream_name,
)
return JSONResponse(
content={
"success": False,
"message": "Restricted stream source type",
},
status_code=400,
)
try:
params = {"name": stream_name}
if src:
@@ -3,7 +3,10 @@ from typing import Optional, Union
from pydantic import BaseModel, Field
from pydantic.json_schema import SkipJsonSchema
from frigate.record.export import PlaybackSourceEnum
from frigate.record.export import (
ChaptersEnum,
PlaybackSourceEnum,
)
class ExportRecordingsBody(BaseModel):
@@ -18,6 +21,14 @@ class ExportRecordingsBody(BaseModel):
max_length=30,
description="ID of the export case to assign this export to",
)
chapters: Optional[ChaptersEnum] = Field(
default=None,
title="Chapter mode",
description=(
"Optional chapter metadata to embed in the export. When omitted, "
"the camera's configured export chapter mode is used."
),
)
class ExportRecordingsCustomBody(BaseModel):
+24
View File
@@ -68,6 +68,7 @@ from frigate.jobs.export import (
from frigate.models import Export, ExportCase, Previews, Recordings
from frigate.record.export import (
DEFAULT_TIME_LAPSE_FFMPEG_ARGS,
ChaptersEnum,
PlaybackSourceEnum,
validate_ffmpeg_args,
)
@@ -128,6 +129,15 @@ def _validate_export_case(export_case_id: Optional[str]) -> Optional[JSONRespons
def _sanitize_existing_image(
image_path: Optional[str],
) -> tuple[Optional[str], Optional[JSONResponse]]:
# sanitize_filepath normalizes "\" to "/" but leaves ".." intact, so a path
# like "clips\..\..\etc/passwd" passes the CLIPS_DIR prefix check yet still
# escapes the directory once resolved. A valid snapshot path never uses "..".
if image_path and ".." in image_path:
return None, JSONResponse(
content={"success": False, "message": "Invalid image path"},
status_code=400,
)
existing_image = sanitize_filepath(image_path) if image_path else None
if existing_image and not existing_image.startswith(CLIPS_DIR):
@@ -254,6 +264,7 @@ def _build_export_job(
ffmpeg_input_args: Optional[str] = None,
ffmpeg_output_args: Optional[str] = None,
cpu_fallback: bool = False,
chapters: Optional[ChaptersEnum] = None,
) -> ExportJob:
return ExportJob(
id=_generate_export_id(camera_name),
@@ -267,6 +278,7 @@ def _build_export_job(
ffmpeg_input_args=ffmpeg_input_args,
ffmpeg_output_args=ffmpeg_output_args,
cpu_fallback=cpu_fallback,
chapters=chapters,
)
@@ -725,6 +737,9 @@ def export_recordings_batch(
sanitized_images[index],
PlaybackSourceEnum.recordings,
export_case_id,
chapters=request.app.frigate_config.cameras[
item.camera
].record.export.chapters,
)
try:
start_export_job(request.app.frigate_config, export_job)
@@ -803,6 +818,14 @@ def export_recording(
export_case_id = body.export_case_id
# a chapters value in the request body overrides the camera's export config
camera_config = request.app.frigate_config.cameras[camera_name]
chapters = (
body.chapters
if body.chapters is not None
else camera_config.record.export.chapters
)
# Attaching to an existing case requires admin. Single-export for
# cameras the user can access is otherwise non-admin; we only gate
# the case-attachment side effect.
@@ -839,6 +862,7 @@ def export_recording(
existing_image,
playback_source,
export_case_id,
chapters=chapters,
)
try:
start_export_job(request.app.frigate_config, export_job)
+32 -13
View File
@@ -60,6 +60,19 @@ logger = logging.getLogger(__name__)
router = APIRouter(tags=[Tags.media])
def _resolve_cache_age(max_cache_age: int) -> int:
"""Return max_cache_age as an int.
When a media handler is invoked directly by another handler instead of
through its route, FastAPI doesn't resolve the Query() default and
max_cache_age arrives as the Query object; fall back to its int default.
"""
if isinstance(max_cache_age, int):
return max_cache_age
return max_cache_age.default
@router.get("/{camera_name}", dependencies=[Depends(require_camera_access)])
async def mjpeg_feed(
request: Request,
@@ -413,7 +426,9 @@ async def submit_recording_snapshot_to_plus(
)
nd = cv2.imdecode(np.frombuffer(image_data, dtype=np.int8), cv2.IMREAD_COLOR)
request.app.frigate_config.plus_api.upload_image(nd, camera_name)
await asyncio.to_thread(
request.app.frigate_config.plus_api.upload_image, nd, camera_name
)
return JSONResponse(
content={
@@ -936,7 +951,7 @@ async def event_thumbnail(
thumbnail_bytes,
media_type=extension.get_mime_type(),
headers={
"Cache-Control": f"private, max-age={max_cache_age}"
"Cache-Control": f"private, max-age={_resolve_cache_age(max_cache_age)}"
if event_complete
else "no-store",
},
@@ -1270,14 +1285,14 @@ async def event_preview(request: Request, event_id: str):
end_ts = start_ts + (
min(event.end_time - event.start_time, 20) if event.end_time else 20
)
return preview_gif(request, event.camera, start_ts, end_ts)
return await preview_gif(request, event.camera, start_ts, end_ts)
@router.get(
"/{camera_name}/start/{start_ts}/end/{end_ts}/preview.gif",
dependencies=[Depends(require_camera_access)],
)
def preview_gif(
async def preview_gif(
request: Request,
camera_name: str,
start_ts: float,
@@ -1340,7 +1355,8 @@ def preview_gif(
"-",
]
process = sp.run(
process = await asyncio.to_thread(
sp.run,
ffmpeg_cmd,
capture_output=True,
)
@@ -1419,7 +1435,8 @@ def preview_gif(
"-",
]
process = sp.run(
process = await asyncio.to_thread(
sp.run,
ffmpeg_cmd,
input=str.encode("\n".join(selected_previews)),
capture_output=True,
@@ -1438,7 +1455,7 @@ def preview_gif(
gif_bytes,
media_type="image/gif",
headers={
"Cache-Control": f"private, max-age={max_cache_age}",
"Cache-Control": f"private, max-age={_resolve_cache_age(max_cache_age)}",
"Content-Type": "image/gif",
},
)
@@ -1448,7 +1465,7 @@ def preview_gif(
"/{camera_name}/start/{start_ts}/end/{end_ts}/preview.mp4",
dependencies=[Depends(require_camera_access)],
)
def preview_mp4(
async def preview_mp4(
request: Request,
camera_name: str,
start_ts: float,
@@ -1528,7 +1545,8 @@ def preview_mp4(
path,
]
process = sp.run(
process = await asyncio.to_thread(
sp.run,
ffmpeg_cmd,
capture_output=True,
)
@@ -1604,7 +1622,8 @@ def preview_mp4(
path,
]
process = sp.run(
process = await asyncio.to_thread(
sp.run,
ffmpeg_cmd,
input=str.encode("\n".join(selected_previews)),
capture_output=True,
@@ -1619,7 +1638,7 @@ def preview_mp4(
headers = {
"Content-Description": "File Transfer",
"Cache-Control": f"private, max-age={max_cache_age}",
"Cache-Control": f"private, max-age={_resolve_cache_age(max_cache_age)}",
"Content-Type": "video/mp4",
"Content-Length": str(os.path.getsize(path)),
# nginx: https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ignore_headers
@@ -1657,9 +1676,9 @@ async def review_preview(
)
if format == "gif":
return preview_gif(request, review.camera, start_ts, end_ts)
return await preview_gif(request, review.camera, start_ts, end_ts)
else:
return preview_mp4(request, review.camera, start_ts, end_ts)
return await preview_mp4(request, review.camera, start_ts, end_ts)
@router.get(
+39 -7
View File
@@ -72,11 +72,16 @@ _WS_VIEWER_TOPICS = frozenset(
}
)
# Camera-scoped command topics a camera-authorized (non-admin) user may send.
_WS_CAMERA_COMMAND_TOPICS = frozenset({"ptz"})
def _check_ws_authorization(
topic: str,
role_header: str | None,
separator: str,
roles_config: dict[str, list[str]] | None = None,
camera_names: set[str] | None = None,
) -> bool:
"""Check if a WebSocket message is authorized.
@@ -84,6 +89,10 @@ def _check_ws_authorization(
topic: The message topic.
role_header: The HTTP_REMOTE_ROLE header value, or None.
separator: The role separator character from proxy config.
roles_config: The auth.roles mapping (role -> allowed cameras), used to
authorize camera-scoped commands for non-admin users.
camera_names: All configured camera names, used to resolve a role's
allowed cameras.
Returns:
True if authorized, False if blocked.
@@ -93,16 +102,33 @@ def _check_ws_authorization(
return False
# No role header: default to viewer (fail-closed)
if role_header is None:
return topic in _WS_VIEWER_TOPICS
roles = [r.strip() for r in role_header.split(separator)] if role_header else []
# Check if any role is admin
roles = [r.strip() for r in role_header.split(separator)]
# Admin can send anything
if "admin" in roles:
return True
# Non-admin: only viewer topics allowed
return topic in _WS_VIEWER_TOPICS
# Read-only topics any authenticated user can send
if topic in _WS_VIEWER_TOPICS:
return True
# Camera-scoped command like "<camera>/ptz": allow when the user's role(s)
# grant access to that camera.
parts = topic.split("/")
if (
roles_config is not None
and len(parts) == 2
and parts[1] in _WS_CAMERA_COMMAND_TOPICS
):
allowed: set[str] = set()
# No role header maps to the default viewer role (e.g. proxy-only setups)
for role in roles or ["viewer"]:
allowed.update(
User.get_allowed_cameras(role, roles_config, camera_names or set())
)
return parts[0] in allowed
return False
# ---- Outbound filtering ---------------------------------------------------
@@ -449,6 +475,8 @@ class WebSocketClient(Communicator):
class _WebSocketHandler(WebSocket):
receiver = self._dispatcher
role_separator = self.config.proxy.separator or ","
roles_config = self.config.auth.roles
camera_names = set(self.config.cameras.keys())
def received_message(self, message: WebSocket.received_message) -> None: # type: ignore[name-defined]
try:
@@ -470,7 +498,11 @@ class WebSocketClient(Communicator):
self.environ.get("HTTP_REMOTE_ROLE") if self.environ else None
)
if self.environ is not None and not _check_ws_authorization(
topic, role_header, self.role_separator
topic,
role_header,
self.role_separator,
self.roles_config,
self.camera_names,
):
logger.warning(
"Blocked unauthorized WebSocket message: topic=%s, role=%s",
+11
View File
@@ -9,6 +9,7 @@ from frigate.review.types import SeverityEnum
from ..base import FrigateBaseModel
__all__ = [
"ChaptersEnum",
"RecordConfig",
"RecordExportConfig",
"RecordPreviewConfig",
@@ -86,6 +87,12 @@ class RecordPreviewConfig(FrigateBaseModel):
)
class ChaptersEnum(str, Enum):
none = "none"
recording_segments = "recording_segments"
review_items = "review_items"
class RecordExportConfig(FrigateBaseModel):
hwaccel_args: Union[str, list[str]] = Field(
default="auto",
@@ -98,6 +105,10 @@ class RecordExportConfig(FrigateBaseModel):
title="Maximum concurrent exports",
description="Maximum number of export jobs to process at the same time.",
)
chapters: ChaptersEnum = Field(
default=ChaptersEnum.review_items,
title="Chapter metadata to embed in exported recordings",
)
class RecordConfig(FrigateBaseModel):
+3
View File
@@ -13,6 +13,7 @@ from peewee import DoesNotExist
from frigate.comms.inter_process import InterProcessRequestor
from frigate.config import FrigateConfig
from frigate.config.camera.record import ChaptersEnum
from frigate.const import UPDATE_JOB_STATE
from frigate.jobs.job import Job
from frigate.models import Export
@@ -55,6 +56,7 @@ class ExportJob(Job):
ffmpeg_input_args: Optional[str] = None
ffmpeg_output_args: Optional[str] = None
cpu_fallback: bool = False
chapters: Optional[ChaptersEnum] = None
current_step: str = "queued"
progress_percent: float = 0.0
@@ -343,6 +345,7 @@ class ExportJobManager:
job.ffmpeg_input_args,
job.ffmpeg_output_args,
job.cpu_fallback,
job.chapters,
on_progress=self._make_progress_callback(job),
)
+113 -5
View File
@@ -17,6 +17,7 @@ import pytz # type: ignore[import-untyped]
from peewee import DoesNotExist
from frigate.config import FfmpegConfig, FrigateConfig
from frigate.config.camera.record import ChaptersEnum
from frigate.const import (
CACHE_DIR,
CLIPS_DIR,
@@ -217,6 +218,7 @@ class RecordingExporter(threading.Thread):
ffmpeg_input_args: Optional[str] = None,
ffmpeg_output_args: Optional[str] = None,
cpu_fallback: bool = False,
chapters: Optional[ChaptersEnum] = None,
on_progress: Optional[Callable[[str, float], None]] = None,
) -> None:
super().__init__()
@@ -232,6 +234,7 @@ class RecordingExporter(threading.Thread):
self.ffmpeg_input_args = ffmpeg_input_args
self.ffmpeg_output_args = ffmpeg_output_args
self.cpu_fallback = cpu_fallback
self.chapters = chapters
self.on_progress = on_progress
# ensure export thumb dir
@@ -509,6 +512,74 @@ class RecordingExporter(threading.Thread):
return meta_path
def _build_recording_segment_chapter_metadata_file(
self, recordings: list
) -> Optional[str]:
"""Write an FFmpeg metadata file with one chapter per recording segment.
Each chapter's title is the segment's wallclock start time in
strict ISO 8601 form so a viewer can map any point in the
export's playback timeline back to real-world time without
OCR-ing a burnt-in timestamp. Chapter offsets are computed in
*output time*: the VOD endpoint concatenates recording clips
back-to-back, so wall-clock gaps between recordings collapse in
the produced video. Returns ``None`` when there are no
recordings or every segment is empty after clipping.
"""
if not recordings:
return None
tz_name = self.config.ui.timezone
tz: Optional[datetime.tzinfo] = None
if tz_name:
try:
tz = pytz.timezone(tz_name)
except pytz.UnknownTimeZoneError:
tz = None
if tz is None:
tz = datetime.timezone.utc
chapter_blocks: list[str] = []
output_offset_ms = 0
for rec in recordings:
clipped_start = max(float(rec.start_time), float(self.start_time))
clipped_end = min(float(rec.end_time), float(self.end_time))
if clipped_end <= clipped_start:
continue
duration_ms = int(round((clipped_end - clipped_start) * 1000))
if duration_ms <= 0:
continue
title = datetime.datetime.fromtimestamp(clipped_start, tz=tz).isoformat(
timespec="seconds"
)
chapter_blocks.append(
"[CHAPTER]\n"
"TIMEBASE=1/1000\n"
f"START={output_offset_ms}\n"
f"END={output_offset_ms + duration_ms}\n"
f"title={title}"
)
output_offset_ms += duration_ms
if not chapter_blocks:
return None
meta_path = self._chapter_metadata_path()
try:
with open(meta_path, "w", encoding="utf-8") as f:
f.write(";FFMETADATA1\n")
f.write("\n".join(chapter_blocks))
f.write("\n")
except OSError:
logger.exception(
"Failed to write chapter metadata file for export %s", self.export_id
)
return None
return meta_path
def save_thumbnail(self, id: str) -> str:
thumb_path = os.path.join(CLIPS_DIR, f"export/{id}.webp")
@@ -672,7 +743,18 @@ class RecordingExporter(threading.Thread):
)
).split(" ")
else:
chapters_path = self._build_chapter_metadata_file(recordings)
# Realtime/stream-copy export. Embed chapter metadata according to
# the camera's configured chapter mode: per-recording-segment
# timestamps or per-review-item titles.
if self.chapters == ChaptersEnum.recording_segments:
chapters_path = self._build_recording_segment_chapter_metadata_file(
recordings
)
elif self.chapters == ChaptersEnum.review_items:
chapters_path = self._build_chapter_metadata_file(recordings)
else:
chapters_path = None
chapter_args = (
f" -i {chapters_path} -map 0 -dn -map_metadata 1"
if chapters_path
@@ -684,7 +766,19 @@ class RecordingExporter(threading.Thread):
# add metadata
title = f"Frigate Recording for {self.camera}, {self.get_datetime_from_timestamp(self.start_time)} - {self.get_datetime_from_timestamp(self.end_time)}"
ffmpeg_cmd.extend(["-metadata", f"title={title}"])
creation_time = datetime.datetime.fromtimestamp(
self.start_time, tz=datetime.timezone.utc
).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
ffmpeg_cmd.extend(
[
"-metadata",
f"title={title}",
"-metadata",
f"creation_time={creation_time}",
"-metadata",
f"comment=Camera: {self.camera}",
]
)
ffmpeg_cmd.append(video_path)
@@ -770,18 +864,32 @@ class RecordingExporter(threading.Thread):
self.config.ffmpeg.ffmpeg_path,
hwaccel_args,
f"{self.ffmpeg_input_args} {TIMELAPSE_DATA_INPUT_ARGS} {ffmpeg_input}".strip(),
f"{self.ffmpeg_output_args} -movflags +faststart {video_path}".strip(),
f"{self.ffmpeg_output_args} -movflags +faststart".strip(),
EncodeTypeEnum.timelapse,
)
).split(" ")
else:
ffmpeg_cmd = (
f"{self.config.ffmpeg.ffmpeg_path} -hide_banner {ffmpeg_input} {codec} -movflags +faststart {video_path}"
f"{self.config.ffmpeg.ffmpeg_path} -hide_banner {ffmpeg_input} {codec} -movflags +faststart"
).split(" ")
# add metadata
title = f"Frigate Preview for {self.camera}, {self.get_datetime_from_timestamp(self.start_time)} - {self.get_datetime_from_timestamp(self.end_time)}"
ffmpeg_cmd.extend(["-metadata", f"title={title}"])
creation_time = datetime.datetime.fromtimestamp(
self.start_time, tz=datetime.timezone.utc
).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
ffmpeg_cmd.extend(
[
"-metadata",
f"title={title}",
"-metadata",
f"creation_time={creation_time}",
"-metadata",
f"comment=Camera: {self.camera}",
]
)
ffmpeg_cmd.append(video_path)
return ffmpeg_cmd, playlist_lines
+2
View File
@@ -650,6 +650,8 @@ class RecordingMaintainer(threading.Thread):
"copy",
"-movflags",
"+faststart",
"-metadata",
f"creation_time={start_time.strftime('%Y-%m-%dT%H:%M:%S.%fZ')}",
file_path,
stderr=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.DEVNULL,
+128
View File
@@ -11,6 +11,16 @@ class TestCheckWsAuthorization(unittest.TestCase):
DEFAULT_SEPARATOR = ","
# admin/viewer are reserved and always map to all cameras (empty list);
# custom roles map to a specific set of cameras.
ROLES_CONFIG = {
"admin": [],
"viewer": [],
"yard": ["front_door", "backyard"],
"garage_only": ["garage"],
}
CAMERA_NAMES = {"front_door", "backyard", "garage"}
# --- IPC topic blocking (unconditional, regardless of role) ---
def test_ipc_topic_blocked_for_admin(self):
@@ -161,6 +171,124 @@ class TestCheckWsAuthorization(unittest.TestCase):
_check_ws_authorization("onConnect", None, self.DEFAULT_SEPARATOR)
)
# --- Camera-scoped PTZ access (non-admin with camera access) ---
def test_viewer_can_ptz_camera_with_access(self):
# viewer maps to all cameras, so PTZ is allowed
self.assertTrue(
_check_ws_authorization(
"front_door/ptz",
"viewer",
self.DEFAULT_SEPARATOR,
self.ROLES_CONFIG,
self.CAMERA_NAMES,
)
)
def test_custom_role_can_ptz_assigned_camera(self):
self.assertTrue(
_check_ws_authorization(
"front_door/ptz",
"yard",
self.DEFAULT_SEPARATOR,
self.ROLES_CONFIG,
self.CAMERA_NAMES,
)
)
def test_custom_role_blocked_from_ptz_unassigned_camera(self):
self.assertFalse(
_check_ws_authorization(
"garage/ptz",
"yard",
self.DEFAULT_SEPARATOR,
self.ROLES_CONFIG,
self.CAMERA_NAMES,
)
)
def test_multiple_roles_union_camera_access_for_ptz(self):
# "yard" covers front_door/backyard, "garage_only" covers garage
self.assertTrue(
_check_ws_authorization(
"garage/ptz",
"yard,garage_only",
self.DEFAULT_SEPARATOR,
self.ROLES_CONFIG,
self.CAMERA_NAMES,
)
)
def test_unknown_role_blocked_from_ptz(self):
self.assertFalse(
_check_ws_authorization(
"front_door/ptz",
"nonexistent",
self.DEFAULT_SEPARATOR,
self.ROLES_CONFIG,
self.CAMERA_NAMES,
)
)
def test_no_role_header_treated_as_viewer_for_ptz(self):
# proxy-only / auth-disabled setups default to the viewer role
self.assertTrue(
_check_ws_authorization(
"front_door/ptz",
None,
self.DEFAULT_SEPARATOR,
self.ROLES_CONFIG,
self.CAMERA_NAMES,
)
)
def test_camera_access_does_not_grant_set_commands(self):
# camera access enables PTZ only, not config-changing "set" commands
self.assertFalse(
_check_ws_authorization(
"front_door/detect/set",
"yard",
self.DEFAULT_SEPARATOR,
self.ROLES_CONFIG,
self.CAMERA_NAMES,
)
)
def test_ptz_autotracker_stays_admin_only(self):
# ptz_autotracker is a config toggle, not a live-view action
self.assertFalse(
_check_ws_authorization(
"front_door/ptz_autotracker/set",
"viewer",
self.DEFAULT_SEPARATOR,
self.ROLES_CONFIG,
self.CAMERA_NAMES,
)
)
def test_admin_can_ptz_any_camera_with_config(self):
self.assertTrue(
_check_ws_authorization(
"garage/ptz",
"admin",
self.DEFAULT_SEPARATOR,
self.ROLES_CONFIG,
self.CAMERA_NAMES,
)
)
def test_ipc_topic_still_blocked_with_camera_access(self):
# IPC topics are blocked unconditionally, even with camera access
self.assertFalse(
_check_ws_authorization(
UPDATE_CAMERA_ACTIVITY,
"viewer",
self.DEFAULT_SEPARATOR,
self.ROLES_CONFIG,
self.CAMERA_NAMES,
)
)
if __name__ == "__main__":
unittest.main()
+2 -2
View File
@@ -790,7 +790,7 @@ def get_hailo_temps() -> dict[str, float]:
return temps
def _go2rtc_arbitrary_exec_allowed() -> bool:
def is_go2rtc_arbitrary_exec_allowed() -> bool:
"""Read the GO2RTC_ALLOW_ARBITRARY_EXEC override from env, docker
secrets, or the Home Assistant add-on options file."""
raw: Optional[str] = None
@@ -822,7 +822,7 @@ def is_restricted_go2rtc_source(stream_source: str) -> bool:
and the GO2RTC_ALLOW_ARBITRARY_EXEC override is not set."""
if not stream_source.strip().startswith(("echo:", "expr:", "exec:")):
return False
return not _go2rtc_arbitrary_exec_allowed()
return not is_go2rtc_arbitrary_exec_allowed()
def ffprobe_stream(ffmpeg, path: str, detailed: bool = False) -> sp.CompletedProcess:
+13 -1
View File
@@ -149,5 +149,17 @@
"cluck": "قرقرة",
"cock_a_doodle_doo": "كوكو-كو-كوووووو",
"turkey": "ديك رومى",
"gobble": "كركرة"
"gobble": "كركرة",
"tearing": "يمزق",
"ping": "طنّة",
"clang": "صوت رنين",
"squeal": "يصرخ",
"creak": "صرير",
"sizzle": "صوت الأزيز",
"clicking": "النقر",
"clickety_clack": "طقطقة",
"rumble": "الحلبة",
"skateboard": "لوح تزلج",
"echo": "صدى الصوت",
"noise": "ازعاج"
}
+2 -1
View File
@@ -22,5 +22,6 @@
"mouse": "فأر",
"keyboard": "لوحة المفاتيح",
"goat": "معزة",
"sheep": "غنم"
"sheep": "غنم",
"skateboard": "لوح تزلج"
}
@@ -24,6 +24,9 @@
},
"state": {
"submitted": "Enviat"
},
"toast": {
"error": "No s'ha pogut enviar a Frigate+. Si us plau, comproveu la vostra connexió de xarxa i torneu-ho a provar."
}
}
},
+13 -2
View File
@@ -180,7 +180,7 @@
"rats": "Rotter",
"patter": "Dråbelyd",
"insect": "Insekt",
"cricket": "Cricket",
"cricket": "Fårekylling",
"guitar": "Guitar",
"electric_guitar": "Elektrisk Guitar",
"bass_guitar": "Basguitar",
@@ -195,5 +195,16 @@
"rimshot": "Kantslag",
"drum_roll": "Trommehvirvel",
"bass_drum": "Stortromme",
"techno": "Techno"
"techno": "Techno",
"mosquito": "Myg",
"fly": "Flue",
"buzz": "Summen",
"frog": "Frø",
"croak": "Kvæk",
"snake": "Slange",
"rattle": "Klapren",
"whale_vocalization": "Hvallyde",
"music": "Musik",
"musical_instrument": "Musikinstrument",
"plucked_string_instrument": "Strengeinstrument"
}
+37 -13
View File
@@ -24,7 +24,7 @@
"am": "am",
"year_one": "{{time}} år",
"year_other": "{{time}} år",
"mo": "{{time}}må",
"mo": "{{time}}md.",
"month_one": "{{time}} måned",
"month_other": "{{time}} måneder",
"d": "{{time}}d",
@@ -121,18 +121,18 @@
"back": "Tilbage",
"history": "Historik",
"fullscreen": "Fuldskærm",
"exitFullscreen": "Afslut Fludskærm",
"pictureInPicture": "Billede i Billede",
"twoWayTalk": "2 vejs samtale",
"cameraAudio": "Kamera Lyd",
"on": "ON",
"off": "OFF",
"exitFullscreen": "Afslut Fuldskærm",
"pictureInPicture": "Billede i billede",
"twoWayTalk": "Samtale",
"cameraAudio": "Kameralyd",
"on": "Til",
"off": "Fra",
"edit": "Rediger",
"copyCoordinates": "Kopier koordinater",
"delete": "Slet",
"yes": "Ja",
"no": "Nej",
"download": "Download",
"download": "Hent",
"info": "Info",
"suspended": "Sat på pause",
"unsuspended": "Genoptag",
@@ -141,7 +141,19 @@
"export": "Eksporter",
"deleteNow": "Slet nu",
"next": "Næste",
"continue": "Fortsæt"
"continue": "Fortsæt",
"add": "Tilføj",
"applying": "Anvender…",
"undo": "Annuler",
"copiedToClipboard": "Kopieret til udklipsholder",
"modified": "Ændret",
"overridden": "Overskrevet",
"resetToGlobal": "Gendan til global",
"resetToDefault": "Gendan standard",
"saveAll": "Gem alle",
"savingAll": "Gemmer alle…",
"undoAll": "Fortryd alle",
"retry": "Prøv igen"
},
"menu": {
"system": "System",
@@ -194,7 +206,9 @@
"gl": "Galego (Galisisk)",
"id": "Bahasa Indonesia (Indonesisk)",
"ur": "اردو (Urdu)",
"hr": "Hrvatski (Kroatisk)"
"hr": "Hrvatski (Kroatisk)",
"zhHant": "繁體中文 (Traditionel Kinesisk)",
"bs": "Bosanski (Bosnisk)"
},
"appearance": "Udseende",
"darkMode": {
@@ -243,7 +257,11 @@
"logout": "Log ud",
"setPassword": "Vælg kodeord"
},
"classification": "Kategorisering"
"classification": "Kategorisering",
"profiles": "Profiler",
"actions": "Handlinger",
"features": "Funktioner",
"chat": "Chat"
},
"toast": {
"copyUrlToClipboard": "Kopieret URL til udklipsholder.",
@@ -252,13 +270,14 @@
"error": {
"title": "Ændringer kunne ikke gemmes: {{errorMessage}}",
"noMessage": "Kunne ikke gemme konfigurationsændringer"
}
},
"success": "Gemte ændringerne i konfigurationen."
}
},
"role": {
"title": "Rolle",
"admin": "Admin",
"viewer": "Viewer",
"viewer": "Seer",
"desc": "Admins har fuld adgang til Frigate UI. Viewers er begrænset til at se kameraer, gennemse items, og historik i UI."
},
"pagination": {
@@ -296,5 +315,10 @@
},
"information": {
"pixels": "{{area}}px"
},
"no_items": "Intet fundet",
"validation_errors": "Valideringsfejl",
"credentialField": {
"savedPlaceholder": "Gemt - efterlad blank for at bevare nuværende"
}
}
+7 -3
View File
@@ -31,7 +31,7 @@
"setting": {
"label": "Kamera Streaming Indstillinger",
"title": "{{cameraName}} Streaming Indstillinger",
"desc": "Skift de live streaming muligheder for denne kameragruppes dashboard. <em> Disse indstillinger er enheds- og browserspecifikke.</em>",
"desc": "Skift indstillingerne for direkte visning af denne kameragruppes dashboard. <em> Disse indstillinger er enheds- og browserspecifikke.</em>",
"audioIsAvailable": "Lyd er tilgængelig for denne stream",
"audioIsUnavailable": "Lyd er ikke tilgængelig for denne strøm",
"audio": {
@@ -67,7 +67,10 @@
"desc": "Aktivér kun denne mulighed, hvis kameraets live stream viser farve artefakter og har en diagonal linje på højre side af billedet."
}
}
}
},
"showAll": "Vis alle kameragrupper",
"showLess": "Vis mindre",
"editGroups": "Rediger kameragrupper"
},
"debug": {
"options": {
@@ -81,6 +84,7 @@
"zones": "Zoner",
"mask": "Maske",
"motion": "Bevægelse",
"regions": "Regioner"
"regions": "Regioner",
"paths": "Stier"
}
}
+39 -1
View File
@@ -1,6 +1,6 @@
{
"restart": {
"title": "Er du sikker på at du vil genstarte Frigate?",
"title": "Er du sikker på, at du vil genstarte Frigate?",
"button": "Genstart",
"restarting": {
"title": "Frigate genstarter",
@@ -21,8 +21,46 @@
"ask_a": "Er dette objekt et <code>{{label}}</code>?",
"ask_an": "Er dette objekt en <code>{{label}}</code>?",
"ask_full": "Er dette objekt en <code>{{untranslatedLabel}}</code> ({{translatedLabel}})?"
},
"state": {
"submitted": "Indsendt"
},
"toast": {
"error": "Fejl under indsendelse til Frigate+. Kontroller venligst din netværksforbindelse og prøv igen."
}
}
},
"video": {
"viewInHistory": "Se i historik"
}
},
"export": {
"time": {
"fromTimeline": "Vælg fra tidslinje",
"lastHour_one": "Sidste time",
"lastHour_other": "Sidste {{count}} timer",
"custom": "Tidsinterval",
"start": {
"title": "Starttidspunkt",
"label": "Vælg starttidspunkt"
},
"end": {
"title": "Sluttidspunkt",
"label": "Vælg sluttidspunkt"
}
},
"name": {
"placeholder": "Navngiv eksport"
},
"case": {
"newCaseOption": "Opret ny sag",
"newCaseNamePlaceholder": "Nyt sagsnavn",
"newCaseDescriptionPlaceholder": "Sagsbeskrivelse",
"label": "Sag",
"nonAdminHelp": "En ny sag oprettes til disse eksporter.",
"placeholder": "Vælg en sag"
},
"select": "Vælg",
"export": "Eksporter"
}
}
+2 -1
View File
@@ -14,6 +14,7 @@
"notifications": "Notifikations indstillinger - Frigate"
},
"menu": {
"ui": "Brugergrænseflade"
"ui": "Brugergrænseflade",
"profiles": "Profiler"
}
}
@@ -493,6 +493,9 @@
"max_concurrent": {
"label": "Maximum concurrent exports",
"description": "Maximum number of export jobs to process at the same time."
},
"chapters": {
"label": "Chapter metadata to embed in exported recordings"
}
},
"preview": {
+3
View File
@@ -1035,6 +1035,9 @@
"max_concurrent": {
"label": "Maximum concurrent exports",
"description": "Maximum number of export jobs to process at the same time."
},
"chapters": {
"label": "Chapter metadata to embed in exported recordings"
}
},
"preview": {
+7 -1
View File
@@ -427,5 +427,11 @@
"shatter": "Összetörés",
"field_recording": "Helyszíni felvétel",
"noise": "Zaj",
"liquid": "Folyadék"
"liquid": "Folyadék",
"splash": "Csobbanás",
"slosh": "Löttyenés",
"trickle": "Csörgedezés",
"gush": "Folyás",
"stir": "Kavarás",
"thump": "Puffanás"
}
+2 -1
View File
@@ -14,5 +14,6 @@
"attachment_chip_remove": "Melléklet eltávolítása",
"open_in_explore": "Megnyitás Böngészőben",
"attachment_picker_paste_label": "Vagy illeszd be az esemény ID-t",
"attachment_picker_attach": "Melléklet"
"attachment_picker_attach": "Melléklet",
"arguments": "Argumentumok:"
}
+2 -1
View File
@@ -497,5 +497,6 @@
"echo": "Eco",
"mains_hum": "Zumbido Elétrico",
"sidetone": "Retorno de Voz",
"throbbing": "Latejante"
"throbbing": "Latejante",
"sodeling": "Soldagem"
}
+2 -1
View File
@@ -210,7 +210,8 @@
"id": "Bahasa Indonesia (Indonésio)",
"ur": "اردو (Urdu)",
"hr": "Hrvatski (Croata)",
"bs": "Bosanski (Bósnio)"
"bs": "Bosanski (Bósnio)",
"zhHant": "Chinês Tradicional"
},
"systemLogs": "Logs de sistema",
"settings": "Configurações",
@@ -68,7 +68,10 @@
}
},
"birdseye": "Visão Panorâmica"
}
},
"showAll": "Exibir todos os grupos de câmeras",
"showLess": "Mostrar menos",
"editGroups": "Editar grupos de câmeras"
},
"debug": {
"options": {
@@ -24,6 +24,9 @@
},
"state": {
"submitted": "Enviado"
},
"toast": {
"error": "Falha ao enviar para o Frigate+. Verifique sua conexão de rede e tente novamente."
}
}
},
@@ -56,15 +59,24 @@
"toast": {
"success": "Exportação iniciada com sucesso. Veja o arquivo na tela exportar.",
"error": {
"failed": "Falha em iniciar exportação: {{error}}",
"failed": "Falha ao iniciar exportação: {{error}}",
"endTimeMustAfterStartTime": "Tempo de finalização deve ser após tempo de início",
"noVaildTimeSelected": "Nenhuma faixa de tempo válida selecionada"
},
"view": "Ver"
"view": "Ver",
"batchQueuedSuccess_one": "1 exportação na fila. Abrindo o caso agora.",
"batchQueuedSuccess_many": "{{count}} exportações colocadas na fila. Abrindo o caso agora.",
"batchQueuedSuccess_other": "",
"batchQueuedPartial": "{{successful}} de {{total}} exportações na fila. Câmeras com falha: {{failedCameras}}",
"batchQueueFailed": "Falha ao enfileirar {{total}} exportações. Câmeras com falha: {{failedCameras}}",
"batchPartial": "Iniciadas {{successful}} de {{total}} exportações. Câmeras com falha: {{failedCameras}}",
"batchFailed": "Falha ao iniciar {{total}} exportações. Câmeras com falha: {{failedCameras}}"
},
"fromTimeline": {
"saveExport": "Salvar Exportação",
"previewExport": "Pré-Visualizar Exportação"
"previewExport": "Pré-Visualizar Exportação",
"queueingExport": "Enfileirando exportação...",
"useThisRange": "Use esta faixa"
},
"case": {
"label": "Caso",
@@ -94,7 +106,14 @@
"queueingButton": "Exportações na fila...",
"exportButton_one": "Exportar câmera",
"exportButton_many": "Exportar {{count}} câmeras",
"exportButton_other": "Exportar {{count}} câmeras"
"exportButton_other": "Exportar {{count}} câmeras",
"searchOrSelectGroup": "Procurar, ou selecionar um grupo de câmeras...",
"selectAll": "Selecionar todas as câmeras",
"clearSelection": "Limpar seleção",
"selectWithActivity": "Câmeras com rastreamento de objetos",
"selectGroup": "Selecione grupo",
"noMatchingCameras": "Nenhuma câmera corresponde à sua pesquisa",
"selectedCount": "{{selected}} / {{total}} selecionados"
},
"multi": {
"title_one": "Exportar análise",
@@ -106,7 +125,12 @@
"exportButton_one": "Exportar análise",
"exportButton_many": "Exportar {{count}} análises",
"exportButton_other": "Exportar {{count}} análises",
"exportingButton": "Exportando..."
"exportingButton": "Exportando...",
"toast": {
"started_one": "Uma exportação iniciada. Abrindo o caso agora.",
"started_many": "Iniciadas {{count}} exportações. Abrindo o caso agora.",
"started_other": ""
}
}
},
"streaming": {
@@ -154,6 +178,14 @@
"export": "Exportar",
"deleteNow": "Deletar Agora",
"markAsUnreviewed": "Marcar como não revisado"
},
"shareTimestamp": {
"label": "Compartilhar marca de tempo",
"title": "Compartilhar marca de tempo",
"description": "Compartilhe uma URL com marca de tempo da posição atual da reprodução ou escolha uma marca de tempo personalizada. Observe que esta não é uma URL de compartilhamento público e só pode ser acessada por usuários que tenham acesso ao Frigate e a esta câmera.",
"custom": "Marcação de data/hora personalizado",
"button": "Compartilhar URL com marcação de tempo",
"shareTitle": "Marcação de data/hora da análise do Frigate: {{camera}}"
}
},
"imagePicker": {
@@ -4,7 +4,8 @@
"noPreviewFoundFor": "Nenhuma Pré-Visualização Encontrada em {{cameraName}}",
"submitFrigatePlus": {
"title": "Enviar esse frame para Frigate+?",
"submit": "Enviar"
"submit": "Enviar",
"previewError": "Não foi possível carregar a prévia do instantâneo. A gravação pode não estar disponível no momento."
},
"livePlayerRequiredIOSVersion": "iOS 17.1 ou superior é necessário para esse tipo de transmissão ao vivo.",
"streamOffline": {
@@ -47,5 +48,6 @@
"error": {
"submitFrigatePlusFailed": "Falha em submeter quadro ao Frigate+"
}
}
},
"cameraOff": "A câmera está desligada"
}
+121 -6
View File
@@ -12,7 +12,7 @@
"description": "Habilitado"
},
"audio": {
"label": "Eventos de áudio",
"label": "Detecção de áudio",
"description": "Configurações para detecção de eventos baseados em áudio para esta câmera.",
"enabled": {
"label": "Habilitar detecção de áudio",
@@ -28,11 +28,15 @@
},
"listen": {
"label": "Tipos de escuta",
"description": "Lista de tipos de eventos de áudio a serem detectados (por exemplo: latido, alarme de incêndio, grito, fala, berro)."
"description": "Lista de tipos de eventos de áudio a serem detectados (por exemplo: latido, alarme de incêndio, fala, grito)."
},
"filters": {
"label": "Filtros de áudio",
"description": "Configurações de filtro por tipo de áudio, como limites de confiança, usadas para reduzir falsos positivos."
"description": "Configurações de filtro por tipo de áudio, como limites de confiança, usadas para reduzir falsos positivos.",
"threshold": {
"label": "Confiança mínima do áudio",
"description": "Limite mínimo de confiança para que o evento de áudio seja contabilizado."
}
},
"enabled_in_config": {
"label": "Estado de áudio original",
@@ -47,11 +51,122 @@
"audio_transcription": {
"label": "Transcrição de áudio",
"enabled": {
"label": "Habilitar transcrição"
"label": "Habilitar transcrição",
"description": "Ative ou desative a transcrição de eventos de áudio acionada manualmente."
},
"live_enabled": {
"label": "Transcrição em tempo real"
"label": "Transcrição em tempo real",
"description": "Ative a transcrição ao vivo por streaming para o áudio à medida que ele é recebido."
},
"description": "Configurações de transcrição de áudio e voz ao vivo para eventos e legendas em tempo real."
"description": "Configurações de transcrição de áudio e voz ao vivo para eventos e legendas em tempo real.",
"enabled_in_config": {
"label": "Estado original da transcrição"
}
},
"detect": {
"enabled": {
"label": "Ativar detecção de objetos",
"description": "Ative ou desative a detecção de objetos para esta câmera."
},
"height": {
"label": "Detectar altura",
"description": "Altura (em pixels) dos quadros usados para o fluxo de detecção; deixe em branco para usar a resolução nativa do fluxo."
},
"width": {
"label": "Detectar largura",
"description": "Largura (em pixels) dos quadros usados para o fluxo de detecção; deixe em branco para usar a resolução nativa do fluxo."
},
"fps": {
"label": "Detectar FPS",
"description": "Taxa de quadros por segundo desejada para a execução da detecção; valores mais baixos reduzem o uso da CPU (o valor recomendado é 5; defina um valor mais alto — no máximo 10 — apenas se estiver rastreando objetos que se movem extremamente rápido)."
},
"min_initialized": {
"label": "Quadros de inicialização mínimos",
"description": "Número de detecções consecutivas necessárias antes de criar um objeto rastreado. Aumente esse valor para reduzir inicializações falsas. O valor padrão é o fps dividido por 2."
},
"max_disappeared": {
"label": "Máximo de quadros perdidos",
"description": "Número de quadros sem detecção antes que um objeto rastreado seja considerado desaparecido."
},
"stationary": {
"label": "Configuração de objetos estáticos",
"description": "Configurações para detectar e gerenciar objetos que permanecem parados por um período de tempo.",
"interval": {
"label": "Intervalo estacionário",
"description": "Com que frequência (em quadros) realizar uma verificação de detecção para confirmar um objeto estacionário."
},
"threshold": {
"label": "Limiar estacionário",
"description": "Número de quadros sem alteração de posição necessários para marcar um objeto como estacionário."
},
"max_frames": {
"label": "Quadros máximos",
"description": "Limita por quanto tempo objetos estacionários são rastreados antes de serem descartados.",
"default": {
"label": "Máximo de quadros padrão",
"description": "Número máximo padrão de quadros para rastrear um objeto estacionário antes de parar."
},
"objects": {
"label": "Quadros máximos do objeto",
"description": "Substituições por objeto para o número máximo de quadros de rastreamento de objetos estacionários."
}
},
"classifier": {
"label": "Ativar classificador visual",
"description": "Use um classificador visual para detectar objetos verdadeiramente estacionários, mesmo quando as caixas delimitadoras oscilam."
}
},
"annotation_offset": {
"label": "Deslocamento da anotação",
"description": "Milissegundos para deslocar as anotações de detecção, a fim de alinhar melhor as caixas delimitadoras da linha do tempo com as gravações; pode ser um valor positivo ou negativo."
},
"label": "Detecção de Objetos",
"description": "Configurações para a função de detecção (*detect*) usada para executar a detecção de objetos e inicializar rastreadores."
},
"face_recognition": {
"label": "Reconhecimento facial",
"description": "Configurações de detecção e reconhecimento facial para esta câmera.",
"enabled": {
"label": "Ativar reconhecimento facial",
"description": "Ative ou desative o reconhecimento facial."
},
"min_area": {
"label": "Área mínima do rosto",
"description": "Área mínima (em pixels) da caixa delimitadora de um rosto detectado necessária para tentar o reconhecimento."
}
},
"ffmpeg": {
"label": "Fluxos (FFmpeg)",
"description": "Entradas de fluxo de câmera e opções do FFmpeg, incluindo caminho do binário, argumentos, aceleração de hardware (hwaccel) e argumentos de saída por função.",
"path": {
"label": "Caminho do FFmpeg",
"description": "Caminho para o binário do FFmpeg a ser utilizado ou um alias de versão (\"7.0\" ou \"8.0\")."
},
"global_args": {
"label": "Argumentos globais do FFmpeg",
"description": "Argumentos globais passados para processos do FFmpeg."
},
"hwaccel_args": {
"label": "Argumentos de aceleração de hardware",
"description": "Argumentos de aceleração de hardware para o FFmpeg. Recomenda-se o uso de predefinições específicas do provedor."
},
"input_args": {
"label": "Argumentos de entrada"
}
},
"birdseye": {
"mode": {
"label": "Modo de rastreamento",
"description": "Modo de inclusão de câmeras no Birdseye: 'objetos', 'movimento' ou 'contínuo'."
},
"order": {
"label": "Posição",
"description": "Posição numérica que controla a ordem da câmera no layout de vista aérea (Birdseye)."
},
"label": "Birdseye",
"description": "Configurações para a visualização composta \"Birdseye\", que combina múltiplas transmissões de câmera em um único layout.",
"enabled": {
"label": "Ativar Birdseye"
}
}
}
+111 -4
View File
@@ -34,11 +34,15 @@
},
"listen": {
"label": "Tipos de escuta",
"description": "Lista de tipos de eventos de áudio a serem detectados (por exemplo: latido, alarme de incêndio, grito, fala, berro)."
"description": "Lista de tipos de eventos de áudio a serem detectados (por exemplo: latido, alarme de incêndio, fala, grito)."
},
"filters": {
"label": "Filtros de áudio",
"description": "Configurações de filtro por tipo de áudio, como limites de confiança, usadas para reduzir falsos positivos."
"description": "Configurações de filtro por tipo de áudio, como limites de confiança, usadas para reduzir falsos positivos.",
"threshold": {
"label": "Confiança mínima do áudio",
"description": "Limite mínimo de confiança para que o evento de áudio seja contabilizado."
}
},
"enabled_in_config": {
"label": "Estado de áudio original",
@@ -47,7 +51,8 @@
"num_threads": {
"label": "Threads de detecção",
"description": "Número de threads a serem usadas para o processamento de detecção de áudio."
}
},
"label": "Detecção de áudio"
},
"auth": {
"label": "Autenticação",
@@ -82,8 +87,110 @@
"audio_transcription": {
"label": "Transcrição de áudio",
"live_enabled": {
"label": "Transcrição em tempo real"
"label": "Transcrição em tempo real",
"description": "Ative a transcrição ao vivo por streaming para o áudio à medida que ele é recebido."
},
"description": "Configurações de transcrição de áudio e voz ao vivo para eventos e legendas em tempo real."
},
"detect": {
"enabled": {
"label": "Ativar detecção de objetos"
},
"height": {
"label": "Detectar altura",
"description": "Altura (em pixels) dos quadros usados para o fluxo de detecção; deixe em branco para usar a resolução nativa do fluxo."
},
"width": {
"label": "Detectar largura",
"description": "Largura (em pixels) dos quadros usados para o fluxo de detecção; deixe em branco para usar a resolução nativa do fluxo."
},
"fps": {
"label": "Detectar FPS",
"description": "Taxa de quadros por segundo desejada para a execução da detecção; valores mais baixos reduzem o uso da CPU (o valor recomendado é 5; defina um valor mais alto — no máximo 10 — apenas se estiver rastreando objetos que se movem extremamente rápido)."
},
"min_initialized": {
"label": "Quadros de inicialização mínimos",
"description": "Número de detecções consecutivas necessárias antes de criar um objeto rastreado. Aumente esse valor para reduzir inicializações falsas. O valor padrão é o fps dividido por 2."
},
"max_disappeared": {
"label": "Máximo de quadros perdidos",
"description": "Número de quadros sem detecção antes que um objeto rastreado seja considerado desaparecido."
},
"stationary": {
"label": "Configuração de objetos estáticos",
"description": "Configurações para detectar e gerenciar objetos que permanecem parados por um período de tempo.",
"interval": {
"label": "Intervalo estacionário",
"description": "Com que frequência (em quadros) realizar uma verificação de detecção para confirmar um objeto estacionário."
},
"threshold": {
"label": "Limiar estacionário",
"description": "Número de quadros sem alteração de posição necessários para marcar um objeto como estacionário."
},
"max_frames": {
"label": "Quadros máximos",
"description": "Limita por quanto tempo objetos estacionários são rastreados antes de serem descartados.",
"default": {
"label": "Máximo de quadros padrão",
"description": "Número máximo padrão de quadros para rastrear um objeto estacionário antes de parar."
},
"objects": {
"label": "Quadros máximos do objeto",
"description": "Substituições por objeto para o número máximo de quadros de rastreamento de objetos estacionários."
}
},
"classifier": {
"label": "Ativar classificador visual",
"description": "Use um classificador visual para detectar objetos verdadeiramente estacionários, mesmo quando as caixas delimitadoras oscilam."
}
},
"annotation_offset": {
"label": "Deslocamento da anotação",
"description": "Milissegundos para deslocar as anotações de detecção, a fim de alinhar melhor as caixas delimitadoras da linha do tempo com as gravações; pode ser um valor positivo ou negativo."
},
"label": "Detecção de Objetos",
"description": "Configurações para a função de detecção (*detect*) usada para executar a detecção de objetos e inicializar rastreadores."
},
"face_recognition": {
"label": "Reconhecimento facial",
"enabled": {
"label": "Ativar reconhecimento facial"
},
"min_area": {
"label": "Área mínima do rosto",
"description": "Área mínima (em pixels) da caixa delimitadora de um rosto detectado necessária para tentar o reconhecimento."
}
},
"ffmpeg": {
"path": {
"label": "Caminho do FFmpeg",
"description": "Caminho para o binário do FFmpeg a ser utilizado ou um alias de versão (\"7.0\" ou \"8.0\")."
},
"global_args": {
"label": "Argumentos globais do FFmpeg",
"description": "Argumentos globais passados para processos do FFmpeg."
},
"hwaccel_args": {
"label": "Argumentos de aceleração de hardware",
"description": "Argumentos de aceleração de hardware para o FFmpeg. Recomenda-se o uso de predefinições específicas do provedor."
},
"input_args": {
"label": "Argumentos de entrada"
}
},
"birdseye": {
"mode": {
"label": "Modo de rastreamento",
"description": "Modo de inclusão de câmeras no Birdseye: 'objetos', 'movimento' ou 'contínuo'."
},
"order": {
"label": "Posição",
"description": "Posição numérica que controla a ordem da câmera no layout de vista aérea (Birdseye)."
},
"label": "Birdseye",
"description": "Configurações para a visualização composta \"Birdseye\", que combina múltiplas transmissões de câmera em um único layout.",
"enabled": {
"label": "Ativar Birdseye"
}
}
}
@@ -64,5 +64,10 @@
"retention": "Retenção",
"events": "Eventos"
}
},
"ffmpeg": {
"cameras": {
"cameraFfmpeg": "Argumentos do FFmpeg específicos para a câmera"
}
}
}
@@ -28,5 +28,8 @@
"detectRequired": "Pelo menos um fluxo de entrada deve ter a função 'detectar' atribuída.",
"hwaccelDetectOnly": "Somente o fluxo de entrada com a função de detecção pode definir argumentos de aceleração de hardware."
}
},
"detect": {
"dimensionMustBeEven": "Deve ser um número par."
}
}
+63 -1
View File
@@ -1 +1,63 @@
{}
{
"documentTitle": "Frigate-Chat",
"title": "Chat-Frigate",
"subtitle": "Seu assistente de IA para gerenciamento de câmeras e insights",
"placeholder": "Pergunte o que quiser...",
"error": "Algo deu errado. Por favor, tente novamente.",
"processing": "Processando...",
"toolsUsed": "Usado: {{tools}}",
"showTools": "Mostrar ferramentas ({{count}})",
"hideTools": "Ocultar ferramentas",
"call": "Chamar",
"result": "Resultado",
"arguments": "Argumentos:",
"response": "Resposta:",
"attachment_chip_label": "{{label}} em {{camera}}",
"attachment_chip_remove": "Remover anexo",
"open_in_explore": "Abrir no Explorar",
"attach_event_aria": "Anexar evento {{eventId}}",
"attachment_picker_paste_label": "Ou cole o ID do evento",
"attachment_picker_attach": "Anexar",
"attachment_picker_placeholder": "Anexar um evento",
"quick_reply_find_similar": "Encontre avistamentos semelhantes",
"quick_reply_tell_me_more": "Conte-me mais sobre isso",
"quick_reply_when_else": "Em que outra ocasião isso foi visto?",
"quick_reply_find_similar_text": "Encontre avistamentos semelhantes a este.",
"quick_reply_tell_me_more_text": "Conte-me mais sobre este.",
"quick_reply_when_else_text": "Em que outra ocasião isso foi visto?",
"anchor": "Referência",
"similarity_score": "Semelhança",
"no_similar_objects_found": "Nenhum objeto semelhante encontrado.",
"semantic_search_required": "A busca semântica deve estar habilitada para encontrar objetos semelhantes.",
"send": "Enviar",
"suggested_requests": "Experimente perguntar:",
"starting_requests": {
"show_recent_events": "Mostrar eventos recentes",
"show_camera_status": "Mostrar status da câmera",
"recap": "O que aconteceu enquanto eu estava fora?",
"watch_camera": "Monitore uma câmera em busca de atividade"
},
"starting_requests_prompts": {
"show_recent_events": "Mostre-me os eventos recentes da última hora",
"show_camera_status": "Qual é o status atual das minhas câmeras?",
"recap": "O que aconteceu enquanto eu estava fora?",
"watch_camera": "Fique de olho na porta da frente e me avise se alguém aparecer"
},
"new_chat": "Novo chat",
"settings": {
"title": "Configurações do chat",
"show_stats": {
"title": "Mostrar estatísticas",
"desc": "Exibir a taxa de geração e o tamanho do contexto para respostas de chat.",
"while_generating": "Durante a geração",
"always": "Sempre"
},
"auto_scroll": {
"title": "Rolagem automática",
"desc": "Acompanhe as novas mensagens à medida que chegam."
}
},
"stats": {
"context": "{{tokens}} tokens"
}
}
@@ -13,7 +13,7 @@
"toast": {
"success": {
"deletedCategory_one": "Classe Apagada",
"deletedCategory_many": "",
"deletedCategory_many": "Classes apagadas",
"deletedCategory_other": "",
"deletedImage_one": "Imagen Apagada",
"deletedImage_many": "Imagens Apagadas",
@@ -94,7 +94,14 @@
},
"noModels": {
"object": {
"title": "Nenhum Modelo de Classificação de Objetos"
"title": "Nenhum Modelo de Classificação de Objetos",
"description": "Crie um modelo personalizado para classificar objetos detectados.",
"buttonText": "Criar modelo de objeto"
},
"state": {
"title": "Modelos de Classificação Sem Estado",
"description": "Crie um modelo personalizado para monitorar e classificar mudanças de estado em áreas específicas da câmera.",
"buttonText": "Criar modelo de estado"
}
},
"deleteDatasetImages": {
@@ -104,5 +111,13 @@
"title": "Editar Modelo de Classificação",
"descriptionState": "Edite as classes deste modelo de classificação de estado. As alterações exigirão treinar novamente o modelo.",
"descriptionObject": "Edite o tipo de objeto e tipo de classificação para este modelo de classificação de objeto."
},
"reclassifyImageAs": "Reclassificar imagem como:",
"reclassifyImage": "Reclassificar imagem",
"wizard": {
"title": "Criar nova classificação",
"steps": {
"nameAndDefine": "Nomear e definir"
}
}
}
@@ -72,7 +72,11 @@
"title": "Reconhecimentos Recentes",
"aria": "Selecionar reconhecimentos recentes",
"empty": "Não há tentativas recentes de reconhecimento facial",
"titleShort": "Recente"
"titleShort": "Recente",
"emptyNoLibrary": {
"title": "Carregue uma foto de rosto",
"description": "Você deve adicionar pelo menos um rosto à biblioteca para que o reconhecimento facial funcione."
}
},
"selectFace": "Selecionar Rosto",
"trainFaceAs": "Treinar Rosto como:",
@@ -89,7 +93,8 @@
"renamedFace": "O rosto foi renomeado com sucesso para {{name}}",
"deletedName_one": "{{count}} rosto foi deletado com sucesso.",
"deletedName_many": "{{count}} rostos foram deletados com sucesso.",
"deletedName_other": "{{count}} rostos foram deletados com sucesso."
"deletedName_other": "{{count}} rostos foram deletados com sucesso.",
"reclassifiedFace": "Rosto reclassificado com sucesso."
},
"error": {
"uploadingImageFailed": "Falha ao enviar a imagem: {{errorMessage}}",
@@ -98,7 +103,10 @@
"deleteNameFailed": "Falha ao deletar nome: {{errorMessage}}",
"renameFaceFailed": "Falha ao renomear rosto: {{errorMessage}}",
"trainFailed": "Falha ao treinar: {{errorMessage}}",
"updateFaceScoreFailed": "Falha ao atualizar pontuação de rosto: {{errorMessage}}"
"updateFaceScoreFailed": "Falha ao atualizar pontuação de rosto: {{errorMessage}}",
"reclassifyFailed": "Falha ao reclassificar o rosto: {{errorMessage}}"
}
}
},
"reclassifyFaceAs": "Reclassificar rosto como:",
"reclassifyFace": "Reclassificar rosto"
}
+7 -3
View File
@@ -58,7 +58,9 @@
},
"camera": {
"enable": "Ativar Câmera",
"disable": "Desabilitar Câmera"
"disable": "Desabilitar Câmera",
"turnOn": "Ligar a câmera",
"turnOff": "Desligar a câmera"
},
"muteCameras": {
"enable": "Silenciar Todas as Câmeras",
@@ -70,7 +72,8 @@
},
"recording": {
"enable": "Ativar Gravação",
"disable": "Desativar Gravação"
"disable": "Desativar Gravação",
"disabledInConfig": "A gravação deve ser habilitada primeiro nas configurações desta câmera."
},
"snapshots": {
"enable": "Permitir Capturas de Imagem",
@@ -150,7 +153,8 @@
"snapshots": "Capturas de Imagem",
"audioDetection": "Detecção de Áudio",
"autotracking": "Auto Rastreamento",
"transcription": "Transcrição de Áudio"
"transcription": "Transcrição de Áudio",
"camera": "Câmera"
},
"history": {
"label": "Exibir gravação histórica"
+2 -1
View File
@@ -242,7 +242,8 @@
"id": "Indonésio Bahasa",
"ur": "Urdu",
"hr": "Hrvatski (Croata)",
"bs": "Bosanski (Bósnio)"
"bs": "Bosanski (Bósnio)",
"zhHant": "繁體中文 (Chinês Tradicional)"
},
"appearance": "Aparência",
"darkMode": {
+6 -2
View File
@@ -68,7 +68,10 @@
"stream": "Transmissão"
},
"birdseye": "Vista Aérea"
}
},
"showAll": "Mostrar todos os Grupos de Câmeras",
"showLess": "Mostrar Menos",
"editGroups": "Editar Grupos de Câmeras"
},
"debug": {
"options": {
@@ -82,6 +85,7 @@
"zones": "Zonas",
"mask": "Máscara",
"motion": "Movimento",
"regions": "Regiões"
"regions": "Regiões",
"paths": "Caminhos"
}
}
@@ -36,6 +36,9 @@
"ask_a": "Este objeto é um <code>{{label}}</code>?",
"ask_an": "Este objeto é um <code>{{label}}</code>?",
"ask_full": "Este objeto é um <code>{{untranslatedLabel}}</code> ({{translatedLabel}})?"
},
"toast": {
"error": "Não foi possível submeter para Frigate+. Por favor verifique a sua connecção e tente novamente."
}
}
},
@@ -76,6 +79,13 @@
"select": "Selecionar",
"name": {
"placeholder": "Nome da Exportação"
},
"case": {
"newCaseOption": "Criar um novo caso",
"newCaseNamePlaceholder": "Nome do novo caso"
},
"multiCamera": {
"selectFromTimeline": "Selecionar da Timeline"
}
},
"streaming": {
@@ -20,6 +20,9 @@
},
"state": {
"submitted": "已提交"
},
"toast": {
"error": "無法提交至 Frigate+,請檢查您的網路連線並再試一次。"
}
},
"submitToPlus": {
@@ -697,7 +697,7 @@
"description": "在接收到音訊時開啟即時監控持續轉寫。"
},
"enabled": {
"label": "開啟轉錄",
"label": "啟用語音轉文字",
"description": "開啟或關閉手動觸發的音訊事件轉寫。"
},
"enabled_in_config": {
@@ -5,7 +5,8 @@ import { LiveStreamMetadata } from "@/types/live";
const FETCH_TIMEOUT_MS = 10000;
const DEFER_DELAY_MS = 2000;
const EMPTY_METADATA: { [key: string]: LiveStreamMetadata } = {};
const emptyObject: Readonly<{ [key: string]: LiveStreamMetadata }> =
Object.freeze({});
/**
* Hook that fetches go2rtc stream metadata with deferred loading.
@@ -78,7 +79,7 @@ export default function useDeferredStreamMetadata(streamNames: string[]) {
return metadata;
}, []);
const { data: metadata = EMPTY_METADATA } = useSWR<{
const { data: metadata = emptyObject } = useSWR<{
[key: string]: LiveStreamMetadata;
}>(swrKey, fetcher, {
revalidateOnFocus: false,