Compare commits

...

220 Commits

Author SHA1 Message Date
Anonymous
d16fd0cd05 Translated using Weblate (Esperanto)
Currently translated at 37.8% (103 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/eo/
2022-12-17 07:59:16 +01:00
Anonymous
6dc6ee54ea Translated using Weblate (Hindi)
Currently translated at 3.3% (9 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hi/
2022-12-17 07:59:16 +01:00
Anonymous
5b515822d3 Translated using Weblate (Malay)
Currently translated at 93.0% (253 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ms/
2022-12-17 07:59:15 +01:00
Anonymous
f81d754044 Translated using Weblate (Danish)
Currently translated at 72.7% (198 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/da/
2022-12-17 07:59:15 +01:00
Anonymous
483c5cf87a Translated using Weblate (Czech)
Currently translated at 82.7% (225 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/cs/
2022-12-17 07:59:15 +01:00
Anonymous
6b634b4ef5 Translated using Weblate (Arabic)
Currently translated at 16.1% (44 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ar/
2022-12-17 07:59:15 +01:00
Anonymous
f58ae97c6e Translated using Weblate (Serbian)
Currently translated at 3.3% (9 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/sr/
2022-12-17 07:59:14 +01:00
Anonymous
5deb58f092 Translated using Weblate (Turkish)
Currently translated at 84.9% (231 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/tr/
2022-12-17 07:59:14 +01:00
Anonymous
55cd4c5ada Translated using Weblate (Bulgarian)
Currently translated at 16.9% (46 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/bg/
2022-12-17 07:59:14 +01:00
Anonymous
27a7adc282 Translated using Weblate (Telugu)
Currently translated at 79.7% (217 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/te/
2022-12-17 07:59:14 +01:00
Anonymous
6b72b60b5e Translated using Weblate (Finnish)
Currently translated at 45.2% (123 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/fi/
2022-12-17 07:59:13 +01:00
Anonymous
c2d6b9738f Translated using Weblate (Yue)
Currently translated at 43.3% (118 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/yue/
2022-12-17 07:59:13 +01:00
Anonymous
44dbff308a Translated using Weblate (Portuguese (Brazil))
Currently translated at 43.0% (117 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pt_BR/
2022-12-17 07:59:13 +01:00
Anonymous
c224a26bc8 Translated using Weblate (Romanian)
Currently translated at 50.3% (137 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ro/
2022-12-17 07:59:13 +01:00
Anonymous
a1e9afae85 Translated using Weblate (Hebrew)
Currently translated at 37.1% (101 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/he/
2022-12-17 07:59:13 +01:00
Anonymous
bca6340ded Translated using Weblate (Hungarian)
Currently translated at 39.7% (108 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hu/
2022-12-17 07:59:12 +01:00
Anonymous
f1e7df394e Translated using Weblate (Croatian)
Currently translated at 94.1% (256 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hr/
2022-12-17 07:59:12 +01:00
Anonymous
a2bbcce69e Translated using Weblate (Swedish)
Currently translated at 47.7% (130 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/sv/
2022-12-17 07:59:12 +01:00
Anonymous
de8747c9f5 Translated using Weblate (Polish)
Currently translated at 94.8% (258 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pl/
2022-12-17 07:59:12 +01:00
Anonymous
0b8ce67e10 Translated using Weblate (Catalan)
Currently translated at 96.3% (262 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ca/
2022-12-17 07:59:11 +01:00
Anonymous
0de4d00d83 Translated using Weblate (Chinese (Traditional))
Currently translated at 3.3% (9 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/zh_Hant/
2022-12-17 07:59:11 +01:00
Anonymous
0b47e083ea Translated using Weblate (Dutch)
Currently translated at 23.1% (63 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/nl/
2022-12-17 07:59:11 +01:00
Anonymous
2e0b55bd37 Translated using Weblate (Vietnamese)
Currently translated at 16.1% (44 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/vi/
2022-12-17 07:59:11 +01:00
Anonymous
ee5bb5364b Translated using Weblate (Norwegian Bokmål)
Currently translated at 29.0% (79 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/nb_NO/
2022-12-17 07:59:11 +01:00
Anonymous
bb3fd88a90 Translated using Weblate (Italian)
Currently translated at 84.1% (229 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/it/
2022-12-17 07:59:10 +01:00
Anonymous
ac5dfe5a9c Translated using Weblate (Chinese (Simplified))
Currently translated at 61.7% (168 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/zh_Hans/
2022-12-17 07:59:10 +01:00
Anonymous
0bc65e4345 Translated using Weblate (Russian)
Currently translated at 17.6% (48 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ru/
2022-12-17 07:59:10 +01:00
Anonymous
92b80d1e46 Translated using Weblate (Portuguese)
Currently translated at 60.2% (164 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pt/
2022-12-17 07:59:10 +01:00
Anonymous
dab627e248 Translated using Weblate (French)
Currently translated at 97.7% (266 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/fr/
2022-12-17 07:59:09 +01:00
Anonymous
70a77718db Translated using Weblate (Spanish)
Currently translated at 96.3% (262 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/es/
2022-12-17 07:59:09 +01:00
Anonymous
9a81c9a418 Translated using Weblate (German)
Currently translated at 83.8% (228 of 272 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/de/
2022-12-17 07:59:09 +01:00
shamoon
6ef05b8118 Merge pull request #648 from benphelps/fix-619
Fix rate unit displays
2022-12-16 22:58:30 -08:00
Anonymous
6fd187c54c Translated using Weblate (Esperanto)
Currently translated at 38.1% (103 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/eo/
2022-12-17 07:47:44 +01:00
Anonymous
abf65caba7 Translated using Weblate (Hindi)
Currently translated at 3.3% (9 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hi/
2022-12-17 07:47:43 +01:00
Anonymous
5470c68cb4 Translated using Weblate (Malay)
Currently translated at 93.7% (253 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ms/
2022-12-17 07:47:43 +01:00
Anonymous
f0bca41c5e Translated using Weblate (Danish)
Currently translated at 73.3% (198 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/da/
2022-12-17 07:47:43 +01:00
Anonymous
c985c4788b Translated using Weblate (Czech)
Currently translated at 83.3% (225 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/cs/
2022-12-17 07:47:43 +01:00
Anonymous
2df5e8d791 Translated using Weblate (Arabic)
Currently translated at 16.2% (44 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ar/
2022-12-17 07:47:42 +01:00
Anonymous
2e8ea3b0d8 Translated using Weblate (Serbian)
Currently translated at 3.3% (9 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/sr/
2022-12-17 07:47:42 +01:00
Anonymous
8a2a624956 Translated using Weblate (Turkish)
Currently translated at 85.5% (231 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/tr/
2022-12-17 07:47:42 +01:00
Anonymous
b1aa093f5d Translated using Weblate (Bulgarian)
Currently translated at 17.0% (46 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/bg/
2022-12-17 07:47:42 +01:00
Anonymous
216a67fc12 Translated using Weblate (Telugu)
Currently translated at 80.3% (217 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/te/
2022-12-17 07:47:41 +01:00
Anonymous
85d9b2fc8a Translated using Weblate (Finnish)
Currently translated at 45.5% (123 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/fi/
2022-12-17 07:47:41 +01:00
Anonymous
65454014ef Translated using Weblate (Yue)
Currently translated at 43.7% (118 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/yue/
2022-12-17 07:47:41 +01:00
Anonymous
4e23a931cd Translated using Weblate (Portuguese (Brazil))
Currently translated at 43.3% (117 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pt_BR/
2022-12-17 07:47:41 +01:00
Anonymous
3446c9b2a1 Translated using Weblate (Romanian)
Currently translated at 50.7% (137 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ro/
2022-12-17 07:47:41 +01:00
Anonymous
ccda10fca4 Translated using Weblate (Hebrew)
Currently translated at 37.4% (101 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/he/
2022-12-17 07:47:40 +01:00
Anonymous
a14dfb9360 Translated using Weblate (Hungarian)
Currently translated at 40.0% (108 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hu/
2022-12-17 07:47:40 +01:00
Anonymous
f3e52f0bdf Translated using Weblate (Croatian)
Currently translated at 94.8% (256 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hr/
2022-12-17 07:47:40 +01:00
Anonymous
f7a666e8c5 Translated using Weblate (Swedish)
Currently translated at 48.1% (130 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/sv/
2022-12-17 07:47:40 +01:00
Anonymous
1feaae7291 Translated using Weblate (Polish)
Currently translated at 95.5% (258 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pl/
2022-12-17 07:47:40 +01:00
Anonymous
112cbaf813 Translated using Weblate (Catalan)
Currently translated at 97.0% (262 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ca/
2022-12-17 07:47:39 +01:00
Anonymous
5263a66fbe Translated using Weblate (Chinese (Traditional))
Currently translated at 3.3% (9 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/zh_Hant/
2022-12-17 07:47:39 +01:00
Anonymous
d1971986ea Translated using Weblate (Dutch)
Currently translated at 23.3% (63 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/nl/
2022-12-17 07:47:39 +01:00
Anonymous
988f3dfdd5 Translated using Weblate (Vietnamese)
Currently translated at 16.2% (44 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/vi/
2022-12-17 07:47:39 +01:00
Anonymous
95794dc837 Translated using Weblate (Norwegian Bokmål)
Currently translated at 29.2% (79 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/nb_NO/
2022-12-17 07:47:39 +01:00
Anonymous
8bb716e1c5 Translated using Weblate (Italian)
Currently translated at 84.8% (229 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/it/
2022-12-17 07:47:38 +01:00
Anonymous
ec3faa31a5 Translated using Weblate (Chinese (Simplified))
Currently translated at 62.2% (168 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/zh_Hans/
2022-12-17 07:47:38 +01:00
Anonymous
09d7b8f5ed Translated using Weblate (Russian)
Currently translated at 17.7% (48 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ru/
2022-12-17 07:47:38 +01:00
Anonymous
60fcbbfeaf Translated using Weblate (Portuguese)
Currently translated at 60.7% (164 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pt/
2022-12-17 07:47:38 +01:00
Anonymous
6d1c9263b7 Translated using Weblate (French)
Currently translated at 98.5% (266 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/fr/
2022-12-17 07:47:37 +01:00
Anonymous
5bd1dd71f6 Translated using Weblate (Spanish)
Currently translated at 97.0% (262 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/es/
2022-12-17 07:47:37 +01:00
Anonymous
4fcc8f004d Translated using Weblate (German)
Currently translated at 84.4% (228 of 270 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/de/
2022-12-17 07:47:37 +01:00
shamoon
a11f22cd49 Merge pull request #688 from luiseduardobrito/feat-nextdns
Adds widget for NextDNS
2022-12-16 22:47:20 -08:00
Anonymous
d79bf7ef80 Translated using Weblate (Esperanto)
Currently translated at 38.4% (103 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/eo/
2022-12-17 07:41:49 +01:00
Anonymous
a88a803d38 Translated using Weblate (Hindi)
Currently translated at 3.3% (9 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hi/
2022-12-17 07:41:48 +01:00
Anonymous
23b59dc542 Translated using Weblate (Malay)
Currently translated at 94.4% (253 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ms/
2022-12-17 07:41:48 +01:00
Anonymous
751167fb17 Translated using Weblate (Danish)
Currently translated at 73.8% (198 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/da/
2022-12-17 07:41:48 +01:00
Anonymous
d1336c5b95 Translated using Weblate (Czech)
Currently translated at 83.9% (225 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/cs/
2022-12-17 07:41:48 +01:00
Anonymous
18765f7b4d Translated using Weblate (Arabic)
Currently translated at 16.4% (44 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ar/
2022-12-17 07:41:48 +01:00
Anonymous
04f2d2a0e4 Translated using Weblate (Serbian)
Currently translated at 3.3% (9 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/sr/
2022-12-17 07:41:47 +01:00
Anonymous
64a6af7bf6 Translated using Weblate (Turkish)
Currently translated at 86.1% (231 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/tr/
2022-12-17 07:41:47 +01:00
Anonymous
158bb0ae41 Translated using Weblate (Bulgarian)
Currently translated at 17.1% (46 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/bg/
2022-12-17 07:41:47 +01:00
Anonymous
c3e14a0593 Translated using Weblate (Telugu)
Currently translated at 80.9% (217 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/te/
2022-12-17 07:41:46 +01:00
Anonymous
3e177ec39d Translated using Weblate (Finnish)
Currently translated at 45.8% (123 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/fi/
2022-12-17 07:41:46 +01:00
Anonymous
b7d9124228 Translated using Weblate (Yue)
Currently translated at 44.0% (118 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/yue/
2022-12-17 07:41:46 +01:00
Anonymous
67ed9b294b Translated using Weblate (Portuguese (Brazil))
Currently translated at 43.6% (117 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pt_BR/
2022-12-17 07:41:46 +01:00
Anonymous
931947bffc Translated using Weblate (Romanian)
Currently translated at 51.1% (137 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ro/
2022-12-17 07:41:46 +01:00
Anonymous
de9ca09e70 Translated using Weblate (Hebrew)
Currently translated at 37.6% (101 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/he/
2022-12-17 07:41:46 +01:00
Anonymous
7ef294291a Translated using Weblate (Hungarian)
Currently translated at 40.2% (108 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hu/
2022-12-17 07:41:45 +01:00
Anonymous
e44972ec44 Translated using Weblate (Croatian)
Currently translated at 95.5% (256 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hr/
2022-12-17 07:41:45 +01:00
Anonymous
b7a45512cf Translated using Weblate (Swedish)
Currently translated at 48.5% (130 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/sv/
2022-12-17 07:41:45 +01:00
Anonymous
4621724fc6 Translated using Weblate (Polish)
Currently translated at 96.2% (258 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pl/
2022-12-17 07:41:45 +01:00
Anonymous
fb9c673130 Translated using Weblate (Catalan)
Currently translated at 97.7% (262 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ca/
2022-12-17 07:41:44 +01:00
Anonymous
15b08cdd44 Translated using Weblate (Chinese (Traditional))
Currently translated at 3.3% (9 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/zh_Hant/
2022-12-17 07:41:44 +01:00
Anonymous
76db303368 Translated using Weblate (Dutch)
Currently translated at 23.5% (63 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/nl/
2022-12-17 07:41:44 +01:00
Anonymous
d5d0313b21 Translated using Weblate (Vietnamese)
Currently translated at 16.4% (44 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/vi/
2022-12-17 07:41:44 +01:00
Anonymous
f419b3753a Translated using Weblate (Norwegian Bokmål)
Currently translated at 29.4% (79 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/nb_NO/
2022-12-17 07:41:44 +01:00
Anonymous
705258d30d Translated using Weblate (Italian)
Currently translated at 85.4% (229 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/it/
2022-12-17 07:41:43 +01:00
Anonymous
413b7a7203 Translated using Weblate (Chinese (Simplified))
Currently translated at 62.6% (168 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/zh_Hans/
2022-12-17 07:41:43 +01:00
Anonymous
d3a8d27276 Translated using Weblate (Russian)
Currently translated at 17.9% (48 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ru/
2022-12-17 07:41:43 +01:00
Anonymous
725c1b7da4 Translated using Weblate (Portuguese)
Currently translated at 61.1% (164 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pt/
2022-12-17 07:41:43 +01:00
Anonymous
45767d4b27 Translated using Weblate (French)
Currently translated at 99.2% (266 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/fr/
2022-12-17 07:41:42 +01:00
Anonymous
0e4d1da1cc Translated using Weblate (Spanish)
Currently translated at 97.7% (262 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/es/
2022-12-17 07:41:42 +01:00
Anonymous
55f9d77054 Translated using Weblate (German)
Currently translated at 85.0% (228 of 268 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/de/
2022-12-17 07:41:42 +01:00
Nonoss117
757ef40afc Translated using Weblate (French)
Currently translated at 100.0% (266 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/fr/
2022-12-17 07:41:20 +01:00
shamoon
ee95c23c3d Merge pull request #631 from henry40408/feature/miniflux
Miniflux widget
2022-12-16 22:41:17 -08:00
Michael Shamoon
93445a2831 Use credentialed proxy for miniflux 2022-12-16 22:33:24 -08:00
Heng-Yi Wu
3bef3dd6c6 feat: miniflux widget 2022-12-16 22:33:23 -08:00
Michael Shamoon
db46931246 Allow map in credentialedproxy 2022-12-16 22:32:28 -08:00
shamoon
7ca7a9cc58 Merge pull request #660 from vinaydawani/features/basic-docker-swarm
Support for docker swarm services
2022-12-16 21:49:37 -08:00
Michael Shamoon
f7d5582f57 Handle nextdns no devices setup, data pending 2022-12-16 14:29:13 -08:00
Anonymous
82486d1e13 Translated using Weblate (Esperanto)
Currently translated at 38.7% (103 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/eo/
2022-12-16 22:51:14 +01:00
Anonymous
80cc9fc984 Translated using Weblate (Hindi)
Currently translated at 3.3% (9 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hi/
2022-12-16 22:51:14 +01:00
Anonymous
17b21eb029 Translated using Weblate (Malay)
Currently translated at 95.1% (253 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ms/
2022-12-16 22:51:14 +01:00
Anonymous
0a2528d476 Translated using Weblate (Danish)
Currently translated at 74.4% (198 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/da/
2022-12-16 22:51:13 +01:00
Anonymous
db9afd4d7c Translated using Weblate (Czech)
Currently translated at 84.5% (225 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/cs/
2022-12-16 22:51:13 +01:00
Anonymous
5f116226cf Translated using Weblate (Arabic)
Currently translated at 16.5% (44 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ar/
2022-12-16 22:51:13 +01:00
Anonymous
41ce91cf81 Translated using Weblate (Serbian)
Currently translated at 3.3% (9 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/sr/
2022-12-16 22:51:13 +01:00
Anonymous
a6599b1ea0 Translated using Weblate (Turkish)
Currently translated at 86.8% (231 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/tr/
2022-12-16 22:51:12 +01:00
Anonymous
aed29762fd Translated using Weblate (Bulgarian)
Currently translated at 17.2% (46 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/bg/
2022-12-16 22:51:12 +01:00
Anonymous
411ca43729 Translated using Weblate (Telugu)
Currently translated at 81.5% (217 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/te/
2022-12-16 22:51:12 +01:00
Anonymous
5401896829 Translated using Weblate (Finnish)
Currently translated at 46.2% (123 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/fi/
2022-12-16 22:51:12 +01:00
Anonymous
92a5e20c7e Translated using Weblate (Yue)
Currently translated at 44.3% (118 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/yue/
2022-12-16 22:51:11 +01:00
Anonymous
c6677d55da Translated using Weblate (Portuguese (Brazil))
Currently translated at 43.9% (117 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pt_BR/
2022-12-16 22:51:11 +01:00
Anonymous
1168b301dd Translated using Weblate (Romanian)
Currently translated at 51.5% (137 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ro/
2022-12-16 22:51:11 +01:00
Anonymous
e1d835259d Translated using Weblate (Hebrew)
Currently translated at 37.9% (101 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/he/
2022-12-16 22:51:11 +01:00
Anonymous
4b38db6380 Translated using Weblate (Hungarian)
Currently translated at 40.6% (108 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hu/
2022-12-16 22:51:10 +01:00
Anonymous
d7f06dbfbb Translated using Weblate (Croatian)
Currently translated at 96.2% (256 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hr/
2022-12-16 22:51:10 +01:00
Anonymous
86485740eb Translated using Weblate (Swedish)
Currently translated at 48.8% (130 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/sv/
2022-12-16 22:51:10 +01:00
Anonymous
97550a67b7 Translated using Weblate (Polish)
Currently translated at 96.9% (258 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pl/
2022-12-16 22:51:10 +01:00
Anonymous
e8d50201a5 Translated using Weblate (Catalan)
Currently translated at 98.4% (262 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ca/
2022-12-16 22:51:09 +01:00
Anonymous
39e276644f Translated using Weblate (Chinese (Traditional))
Currently translated at 3.3% (9 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/zh_Hant/
2022-12-16 22:51:09 +01:00
Anonymous
d72cbb79f5 Translated using Weblate (Dutch)
Currently translated at 23.6% (63 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/nl/
2022-12-16 22:51:09 +01:00
Anonymous
0a260db335 Translated using Weblate (Vietnamese)
Currently translated at 16.5% (44 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/vi/
2022-12-16 22:51:09 +01:00
Anonymous
64a77ce04f Translated using Weblate (Norwegian Bokmål)
Currently translated at 29.6% (79 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/nb_NO/
2022-12-16 22:51:09 +01:00
Anonymous
5f33f061cc Translated using Weblate (Italian)
Currently translated at 86.0% (229 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/it/
2022-12-16 22:51:08 +01:00
Anonymous
7606763cc6 Translated using Weblate (Chinese (Simplified))
Currently translated at 63.1% (168 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/zh_Hans/
2022-12-16 22:51:08 +01:00
Anonymous
d73430b299 Translated using Weblate (Russian)
Currently translated at 18.0% (48 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ru/
2022-12-16 22:51:08 +01:00
Anonymous
0ab988119d Translated using Weblate (Portuguese)
Currently translated at 61.6% (164 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pt/
2022-12-16 22:51:08 +01:00
Anonymous
6d97ac77f6 Translated using Weblate (French)
Currently translated at 98.4% (262 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/fr/
2022-12-16 22:51:07 +01:00
Anonymous
cd2b9d2e0a Translated using Weblate (Spanish)
Currently translated at 98.4% (262 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/es/
2022-12-16 22:51:07 +01:00
Anonymous
32ef47e6d2 Translated using Weblate (German)
Currently translated at 85.7% (228 of 266 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/de/
2022-12-16 22:51:07 +01:00
shamoon
cb3c20ecbd Merge pull request #687 from rshide/main
added tdarr widget
2022-12-16 13:50:32 -08:00
Michael Shamoon
12ed730897 Code cleanup + lint 2022-12-16 13:49:41 -08:00
Rickey Shideler
aaa1f76176 added tdarr widget 2022-12-16 13:49:40 -08:00
Luis Brito
fb2492e577 Adds widget for NextDNS 2022-12-16 14:38:48 -03:00
Zack
e7b0fc1419 Translated using Weblate (German)
Currently translated at 87.0% (228 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/de/
2022-12-15 20:58:40 +01:00
guineu
7dd0b0e4eb Translated using Weblate (Catalan)
Currently translated at 100.0% (262 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ca/
2022-12-13 16:06:18 +01:00
Luis Silva
6b55a50b05 Translated using Weblate (Portuguese)
Currently translated at 62.5% (164 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pt/
2022-12-12 02:50:33 +01:00
shamoon
4f38c0ee80 Merge pull request #664 from Snekussaurier/feature-565
Add ability to use custom logo
2022-12-11 11:01:54 -08:00
Michael Shamoon
95dcb8802a Use ResolvedIcon, remove extra div 2022-12-11 10:30:04 -08:00
Vinay Dawani
9188f5cdd9 cleanup 2022-12-11 12:49:23 -05:00
Snekussaurier
645cf211dd Add ability to use custom logo 2022-12-11 17:31:59 +01:00
Vinay Dawani
c720df0805 double negation instead of ternary. force boolean 2022-12-11 04:01:06 -05:00
Vinay Dawani
b7722ed333 check swarm value before passing 2022-12-11 03:15:59 -05:00
Vinay Dawani
fb883c7b27 added yaml file variable for swarm mode checks 2022-12-11 03:03:20 -05:00
Vinay Dawani
f51e755216 Merge remote-tracking branch 'forkorigin/main' into features/basic-docker-swarm 2022-12-11 02:42:22 -05:00
Michael Shamoon
146326f427 fix plex proxy async bug 2022-12-10 22:26:35 -08:00
Michael Shamoon
ccc1229098 fix intermittent loss of plex stats
closes #656
2022-12-10 08:42:32 -08:00
shamoon
f6b6c64b93 Merge pull request #657 from benphelps/fix-649
Fix: allow multiple instances of certain widgets
2022-12-09 22:39:51 -08:00
Michael Shamoon
91d8e56471 append service name to cache keys 2022-12-09 22:39:10 -08:00
shamoon
702dbd8a82 Merge pull request #658 from benphelps/fix-656
Fix: plex widget error with 1 library
2022-12-09 22:37:50 -08:00
Michael Shamoon
5e722b4d11 Fix plex widget error with 1 library 2022-12-09 22:33:26 -08:00
Michael Shamoon
712fbb53c7 Fix rate unit displays 2022-12-08 15:54:01 -08:00
shamoon
88c437562b Merge pull request #640 from clbx/main
Correct stylized spelling of Authentik
2022-12-07 21:13:38 -08:00
clay
537fd0ac21 Correct spelling of Authentik 2022-12-08 00:05:33 -05:00
Diogo Gaspar
59cb7baf0b Translated using Weblate (Portuguese)
Currently translated at 52.2% (137 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pt/
2022-12-08 01:48:35 +01:00
Michael Shamoon
94af8044f1 fix minor flood without a backend error 2022-12-06 15:36:53 -08:00
Milo Ivir
5d8b937e9c Translated using Weblate (Croatian)
Currently translated at 97.7% (256 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hr/
2022-12-06 20:48:10 +01:00
Michael Shamoon
f89093a067 Update openmeteo-condition-map.js 2022-12-05 09:57:32 -08:00
shamoon
e303888119 Merge pull request #627 from benphelps/fix-openmeteo-icons
Fix weather icons for openmeteo, add optional tz parameter
2022-12-05 09:46:59 -08:00
Michael Shamoon
a7676c4daa Update README.md 2022-12-05 09:41:54 -08:00
Michael Shamoon
535a7d2f2d Add optional tz 2022-12-05 09:41:19 -08:00
Michael Shamoon
656b818488 Fix conditions map for openmeteo 2022-12-05 08:52:45 -08:00
shamoon
249c3eab8c Merge pull request #626 from saxobroko/patch-1
Linked weather providers and added an attributi...
2022-12-05 07:37:08 -08:00
Saxo_Broko
6de0205d07 Linked weather providers and added an attributi...
attribution for Open-Meteo
2022-12-06 00:22:44 +11:00
shamoon
826fe15e15 Merge pull request #620 from Tarow/scrutiny-widget-thresholds
Respect scrutiny device status threshold setting
2022-12-04 23:33:24 -08:00
Niklas
0ce5311b5f fix lint errors 2022-12-05 08:13:41 +01:00
Niklas
90cb395dc6 change calculation of passed devices 2022-12-05 08:01:24 +01:00
shamoon
cbf72eedab fix missing passing condition 2022-12-04 15:33:51 -08:00
Niklas
3f79a2fdda thresholdStatusMapping bugfix 2022-12-04 20:01:00 +01:00
Niklas
1a94453849 Respect scrutiny device status threshold setting 2022-12-04 19:33:15 +01:00
retmas-gh
6045e53207 Translated using Weblate (Polish)
Currently translated at 98.4% (258 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pl/
2022-12-04 16:47:38 +01:00
shamoon
d3c6d1fe85 Merge pull request #610 from benphelps/fix-604
Fix: Hide docker memory stats when unsupported
2022-12-03 16:28:22 -08:00
gallegonovato
35a7ba77e3 Translated using Weblate (Spanish)
Currently translated at 100.0% (262 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/es/
2022-12-03 15:37:11 +01:00
Michael Shamoon
57d12c32fc Hide docker memory stats when unsupported 2022-12-03 01:43:53 -08:00
shamoon
f09268230e Merge pull request #607 from benphelps/fix-590
Fix: unable to build from source on some installs
2022-12-02 21:00:08 -08:00
Nonoss117
98cefe37d2 Translated using Weblate (French)
Currently translated at 100.0% (262 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/fr/
2022-12-02 15:08:38 +01:00
Michael Shamoon
01b55a17f1 fix flood lint 2022-12-01 16:53:57 -08:00
Anonymous
aaacf2ea4b Translated using Weblate (Esperanto)
Currently translated at 39.3% (103 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/eo/
2022-12-02 01:46:55 +01:00
Anonymous
8672998f08 Translated using Weblate (Hindi)
Currently translated at 3.4% (9 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hi/
2022-12-02 01:46:54 +01:00
Anonymous
d8039031ca Translated using Weblate (Malay)
Currently translated at 96.5% (253 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ms/
2022-12-02 01:46:54 +01:00
Anonymous
f5ad46f1e1 Translated using Weblate (Danish)
Currently translated at 75.5% (198 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/da/
2022-12-02 01:46:54 +01:00
Anonymous
c1473b4045 Translated using Weblate (Czech)
Currently translated at 85.8% (225 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/cs/
2022-12-02 01:46:53 +01:00
Anonymous
285ae970c8 Translated using Weblate (Arabic)
Currently translated at 16.7% (44 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ar/
2022-12-02 01:46:53 +01:00
Anonymous
bf1c67a7ac Translated using Weblate (Serbian)
Currently translated at 3.4% (9 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/sr/
2022-12-02 01:46:53 +01:00
Anonymous
98165bf9dd Translated using Weblate (Turkish)
Currently translated at 88.1% (231 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/tr/
2022-12-02 01:46:53 +01:00
Anonymous
19e297e4c6 Translated using Weblate (Bulgarian)
Currently translated at 17.5% (46 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/bg/
2022-12-02 01:46:52 +01:00
Anonymous
8eaa942572 Translated using Weblate (Telugu)
Currently translated at 82.8% (217 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/te/
2022-12-02 01:46:52 +01:00
Anonymous
ce1be46c0b Translated using Weblate (Finnish)
Currently translated at 46.9% (123 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/fi/
2022-12-02 01:46:52 +01:00
Anonymous
da75b7b0d3 Translated using Weblate (Yue)
Currently translated at 45.0% (118 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/yue/
2022-12-02 01:46:52 +01:00
Anonymous
d70c618442 Translated using Weblate (Portuguese (Brazil))
Currently translated at 44.6% (117 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pt_BR/
2022-12-02 01:46:51 +01:00
Anonymous
f4e3cafa25 Translated using Weblate (Romanian)
Currently translated at 52.2% (137 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ro/
2022-12-02 01:46:51 +01:00
Anonymous
dcafcb983e Translated using Weblate (Hebrew)
Currently translated at 38.5% (101 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/he/
2022-12-02 01:46:51 +01:00
Anonymous
faac4518f5 Translated using Weblate (Hungarian)
Currently translated at 41.2% (108 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hu/
2022-12-02 01:46:51 +01:00
Anonymous
45ba9c6961 Translated using Weblate (Croatian)
Currently translated at 95.0% (249 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hr/
2022-12-02 01:46:50 +01:00
Anonymous
25c5f36a0c Translated using Weblate (Swedish)
Currently translated at 49.6% (130 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/sv/
2022-12-02 01:46:50 +01:00
Anonymous
2ff06d12b0 Translated using Weblate (Polish)
Currently translated at 90.8% (238 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pl/
2022-12-02 01:46:50 +01:00
Anonymous
c3305c2cd7 Translated using Weblate (Catalan)
Currently translated at 50.0% (131 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ca/
2022-12-02 01:46:50 +01:00
Anonymous
7cae96a77b Translated using Weblate (Chinese (Traditional))
Currently translated at 3.4% (9 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/zh_Hant/
2022-12-02 01:46:49 +01:00
Anonymous
0a7616f0f6 Translated using Weblate (Dutch)
Currently translated at 24.0% (63 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/nl/
2022-12-02 01:46:49 +01:00
Anonymous
5de93bcad2 Translated using Weblate (Vietnamese)
Currently translated at 16.7% (44 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/vi/
2022-12-02 01:46:49 +01:00
Anonymous
c72961573b Translated using Weblate (Norwegian Bokmål)
Currently translated at 30.1% (79 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/nb_NO/
2022-12-02 01:46:49 +01:00
Anonymous
2a9c39532a Translated using Weblate (Italian)
Currently translated at 87.4% (229 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/it/
2022-12-02 01:46:48 +01:00
Anonymous
2e6d760c53 Translated using Weblate (Chinese (Simplified))
Currently translated at 64.1% (168 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/zh_Hans/
2022-12-02 01:46:48 +01:00
Anonymous
d2f3098b2a Translated using Weblate (Russian)
Currently translated at 18.3% (48 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ru/
2022-12-02 01:46:48 +01:00
Anonymous
22dd4e5f77 Translated using Weblate (Portuguese)
Currently translated at 43.8% (115 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pt/
2022-12-02 01:46:48 +01:00
Anonymous
31e6c1fa86 Translated using Weblate (French)
Currently translated at 98.4% (258 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/fr/
2022-12-02 01:46:47 +01:00
Anonymous
ba44c0ae9d Translated using Weblate (Spanish)
Currently translated at 98.0% (257 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/es/
2022-12-02 01:46:47 +01:00
Anonymous
8c918f1ea6 Translated using Weblate (German)
Currently translated at 85.8% (225 of 262 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/de/
2022-12-02 01:46:47 +01:00
shamoon
ff31b36b46 Merge pull request #598 from JigSawFr/widget/flood
Add flood widget
2022-12-01 16:46:17 -08:00
Jason Fischer
01704ec38b Merge pull request #600 from benphelps/fix-444
Feature: Add default interval setting for coinmarketcap
2022-12-01 16:30:00 -08:00
JigSawFr
8a84eba232 fix(widgets): move flood auth from BASIC to raw json body 2022-12-01 20:55:14 +01:00
Nonoss117
0d28fe25f8 Translated using Weblate (French)
Currently translated at 100.0% (258 of 258 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/fr/
2022-12-01 20:47:17 +01:00
gallegonovato
97a3346ff6 Translated using Weblate (Spanish)
Currently translated at 99.6% (257 of 258 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/es/
2022-12-01 20:47:17 +01:00
shamoon
9d2f1ab8c8 Add default interval setting for coinmarketcap 2022-12-01 06:36:51 -08:00
JigSawFr
7087ed80cc Add flood widget 2022-12-01 12:40:19 +01:00
Michael Shamoon
1aeb3a3b63 Add a perfunctory key to <span> 2022-11-30 20:52:30 -08:00
Ivan Bravo Bravo
99b2ba8944 added basic support for Docker Swarm services 2022-10-26 00:12:46 -05:00
69 changed files with 1776 additions and 342 deletions

View File

@@ -47,12 +47,12 @@
- Service Integration
- Sonarr, Radarr, Readarr, Prowlarr, Bazarr, Lidarr, Emby, Jellyfin, Tautulli (Plex)
- Ombi, Overseerr, Jellyseerr, Jackett, NZBGet, SABnzbd, ruTorrent, Transmission, qBittorrent
- Portainer, Traefik, Speedtest Tracker, PiHole, AdGuard Home, Nginx Proxy Manager, Gotify, Syncthing Relay Server, Authentic, Proxmox
- Portainer, Traefik, Speedtest Tracker, PiHole, AdGuard Home, Nginx Proxy Manager, Gotify, Syncthing Relay Server, Authentik, Proxmox
- Information Providers
- Coin Market Cap, Mastodon
- Information & Utility Widgets
- System Stats (Disk, CPU, Memory)
- Weather via WeatherAPI.com or OpenWeatherMap
- Weather via [OpenWeatherMap](https://openweathermap.org/) or [Open-Meteo](https://open-meteo.com/)
- Search Bar
- Customizable
- 21 theme colors with light and dark mode support

View File

@@ -98,20 +98,21 @@ module.exports = {
);
i18next.services.formatter.add("rate", (value, lng, options) => {
if (value === 0) return "0 Bps";
const bits = options.bits ? value : value / 8;
const k = 1024;
const k = options.binary ? 1024 : 1000;
const sizes = options.bits ? (options.binary ? BIBIT_UNITS : BIT_UNITS) : (options.binary ? BIBYTE_UNITS : BYTE_UNITS);
if (value === 0) return `0 ${sizes[0]}/s`;
const dm = options.decimals ? options.decimals : 0;
const sizes = ["Bps", "KiBps", "MiBps", "GiBps", "TiBps", "PiBps", "EiBps", "ZiBps", "YiBps"];
const i = Math.floor(Math.log(bits) / Math.log(k));
const i = options.binary ? 2 : Math.floor(Math.log(value) / Math.log(k));
const formatted = new Intl.NumberFormat(lng, { maximumFractionDigits: dm, minimumFractionDigits: dm }).format(
parseFloat(bits / k ** i)
parseFloat(value / k ** i)
);
return `${formatted} ${sizes[i]}`;
return `${formatted} ${sizes[i]}/s`;
});
i18next.services.formatter.add("percent", (value, lng, options) =>

View File

@@ -355,5 +355,29 @@
"seed": "Seed",
"download": "Download",
"upload": "Upload"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"download": "Download",
"upload": "Upload",
"leech": "Leech"
},
"flood": {
"leech": "Leech",
"seed": "Seed",
"download": "Download",
"upload": "Upload"
},
"tdarr": {
"saved": "Saved",
"queue": "Queue",
"processed": "Processed",
"errored": "Errored"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -3,10 +3,10 @@
"missing_type": "Falta el tipus de widget: {{type}}",
"api_error": "Error d'API",
"status": "Estat",
"information": "Information",
"information": "Informació",
"url": "URL",
"raw_error": "Raw Error",
"response_data": "Response Data"
"raw_error": "Error sense processar",
"response_data": "Dades de resposta"
},
"weather": {
"allow": "Feu clic per permetre",
@@ -20,8 +20,8 @@
"transmission": {
"seed": "Llavors",
"download": "Descàrrega",
"upload": "Càrrega",
"leech": "Companys"
"upload": "Pujada",
"leech": "Company"
},
"sonarr": {
"wanted": "Volgut",
@@ -30,13 +30,13 @@
},
"speedtest": {
"ping": "Ping",
"upload": "Càrrega",
"upload": "Pujada",
"download": "Descàrrega"
},
"resources": {
"total": "Total",
"free": "Lliure",
"used": "Usat",
"used": "Utilitzat",
"load": "Càrrega",
"cpu": "CPU"
},
@@ -47,13 +47,13 @@
"cpu": "Processador",
"offline": "Fora de línia",
"error": "Error",
"unknown": "Unknown"
"unknown": "Desconegut"
},
"emby": {
"playing": "Reproduint",
"transcoding": "Transcodificant",
"bitrate": "Taxa de bits",
"no_active": "Sense transmissions actives"
"no_active": "Sense reproduccions actives"
},
"tautulli": {
"playing": "Reproduint",
@@ -73,14 +73,14 @@
},
"rutorrent": {
"active": "Actiu",
"upload": "Càrrega",
"upload": "Pujada",
"download": "Descàrrega"
},
"radarr": {
"wanted": "Volgut",
"queued": "En cua",
"movies": "Pel·lícules",
"missing": "Missing"
"missing": "Faltant"
},
"readarr": {
"wanted": "Volgut",
@@ -101,7 +101,7 @@
"pending": "Pendent",
"approved": "Aprovat",
"available": "Disponible",
"processing": "Processing"
"processing": "Processant"
},
"pihole": {
"queries": "Consultes",
@@ -163,8 +163,8 @@
},
"qbittorrent": {
"download": "Descàrrega",
"upload": "Càrrega",
"leech": "Companys",
"upload": "Pujada",
"leech": "Company",
"seed": "Llavors"
},
"mastodon": {
@@ -184,26 +184,26 @@
"failedLoginsLast24H": "Errors d'inici de sessió (24h)"
},
"proxmox": {
"vms": "VMs",
"vms": "Màquines Virtuals",
"mem": "Memòria",
"cpu": "Processador",
"lxc": "LXC"
},
"unifi": {
"users": "Usuaris",
"uptime": "System Uptime",
"days": "Días",
"uptime": "Temps actiu",
"days": "Dies",
"wan": "WAN",
"lan_users": "LAN Users",
"wlan_users": "WLAN Users",
"up": "UP",
"down": "DOWN",
"lan_users": "Usuaris LAN",
"wlan_users": "Usuaris WLAN",
"up": "ACTIU",
"down": "INACTIU",
"wait": "Si us plau, espereu",
"lan": "LAN",
"wlan": "WLAN",
"devices": "Devices",
"lan_devices": "LAN Devices",
"wlan_devices": "WLAN Devices"
"devices": "Dispositius",
"lan_devices": "Dispositius LAN",
"wlan_devices": "Dispositius WLAN"
},
"plex": {
"streams": "Transmissions actives",
@@ -216,119 +216,119 @@
"wait": "Si us plau, espereu"
},
"changedetectionio": {
"totalObserved": "Total Observed",
"diffsDetected": "Diffs Detected"
"totalObserved": "Total d'observats",
"diffsDetected": "Diferències detectades"
},
"wmo": {
"66-day": "Freezing Rain",
"95-day": "Thunderstorm",
"95-night": "Thunderstorm",
"96-day": "Thunderstorm With Hail",
"0-day": "Sunny",
"0-night": "Clear",
"1-day": "Mainly Sunny",
"1-night": "Mainly Clear",
"2-day": "Partly Cloudy",
"2-night": "Partly Cloudy",
"3-day": "Cloudy",
"3-night": "Cloudy",
"45-day": "Foggy",
"45-night": "Foggy",
"48-day": "Foggy",
"48-night": "Foggy",
"51-day": "Light Drizzle",
"51-night": "Light Drizzle",
"53-day": "Drizzle",
"53-night": "Drizzle",
"55-day": "Heavy Drizzle",
"55-night": "Heavy Drizzle",
"56-day": "Light Freezing Drizzle",
"56-night": "Light Freezing Drizzle",
"65-night": "Heavy Rain",
"57-day": "Freezing Drizzle",
"57-night": "Freezing Drizzle",
"61-day": "Light Rain",
"61-night": "Light Rain",
"63-day": "Rain",
"63-night": "Rain",
"65-day": "Heavy Rain",
"66-night": "Freezing Rain",
"67-day": "Freezing Rain",
"67-night": "Freezing Rain",
"71-day": "Light Snow",
"71-night": "Light Snow",
"73-day": "Snow",
"73-night": "Snow",
"75-day": "Heavy Snow",
"75-night": "Heavy Snow",
"77-day": "Snow Grains",
"77-night": "Snow Grains",
"80-day": "Light Showers",
"80-night": "Light Showers",
"81-day": "Showers",
"81-night": "Showers",
"82-day": "Heavy Showers",
"82-night": "Heavy Showers",
"85-day": "Snow Showers",
"85-night": "Snow Showers",
"86-day": "Snow Showers",
"86-night": "Snow Showers",
"96-night": "Thunderstorm With Hail",
"99-day": "Thunderstorm With Hail",
"99-night": "Thunderstorm With Hail"
"66-day": "Pluja gelada",
"95-day": "Tempesta",
"95-night": "Tempesta",
"96-day": "Tempesta amb calamarsa",
"0-day": "Assolellat",
"0-night": "Cel clar",
"1-day": "Majorment assolellat",
"1-night": "Majorment clar",
"2-day": "Parcialment ennuvolat",
"2-night": "Parcialment ennuvolat",
"3-day": "Ennuvolat",
"3-night": "Ennuvolat",
"45-day": "Boirós",
"45-night": "Boirós",
"48-day": "Boirós",
"48-night": "Boirós",
"51-day": "Ruixats lleugers",
"51-night": "Ruixats lleugers",
"53-day": "Ruixat",
"53-night": "Ruxiat",
"55-day": "Ruixat intens",
"55-night": "Ruixat intens",
"56-day": "Lleuger ruixat gelat",
"56-night": "Lleuger ruixat gelat",
"65-night": "Pluja intensa",
"57-day": "Ruixat gelat",
"57-night": "Ruixat gelat",
"61-day": "Pluja lleugera",
"61-night": "Pluja lleugera",
"63-day": "Pluja",
"63-night": "Pluja",
"65-day": "Pluja intensa",
"66-night": "Pluja gelada",
"67-day": "Pluja gelada",
"67-night": "Pluja gelada",
"71-day": "Neu lleugera",
"71-night": "Neu lleugera",
"73-day": "Neu",
"73-night": "Neu",
"75-day": "Neu intensa",
"75-night": "Neu intensa",
"77-day": "Neu lleugera",
"77-night": "Neu lleugera",
"80-day": "Plovisqueig",
"80-night": "Plovisqueig",
"81-day": "Xàfecs",
"81-night": "Xàfecs",
"82-day": "Xàfecs intensos",
"82-night": "Xàfecs intensos",
"85-day": "Xàfecs de neu",
"85-night": "Xàfecs de neu",
"86-day": "Xàfecs de neu",
"86-night": "Xàfecs de neu",
"96-night": "Tempesta amb calamarsa",
"99-day": "Tempesta amb calamarsa",
"99-night": "Tempesta amb calamarsa"
},
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
"bookmark": "Marcador",
"service": "Servei"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"available_update": "Sistema",
"updates": "Actualitzacions",
"update_available": "Actualització disponible",
"up_to_date": "Actualitzat",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
"approvedPushes": "Aprovat",
"rejectedPushes": "Rebutjat",
"filters": "Filtres",
"indexers": "Indexadors"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
"containers_scanned": "Escanejat",
"containers_updated": "Actualitzat",
"containers_failed": "Error"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
"downloads": "Cua",
"videos": "Vídeos",
"channels": "Canals",
"playlists": "Llistes de reproducció"
},
"truenas": {
"load": "System Load",
"uptime": "Uptime",
"alerts": "Alerts",
"load": "Càrrega del sistema",
"uptime": "Temps actiu",
"alerts": "Alertes",
"time": "{{value, number(style: unit; unitDisplay: long;)}}"
},
"navidrome": {
"nothing_streaming": "No Active Streams",
"please_wait": "Please Wait"
"nothing_streaming": "Cap reproducció activa",
"please_wait": "Espereu si us plau"
},
"pyload": {
"speed": "Speed",
"active": "Active",
"queue": "Queue",
"speed": "Velocitat",
"active": "Actiu",
"queue": "Cua",
"total": "Total"
},
"gluetun": {
"public_ip": "Public IP",
"region": "Region",
"country": "Country"
"public_ip": "IP Pública",
"region": "Regió",
"country": "País"
},
"hdhomerun": {
"channels": "Channels",
"channels": "Canals",
"hd": "HD"
},
"ping": {
@@ -336,24 +336,48 @@
"ping": "Ping"
},
"scrutiny": {
"passed": "Passed",
"failed": "Failed",
"unknown": "Unknown"
"passed": "Aprobat",
"failed": "Error",
"unknown": "Desconegut"
},
"paperlessngx": {
"inbox": "Inbox",
"inbox": "Safata d'entrada",
"total": "Total"
},
"deluge": {
"seed": "Seed",
"download": "Download",
"upload": "Upload",
"leech": "Leech"
"seed": "Llavor",
"download": "Descàrrega",
"upload": "Pujada",
"leech": "Company"
},
"diskstation": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
"download": "Descàrrega",
"upload": "Pujada",
"leech": "Company",
"seed": "Llavor"
},
"flood": {
"download": "Descarregar",
"upload": "Pujada",
"leech": "Company",
"seed": "Llavor"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"leech": "Leech",
"download": "Download",
"upload": "Upload",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -6,7 +6,7 @@
"url": "URL",
"information": "Information",
"raw_error": "Raw Error",
"response_data": "Response Data"
"response_data": "Empfangene Daten"
},
"search": {
"placeholder": "Suche…"
@@ -355,5 +355,29 @@
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"unread": "Unread",
"read": "Read"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -3,9 +3,11 @@
"bytes": "{{value, bytes}}",
"bits": "{{value, bytes(bits: true)}}",
"bbytes": "{{value, bytes(binary: true)}}",
"bbits": "{{value, bytes(bits: true, binary: true)}}",
"byterate": "{{value, rate}}",
"bbits": "{{value, bytes(bits: true; binary: true)}}",
"byterate": "{{value, rate(bits: false)}}",
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bitrate": "{{value, rate(bits: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}",
"percent": "{{value, percent}}",
"number": "{{value, number}}",
"ms": "{{value, number}}"
@@ -70,6 +72,12 @@
"bitrate": "Bitrate",
"no_active": "No Active Streams"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"changedetectionio": {
"totalObserved": "Total Observed",
"diffsDetected": "Diffs Detected"
@@ -186,6 +194,12 @@
"stopped": "Stopped",
"total": "Total"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"traefik": {
"routers": "Routers",
"services": "Services",
@@ -234,6 +248,10 @@
"status_count": "Posts",
"domain_count": "Domains"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"authentik": {
"users": "Users",
"loginsLast24H": "Logins (24h)",
@@ -366,5 +384,9 @@
"paperlessngx": {
"inbox": "Inbox",
"total": "Total"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
}
}
}

View File

@@ -355,5 +355,29 @@
"leech": "Leech",
"upload": "Upload",
"seed": "Seed"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -105,7 +105,7 @@
"pending": "Pendiente",
"approved": "Aprobado",
"available": "Disponible",
"processing": "Processing"
"processing": "Procesando"
},
"sabnzbd": {
"rate": "Tasa",
@@ -139,7 +139,7 @@
"transmission": {
"download": "Bajada",
"upload": "Subida",
"leech": "Compañeros",
"leech": "Leech",
"seed": "Semillas"
},
"jackett": {
@@ -164,7 +164,7 @@
"qbittorrent": {
"download": "Bajada",
"upload": "Subida",
"leech": "Compañeros",
"leech": "Leech",
"seed": "Semillas"
},
"mastodon": {
@@ -351,9 +351,33 @@
"seed": "Semilla"
},
"diskstation": {
"download": "Download",
"upload": "Upload",
"download": "Descargar",
"upload": "Cargar",
"leech": "Leech",
"seed": "Semilla"
},
"flood": {
"download": "Descargar",
"upload": "Subir",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"saved": "Saved",
"errored": "Errored"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -351,9 +351,33 @@
"seed": "Seed"
},
"diskstation": {
"download": "Download",
"upload": "Upload",
"download": "Réception",
"upload": "Envoi",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"download": "Récep.",
"upload": "Envoi",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "À traiter",
"processed": "Traité",
"errored": "En erreur",
"saved": "Enregistré"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibitrate": "{{value, rate(bits: true; binary: true)}}",
"bibyterate": "{{value, rate(bits: false; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"leech": "Leech",
"download": "Download",
"seed": "Seed"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -24,7 +24,7 @@
"available": "Dostupno",
"pending": "Predstoji",
"approved": "Odobreno",
"processing": "Processing"
"processing": "Obrada"
},
"pihole": {
"queries": "Upiti",
@@ -345,15 +345,39 @@
"total": "Ukupno"
},
"deluge": {
"download": "Download",
"upload": "Upload",
"download": "Preuzimanje",
"upload": "Prijenos",
"leech": "Leech",
"seed": "Seed"
},
"diskstation": {
"download": "Download",
"upload": "Upload",
"download": "Preuzimanje",
"upload": "Prijenos",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"download": "Preuzimanje",
"upload": "Prijenos",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"unread": "Unread",
"read": "Read"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"download": "Download",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"download": "Download",
"upload": "Upload",
"seed": "Seed"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -127,7 +127,7 @@
"pending": "Oczekiwane",
"approved": "Zaakceptowane",
"available": "Dostępne",
"processing": "Processing"
"processing": "Przetwarzane"
},
"pihole": {
"queries": "Zapytania",
@@ -336,24 +336,48 @@
"ping": "Ping"
},
"scrutiny": {
"passed": "Passed",
"failed": "Failed",
"unknown": "Unknown"
"passed": "Powodzenie",
"failed": "Niepowodzenie",
"unknown": "Nieznane"
},
"paperlessngx": {
"inbox": "Inbox",
"total": "Total"
"inbox": "Skrzynka odbiorcza",
"total": "W sumie"
},
"deluge": {
"download": "Download",
"upload": "Upload",
"download": "Pobieranie",
"upload": "Wysyłanie",
"leech": "Leech",
"seed": "Seed"
},
"diskstation": {
"download": "Download",
"upload": "Upload",
"download": "Pobieranie",
"upload": "Wysyłanie",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"download": "Pobieranie",
"upload": "Wysyłanie",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -2,11 +2,11 @@
"widget": {
"missing_type": "Widget ausente: {{type}}",
"api_error": "Erro da API",
"status": "Status",
"information": "Information",
"url": "URL",
"raw_error": "Raw Error",
"response_data": "Response Data"
"status": "Estado",
"information": "Informação",
"url": "Endereço URL",
"raw_error": "Erro",
"response_data": "Dados da Resposta"
},
"search": {
"placeholder": "Pesquisar…"
@@ -24,8 +24,8 @@
"mem": "Mem",
"cpu": "CPU",
"offline": "Desligado",
"error": "Error",
"unknown": "Unknown"
"error": "Erro",
"unknown": "Desconhecido"
},
"emby": {
"playing": "A reproduzir",
@@ -104,7 +104,9 @@
"byterate": "{{value, bytes}}",
"ms": "{{value, number}}",
"bitrate": "{{value, bytes(bits: true)}}",
"percent": "{{value, percent}}"
"percent": "{{value, percent}}",
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
},
"weather": {
"current": "Localização atual",
@@ -150,7 +152,7 @@
"transmission": {
"download": "Baixando",
"upload": "Enviando",
"leech": "Sanguessugas",
"leech": "Leech",
"seed": "Semeadores"
},
"jackett": {
@@ -201,55 +203,55 @@
"vms": "VMs"
},
"unifi": {
"users": "Users",
"uptime": "System Uptime",
"days": "Days",
"users": "Utilizadores",
"uptime": "Tempo de Atividade do Sistema",
"days": "Dias",
"wan": "WAN",
"lan_users": "LAN Users",
"wlan_users": "WLAN Users",
"up": "UP",
"down": "DOWN",
"wait": "Please wait",
"lan_users": "Utilizadores LAN",
"wlan_users": "Utilizadores WLAN",
"up": "Ligados",
"down": "Desligados",
"wait": "Por favor, aguarde",
"lan": "LAN",
"wlan": "WLAN",
"devices": "Devices",
"lan_devices": "LAN Devices",
"wlan_devices": "WLAN Devices"
"devices": "Dispositivos",
"lan_devices": "Dispositivos LAN",
"wlan_devices": "Dispositivos WLAN"
},
"plex": {
"streams": "Active Streams",
"movies": "Movies",
"tv": "TV Shows"
"streams": "Streams Ativas",
"movies": "Filmes",
"tv": "Series de TV"
},
"glances": {
"cpu": "CPU",
"mem": "MEM",
"wait": "Please wait"
"wait": "Por favor, aguarde"
},
"changedetectionio": {
"totalObserved": "Total Observed",
"diffsDetected": "Diffs Detected"
"totalObserved": "Total Observado",
"diffsDetected": "Diferenças Detetadas"
},
"wmo": {
"0-day": "Sunny",
"0-night": "Clear",
"1-day": "Mainly Sunny",
"1-night": "Mainly Clear",
"2-day": "Partly Cloudy",
"2-night": "Partly Cloudy",
"3-day": "Cloudy",
"3-night": "Cloudy",
"0-day": "Solarengo",
"0-night": "Limpo",
"1-day": "Maioritariamente ensolarado",
"1-night": "Maioritariamente Limpo",
"2-day": "Parcialmente Nublado",
"2-night": "Parcialmente nublado",
"3-day": "Nublado",
"3-night": "Nublado",
"99-night": "Thunderstorm With Hail",
"45-day": "Foggy",
"45-night": "Foggy",
"48-day": "Foggy",
"48-night": "Foggy",
"51-day": "Light Drizzle",
"51-night": "Light Drizzle",
"53-day": "Drizzle",
"53-night": "Drizzle",
"55-day": "Heavy Drizzle",
"55-night": "Heavy Drizzle",
"45-day": "Nevoeiro",
"45-night": "Nevoeiro",
"48-day": "Nevoeiro",
"48-night": "Nevoeiro",
"51-day": "Aguaceiros",
"51-night": "Aguaceiros",
"53-day": "Chuvisco",
"53-night": "Chuvisco",
"55-day": "Aguaceiro Forte",
"55-night": "Aguaceiro Forte",
"56-day": "Light Freezing Drizzle",
"56-night": "Light Freezing Drizzle",
"57-day": "Freezing Drizzle",
@@ -289,8 +291,8 @@
"99-day": "Thunderstorm With Hail"
},
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
"bookmark": "Marcador",
"service": "Serviço"
},
"homebridge": {
"available_update": "System",
@@ -343,7 +345,7 @@
"hd": "HD"
},
"ping": {
"error": "Error",
"error": "Erro",
"ping": "Ping"
},
"scrutiny": {
@@ -366,5 +368,25 @@
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"download": "Descarregar",
"upload": "Carregar",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
}
}

View File

@@ -355,5 +355,29 @@
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"upload": "Upload",
"seed": "Seed",
"leech": "Leech"
},
"flood": {
"upload": "Upload",
"download": "Download",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"download": "Download",
"seed": "Seed",
"upload": "Upload",
"leech": "Leech"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"download": "Download",
"upload": "Upload",
"seed": "Seed"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"download": "Download",
"upload": "Upload",
"seed": "Seed",
"leech": "Leech"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"leech": "Leech",
"download": "Download",
"upload": "Upload",
"seed": "Seed"
},
"tdarr": {
"saved": "Saved",
"queue": "Queue",
"processed": "Processed",
"errored": "Errored"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -355,5 +355,29 @@
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
}
}

View File

@@ -107,7 +107,8 @@ export default function QuickLaunch({servicesAndBookmarks, searchString, setSear
function highlightText(text) {
const parts = text.split(new RegExp(`(${searchString})`, 'gi'));
return <span>{parts.map(part => part.toLowerCase() === searchString.toLowerCase() ? <span className="bg-theme-300/10">{part}</span> : part)}</span>;
// eslint-disable-next-line react/no-array-index-key
return <span>{parts.map((part, i) => part.toLowerCase() === searchString.toLowerCase() ? <span key={`${searchString}_${i}`} className="bg-theme-300/10">{part}</span> : part)}</span>;
}
return (

View File

@@ -1,9 +1,9 @@
import Image from "next/future/image";
export default function ResolvedIcon({ icon }) {
export default function ResolvedIcon({ icon, width = 32, height = 32 }) {
// direct or relative URLs
if (icon.startsWith("http") || icon.startsWith("/")) {
return <Image src={`${icon}`} width={32} height={32} alt="logo" />;
return <Image src={`${icon}`} width={width} height={height} alt="logo" />;
}
// mdi- prefixed, material design icons
@@ -12,8 +12,8 @@ export default function ResolvedIcon({ icon }) {
return (
<div
style={{
width: 32,
height: 32,
width,
height,
maxWidth: '100%',
maxHeight: '100%',
background: "linear-gradient(180deg, rgb(var(--color-logo-start)), rgb(var(--color-logo-stop)))",
@@ -29,8 +29,8 @@ export default function ResolvedIcon({ icon }) {
return (
<Image
src={`https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${iconName}.png`}
width={32}
height={32}
width={width}
height={height}
alt="logo"
/>
);

View File

@@ -1,56 +1,62 @@
export default function Logo() {
import ResolvedIcon from "components/resolvedicon"
export default function Logo({ options }) {
return (
<div className="w-12 h-12 flex flex-row items-center align-middle mr-3 self-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1024 1024"
style={{
enableBackground: "new 0 0 1024 1024",
}}
xmlSpace="preserve"
className="w-full h-full"
>
<style>
{
".st0{display:none}.st3{stroke-linecap:square}.st3,.st4{fill:none;stroke:#fff;stroke-miterlimit:10}.st6{display:inline;fill:#333}.st7{fill:#fff}"
}
</style>
<g id="Icon">
<path
d="M771.9 191c27.7 0 50.1 26.5 50.1 59.3v186.4l-100.2.3V250.3c0-32.8 22.4-59.3 50.1-59.3z"
style={{
fill: "rgba(var(--color-logo-start))",
}}
/>
<linearGradient
id="homepage_logo_gradient"
gradientUnits="userSpaceOnUse"
x1={200.746}
y1={225.015}
x2={764.986}
y2={789.255}
>
<stop
offset={0}
{options.icon ?
<ResolvedIcon icon={options.icon} width={48} height={48} /> :
// fallback to homepage logo
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1024 1024"
style={{
enableBackground: "new 0 0 1024 1024",
}}
xmlSpace="preserve"
className="w-full h-full"
>
<style>
{
".st0{display:none}.st3{stroke-linecap:square}.st3,.st4{fill:none;stroke:#fff;stroke-miterlimit:10}.st6{display:inline;fill:#333}.st7{fill:#fff}"
}
</style>
<g id="Icon">
<path
d="M771.9 191c27.7 0 50.1 26.5 50.1 59.3v186.4l-100.2.3V250.3c0-32.8 22.4-59.3 50.1-59.3z"
style={{
stopColor: "rgba(var(--color-logo-start))",
fill: "rgba(var(--color-logo-start))",
}}
/>
<stop
offset={1}
<linearGradient
id="homepage_logo_gradient"
gradientUnits="userSpaceOnUse"
x1={200.746}
y1={225.015}
x2={764.986}
y2={789.255}
>
<stop
offset={0}
style={{
stopColor: "rgba(var(--color-logo-start))",
}}
/>
<stop
offset={1}
style={{
stopColor: "rgba(var(--color-logo-stop))",
}}
/>
</linearGradient>
<path
d="M721.8 250.3c0-32.7 22.4-59.3 50.1-59.3H253.1c-27.7 0-50.1 26.5-50.1 59.3v582.2l90.2-75.7-.1-130.3H375v61.8l88-73.8 258.8 217.9V250.6"
style={{
stopColor: "rgba(var(--color-logo-stop))",
fill: "url(#homepage_logo_gradient)",
}}
/>
</linearGradient>
<path
d="M721.8 250.3c0-32.7 22.4-59.3 50.1-59.3H253.1c-27.7 0-50.1 26.5-50.1 59.3v582.2l90.2-75.7-.1-130.3H375v61.8l88-73.8 258.8 217.9V250.6"
style={{
fill: "url(#homepage_logo_gradient)",
}}
/>
</g>
</svg>
</g>
</svg>
}
</div>
);
)
}

View File

@@ -1,4 +1,4 @@
import mapIcon from "utils/weather/owm-condition-map";
import mapIcon from "utils/weather/openmeteo-condition-map";
export default function Icon({ condition, timeOfDay }) {
const IconComponent = mapIcon(condition, timeOfDay);

View File

@@ -14,7 +14,8 @@ export default async function handler(req, res) {
}
try {
const docker = new Docker(getDockerArguments(containerServer));
const dockerArgs = getDockerArguments(containerServer);
const docker = new Docker(dockerArgs.conn);
const containers = await docker.listContainers({
all: true,
});
@@ -31,18 +32,44 @@ export default async function handler(req, res) {
const containerNames = containers.map((container) => container.Names[0].replace(/^\//, ""));
const containerExists = containerNames.includes(containerName);
if (!containerExists) {
res.status(200).send({
error: "not found",
if (containerExists) {
const container = docker.getContainer(containerName);
const stats = await container.stats({ stream: false });
res.status(200).json({
stats,
});
return;
}
const container = docker.getContainer(containerName);
const stats = await container.stats({ stream: false });
// Try with a service deployed in Docker Swarm, if enabled
if (dockerArgs.swarm) {
const tasks = await docker.listTasks({
filters: {
service: [containerName],
// A service can have several offline containers, so we only look for an active one.
"desired-state": ["running"],
},
})
.catch(() => []);
res.status(200).json({
stats,
// For now we are only interested in the first one (in case replicas > 1).
// TODO: Show the result for all replicas/containers?
const taskContainerId = tasks.at(0)?.Status?.ContainerStatus?.ContainerID;
if (taskContainerId) {
const container = docker.getContainer(taskContainerId);
const stats = await container.stats({ stream: false });
res.status(200).json({
stats,
});
return;
}
}
res.status(200).send({
error: "not found",
});
} catch {
res.status(500).send({

View File

@@ -13,7 +13,8 @@ export default async function handler(req, res) {
}
try {
const docker = new Docker(getDockerArguments(containerServer));
const dockerArgs = getDockerArguments(containerServer);
const docker = new Docker(dockerArgs.conn);
const containers = await docker.listContainers({
all: true,
});
@@ -29,18 +30,43 @@ export default async function handler(req, res) {
const containerNames = containers.map((container) => container.Names[0].replace(/^\//, ""));
const containerExists = containerNames.includes(containerName);
if (!containerExists) {
return res.status(200).send({
error: "not found",
if (containerExists) {
const container = docker.getContainer(containerName);
const info = await container.inspect();
return res.status(200).json({
status: info.State.Status,
health: info.State.Health?.Status,
});
}
const container = docker.getContainer(containerName);
const info = await container.inspect();
if (dockerArgs.swarm) {
const tasks = await docker.listTasks({
filters: {
service: [containerName],
// A service can have several offline containers, we only look for an active one.
"desired-state": ["running"],
},
})
.catch(() => []);
return res.status(200).json({
status: info.State.Status,
health: info.State.Health?.Status
// For now we are only interested in the first one (in case replicas > 1).
// TODO: Show the result for all replicas/containers?
const taskContainerId = tasks.at(0)?.Status?.ContainerStatus?.ContainerID;
if (taskContainerId) {
const container = docker.getContainer(taskContainerId);
const info = await container.inspect();
return res.status(200).json({
status: info.State.Status,
health: info.State.Health?.Status,
});
}
}
return res.status(200).send({
error: "not found",
});
} catch {
return res.status(500).send({

View File

@@ -1,8 +1,9 @@
import cachedFetch from "utils/proxy/cached-fetch";
export default async function handler(req, res) {
const { latitude, longitude, units, cache } = req.query;
const { latitude, longitude, units, cache, timezone } = req.query;
const degrees = units === "imperial" ? "fahrenheit" : "celsius";
const apiUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&daily=sunrise,sunset&current_weather=true&temperature_unit=${degrees}&timezone=auto`;
const timezeone = timezone ?? 'auto'
const apiUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&daily=sunrise,sunset&current_weather=true&temperature_unit=${degrees}&timezone=${timezeone}`;
return res.send(await cachedFetch(apiUrl, cache));
}

View File

@@ -22,11 +22,14 @@ export default function getDockerArguments(server) {
if (servers[server]) {
if (servers[server].socket) {
return { socketPath: servers[server].socket };
return { conn: { socketPath: servers[server].socket }, swarm: !!servers[server].swarm };
}
if (servers[server].host) {
return { host: servers[server].host, port: servers[server].port || null };
return {
conn: { host: servers[server].host, port: servers[server].port || null },
swarm: !!servers[server].swarm,
};
}
return servers[server];

View File

@@ -44,7 +44,7 @@ export async function servicesFromDocker() {
const serviceServers = await Promise.all(
Object.keys(servers).map(async (serverName) => {
const docker = new Docker(getDockerArguments(serverName));
const docker = new Docker(getDockerArguments(serverName).conn);
const containers = await docker.listContainers({
all: true,
});
@@ -118,6 +118,7 @@ export function cleanServiceGroups(groups) {
container,
currency, // coinmarketcap widget
symbols,
defaultinterval
} = cleanedService.widget;
cleanedService.widget = {
@@ -129,6 +130,7 @@ export function cleanServiceGroups(groups) {
if (currency) cleanedService.widget.currency = currency;
if (symbols) cleanedService.widget.symbols = symbols;
if (defaultinterval) cleanedService.widget.defaultinterval = defaultinterval;
if (type === "docker") {
if (server) cleanedService.widget.server = server;

View File

@@ -7,7 +7,7 @@ import widgets from "widgets/widgets";
const logger = createLogger("credentialedProxyHandler");
export default async function credentialedProxyHandler(req, res) {
export default async function credentialedProxyHandler(req, res, map) {
const { group, service, endpoint } = req.query;
if (group && service) {
@@ -36,6 +36,8 @@ export default async function credentialedProxyHandler(req, res) {
headers["X-API-Token"] = `${widget.key}`;
} else if (widget.type === "tubearchivist") {
headers.Authorization = `Token ${widget.key}`;
} else if (widget.type === "miniflux") {
headers["X-Auth-Token"] = `${widget.key}`;
} else {
headers["X-API-Key"] = `${widget.key}`;
}
@@ -47,6 +49,8 @@ export default async function credentialedProxyHandler(req, res) {
headers,
});
let resultData = data;
if (status === 204 || status === 304) {
return res.status(status).end();
}
@@ -59,8 +63,12 @@ export default async function credentialedProxyHandler(req, res) {
return res.status(500).json({error: {message: "Invalid data", url, data}});
}
if (status === 200 && map) {
resultData = map(data);
}
if (contentType) res.setHeader("Content-Type", contentType);
return res.status(status).send(data);
return res.status(status).send(resultData);
}
}

View File

@@ -0,0 +1,211 @@
import * as Icons from "react-icons/wi";
// see https://open-meteo.com/en/docs
const conditions = [
{
code: 1,
icon: {
day: Icons.WiDayCloudy,
night: Icons.WiNightAltCloudy,
},
},
{
code: 2,
icon: {
day: Icons.WiDayCloudy,
night: Icons.WiNightAltCloudy,
},
},
{
code: 3,
icon: {
day: Icons.WiDayCloudy,
night: Icons.WiNightAltCloudy,
},
},
{
code: 45,
icon: {
day: Icons.WiDayFog,
night: Icons.WiNightFog,
},
},
{
code: 48,
icon: {
day: Icons.WiDayFog,
night: Icons.WiNightFog,
},
},
{
code: 51,
icon: {
day: Icons.WiDaySprinkle,
night: Icons.WiNightAltSprinkle,
},
},
{
code: 53,
icon: {
day: Icons.WiDaySprinkle,
night: Icons.WiNightAltSprinkle,
},
},
{
code: 55,
icon: {
day: Icons.WiDaySprinkle,
night: Icons.WiNightAltSprinkle,
},
},
{
code: 56,
icon: {
day: Icons.WiDaySleet,
night: Icons.WiNightAltSleet,
},
},
{
code: 57,
icon: {
day: Icons.WiDaySleet,
night: Icons.WiNightAltSleet,
},
},
{
code: 61,
icon: {
day: Icons.WiDayShowers,
night: Icons.WiNightAltShowers,
},
},
{
code: 63,
icon: {
day: Icons.WiDayShowers,
night: Icons.WiNightAltShowers,
},
},
{
code: 65,
icon: {
day: Icons.WiDayShowers,
night: Icons.WiNightAltShowers,
},
},
{
code: 66,
icon: {
day: Icons.WiDaySleet,
night: Icons.WiNightAltSleet,
},
},
{
code: 67,
icon: {
day: Icons.WiDaySleet,
night: Icons.WiNightAltSleet,
},
},
{
code: 71,
icon: {
day: Icons.WiDaySnow,
night: Icons.WiNightAltSnow,
},
},
{
code: 73,
icon: {
day: Icons.WiDaySnow,
night: Icons.WiNightAltSnow,
},
},
{
code: 75,
icon: {
day: Icons.WiDaySnow,
night: Icons.WiNightAltSnow,
},
},
{
code: 77,
icon: {
day: Icons.WiDaySnow,
night: Icons.WiNightAltSnow,
},
},
{
code: 80,
icon: {
day: Icons.WiDaySnow,
night: Icons.WiNightAltSnow,
},
},
{
code: 81,
icon: {
day: Icons.WiDaySnow,
night: Icons.WiNightAltSnow,
},
},
{
code: 82,
icon: {
day: Icons.WiDaySnow,
night: Icons.WiNightAltSnow,
},
},
{
code: 85,
icon: {
day: Icons.WiDaySnow,
night: Icons.WiNightAltSnow,
},
},
{
code: 86,
icon: {
day: Icons.WiDaySnow,
night: Icons.WiNightAltSnow,
},
},
{
code: 95,
icon: {
day: Icons.WiDayThunderstorm,
night: Icons.WiNightAltThunderstorm,
},
},
{
code: 96,
icon: {
day: Icons.WiDayThunderstorm,
night: Icons.WiNightAltThunderstorm,
},
},
{
code: 99,
icon: {
day: Icons.WiDayThunderstorm,
night: Icons.WiNightAltThunderstorm,
},
},
];
export default function mapIcon(weatherStatusCode, timeOfDay) {
const mapping = conditions.find((condition) => condition.code === weatherStatusCode);
if (mapping) {
if (timeOfDay === "day") {
return mapping.icon.day;
}
if (timeOfDay === "night") {
return mapping.icon.night;
}
}
return Icons.WiDaySunny;
}

View File

@@ -17,11 +17,12 @@ export default function Component({ service }) {
{ label: t("coinmarketcap.30days"), value: "30d" },
];
const [dateRange, setDateRange] = useState(dateRangeOptions[0].value);
const { widget } = service;
const { symbols } = widget;
const currencyCode = widget.currency ?? "USD";
const interval = widget.defaultinterval ?? dateRangeOptions[0].value;
const [dateRange, setDateRange] = useState(interval);
const { data: statsData, error: statsError } = useWidgetAPI(widget, "v1/cryptocurrency/quotes/latest", {
symbol: `${symbols.join(",")}`,

View File

@@ -11,6 +11,7 @@ const components = {
diskstation: dynamic(() => import("./diskstation/component")),
docker: dynamic(() => import("./docker/component")),
emby: dynamic(() => import("./emby/component")),
flood: dynamic(() => import("./flood/component")),
gluetun: dynamic(() => import("./gluetun/component")),
gotify: dynamic(() => import("./gotify/component")),
hdhomerun: dynamic(() => import("./hdhomerun/component")),
@@ -20,7 +21,9 @@ const components = {
jellyseerr: dynamic(() => import("./jellyseerr/component")),
lidarr: dynamic(() => import("./lidarr/component")),
mastodon: dynamic(() => import("./mastodon/component")),
miniflux: dynamic(() => import("./miniflux/component")),
navidrome: dynamic(() => import("./navidrome/component")),
nextdns: dynamic(() => import("./nextdns/component")),
npm: dynamic(() => import("./npm/component")),
nzbget: dynamic(() => import("./nzbget/component")),
ombi: dynamic(() => import("./ombi/component")),
@@ -42,6 +45,7 @@ const components = {
speedtest: dynamic(() => import("./speedtest/component")),
strelaysrv: dynamic(() => import("./strelaysrv/component")),
tautulli: dynamic(() => import("./tautulli/component")),
tdarr: dynamic(() => import("./tdarr/component")),
traefik: dynamic(() => import("./traefik/component")),
transmission: dynamic(() => import("./transmission/component")),
tubearchivist: dynamic(() => import("./tubearchivist/component")),

View File

@@ -46,7 +46,9 @@ export default function Component({ service }) {
return (
<Container service={service}>
<Block label="docker.cpu" value={t("common.percent", { value: calculateCPUPercent(statsData.stats) })} />
<Block label="docker.mem" value={t("common.bytes", { value: statsData.stats.memory_stats.usage })} />
{statsData.stats.memory_stats.usage &&
<Block label="docker.mem" value={t("common.bytes", { value: statsData.stats.memory_stats.usage })} />
}
{network && (
<>
<Block label="docker.rx" value={t("common.bytes", { value: network.rx_bytes })} />

View File

@@ -0,0 +1,53 @@
import { useTranslation } from "next-i18next";
import Container from "components/services/widget/container";
import Block from "components/services/widget/block";
import useWidgetAPI from "utils/proxy/use-widget-api";
export default function Component({ service }) {
const { t } = useTranslation();
const { widget } = service;
const { data: torrentData, error: torrentError } = useWidgetAPI(widget, "torrents");
if (torrentError || !torrentData?.torrents) {
return <Container error={torrentError ?? {message: "No torrent data returned"}} />;
}
if (!torrentData || !torrentData.torrents) {
return (
<Container service={service}>
<Block label="flood.leech" />
<Block label="flood.download" />
<Block label="flood.seed" />
<Block label="flood.upload" />
</Container>
);
}
let rateDl = 0;
let rateUl = 0;
let completed = 0;
let leech = 0;
Object.values(torrentData.torrents).forEach(torrent => {
rateDl += torrent.downRate;
rateUl += torrent.upRate;
if(torrent.status.includes('complete')){
completed += 1;
}
if(torrent.status.includes('downloading')){
leech += 1;
}
})
return (
<Container service={service}>
<Block label="flood.leech" value={t("common.number", { value: leech })} />
<Block label="flood.download" value={t("common.bitrate", { value: rateDl })} />
<Block label="flood.seed" value={t("common.number", { value: completed })} />
<Block label="flood.upload" value={t("common.bitrate", { value: rateUl })} />
</Container>
);
}

View File

@@ -0,0 +1,66 @@
import { formatApiCall } from "utils/proxy/api-helpers";
import { httpProxy } from "utils/proxy/http";
import getServiceWidget from "utils/config/service-helpers";
import createLogger from "utils/logger";
const logger = createLogger("floodProxyHandler");
async function login(widget) {
logger.debug("flood is rejecting the request, logging in.");
const loginUrl = new URL(`${widget.url}/api/auth/authenticate`).toString();
const loginParams = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: null
};
if (widget.username && widget.password) {
loginParams.body = JSON.stringify({
"username": widget.username,
"password": widget.password
});
}
// eslint-disable-next-line no-unused-vars
const [status, contentType, data] = await httpProxy(loginUrl, loginParams);
return [status, data];
}
export default async function floodProxyHandler(req, res) {
const { group, service, endpoint } = req.query;
if (!group || !service) {
logger.debug("Invalid or missing service '%s' or group '%s'", service, group);
return res.status(400).json({ error: "Invalid proxy service type" });
}
const widget = await getServiceWidget(group, service);
if (!widget) {
logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group);
return res.status(400).json({ error: "Invalid proxy service type" });
}
const url = new URL(formatApiCall("{url}/api/{endpoint}", { endpoint, ...widget }));
const params = { method: "GET", headers: {} };
let [status, contentType, data] = await httpProxy(url, params);
if (status === 401) {
[status, data] = await login(widget);
if (status !== 200) {
logger.error("HTTP %d logging in to flood. Data: %s", status, data);
return res.status(status).end(data);
}
[status, contentType, data] = await httpProxy(url, params);
}
if (status !== 200) {
logger.error("HTTP %d getting data from flood. Data: %s", status, data);
}
if (contentType) res.setHeader("Content-Type", contentType);
return res.status(status).send(data);
}

View File

@@ -0,0 +1,7 @@
import floodProxyHandler from "./proxy";
const widget = {
proxyHandler: floodProxyHandler,
};
export default widget;

View File

@@ -10,7 +10,7 @@ const proxyName = "homebridgeProxyHandler";
const sessionTokenCacheKey = `${proxyName}__sessionToken`;
const logger = createLogger(proxyName);
async function login(widget) {
async function login(widget, service) {
const endpoint = "auth/login";
const api = widgets?.[widget.type]?.api
const loginUrl = new URL(formatApiCall(api, { endpoint, ...widget }));
@@ -26,7 +26,7 @@ async function login(widget) {
try {
const { access_token: accessToken, expires_in: expiresIn } = JSON.parse(data.toString());
cache.put(sessionTokenCacheKey, accessToken, (expiresIn * 1000) - 5 * 60 * 1000); // expiresIn (s) - 5m
cache.put(`${sessionTokenCacheKey}.${service}`, accessToken, (expiresIn * 1000) - 5 * 60 * 1000); // expiresIn (s) - 5m
return { accessToken };
} catch (e) {
logger.error("Unable to login to Homebridge API: %s", e);
@@ -35,10 +35,11 @@ async function login(widget) {
return { accessToken: false };
}
async function apiCall(widget, endpoint) {
async function apiCall(widget, endpoint, service) {
const key = `${sessionTokenCacheKey}.${service}`;
const headers = {
"content-type": "application/json",
"Authorization": `Bearer ${cache.get(sessionTokenCacheKey)}`,
"Authorization": `Bearer ${cache.get(key)}`,
}
const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
@@ -51,7 +52,7 @@ async function apiCall(widget, endpoint) {
if (status === 401) {
logger.debug("Homebridge API rejected the request, attempting to obtain new session token");
const { accessToken } = login(widget);
const { accessToken } = login(widget, service);
headers.Authorization = `Bearer ${accessToken}`;
// retry the request, now with the new session token
@@ -83,14 +84,14 @@ export default async function homebridgeProxyHandler(req, res) {
return res.status(400).json({ error: "Invalid proxy service type" });
}
if (!cache.get(sessionTokenCacheKey)) {
await login(widget);
if (!cache.get(`${sessionTokenCacheKey}.${service}`)) {
await login(widget, service);
}
const { data: statusData } = await apiCall(widget, "status/homebridge");
const { data: versionData } = await apiCall(widget, "status/homebridge-version");
const { data: childBridgeData } = await apiCall(widget, "status/homebridge/child-bridges");
const { data: pluginsData } = await apiCall(widget, "plugins");
const { data: statusData } = await apiCall(widget, "status/homebridge", service);
const { data: versionData } = await apiCall(widget, "status/homebridge-version", service);
const { data: childBridgeData } = await apiCall(widget, "status/homebridge/child-bridges", service);
const { data: pluginsData } = await apiCall(widget, "plugins", service);
return res.status(200).send({
status: statusData?.status,

View File

@@ -0,0 +1,33 @@
import { useTranslation } from "next-i18next";
import Container from "components/services/widget/container";
import Block from "components/services/widget/block";
import useWidgetAPI from "utils/proxy/use-widget-api";
export default function Component({ service }) {
const { t } = useTranslation();
const { widget } = service;
const { data: minifluxData, error: minifluxError } = useWidgetAPI(widget, "counters");
if (minifluxError) {
return <Container error={minifluxError} />;
}
if (!minifluxData) {
return (
<Container service={service}>
<Block label="miniflux.unread" />
<Block label="miniflux.read" />
</Container>
);
}
return (
<Container service={service}>
<Block label="miniflux.unread" value={t("common.number", { value: minifluxData.unread })} />
<Block label="miniflux.read" value={t("common.number", { value: minifluxData.read })} />
</Container>
);
}

View File

@@ -0,0 +1,19 @@
import { asJson } from "utils/proxy/api-helpers";
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
const widget = {
api: "{url}/v1/{endpoint}",
proxyHandler: credentialedProxyHandler,
mappings: {
counters: {
endpoint: "feeds/counters",
map: (data) => ({
read: Object.values(asJson(data).reads).reduce((acc, i) => acc + i, 0),
unread: Object.values(asJson(data).unreads).reduce((acc, i) => acc + i, 0)
}),
},
}
};
export default widget;

View File

@@ -0,0 +1,39 @@
import { useTranslation } from "next-i18next";
import Container from "components/services/widget/container";
import Block from "components/services/widget/block";
import useWidgetAPI from "utils/proxy/use-widget-api";
export default function Component({ service }) {
const { t } = useTranslation();
const { widget } = service;
const { data: nextdnsData, error: nextdnsError } = useWidgetAPI(widget, "analytics/status");
if (nextdnsError) {
return <Container error={nextdnsError} />;
}
if (!nextdnsData) {
return (
<Container service={service}>
<Block key="status" label="widget.status" value={t("nextdns.wait")} />
</Container>
);
}
if (!nextdnsData?.data?.length) {
return (
<Container service={service}>
<Block key="status" label="widget.status" value={t("nextdns.no_devices")} />
</Container>
);
}
return (
<Container service={service}>
{nextdnsData.data.map(d => <Block key={d.status} label={d.status} value={t("common.number", { value: d.queries })} />)}
</Container>
);
}

View File

@@ -0,0 +1,17 @@
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
const widget = {
api: "https://api.nextdns.io/profiles/{profile}/{endpoint}",
proxyHandler: credentialedProxyHandler,
mappings: {
"analytics/status": {
endpoint: "analytics/status",
validate: [
"data",
]
},
},
};
export default widget;

View File

@@ -10,7 +10,7 @@ const proxyName = "npmProxyHandler";
const tokenCacheKey = `${proxyName}__token`;
const logger = createLogger(proxyName);
async function login(loginUrl, username, password) {
async function login(loginUrl, username, password, service) {
const authResponse = await httpProxy(loginUrl, {
method: "POST",
body: JSON.stringify({ identity: username, secret: password }),
@@ -27,7 +27,7 @@ async function login(loginUrl, username, password) {
if (status === 200) {
const expiration = new Date(data.expires) - Date.now();
cache.put(tokenCacheKey, data.token, expiration - (5 * 60 * 1000)); // expiration -5 minutes
cache.put(`${tokenCacheKey}.${service}`, data.token, expiration - (5 * 60 * 1000)); // expiration -5 minutes
}
} catch (e) {
logger.error(`Error ${status} logging into npm`, authResponse[2]);
@@ -53,9 +53,9 @@ export default async function npmProxyHandler(req, res) {
let contentType;
let data;
let token = cache.get(tokenCacheKey);
let token = cache.get(`${tokenCacheKey}.${service}`);
if (!token) {
[status, token] = await login(loginUrl, widget.username, widget.password);
[status, token] = await login(loginUrl, widget.username, widget.password, service);
if (status !== 200) {
logger.debug(`HTTTP ${status} logging into npm api: ${token}`);
return res.status(status).send(token);
@@ -72,8 +72,8 @@ export default async function npmProxyHandler(req, res) {
if (status === 403) {
logger.debug(`HTTTP ${status} retrieving data from npm api, logging in and trying again.`);
cache.del(tokenCacheKey);
[status, token] = await login(loginUrl, widget.username, widget.password);
cache.del(`${tokenCacheKey}.${service}`);
[status, token] = await login(loginUrl, widget.username, widget.password, service);
if (status !== 200) {
logger.debug(`HTTTP ${status} logging into npm api: ${data}`);

View File

@@ -58,6 +58,9 @@ async function fetchFromPlexAPI(endpoint, widget) {
export default async function plexProxyHandler(req, res) {
const widget = await getWidget(req);
const { service } = req.query;
if (!widget) {
return res.status(400).json({ error: "Invalid proxy service type" });
}
@@ -74,23 +77,24 @@ export default async function plexProxyHandler(req, res) {
streams = apiData.MediaContainer._attributes.size;
}
let libraries = cache.get(librariesCacheKey);
let libraries = cache.get(`${librariesCacheKey}.${service}`);
if (libraries === null) {
logger.debug("Getting libraries from Plex API");
[status, apiData] = await fetchFromPlexAPI("/library/sections", widget);
if (apiData && apiData.MediaContainer) {
libraries = apiData.MediaContainer.Directory;
cache.put(librariesCacheKey, libraries, 1000 * 60 * 60 * 6);
libraries = [].concat(apiData.MediaContainer.Directory);
cache.put(`${librariesCacheKey}.${service}`, libraries, 1000 * 60 * 60 * 6);
}
}
let movies = cache.get(moviesCacheKey);
let tv = cache.get(tvCacheKey);
let movies = cache.get(`${moviesCacheKey}.${service}`);
let tv = cache.get(`${tvCacheKey}.${service}`);
if (movies === null || tv === null) {
movies = 0;
tv = 0;
logger.debug("Getting movie + tv counts from Plex API");
libraries.filter(l => ["movie", "show"].includes(l._attributes.type)).forEach(async (library) => {
const movieTVLibraries = libraries.filter(l => ["movie", "show"].includes(l._attributes.type));
await Promise.all(movieTVLibraries.map(async (library) => {
[status, apiData] = await fetchFromPlexAPI(`/library/sections/${library._attributes.key}/all`, widget);
if (apiData && apiData.MediaContainer) {
const size = parseInt(apiData.MediaContainer._attributes.size, 10);
@@ -100,9 +104,9 @@ export default async function plexProxyHandler(req, res) {
tv += size;
}
}
cache.put(tvCacheKey, tv, 1000 * 60 * 10);
cache.put(moviesCacheKey, movies, 1000 * 60 * 10);
});
}));
cache.put(`${tvCacheKey}.${service}`, tv, 1000 * 60 * 10);
cache.put(`${moviesCacheKey}.${service}`, movies, 1000 * 60 * 10);
}
const data = {

View File

@@ -11,7 +11,7 @@ const logger = createLogger(proxyName);
const sessionCacheKey = `${proxyName}__sessionId`;
const isNgCacheKey = `${proxyName}__isNg`;
async function fetchFromPyloadAPI(url, sessionId, params) {
async function fetchFromPyloadAPI(url, sessionId, params, service) {
const options = {
body: params
? Object.keys(params)
@@ -25,10 +25,10 @@ async function fetchFromPyloadAPI(url, sessionId, params) {
};
// see https://github.com/benphelps/homepage/issues/517
const isNg = cache.get(isNgCacheKey);
const isNg = cache.get(`${isNgCacheKey}.${service}`);
if (isNg && !params) {
delete options.body;
options.headers.Cookie = cache.get(sessionCacheKey);
options.headers.Cookie = cache.get(`${sessionCacheKey}.${service}`);
}
// eslint-disable-next-line no-unused-vars
@@ -43,19 +43,19 @@ async function fetchFromPyloadAPI(url, sessionId, params) {
return [status, returnData, responseHeaders];
}
async function login(loginUrl, username, password = '') {
const [status, sessionId, responseHeaders] = await fetchFromPyloadAPI(loginUrl, null, { username, password });
async function login(loginUrl, service, username, password = '') {
const [status, sessionId, responseHeaders] = await fetchFromPyloadAPI(loginUrl, null, { username, password }, service);
// this API actually returns status 200 even on login failure
if (status !== 200 || sessionId === false) {
logger.error(`HTTP ${status} logging into Pyload API, returned: ${JSON.stringify(sessionId)}`);
} else if (responseHeaders['set-cookie']?.join().includes('pyload_session')) {
// Support pyload-ng, see https://github.com/benphelps/homepage/issues/517
cache.put(isNgCacheKey, true);
cache.put(`${isNgCacheKey}.${service}`, true);
const sessionCookie = responseHeaders['set-cookie'][0];
cache.put(sessionCacheKey, sessionCookie, 60 * 60 * 23 * 1000); // cache for 23h
cache.put(`${sessionCacheKey}.${service}`, sessionCookie, 60 * 60 * 23 * 1000); // cache for 23h
} else {
cache.put(sessionCacheKey, sessionId);
cache.put(`${sessionCacheKey}.${service}`, sessionId);
}
return sessionId;
@@ -72,14 +72,14 @@ export default async function pyloadProxyHandler(req, res) {
const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
const loginUrl = `${widget.url}/api/login`;
let sessionId = cache.get(sessionCacheKey) ?? await login(loginUrl, widget.username, widget.password);
let [status, data] = await fetchFromPyloadAPI(url, sessionId);
let sessionId = cache.get(`${sessionCacheKey}.${service}`) ?? await login(loginUrl, service, widget.username, widget.password);
let [status, data] = await fetchFromPyloadAPI(url, sessionId, null, service);
if (status === 403 || status === 401) {
logger.info('Failed to retrieve data from Pyload API, trying to login again...');
cache.del(sessionCacheKey);
sessionId = await login(loginUrl, widget.username, widget.password);
[status, data] = await fetchFromPyloadAPI(url, sessionId);
cache.del(`${sessionCacheKey}.${service}`);
sessionId = await login(loginUrl, service, widget.username, widget.password);
[status, data] = await fetchFromPyloadAPI(url, sessionId, null, service);
}
if (data?.error || status !== 200) {

View File

@@ -44,9 +44,9 @@ export default function Component({ service }) {
return (
<Container service={service}>
<Block label="qbittorrent.leech" value={t("common.number", { value: leech })} />
<Block label="qbittorrent.download" value={t("common.bitrate", { value: rateDl })} />
<Block label="qbittorrent.download" value={t("common.bibyterate", { value: rateDl, decimals: 1 })} />
<Block label="qbittorrent.seed" value={t("common.number", { value: completed })} />
<Block label="qbittorrent.upload" value={t("common.bitrate", { value: rateUl })} />
<Block label="qbittorrent.upload" value={t("common.bibyterate", { value: rateUl, decimals: 1 })} />
</Container>
);
}

View File

@@ -2,16 +2,37 @@ import Container from "components/services/widget/container";
import Block from "components/services/widget/block";
import useWidgetAPI from "utils/proxy/use-widget-api";
// @see https://github.com/AnalogJ/scrutiny/blob/d8d56f77f9e868127c4849dac74d65512db658e8/webapp/frontend/src/app/shared/device-status.pipe.ts
const DeviceStatus = {
passed: 0,
failed_smart: 1,
failed_scrutiny: 2,
failed_both: 3,
isFailed(s){ return s > this.passed && s <= this.failed_both},
isUnknown(s){ return s < this.passed || s > this.failed_both}
}
// @see https://github.com/AnalogJ/scrutiny/blob/d8d56f77f9e868127c4849dac74d65512db658e8/webapp/frontend/src/app/core/config/app.config.ts
const DeviceStatusThreshold = {
smart: 1,
scrutiny: 2,
both: 3
}
export default function Component({ service }) {
const { widget } = service;
const { data: scrutinySettings, error: scrutinySettingsError } = useWidgetAPI(widget, "settings");
const { data: scrutinyData, error: scrutinyError } = useWidgetAPI(widget, "summary");
if (scrutinyError) {
return <Container error={scrutinyError} />;
if (scrutinyError || scrutinySettingsError) {
const finalError = scrutinyError ?? scrutinySettingsError;
return <Container error={finalError} />;
}
if (!scrutinyData) {
if (!scrutinyData || !scrutinySettings) {
return (
<Container service={service}>
<Block label="scrutiny.passed" />
@@ -19,13 +40,14 @@ export default function Component({ service }) {
<Block label="scrutiny.unknown" />
</Container>
);
}
}
const deviceIds = Object.values(scrutinyData.data.summary);
const passed = deviceIds.filter(deviceId => deviceId.device.device_status === 0)?.length || 0;
const failed = deviceIds.filter(deviceId => deviceId.device.device_status > 0 && deviceId.device.device_status <= 3)?.length || 0;
const unknown = deviceIds.length - (passed + failed) || 0;
const statusThreshold = scrutinySettings.settings.metrics.status_threshold;
const failed = deviceIds.filter(deviceId => (DeviceStatus.isFailed(deviceId.device.device_status) && statusThreshold === DeviceStatusThreshold.both) || [statusThreshold, DeviceStatus.failed_both].includes(deviceId.device.device_status))?.length || 0;
const unknown = deviceIds.filter(deviceId => DeviceStatus.isUnknown(deviceId.device.device_status))?.length || 0;
const passed = deviceIds.length - (failed + unknown);
return (
<Container service={service}>
@@ -33,5 +55,8 @@ export default function Component({ service }) {
<Block label="scrutiny.failed" value={failed} />
<Block label="scrutiny.unknown" value={unknown} />
</Container>
);
}

View File

@@ -11,6 +11,12 @@ const widget = {
"data",
]
},
settings: {
endpoint: "settings",
validate: [
"settings",
]
}
},
};

View File

@@ -29,9 +29,9 @@ export default function Component({ service }) {
<Container service={service}>
<Block
label="speedtest.download"
value={t("common.bitrate", { value: speedtestData.data.download * 1024 * 1024 })}
value={t("common.bitrate", { value: speedtestData.data.download * 1000 * 1000 })}
/>
<Block label="speedtest.upload" value={t("common.bitrate", { value: speedtestData.data.upload * 1024 * 1024 })} />
<Block label="speedtest.upload" value={t("common.bitrate", { value: speedtestData.data.upload * 1000 * 1000 })} />
<Block
label="speedtest.ping"
value={t("common.ms", {

View File

@@ -0,0 +1,42 @@
import { useTranslation } from "next-i18next";
import Container from "components/services/widget/container";
import Block from "components/services/widget/block";
import useWidgetAPI from "utils/proxy/use-widget-api";
export default function Component({ service }) {
const { t } = useTranslation();
const { widget } = service;
const { data: tdarrData, error: tdarrError } = useWidgetAPI(widget);
if (tdarrError) {
return <Container error={tdarrError} />;
}
if (!tdarrData) {
return (
<Container service={service}>
<Block label="tdarr.queue" />
<Block label="tdarr.processed" />
<Block label="tdarr.errored" />
<Block label="tdarr.saved" />
</Container>
);
}
const queue = parseInt(tdarrData.table1Count, 10) + parseInt(tdarrData.table4Count, 10);
const processed = parseInt(tdarrData.table2Count, 10) + parseInt(tdarrData.table5Count, 10);
const errored = parseInt(tdarrData.table3Count, 10) + parseInt(tdarrData.table6Count, 10);
const saved = parseFloat(tdarrData.sizeDiff, 10) * 1000000000;
return (
<Container service={service}>
<Block label="tdarr.queue" value={t("common.number", { value: queue })} />
<Block label="tdarr.processed" value={t("common.number", { value: processed })} />
<Block label="tdarr.errored" value={t("common.number", { value: errored })} />
<Block label="tdarr.saved" value={t("common.bytes", { value: saved })} />
</Container>
);
}

View File

@@ -0,0 +1,48 @@
import { httpProxy } from "utils/proxy/http";
import { formatApiCall } from "utils/proxy/api-helpers";
import getServiceWidget from "utils/config/service-helpers";
import createLogger from "utils/logger";
import widgets from "widgets/widgets";
const proxyName = "tdarrProxyHandler";
const logger = createLogger(proxyName);
export default async function tdarrProxyHandler(req, res) {
const { group, service, endpoint } = req.query;
if (!group || !service) {
logger.debug("Invalid or missing service '%s' or group '%s'", service, group);
return res.status(400).json({ error: "Invalid proxy service type" });
}
const widget = await getServiceWidget(group, service);
if (!widget) {
logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group);
return res.status(400).json({ error: "Invalid proxy service type" });
}
const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
const [status, contentType, data] = await httpProxy(url, {
method: "POST",
body: JSON.stringify({
"data": {
"collection": "StatisticsJSONDB",
"mode": "getById",
"docID": "statistics"
},
}),
headers: {
"content-type": "application/json",
},
});
if (status !== 200) {
logger.error("Error getting data from Tdarr: %d. Data: %s", status, data);
return res.status(500).send({error: {message:"Error getting data from Tdarr", url, data}});
}
if (contentType) res.setHeader("Content-Type", contentType);
return res.status(status).send(data);
}

View File

@@ -0,0 +1,8 @@
import tdarrProxyHandler from "./proxy";
const widget = {
api: "{url}/api/v2/cruddb",
proxyHandler: tdarrProxyHandler,
};
export default widget;

View File

@@ -25,12 +25,12 @@ export default async function transmissionProxyHandler(req, res) {
return res.status(400).json({ error: "Invalid proxy service type" });
}
let headers = cache.get(headerCacheKey);
let headers = cache.get(`${headerCacheKey}.${service}`);
if (!headers) {
headers = {
"content-type": "application/json",
}
cache.put(headerCacheKey, headers);
cache.put(`${headerCacheKey}.${service}`, headers);
}
const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
@@ -55,7 +55,7 @@ export default async function transmissionProxyHandler(req, res) {
if (status === 409) {
logger.debug("Transmission is rejecting the request, but returning a CSRF token");
headers[csrfHeaderName] = responseHeaders[csrfHeaderName];
cache.put(headerCacheKey, headers);
cache.put(`${headerCacheKey}.${service}`, headers);
// retry the request, now with the CSRF token
[status, contentType, data, responseHeaders] = await httpProxy(url, {

View File

@@ -58,6 +58,7 @@ async function login(widget) {
export default async function unifiProxyHandler(req, res) {
const widget = await getWidget(req);
const { service } = req.query;
if (!widget) {
return res.status(400).json({ error: "Invalid proxy service type" });
}
@@ -68,7 +69,7 @@ export default async function unifiProxyHandler(req, res) {
}
let [status, contentType, data, responseHeaders] = [];
let prefix = cache.get(prefixCacheKey);
let prefix = cache.get(`${prefixCacheKey}.${service}`);
if (prefix === null) {
// auto detect if we're talking to a UDM Pro, and cache the result so that we
// don't make two requests each time data from Unifi is required
@@ -77,7 +78,7 @@ export default async function unifiProxyHandler(req, res) {
if (responseHeaders?.["x-csrf-token"]) {
prefix = udmpPrefix;
}
cache.put(prefixCacheKey, prefix);
cache.put(`${prefixCacheKey}.${service}`, prefix);
}
widget.prefix = prefix;

View File

@@ -7,6 +7,7 @@ import coinmarketcap from "./coinmarketcap/widget";
import deluge from "./deluge/widget";
import diskstation from "./diskstation/widget";
import emby from "./emby/widget";
import flood from "./flood/widget";
import gluetun from "./gluetun/widget";
import gotify from "./gotify/widget";
import hdhomerun from "./hdhomerun/widget";
@@ -15,7 +16,9 @@ import jackett from "./jackett/widget";
import jellyseerr from "./jellyseerr/widget";
import lidarr from "./lidarr/widget";
import mastodon from "./mastodon/widget";
import miniflux from "./miniflux/widget";
import navidrome from "./navidrome/widget";
import nextdns from "./nextdns/widget";
import npm from "./npm/widget";
import nzbget from "./nzbget/widget";
import ombi from "./ombi/widget";
@@ -37,6 +40,7 @@ import sonarr from "./sonarr/widget";
import speedtest from "./speedtest/widget";
import strelaysrv from "./strelaysrv/widget";
import tautulli from "./tautulli/widget";
import tdarr from "./tdarr/widget";
import traefik from "./traefik/widget";
import transmission from "./transmission/widget";
import tubearchivist from "./tubearchivist/widget";
@@ -54,6 +58,7 @@ const widgets = {
deluge,
diskstation,
emby,
flood,
gluetun,
gotify,
hdhomerun,
@@ -63,7 +68,9 @@ const widgets = {
jellyseerr,
lidarr,
mastodon,
miniflux,
navidrome,
nextdns,
npm,
nzbget,
ombi,
@@ -85,6 +92,7 @@ const widgets = {
speedtest,
strelaysrv,
tautulli,
tdarr,
traefik,
transmission,
tubearchivist,